diff --git a/CookieTest.java b/CookieTest.java index 8f4cf14..d8453e9 100644 --- a/CookieTest.java +++ b/CookieTest.java @@ -1,6 +1,5 @@ package org.json.junit; -import java.io.*; import java.util.*; import static org.junit.Assert.*; @@ -11,33 +10,57 @@ import org.junit.Test; /** * Tests for JSON-Java Cookie.java + * Paraphrased from: + * http://www.nczonline.net/blog/2009/05/05/http-cookies-explained/ + * + * A web server specifies a cookie to be stored by sending an HTTP header + * called Set-Cookie. The format of the Set-Cookie header is a string as + * follows (parts in square brackets are optional): + * Set-Cookie: value[; expires=date][; domain=domain][; path=path][; secure] + * Where value is usually, but not always, a key/value pair: name=value + * Separators between the optional segments (e.g. expires=date) consist of a + * semicolon followed by a space. + * + * Although cookies are typically url-encoded, they don't have to be. + * + * expires date example: + * Set-Cookie: name=Nicholas; expires=Sat, 02 May 2009 23:38:25 GMT + * + * domain option example: + * Set-Cookie: name=Nicholas; domain=nczonline.net + * + * Path option example: + * Set-Cookie: name=Nicholas; path=/blog + * + * Secure option example (it is just a flag): + * Set-Cookie: name=Nicholas; secure + * + * Subcookies. There is a hard limit of size (4k) that can't be finessed. + * But many browsers (not Chrome) have a max cookies per domain limit + * (usually 50). To get around this, subcookies are encoded in the initial + * name/value pair as follows: + * name=a=b&c=d&e=f&g=h */ public class CookieTest { - String realWorldCookie = - "hpc=d=I.aZLE4l.8DeqRynle2fTnKxdAycw3CvCrzMNofhR9a5vYaU.XnHk6n3ZenMs6Xqq"+ - "3Mc5kMw.M1c.vR6zdxVMsfAQn75WNaFp8mY3UQgEw8lvIAbZvT_PiJofv7OMCbabUOe1Efd"+ - "i2M5.aVTX2bHB3EJPhNQNe0B5PL6mGbz7KYYyujkcn6hVS7U5d5OYv7L0GSAiKY-&v=2; y"+ - "wadp115488662=3370273056; AO=u=1&o=1; ywandp=10001806365479:1024785001;"+ - "10001576721379:3531995934; fpc=10001806365479:ZblWsSPj||;10001576721379"+ - ":ZY1jZhRq||; V=v=0.7&m=0&ccOptions={\"show\":false,\"lang\":\"en\",\"fo"+ - "ntSize\":24,\"fontName\":\"Helvetica Neue,Helvetica,Arial,_sans\",\"fon"+ - "tColor\":\"#ffffff\",\"fontOpacity\":1,\"fontEffect\":\"none\",\"bgColo"+ - "r\":\"#000000\",\"bgOpacity\":0.75}; yvap=193@yvap=193@cc=1@al=1@vl=0@r"+ - "vl=0@ac=1@rvl_NFL=0@session_NFL=0@lmsID=@rcc=0; YLS=v=1&p=1&n=1; ucs=tr"+ - "=1424831973913&sfcTs=1425971131&sfc=1; B=26tgei1adfl2v&b=4&d=j7.bbChrYH"+ - "1Ww.22z25N3S2YRsiX.e8VKSZpZdjeYXeN.w--&s=lr; F=a=MVvM8WsMvSxoU9K4FcyMxZ"+ - ".lwmw1yLWpNLOZbMVqjDB8d.bZm1C1JJVJFfCXcy3YfSZy47VAvKKSGZBmM1HQdIUWJA--&"+ - "b=PW8Y; YP=v=AwAAY&d=AEcAMEQCIHHEk.ugtA0iqWk_ctLMBWKG_gJfDzKX.tlKIIGBVH"+ - "cTAiBgmZUHV73V2i80FgqcVjQnvNTyor0rYBXsjhXBul2PzwA-; ypcdb=096e88ca6ff13"+ - "fee954ee414bb7b9362; Y=v=1&n=edbmi9njnt2h1&p=; CRZY={\"33935700511_2015"+ - "0317\":{\"expires\":1426808579870,\"data\":{\"nv\":1,\"bn\":1,\"collaps"+ - "ed\":0}},\"33726925511_20150318\":{\"expires\":1426859124988,\"data\":{"+ - "\"nv\":7,\"bn\":0,\"collapsed\":0}},\"33748770511_20150318\":{\"expires"+ - "\":1426911961098,\"data\":{\"nv\":2,\"bn\":0,\"collapsed\":0}}}; apeaf="+ - "td-applet-stream={\"tmpl\":\"items\",\"po\":{\"2409678.20150318\":{\"c"+ - "\":0,\"v\":2,\"ts\":1426719393315}}}"; - + String simpleCookieStr = + "PH=deleted"+ + "; expires=Wed, 19-Mar-2014 17:53:53 GMT"+ + ";path=/"+ + "; domain=.yahoo.com"+ + ";secure"+ + ";not=included"; + + String encodedCookieStr = + "PH=contains+some+chars"+ + ";expires=Wed, 19-Mar-2014 17:53:53 GMT"+ + "; path=/"+ + ";domain=.yahoo.com?some+escape+chars"+ + "; secure"+ + "; CRZY=%7B%2233748770511_20150319%22%3A%7B%22expires%22%3A142696041"+ + "3419%2C%22data%22%3A%7B%22nv%22%3A3%2C%22bn%22%3A0%2C%22collapsed%2"+ + "2%3A0%7D%7D%7D"; + @Test(expected=NullPointerException.class) public void shouldHandleNullCookie() { String cookieStr = null; @@ -51,16 +74,147 @@ public class CookieTest { } @Test - public void shouldHandleSimpleCookie() { - String cookieStr = "abc=def"; - JSONObject jsonObject = Cookie.toJSONObject(cookieStr); + public void shouldHandleNonEncodedCookie() { + JSONObject jsonObject = Cookie.toJSONObject(simpleCookieStr); Set keySet = jsonObject.keySet(); - assertTrue("Keyset should have exactly 2 keys", keySet.size() == 2); + assertTrue("Keyset should have exactly 7 keys", keySet.size() == 7); assertTrue("name should have expected value", - jsonObject.getString("name").equals("abc")); + "PH".equals(jsonObject.getString("name"))); assertTrue("Value should have expected value", - jsonObject.getString("value").equals("def")); - + "deleted".equals(jsonObject.getString("value"))); + assertTrue("expires should have expected value", + "Wed, 19-Mar-2014 17:53:53 GMT".equals( + jsonObject.getString("expires"))); + assertTrue("domain should have expected value", + ".yahoo.com".equals( + jsonObject.getString("domain"))); + assertTrue("path should have expected value", + "/".equals( + jsonObject.getString("path"))); + assertTrue("not should have expected value", + "included".equals( + jsonObject.getString("not"))); + Boolean secureBool = jsonObject.getBoolean("secure"); + assertTrue("secure should be found in jsonObject", secureBool != null); + assertTrue("secure should have expected value", + secureBool.equals(true)); } + @Test + public void shouldConvertNonEncodedCookieToString() { + int idx; + String expectedStr; + JSONObject jsonObject = Cookie.toJSONObject(simpleCookieStr); + String cookieStr = Cookie.toString(jsonObject); + + // check for unordered expected output + expectedStr = "path=/"; + idx = cookieStr.indexOf(expectedStr); + assertTrue("path should be included in string output", idx != -1); + cookieStr = cookieStr.substring(0, idx)+ + cookieStr.substring(idx+expectedStr.length()); + + expectedStr = "expires=Wed, 19-Mar-2014 17:53:53 GMT"; + idx = cookieStr.indexOf(expectedStr); + assertTrue("expires should be included in string output", idx != -1); + cookieStr = cookieStr.substring(0, idx)+ + cookieStr.substring(idx+expectedStr.length()); + + expectedStr = "domain=.yahoo.com"; + idx = cookieStr.indexOf(expectedStr); + assertTrue("domain should be included in string output", idx != -1); + cookieStr = cookieStr.substring(0, idx)+ + cookieStr.substring(idx+expectedStr.length()); + + expectedStr = "PH=deleted"; + idx = cookieStr.indexOf(expectedStr); + assertTrue("name/value should be included in string output", idx != -1); + cookieStr = cookieStr.substring(0, idx)+ + cookieStr.substring(idx+expectedStr.length()); + + expectedStr = "secure"; + idx = cookieStr.indexOf(expectedStr); + assertTrue("secure should be included in string output", idx != -1); + cookieStr = cookieStr.substring(0, idx)+ + cookieStr.substring(idx+expectedStr.length()); + + // after semicolons, nothing should be left + cookieStr = cookieStr.replaceAll(";", ""); + assertTrue("nothing else should remain in cookie toString()", + cookieStr.length() == 0); + } + + @Test + public void shouldHandleEncodedCookie() { + JSONObject jsonObject = Cookie.toJSONObject(encodedCookieStr); + Set keySet = jsonObject.keySet(); + // Note: the 7th key/value is not used by Cookie.java + assertTrue("Keyset should have exactly 7 keys", keySet.size() == 7); + assertTrue("name should have expected value", + "PH".equals(jsonObject.getString("name"))); + assertTrue("Value should have expected value", + "contains+some+chars".equals(jsonObject.getString("value"))); + assertTrue("expires should have expected value", + "Wed, 19-Mar-2014 17:53:53 GMT".equals( + jsonObject.getString("expires"))); + assertTrue("domain should have expected value", + ".yahoo.com?some escape chars".equals( + jsonObject.getString("domain"))); + assertTrue("path should have expected value", + "/".equals( + jsonObject.getString("path"))); + Boolean secureBool = jsonObject.getBoolean("secure"); + assertTrue("secure should be found in jsonObject", secureBool != null); + assertTrue("secure should have expected value", + secureBool.equals(true)); + String expectedStr = "{\"33748770511_20150319\":{\"expires\":14269604134"+ + "19,\"data\":{\"nv\":3,\"bn\":0,\"collapsed\":0}}}"; + assertTrue("CRZY should have expected value", + expectedStr.equals(jsonObject.getString("CRZY"))); + } + + @Test + public void shouldConvertEncodedCookieToString() { + int idx; + String expectedStr; + JSONObject jsonObject = Cookie.toJSONObject(encodedCookieStr); + String cookieStr = Cookie.toString(jsonObject); + + // check for unordered expected output + expectedStr = "path=/"; + idx = cookieStr.indexOf(expectedStr); + assertTrue("path should be included in string output", idx != -1); + cookieStr = cookieStr.substring(0, idx)+ + cookieStr.substring(idx+expectedStr.length()); + + expectedStr = "expires=Wed, 19-Mar-2014 17:53:53 GMT"; + idx = cookieStr.indexOf(expectedStr); + assertTrue("expires should be included in string output", idx != -1); + cookieStr = cookieStr.substring(0, idx)+ + cookieStr.substring(idx+expectedStr.length()); + + expectedStr = "domain=.yahoo.com?some escape chars"; + idx = cookieStr.indexOf(expectedStr); + assertTrue("domain should be included in string output", idx != -1); + cookieStr = cookieStr.substring(0, idx)+ + cookieStr.substring(idx+expectedStr.length()); + + expectedStr = "PH=contains%2bsome%2bchars"; + idx = cookieStr.indexOf(expectedStr); + assertTrue("name/value should be included in string output", idx != -1); + cookieStr = cookieStr.substring(0, idx)+ + cookieStr.substring(idx+expectedStr.length()); + + expectedStr = "secure"; + idx = cookieStr.indexOf(expectedStr); + assertTrue("secure should be included in string output", idx != -1); + cookieStr = cookieStr.substring(0, idx)+ + cookieStr.substring(idx+expectedStr.length()); + + // after semicolons, nothing should be left + cookieStr = cookieStr.replaceAll(";", ""); + assertTrue("nothing else should remain in cookie toString()", + cookieStr.length() == 0); + } + }