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.junit.jupiter.api.Assertions;
53  import org.junit.jupiter.api.BeforeEach;
54  import org.junit.jupiter.api.Test;
55  import org.mockito.ArgumentMatchers;
56  import org.mockito.Mockito;
57  
58  public class TestRequestAddCookies {
59  
60      private HttpHost target;
61      private CookieStore cookieStore;
62      private Lookup<CookieSpecFactory> cookieSpecRegistry;
63  
64      @BeforeEach
65      public void setUp() {
66          this.target = new HttpHost("localhost.local", 80);
67          this.cookieStore = new BasicCookieStore();
68          final BasicClientCookie cookie1 = new BasicClientCookie("name1", "value1");
69          cookie1.setDomain("localhost.local");
70          cookie1.setPath("/");
71          this.cookieStore.addCookie(cookie1);
72          final BasicClientCookie cookie2 = new BasicClientCookie("name2", "value2");
73          cookie2.setDomain("localhost.local");
74          cookie2.setPath("/");
75          this.cookieStore.addCookie(cookie2);
76  
77          this.cookieSpecRegistry = RegistryBuilder.<CookieSpecFactory>create()
78              .register(StandardCookieSpec.RELAXED, new RFC6265CookieSpecFactory(
79                      RFC6265CookieSpecFactory.CompatibilityLevel.RELAXED, null))
80              .register(StandardCookieSpec.STRICT,  new RFC6265CookieSpecFactory(
81                      RFC6265CookieSpecFactory.CompatibilityLevel.STRICT, null))
82              .register(StandardCookieSpec.IGNORE, new IgnoreCookieSpecFactory())
83              .build();
84      }
85  
86      @Test
87      public void testRequestParameterCheck() throws Exception {
88          final HttpClientContext context = HttpClientContext.create();
89          final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE;
90          Assertions.assertThrows(NullPointerException.class, () ->
91                  interceptor.process(null, null, context));
92      }
93  
94      @Test
95      public void testContextParameterCheck() throws Exception {
96          final HttpRequest request = new BasicHttpRequest("GET", "/");
97          final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE;
98          Assertions.assertThrows(NullPointerException.class, () ->
99                  interceptor.process(request, null, null));
100     }
101 
102     @Test
103     public void testAddCookies() throws Exception {
104         final HttpRequest request = new BasicHttpRequest("GET", "/");
105 
106         final HttpRoute route = new HttpRoute(this.target, null, false);
107 
108         final HttpClientContext context = HttpClientContext.create();
109         context.setRoute(route);
110         context.setCookieStore(this.cookieStore);
111         context.setCookieSpecRegistry(this.cookieSpecRegistry);
112 
113         final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE;
114         interceptor.process(request, null, context);
115 
116         final Header[] headers = request.getHeaders("Cookie");
117         Assertions.assertNotNull(headers);
118         Assertions.assertEquals(1, headers.length);
119         Assertions.assertEquals("name1=value1; name2=value2", headers[0].getValue());
120 
121         final CookieOrigin cookieOrigin = context.getCookieOrigin();
122         Assertions.assertNotNull(cookieOrigin);
123         Assertions.assertEquals(this.target.getHostName(), cookieOrigin.getHost());
124         Assertions.assertEquals(this.target.getPort(), cookieOrigin.getPort());
125         Assertions.assertEquals("/", cookieOrigin.getPath());
126         Assertions.assertFalse(cookieOrigin.isSecure());
127     }
128 
129     @Test
130     public void testCookiesForConnectRequest() throws Exception {
131         final HttpRequest request = new BasicHttpRequest("CONNECT", "www.somedomain.com");
132 
133         final HttpRoute route = new HttpRoute(this.target, null, false);
134 
135         final HttpClientContext context = HttpClientContext.create();
136         context.setRoute(route);
137         context.setCookieStore(this.cookieStore);
138         context.setCookieSpecRegistry(this.cookieSpecRegistry);
139 
140         final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE;
141         interceptor.process(request, null, context);
142 
143         final Header[] headers = request.getHeaders("Cookie");
144         Assertions.assertNotNull(headers);
145         Assertions.assertEquals(0, headers.length);
146     }
147 
148     @Test
149     public void testNoCookieStore() throws Exception {
150         final HttpRequest request = new BasicHttpRequest("GET", "/");
151 
152         final HttpRoute route = new HttpRoute(this.target, null, false);
153 
154         final HttpClientContext context = HttpClientContext.create();
155         context.setRoute(route);
156         context.setCookieStore(null);
157         context.setCookieSpecRegistry(this.cookieSpecRegistry);
158 
159         final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE;
160         interceptor.process(request, null, context);
161 
162         final Header[] headers = request.getHeaders("Cookie");
163         Assertions.assertNotNull(headers);
164         Assertions.assertEquals(0, headers.length);
165     }
166 
167     @Test
168     public void testNoCookieSpecRegistry() throws Exception {
169         final HttpRequest request = new BasicHttpRequest("GET", "/");
170 
171         final HttpRoute route = new HttpRoute(this.target, null, false);
172 
173         final HttpClientContext context = HttpClientContext.create();
174         context.setRoute(route);
175         context.setCookieStore(this.cookieStore);
176         context.setCookieSpecRegistry(null);
177 
178         final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE;
179         interceptor.process(request, null, context);
180 
181         final Header[] headers = request.getHeaders("Cookie");
182         Assertions.assertNotNull(headers);
183         Assertions.assertEquals(0, headers.length);
184     }
185 
186     @Test
187     public void testNoHttpConnection() throws Exception {
188         final HttpRequest request = new BasicHttpRequest("GET", "/");
189 
190         final HttpClientContext context = HttpClientContext.create();
191         context.setEndpointDetails(null);
192         context.setCookieStore(this.cookieStore);
193         context.setCookieSpecRegistry(this.cookieSpecRegistry);
194 
195         final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE;
196         interceptor.process(request, null, context);
197 
198         final Header[] headers = request.getHeaders("Cookie");
199         Assertions.assertNotNull(headers);
200         Assertions.assertEquals(0, headers.length);
201     }
202 
203     @Test
204     public void testAddCookiesUsingExplicitCookieSpec() throws Exception {
205         final HttpRequest request = new BasicHttpRequest("GET", "/");
206         final RequestConfig config = RequestConfig.custom()
207                 .setCookieSpec(StandardCookieSpec.STRICT)
208                 .build();
209         final HttpRoute route = new HttpRoute(this.target, null, false);
210 
211         final HttpClientContext context = HttpClientContext.create();
212         context.setRoute(route);
213         context.setRequestConfig(config);
214         context.setCookieStore(this.cookieStore);
215         context.setCookieSpecRegistry(this.cookieSpecRegistry);
216 
217         final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE;
218         interceptor.process(request, null, context);
219 
220         final CookieSpec cookieSpec = context.getCookieSpec();
221         Assertions.assertTrue(cookieSpec instanceof RFC6265StrictSpec);
222 
223         final Header[] headers1 = request.getHeaders("Cookie");
224         Assertions.assertNotNull(headers1);
225         Assertions.assertEquals(1, headers1.length);
226         Assertions.assertEquals("name1=value1; name2=value2", headers1[0].getValue());
227     }
228 
229     @Test
230     public void testAuthScopeInvalidRequestURI() throws Exception {
231         final HttpRequest request = new BasicHttpRequest("GET", "crap:");
232 
233         final HttpRoute route = new HttpRoute(this.target, null, false);
234 
235         final HttpClientContext context = HttpClientContext.create();
236         context.setRoute(route);
237         context.setCookieStore(this.cookieStore);
238         context.setCookieSpecRegistry(this.cookieSpecRegistry);
239 
240         final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE;
241         interceptor.process(request, null, context);
242     }
243 
244     @Test
245     public void testAuthScopeRemotePortWhenDirect() throws Exception {
246         final HttpRequest request = new BasicHttpRequest("GET", "/stuff");
247 
248         this.target = new HttpHost("localhost.local");
249         final HttpRoute route = new HttpRoute(new HttpHost("localhost.local", 1234), null, false);
250 
251         final HttpClientContext context = HttpClientContext.create();
252         context.setRoute(route);
253         context.setCookieStore(this.cookieStore);
254         context.setCookieSpecRegistry(this.cookieSpecRegistry);
255 
256         final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE;
257         interceptor.process(request, null, context);
258 
259         final CookieOrigin cookieOrigin = context.getCookieOrigin();
260         Assertions.assertNotNull(cookieOrigin);
261         Assertions.assertEquals(this.target.getHostName(), cookieOrigin.getHost());
262         Assertions.assertEquals(1234, cookieOrigin.getPort());
263         Assertions.assertEquals("/stuff", cookieOrigin.getPath());
264         Assertions.assertFalse(cookieOrigin.isSecure());
265     }
266 
267     @Test
268     public void testAuthDefaultHttpPortWhenProxy() throws Exception {
269         final HttpRequest request = new BasicHttpRequest("GET", "/stuff");
270 
271         this.target = new HttpHost("localhost.local");
272         final HttpRoute route = new HttpRoute(
273                 new HttpHost("localhost.local", 80), null, new HttpHost("localhost", 8888), false);
274 
275         final HttpClientContext context = HttpClientContext.create();
276         context.setRoute(route);
277         context.setCookieStore(this.cookieStore);
278         context.setCookieSpecRegistry(this.cookieSpecRegistry);
279 
280         final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE;
281         interceptor.process(request, null, context);
282 
283         final CookieOrigin cookieOrigin = context.getCookieOrigin();
284         Assertions.assertNotNull(cookieOrigin);
285         Assertions.assertEquals(this.target.getHostName(), cookieOrigin.getHost());
286         Assertions.assertEquals(80, cookieOrigin.getPort());
287         Assertions.assertEquals("/stuff", cookieOrigin.getPath());
288         Assertions.assertFalse(cookieOrigin.isSecure());
289     }
290 
291     @Test
292     public void testAuthDefaultHttpsPortWhenProxy() throws Exception {
293         final HttpRequest request = new BasicHttpRequest("GET", "/stuff");
294 
295         this.target = new HttpHost("https", "localhost", -1);
296         final HttpRoute route = new HttpRoute(
297                 new HttpHost("https", "localhost", 443), null,
298                 new HttpHost("http", "localhost", 8888), true, TunnelType.TUNNELLED, LayerType.LAYERED);
299 
300         final HttpClientContext context = HttpClientContext.create();
301         context.setRoute(route);
302         context.setCookieStore(this.cookieStore);
303         context.setCookieSpecRegistry(this.cookieSpecRegistry);
304 
305         final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE;
306         interceptor.process(request, null, context);
307 
308         final CookieOrigin cookieOrigin = context.getCookieOrigin();
309         Assertions.assertNotNull(cookieOrigin);
310         Assertions.assertEquals(this.target.getHostName(), cookieOrigin.getHost());
311         Assertions.assertEquals(443, cookieOrigin.getPort());
312         Assertions.assertEquals("/stuff", cookieOrigin.getPath());
313         Assertions.assertTrue(cookieOrigin.isSecure());
314     }
315 
316     @Test
317     public void testExcludeExpiredCookies() throws Exception {
318         final HttpRequest request = new BasicHttpRequest("GET", "/");
319 
320         final BasicClientCookie cookie3 = new BasicClientCookie("name3", "value3");
321         cookie3.setDomain("localhost.local");
322         cookie3.setPath("/");
323         cookie3.setExpiryDate(Instant.now().plusMillis(100));
324         this.cookieStore.addCookie(cookie3);
325 
326         Assertions.assertEquals(3, this.cookieStore.getCookies().size());
327 
328         this.cookieStore = Mockito.spy(this.cookieStore);
329 
330         final HttpRoute route = new HttpRoute(this.target, null, false);
331 
332         final HttpClientContext context = HttpClientContext.create();
333         context.setRoute(route);
334         context.setCookieStore(this.cookieStore);
335         context.setCookieSpecRegistry(this.cookieSpecRegistry);
336 
337         // Make sure the third cookie expires
338         Thread.sleep(200);
339 
340         final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE;
341         interceptor.process(request, null, context);
342 
343         final Header[] headers = request.getHeaders("Cookie");
344         Assertions.assertNotNull(headers);
345         Assertions.assertEquals(1, headers.length);
346         Assertions.assertEquals("name1=value1; name2=value2", headers[0].getValue());
347 
348         Mockito.verify(this.cookieStore, Mockito.times(1)).clearExpired(ArgumentMatchers.any(Instant.class));
349     }
350 
351     @Test
352     public void testNoMatchingCookies() throws Exception {
353         final HttpRequest request = new BasicHttpRequest("GET", "/");
354 
355         this.cookieStore.clear();
356         final BasicClientCookie cookie3 = new BasicClientCookie("name3", "value3");
357         cookie3.setDomain("www.somedomain.com");
358         cookie3.setPath("/");
359         this.cookieStore.addCookie(cookie3);
360 
361         final HttpRoute route = new HttpRoute(this.target, null, false);
362 
363         final HttpClientContext context = HttpClientContext.create();
364         context.setRoute(route);
365         context.setCookieStore(this.cookieStore);
366         context.setCookieSpecRegistry(this.cookieSpecRegistry);
367 
368         final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE;
369         interceptor.process(request, null, context);
370 
371         final Header[] headers = request.getHeaders("Cookie");
372         Assertions.assertNotNull(headers);
373         Assertions.assertEquals(0, headers.length);
374     }
375 
376     // Helper method
377     private BasicClientCookie makeCookie(final String name, final String value, final String domain, final String path) {
378         final BasicClientCookie cookie = new BasicClientCookie(name, value);
379         cookie.setDomain(domain);
380         cookie.setPath(path);
381         return cookie;
382     }
383 
384     @Test
385     // Test for ordering adapted from test in Commons HC 3.1
386     public void testCookieOrder() throws Exception {
387         final HttpRequest request = new BasicHttpRequest("GET", "/foobar/yada/yada");
388 
389         this.cookieStore.clear();
390 
391         cookieStore.addCookie(makeCookie("nomatch", "value", "localhost.local", "/noway"));
392         cookieStore.addCookie(makeCookie("name2",   "value", "localhost.local", "/foobar/yada"));
393         cookieStore.addCookie(makeCookie("name3",   "value", "localhost.local", "/foobar"));
394         cookieStore.addCookie(makeCookie("name1",   "value", "localhost.local", "/foobar/yada/yada"));
395 
396         final HttpRoute route = new HttpRoute(this.target, null, false);
397 
398         final HttpClientContext context = HttpClientContext.create();
399         context.setRoute(route);
400         context.setCookieStore(this.cookieStore);
401         context.setCookieSpecRegistry(this.cookieSpecRegistry);
402 
403         final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE;
404         interceptor.process(request, null, context);
405 
406         final Header[] headers1 = request.getHeaders("Cookie");
407         Assertions.assertNotNull(headers1);
408         Assertions.assertEquals(1, headers1.length);
409 
410         Assertions.assertEquals("name1=value; name2=value; name3=value", headers1[0].getValue());
411     }
412 
413     @Test
414     public void testSkipAddingCookiesWhenCookieHeaderPresent() throws Exception {
415         // Prepare a request with an existing Cookie header
416         final HttpRequest request = new BasicHttpRequest("GET", "/");
417         request.addHeader("Cookie", "existingCookie=existingValue");
418 
419         final HttpRoute route = new HttpRoute(this.target, null, false);
420 
421         final HttpClientContext context = HttpClientContext.create();
422         context.setRoute(route);
423         context.setCookieStore(this.cookieStore);
424         context.setCookieSpecRegistry(this.cookieSpecRegistry);
425 
426         final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE;
427         interceptor.process(request, null, context);
428 
429         // Check that no additional cookies were added
430         final Header[] headers = request.getHeaders("Cookie");
431         Assertions.assertNotNull(headers);
432         Assertions.assertEquals(1, headers.length);
433         Assertions.assertEquals("existingCookie=existingValue", headers[0].getValue());
434     }
435 
436 }