View Javadoc
1   /*
2    * Copyright (c) 2002-2025 Gargoyle Software Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * https://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package org.htmlunit.httpclient;
16  
17  import java.net.MalformedURLException;
18  import java.net.URL;
19  import java.util.ArrayList;
20  import java.util.Collection;
21  import java.util.List;
22  import java.util.Set;
23  
24  import org.apache.http.NoHttpResponseException;
25  import org.apache.http.cookie.ClientCookie;
26  import org.apache.http.cookie.Cookie;
27  import org.apache.http.cookie.CookieOrigin;
28  import org.apache.http.cookie.CookieSpec;
29  import org.apache.http.cookie.MalformedCookieException;
30  import org.apache.http.message.BasicNameValuePair;
31  import org.apache.http.message.BufferedHeader;
32  import org.apache.http.util.CharArrayBuffer;
33  import org.htmlunit.BrowserVersion;
34  import org.htmlunit.util.NameValuePair;
35  import org.htmlunit.util.UrlUtils;
36  
37  /**
38   * Helper methods to convert from/to HttpClient.
39   *
40   * @author Ronald Brill
41   */
42  public final class HttpClientConverter {
43  
44      private HttpClientConverter() {
45          // util class
46      }
47  
48      /**
49       * Converts the specified name/value pairs into HttpClient name/value pairs.
50       * @param pairs the name/value pairs to convert
51       * @return the converted name/value pairs
52       */
53      public static List<org.apache.http.NameValuePair> nameValuePairsToHttpClient(final List<NameValuePair> pairs) {
54          final List<org.apache.http.NameValuePair> resultingPairs = new ArrayList<>(pairs.size());
55          for (final NameValuePair pair : pairs) {
56              resultingPairs.add(new BasicNameValuePair(pair.getName(), pair.getValue()));
57          }
58          return resultingPairs;
59      }
60  
61      /**
62       * @param e the exception to check
63       * @return true if the provided Exception is na {@link NoHttpResponseException}
64       */
65      public static boolean isNoHttpResponseException(final Exception e) {
66          return e instanceof NoHttpResponseException;
67      }
68  
69      /**
70       * Helper that builds a CookieOrigin.
71       * @param url the url to be used
72       * @return the new CookieOrigin
73       */
74      public static CookieOrigin buildCookieOrigin(final URL url) {
75          final URL normalizedUrl = replaceForCookieIfNecessary(url);
76  
77          int port = normalizedUrl.getPort();
78          if (port == -1) {
79              port = normalizedUrl.getDefaultPort();
80          }
81  
82          return new CookieOrigin(
83                  normalizedUrl.getHost(),
84                  port,
85                  normalizedUrl.getPath(),
86                  "https".equals(normalizedUrl.getProtocol()));
87      }
88  
89      /**
90       * {@link CookieOrigin} doesn't like empty hosts and negative ports,
91       * but these things happen if we're dealing with a local file.
92       * This method allows us to work around this limitation in HttpClient by feeding it a bogus host and port.
93       *
94       * @param url the URL to replace if necessary
95       * @return the replacement URL, or the original URL if no replacement was necessary
96       */
97      public static URL replaceForCookieIfNecessary(URL url) {
98          final String protocol = url.getProtocol();
99          final boolean file = "file".equals(protocol);
100         if (file) {
101             try {
102                 url = UrlUtils.getUrlWithNewHostAndPort(url,
103                         HtmlUnitBrowserCompatCookieSpec.LOCAL_FILESYSTEM_DOMAIN, 0);
104             }
105             catch (final MalformedURLException e) {
106                 throw new RuntimeException(e);
107             }
108         }
109         return url;
110     }
111 
112     /**
113      * @param cookieString the string to parse
114      * @param pageUrl the page url as root
115      * @param browserVersion the {@link BrowserVersion}
116      * @return a list of {@link org.htmlunit.util.Cookie}'s
117      * @throws MalformedCookieException in case the cookie does not conform to the spec
118      */
119     public static List<org.htmlunit.util.Cookie> parseCookie(final String cookieString, final URL pageUrl,
120             final BrowserVersion browserVersion)
121             throws MalformedCookieException {
122         final CharArrayBuffer buffer = new CharArrayBuffer(cookieString.length() + 22);
123         buffer.append("Set-Cookie: ");
124         buffer.append(cookieString);
125 
126         final CookieSpec cookieSpec = new HtmlUnitBrowserCompatCookieSpec(browserVersion);
127         final List<Cookie> cookies = cookieSpec.parse(new BufferedHeader(buffer), buildCookieOrigin(pageUrl));
128 
129         final List<org.htmlunit.util.Cookie> htmlUnitCookies = new ArrayList<>(cookies.size());
130         for (final Cookie cookie : cookies) {
131             final org.htmlunit.util.Cookie htmlUnitCookie = new org.htmlunit.util.Cookie((ClientCookie) cookie);
132             htmlUnitCookies.add(htmlUnitCookie);
133         }
134         return htmlUnitCookies;
135     }
136 
137     /**
138      * Converts the specified collection of cookies into a collection of HttpClient cookies.
139      * @param cookies the cookies to be converted
140      * @return the specified cookies, as HttpClient cookies
141      */
142     public static List<Cookie> toHttpClient(final Collection<org.htmlunit.util.Cookie> cookies) {
143         final ArrayList<Cookie> array = new ArrayList<>(cookies.size());
144         for (final org.htmlunit.util.Cookie cookie : cookies) {
145             array.add(cookie.toHttpClient());
146         }
147         return array;
148     }
149 
150     /**
151      * Converts the specified array of HttpClient cookies into a list of cookies.
152      * @param cookies the cookies to be converted
153      * @return the specified HttpClient cookies, as cookies
154      */
155     public static List<org.htmlunit.util.Cookie> fromHttpClient(final List<Cookie> cookies) {
156         final List<org.htmlunit.util.Cookie> list = new ArrayList<>(cookies.size());
157         for (final Cookie c : cookies) {
158             list.add(new org.htmlunit.util.Cookie((ClientCookie) c));
159         }
160         return list;
161     }
162 
163     /**
164      * Adds all matching cookies to the provided set.
165      * @param cookies the cookies to select from
166      * @param normalizedUrl the url to match against
167      * @param browserVersion the {@link BrowserVersion}
168      * @param matches the set to add
169      */
170     public static void addMatching(final Set<org.htmlunit.util.Cookie> cookies,
171             final URL normalizedUrl, final BrowserVersion browserVersion,
172             final Set<org.htmlunit.util.Cookie> matches) {
173         if (!cookies.isEmpty()) {
174             final CookieOrigin cookieOrigin = HttpClientConverter.buildCookieOrigin(normalizedUrl);
175             final CookieSpec cookieSpec = new HtmlUnitBrowserCompatCookieSpec(browserVersion);
176             for (final org.htmlunit.util.Cookie cookie : cookies) {
177                 if (cookieSpec.match(cookie.toHttpClient(), cookieOrigin)) {
178                     matches.add(cookie);
179                 }
180             }
181         }
182     }
183 }