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