Merge remote-tracking branch 'origin/jetty-9.4.x'

This commit is contained in:
Joakim Erdfelt 2017-05-15 10:08:10 -07:00
commit cf589fca83
5 changed files with 425 additions and 54 deletions

View File

@ -99,7 +99,7 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<additionalparam>-Xdoclint:none</additionalparam>
<additionalparam>-Xdoclint:none &#45;&#45;allow-script-in-comments</additionalparam>
</configuration>
<executions>
<execution>

View File

@ -23,7 +23,6 @@ import java.util.Locale;
import javax.servlet.http.Cookie;
import org.eclipse.jetty.http.QuotedCSV;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -115,6 +114,8 @@ public class CookieCutter
while (_fieldList.size()>_fields)
_fieldList.remove(_fields);
StringBuilder unquoted=null;
// For each cookie field
for (String hdr : _fieldList)
{
@ -126,44 +127,46 @@ public class CookieCutter
boolean invalue=false;
boolean quoted=false;
boolean unquotedToken=false;
boolean escaped=false;
int tokenstart=-1;
int tokenend=-1;
for (int i = 0, length = hdr.length(), last=length-1; i < length; i++)
{
char c = hdr.charAt(i);
// Handle quoted values for name or value
if (quoted)
{
if (escaped)
{
escaped=false;
unquoted.append(c);
continue;
}
switch (c)
{
case '"':
tokenend=i;
quoted=false;
if (invalue)
value = hdr.substring(tokenstart+1, tokenend).replace("\\\"","\"");
if (i==last)
{
value = unquoted.toString();
}
else
{
name = hdr.substring(tokenstart+1, tokenend).replace("\\\"","\"");
if (i==last)
value = "";
unquotedToken=true;
tokenstart=i;
tokenend=-1;
}
tokenstart=-1;
tokenend=-1;
break;
case '\\':
escaped=true;
continue;
default:
unquoted.append(c);
continue;
}
}
@ -179,30 +182,42 @@ public class CookieCutter
case '\t':
continue;
case ';':
if (unquotedToken)
{
value = unquoted.toString();
unquoted.setLength(0);
unquotedToken = false;
}
else if(tokenstart>=0 && tokenend>=0)
value = hdr.substring(tokenstart, tokenend+1);
else
value="";
tokenstart = -1;
invalue=false;
break;
case '"':
if (tokenstart<0)
{
quoted=true;
tokenstart=i;
if (unquoted==null)
unquoted=new StringBuilder();
continue;
}
tokenend=i;
if (i==last)
{
value = hdr.substring(tokenstart, tokenend+1);
break;
}
continue;
// fall through to default case
case ';':
if (tokenstart>=0)
value = hdr.substring(tokenstart, tokenend+1);
else
value="";
tokenstart = -1;
invalue=false;
break;
default:
if (unquotedToken)
{
// must have been a bad internal quote. let's fix as best we can
unquoted.append(hdr.substring(tokenstart,i));
quoted = true;
unquotedToken = false;
i--;
continue;
}
if (tokenstart<0)
tokenstart=i;
tokenend=i;
@ -222,39 +237,49 @@ public class CookieCutter
case ' ':
case '\t':
continue;
case '"':
if (tokenstart<0)
{
quoted=true;
tokenstart=i;
}
tokenend=i;
if (i==last)
{
name = hdr.substring(tokenstart, tokenend+1);
value = "";
break;
}
continue;
case ';':
if (tokenstart>=0)
if (unquotedToken)
{
name = unquoted.toString();
unquoted.setLength(0);
unquotedToken = false;
}
else if(tokenstart>=0 && tokenend>=0)
{
name = hdr.substring(tokenstart, tokenend+1);
value = "";
}
value = "";
tokenstart = -1;
break;
case '=':
if (tokenstart>=0)
if (unquotedToken)
{
name = unquoted.toString();
unquoted.setLength(0);
unquotedToken = false;
}
else if(tokenstart>=0 && tokenend>=0)
{
name = hdr.substring(tokenstart, tokenend+1);
}
tokenstart = -1;
invalue=true;
continue;
default:
if (unquotedToken)
{
// must have been a bad internal quote. let's fix as best we can
unquoted.append(hdr.substring(tokenstart,i));
quoted = true;
unquotedToken = false;
i--;
continue;
}
if (tokenstart<0)
tokenstart=i;
tokenend=i;
@ -296,6 +321,13 @@ public class CookieCutter
{
version = Integer.parseInt(value);
}
else
{
cookie = new Cookie(name, value);
if (version > 0)
cookie.setVersion(version);
cookies.add(cookie);
}
}
else
{

View File

@ -0,0 +1,197 @@
//
// ========================================================================
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.server;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import javax.servlet.http.Cookie;
import org.junit.Ignore;
import org.junit.Test;
public class CookieCutterTest
{
private Cookie[] parseCookieHeaders(String... headers)
{
CookieCutter cutter = new CookieCutter();
for (String header : headers)
{
cutter.addCookieField(header);
}
return cutter.getCookies();
}
private void assertCookie(String prefix, Cookie cookie,
String expectedName,
String expectedValue,
int expectedVersion,
String expectedPath)
{
assertThat(prefix + ".name", cookie.getName(), is(expectedName));
assertThat(prefix + ".value", cookie.getValue(), is(expectedValue));
assertThat(prefix + ".version", cookie.getVersion(), is(expectedVersion));
assertThat(prefix + ".path", cookie.getPath(), is(expectedPath));
}
/**
* Example from RFC2109 and RFC2965
*/
@Test
public void testRFC_Single()
{
String rawCookie = "$Version=\"1\"; Customer=\"WILE_E_COYOTE\"; $Path=\"/acme\"";
Cookie cookies[] = parseCookieHeaders(rawCookie);
assertThat("Cookies.length", cookies.length, is(1));
assertCookie("Cookies[0]", cookies[0], "Customer", "WILE_E_COYOTE", 1, "/acme");
}
/**
* Example from RFC2109 and RFC2965
*/
@Test
public void testRFC_Double()
{
String rawCookie = "$Version=\"1\"; " +
"Customer=\"WILE_E_COYOTE\"; $Path=\"/acme\"; " +
"Part_Number=\"Rocket_Launcher_0001\"; $Path=\"/acme\"";
Cookie cookies[] = parseCookieHeaders(rawCookie);
assertThat("Cookies.length", cookies.length, is(2));
assertCookie("Cookies[0]", cookies[0], "Customer", "WILE_E_COYOTE", 1, "/acme");
assertCookie("Cookies[1]", cookies[1], "Part_Number", "Rocket_Launcher_0001", 1, "/acme");
}
/**
* Example from RFC2109 and RFC2965
*/
@Test
public void testRFC_Triple()
{
String rawCookie = "$Version=\"1\"; " +
"Customer=\"WILE_E_COYOTE\"; $Path=\"/acme\"; " +
"Part_Number=\"Rocket_Launcher_0001\"; $Path=\"/acme\"; " +
"Shipping=\"FedEx\"; $Path=\"/acme\"";
Cookie cookies[] = parseCookieHeaders(rawCookie);
assertThat("Cookies.length", cookies.length, is(3));
assertCookie("Cookies[0]", cookies[0], "Customer", "WILE_E_COYOTE", 1, "/acme");
assertCookie("Cookies[1]", cookies[1], "Part_Number", "Rocket_Launcher_0001", 1, "/acme");
assertCookie("Cookies[2]", cookies[2], "Shipping", "FedEx", 1, "/acme");
}
/**
* Example from RFC2109 and RFC2965
*/
@Test
public void testRFC_PathExample()
{
String rawCookie = "$Version=\"1\"; " +
"Part_Number=\"Riding_Rocket_0023\"; $Path=\"/acme/ammo\"; " +
"Part_Number=\"Rocket_Launcher_0001\"; $Path=\"/acme\"";
Cookie cookies[] = parseCookieHeaders(rawCookie);
assertThat("Cookies.length", cookies.length, is(2));
assertCookie("Cookies[0]", cookies[0], "Part_Number", "Riding_Rocket_0023", 1, "/acme/ammo");
assertCookie("Cookies[1]", cookies[1], "Part_Number", "Rocket_Launcher_0001", 1, "/acme");
}
/**
* Example from RFC2109
*/
@Test
public void testRFC2109_CookieSpoofingExample()
{
String rawCookie = "$Version=\"1\"; " +
"session_id=\"1234\"; " +
"session_id=\"1111\"; $Domain=\".cracker.edu\"";
Cookie cookies[] = parseCookieHeaders(rawCookie);
assertThat("Cookies.length", cookies.length, is(2));
assertCookie("Cookies[0]", cookies[0], "session_id", "1234", 1, null);
assertCookie("Cookies[1]", cookies[1], "session_id", "1111", 1, null);
}
/**
* Example from RFC2965
*/
@Test
@Ignore
public void testRFC2965_CookieSpoofingExample()
{
// Ignored because comma separation no longer supported by RFC6265
String rawCookie = "$Version=\"1\"; session_id=\"1234\", " +
"$Version=\"1\"; session_id=\"1111\"; $Domain=\".cracker.edu\"";
Cookie cookies[] = parseCookieHeaders(rawCookie);
assertThat("Cookies.length", cookies.length, is(2));
assertCookie("Cookies[0]", cookies[0], "session_id", "1234", 1, null);
assertCookie("Cookies[1]", cookies[1], "session_id", "1111", 1, null);
}
/**
* Example from RFC6265
*/
@Test
public void testRFC6265_SidExample()
{
String rawCookie = "SID=31d4d96e407aad42";
Cookie cookies[] = parseCookieHeaders(rawCookie);
assertThat("Cookies.length", cookies.length, is(1));
assertCookie("Cookies[0]", cookies[0], "SID", "31d4d96e407aad42", 0, null);
}
/**
* Example from RFC6265
*/
@Test
public void testRFC6265_SidLangExample()
{
String rawCookie = "SID=31d4d96e407aad42; lang=en-US";
Cookie cookies[] = parseCookieHeaders(rawCookie);
assertThat("Cookies.length", cookies.length, is(2));
assertCookie("Cookies[0]", cookies[0], "SID", "31d4d96e407aad42", 0, null);
assertCookie("Cookies[1]", cookies[1], "lang", "en-US", 0, null);
}
/**
* Basic key=value, following RFC6265 rules
*/
@Test
public void testKeyValue()
{
String rawCookie = "key=value";
Cookie cookies[] = parseCookieHeaders(rawCookie);
assertThat("Cookies.length", cookies.length, is(1));
assertCookie("Cookies[0]", cookies[0], "key", "value", 0, null);
}
}

View File

@ -0,0 +1,142 @@
//
// ========================================================================
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.server;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.Cookie;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
/**
* Tests of poor various name=value scenarios and expectations of results
* due to our efforts at being lenient with what we receive.
*/
@RunWith(Parameterized.class)
public class CookieCutter_LenientTest
{
@Parameterized.Parameters(name = "{0}")
public static List<String[]> data()
{
List<String[]> ret = new ArrayList<>();
// Simple test to verify behavior
ret.add(new String[]{"key=value", "key", "value"});
// Tests that conform to RFC2109
// RFC2109 - token values
// token = 1*<any CHAR except CTLs or tspecials>
// CHAR = <any US-ASCII character (octets 0 - 127)>
// CTL = <any US-ASCII control character
// (octets 0 - 31) and DEL (127)>
// SP = <US-ASCII SP, space (32)>
// HT = <US-ASCII HT, horizontal-tab (9)>
// tspecials = "(" | ")" | "<" | ">" | "@"
// | "," | ";" | ":" | "\" | <">
// | "/" | "[" | "]" | "?" | "="
// | "{" | "}" | SP | HT
ret.add(new String[]{"abc=xyz", "abc", "xyz"});
ret.add(new String[]{"abc=!bar", "abc", "!bar"});
ret.add(new String[]{"abc=#hash", "abc", "#hash"});
ret.add(new String[]{"abc=#hash", "abc", "#hash"});
// RFC2109 - quoted-string values
// quoted-string = ( <"> *(qdtext) <"> )
// qdtext = <any TEXT except <">>
// The backslash character ("\") may be used as a single-character quoting
// mechanism only within quoted-string and comment constructs.
// quoted-pair = "\" CHAR
ret.add(new String[]{"abc=\"xyz\"", "abc", "xyz"});
ret.add(new String[]{"abc=\"!bar\"", "abc", "!bar"});
ret.add(new String[]{"abc=\"#hash\"", "abc", "#hash"});
// RFC2109 - other valid name types that conform to 'attr'/'token' syntax
ret.add(new String[]{"!f!o!o!=wat", "!f!o!o!", "wat"});
ret.add(new String[]{"__MyHost=Foo", "__MyHost", "Foo"});
ret.add(new String[]{"some-thing-else=to-parse", "some-thing-else", "to-parse"});
// RFC2109 - names with attr/token syntax starting with '$' (and not a cookie reserved word)
// See https://tools.ietf.org/html/draft-ietf-httpbis-cookie-prefixes-00#section-5.2
// Cannot pass names through as Cookie class does not allow them
ret.add(new String[]{"$foo=bar", null, null});
// Tests that conform to RFC6265
ret.add(new String[]{"abc=foobar!", "abc", "foobar!"});
ret.add(new String[]{"abc=\"foobar!\"", "abc", "foobar!"});
// Internal quotes
ret.add(new String[]{"foo=bar\"baz", "foo", "bar\"baz"});
ret.add(new String[]{"foo=\"bar\"baz\"", "foo", "bar\"baz"});
ret.add(new String[]{"foo=\"bar\"-\"baz\"", "foo", "bar\"-\"baz"});
ret.add(new String[]{"foo=\"bar'-\"baz\"", "foo", "bar'-\"baz"});
ret.add(new String[]{"foo=\"bar''-\"baz\"", "foo", "bar''-\"baz"});
// These seem dubious until you realize the "lots of equals signs" below works
ret.add(new String[]{"foo=\"bar\"=\"baz\"", "foo", "bar\"=\"baz"});
ret.add(new String[]{"query=\"?b=c\"&\"d=e\"", "query", "?b=c\"&\"d=e"});
// Escaped quotes
ret.add(new String[]{"foo=\"bar\\\"=\\\"baz\"", "foo", "bar\"=\"baz"});
// UTF-8 values
ret.add(new String[]{"2sides=\u262F", "2sides", "\u262f"}); // 2 byte
ret.add(new String[]{"currency=\"\u20AC\"", "currency", "\u20AC"}); // 3 byte
ret.add(new String[]{"gothic=\"\uD800\uDF48\"", "gothic", "\uD800\uDF48"}); // 4 byte
// Lots of equals signs
ret.add(new String[]{"query=b=c&d=e", "query", "b=c&d=e"});
// Escaping
ret.add(new String[]{"query=%7B%22sessionCount%22%3A5%2C%22sessionTime%22%3A14151%7D", "query", "%7B%22sessionCount%22%3A5%2C%22sessionTime%22%3A14151%7D"});
// Google cookies (seen in wild, has `tspecials` of ':' in value)
ret.add(new String[]{"GAPS=1:A1aaaAaAA1aaAAAaa1a11a:aAaaAa-aaA1-", "GAPS", "1:A1aaaAaAA1aaAAAaa1a11a:aAaaAa-aaA1-"});
// Strong abuse of cookie spec (lots of tspecials)
ret.add(new String[]{"$Version=0; rToken=F_TOKEN''!--\"</a>=&{()}", "rToken", "F_TOKEN''!--\"</a>=&{()}"});
return ret;
}
@Parameterized.Parameter
public String rawHeader;
@Parameterized.Parameter(1)
public String expectedName;
@Parameterized.Parameter(2)
public String expectedValue;
@Test
public void testLenientBehavior()
{
CookieCutter cutter = new CookieCutter();
cutter.addCookieField(rawHeader);
Cookie[] cookies = cutter.getCookies();
if (expectedName==null)
assertThat("Cookies.length", cookies.length, is(0));
else
{
assertThat("Cookies.length", cookies.length, is(1));
assertThat("Cookie.name", cookies[0].getName(), is(expectedName));
assertThat("Cookie.value", cookies[0].getValue(), is(expectedValue));
}
}
}

View File

@ -44,7 +44,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@ -123,9 +122,8 @@ public class RequestTest
{
try
{
Map<String, String[]> map = null;
// do the parse
map = request.getParameterMap();
request.getParameterMap();
return false;
}
catch(BadMessageException e)
@ -952,7 +950,7 @@ public class RequestTest
_server.setHandler(handler);
_server.start();
String request="GET / HTTP/1.1\r\n"+
String requests="GET / HTTP/1.1\r\n"+
"Host: whatever\r\n"+
"Content-Type: text/plane\r\n"+
"Content-Length: "+10+"\r\n"+
@ -966,7 +964,9 @@ public class RequestTest
"\r\n"+
"ABCDEFGHIJ\r\n";
String responses = _connector.getResponses(request);
LocalEndPoint endp = _connector.executeRequest(requests);
String responses = endp.getResponse() + endp.getResponse();
int index=responses.indexOf("read="+(int)'0');
assertTrue(index>0);
@ -1325,7 +1325,7 @@ public class RequestTest
response=_connector.getResponse(
"POST / HTTP/1.1\r\n"+
"Host: whatever\r\n"+
"Cookie: name0=value0; name1 = value1 ; \"name2\" = \"\\\"value2\\\"\" \n" +
"Cookie: name0=value0; name1 = value1 ; name2 = \"\\\"value2\\\"\" \n" +
"Cookie: $Version=2; name3=value3=value3;$path=/path;$domain=acme.com;$port=8080; name4=; name5 = ; name6\n" +
"Cookie: name7=value7;\n" +
"Connection: close\r\n"+