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 17package com.squareup.okhttp.internal.http; 18 19import com.squareup.okhttp.OkHttpClient; 20import com.squareup.okhttp.OkUrlFactory; 21import com.squareup.okhttp.mockwebserver.MockResponse; 22import com.squareup.okhttp.mockwebserver.MockWebServer; 23import com.squareup.okhttp.mockwebserver.RecordedRequest; 24import java.io.IOException; 25import java.net.CookieHandler; 26import java.net.CookieManager; 27import java.net.HttpCookie; 28import java.net.HttpURLConnection; 29import java.net.URI; 30import java.net.URLConnection; 31import java.util.Collection; 32import java.util.Collections; 33import java.util.HashMap; 34import java.util.List; 35import java.util.Map; 36import org.junit.After; 37import org.junit.Before; 38import org.junit.Test; 39 40import static java.net.CookiePolicy.ACCEPT_ORIGINAL_SERVER; 41import static org.junit.Assert.assertEquals; 42import static org.junit.Assert.assertFalse; 43import static org.junit.Assert.assertNull; 44import static org.junit.Assert.assertTrue; 45import static org.junit.Assert.fail; 46 47/** Android's CookiesTest. */ 48public 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.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 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.getUrl("/"))); 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.getUrl("/").toURI(), 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