Issue #1402 - Moving RFC asserts to jetty-http Syntax class
This commit is contained in:
parent
5bc7882190
commit
ed9f7ddd4c
|
@ -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.http;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Collection of Syntax validation methods.
|
||||
* <p>
|
||||
* Use in a similar way as you would {@link java.util.Objects#requireNonNull(Object)}
|
||||
* </p>
|
||||
*/
|
||||
public final class Syntax
|
||||
{
|
||||
|
||||
/**
|
||||
* Per RFC2616: Section 2.2, a token follows these syntax rules
|
||||
* <pre>
|
||||
* token = 1*<any CHAR except CTLs or separators>
|
||||
* CHAR = <any US-ASCII character (octets 0 - 127)>
|
||||
* CTL = <any US-ASCII control character
|
||||
* (octets 0 - 31) and DEL (127)>
|
||||
* separators = "(" | ")" | "<" | ">" | "@"
|
||||
* | "," | ";" | ":" | "\" | <">
|
||||
* | "/" | "[" | "]" | "?" | "="
|
||||
* | "{" | "}" | SP | HT
|
||||
* </pre>
|
||||
*
|
||||
* @param value the value to test
|
||||
* @param msg the message to be prefixed if an {@link IllegalArgumentException} is thrown.
|
||||
* @throws IllegalArgumentException if the value is invalid per spec
|
||||
*/
|
||||
public static void requireValidRFC2616Token(String value, String msg)
|
||||
{
|
||||
Objects.requireNonNull(msg, "msg cannot be null");
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int valueLen = value.length();
|
||||
if (valueLen == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < valueLen; i++)
|
||||
{
|
||||
char c = value.charAt(i);
|
||||
|
||||
// 0x00 - 0x1F are low order control characters
|
||||
// 0x7F is the DEL control character
|
||||
if ((c <= 0x1F) || (c == 0x7F))
|
||||
throw new IllegalArgumentException(msg + ": Control characters not allowed in RFC2616 token");
|
||||
if (c == '(' || c == ')' || c == '<' || c == '>' || c == '@'
|
||||
|| c == ',' || c == ';' || c == ':' || c == '\\' || c == '"'
|
||||
|| c == '/' || c == '[' || c == ']' || c == '?' || c == '='
|
||||
|| c == '{' || c == '}' || c == ' ')
|
||||
{
|
||||
throw new IllegalArgumentException(msg + ": RFC2616 token may not contain separator character: [" + c + "]");
|
||||
}
|
||||
if (c >= 0x80)
|
||||
throw new IllegalArgumentException(msg + ": RFC2616 token characters restricted to US-ASCII range: 0x" + Integer.toHexString(c));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Per RFC6265, Cookie.value follows these syntax rules
|
||||
* <pre>
|
||||
* cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE )
|
||||
* cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
|
||||
* ; US-ASCII characters excluding CTLs,
|
||||
* ; whitespace DQUOTE, comma, semicolon,
|
||||
* ; and backslash
|
||||
* </pre>
|
||||
*
|
||||
* @param value the value to test
|
||||
* @throws IllegalArgumentException if the value is invalid per spec
|
||||
*/
|
||||
public static void requireValidRFC6265CookieValue(String value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int valueLen = value.length();
|
||||
if (valueLen == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
if (value.charAt(0) == '"')
|
||||
{
|
||||
// Has starting DQUOTE
|
||||
if (valueLen <= 1 || (value.charAt(valueLen - 1) != '"'))
|
||||
{
|
||||
throw new IllegalArgumentException("RFC6265 Cookie value must have balanced DQUOTES (if used)");
|
||||
}
|
||||
|
||||
// adjust search range to exclude DQUOTES
|
||||
i++;
|
||||
valueLen--;
|
||||
}
|
||||
for (; i < valueLen; i++)
|
||||
{
|
||||
char c = value.charAt(i);
|
||||
|
||||
// 0x00 - 0x1F are low order control characters
|
||||
// 0x7F is the DEL control character
|
||||
if ((c <= 0x1F) || (c == 0x7F))
|
||||
throw new IllegalArgumentException("Control characters not allowed in RFC6265 Cookie value");
|
||||
if ((c == ' ' /* 0x20 */) ||
|
||||
(c == '"' /* 0x2C */) ||
|
||||
(c == ';' /* 0x3B */) ||
|
||||
(c == '\\' /* 0x5C */))
|
||||
{
|
||||
throw new IllegalArgumentException("RFC6265 Cookie value may not contain character: [" + c + "]");
|
||||
}
|
||||
if (c >= 0x80)
|
||||
throw new IllegalArgumentException("RFC6265 Cookie value characters restricted to US-ASCII range: 0x" + Integer.toHexString(c));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.http;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.allOf;
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class SyntaxTest
|
||||
{
|
||||
@Test
|
||||
public void testRequireValidRFC2616Token_Good()
|
||||
{
|
||||
String tokens[] = {
|
||||
"name",
|
||||
"",
|
||||
null,
|
||||
"n.a.m.e",
|
||||
"na-me",
|
||||
"+name",
|
||||
"na*me",
|
||||
"na$me",
|
||||
"#name"
|
||||
};
|
||||
|
||||
for (String token : tokens)
|
||||
{
|
||||
Syntax.requireValidRFC2616Token(token, "Test Based");
|
||||
// No exception should occur here
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequireValidRFC2616Token_Bad()
|
||||
{
|
||||
String tokens[] = {
|
||||
"\"name\"",
|
||||
"name\t",
|
||||
"na me",
|
||||
"name\u0082",
|
||||
"na\tme",
|
||||
"na;me",
|
||||
"{name}",
|
||||
"[name]",
|
||||
"\""
|
||||
};
|
||||
|
||||
for (String token : tokens)
|
||||
{
|
||||
try
|
||||
{
|
||||
Syntax.requireValidRFC2616Token(token, "Test Based");
|
||||
fail("RFC2616 Token [" + token + "] Should have thrown " + IllegalArgumentException.class.getName());
|
||||
}
|
||||
catch (IllegalArgumentException e)
|
||||
{
|
||||
assertThat("Testing Bad RFC2616 Token [" + token + "]", e.getMessage(),
|
||||
allOf(containsString("Test Based"),
|
||||
containsString("RFC2616")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequireValidRFC6265CookieValue_Good()
|
||||
{
|
||||
String values[] = {
|
||||
"value",
|
||||
"",
|
||||
null,
|
||||
"val=ue",
|
||||
"val-ue",
|
||||
"\"value\"",
|
||||
"val/ue",
|
||||
"v.a.l.u.e"
|
||||
};
|
||||
|
||||
for (String value : values)
|
||||
{
|
||||
Syntax.requireValidRFC6265CookieValue(value);
|
||||
// No exception should occur here
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequireValidRFC6265CookieValue_Bad()
|
||||
{
|
||||
String values[] = {
|
||||
"va\tlue",
|
||||
"\t",
|
||||
"value\u0000",
|
||||
"val\u0082ue",
|
||||
"va lue",
|
||||
"va;lue",
|
||||
"\"value",
|
||||
"value\"",
|
||||
"val\\ue",
|
||||
"val\"ue",
|
||||
"\""
|
||||
};
|
||||
|
||||
for (String value : values)
|
||||
{
|
||||
try
|
||||
{
|
||||
Syntax.requireValidRFC6265CookieValue(value);
|
||||
fail("RFC6265 Cookie Value [" + value + "] Should have thrown " + IllegalArgumentException.class.getName());
|
||||
}
|
||||
catch (IllegalArgumentException e)
|
||||
{
|
||||
assertThat("Testing Bad RFC6265 Cookie Value [" + value + "]", e.getMessage(), containsString("RFC6265"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -50,6 +50,7 @@ import org.eclipse.jetty.http.HttpVersion;
|
|||
import org.eclipse.jetty.http.MetaData;
|
||||
import org.eclipse.jetty.http.MimeTypes;
|
||||
import org.eclipse.jetty.http.PreEncodedHttpField;
|
||||
import org.eclipse.jetty.http.Syntax;
|
||||
import org.eclipse.jetty.io.RuntimeIOException;
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
import org.eclipse.jetty.server.handler.ErrorHandler;
|
||||
|
@ -259,9 +260,9 @@ public class Response implements HttpServletResponse
|
|||
|
||||
// Name is checked for legality by servlet spec, but can also be passed directly so check again for quoting
|
||||
// Per RFC6265, Cookie.name follows RFC2616 Section 2.2 token rules
|
||||
assertRFC2616Token("RFC6265 Cookie name", name);
|
||||
Syntax.requireValidRFC2616Token(name, "RFC6265 Cookie name");
|
||||
// Ensure that Per RFC6265, Cookie.value follows syntax rules
|
||||
assertRFC6265CookieValue(value);
|
||||
Syntax.requireValidRFC6265CookieValue(value);
|
||||
|
||||
// Format value and params
|
||||
StringBuilder buf = __cookieBuilder.get();
|
||||
|
@ -305,112 +306,6 @@ public class Response implements HttpServletResponse
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Per RFC6265, Cookie.value follows these syntax rules
|
||||
* <pre>
|
||||
* cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE )
|
||||
* cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
|
||||
* ; US-ASCII characters excluding CTLs,
|
||||
* ; whitespace DQUOTE, comma, semicolon,
|
||||
* ; and backslash
|
||||
* </pre>
|
||||
* @param value the value to test
|
||||
* @throws IllegalArgumentException if the value is invalid per spec
|
||||
*/
|
||||
public static void assertRFC6265CookieValue(String value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int valueLen = value.length();
|
||||
if (valueLen == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
if (value.charAt(0) == '"')
|
||||
{
|
||||
// Has starting DQUOTE
|
||||
if (valueLen <= 1 || (value.charAt(valueLen - 1) != '"'))
|
||||
{
|
||||
throw new IllegalArgumentException("RFC6265 Cookie value must have balanced DQUOTES (if used)");
|
||||
}
|
||||
|
||||
// adjust search range to exclude DQUOTES
|
||||
i++;
|
||||
valueLen--;
|
||||
}
|
||||
for(; i<valueLen; i++)
|
||||
{
|
||||
char c = value.charAt(i);
|
||||
|
||||
// 0x00 - 0x1F are low order control characters
|
||||
// 0x7F is the DEL control character
|
||||
if ((c <= 0x1F) || (c == 0x7F))
|
||||
throw new IllegalArgumentException("Control characters not allowed in RFC6265 Cookie value");
|
||||
if ((c == ' ' /* 0x20 */) ||
|
||||
(c == '"' /* 0x2C */) ||
|
||||
(c == ';' /* 0x3B */) ||
|
||||
(c == '\\' /* 0x5C */))
|
||||
{
|
||||
throw new IllegalArgumentException("RFC6265 Cookie value may not contain character: [" + c + "]");
|
||||
}
|
||||
if (c >= 0x80)
|
||||
throw new IllegalArgumentException("RFC6265 Cookie value characters restricted to US-ASCII range: 0x" + Integer.toHexString(c));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Per RFC2616: Section 2.2, a token follows these syntax rules
|
||||
* <pre>
|
||||
* token = 1*<any CHAR except CTLs or separators>
|
||||
* CHAR = <any US-ASCII character (octets 0 - 127)>
|
||||
* CTL = <any US-ASCII control character
|
||||
* (octets 0 - 31) and DEL (127)>
|
||||
* separators = "(" | ")" | "<" | ">" | "@"
|
||||
* | "," | ";" | ":" | "\" | <">
|
||||
* | "/" | "[" | "]" | "?" | "="
|
||||
* | "{" | "}" | SP | HT
|
||||
* </pre>
|
||||
* @param value the value to test
|
||||
* @throws IllegalArgumentException if the value is invalid per spec
|
||||
*/
|
||||
public static void assertRFC2616Token(String scope, String value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int valueLen = value.length();
|
||||
if (valueLen == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < valueLen; i++)
|
||||
{
|
||||
char c = value.charAt(i);
|
||||
|
||||
// 0x00 - 0x1F are low order control characters
|
||||
// 0x7F is the DEL control character
|
||||
if ((c <= 0x1F) || (c == 0x7F))
|
||||
throw new IllegalArgumentException(scope + ": Control characters not allowed in RFC2616 token");
|
||||
if (c == '(' || c == ')' || c == '<' || c == '>' || c == '@'
|
||||
|| c == ',' || c == ';' || c == ':' || c == '\\' || c == '"'
|
||||
|| c == '/' || c == '[' || c == ']' || c == '?' || c == '='
|
||||
|| c == '{' || c == '}' || c == ' ')
|
||||
{
|
||||
throw new IllegalArgumentException(scope + ": RFC2616 token may not contain separator character: [" + c + "]");
|
||||
}
|
||||
if (c >= 0x80)
|
||||
throw new IllegalArgumentException(scope + ": RFC2616 token characters restricted to US-ASCII range: 0x" + Integer.toHexString(c));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a set cookie value
|
||||
*
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
package org.eclipse.jetty.server;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.hamcrest.CoreMatchers.allOf;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
@ -30,19 +31,18 @@ import static org.junit.Assert.assertNotNull;
|
|||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.LineNumberReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.HttpCookie;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.HttpCookie;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import java.net.URLEncoder;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
@ -57,7 +57,6 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.eclipse.jetty.http.HttpCompliance;
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
|
@ -70,8 +69,8 @@ import org.eclipse.jetty.io.RuntimeIOException;
|
|||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
import org.eclipse.jetty.server.handler.ErrorHandler;
|
||||
import org.eclipse.jetty.server.session.DefaultSessionIdManager;
|
||||
import org.eclipse.jetty.server.session.DefaultSessionCache;
|
||||
import org.eclipse.jetty.server.session.DefaultSessionIdManager;
|
||||
import org.eclipse.jetty.server.session.NullSessionDataStore;
|
||||
import org.eclipse.jetty.server.session.Session;
|
||||
import org.eclipse.jetty.server.session.SessionData;
|
||||
|
@ -79,7 +78,6 @@ import org.eclipse.jetty.server.session.SessionHandler;
|
|||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.thread.Scheduler;
|
||||
import org.eclipse.jetty.util.thread.TimerScheduler;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
|
@ -180,6 +178,7 @@ public class ResponseTest
|
|||
_server.join();
|
||||
}
|
||||
|
||||
@SuppressWarnings("InjectedReferences") // to allow for invalid encoding strings in this testcase
|
||||
@Test
|
||||
public void testContentType() throws Exception
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue