Home | History | Annotate | Download | only in http
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      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  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.squareup.okhttp.internal.http;
     18 
     19 import com.squareup.okhttp.OkHttpClient;
     20 import com.squareup.okhttp.OkUrlFactory;
     21 import com.squareup.okhttp.mockwebserver.MockResponse;
     22 import com.squareup.okhttp.mockwebserver.MockWebServer;
     23 import com.squareup.okhttp.mockwebserver.RecordedRequest;
     24 import java.io.IOException;
     25 import java.net.CookieHandler;
     26 import java.net.CookieManager;
     27 import java.net.HttpCookie;
     28 import java.net.HttpURLConnection;
     29 import java.net.URI;
     30 import java.net.URLConnection;
     31 import java.util.Collection;
     32 import java.util.Collections;
     33 import java.util.HashMap;
     34 import java.util.List;
     35 import java.util.Map;
     36 import org.junit.After;
     37 import org.junit.Before;
     38 import org.junit.Test;
     39 
     40 import static java.net.CookiePolicy.ACCEPT_ORIGINAL_SERVER;
     41 import static org.junit.Assert.assertEquals;
     42 import static org.junit.Assert.assertFalse;
     43 import static org.junit.Assert.assertNull;
     44 import static org.junit.Assert.assertTrue;
     45 import static org.junit.Assert.fail;
     46 
     47 /** Android's CookiesTest. */
     48 public class CookiesTest {
     49 
     50   private OkHttpClient client;
     51 
     52   @Before
     53   public void setUp() throws Exception {
     54     client = new OkHttpClient();
     55   }
     56 
     57   @After
     58   public void tearDown() throws Exception {
     59     CookieHandler.setDefault(null);
     60   }
     61 
     62   @Test
     63   public void testNetscapeResponse() throws Exception {
     64     CookieManager cookieManager = new CookieManager(null, ACCEPT_ORIGINAL_SERVER);
     65     CookieHandler.setDefault(cookieManager);
     66     MockWebServer server = new MockWebServer();
     67     server.start();
     68 
     69     server.enqueue(new MockResponse().addHeader("Set-Cookie: a=android; "
     70         + "expires=Fri, 31-Dec-9999 23:59:59 GMT; "
     71         + "path=/path; "
     72         + "domain=" + server.getCookieDomain() + "; "
     73         + "secure"));
     74     get(server, "/path/foo");
     75 
     76     List<HttpCookie> cookies = cookieManager.getCookieStore().getCookies();
     77     assertEquals(1, cookies.size());
     78     HttpCookie cookie = cookies.get(0);
     79     assertEquals("a", cookie.getName());
     80     assertEquals("android", cookie.getValue());
     81     assertEquals(null, cookie.getComment());
     82     assertEquals(null, cookie.getCommentURL());
     83     assertEquals(false, cookie.getDiscard());
     84     assertTrue(server.getCookieDomain().equalsIgnoreCase(cookie.getDomain()));
     85     assertTrue(cookie.getMaxAge() > 100000000000L);
     86     assertEquals("/path", cookie.getPath());
     87     assertEquals(true, cookie.getSecure());
     88     assertEquals(0, cookie.getVersion());
     89   }
     90 
     91   @Test public void testRfc2109Response() throws Exception {
     92     CookieManager cookieManager = new CookieManager(null, ACCEPT_ORIGINAL_SERVER);
     93     CookieHandler.setDefault(cookieManager);
     94     MockWebServer server = new MockWebServer();
     95     server.start();
     96 
     97     server.enqueue(new MockResponse().addHeader("Set-Cookie: a=android; "
     98         + "Comment=this cookie is delicious; "
     99         + "Domain=" + server.getCookieDomain() + "; "
    100         + "Max-Age=60; "
    101         + "Path=/path; "
    102         + "Secure; "
    103         + "Version=1"));
    104     get(server, "/path/foo");
    105 
    106     List<HttpCookie> cookies = cookieManager.getCookieStore().getCookies();
    107     assertEquals(1, cookies.size());
    108     HttpCookie cookie = cookies.get(0);
    109     assertEquals("a", cookie.getName());
    110     assertEquals("android", cookie.getValue());
    111     assertEquals("this cookie is delicious", cookie.getComment());
    112     assertEquals(null, cookie.getCommentURL());
    113     assertEquals(false, cookie.getDiscard());
    114     assertTrue(server.getCookieDomain().equalsIgnoreCase(cookie.getDomain()));
    115     assertEquals(60, cookie.getMaxAge());
    116     assertEquals("/path", cookie.getPath());
    117     assertEquals(true, cookie.getSecure());
    118     assertEquals(1, cookie.getVersion());
    119   }
    120 
    121   @Test public void testRfc2965Response() throws Exception {
    122     CookieManager cookieManager = new CookieManager(null, ACCEPT_ORIGINAL_SERVER);
    123     CookieHandler.setDefault(cookieManager);
    124     MockWebServer server = new MockWebServer();
    125     server.start();
    126 
    127     server.enqueue(new MockResponse().addHeader("Set-Cookie2: a=android; "
    128         + "Comment=this cookie is delicious; "
    129         + "CommentURL=http://google.com/; "
    130         + "Discard; "
    131         + "Domain=" + server.getCookieDomain() + "; "
    132         + "Max-Age=60; "
    133         + "Path=/path; "
    134         + "Port=\"80,443," + server.getPort() + "\"; "
    135         + "Secure; "
    136         + "Version=1"));
    137     get(server, "/path/foo");
    138 
    139     List<HttpCookie> cookies = cookieManager.getCookieStore().getCookies();
    140     assertEquals(1, cookies.size());
    141     HttpCookie cookie = cookies.get(0);
    142     assertEquals("a", cookie.getName());
    143     assertEquals("android", cookie.getValue());
    144     assertEquals("this cookie is delicious", cookie.getComment());
    145     assertEquals("http://google.com/", cookie.getCommentURL());
    146     assertEquals(true, cookie.getDiscard());
    147     assertTrue(server.getCookieDomain().equalsIgnoreCase(cookie.getDomain()));
    148     assertEquals(60, cookie.getMaxAge());
    149     assertEquals("/path", cookie.getPath());
    150     assertEquals("80,443," + server.getPort(), cookie.getPortlist());
    151     assertEquals(true, cookie.getSecure());
    152     assertEquals(1, cookie.getVersion());
    153   }
    154 
    155   @Test public void testQuotedAttributeValues() throws Exception {
    156     CookieManager cookieManager = new CookieManager(null, ACCEPT_ORIGINAL_SERVER);
    157     CookieHandler.setDefault(cookieManager);
    158     MockWebServer server = new MockWebServer();
    159     server.start();
    160 
    161     server.enqueue(new MockResponse().addHeader("Set-Cookie2: a=\"android\"; "
    162         + "Comment=\"this cookie is delicious\"; "
    163         + "CommentURL=\"http://google.com/\"; "
    164         + "Discard; "
    165         + "Domain=\"" + server.getCookieDomain() + "\"; "
    166         + "Max-Age=\"60\"; "
    167         + "Path=\"/path\"; "
    168         + "Port=\"80,443," + server.getPort() + "\"; "
    169         + "Secure; "
    170         + "Version=\"1\""));
    171     get(server, "/path/foo");
    172 
    173     List<HttpCookie> cookies = cookieManager.getCookieStore().getCookies();
    174     assertEquals(1, cookies.size());
    175     HttpCookie cookie = cookies.get(0);
    176     assertEquals("a", cookie.getName());
    177     assertEquals("android", cookie.getValue());
    178     assertEquals("this cookie is delicious", cookie.getComment());
    179     assertEquals("http://google.com/", cookie.getCommentURL());
    180     assertEquals(true, cookie.getDiscard());
    181     assertTrue(server.getCookieDomain().equalsIgnoreCase(cookie.getDomain()));
    182     assertEquals(60, cookie.getMaxAge());
    183     assertEquals("/path", cookie.getPath());
    184     assertEquals("80,443," + server.getPort(), cookie.getPortlist());
    185     assertEquals(true, cookie.getSecure());
    186     assertEquals(1, cookie.getVersion());
    187   }
    188 
    189   @Test public void testSendingCookiesFromStore() throws Exception {
    190     MockWebServer server = new MockWebServer();
    191     server.enqueue(new MockResponse());
    192     server.start();
    193 
    194     CookieManager cookieManager = new CookieManager(null, ACCEPT_ORIGINAL_SERVER);
    195     HttpCookie cookieA = new HttpCookie("a", "android");
    196     cookieA.setDomain(server.getCookieDomain());
    197     cookieA.setPath("/");
    198     cookieManager.getCookieStore().add(server.url("/").uri(), cookieA);
    199     HttpCookie cookieB = new HttpCookie("b", "banana");
    200     cookieB.setDomain(server.getCookieDomain());
    201     cookieB.setPath("/");
    202     cookieManager.getCookieStore().add(server.url("/").uri(), cookieB);
    203     CookieHandler.setDefault(cookieManager);
    204 
    205     get(server, "/");
    206     RecordedRequest request = server.takeRequest();
    207 
    208     assertEquals("$Version=\"1\"; "
    209             + "a=\"android\";$Path=\"/\";$Domain=\""
    210             + server.getCookieDomain()
    211             + "\"; "
    212             + "b=\"banana\";$Path=\"/\";$Domain=\""
    213             + server.getCookieDomain()
    214             + "\"", request.getHeader("Cookie"));
    215   }
    216 
    217   @Test public void testRedirectsDoNotIncludeTooManyCookies() throws Exception {
    218     MockWebServer redirectTarget = new MockWebServer();
    219     redirectTarget.enqueue(new MockResponse().setBody("A"));
    220     redirectTarget.start();
    221 
    222     MockWebServer redirectSource = new MockWebServer();
    223     redirectSource.enqueue(new MockResponse()
    224         .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
    225         .addHeader("Location: " + redirectTarget.url("/")));
    226     redirectSource.start();
    227 
    228     CookieManager cookieManager = new CookieManager(null, ACCEPT_ORIGINAL_SERVER);
    229     HttpCookie cookie = new HttpCookie("c", "cookie");
    230     cookie.setDomain(redirectSource.getCookieDomain());
    231     cookie.setPath("/");
    232     String portList = Integer.toString(redirectSource.getPort());
    233     cookie.setPortlist(portList);
    234     cookieManager.getCookieStore().add(redirectSource.url("/").uri(), cookie);
    235     CookieHandler.setDefault(cookieManager);
    236 
    237     get(redirectSource, "/");
    238     RecordedRequest request = redirectSource.takeRequest();
    239 
    240     assertEquals("$Version=\"1\"; "
    241             + "c=\"cookie\";$Path=\"/\";$Domain=\""
    242             + redirectSource.getCookieDomain()
    243             + "\";$Port=\""
    244             + portList
    245             + "\"", request.getHeader("Cookie"));
    246 
    247     for (String header : redirectTarget.takeRequest().getHeaders().names()) {
    248       if (header.startsWith("Cookie")) {
    249         fail(header);
    250       }
    251     }
    252   }
    253 
    254   /**
    255    * Test which headers show up where. The cookie manager should be notified
    256    * of both user-specified and derived headers like {@code Host}. Headers
    257    * named {@code Cookie} or {@code Cookie2} that are returned by the cookie
    258    * manager should show up in the request and in {@code
    259    * getRequestProperties}.
    260    */
    261   @Test public void testHeadersSentToCookieHandler() throws IOException, InterruptedException {
    262     final Map<String, List<String>> cookieHandlerHeaders = new HashMap<>();
    263     CookieHandler.setDefault(new CookieManager() {
    264       @Override
    265       public Map<String, List<String>> get(URI uri,
    266           Map<String, List<String>> requestHeaders) throws IOException {
    267         cookieHandlerHeaders.putAll(requestHeaders);
    268         Map<String, List<String>> result = new HashMap<>();
    269         result.put("Cookie", Collections.singletonList("Bar=bar"));
    270         result.put("Cookie2", Collections.singletonList("Baz=baz"));
    271         result.put("Quux", Collections.singletonList("quux"));
    272         return result;
    273       }
    274     });
    275     MockWebServer server = new MockWebServer();
    276     server.enqueue(new MockResponse());
    277     server.start();
    278 
    279     HttpURLConnection connection = new OkUrlFactory(client).open(server.getUrl("/"));
    280     assertEquals(Collections.<String, List<String>>emptyMap(),
    281         connection.getRequestProperties());
    282 
    283     connection.setRequestProperty("Foo", "foo");
    284     connection.setDoOutput(true);
    285     connection.getOutputStream().write(5);
    286     connection.getOutputStream().close();
    287     connection.getInputStream().close();
    288 
    289     RecordedRequest request = server.takeRequest();
    290 
    291     assertContainsAll(cookieHandlerHeaders.keySet(), "Foo");
    292     assertContainsAll(cookieHandlerHeaders.keySet(),
    293         "Content-type", "User-Agent", "Connection", "Host");
    294     assertFalse(cookieHandlerHeaders.containsKey("Cookie"));
    295 
    296     /*
    297      * The API specifies that calling getRequestProperties() on a connected instance should fail
    298      * with an IllegalStateException, but the RI violates the spec and returns a valid map.
    299      * http://www.mail-archive.com/net-dev@openjdk.java.net/msg01768.html
    300      */
    301     try {
    302       assertContainsAll(connection.getRequestProperties().keySet(), "Foo");
    303       assertContainsAll(connection.getRequestProperties().keySet(),
    304           "Content-type", "Content-Length", "User-Agent", "Connection", "Host");
    305       assertContainsAll(connection.getRequestProperties().keySet(), "Cookie", "Cookie2");
    306       assertFalse(connection.getRequestProperties().containsKey("Quux"));
    307     } catch (IllegalStateException expected) {
    308     }
    309 
    310     assertEquals("foo", request.getHeader("Foo"));
    311     assertEquals("Bar=bar", request.getHeader("Cookie"));
    312     assertEquals("Baz=baz", request.getHeader("Cookie2"));
    313     assertNull(request.getHeader("Quux"));
    314   }
    315 
    316   @Test public void testCookiesSentIgnoresCase() throws Exception {
    317     CookieHandler.setDefault(new CookieManager() {
    318       @Override public Map<String, List<String>> get(URI uri,
    319           Map<String, List<String>> requestHeaders) throws IOException {
    320         Map<String, List<String>> result = new HashMap<>();
    321         result.put("COOKIE", Collections.singletonList("Bar=bar"));
    322         result.put("cooKIE2", Collections.singletonList("Baz=baz"));
    323         return result;
    324       }
    325     });
    326     MockWebServer server = new MockWebServer();
    327     server. enqueue(new MockResponse());
    328     server.start();
    329 
    330     get(server, "/");
    331 
    332     RecordedRequest request = server.takeRequest();
    333     assertEquals("Bar=bar", request.getHeader("Cookie"));
    334     assertEquals("Baz=baz", request.getHeader("Cookie2"));
    335     assertNull(request.getHeader("Quux"));
    336   }
    337 
    338   private void assertContains(Collection<String> collection, String element) {
    339     for (String c : collection) {
    340       if (c != null && c.equalsIgnoreCase(element)) {
    341         return;
    342       }
    343     }
    344     fail("No " + element + " in " + collection);
    345   }
    346 
    347   private void assertContainsAll(Collection<String> collection, String... toFind) {
    348     for (String s : toFind) {
    349       assertContains(collection, s);
    350     }
    351   }
    352 
    353   private Map<String,List<String>> get(MockWebServer server, String path) throws Exception {
    354     URLConnection connection = new OkUrlFactory(client).open(server.getUrl(path));
    355     Map<String, List<String>> headers = connection.getHeaderFields();
    356     connection.getInputStream().close();
    357     return headers;
    358   }
    359 
    360 }
    361