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.mockwebserver.MockResponse;
     21 import com.squareup.okhttp.mockwebserver.MockWebServer;
     22 import com.squareup.okhttp.mockwebserver.RecordedRequest;
     23 
     24 import org.junit.After;
     25 import org.junit.Before;
     26 import org.junit.Test;
     27 
     28 import java.io.IOException;
     29 import java.net.CookieHandler;
     30 import java.net.CookieManager;
     31 import java.net.HttpCookie;
     32 import java.net.HttpURLConnection;
     33 import java.net.URI;
     34 import java.net.URLConnection;
     35 import java.util.Collection;
     36 import java.util.Collections;
     37 import java.util.HashMap;
     38 import java.util.List;
     39 import java.util.Map;
     40 
     41 import static java.net.CookiePolicy.ACCEPT_ORIGINAL_SERVER;
     42 import static org.junit.Assert.assertEquals;
     43 import static org.junit.Assert.assertFalse;
     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.play();
     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     assertEquals(server.getCookieDomain(), 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.play();
     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     assertEquals(server.getCookieDomain(), 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.play();
    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     assertEquals(server.getCookieDomain(), 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.play();
    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     assertEquals(server.getCookieDomain(), 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.play();
    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.getUrl("/").toURI(), cookieA);
    199     HttpCookie cookieB = new HttpCookie("b", "banana");
    200     cookieB.setDomain(server.getCookieDomain());
    201     cookieB.setPath("/");
    202     cookieManager.getCookieStore().add(server.getUrl("/").toURI(), cookieB);
    203     CookieHandler.setDefault(cookieManager);
    204 
    205     get(server, "/");
    206     RecordedRequest request = server.takeRequest();
    207 
    208     List<String> receivedHeaders = request.getHeaders();
    209     assertContains(receivedHeaders, "Cookie: $Version=\"1\"; "
    210         + "a=\"android\";$Path=\"/\";$Domain=\"" + server.getCookieDomain() + "\"; "
    211         + "b=\"banana\";$Path=\"/\";$Domain=\"" + server.getCookieDomain() + "\"");
    212   }
    213 
    214   @Test public void testRedirectsDoNotIncludeTooManyCookies() throws Exception {
    215     MockWebServer redirectTarget = new MockWebServer();
    216     redirectTarget.enqueue(new MockResponse().setBody("A"));
    217     redirectTarget.play();
    218 
    219     MockWebServer redirectSource = new MockWebServer();
    220     redirectSource.enqueue(new MockResponse()
    221         .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
    222         .addHeader("Location: " + redirectTarget.getUrl("/")));
    223     redirectSource.play();
    224 
    225     CookieManager cookieManager = new CookieManager(null, ACCEPT_ORIGINAL_SERVER);
    226     HttpCookie cookie = new HttpCookie("c", "cookie");
    227     cookie.setDomain(redirectSource.getCookieDomain());
    228     cookie.setPath("/");
    229     String portList = Integer.toString(redirectSource.getPort());
    230     cookie.setPortlist(portList);
    231     cookieManager.getCookieStore().add(redirectSource.getUrl("/").toURI(), cookie);
    232     CookieHandler.setDefault(cookieManager);
    233 
    234     get(redirectSource, "/");
    235     RecordedRequest request = redirectSource.takeRequest();
    236 
    237     assertContains(request.getHeaders(), "Cookie: $Version=\"1\"; "
    238         + "c=\"cookie\";$Path=\"/\";$Domain=\"" + redirectSource.getCookieDomain()
    239         + "\";$Port=\"" + portList + "\"");
    240 
    241     for (String header : redirectTarget.takeRequest().getHeaders()) {
    242       if (header.startsWith("Cookie")) {
    243         fail(header);
    244       }
    245     }
    246   }
    247 
    248   /**
    249    * Test which headers show up where. The cookie manager should be notified
    250    * of both user-specified and derived headers like {@code Host}. Headers
    251    * named {@code Cookie} or {@code Cookie2} that are returned by the cookie
    252    * manager should show up in the request and in {@code
    253    * getRequestProperties}.
    254    */
    255   @Test public void testHeadersSentToCookieHandler() throws IOException, InterruptedException {
    256     final Map<String, List<String>> cookieHandlerHeaders = new HashMap<String, List<String>>();
    257     CookieHandler.setDefault(new CookieManager() {
    258       @Override
    259       public Map<String, List<String>> get(URI uri,
    260           Map<String, List<String>> requestHeaders) throws IOException {
    261         cookieHandlerHeaders.putAll(requestHeaders);
    262         Map<String, List<String>> result = new HashMap<String, List<String>>();
    263         result.put("Cookie", Collections.singletonList("Bar=bar"));
    264         result.put("Cookie2", Collections.singletonList("Baz=baz"));
    265         result.put("Quux", Collections.singletonList("quux"));
    266         return result;
    267       }
    268     });
    269     MockWebServer server = new MockWebServer();
    270     server.enqueue(new MockResponse());
    271     server.play();
    272 
    273     HttpURLConnection connection = client.open(server.getUrl("/"));
    274     assertEquals(Collections.<String, List<String>>emptyMap(),
    275         connection.getRequestProperties());
    276 
    277     connection.setRequestProperty("Foo", "foo");
    278     connection.setDoOutput(true);
    279     connection.getOutputStream().write(5);
    280     connection.getOutputStream().close();
    281     connection.getInputStream().close();
    282 
    283     RecordedRequest request = server.takeRequest();
    284 
    285     assertContainsAll(cookieHandlerHeaders.keySet(), "Foo");
    286     assertContainsAll(cookieHandlerHeaders.keySet(),
    287         "Content-type", "User-Agent", "Connection", "Host");
    288     assertFalse(cookieHandlerHeaders.containsKey("Cookie"));
    289 
    290     /*
    291      * The API specifies that calling getRequestProperties() on a connected instance should fail
    292      * with an IllegalStateException, but the RI violates the spec and returns a valid map.
    293      * http://www.mail-archive.com/net-dev@openjdk.java.net/msg01768.html
    294      */
    295     try {
    296       assertContainsAll(connection.getRequestProperties().keySet(), "Foo");
    297       assertContainsAll(connection.getRequestProperties().keySet(),
    298           "Content-type", "Content-Length", "User-Agent", "Connection", "Host");
    299       assertContainsAll(connection.getRequestProperties().keySet(), "Cookie", "Cookie2");
    300       assertFalse(connection.getRequestProperties().containsKey("Quux"));
    301     } catch (IllegalStateException expected) {
    302     }
    303 
    304     assertContainsAll(request.getHeaders(), "Foo: foo", "Cookie: Bar=bar", "Cookie2: Baz=baz");
    305     assertFalse(request.getHeaders().contains("Quux: quux"));
    306   }
    307 
    308   @Test public void testCookiesSentIgnoresCase() throws Exception {
    309     CookieHandler.setDefault(new CookieManager() {
    310       @Override public Map<String, List<String>> get(URI uri,
    311           Map<String, List<String>> requestHeaders) throws IOException {
    312         Map<String, List<String>> result = new HashMap<String, List<String>>();
    313         result.put("COOKIE", Collections.singletonList("Bar=bar"));
    314         result.put("cooKIE2", Collections.singletonList("Baz=baz"));
    315         return result;
    316       }
    317     });
    318     MockWebServer server = new MockWebServer();
    319     server. enqueue(new MockResponse());
    320     server.play();
    321 
    322     get(server, "/");
    323 
    324     RecordedRequest request = server.takeRequest();
    325     assertContainsAll(request.getHeaders(), "COOKIE: Bar=bar", "cooKIE2: Baz=baz");
    326     assertFalse(request.getHeaders().contains("Quux: quux"));
    327   }
    328 
    329   private void assertContains(Collection<String> collection, String element) {
    330     for (String c : collection) {
    331       if (c != null && c.equalsIgnoreCase(element)) {
    332         return;
    333       }
    334     }
    335     fail("No " + element + " in " + collection);
    336   }
    337 
    338   private void assertContainsAll(Collection<String> collection, String... toFind) {
    339     for (String s : toFind) {
    340       assertContains(collection, s);
    341     }
    342   }
    343 
    344   private Map<String,List<String>> get(MockWebServer server, String path) throws Exception {
    345     URLConnection connection = client.open(server.getUrl(path));
    346     Map<String, List<String>> headers = connection.getHeaderFields();
    347     connection.getInputStream().close();
    348     return headers;
    349   }
    350 
    351 }
    352