View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  package org.apache.hc.client5.http.protocol;
28  
29  import java.time.Instant;
30  
31  import org.apache.hc.client5.http.HttpRoute;
32  import org.apache.hc.client5.http.RouteInfo.LayerType;
33  import org.apache.hc.client5.http.RouteInfo.TunnelType;
34  import org.apache.hc.client5.http.config.RequestConfig;
35  import org.apache.hc.client5.http.cookie.BasicCookieStore;
36  import org.apache.hc.client5.http.cookie.CookieOrigin;
37  import org.apache.hc.client5.http.cookie.CookieSpec;
38  import org.apache.hc.client5.http.cookie.CookieSpecFactory;
39  import org.apache.hc.client5.http.cookie.CookieStore;
40  import org.apache.hc.client5.http.cookie.StandardCookieSpec;
41  import org.apache.hc.client5.http.impl.cookie.BasicClientCookie;
42  import org.apache.hc.client5.http.impl.cookie.IgnoreCookieSpecFactory;
43  import org.apache.hc.client5.http.impl.cookie.RFC6265CookieSpecFactory;
44  import org.apache.hc.client5.http.impl.cookie.RFC6265StrictSpec;
45  import org.apache.hc.core5.http.Header;
46  import org.apache.hc.core5.http.HttpHost;
47  import org.apache.hc.core5.http.HttpRequest;
48  import org.apache.hc.core5.http.HttpRequestInterceptor;
49  import org.apache.hc.core5.http.config.Lookup;
50  import org.apache.hc.core5.http.config.RegistryBuilder;
51  import org.apache.hc.core5.http.message.BasicHttpRequest;
52  import org.apache.hc.core5.http.protocol.HttpCoreContext;
53  import org.junit.jupiter.api.Assertions;
54  import org.junit.jupiter.api.BeforeEach;
55  import org.junit.jupiter.api.Test;
56  import org.mockito.ArgumentMatchers;
57  import org.mockito.Mockito;
58  
59  public class TestRequestAddCookies {
60  
61      private HttpHost target;
62      private CookieStore cookieStore;
63      private Lookup<CookieSpecFactory> cookieSpecRegistry;
64  
65      @BeforeEach
66      public void setUp() {
67          this.target = new HttpHost("localhost.local", 80);
68          this.cookieStore = new BasicCookieStore();
69          final BasicClientCookie cookie1 = new BasicClientCookie("name1", "value1");
70          cookie1.setDomain("localhost.local");
71          cookie1.setPath("/");
72          this.cookieStore.addCookie(cookie1);
73          final BasicClientCookie cookie2 = new BasicClientCookie("name2", "value2");
74          cookie2.setDomain("localhost.local");
75          cookie2.setPath("/");
76          this.cookieStore.addCookie(cookie2);
77  
78          this.cookieSpecRegistry = RegistryBuilder.<CookieSpecFactory>create()
79              .register(StandardCookieSpec.RELAXED, new RFC6265CookieSpecFactory(
80                      RFC6265CookieSpecFactory.CompatibilityLevel.RELAXED, null))
81              .register(StandardCookieSpec.STRICT,  new RFC6265CookieSpecFactory(
82                      RFC6265CookieSpecFactory.CompatibilityLevel.STRICT, null))
83              .register(StandardCookieSpec.IGNORE, new IgnoreCookieSpecFactory())
84              .build();
85      }
86  
87      @Test
88      public void testRequestParameterCheck() throws Exception {
89          final HttpClientContext context = HttpClientContext.create();
90          final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE;
91          Assertions.assertThrows(NullPointerException.class, () ->
92                  interceptor.process(null, null, context));
93      }
94  
95      @Test
96      public void testContextParameterCheck() throws Exception {
97          final HttpRequest request = new BasicHttpRequest("GET", "/");
98          final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE;
99          Assertions.assertThrows(NullPointerException.class, () ->
100                 interceptor.process(request, null, null));
101     }
102 
103     @Test
104     public void testAddCookies() throws Exception {
105         final HttpRequest request = new BasicHttpRequest("GET", "/");
106 
107         final HttpRoute route = new HttpRoute(this.target, null, false);
108 
109         final HttpClientContext context = HttpClientContext.create();
110         context.setAttribute(HttpClientContext.HTTP_ROUTE, route);
111         context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore);
112         context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, this.cookieSpecRegistry);
113 
114         final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE;
115         interceptor.process(request, null, context);
116 
117         final Header[] headers = request.getHeaders("Cookie");
118         Assertions.assertNotNull(headers);
119         Assertions.assertEquals(1, headers.length);
120         Assertions.assertEquals("name1=value1; name2=value2", headers[0].getValue());
121 
122         final CookieOrigin cookieOrigin = context.getCookieOrigin();
123         Assertions.assertNotNull(cookieOrigin);
124         Assertions.assertEquals(this.target.getHostName(), cookieOrigin.getHost());
125         Assertions.assertEquals(this.target.getPort(), cookieOrigin.getPort());
126         Assertions.assertEquals("/", cookieOrigin.getPath());
127         Assertions.assertFalse(cookieOrigin.isSecure());
128     }
129 
130     @Test
131     public void testCookiesForConnectRequest() throws Exception {
132         final HttpRequest request = new BasicHttpRequest("CONNECT", "www.somedomain.com");
133 
134         final HttpRoute route = new HttpRoute(this.target, null, false);
135 
136         final HttpClientContext context = HttpClientContext.create();
137         context.setAttribute(HttpClientContext.HTTP_ROUTE, route);
138         context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore);
139         context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, this.cookieSpecRegistry);
140 
141         final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE;
142         interceptor.process(request, null, context);
143 
144         final Header[] headers = request.getHeaders("Cookie");
145         Assertions.assertNotNull(headers);
146         Assertions.assertEquals(0, headers.length);
147     }
148 
149     @Test
150     public void testNoCookieStore() throws Exception {
151         final HttpRequest request = new BasicHttpRequest("GET", "/");
152 
153         final HttpRoute route = new HttpRoute(this.target, null, false);
154 
155         final HttpClientContext context = HttpClientContext.create();
156         context.setAttribute(HttpClientContext.HTTP_ROUTE, route);
157         context.setAttribute(HttpClientContext.COOKIE_STORE, null);
158         context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, this.cookieSpecRegistry);
159 
160         final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE;
161         interceptor.process(request, null, context);
162 
163         final Header[] headers = request.getHeaders("Cookie");
164         Assertions.assertNotNull(headers);
165         Assertions.assertEquals(0, headers.length);
166     }
167 
168     @Test
169     public void testNoCookieSpecRegistry() throws Exception {
170         final HttpRequest request = new BasicHttpRequest("GET", "/");
171 
172         final HttpRoute route = new HttpRoute(this.target, null, false);
173 
174         final HttpClientContext context = HttpClientContext.create();
175         context.setAttribute(HttpClientContext.HTTP_ROUTE, route);
176         context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore);
177         context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, null);
178 
179         final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE;
180         interceptor.process(request, null, context);
181 
182         final Header[] headers = request.getHeaders("Cookie");
183         Assertions.assertNotNull(headers);
184         Assertions.assertEquals(0, headers.length);
185     }
186 
187     @Test
188     public void testNoHttpConnection() throws Exception {
189         final HttpRequest request = new BasicHttpRequest("GET", "/");
190 
191         final HttpClientContext context = HttpClientContext.create();
192         context.setAttribute(HttpCoreContext.CONNECTION_ENDPOINT, null);
193         context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore);
194         context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, this.cookieSpecRegistry);
195 
196         final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE;
197         interceptor.process(request, null, context);
198 
199         final Header[] headers = request.getHeaders("Cookie");
200         Assertions.assertNotNull(headers);
201         Assertions.assertEquals(0, headers.length);
202     }
203 
204     @Test
205     public void testAddCookiesUsingExplicitCookieSpec() throws Exception {
206         final HttpRequest request = new BasicHttpRequest("GET", "/");
207         final RequestConfig config = RequestConfig.custom()
208                 .setCookieSpec(StandardCookieSpec.STRICT)
209                 .build();
210         final HttpRoute route = new HttpRoute(this.target, null, false);
211 
212         final HttpClientContext context = HttpClientContext.create();
213         context.setAttribute(HttpClientContext.HTTP_ROUTE, route);
214         context.setAttribute(HttpClientContext.REQUEST_CONFIG, config);
215         context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore);
216         context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, this.cookieSpecRegistry);
217 
218         final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE;
219         interceptor.process(request, null, context);
220 
221         final CookieSpec cookieSpec = context.getCookieSpec();
222         Assertions.assertTrue(cookieSpec instanceof RFC6265StrictSpec);
223 
224         final Header[] headers1 = request.getHeaders("Cookie");
225         Assertions.assertNotNull(headers1);
226         Assertions.assertEquals(1, headers1.length);
227         Assertions.assertEquals("name1=value1; name2=value2", headers1[0].getValue());
228     }
229 
230     @Test
231     public void testAuthScopeInvalidRequestURI() throws Exception {
232         final HttpRequest request = new BasicHttpRequest("GET", "crap:");
233 
234         final HttpRoute route = new HttpRoute(this.target, null, false);
235 
236         final HttpClientContext context = HttpClientContext.create();
237         context.setAttribute(HttpClientContext.HTTP_ROUTE, route);
238         context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore);
239         context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, this.cookieSpecRegistry);
240 
241         final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE;
242         interceptor.process(request, null, context);
243     }
244 
245     @Test
246     public void testAuthScopeRemotePortWhenDirect() throws Exception {
247         final HttpRequest request = new BasicHttpRequest("GET", "/stuff");
248 
249         this.target = new HttpHost("localhost.local");
250         final HttpRoute route = new HttpRoute(new HttpHost("localhost.local", 1234), null, false);
251 
252         final HttpClientContext context = HttpClientContext.create();
253         context.setAttribute(HttpClientContext.HTTP_ROUTE, route);
254         context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore);
255         context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, this.cookieSpecRegistry);
256 
257         final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE;
258         interceptor.process(request, null, context);
259 
260         final CookieOrigin cookieOrigin = context.getCookieOrigin();
261         Assertions.assertNotNull(cookieOrigin);
262         Assertions.assertEquals(this.target.getHostName(), cookieOrigin.getHost());
263         Assertions.assertEquals(1234, cookieOrigin.getPort());
264         Assertions.assertEquals("/stuff", cookieOrigin.getPath());
265         Assertions.assertFalse(cookieOrigin.isSecure());
266     }
267 
268     @Test
269     public void testAuthDefaultHttpPortWhenProxy() throws Exception {
270         final HttpRequest request = new BasicHttpRequest("GET", "/stuff");
271 
272         this.target = new HttpHost("localhost.local");
273         final HttpRoute route = new HttpRoute(
274                 new HttpHost("localhost.local", 80), null, new HttpHost("localhost", 8888), false);
275 
276         final HttpClientContext context = HttpClientContext.create();
277         context.setAttribute(HttpClientContext.HTTP_ROUTE, route);
278         context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore);
279         context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, this.cookieSpecRegistry);
280 
281         final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE;
282         interceptor.process(request, null, context);
283 
284         final CookieOrigin cookieOrigin = context.getCookieOrigin();
285         Assertions.assertNotNull(cookieOrigin);
286         Assertions.assertEquals(this.target.getHostName(), cookieOrigin.getHost());
287         Assertions.assertEquals(80, cookieOrigin.getPort());
288         Assertions.assertEquals("/stuff", cookieOrigin.getPath());
289         Assertions.assertFalse(cookieOrigin.isSecure());
290     }
291 
292     @Test
293     public void testAuthDefaultHttpsPortWhenProxy() throws Exception {
294         final HttpRequest request = new BasicHttpRequest("GET", "/stuff");
295 
296         this.target = new HttpHost("https", "localhost", -1);
297         final HttpRoute route = new HttpRoute(
298                 new HttpHost("https", "localhost", 443), null,
299                 new HttpHost("http", "localhost", 8888), true, TunnelType.TUNNELLED, LayerType.LAYERED);
300 
301         final HttpClientContext context = HttpClientContext.create();
302         context.setAttribute(HttpClientContext.HTTP_ROUTE, route);
303         context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore);
304         context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, this.cookieSpecRegistry);
305 
306         final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE;
307         interceptor.process(request, null, context);
308 
309         final CookieOrigin cookieOrigin = context.getCookieOrigin();
310         Assertions.assertNotNull(cookieOrigin);
311         Assertions.assertEquals(this.target.getHostName(), cookieOrigin.getHost());
312         Assertions.assertEquals(443, cookieOrigin.getPort());
313         Assertions.assertEquals("/stuff", cookieOrigin.getPath());
314         Assertions.assertTrue(cookieOrigin.isSecure());
315     }
316 
317     @Test
318     public void testExcludeExpiredCookies() throws Exception {
319         final HttpRequest request = new BasicHttpRequest("GET", "/");
320 
321         final BasicClientCookie cookie3 = new BasicClientCookie("name3", "value3");
322         cookie3.setDomain("localhost.local");
323         cookie3.setPath("/");
324         cookie3.setExpiryDate(Instant.now().plusMillis(100));
325         this.cookieStore.addCookie(cookie3);
326 
327         Assertions.assertEquals(3, this.cookieStore.getCookies().size());
328 
329         this.cookieStore = Mockito.spy(this.cookieStore);
330 
331         final HttpRoute route = new HttpRoute(this.target, null, false);
332 
333         final HttpClientContext context = HttpClientContext.create();
334         context.setAttribute(HttpClientContext.HTTP_ROUTE, route);
335         context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore);
336         context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, this.cookieSpecRegistry);
337 
338         // Make sure the third cookie expires
339         Thread.sleep(200);
340 
341         final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE;
342         interceptor.process(request, null, context);
343 
344         final Header[] headers = request.getHeaders("Cookie");
345         Assertions.assertNotNull(headers);
346         Assertions.assertEquals(1, headers.length);
347         Assertions.assertEquals("name1=value1; name2=value2", headers[0].getValue());
348 
349         Mockito.verify(this.cookieStore, Mockito.times(1)).clearExpired(ArgumentMatchers.any(Instant.class));
350     }
351 
352     @Test
353     public void testNoMatchingCookies() throws Exception {
354         final HttpRequest request = new BasicHttpRequest("GET", "/");
355 
356         this.cookieStore.clear();
357         final BasicClientCookie cookie3 = new BasicClientCookie("name3", "value3");
358         cookie3.setDomain("www.somedomain.com");
359         cookie3.setPath("/");
360         this.cookieStore.addCookie(cookie3);
361 
362         final HttpRoute route = new HttpRoute(this.target, null, false);
363 
364         final HttpClientContext context = HttpClientContext.create();
365         context.setAttribute(HttpClientContext.HTTP_ROUTE, route);
366         context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore);
367         context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, this.cookieSpecRegistry);
368 
369         final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE;
370         interceptor.process(request, null, context);
371 
372         final Header[] headers = request.getHeaders("Cookie");
373         Assertions.assertNotNull(headers);
374         Assertions.assertEquals(0, headers.length);
375     }
376 
377     // Helper method
378     private BasicClientCookie makeCookie(final String name, final String value, final String domain, final String path) {
379         final BasicClientCookie cookie = new BasicClientCookie(name, value);
380         cookie.setDomain(domain);
381         cookie.setPath(path);
382         return cookie;
383     }
384 
385     @Test
386     // Test for ordering adapted from test in Commons HC 3.1
387     public void testCookieOrder() throws Exception {
388         final HttpRequest request = new BasicHttpRequest("GET", "/foobar/yada/yada");
389 
390         this.cookieStore.clear();
391 
392         cookieStore.addCookie(makeCookie("nomatch", "value", "localhost.local", "/noway"));
393         cookieStore.addCookie(makeCookie("name2",   "value", "localhost.local", "/foobar/yada"));
394         cookieStore.addCookie(makeCookie("name3",   "value", "localhost.local", "/foobar"));
395         cookieStore.addCookie(makeCookie("name1",   "value", "localhost.local", "/foobar/yada/yada"));
396 
397         final HttpRoute route = new HttpRoute(this.target, null, false);
398 
399         final HttpClientContext context = HttpClientContext.create();
400         context.setAttribute(HttpClientContext.HTTP_ROUTE, route);
401         context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore);
402         context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, this.cookieSpecRegistry);
403 
404         final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE;
405         interceptor.process(request, null, context);
406 
407         final Header[] headers1 = request.getHeaders("Cookie");
408         Assertions.assertNotNull(headers1);
409         Assertions.assertEquals(1, headers1.length);
410 
411         Assertions.assertEquals("name1=value; name2=value; name3=value", headers1[0].getValue());
412     }
413 
414 }