Merge branch 'master' into release-9
This commit is contained in:
commit
e6f1e8a52a
70
VERSION.txt
70
VERSION.txt
|
@ -108,7 +108,6 @@ jetty-9.0.4.v20130625 - 25 June 2013
|
|||
+ 410405 Avoid NPE for requestDispatcher(../)
|
||||
+ 410469 UpgradeRequest is sent twice when using SSL, one fails warning about
|
||||
WritePendingException
|
||||
+ 410498 ignore type of exception in GoAwayTest.testDataNotProcessedAfterGoAway
|
||||
+ 410522 jetty start broken for command line options
|
||||
+ 410537 Exceptions during @OnWebSocketConnect not reported to
|
||||
@OnWebSocketError
|
||||
|
@ -301,6 +300,75 @@ jetty-9.0.0.v20130308 - 08 March 2013
|
|||
+ 402757 WebSocket client module can't be used with WebSocket server module in
|
||||
the same WAR
|
||||
|
||||
jetty-8.1.12.v20130726 - 26 July 2013
|
||||
+ 396706 CGI support parameters
|
||||
+ 397193 MongoSessionManager refresh updates last access time
|
||||
+ 407342 ReloadedSessionMissingClassTest uses class compiled with jdk7
|
||||
+ 408529 Etags set in 304 response
|
||||
+ 408600 set correct jetty.url in all pom files
|
||||
+ 408642 setContentType from addHeader
|
||||
+ 408662 In pax-web servlet services requests even if init() has not finished
|
||||
running
|
||||
+ 408806 getParameter returns null on Multipart request if called before
|
||||
request.getPart()/getParts()
|
||||
+ 408909 GzipFilter setting of headers when reset and/or not compressed
|
||||
+ 409028 Jetty HttpClient does not work with proxy CONNECT method.
|
||||
+ 409133 Empty <welcome-file> causes StackOverflowError
|
||||
+ 409436 NPE on context restart using dynamic servlet registration
|
||||
+ 409449 Ensure servlets, filters and listeners added via dynamic
|
||||
registration, annotations or descriptors are cleaned on context restarts
|
||||
+ 409556 FileInputStream not closed in DirectNIOBuffer
|
||||
+ 410405 Avoid NPE for requestDispatcher(../)
|
||||
+ 410630 MongoSessionManager conflicting session update op
|
||||
+ 410750 NoSQLSessions: implement session context data persistence across
|
||||
server restarts
|
||||
+ 410893 async support defaults to false for spec created servlets and filters
|
||||
+ 411135 HttpClient may send proxied https requests to the proxy instead of
|
||||
the target server.
|
||||
+ 411216 RequestLogHandler handles async completion
|
||||
+ 411458 MultiPartFilter getParameterMap doesn't preserve multivalued
|
||||
parameters 411459 MultiPartFilter.Wrapper getParameter should use charset
|
||||
encoding of part
|
||||
+ 411755 MultiPartInputStreamParser fails on base64 encoded content
|
||||
+ 411909 GzipFilter flushbuffer() results in erroneous finish() call
|
||||
+ 412712 HttpClient does not send the terminal chunk after partial writes.
|
||||
+ 412750 HttpClient close expired connections fix
|
||||
+ 413371 Default JSON.Converters for List and Set.
|
||||
+ 413372 JSON Enum uses name rather than toString()
|
||||
+ 413684 Trailing slash shows JSP source
|
||||
+ 413812 Make RateTracker serializable
|
||||
|
||||
jetty-7.6.12.v20130726 - 26 July 2013
|
||||
+ 396706 CGI support parameters
|
||||
+ 397193 MongoSessionManager refresh updates last access time
|
||||
+ 407342 ReloadedSessionMissingClassTest uses class compiled with jdk7
|
||||
+ 408529 Etags set in 304 response
|
||||
+ 408600 set correct jetty.url in all pom files
|
||||
+ 408642 setContentType from addHeader
|
||||
+ 408662 In pax-web servlet services requests even if init() has not finished
|
||||
running
|
||||
+ 408909 GzipFilter setting of headers when reset and/or not compressed
|
||||
+ 409028 Jetty HttpClient does not work with proxy CONNECT method.
|
||||
+ 409133 Empty <welcome-file> causes StackOverflowError
|
||||
+ 409556 FileInputStream not closed in DirectNIOBuffer
|
||||
+ 410630 MongoSessionManager conflicting session update op
|
||||
+ 410750 NoSQLSessions: implement session context data persistence across
|
||||
server restarts
|
||||
+ 411135 HttpClient may send proxied https requests to the proxy instead of
|
||||
the target server.
|
||||
+ 411216 RequestLogHandler handles async completion
|
||||
+ 411458 MultiPartFilter getParameterMap doesn't preserve multivalued
|
||||
parameters 411459 MultiPartFilter.Wrapper getParameter should use charset
|
||||
encoding of part
|
||||
+ 411755 MultiPartInputStreamParser fails on base64 encoded content
|
||||
+ 411909 GzipFilter flushbuffer() results in erroneous finish() call
|
||||
+ 412712 HttpClient does not send the terminal chunk after partial writes.
|
||||
+ 412750 HttpClient close expired connections fix
|
||||
+ 413371 Default JSON.Converters for List and Set.
|
||||
+ 413372 JSON Enum uses name rather than toString()
|
||||
+ 413684 Trailing slash shows JSP source
|
||||
+ 413812 Make RateTracker serializable
|
||||
|
||||
jetty-8.1.11.v20130520 - 20 May 2013
|
||||
+ 402844 STOP.PORT & STOP.KEY behaviour has changed
|
||||
+ 403281 jetty.sh waits for started or failure before returning
|
||||
|
|
|
@ -23,8 +23,6 @@ import org.eclipse.jetty.server.Server;
|
|||
import org.eclipse.jetty.server.handler.DefaultHandler;
|
||||
import org.eclipse.jetty.server.handler.HandlerList;
|
||||
import org.eclipse.jetty.server.handler.ResourceHandler;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Simple Jetty FileServer.
|
||||
|
|
|
@ -55,8 +55,7 @@ public class SpdyServer
|
|||
System.setProperty("jetty.home",jetty_home);
|
||||
|
||||
// Setup Threadpool
|
||||
QueuedThreadPool threadPool = new QueuedThreadPool();
|
||||
threadPool.setMaxThreads(500);
|
||||
QueuedThreadPool threadPool = new QueuedThreadPool(512);
|
||||
|
||||
Server server = new Server(threadPool);
|
||||
server.manage(threadPool);
|
||||
|
|
|
@ -71,6 +71,11 @@
|
|||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.toolchain</groupId>
|
||||
<artifactId>jetty-test-helper</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.toolchain</groupId>
|
||||
<artifactId>jetty-test-helper</artifactId>
|
||||
|
|
|
@ -753,7 +753,8 @@ public class AnnotationParser
|
|||
public void parseDir (Resource dir, ClassNameResolver resolver)
|
||||
throws Exception
|
||||
{
|
||||
if (!dir.isDirectory() || !dir.exists())
|
||||
//skip dirs whose name start with . (ie hidden)
|
||||
if (!dir.isDirectory() || !dir.exists() || dir.getName().startsWith("."))
|
||||
return;
|
||||
|
||||
if (LOG.isDebugEnabled()) {LOG.debug("Scanning dir {}", dir);};
|
||||
|
@ -766,16 +767,21 @@ public class AnnotationParser
|
|||
Resource res = dir.addPath(files[f]);
|
||||
if (res.isDirectory())
|
||||
parseDir(res, resolver);
|
||||
String name = res.getName();
|
||||
if (name.endsWith(".class"))
|
||||
else
|
||||
{
|
||||
if ((resolver == null)|| (!resolver.isExcluded(name) && (!isParsed(name) || resolver.shouldOverride(name))))
|
||||
//we've already verified the directories, so just verify the class file name
|
||||
String filename = res.getFile().getName();
|
||||
if (isValidClassFileName(filename))
|
||||
{
|
||||
Resource r = Resource.newResource(res.getURL());
|
||||
if (LOG.isDebugEnabled()) {LOG.debug("Scanning class {}", r);};
|
||||
scanClass(r.getInputStream());
|
||||
}
|
||||
String name = res.getName();
|
||||
if ((resolver == null)|| (!resolver.isExcluded(name) && (!isParsed(name) || resolver.shouldOverride(name))))
|
||||
{
|
||||
Resource r = Resource.newResource(res.getURL());
|
||||
if (LOG.isDebugEnabled()) {LOG.debug("Scanning class {}", r);};
|
||||
scanClass(r.getInputStream());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -812,23 +818,11 @@ public class AnnotationParser
|
|||
{
|
||||
try
|
||||
{
|
||||
String name = entry.getName();
|
||||
if (name.toLowerCase(Locale.ENGLISH).endsWith(".class"))
|
||||
{
|
||||
String shortName = name.replace('/', '.').substring(0,name.length()-6);
|
||||
if ((resolver == null)
|
||||
||
|
||||
(!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName))))
|
||||
{
|
||||
|
||||
Resource clazz = Resource.newResource("jar:"+jarUri+"!/"+name);
|
||||
scanClass(clazz.getInputStream());
|
||||
}
|
||||
}
|
||||
parseJarEntry(jarUri, entry, resolver);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn("Problem processing jar entry "+entry, e);
|
||||
LOG.warn("Problem parsing jar entry: {}", entry.getName());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -896,6 +890,8 @@ public class AnnotationParser
|
|||
scanClass(r.getInputStream());
|
||||
return;
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled()) LOG.warn("Resource not scannable for classes: {}", uri);
|
||||
}
|
||||
|
||||
|
||||
|
@ -929,29 +925,8 @@ public class AnnotationParser
|
|||
{
|
||||
JarEntry entry = jar_in.getNextJarEntry();
|
||||
while (entry!=null)
|
||||
{
|
||||
try
|
||||
{
|
||||
String name = entry.getName();
|
||||
if (name.toLowerCase(Locale.ENGLISH).endsWith(".class"))
|
||||
{
|
||||
String shortName = name.replace('/', '.').substring(0,name.length()-6);
|
||||
|
||||
if ((resolver == null)
|
||||
||
|
||||
(!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName))))
|
||||
{
|
||||
Resource clazz = Resource.newResource("jar:"+uri+"!/"+name);
|
||||
if (LOG.isDebugEnabled()) {LOG.debug("Scanning class from jar {}", clazz);};
|
||||
scanClass(clazz.getInputStream());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn("Problem processing jar entry "+entry, e);
|
||||
}
|
||||
|
||||
{
|
||||
parseJarEntry(uri, entry, resolver);
|
||||
entry = jar_in.getNextJarEntry();
|
||||
}
|
||||
}
|
||||
|
@ -961,8 +936,44 @@ public class AnnotationParser
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a single entry in a jar file
|
||||
* @param jar
|
||||
* @param entry
|
||||
* @param resolver
|
||||
* @throws Exception
|
||||
*/
|
||||
protected void parseJarEntry (URI jar, JarEntry entry, final ClassNameResolver resolver)
|
||||
throws Exception
|
||||
{
|
||||
if (jar == null || entry == null)
|
||||
return;
|
||||
|
||||
//skip directories
|
||||
if (entry.isDirectory())
|
||||
return;
|
||||
|
||||
String name = entry.getName();
|
||||
|
||||
//check file is a valid class file name
|
||||
if (isValidClassFileName(name) && isValidClassFilePath(name))
|
||||
{
|
||||
String shortName = name.replace('/', '.').substring(0,name.length()-6);
|
||||
|
||||
if ((resolver == null)
|
||||
||
|
||||
(!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName))))
|
||||
{
|
||||
Resource clazz = Resource.newResource("jar:"+jar+"!/"+name);
|
||||
if (LOG.isDebugEnabled()) {LOG.debug("Scanning class from jar {}", clazz);};
|
||||
scanClass(clazz.getInputStream());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Use ASM on a class
|
||||
*
|
||||
|
@ -975,5 +986,66 @@ public class AnnotationParser
|
|||
ClassReader reader = new ClassReader(is);
|
||||
reader.accept(new MyClassVisitor(), ClassReader.SKIP_CODE|ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the given path represents a valid class file name.
|
||||
* The check is fairly cursory, checking that:
|
||||
* <ul>
|
||||
* <li> the name ends with .class</li>
|
||||
* <li> it isn't a dot file or in a hidden directory </li>
|
||||
* <li> the name of the class at least begins with a valid identifier for a class name </li>
|
||||
* </ul>
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
private boolean isValidClassFileName (String name)
|
||||
{
|
||||
//no name cannot be valid
|
||||
if (name == null || name.length()==0)
|
||||
return false;
|
||||
|
||||
//skip anything that is not a class file
|
||||
if (!name.toLowerCase(Locale.ENGLISH).endsWith(".class"))
|
||||
{
|
||||
if (LOG.isDebugEnabled()) LOG.debug("Not a class: {}",name);
|
||||
return false;
|
||||
}
|
||||
|
||||
//skip any classfiles that are not a valid java identifier
|
||||
int c0 = 0;
|
||||
int ldir = name.lastIndexOf('/', name.length()-6);
|
||||
c0 = (ldir > -1 ? ldir+1 : c0);
|
||||
if (!Character.isJavaIdentifierStart(name.charAt(c0)))
|
||||
{
|
||||
if (LOG.isDebugEnabled()) LOG.debug("Not a java identifier: {}"+name);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check that the given path does not contain hidden directories
|
||||
*
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
private boolean isValidClassFilePath (String path)
|
||||
{
|
||||
//no path is not valid
|
||||
if (path == null || path.length()==0)
|
||||
return false;
|
||||
|
||||
|
||||
//skip any classfiles that are in a hidden directory
|
||||
if (path.startsWith(".") || path.contains("/."))
|
||||
{
|
||||
if (LOG.isDebugEnabled()) LOG.debug("Contains hidden dirs: {}"+path);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,71 +18,109 @@
|
|||
|
||||
package org.eclipse.jetty.annotations;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler;
|
||||
import org.eclipse.jetty.annotations.AnnotationParser.Value;
|
||||
import org.eclipse.jetty.toolchain.test.FS;
|
||||
import org.eclipse.jetty.toolchain.test.IO;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.toolchain.test.TestingDir;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestAnnotationParser
|
||||
{
|
||||
public static class TrackingAnnotationHandler implements DiscoverableAnnotationHandler
|
||||
{
|
||||
private final String annotationName;
|
||||
public final Set<String> foundClasses;
|
||||
|
||||
public TrackingAnnotationHandler(String annotationName)
|
||||
{
|
||||
this.annotationName = annotationName;
|
||||
this.foundClasses = new HashSet<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation,
|
||||
List<Value> values)
|
||||
{
|
||||
foundClasses.add(className);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMethod(String className, String methodName, int access, String desc, String signature, String[] exceptions, String annotation,
|
||||
List<Value> values)
|
||||
{
|
||||
/* ignore */
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation,
|
||||
List<Value> values)
|
||||
{
|
||||
/* ignore */
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAnnotationName()
|
||||
{
|
||||
return this.annotationName;
|
||||
}
|
||||
}
|
||||
|
||||
@Rule
|
||||
public TestingDir testdir = new TestingDir();
|
||||
|
||||
@Test
|
||||
public void testSampleAnnotation() throws Exception
|
||||
{
|
||||
String[] classNames = new String[]{"org.eclipse.jetty.annotations.ClassA"};
|
||||
String[] classNames = new String[]
|
||||
{ "org.eclipse.jetty.annotations.ClassA" };
|
||||
AnnotationParser parser = new AnnotationParser();
|
||||
|
||||
class SampleAnnotationHandler implements DiscoverableAnnotationHandler
|
||||
{
|
||||
private List<String> methods = Arrays.asList("a", "b", "c", "d", "l");
|
||||
private List<String> methods = Arrays.asList("a","b","c","d","l");
|
||||
|
||||
|
||||
|
||||
|
||||
public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation,
|
||||
List<Value> values)
|
||||
List<Value> values)
|
||||
{
|
||||
assertEquals ("org.eclipse.jetty.annotations.ClassA", className);
|
||||
assertEquals("org.eclipse.jetty.annotations.ClassA",className);
|
||||
}
|
||||
|
||||
public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation,
|
||||
List<Value> values)
|
||||
List<Value> values)
|
||||
{
|
||||
assertEquals ("m", fieldName);
|
||||
assertEquals (org.objectweb.asm.Type.OBJECT, org.objectweb.asm.Type.getType(fieldType).getSort());
|
||||
assertEquals (1, values.size());
|
||||
Value anv1 = values.get(0);
|
||||
assertEquals ("value", anv1.getName());
|
||||
assertEquals (7, anv1.getValue());
|
||||
assertEquals("m",fieldName);
|
||||
assertEquals(org.objectweb.asm.Type.OBJECT,org.objectweb.asm.Type.getType(fieldType).getSort());
|
||||
assertEquals(1,values.size());
|
||||
Value anv1 = values.get(0);
|
||||
assertEquals("value",anv1.getName());
|
||||
assertEquals(7,anv1.getValue());
|
||||
|
||||
}
|
||||
|
||||
public void handleMethod(String className, String methodName, int access, String desc, String signature, String[] exceptions, String annotation,
|
||||
List<Value> values)
|
||||
List<Value> values)
|
||||
{
|
||||
System.err.println("Sample annotated method : classname="+className+" methodName="+methodName+" access="+access+" desc="+desc+" signature="+signature);
|
||||
|
||||
org.objectweb.asm.Type retType = org.objectweb.asm.Type.getReturnType(desc);
|
||||
System.err.println("REturn type = "+retType);
|
||||
org.objectweb.asm.Type[] params = org.objectweb.asm.Type.getArgumentTypes(desc);
|
||||
if (params == null)
|
||||
System.err.println("No params");
|
||||
else
|
||||
System.err.println(params.length+" params");
|
||||
|
||||
if (exceptions == null)
|
||||
System.err.println("No exceptions");
|
||||
else
|
||||
System.err.println(exceptions.length+" exceptions");
|
||||
|
||||
assertEquals("org.eclipse.jetty.annotations.ClassA", className);
|
||||
assertEquals("org.eclipse.jetty.annotations.ClassA",className);
|
||||
assertTrue(methods.contains(methodName));
|
||||
assertEquals("org.eclipse.jetty.annotations.Sample", annotation);
|
||||
assertEquals("org.eclipse.jetty.annotations.Sample",annotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -95,7 +133,7 @@ public class TestAnnotationParser
|
|||
parser.registerHandler(new SampleAnnotationHandler());
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
parser.parse(classNames, new ClassNameResolver ()
|
||||
parser.parse(classNames,new ClassNameResolver()
|
||||
{
|
||||
public boolean isExcluded(String name)
|
||||
{
|
||||
|
@ -110,44 +148,36 @@ public class TestAnnotationParser
|
|||
});
|
||||
long end = System.currentTimeMillis();
|
||||
|
||||
System.err.println("Time to parse class: "+((end-start)));
|
||||
//System.err.println("Time to parse class: " + ((end - start)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiAnnotation() throws Exception
|
||||
{
|
||||
String[] classNames = new String[]{"org.eclipse.jetty.annotations.ClassB"};
|
||||
String[] classNames = new String[]
|
||||
{ "org.eclipse.jetty.annotations.ClassB" };
|
||||
AnnotationParser parser = new AnnotationParser();
|
||||
|
||||
class MultiAnnotationHandler implements DiscoverableAnnotationHandler
|
||||
{
|
||||
public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation,
|
||||
List<Value> values)
|
||||
List<Value> values)
|
||||
{
|
||||
assertTrue("org.eclipse.jetty.annotations.ClassB".equals(className));
|
||||
|
||||
for (Value anv: values)
|
||||
{
|
||||
System.err.println(anv.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation,
|
||||
List<Value> values)
|
||||
List<Value> values)
|
||||
{
|
||||
//there should not be any
|
||||
// there should not be any
|
||||
fail();
|
||||
}
|
||||
|
||||
public void handleMethod(String className, String methodName, int access, String params, String signature, String[] exceptions, String annotation,
|
||||
List<Value> values)
|
||||
List<Value> values)
|
||||
{
|
||||
assertTrue("org.eclipse.jetty.annotations.ClassB".equals(className));
|
||||
assertTrue("a".equals(methodName));
|
||||
for (Value anv: values)
|
||||
{
|
||||
System.err.println(anv.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -155,11 +185,71 @@ public class TestAnnotationParser
|
|||
{
|
||||
return "org.eclipse.jetty.annotations.Multi";
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
parser.registerHandler(new MultiAnnotationHandler());
|
||||
parser.parse(classNames, null);
|
||||
parser.parse(classNames,null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHiddenFilesInJar() throws Exception
|
||||
{
|
||||
File badClassesJar = MavenTestingUtils.getTestResourceFile("bad-classes.jar");
|
||||
AnnotationParser parser = new AnnotationParser();
|
||||
parser.parse(badClassesJar.toURI(),null);
|
||||
// only the valid classes inside bad-classes.jar should be parsed. If any invalid classes are parsed and exception would be thrown here
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasedirExclusion() throws Exception
|
||||
{
|
||||
// Build up basedir, which itself has a path segment that violates java package and classnaming.
|
||||
// The basedir should have no effect on annotation scanning.
|
||||
// Intentionally using a base director name that starts with a "."
|
||||
// This mimics what you see in jenkins, hudson, hadoop, solr, camel, and selenium for their
|
||||
// installed and/or managed webapps
|
||||
File basedir = testdir.getFile(".base/workspace/classes");
|
||||
FS.ensureEmpty(basedir);
|
||||
|
||||
// Copy in class that is known to have annotations.
|
||||
copyClass(ClassA.class,basedir);
|
||||
|
||||
// Setup Tracker
|
||||
TrackingAnnotationHandler tracker = new TrackingAnnotationHandler(Sample.class.getName());
|
||||
|
||||
// Setup annotation scanning
|
||||
AnnotationParser parser = new AnnotationParser();
|
||||
parser.registerHandler(tracker);
|
||||
|
||||
// Parse
|
||||
parser.parse(basedir.toURI(),null);
|
||||
|
||||
// Validate
|
||||
Assert.assertThat("Found Class", tracker.foundClasses, contains(ClassA.class.getName()));
|
||||
}
|
||||
|
||||
private void copyClass(Class<?> clazz, File basedir) throws IOException
|
||||
{
|
||||
String classname = clazz.getName().replace('.',File.separatorChar) + ".class";
|
||||
URL url = this.getClass().getResource('/'+classname);
|
||||
Assert.assertThat("URL for: " + classname,url,notNullValue());
|
||||
|
||||
String classpath = classname.substring(0,classname.lastIndexOf(File.separatorChar));
|
||||
FS.ensureDirExists(new File(basedir,classpath));
|
||||
|
||||
InputStream in = null;
|
||||
OutputStream out = null;
|
||||
try
|
||||
{
|
||||
in = url.openStream();
|
||||
out = new FileOutputStream(new File(basedir,classname));
|
||||
IO.copy(in,out);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IO.close(out);
|
||||
IO.close(in);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
|
@ -176,11 +176,7 @@ public class HttpClient extends ContainerLifeCycle
|
|||
protected void doStart() throws Exception
|
||||
{
|
||||
if (sslContextFactory != null)
|
||||
{
|
||||
addBean(sslContextFactory);
|
||||
// Avoid to double dispatch when using SSL
|
||||
setDispatchIO(false);
|
||||
}
|
||||
|
||||
String name = HttpClient.class.getSimpleName() + "@" + hashCode();
|
||||
|
||||
|
@ -391,7 +387,7 @@ public class HttpClient extends ContainerLifeCycle
|
|||
protected Request copyRequest(Request oldRequest, URI newURI)
|
||||
{
|
||||
Request newRequest = new HttpRequest(this, oldRequest.getConversationID(), newURI);
|
||||
newRequest.method(oldRequest.getMethod())
|
||||
newRequest.method(oldRequest.method())
|
||||
.version(oldRequest.getVersion())
|
||||
.content(oldRequest.getContent());
|
||||
for (HttpField header : oldRequest.getHeaders())
|
||||
|
|
|
@ -53,7 +53,7 @@ public class HttpConnection extends AbstractConnection implements Connection
|
|||
private final HttpSender sender;
|
||||
private final HttpReceiver receiver;
|
||||
private long idleTimeout;
|
||||
private volatile boolean closed;
|
||||
private boolean closed;
|
||||
|
||||
public HttpConnection(HttpClient client, EndPoint endPoint, HttpDestination destination)
|
||||
{
|
||||
|
@ -88,15 +88,9 @@ public class HttpConnection extends AbstractConnection implements Connection
|
|||
super.onClose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillInterested()
|
||||
protected boolean isClosed()
|
||||
{
|
||||
// This is necessary when "upgrading" the connection for example after proxied
|
||||
// CONNECT requests, because the old connection will read the CONNECT response
|
||||
// and then set the read interest, while the new connection attached to the same
|
||||
// EndPoint also will set the read interest, causing a ReadPendingException.
|
||||
if (!closed)
|
||||
super.fillInterested();
|
||||
return closed;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -154,7 +148,7 @@ public class HttpConnection extends AbstractConnection implements Connection
|
|||
|
||||
private void normalizeRequest(Request request)
|
||||
{
|
||||
if (request.getMethod() == null)
|
||||
if (request.method() == null)
|
||||
request.method(HttpMethod.GET);
|
||||
|
||||
if (request.getVersion() == null)
|
||||
|
@ -163,7 +157,7 @@ public class HttpConnection extends AbstractConnection implements Connection
|
|||
if (request.getIdleTimeout() <= 0)
|
||||
request.idleTimeout(client.getIdleTimeout(), TimeUnit.MILLISECONDS);
|
||||
|
||||
HttpMethod method = request.getMethod();
|
||||
String method = request.method();
|
||||
HttpVersion version = request.getVersion();
|
||||
HttpFields headers = request.getHeaders();
|
||||
ContentProvider content = request.getContent();
|
||||
|
@ -178,7 +172,7 @@ public class HttpConnection extends AbstractConnection implements Connection
|
|||
path = "/";
|
||||
request.path(path);
|
||||
}
|
||||
if (destination.isProxied() && HttpMethod.CONNECT != method)
|
||||
if (destination.isProxied() && !HttpMethod.CONNECT.is(method))
|
||||
{
|
||||
path = request.getURI().toString();
|
||||
request.path(path);
|
||||
|
|
|
@ -428,6 +428,19 @@ public class HttpDestination implements Destination, Closeable, Dumpable
|
|||
getResponseNotifier().notifyComplete(listeners, new Result(request, cause, response, cause));
|
||||
}
|
||||
|
||||
protected void tunnelSucceeded(Connection connection, Promise<Connection> promise)
|
||||
{
|
||||
// Wrap the connection with TLS
|
||||
Connection tunnel = client.tunnel(connection);
|
||||
promise.succeeded(tunnel);
|
||||
}
|
||||
|
||||
protected void tunnelFailed(Connection connection, Promise<Connection> promise, Throwable failure)
|
||||
{
|
||||
promise.failed(failure);
|
||||
connection.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String dump()
|
||||
{
|
||||
|
@ -516,22 +529,18 @@ public class HttpDestination implements Destination, Closeable, Dumpable
|
|||
{
|
||||
if (result.isFailed())
|
||||
{
|
||||
failed(result.getFailure());
|
||||
connection.close();
|
||||
tunnelFailed(connection, delegate, result.getFailure());
|
||||
}
|
||||
else
|
||||
{
|
||||
Response response = result.getResponse();
|
||||
if (response.getStatus() == 200)
|
||||
{
|
||||
// Wrap the connection with TLS
|
||||
Connection tunnel = client.tunnel(connection);
|
||||
delegate.succeeded(tunnel);
|
||||
tunnelSucceeded(connection, delegate);
|
||||
}
|
||||
else
|
||||
{
|
||||
failed(new HttpResponseException("Received " + response + " for " + result.getRequest(), response));
|
||||
connection.close();
|
||||
tunnelFailed(connection, delegate, new HttpResponseException("Received " + response + " for " + result.getRequest(), response));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ public class HttpExchange
|
|||
private volatile HttpConnection connection;
|
||||
private volatile Throwable requestFailure;
|
||||
private volatile Throwable responseFailure;
|
||||
|
||||
|
||||
public HttpExchange(HttpConversation conversation, HttpDestination destination, Request request, List<Response.ResponseListener> listeners)
|
||||
{
|
||||
|
|
|
@ -68,21 +68,30 @@ public class HttpReceiver implements HttpParser.ResponseHandler<ByteBuffer>
|
|||
{
|
||||
while (true)
|
||||
{
|
||||
int read = endPoint.fill(buffer);
|
||||
LOG.debug("Read {} bytes from {}", read, connection);
|
||||
if (read > 0)
|
||||
// Connection may be closed in a parser callback
|
||||
if (connection.isClosed())
|
||||
{
|
||||
parse(buffer);
|
||||
}
|
||||
else if (read == 0)
|
||||
{
|
||||
fillInterested();
|
||||
LOG.debug("{} closed", connection);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
shutdown();
|
||||
break;
|
||||
int read = endPoint.fill(buffer);
|
||||
LOG.debug("Read {} bytes from {}", read, connection);
|
||||
if (read > 0)
|
||||
{
|
||||
parse(buffer);
|
||||
}
|
||||
else if (read == 0)
|
||||
{
|
||||
fillInterested();
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
shutdown();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -147,7 +156,8 @@ public class HttpReceiver implements HttpParser.ResponseHandler<ByteBuffer>
|
|||
HttpConversation conversation = exchange.getConversation();
|
||||
HttpResponse response = exchange.getResponse();
|
||||
|
||||
parser.setHeadResponse(exchange.getRequest().getMethod() == HttpMethod.HEAD);
|
||||
String method = exchange.getRequest().method();
|
||||
parser.setHeadResponse(HttpMethod.HEAD.is(method) || HttpMethod.CONNECT.is(method));
|
||||
response.version(version).status(status).reason(reason);
|
||||
|
||||
// Probe the protocol handlers
|
||||
|
@ -414,6 +424,12 @@ public class HttpReceiver implements HttpParser.ResponseHandler<ByteBuffer>
|
|||
return updated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s@%x on %s", getClass().getSimpleName(), hashCode(), connection);
|
||||
}
|
||||
|
||||
private enum State
|
||||
{
|
||||
IDLE, RECEIVE, FAILURE
|
||||
|
|
|
@ -29,6 +29,7 @@ import java.util.ArrayList;
|
|||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
@ -65,7 +66,7 @@ public class HttpRequest implements Request
|
|||
private String scheme;
|
||||
private String path;
|
||||
private String query;
|
||||
private HttpMethod method;
|
||||
private String method;
|
||||
private HttpVersion version;
|
||||
private long idleTimeout;
|
||||
private long timeout;
|
||||
|
@ -126,6 +127,12 @@ public class HttpRequest implements Request
|
|||
|
||||
@Override
|
||||
public HttpMethod getMethod()
|
||||
{
|
||||
return HttpMethod.fromString(method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String method()
|
||||
{
|
||||
return method;
|
||||
}
|
||||
|
@ -133,7 +140,14 @@ public class HttpRequest implements Request
|
|||
@Override
|
||||
public Request method(HttpMethod method)
|
||||
{
|
||||
this.method = method;
|
||||
this.method = method.asString();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request method(String method)
|
||||
{
|
||||
this.method = Objects.requireNonNull(method).toUpperCase(Locale.ENGLISH);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -196,6 +210,7 @@ public class HttpRequest implements Request
|
|||
{
|
||||
params.add(name, value);
|
||||
this.query = buildQuery();
|
||||
this.uri = buildURI(true);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -541,7 +556,13 @@ public class HttpRequest implements Request
|
|||
for (String nameValue : query.split("&"))
|
||||
{
|
||||
String[] parts = nameValue.split("=");
|
||||
param(parts[0], parts.length < 2 ? "" : urlDecode(parts[1]));
|
||||
if (parts.length > 0)
|
||||
{
|
||||
String name = parts[0];
|
||||
if (name.trim().length() == 0)
|
||||
continue;
|
||||
param(name, parts.length < 2 ? "" : urlDecode(parts[1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -574,6 +595,6 @@ public class HttpRequest implements Request
|
|||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s[%s %s %s]@%x", HttpRequest.class.getSimpleName(), getMethod(), getPath(), getVersion(), hashCode());
|
||||
return String.format("%s[%s %s %s]@%x", HttpRequest.class.getSimpleName(), method(), getPath(), getVersion(), hashCode());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -176,7 +176,7 @@ public class HttpSender implements AsyncContentProvider.Listener
|
|||
String query = request.getQuery();
|
||||
if (query != null)
|
||||
path += "?" + query;
|
||||
requestInfo = new HttpGenerator.RequestInfo(request.getVersion(), request.getHeaders(), contentLength, request.getMethod().asString(), path);
|
||||
requestInfo = new HttpGenerator.RequestInfo(request.getVersion(), request.getHeaders(), contentLength, request.method(), path);
|
||||
break;
|
||||
}
|
||||
case NEED_HEADER:
|
||||
|
@ -540,9 +540,13 @@ public class HttpSender implements AsyncContentProvider.Listener
|
|||
boolean notCommitted = isBeforeCommit(current);
|
||||
if (result == null && notCommitted && request.getAbortCause() == null)
|
||||
{
|
||||
result = exchange.responseComplete(failure).getReference();
|
||||
exchange.terminateResponse();
|
||||
LOG.debug("Failed on behalf {}", exchange);
|
||||
completion = exchange.responseComplete(failure);
|
||||
if (completion.isMarked())
|
||||
{
|
||||
result = completion.getReference();
|
||||
exchange.terminateResponse();
|
||||
LOG.debug("Failed on behalf {}", exchange);
|
||||
}
|
||||
}
|
||||
|
||||
if (result != null)
|
||||
|
|
|
@ -95,8 +95,9 @@ public class RedirectProtocolHandler extends Response.Listener.Empty implements
|
|||
{
|
||||
case 301:
|
||||
{
|
||||
if (request.getMethod() == HttpMethod.GET || request.getMethod() == HttpMethod.HEAD)
|
||||
redirect(result, request.getMethod(), newURI);
|
||||
String method = request.method();
|
||||
if (HttpMethod.GET.is(method) || HttpMethod.HEAD.is(method))
|
||||
redirect(result, method, newURI);
|
||||
else
|
||||
fail(result, new HttpResponseException("HTTP protocol violation: received 301 for non GET or HEAD request", response));
|
||||
break;
|
||||
|
@ -105,13 +106,13 @@ public class RedirectProtocolHandler extends Response.Listener.Empty implements
|
|||
case 303:
|
||||
{
|
||||
// Redirect must be done using GET
|
||||
redirect(result, HttpMethod.GET, newURI);
|
||||
redirect(result, HttpMethod.GET.asString(), newURI);
|
||||
break;
|
||||
}
|
||||
case 307:
|
||||
{
|
||||
// Keep same method
|
||||
redirect(result, request.getMethod(), newURI);
|
||||
redirect(result, request.method(), newURI);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -174,7 +175,7 @@ public class RedirectProtocolHandler extends Response.Listener.Empty implements
|
|||
}
|
||||
}
|
||||
|
||||
private void redirect(Result result, HttpMethod method, URI location)
|
||||
private void redirect(Result result, String method, URI location)
|
||||
{
|
||||
final Request request = result.getRequest();
|
||||
HttpConversation conversation = client.getConversation(request.getConversationID(), false);
|
||||
|
|
|
@ -76,16 +76,29 @@ public interface Request
|
|||
int getPort();
|
||||
|
||||
/**
|
||||
* @return the method of this request, such as GET or POST
|
||||
* @return the method of this request, such as GET or POST, or null if the method is not a standard HTTP method
|
||||
* @deprecated use {@link #method()} instead
|
||||
*/
|
||||
@Deprecated
|
||||
HttpMethod getMethod();
|
||||
|
||||
/**
|
||||
* @return the method of this request, such as GET or POST, as a String
|
||||
*/
|
||||
String method();
|
||||
|
||||
/**
|
||||
* @param method the method of this request, such as GET or POST
|
||||
* @return this request object
|
||||
*/
|
||||
Request method(HttpMethod method);
|
||||
|
||||
/**
|
||||
* @param method the method of this request, such as GET or POST
|
||||
* @return this request object
|
||||
*/
|
||||
Request method(String method);
|
||||
|
||||
/**
|
||||
* @return the path of this request, such as "/" or "/path" - without the query
|
||||
* @see #getQuery()
|
||||
|
|
|
@ -221,7 +221,7 @@ public class DigestAuthentication implements Authentication
|
|||
String A1 = user + ":" + realm + ":" + password;
|
||||
String hashA1 = toHexString(digester.digest(A1.getBytes(charset)));
|
||||
|
||||
String A2 = request.getMethod().asString() + ":" + request.getURI();
|
||||
String A2 = request.method() + ":" + request.getURI();
|
||||
if ("auth-int".equals(qop))
|
||||
A2 += ":" + toHexString(digester.digest(content));
|
||||
String hashA2 = toHexString(digester.digest(A2.getBytes(charset)));
|
||||
|
|
|
@ -108,13 +108,15 @@ public class HttpClientURITest extends AbstractHttpClientServerTest
|
|||
}
|
||||
});
|
||||
|
||||
String pathQuery = path + "?" + query;
|
||||
Request request = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.path(path + "?" + query);
|
||||
.path(pathQuery);
|
||||
|
||||
Assert.assertEquals(path, request.getPath());
|
||||
Assert.assertEquals(query, request.getQuery());
|
||||
Assert.assertTrue(request.getURI().toString().endsWith(pathQuery));
|
||||
Fields params = request.getParams();
|
||||
Assert.assertEquals(1, params.size());
|
||||
Assert.assertEquals(value, params.get(name).value());
|
||||
|
@ -131,6 +133,7 @@ public class HttpClientURITest extends AbstractHttpClientServerTest
|
|||
String value = "1";
|
||||
final String query = name + "=" + value;
|
||||
final String path = "/path";
|
||||
String pathQuery = path + "?" + query;
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
|
@ -150,6 +153,7 @@ public class HttpClientURITest extends AbstractHttpClientServerTest
|
|||
|
||||
Assert.assertEquals(path, request.getPath());
|
||||
Assert.assertEquals(query, request.getQuery());
|
||||
Assert.assertTrue(request.getURI().toString().endsWith(pathQuery));
|
||||
Fields params = request.getParams();
|
||||
Assert.assertEquals(1, params.size());
|
||||
Assert.assertEquals(value, params.get(name).value());
|
||||
|
@ -168,6 +172,7 @@ public class HttpClientURITest extends AbstractHttpClientServerTest
|
|||
String value2 = "2";
|
||||
final String query = name1 + "=" + value1 + "&" + name2 + "=" + value2;
|
||||
final String path = "/path";
|
||||
String pathQuery = path + "?" + query;
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
|
@ -187,6 +192,7 @@ public class HttpClientURITest extends AbstractHttpClientServerTest
|
|||
|
||||
Assert.assertEquals(path, request.getPath());
|
||||
Assert.assertEquals(query, request.getQuery());
|
||||
Assert.assertTrue(request.getURI().toString().endsWith(pathQuery));
|
||||
Fields params = request.getParams();
|
||||
Assert.assertEquals(2, params.size());
|
||||
Assert.assertEquals(value1, params.get(name1).value());
|
||||
|
@ -208,6 +214,7 @@ public class HttpClientURITest extends AbstractHttpClientServerTest
|
|||
String encodedValue2 = URLEncoder.encode(value2, "UTF-8");
|
||||
final String query = name1 + "=" + encodedValue1 + "&" + name2 + "=" + encodedValue2;
|
||||
final String path = "/path";
|
||||
String pathQuery = path + "?" + query;
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
|
@ -229,6 +236,7 @@ public class HttpClientURITest extends AbstractHttpClientServerTest
|
|||
|
||||
Assert.assertEquals(path, request.getPath());
|
||||
Assert.assertEquals(query, request.getQuery());
|
||||
Assert.assertTrue(request.getURI().toString().endsWith(pathQuery));
|
||||
Fields params = request.getParams();
|
||||
Assert.assertEquals(2, params.size());
|
||||
Assert.assertEquals(value1, params.get(name1).value());
|
||||
|
@ -238,4 +246,70 @@ public class HttpClientURITest extends AbstractHttpClientServerTest
|
|||
|
||||
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoParameterNameNoParameterValue() throws Exception
|
||||
{
|
||||
final String path = "/path";
|
||||
final String query = "="; // Bogus query
|
||||
String pathQuery = path + "?" + query;
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
Assert.assertEquals(path, request.getRequestURI());
|
||||
Assert.assertEquals(query, request.getQueryString());
|
||||
}
|
||||
});
|
||||
|
||||
Request request = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.path(pathQuery);
|
||||
|
||||
Assert.assertEquals(path, request.getPath());
|
||||
Assert.assertEquals(query, request.getQuery());
|
||||
Assert.assertTrue(request.getURI().toString().endsWith(pathQuery));
|
||||
Fields params = request.getParams();
|
||||
Assert.assertEquals(0, params.size());
|
||||
|
||||
ContentResponse response = request.send();
|
||||
|
||||
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoParameterNameWithParameterValue() throws Exception
|
||||
{
|
||||
final String path = "/path";
|
||||
final String query = "=1"; // Bogus query
|
||||
String pathQuery = path + "?" + query;
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
Assert.assertEquals(path, request.getRequestURI());
|
||||
Assert.assertEquals(query, request.getQueryString());
|
||||
}
|
||||
});
|
||||
|
||||
Request request = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.path(pathQuery);
|
||||
|
||||
Assert.assertEquals(path, request.getPath());
|
||||
Assert.assertEquals(query, request.getQuery());
|
||||
Assert.assertTrue(request.getURI().toString().endsWith(pathQuery));
|
||||
Fields params = request.getParams();
|
||||
Assert.assertEquals(0, params.size());
|
||||
|
||||
ContentResponse response = request.send();
|
||||
|
||||
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -188,13 +188,15 @@ if [ -z "$JETTY_HOME" ]
|
|||
then
|
||||
JETTY_SH=$0
|
||||
case "$JETTY_SH" in
|
||||
/*) ;;
|
||||
./*) ;;
|
||||
*) JETTY_SH=./$JETTY_SH ;;
|
||||
/*) JETTY_HOME=${JETTY_SH%/*/*} ;;
|
||||
./*/*) JETTY_HOME=${JETTY_SH%/*/*} ;;
|
||||
./*) JETTY_HOME=.. ;;
|
||||
*/*/*) JETTY_HOME=./${JETTY_SH%/*/*} ;;
|
||||
*/*) JETTY_HOME=. ;;
|
||||
*) JETTY_HOME=.. ;;
|
||||
esac
|
||||
JETTY_HOME=${JETTY_SH%/*/*}
|
||||
|
||||
if [ ! -f "${JETTY_SH%/*/*}/$JETTY_INSTALL_TRACE_FILE" ]
|
||||
if [ ! -f "$JETTY_HOME/$JETTY_INSTALL_TRACE_FILE" ]
|
||||
then
|
||||
JETTY_HOME=
|
||||
fi
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.0.0-SNAPSHOT</version>
|
||||
<version>9.0.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-http-spi</artifactId>
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 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.spi;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.util.component.ContainerLifeCycle;
|
||||
import org.eclipse.jetty.util.component.LifeCycle;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.thread.ThreadPool;
|
||||
|
||||
public class DelegatingThreadPool extends ContainerLifeCycle implements ThreadPool
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(DelegatingThreadPool.class);
|
||||
|
||||
private Executor _executor; // memory barrier provided by start/stop semantics
|
||||
|
||||
public DelegatingThreadPool(Executor executor)
|
||||
{
|
||||
_executor=executor;
|
||||
addBean(_executor);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public Executor getExecutor()
|
||||
{
|
||||
return _executor;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void setExecutor(Executor executor)
|
||||
{
|
||||
if (isRunning())
|
||||
throw new IllegalStateException(getState());
|
||||
updateBean(_executor,executor);
|
||||
_executor=executor;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public void execute(Runnable job)
|
||||
{
|
||||
_executor.execute(job);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public boolean dispatch(Runnable job)
|
||||
{
|
||||
final Executor executor=_executor;
|
||||
if (executor instanceof ThreadPool)
|
||||
return ((ThreadPool)executor).dispatch(job);
|
||||
|
||||
try
|
||||
{
|
||||
_executor.execute(job);
|
||||
return true;
|
||||
}
|
||||
catch(RejectedExecutionException e)
|
||||
{
|
||||
LOG.warn(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public int getIdleThreads()
|
||||
{
|
||||
final Executor executor=_executor;
|
||||
if (executor instanceof ThreadPool)
|
||||
return ((ThreadPool)executor).getIdleThreads();
|
||||
|
||||
if (executor instanceof ThreadPoolExecutor)
|
||||
{
|
||||
final ThreadPoolExecutor tpe = (ThreadPoolExecutor)executor;
|
||||
return tpe.getPoolSize() - tpe.getActiveCount();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public int getThreads()
|
||||
{
|
||||
final Executor executor=_executor;
|
||||
if (executor instanceof ThreadPool)
|
||||
return ((ThreadPool)executor).getThreads();
|
||||
|
||||
if (executor instanceof ThreadPoolExecutor)
|
||||
{
|
||||
final ThreadPoolExecutor tpe = (ThreadPoolExecutor)executor;
|
||||
return tpe.getPoolSize();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public boolean isLowOnThreads()
|
||||
{
|
||||
final Executor executor=_executor;
|
||||
if (executor instanceof ThreadPool)
|
||||
return ((ThreadPool)executor).isLowOnThreads();
|
||||
|
||||
if (executor instanceof ThreadPoolExecutor)
|
||||
{
|
||||
final ThreadPoolExecutor tpe = (ThreadPoolExecutor)executor;
|
||||
// getActiveCount() locks the thread pool, so execute it last
|
||||
return tpe.getPoolSize() == tpe.getMaximumPoolSize() &&
|
||||
tpe.getQueue().size() >= tpe.getPoolSize() - tpe.getActiveCount();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public void join() throws InterruptedException
|
||||
{
|
||||
final Executor executor=_executor;
|
||||
if (executor instanceof ThreadPool)
|
||||
((ThreadPool)executor).join();
|
||||
else if (executor instanceof ExecutorService)
|
||||
((ExecutorService)executor).awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
|
||||
else
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
protected void doStop() throws Exception
|
||||
{
|
||||
super.doStop();
|
||||
if (!(_executor instanceof LifeCycle) && (_executor instanceof ExecutorService))
|
||||
((ExecutorService)_executor).shutdownNow();
|
||||
}
|
||||
|
||||
}
|
|
@ -20,18 +20,26 @@ package org.eclipse.jetty.http.spi;
|
|||
|
||||
import com.sun.net.httpserver.HttpContext;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.NetworkConnector;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
import org.eclipse.jetty.server.handler.HandlerCollection;
|
||||
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
|
||||
import org.eclipse.jetty.server.nio.SelectChannelConnector;
|
||||
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
import org.eclipse.jetty.util.thread.ThreadPool;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
|
@ -68,10 +76,10 @@ public class JettyHttpServer extends com.sun.net.httpserver.HttpServer
|
|||
public void bind(InetSocketAddress addr, int backlog) throws IOException
|
||||
{
|
||||
// check if there is already a connector listening
|
||||
Connector[] connectors = _server.getConnectors();
|
||||
Collection<NetworkConnector> connectors = _server.getBeans(NetworkConnector.class);
|
||||
if (connectors != null)
|
||||
{
|
||||
for (Connector connector : connectors)
|
||||
for (NetworkConnector connector : connectors)
|
||||
{
|
||||
if (connector.getPort() == addr.getPort()) {
|
||||
if (LOG.isDebugEnabled()) LOG.debug("server already bound to port " + addr.getPort() + ", no need to rebind");
|
||||
|
@ -86,8 +94,7 @@ public class JettyHttpServer extends com.sun.net.httpserver.HttpServer
|
|||
this._addr = addr;
|
||||
|
||||
if (LOG.isDebugEnabled()) LOG.debug("binding server to port " + addr.getPort());
|
||||
SelectChannelConnector connector = new SelectChannelConnector();
|
||||
connector.setAcceptors(1);
|
||||
ServerConnector connector = new ServerConnector(_server);
|
||||
connector.setPort(addr.getPort());
|
||||
connector.setHost(addr.getHostName());
|
||||
_server.addConnector(connector);
|
||||
|
@ -121,19 +128,20 @@ public class JettyHttpServer extends com.sun.net.httpserver.HttpServer
|
|||
{
|
||||
if (executor == null)
|
||||
throw new IllegalArgumentException("missing required 'executor' argument");
|
||||
|
||||
if (!(executor instanceof ThreadPoolExecutor))
|
||||
throw new IllegalArgumentException("only java.util.concurrent.ThreadPoolExecutor instances are allowed, got: " + executor.getClass().getName());
|
||||
|
||||
if (LOG.isDebugEnabled()) LOG.debug("using ThreadPoolExecutor for server thread pool");
|
||||
this._executor = (ThreadPoolExecutor) executor;
|
||||
_server.setThreadPool(new ThreadPoolExecutorAdapter(_executor));
|
||||
ThreadPool threadPool = _server.getThreadPool();
|
||||
if (threadPool instanceof DelegatingThreadPool)
|
||||
((DelegatingThreadPool)_server.getThreadPool()).setExecutor(executor);
|
||||
else
|
||||
throw new UnsupportedOperationException("!DelegatingThreadPool");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Executor getExecutor()
|
||||
{
|
||||
return _executor;
|
||||
ThreadPool threadPool = _server.getThreadPool();
|
||||
if (threadPool instanceof DelegatingThreadPool)
|
||||
return ((DelegatingThreadPool)_server.getThreadPool()).getExecutor();
|
||||
return threadPool;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -26,6 +26,9 @@ import org.eclipse.jetty.server.Server;
|
|||
import org.eclipse.jetty.server.handler.DefaultHandler;
|
||||
import org.eclipse.jetty.server.handler.HandlerCollection;
|
||||
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
|
||||
import org.eclipse.jetty.util.thread.ExecutorThreadPool;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.eclipse.jetty.util.thread.ThreadPool;
|
||||
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import com.sun.net.httpserver.HttpsServer;
|
||||
|
@ -53,11 +56,12 @@ public class JettyHttpServerProvider extends HttpServerProvider
|
|||
|
||||
if (server == null)
|
||||
{
|
||||
server = new Server();
|
||||
|
||||
HandlerCollection handlerCollection = new HandlerCollection();
|
||||
handlerCollection.setHandlers(new Handler[] {new ContextHandlerCollection(), new DefaultHandler()});
|
||||
server.setHandler(handlerCollection);
|
||||
ThreadPool threadPool = new DelegatingThreadPool(new QueuedThreadPool());
|
||||
server = new Server(threadPool);
|
||||
|
||||
HandlerCollection handlerCollection = new HandlerCollection();
|
||||
handlerCollection.setHandlers(new Handler[] {new ContextHandlerCollection(), new DefaultHandler()});
|
||||
server.setHandler(handlerCollection);
|
||||
|
||||
shared = false;
|
||||
}
|
||||
|
|
|
@ -1,124 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 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.spi;
|
||||
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
import org.eclipse.jetty.util.thread.ThreadPool;
|
||||
|
||||
/**
|
||||
* Jetty {@link ThreadPool} that bridges requests to a {@link ThreadPoolExecutor}.
|
||||
*/
|
||||
public class ThreadPoolExecutorAdapter extends AbstractLifeCycle implements ThreadPool
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(ThreadPoolExecutorAdapter.class);
|
||||
|
||||
|
||||
private ThreadPoolExecutor executor;
|
||||
|
||||
public ThreadPoolExecutorAdapter(ThreadPoolExecutor executor)
|
||||
{
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
public boolean dispatch(Runnable job)
|
||||
{
|
||||
try
|
||||
{
|
||||
executor.execute(job);
|
||||
return true;
|
||||
}
|
||||
catch(RejectedExecutionException e)
|
||||
{
|
||||
LOG.warn(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int getIdleThreads()
|
||||
{
|
||||
return executor.getPoolSize()-executor.getActiveCount();
|
||||
}
|
||||
|
||||
public int getThreads()
|
||||
{
|
||||
return executor.getPoolSize();
|
||||
}
|
||||
|
||||
public boolean isLowOnThreads()
|
||||
{
|
||||
return executor.getActiveCount()>=executor.getMaximumPoolSize();
|
||||
}
|
||||
|
||||
public void join() throws InterruptedException
|
||||
{
|
||||
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public boolean isFailed()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isRunning()
|
||||
{
|
||||
return !executor.isTerminated() && !executor.isTerminating();
|
||||
}
|
||||
|
||||
public boolean isStarted()
|
||||
{
|
||||
return !executor.isTerminated() && !executor.isTerminating();
|
||||
}
|
||||
|
||||
public boolean isStarting()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isStopped()
|
||||
{
|
||||
return executor.isTerminated();
|
||||
}
|
||||
|
||||
public boolean isStopping()
|
||||
{
|
||||
return executor.isTerminating();
|
||||
}
|
||||
|
||||
protected void doStart() throws Exception
|
||||
{
|
||||
if (executor.isTerminated() || executor.isTerminating() || executor.isShutdown())
|
||||
throw new IllegalStateException("Cannot restart");
|
||||
}
|
||||
|
||||
protected void doStop() throws Exception
|
||||
{
|
||||
executor.shutdown();
|
||||
if (!executor.awaitTermination(60, TimeUnit.SECONDS))
|
||||
executor.shutdownNow();
|
||||
}
|
||||
|
||||
}
|
|
@ -68,6 +68,7 @@ public class HttpField
|
|||
CACHE.put(new CachedHttpField(HttpHeader.CONTENT_LENGTH,"0"));
|
||||
CACHE.put(new CachedHttpField(HttpHeader.CONTENT_ENCODING,"gzip"));
|
||||
CACHE.put(new CachedHttpField(HttpHeader.CONTENT_ENCODING,"deflate"));
|
||||
CACHE.put(new CachedHttpField(HttpHeader.TRANSFER_ENCODING,"chunked"));
|
||||
CACHE.put(new CachedHttpField(HttpHeader.EXPIRES,"Fri, 01 Jan 1990 00:00:00 GMT"));
|
||||
|
||||
// Content types
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
|
||||
package org.eclipse.jetty.http;
|
||||
|
||||
import static org.eclipse.jetty.util.QuotedStringTokenizer.isQuoted;
|
||||
import static org.eclipse.jetty.util.QuotedStringTokenizer.quoteOnly;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.text.SimpleDateFormat;
|
||||
|
@ -54,23 +57,27 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
/**
|
||||
* HTTP Fields. A collection of HTTP header and or Trailer fields.
|
||||
*
|
||||
* <p>This class is not synchronized as it is expected that modifications will only be performed by a
|
||||
* <p>This class is not synchronised as it is expected that modifications will only be performed by a
|
||||
* single thread.
|
||||
*
|
||||
* <p>The cookie handling provided by this class is guided by the Servlet specification and RFC6265.
|
||||
*
|
||||
*/
|
||||
public class HttpFields implements Iterable<HttpField>
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(HttpFields.class);
|
||||
public static final String __COOKIE_DELIM="\"\\\n\r\t\f\b%+ ;=";
|
||||
public static final TimeZone __GMT = TimeZone.getTimeZone("GMT");
|
||||
public static final DateCache __dateCache = new DateCache("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
|
||||
|
||||
public static final String __COOKIE_DELIM_PATH="\"\\\t%+ :;,@?=()<>{}[]";
|
||||
public static final String __COOKIE_DELIM=__COOKIE_DELIM_PATH+"/";
|
||||
|
||||
static
|
||||
{
|
||||
__GMT.setID("GMT");
|
||||
__dateCache.setTimeZone(__GMT);
|
||||
}
|
||||
|
||||
|
||||
public final static String __separators = ", \t";
|
||||
|
||||
private static final String[] DAYS =
|
||||
|
@ -808,69 +815,87 @@ public class HttpFields implements Iterable<HttpField>
|
|||
final boolean isHttpOnly,
|
||||
int version)
|
||||
{
|
||||
String delim=__COOKIE_DELIM;
|
||||
|
||||
// Check arguments
|
||||
if (name == null || name.length() == 0)
|
||||
throw new IllegalArgumentException("Bad cookie name");
|
||||
|
||||
// Format value and params
|
||||
StringBuilder buf = new StringBuilder(128);
|
||||
String name_value_params;
|
||||
QuotedStringTokenizer.quoteIfNeeded(buf, name, delim);
|
||||
buf.append('=');
|
||||
String start=buf.toString();
|
||||
boolean hasDomain = false;
|
||||
boolean hasPath = false;
|
||||
|
||||
if (value != null && value.length() > 0)
|
||||
QuotedStringTokenizer.quoteIfNeeded(buf, value, delim);
|
||||
// Name is checked by servlet spec, but can also be passed directly so check again
|
||||
boolean quote_name=isQuoteNeededForCookie(name);
|
||||
quoteOnlyOrAppend(buf,name,quote_name);
|
||||
|
||||
buf.append('=');
|
||||
|
||||
// Remember name= part to look for other matching set-cookie
|
||||
String name_equals=buf.toString();
|
||||
|
||||
// Append the value
|
||||
boolean quote_value=isQuoteNeededForCookie(value);
|
||||
quoteOnlyOrAppend(buf,value,quote_value);
|
||||
|
||||
if (path != null && path.length() > 0)
|
||||
// Look for domain and path fields and check if they need to be quoted
|
||||
boolean has_domain = domain!=null && domain.length()>0;
|
||||
boolean quote_domain = has_domain && isQuoteNeededForCookie(domain);
|
||||
boolean has_path = path!=null && path.length()>0;
|
||||
boolean quote_path = has_path && isQuoteNeededForCookiePath(path);
|
||||
|
||||
// Upgrade the version if we have a comment or we need to quote value/path/domain or if they were already quoted
|
||||
if (version==0 && ( comment!=null || quote_name || quote_value || quote_domain || quote_path || isQuoted(name) || isQuoted(value) || isQuoted(path) || isQuoted(domain)))
|
||||
version=1;
|
||||
|
||||
// Append version
|
||||
if (version==1)
|
||||
buf.append (";Version=1");
|
||||
else if (version>1)
|
||||
buf.append (";Version=").append(version);
|
||||
|
||||
// Append path
|
||||
if (has_path)
|
||||
{
|
||||
hasPath = true;
|
||||
buf.append(";Path=");
|
||||
if (path.trim().startsWith("\""))
|
||||
buf.append(path);
|
||||
else
|
||||
QuotedStringTokenizer.quoteIfNeeded(buf,path,delim);
|
||||
quoteOnlyOrAppend(buf,path,quote_path);
|
||||
}
|
||||
if (domain != null && domain.length() > 0)
|
||||
|
||||
// Append domain
|
||||
if (has_domain)
|
||||
{
|
||||
hasDomain = true;
|
||||
buf.append(";Domain=");
|
||||
QuotedStringTokenizer.quoteIfNeeded(buf,domain.toLowerCase(Locale.ENGLISH),delim);
|
||||
quoteOnlyOrAppend(buf,domain,quote_domain);
|
||||
}
|
||||
|
||||
// Handle max-age and/or expires
|
||||
if (maxAge >= 0)
|
||||
{
|
||||
// Always add the expires param as some browsers still don't handle max-age
|
||||
// Always use expires
|
||||
// This is required as some browser (M$ this means you!) don't handle max-age even with v1 cookies
|
||||
buf.append(";Expires=");
|
||||
if (maxAge == 0)
|
||||
buf.append(__01Jan1970_COOKIE);
|
||||
else
|
||||
formatCookieDate(buf, System.currentTimeMillis() + 1000L * maxAge);
|
||||
|
||||
buf.append(";Max-Age=");
|
||||
buf.append(maxAge);
|
||||
|
||||
// for v1 cookies, also send max-age
|
||||
if (version>=1)
|
||||
{
|
||||
buf.append(";Max-Age=");
|
||||
buf.append(maxAge);
|
||||
}
|
||||
}
|
||||
|
||||
// add the other fields
|
||||
if (isSecure)
|
||||
buf.append(";Secure");
|
||||
if (isHttpOnly)
|
||||
buf.append(";HttpOnly");
|
||||
|
||||
if (comment != null && comment.length() > 0)
|
||||
if (comment != null)
|
||||
{
|
||||
buf.append(";Comment=");
|
||||
QuotedStringTokenizer.quoteIfNeeded(buf, comment, delim);
|
||||
quoteOnlyOrAppend(buf,comment,isQuoteNeededForCookie(comment));
|
||||
}
|
||||
|
||||
name_value_params = buf.toString();
|
||||
|
||||
// remove existing set-cookie of same name
|
||||
|
||||
// remove any existing set-cookie fields of same name
|
||||
Iterator<HttpField> i=_fields.iterator();
|
||||
while (i.hasNext())
|
||||
{
|
||||
|
@ -878,26 +903,26 @@ public class HttpFields implements Iterable<HttpField>
|
|||
if (field.getHeader()==HttpHeader.SET_COOKIE)
|
||||
{
|
||||
String val = (field.getValue() == null ? null : field.getValue().toString());
|
||||
if (val!=null && val.startsWith(start))
|
||||
if (val!=null && val.startsWith(name_equals))
|
||||
{
|
||||
//existing cookie has same name, does it also match domain and path?
|
||||
if (((!hasDomain && !val.contains("Domain")) || (hasDomain && val.contains("Domain="+domain))) &&
|
||||
((!hasPath && !val.contains("Path")) || (hasPath && val.contains("Path="+path))))
|
||||
if (((!has_domain && !val.contains("Domain")) || (has_domain && val.contains(domain))) &&
|
||||
((!has_path && !val.contains("Path")) || (has_path && val.contains(path))))
|
||||
{
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
add(HttpHeader.SET_COOKIE.toString(), name_value_params);
|
||||
// add the set cookie
|
||||
add(HttpHeader.SET_COOKIE.toString(), buf.toString());
|
||||
|
||||
// Expire responses with set-cookie headers so they do not get cached.
|
||||
put(HttpHeader.EXPIRES.toString(), __01Jan1970);
|
||||
}
|
||||
|
||||
public void putTo(ByteBuffer bufferInFillMode) throws IOException
|
||||
public void putTo(ByteBuffer bufferInFillMode)
|
||||
{
|
||||
for (HttpField field : _fields)
|
||||
{
|
||||
|
@ -1095,19 +1120,20 @@ public class HttpFields implements Iterable<HttpField>
|
|||
}
|
||||
}
|
||||
|
||||
List vl = LazyList.getList(list, false);
|
||||
if (vl.size() < 2) return vl;
|
||||
List<String> vl = LazyList.getList(list, false);
|
||||
if (vl.size() < 2)
|
||||
return vl;
|
||||
|
||||
List ql = LazyList.getList(qual, false);
|
||||
List<Float> ql = LazyList.getList(qual, false);
|
||||
|
||||
// sort list with swaps
|
||||
Float last = __zero;
|
||||
for (int i = vl.size(); i-- > 0;)
|
||||
{
|
||||
Float q = (Float) ql.get(i);
|
||||
Float q = ql.get(i);
|
||||
if (last.compareTo(q) > 0)
|
||||
{
|
||||
Object tmp = vl.get(i);
|
||||
String tmp = vl.get(i);
|
||||
vl.set(i, vl.get(i + 1));
|
||||
vl.set(i + 1, tmp);
|
||||
ql.set(i, ql.get(i + 1));
|
||||
|
@ -1123,4 +1149,66 @@ public class HttpFields implements Iterable<HttpField>
|
|||
}
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Does a cookie value need to be quoted?
|
||||
* @param s value string
|
||||
* @return true if quoted;
|
||||
* @throws IllegalArgumentException If there a control characters in the string
|
||||
*/
|
||||
public static boolean isQuoteNeededForCookie(String s)
|
||||
{
|
||||
if (s==null || s.length()==0)
|
||||
return true;
|
||||
|
||||
if (QuotedStringTokenizer.isQuoted(s))
|
||||
return false;
|
||||
|
||||
for (int i=0;i<s.length();i++)
|
||||
{
|
||||
char c = s.charAt(i);
|
||||
if (__COOKIE_DELIM.indexOf(c)>=0)
|
||||
return true;
|
||||
|
||||
if (c<0x20 || c>=0x7f)
|
||||
throw new IllegalArgumentException("Illegal character in cookie value");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Does a cookie path need to be quoted?
|
||||
* @param s value string
|
||||
* @return true if quoted;
|
||||
* @throws IllegalArgumentException If there a control characters in the string
|
||||
*/
|
||||
public static boolean isQuoteNeededForCookiePath(String s)
|
||||
{
|
||||
if (s==null || s.length()==0)
|
||||
return true;
|
||||
|
||||
if (QuotedStringTokenizer.isQuoted(s))
|
||||
return false;
|
||||
|
||||
for (int i=0;i<s.length();i++)
|
||||
{
|
||||
char c = s.charAt(i);
|
||||
if (__COOKIE_DELIM_PATH.indexOf(c)>=0)
|
||||
return true;
|
||||
|
||||
if (c<0x20 || c>=0x7f)
|
||||
throw new IllegalArgumentException("Illegal character in cookie value");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void quoteOnlyOrAppend(StringBuilder buf, String s, boolean quote)
|
||||
{
|
||||
if (quote)
|
||||
QuotedStringTokenizer.quoteOnly(buf,s);
|
||||
else
|
||||
buf.append(s);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,13 +55,19 @@ public class HttpGenerator
|
|||
private long _contentPrepared = 0;
|
||||
private boolean _noContent = false;
|
||||
private Boolean _persistent = null;
|
||||
private boolean _sendServerVersion;
|
||||
|
||||
private final int _send;
|
||||
private final static int SEND_SERVER = 0x01;
|
||||
private final static int SEND_XPOWEREDBY = 0x02;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
public static void setServerVersion(String version)
|
||||
public static void setJettyVersion(String serverVersion)
|
||||
{
|
||||
SERVER=StringUtil.getBytes("Server: Jetty("+version+")\015\012");
|
||||
SEND[SEND_SERVER] = StringUtil.getBytes("Server: " + serverVersion + "\015\012");
|
||||
SEND[SEND_XPOWEREDBY] = StringUtil.getBytes("X-Powered-By: " + serverVersion + "\015\012");
|
||||
SEND[SEND_SERVER | SEND_XPOWEREDBY] = StringUtil.getBytes("Server: " + serverVersion + "\015\012X-Powered-By: " +
|
||||
serverVersion + "\015\012");
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
|
@ -71,6 +77,13 @@ public class HttpGenerator
|
|||
/* ------------------------------------------------------------------------------- */
|
||||
public HttpGenerator()
|
||||
{
|
||||
this(false,false);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
public HttpGenerator(boolean sendServerVersion,boolean sendXPoweredBy)
|
||||
{
|
||||
_send=(sendServerVersion?SEND_SERVER:0) | (sendXPoweredBy?SEND_XPOWEREDBY:0);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
|
@ -86,15 +99,17 @@ public class HttpGenerator
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Deprecated
|
||||
public boolean getSendServerVersion ()
|
||||
{
|
||||
return _sendServerVersion;
|
||||
return (_send&SEND_SERVER)!=0;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Deprecated
|
||||
public void setSendServerVersion (boolean sendServerVersion)
|
||||
{
|
||||
_sendServerVersion = sendServerVersion;
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -537,11 +552,11 @@ public class HttpGenerator
|
|||
/* ------------------------------------------------------------ */
|
||||
private void generateHeaders(Info _info,ByteBuffer header,ByteBuffer content,boolean last)
|
||||
{
|
||||
final RequestInfo _request=(_info instanceof RequestInfo)?(RequestInfo)_info:null;
|
||||
final ResponseInfo _response=(_info instanceof ResponseInfo)?(ResponseInfo)_info:null;
|
||||
final RequestInfo request=(_info instanceof RequestInfo)?(RequestInfo)_info:null;
|
||||
final ResponseInfo response=(_info instanceof ResponseInfo)?(ResponseInfo)_info:null;
|
||||
|
||||
// default field values
|
||||
boolean has_server = false;
|
||||
int send=_send;
|
||||
HttpField transfer_encoding=null;
|
||||
boolean keep_alive=false;
|
||||
boolean close=false;
|
||||
|
@ -584,7 +599,7 @@ public class HttpGenerator
|
|||
|
||||
case CONNECTION:
|
||||
{
|
||||
if (_request!=null)
|
||||
if (request!=null)
|
||||
field.putTo(header);
|
||||
|
||||
// Lookup and/or split connection value field
|
||||
|
@ -619,7 +634,7 @@ public class HttpGenerator
|
|||
case CLOSE:
|
||||
{
|
||||
close=true;
|
||||
if (_response!=null)
|
||||
if (response!=null)
|
||||
{
|
||||
_persistent=false;
|
||||
if (_endOfContent == EndOfContent.UNKNOWN_CONTENT)
|
||||
|
@ -633,7 +648,7 @@ public class HttpGenerator
|
|||
if (_info.getHttpVersion() == HttpVersion.HTTP_1_0)
|
||||
{
|
||||
keep_alive = true;
|
||||
if (_response!=null)
|
||||
if (response!=null)
|
||||
_persistent=true;
|
||||
}
|
||||
break;
|
||||
|
@ -656,11 +671,8 @@ public class HttpGenerator
|
|||
|
||||
case SERVER:
|
||||
{
|
||||
if (getSendServerVersion())
|
||||
{
|
||||
has_server=true;
|
||||
field.putTo(header);
|
||||
}
|
||||
send=send&~SEND_SERVER;
|
||||
field.putTo(header);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -680,7 +692,7 @@ public class HttpGenerator
|
|||
// 4. Content-Length
|
||||
// 5. multipart/byteranges
|
||||
// 6. close
|
||||
int status=_response!=null?_response.getStatus():-1;
|
||||
int status=response!=null?response.getStatus():-1;
|
||||
switch (_endOfContent)
|
||||
{
|
||||
case UNKNOWN_CONTENT:
|
||||
|
@ -688,14 +700,14 @@ public class HttpGenerator
|
|||
// written yet?
|
||||
|
||||
// Response known not to have a body
|
||||
if (_contentPrepared == 0 && _response!=null && (status < 200 || status == 204 || status == 304))
|
||||
if (_contentPrepared == 0 && response!=null && (status < 200 || status == 204 || status == 304))
|
||||
_endOfContent=EndOfContent.NO_CONTENT;
|
||||
else if (_info.getContentLength()>0)
|
||||
{
|
||||
// we have been given a content length
|
||||
_endOfContent=EndOfContent.CONTENT_LENGTH;
|
||||
long content_length = _info.getContentLength();
|
||||
if ((_response!=null || content_length>0 || content_type ) && !_noContent)
|
||||
if ((response!=null || content_length>0 || content_type ) && !_noContent)
|
||||
{
|
||||
// known length but not actually set.
|
||||
header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace());
|
||||
|
@ -710,7 +722,7 @@ public class HttpGenerator
|
|||
long content_length = _contentPrepared+BufferUtil.length(content);
|
||||
|
||||
// Do we need to tell the headers about it
|
||||
if ((_response!=null || content_length>0 || content_type ) && !_noContent)
|
||||
if ((response!=null || content_length>0 || content_type ) && !_noContent)
|
||||
{
|
||||
header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace());
|
||||
BufferUtil.putDecLong(header, content_length);
|
||||
|
@ -721,7 +733,7 @@ public class HttpGenerator
|
|||
{
|
||||
// No idea, so we must assume that a body is coming
|
||||
_endOfContent = (!isPersistent() || _info.getHttpVersion().ordinal() < HttpVersion.HTTP_1_1.ordinal() ) ? EndOfContent.EOF_CONTENT : EndOfContent.CHUNKED_CONTENT;
|
||||
if (_response!=null && _endOfContent==EndOfContent.EOF_CONTENT)
|
||||
if (response!=null && _endOfContent==EndOfContent.EOF_CONTENT)
|
||||
{
|
||||
_endOfContent=EndOfContent.NO_CONTENT;
|
||||
_noContent=true;
|
||||
|
@ -731,7 +743,7 @@ public class HttpGenerator
|
|||
|
||||
case CONTENT_LENGTH:
|
||||
long content_length = _info.getContentLength();
|
||||
if ((_response!=null || content_length>0 || content_type ) && !_noContent)
|
||||
if ((response!=null || content_length>0 || content_type ) && !_noContent)
|
||||
{
|
||||
// known length but not actually set.
|
||||
header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace());
|
||||
|
@ -741,12 +753,12 @@ public class HttpGenerator
|
|||
break;
|
||||
|
||||
case NO_CONTENT:
|
||||
if (_response!=null && status >= 200 && status != 204 && status != 304)
|
||||
if (response!=null && status >= 200 && status != 204 && status != 304)
|
||||
header.put(CONTENT_LENGTH_0);
|
||||
break;
|
||||
|
||||
case EOF_CONTENT:
|
||||
_persistent = _request!=null;
|
||||
_persistent = request!=null;
|
||||
break;
|
||||
|
||||
case CHUNKED_CONTENT:
|
||||
|
@ -780,7 +792,7 @@ public class HttpGenerator
|
|||
}
|
||||
|
||||
// If this is a response, work out persistence
|
||||
if (_response!=null)
|
||||
if (response!=null)
|
||||
{
|
||||
if (!isPersistent() && (close || _info.getHttpVersion().ordinal() > HttpVersion.HTTP_1_0.ordinal()))
|
||||
{
|
||||
|
@ -814,8 +826,8 @@ public class HttpGenerator
|
|||
}
|
||||
}
|
||||
|
||||
if (!has_server && status>199 && getSendServerVersion())
|
||||
header.put(SERVER);
|
||||
if (status>199)
|
||||
header.put(SEND[send]);
|
||||
|
||||
// end the header.
|
||||
header.put(HttpTokens.CRLF);
|
||||
|
@ -851,7 +863,12 @@ public class HttpGenerator
|
|||
private static final byte[] HTTP_1_1_SPACE = StringUtil.getBytes(HttpVersion.HTTP_1_1+" ");
|
||||
private static final byte[] CRLF = StringUtil.getBytes("\015\012");
|
||||
private static final byte[] TRANSFER_ENCODING_CHUNKED = StringUtil.getBytes("Transfer-Encoding: chunked\015\012");
|
||||
private static byte[] SERVER = StringUtil.getBytes("Server: Jetty(7.0.x)\015\012");
|
||||
private static final byte[][] SEND = new byte[][]{
|
||||
new byte[0],
|
||||
StringUtil.getBytes("Server: Jetty(9.x.x)\015\012"),
|
||||
StringUtil.getBytes("X-Powered-By: Jetty(9.x.x)\015\012"),
|
||||
StringUtil.getBytes("Server: Jetty(9.x.x)\015\012X-Powered-By: Jetty(9.x.x)\015\012")
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
|
|
|
@ -108,6 +108,8 @@ public enum HttpHeader
|
|||
SET_COOKIE2("Set-Cookie2"),
|
||||
MIME_VERSION("MIME-Version"),
|
||||
IDENTITY("identity"),
|
||||
|
||||
X_POWERED_BY("X-Powered-By"),
|
||||
|
||||
UNKNOWN("::UNKNOWN::");
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ public enum HttpMethod
|
|||
MOVE;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
/**
|
||||
* Optimised lookup to find a method name and trailing space in a byte array.
|
||||
* @param bytes Array containing ISO-8859-1 characters
|
||||
* @param position The first valid index
|
||||
|
@ -70,22 +70,22 @@ public enum HttpMethod
|
|||
return HEAD;
|
||||
break;
|
||||
case 'O':
|
||||
if (bytes[position+1]=='O' && bytes[position+2]=='T' && bytes[position+3]=='I' && length>=8 &&
|
||||
if (bytes[position+1]=='O' && bytes[position+2]=='T' && bytes[position+3]=='I' && length>=8 &&
|
||||
bytes[position+4]=='O' && bytes[position+5]=='N' && bytes[position+6]=='S' && bytes[position+7]==' ' )
|
||||
return OPTIONS;
|
||||
break;
|
||||
case 'D':
|
||||
if (bytes[position+1]=='E' && bytes[position+2]=='L' && bytes[position+3]=='E' && length>=7 &&
|
||||
if (bytes[position+1]=='E' && bytes[position+2]=='L' && bytes[position+3]=='E' && length>=7 &&
|
||||
bytes[position+4]=='T' && bytes[position+5]=='E' && bytes[position+6]==' ' )
|
||||
return DELETE;
|
||||
break;
|
||||
case 'T':
|
||||
if (bytes[position+1]=='R' && bytes[position+2]=='A' && bytes[position+3]=='C' && length>=6 &&
|
||||
if (bytes[position+1]=='R' && bytes[position+2]=='A' && bytes[position+3]=='C' && length>=6 &&
|
||||
bytes[position+4]=='E' && bytes[position+5]==' ' )
|
||||
return TRACE;
|
||||
break;
|
||||
case 'C':
|
||||
if (bytes[position+1]=='O' && bytes[position+2]=='N' && bytes[position+3]=='N' && length>=8 &&
|
||||
if (bytes[position+1]=='O' && bytes[position+2]=='N' && bytes[position+3]=='N' && length>=8 &&
|
||||
bytes[position+4]=='E' && bytes[position+5]=='C' && bytes[position+6]=='T' && bytes[position+7]==' ' )
|
||||
return CONNECT;
|
||||
break;
|
||||
|
@ -93,7 +93,7 @@ public enum HttpMethod
|
|||
if (bytes[position+1]=='O' && bytes[position+2]=='V' && bytes[position+3]=='E' && bytes[position+4]==' ')
|
||||
return MOVE;
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ public enum HttpMethod
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
/**
|
||||
* Optimised lookup to find a method name and trailing space in a byte array.
|
||||
* @param buffer buffer containing ISO-8859-1 characters
|
||||
* @return A HttpMethod if a match or null if no easy match.
|
||||
|
@ -110,14 +110,14 @@ public enum HttpMethod
|
|||
{
|
||||
if (buffer.hasArray())
|
||||
return lookAheadGet(buffer.array(),buffer.arrayOffset()+buffer.position(),buffer.arrayOffset()+buffer.limit());
|
||||
|
||||
|
||||
// TODO use cache and check for space
|
||||
// return CACHE.getBest(buffer,0,buffer.remaining());
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public final static Trie<HttpMethod> CACHE= new ArrayTrie<HttpMethod>();
|
||||
public final static Trie<HttpMethod> CACHE= new ArrayTrie<>();
|
||||
static
|
||||
{
|
||||
for (HttpMethod method : HttpMethod.values())
|
||||
|
@ -144,15 +144,15 @@ public enum HttpMethod
|
|||
/* ------------------------------------------------------------ */
|
||||
public boolean is(String s)
|
||||
{
|
||||
return toString().equalsIgnoreCase(s);
|
||||
return toString().equalsIgnoreCase(s);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public ByteBuffer asBuffer()
|
||||
{
|
||||
return _buffer.asReadOnlyBuffer();
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public String asString()
|
||||
{
|
||||
|
|
|
@ -63,11 +63,18 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
* per parser dynamic Trie of {@link HttpFields} from previous parsed messages
|
||||
* is used to help the parsing of subsequent messages.
|
||||
* </p>
|
||||
* <p>
|
||||
* If the system property "org.eclipse.jetty.http.HttpParser.STRICT" is set to true,
|
||||
* then the parser will strictly pass on the exact strings received for methods and header
|
||||
* fields. Otherwise a fast case insensitive string lookup is used that may alter the
|
||||
* case of the method and/or headers
|
||||
* </p>
|
||||
*/
|
||||
public class HttpParser
|
||||
{
|
||||
public static final Logger LOG = Log.getLogger(HttpParser.class);
|
||||
static final int INITIAL_URI_LENGTH=256;
|
||||
public final static boolean __STRICT=Boolean.getBoolean("org.eclipse.jetty.http.HttpParser.STRICT");
|
||||
public final static int INITIAL_URI_LENGTH=256;
|
||||
|
||||
// States
|
||||
public enum State
|
||||
|
@ -82,7 +89,6 @@ public class HttpParser
|
|||
REQUEST_VERSION,
|
||||
REASON,
|
||||
HEADER,
|
||||
HEADER_NAME,
|
||||
HEADER_IN_NAME,
|
||||
HEADER_VALUE,
|
||||
HEADER_IN_VALUE,
|
||||
|
@ -96,10 +102,12 @@ public class HttpParser
|
|||
CLOSED
|
||||
};
|
||||
|
||||
private final boolean DEBUG=LOG.isDebugEnabled();
|
||||
private final HttpHandler<ByteBuffer> _handler;
|
||||
private final RequestHandler<ByteBuffer> _requestHandler;
|
||||
private final ResponseHandler<ByteBuffer> _responseHandler;
|
||||
private final int _maxHeaderBytes;
|
||||
private final boolean _strict;
|
||||
private HttpField _field;
|
||||
private HttpHeader _header;
|
||||
private String _headerString;
|
||||
|
@ -131,31 +139,45 @@ public class HttpParser
|
|||
/* ------------------------------------------------------------------------------- */
|
||||
public HttpParser(RequestHandler<ByteBuffer> handler)
|
||||
{
|
||||
this(handler,-1);
|
||||
this(handler,-1,__STRICT);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
public HttpParser(ResponseHandler<ByteBuffer> handler)
|
||||
{
|
||||
this(handler,-1);
|
||||
this(handler,-1,__STRICT);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
public HttpParser(RequestHandler<ByteBuffer> handler,int maxHeaderBytes)
|
||||
{
|
||||
_handler=handler;
|
||||
_requestHandler=handler;
|
||||
_responseHandler=null;
|
||||
_maxHeaderBytes=maxHeaderBytes;
|
||||
this(handler,maxHeaderBytes,__STRICT);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
public HttpParser(ResponseHandler<ByteBuffer> handler,int maxHeaderBytes)
|
||||
{
|
||||
this(handler,maxHeaderBytes,__STRICT);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
public HttpParser(RequestHandler<ByteBuffer> handler,int maxHeaderBytes,boolean strict)
|
||||
{
|
||||
_handler=handler;
|
||||
_requestHandler=handler;
|
||||
_responseHandler=null;
|
||||
_maxHeaderBytes=maxHeaderBytes;
|
||||
_strict=strict;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
public HttpParser(ResponseHandler<ByteBuffer> handler,int maxHeaderBytes,boolean strict)
|
||||
{
|
||||
_handler=handler;
|
||||
_requestHandler=null;
|
||||
_responseHandler=handler;
|
||||
_maxHeaderBytes=maxHeaderBytes;
|
||||
_strict=strict;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
|
@ -239,6 +261,7 @@ public class HttpParser
|
|||
return _state == state;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
private static class BadMessage extends Error
|
||||
{
|
||||
private final int _code;
|
||||
|
@ -309,7 +332,7 @@ public class HttpParser
|
|||
if (ch==HttpTokens.LINE_FEED)
|
||||
return ch;
|
||||
|
||||
throw new BadMessage();
|
||||
throw new BadMessage("Bad EOL");
|
||||
}
|
||||
|
||||
// Defer lookup of LF
|
||||
|
@ -319,7 +342,7 @@ public class HttpParser
|
|||
|
||||
// Only LF or TAB acceptable special characters
|
||||
if (ch!=HttpTokens.LINE_FEED && ch!=HttpTokens.TAB)
|
||||
throw new BadMessage();
|
||||
throw new BadMessage("Illegal character");
|
||||
|
||||
/*
|
||||
if (ch>HttpTokens.SPACE)
|
||||
|
@ -374,14 +397,16 @@ public class HttpParser
|
|||
return false;
|
||||
}
|
||||
|
||||
private String takeString()
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
private void setString(String s)
|
||||
{
|
||||
String s =_string.toString();
|
||||
_string.setLength(0);
|
||||
return s;
|
||||
_string.append(s);
|
||||
_length=s.length();
|
||||
}
|
||||
|
||||
private String takeLengthString()
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
private String takeString()
|
||||
{
|
||||
_string.setLength(_length);
|
||||
String s =_string.toString();
|
||||
|
@ -402,8 +427,6 @@ public class HttpParser
|
|||
{
|
||||
// process each character
|
||||
byte ch=next(buffer);
|
||||
if (ch==-1)
|
||||
return true;
|
||||
if (ch==0)
|
||||
continue;
|
||||
|
||||
|
@ -429,16 +452,15 @@ public class HttpParser
|
|||
case METHOD:
|
||||
if (ch == HttpTokens.SPACE)
|
||||
{
|
||||
_length=_string.length();
|
||||
_methodString=takeString();
|
||||
HttpMethod method=HttpMethod.CACHE.get(_methodString);
|
||||
if (method!=null)
|
||||
if (method!=null && !_strict)
|
||||
_methodString=method.asString();
|
||||
setState(State.SPACE1);
|
||||
}
|
||||
else if (ch < HttpTokens.SPACE && ch>=0)
|
||||
{
|
||||
throw new BadMessage(HttpStatus.BAD_REQUEST_400,"No URI");
|
||||
}
|
||||
else if (ch<HttpTokens.SPACE)
|
||||
throw new BadMessage(ch<0?"Illegal character":"No URI");
|
||||
else
|
||||
_string.append((char)ch);
|
||||
break;
|
||||
|
@ -446,6 +468,7 @@ public class HttpParser
|
|||
case RESPONSE_VERSION:
|
||||
if (ch == HttpTokens.SPACE)
|
||||
{
|
||||
_length=_string.length();
|
||||
String version=takeString();
|
||||
_version=HttpVersion.CACHE.get(version);
|
||||
if (_version==null)
|
||||
|
@ -454,16 +477,14 @@ public class HttpParser
|
|||
}
|
||||
setState(State.SPACE1);
|
||||
}
|
||||
else if (ch < HttpTokens.SPACE && ch>=0)
|
||||
{
|
||||
throw new BadMessage(HttpStatus.BAD_REQUEST_400,"No Status");
|
||||
}
|
||||
else if (ch < HttpTokens.SPACE)
|
||||
throw new BadMessage(ch<0?"Illegal character":"No Status");
|
||||
else
|
||||
_string.append((char)ch);
|
||||
break;
|
||||
|
||||
case SPACE1:
|
||||
if (ch > HttpTokens.SPACE || ch<0)
|
||||
if (ch > HttpTokens.SPACE)
|
||||
{
|
||||
if (_responseHandler!=null)
|
||||
{
|
||||
|
@ -507,9 +528,7 @@ public class HttpParser
|
|||
}
|
||||
}
|
||||
else if (ch < HttpTokens.SPACE)
|
||||
{
|
||||
throw new BadMessage(HttpStatus.BAD_REQUEST_400,_requestHandler!=null?"No URI":"No Status");
|
||||
}
|
||||
break;
|
||||
|
||||
case STATUS:
|
||||
|
@ -528,7 +547,7 @@ public class HttpParser
|
|||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalStateException();
|
||||
throw new BadMessage();
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -561,7 +580,7 @@ public class HttpParser
|
|||
break;
|
||||
|
||||
case SPACE2:
|
||||
if (ch > HttpTokens.SPACE || ch<0)
|
||||
if (ch > HttpTokens.SPACE)
|
||||
{
|
||||
_string.setLength(0);
|
||||
_string.append((char)ch);
|
||||
|
@ -620,13 +639,18 @@ public class HttpParser
|
|||
return_from_parse=_handler.messageComplete()||return_from_parse;
|
||||
}
|
||||
}
|
||||
else if (ch<HttpTokens.SPACE)
|
||||
throw new BadMessage();
|
||||
break;
|
||||
|
||||
case REQUEST_VERSION:
|
||||
if (ch == HttpTokens.LINE_FEED)
|
||||
{
|
||||
if (_version==null)
|
||||
{
|
||||
_length=_string.length();
|
||||
_version=HttpVersion.CACHE.get(takeString());
|
||||
}
|
||||
if (_version==null)
|
||||
{
|
||||
throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Unknown Version");
|
||||
|
@ -645,26 +669,30 @@ public class HttpParser
|
|||
return_from_parse=_requestHandler.startRequest(_method,_methodString,_uri, _version)||return_from_parse;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
else if (ch>HttpTokens.SPACE)
|
||||
_string.append((char)ch);
|
||||
else
|
||||
throw new BadMessage();
|
||||
|
||||
break;
|
||||
|
||||
case REASON:
|
||||
if (ch == HttpTokens.LINE_FEED)
|
||||
{
|
||||
String reason=takeLengthString();
|
||||
String reason=takeString();
|
||||
|
||||
setState(State.HEADER);
|
||||
return_from_parse=_responseHandler.startResponse(_version, _responseStatus, reason)||return_from_parse;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
else if (ch>=HttpTokens.SPACE)
|
||||
{
|
||||
_string.append((char)ch);
|
||||
if (ch!=' '&&ch!='\t')
|
||||
_length=_string.length();
|
||||
}
|
||||
else
|
||||
throw new BadMessage();
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -741,7 +769,8 @@ public class HttpParser
|
|||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
LOG.debug(e);
|
||||
if (DEBUG)
|
||||
LOG.debug(e);
|
||||
throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad Host header");
|
||||
}
|
||||
break loop;
|
||||
|
@ -806,8 +835,6 @@ public class HttpParser
|
|||
{
|
||||
// process each character
|
||||
byte ch=next(buffer);
|
||||
if (ch==-1)
|
||||
return true;
|
||||
if (ch==0)
|
||||
continue;
|
||||
|
||||
|
@ -827,14 +854,18 @@ public class HttpParser
|
|||
case HttpTokens.TAB:
|
||||
{
|
||||
// header value without name - continuation?
|
||||
_string.setLength(0);
|
||||
if (_valueString!=null)
|
||||
if (_valueString==null)
|
||||
{
|
||||
_string.append(_valueString);
|
||||
_string.append(' ');
|
||||
_string.setLength(0);
|
||||
_length=0;
|
||||
}
|
||||
else
|
||||
{
|
||||
setString(_valueString);
|
||||
_string.append(' ');
|
||||
_length++;
|
||||
_valueString=null;
|
||||
}
|
||||
_length=_string.length();
|
||||
_valueString=null;
|
||||
setState(State.HEADER_VALUE);
|
||||
break;
|
||||
}
|
||||
|
@ -917,6 +948,8 @@ public class HttpParser
|
|||
break;
|
||||
}
|
||||
}
|
||||
else if (ch<=HttpTokens.SPACE)
|
||||
throw new BadMessage();
|
||||
else
|
||||
{
|
||||
if (buffer.hasRemaining())
|
||||
|
@ -928,14 +961,35 @@ public class HttpParser
|
|||
|
||||
if (field!=null)
|
||||
{
|
||||
String n=field.getName();
|
||||
String v=field.getValue();
|
||||
final String n;
|
||||
final String v;
|
||||
|
||||
if (_strict)
|
||||
{
|
||||
// Have to get the fields exactly from the buffer to match case
|
||||
String fn=field.getName();
|
||||
String fv=field.getValue();
|
||||
n=BufferUtil.toString(buffer,buffer.position()-1,fn.length(),StringUtil.__US_ASCII_CHARSET);
|
||||
if (fv==null)
|
||||
v=null;
|
||||
else
|
||||
{
|
||||
v=BufferUtil.toString(buffer,buffer.position()+fn.length()+1,fv.length(),StringUtil.__ISO_8859_1_CHARSET);
|
||||
field=new HttpField(field.getHeader(),n,v);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
n=field.getName();
|
||||
v=field.getValue();
|
||||
}
|
||||
|
||||
_header=field.getHeader();
|
||||
_headerString=n;
|
||||
|
||||
if (v==null)
|
||||
{
|
||||
// Header only
|
||||
_header=field.getHeader();
|
||||
_headerString=n;
|
||||
setState(State.HEADER_VALUE);
|
||||
_string.setLength(0);
|
||||
_length=0;
|
||||
|
@ -951,8 +1005,6 @@ public class HttpParser
|
|||
if (b==HttpTokens.CARRIAGE_RETURN || b==HttpTokens.LINE_FEED)
|
||||
{
|
||||
_field=field;
|
||||
_header=_field.getHeader();
|
||||
_headerString=n;
|
||||
_valueString=v;
|
||||
setState(State.HEADER_IN_VALUE);
|
||||
|
||||
|
@ -965,186 +1017,111 @@ public class HttpParser
|
|||
buffer.position(pos);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
setState(State.HEADER_IN_VALUE);
|
||||
setString(v);
|
||||
buffer.position(pos);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// New header
|
||||
setState(State.HEADER_NAME);
|
||||
setState(State.HEADER_IN_NAME);
|
||||
_string.setLength(0);
|
||||
_string.append((char)ch);
|
||||
_length=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case HEADER_NAME:
|
||||
switch(ch)
|
||||
{
|
||||
case HttpTokens.LINE_FEED:
|
||||
if (_headerString==null)
|
||||
{
|
||||
_headerString=takeLengthString();
|
||||
_header=HttpHeader.CACHE.get(_headerString);
|
||||
}
|
||||
setState(State.HEADER);
|
||||
|
||||
break;
|
||||
|
||||
case HttpTokens.COLON:
|
||||
if (_headerString==null)
|
||||
{
|
||||
_headerString=takeLengthString();
|
||||
_header=HttpHeader.CACHE.get(_headerString);
|
||||
}
|
||||
setState(State.HEADER_VALUE);
|
||||
break;
|
||||
case HttpTokens.SPACE:
|
||||
case HttpTokens.TAB:
|
||||
_string.append((char)ch);
|
||||
break;
|
||||
default:
|
||||
{
|
||||
_string.append((char)ch);
|
||||
_length=_string.length();
|
||||
setState(State.HEADER_IN_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case HEADER_IN_NAME:
|
||||
switch(ch)
|
||||
if (ch==HttpTokens.COLON || ch==HttpTokens.LINE_FEED)
|
||||
{
|
||||
case HttpTokens.LINE_FEED:
|
||||
if (_headerString==null)
|
||||
{
|
||||
_headerString=takeString();
|
||||
_length=-1;
|
||||
_header=HttpHeader.CACHE.get(_headerString);
|
||||
setState(State.HEADER);
|
||||
break;
|
||||
}
|
||||
_length=-1;
|
||||
|
||||
case HttpTokens.COLON:
|
||||
if (_headerString==null)
|
||||
{
|
||||
_headerString=takeString();
|
||||
_header=HttpHeader.CACHE.get(_headerString);
|
||||
}
|
||||
_length=-1;
|
||||
setState(State.HEADER_VALUE);
|
||||
break;
|
||||
case HttpTokens.SPACE:
|
||||
case HttpTokens.TAB:
|
||||
if (_header!=null)
|
||||
{
|
||||
_string.setLength(0);
|
||||
_string.append(_header.asString());
|
||||
_length=_string.length();
|
||||
_header=null;
|
||||
_headerString=null;
|
||||
}
|
||||
setState(State.HEADER_NAME);
|
||||
_string.append((char)ch);
|
||||
break;
|
||||
default:
|
||||
if (_header!=null)
|
||||
{
|
||||
_string.setLength(0);
|
||||
_string.append(_header.asString());
|
||||
_length=_string.length();
|
||||
_header=null;
|
||||
_headerString=null;
|
||||
}
|
||||
_string.append((char)ch);
|
||||
_length++;
|
||||
setState(ch==HttpTokens.LINE_FEED?State.HEADER:State.HEADER_VALUE);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
if (ch>=HttpTokens.SPACE || ch==HttpTokens.TAB)
|
||||
{
|
||||
if (_header!=null)
|
||||
{
|
||||
setString(_header.asString());
|
||||
_header=null;
|
||||
_headerString=null;
|
||||
}
|
||||
|
||||
_string.append((char)ch);
|
||||
if (ch>HttpTokens.SPACE)
|
||||
_length=_string.length();
|
||||
break;
|
||||
}
|
||||
|
||||
throw new BadMessage("Illegal character");
|
||||
|
||||
case HEADER_VALUE:
|
||||
switch(ch)
|
||||
if (ch>HttpTokens.SPACE || ch<0)
|
||||
{
|
||||
case HttpTokens.LINE_FEED:
|
||||
if (_length > 0)
|
||||
{
|
||||
if (_valueString!=null)
|
||||
{
|
||||
// multi line value!
|
||||
_value=null;
|
||||
_valueString+=" "+takeLengthString();
|
||||
}
|
||||
else if (HttpHeaderValue.hasKnownValues(_header))
|
||||
{
|
||||
_valueString=takeLengthString();
|
||||
_value=HttpHeaderValue.CACHE.get(_valueString);
|
||||
}
|
||||
else
|
||||
{
|
||||
_value=null;
|
||||
_valueString=takeLengthString();
|
||||
}
|
||||
}
|
||||
setState(State.HEADER);
|
||||
break;
|
||||
case HttpTokens.SPACE:
|
||||
case HttpTokens.TAB:
|
||||
break;
|
||||
default:
|
||||
{
|
||||
_string.append((char)ch);
|
||||
_length=_string.length();
|
||||
setState(State.HEADER_IN_VALUE);
|
||||
}
|
||||
_string.append((char)(0xff&ch));
|
||||
_length=_string.length();
|
||||
setState(State.HEADER_IN_VALUE);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
if (ch==HttpTokens.SPACE || ch==HttpTokens.TAB)
|
||||
break;
|
||||
|
||||
if (ch==HttpTokens.LINE_FEED)
|
||||
{
|
||||
if (_length > 0)
|
||||
{
|
||||
_value=null;
|
||||
_valueString=(_valueString==null)?takeString():(_valueString+" "+takeString());
|
||||
}
|
||||
setState(State.HEADER);
|
||||
break;
|
||||
}
|
||||
|
||||
throw new BadMessage("Illegal character");
|
||||
|
||||
case HEADER_IN_VALUE:
|
||||
switch(ch)
|
||||
if (ch>=HttpTokens.SPACE || ch<0)
|
||||
{
|
||||
case HttpTokens.LINE_FEED:
|
||||
if (_length > 0)
|
||||
{
|
||||
if (HttpHeaderValue.hasKnownValues(_header))
|
||||
{
|
||||
_valueString=takeString();
|
||||
_value=HttpHeaderValue.CACHE.get(_valueString);
|
||||
}
|
||||
else
|
||||
{
|
||||
_value=null;
|
||||
_valueString=takeString();
|
||||
}
|
||||
_length=-1;
|
||||
}
|
||||
setState(State.HEADER);
|
||||
break;
|
||||
case HttpTokens.SPACE:
|
||||
case HttpTokens.TAB:
|
||||
if (_valueString!=null)
|
||||
{
|
||||
_string.setLength(0);
|
||||
_string.append(_valueString);
|
||||
_length=_valueString.length();
|
||||
_valueString=null;
|
||||
_field=null;
|
||||
}
|
||||
_string.append((char)ch);
|
||||
setState(State.HEADER_VALUE);
|
||||
break;
|
||||
default:
|
||||
if (_valueString!=null)
|
||||
{
|
||||
_string.setLength(0);
|
||||
_string.append(_valueString);
|
||||
_length=_valueString.length();
|
||||
_valueString=null;
|
||||
_field=null;
|
||||
}
|
||||
_string.append((char)ch);
|
||||
_length++;
|
||||
if (_valueString!=null)
|
||||
{
|
||||
setString(_valueString);
|
||||
_valueString=null;
|
||||
_field=null;
|
||||
}
|
||||
_string.append((char)(0xff&ch));
|
||||
if (ch>HttpTokens.SPACE || ch<0)
|
||||
_length=_string.length();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
if (ch==HttpTokens.LINE_FEED)
|
||||
{
|
||||
if (_length > 0)
|
||||
{
|
||||
_value=null;
|
||||
_valueString=takeString();
|
||||
_length=-1;
|
||||
}
|
||||
setState(State.HEADER);
|
||||
break;
|
||||
}
|
||||
throw new BadMessage("Illegal character");
|
||||
|
||||
default:
|
||||
throw new IllegalStateException(_state.toString());
|
||||
|
||||
|
@ -1368,8 +1345,9 @@ public class HttpParser
|
|||
{
|
||||
BufferUtil.clear(buffer);
|
||||
|
||||
LOG.warn("badMessage: "+e._code+(e._message!=null?" "+e._message:"")+" for "+_handler);
|
||||
LOG.debug(e);
|
||||
LOG.warn("BadMessage: "+e._code+(e._message!=null?" "+e._message:"")+" for "+_handler);
|
||||
if (DEBUG)
|
||||
LOG.debug(e);
|
||||
setState(State.CLOSED);
|
||||
_handler.badMessage(e._code, e._message);
|
||||
return false;
|
||||
|
@ -1378,8 +1356,9 @@ public class HttpParser
|
|||
{
|
||||
BufferUtil.clear(buffer);
|
||||
|
||||
LOG.warn("badMessage: "+e.toString()+" for "+_handler);
|
||||
LOG.debug(e);
|
||||
LOG.warn("Parsing Exception: "+e.toString()+" for "+_handler);
|
||||
if (DEBUG)
|
||||
LOG.debug(e);
|
||||
|
||||
if (_state.ordinal()<=State.END.ordinal())
|
||||
{
|
||||
|
@ -1408,7 +1387,8 @@ public class HttpParser
|
|||
*/
|
||||
public boolean shutdownInput()
|
||||
{
|
||||
LOG.debug("shutdownInput {}", this);
|
||||
if (DEBUG)
|
||||
LOG.debug("shutdownInput {}", this);
|
||||
|
||||
// was this unexpected?
|
||||
switch(_state)
|
||||
|
@ -1437,6 +1417,8 @@ public class HttpParser
|
|||
/* ------------------------------------------------------------------------------- */
|
||||
public void close()
|
||||
{
|
||||
if (DEBUG)
|
||||
LOG.debug("close {}", this);
|
||||
switch(_state)
|
||||
{
|
||||
case START:
|
||||
|
@ -1469,6 +1451,8 @@ public class HttpParser
|
|||
/* ------------------------------------------------------------------------------- */
|
||||
public void reset()
|
||||
{
|
||||
if (DEBUG)
|
||||
LOG.debug("reset {}", this);
|
||||
// reset state
|
||||
setState(State.START);
|
||||
_endOfContent=EndOfContent.UNKNOWN_CONTENT;
|
||||
|
@ -1483,7 +1467,8 @@ public class HttpParser
|
|||
/* ------------------------------------------------------------------------------- */
|
||||
private void setState(State state)
|
||||
{
|
||||
// LOG.debug("{} --> {}",_state,state);
|
||||
if (DEBUG)
|
||||
LOG.debug("{} --> {}",_state,state);
|
||||
_state=state;
|
||||
}
|
||||
|
||||
|
|
|
@ -92,6 +92,19 @@ public class HttpTester
|
|||
_version=version;
|
||||
}
|
||||
|
||||
public void setContent(byte[] bytes)
|
||||
{
|
||||
try
|
||||
{
|
||||
_content=new ByteArrayOutputStream();
|
||||
_content.write(bytes);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setContent(String content)
|
||||
{
|
||||
try
|
||||
|
|
|
@ -279,10 +279,10 @@ public class HttpFieldsTest
|
|||
//test cookies with same name, domain and path, only 1 allowed
|
||||
fields.addSetCookie("everything","wrong","domain","path",0,"to be replaced",true,true,0);
|
||||
fields.addSetCookie("everything","value","domain","path",0,"comment",true,true,0);
|
||||
assertEquals("everything=value;Path=path;Domain=domain;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",fields.getStringField("Set-Cookie"));
|
||||
assertEquals("everything=value;Version=1;Path=path;Domain=domain;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",fields.getStringField("Set-Cookie"));
|
||||
Enumeration<String> e =fields.getValues("Set-Cookie");
|
||||
assertTrue(e.hasMoreElements());
|
||||
assertEquals("everything=value;Path=path;Domain=domain;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
|
||||
assertEquals("everything=value;Version=1;Path=path;Domain=domain;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
|
||||
assertFalse(e.hasMoreElements());
|
||||
assertEquals("Thu, 01 Jan 1970 00:00:00 GMT",fields.getStringField("Expires"));
|
||||
assertFalse(e.hasMoreElements());
|
||||
|
@ -293,9 +293,9 @@ public class HttpFieldsTest
|
|||
fields.addSetCookie("everything","value","domain2","path",0,"comment",true,true,0);
|
||||
e =fields.getValues("Set-Cookie");
|
||||
assertTrue(e.hasMoreElements());
|
||||
assertEquals("everything=other;Path=path;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=blah",e.nextElement());
|
||||
assertEquals("everything=other;Version=1;Path=path;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=blah",e.nextElement());
|
||||
assertTrue(e.hasMoreElements());
|
||||
assertEquals("everything=value;Path=path;Domain=domain2;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
|
||||
assertEquals("everything=value;Version=1;Path=path;Domain=domain2;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
|
||||
assertFalse(e.hasMoreElements());
|
||||
|
||||
//test cookies with same name, same path, one with domain, one without
|
||||
|
@ -304,9 +304,9 @@ public class HttpFieldsTest
|
|||
fields.addSetCookie("everything","value","","path",0,"comment",true,true,0);
|
||||
e =fields.getValues("Set-Cookie");
|
||||
assertTrue(e.hasMoreElements());
|
||||
assertEquals("everything=other;Path=path;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=blah",e.nextElement());
|
||||
assertEquals("everything=other;Version=1;Path=path;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=blah",e.nextElement());
|
||||
assertTrue(e.hasMoreElements());
|
||||
assertEquals("everything=value;Path=path;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
|
||||
assertEquals("everything=value;Version=1;Path=path;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
|
||||
assertFalse(e.hasMoreElements());
|
||||
|
||||
|
||||
|
@ -316,9 +316,9 @@ public class HttpFieldsTest
|
|||
fields.addSetCookie("everything","value","domain1","path2",0,"comment",true,true,0);
|
||||
e =fields.getValues("Set-Cookie");
|
||||
assertTrue(e.hasMoreElements());
|
||||
assertEquals("everything=other;Path=path1;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=blah",e.nextElement());
|
||||
assertEquals("everything=other;Version=1;Path=path1;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=blah",e.nextElement());
|
||||
assertTrue(e.hasMoreElements());
|
||||
assertEquals("everything=value;Path=path2;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
|
||||
assertEquals("everything=value;Version=1;Path=path2;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
|
||||
assertFalse(e.hasMoreElements());
|
||||
|
||||
//test cookies with same name, same domain, one with path, one without
|
||||
|
@ -327,9 +327,9 @@ public class HttpFieldsTest
|
|||
fields.addSetCookie("everything","value","domain1","",0,"comment",true,true,0);
|
||||
e =fields.getValues("Set-Cookie");
|
||||
assertTrue(e.hasMoreElements());
|
||||
assertEquals("everything=other;Path=path1;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=blah",e.nextElement());
|
||||
assertEquals("everything=other;Version=1;Path=path1;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=blah",e.nextElement());
|
||||
assertTrue(e.hasMoreElements());
|
||||
assertEquals("everything=value;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
|
||||
assertEquals("everything=value;Version=1;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
|
||||
assertFalse(e.hasMoreElements());
|
||||
|
||||
//test cookies same name only, no path, no domain
|
||||
|
@ -338,13 +338,13 @@ public class HttpFieldsTest
|
|||
fields.addSetCookie("everything","value","","",0,"comment",true,true,0);
|
||||
e =fields.getValues("Set-Cookie");
|
||||
assertTrue(e.hasMoreElements());
|
||||
assertEquals("everything=value;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
|
||||
assertEquals("everything=value;Version=1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
|
||||
assertFalse(e.hasMoreElements());
|
||||
|
||||
fields.clear();
|
||||
fields.addSetCookie("ev erything","va lue","do main","pa th",1,"co mment",true,true,2);
|
||||
fields.addSetCookie("ev erything","va lue","do main","pa th",1,"co mment",true,true,1);
|
||||
String setCookie=fields.getStringField("Set-Cookie");
|
||||
assertThat(setCookie,Matchers.startsWith("\"ev erything\"=\"va lue\";Path=\"pa th\";Domain=\"do main\";Expires="));
|
||||
assertThat(setCookie,Matchers.startsWith("\"ev erything\"=\"va lue\";Version=1;Path=\"pa th\";Domain=\"do main\";Expires="));
|
||||
assertThat(setCookie,Matchers.endsWith(" GMT;Max-Age=1;Secure;HttpOnly;Comment=\"co mment\""));
|
||||
|
||||
fields.clear();
|
||||
|
@ -376,7 +376,7 @@ public class HttpFieldsTest
|
|||
fields=new HttpFields();
|
||||
fields.addSetCookie("name","value==",null,null,-1,null,false,false,0);
|
||||
setCookie=fields.getStringField("Set-Cookie");
|
||||
assertEquals("name=\"value==\"",setCookie);
|
||||
assertEquals("name=\"value==\";Version=1",setCookie);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ import java.util.List;
|
|||
|
||||
import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Test;
|
||||
|
||||
public class HttpGeneratorServerTest
|
||||
|
@ -309,6 +310,54 @@ public class HttpGeneratorServerTest
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendServerXPoweredBy() throws Exception
|
||||
{
|
||||
ByteBuffer header = BufferUtil.allocate(8096);
|
||||
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), -1, 200, null, false);
|
||||
HttpFields fields = new HttpFields();
|
||||
fields.add(HttpHeader.SERVER,"SomeServer");
|
||||
fields.add(HttpHeader.X_POWERED_BY,"SomePower");
|
||||
ResponseInfo infoF = new ResponseInfo(HttpVersion.HTTP_1_1, fields, -1, 200, null, false);
|
||||
String head;
|
||||
|
||||
HttpGenerator gen = new HttpGenerator(true,true);
|
||||
gen.generateResponse(info, header, null, null, true);
|
||||
head = BufferUtil.toString(header);
|
||||
BufferUtil.clear(header);
|
||||
assertThat(head, containsString("HTTP/1.1 200 OK"));
|
||||
assertThat(head, containsString("Server: Jetty(9.x.x)"));
|
||||
assertThat(head, containsString("X-Powered-By: Jetty(9.x.x)"));
|
||||
gen.reset();
|
||||
gen.generateResponse(infoF, header, null, null, true);
|
||||
head = BufferUtil.toString(header);
|
||||
BufferUtil.clear(header);
|
||||
assertThat(head, containsString("HTTP/1.1 200 OK"));
|
||||
assertThat(head, not(containsString("Server: Jetty(9.x.x)")));
|
||||
assertThat(head, containsString("Server: SomeServer"));
|
||||
assertThat(head, containsString("X-Powered-By: Jetty(9.x.x)"));
|
||||
assertThat(head, containsString("X-Powered-By: SomePower"));
|
||||
gen.reset();
|
||||
|
||||
gen = new HttpGenerator(false,false);
|
||||
gen.generateResponse(info, header, null, null, true);
|
||||
head = BufferUtil.toString(header);
|
||||
BufferUtil.clear(header);
|
||||
assertThat(head, containsString("HTTP/1.1 200 OK"));
|
||||
assertThat(head, not(containsString("Server: Jetty(9.x.x)")));
|
||||
assertThat(head, not(containsString("X-Powered-By: Jetty(9.x.x)")));
|
||||
gen.reset();
|
||||
gen.generateResponse(infoF, header, null, null, true);
|
||||
head = BufferUtil.toString(header);
|
||||
BufferUtil.clear(header);
|
||||
assertThat(head, containsString("HTTP/1.1 200 OK"));
|
||||
assertThat(head, not(containsString("Server: Jetty(9.x.x)")));
|
||||
assertThat(head, containsString("Server: SomeServer"));
|
||||
assertThat(head, not(containsString("X-Powered-By: Jetty(9.x.x)")));
|
||||
assertThat(head, containsString("X-Powered-By: SomePower"));
|
||||
gen.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResponseNoContent() throws Exception
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.eclipse.jetty.http;
|
|||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
@ -29,6 +30,8 @@ import java.util.List;
|
|||
import org.eclipse.jetty.http.HttpParser.State;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -313,6 +316,115 @@ public class HttpParserTest
|
|||
assertEquals(9, _h);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodedHeader() throws Exception
|
||||
{
|
||||
ByteBuffer buffer=BufferUtil.allocate(4096);
|
||||
BufferUtil.flipToFill(buffer);
|
||||
BufferUtil.put(BufferUtil.toBuffer("GET "),buffer);
|
||||
buffer.put("/foo/\u0690/".getBytes(StringUtil.__UTF8_CHARSET));
|
||||
BufferUtil.put(BufferUtil.toBuffer(" HTTP/1.0\r\n"),buffer);
|
||||
BufferUtil.put(BufferUtil.toBuffer("Header1: "),buffer);
|
||||
buffer.put("\u00e6 \u00e6".getBytes(StringUtil.__ISO_8859_1_CHARSET));
|
||||
BufferUtil.put(BufferUtil.toBuffer(" \r\n\r\n"),buffer);
|
||||
BufferUtil.flipToFlush(buffer,0);
|
||||
|
||||
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
|
||||
HttpParser parser= new HttpParser(handler);
|
||||
parseAll(parser,buffer);
|
||||
|
||||
assertEquals("GET", _methodOrVersion);
|
||||
assertEquals("/foo/\u0690/", _uriOrStatus);
|
||||
assertEquals("HTTP/1.0", _versionOrReason);
|
||||
assertEquals("Header1", _hdr[0]);
|
||||
assertEquals("\u00e6 \u00e6", _val[0]);
|
||||
assertEquals(0, _h);
|
||||
assertEquals(null,_bad);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testBadMethodEncoding() throws Exception
|
||||
{
|
||||
ByteBuffer buffer= BufferUtil.toBuffer(
|
||||
"G\u00e6T / HTTP/1.0\r\nHeader0: value0\r\n\n\n");
|
||||
|
||||
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
|
||||
HttpParser parser= new HttpParser(handler);
|
||||
parseAll(parser,buffer);
|
||||
assertThat(_bad,Matchers.notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadVersionEncoding() throws Exception
|
||||
{
|
||||
ByteBuffer buffer= BufferUtil.toBuffer(
|
||||
"GET / H\u00e6P/1.0\r\nHeader0: value0\r\n\n\n");
|
||||
|
||||
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
|
||||
HttpParser parser= new HttpParser(handler);
|
||||
parseAll(parser,buffer);
|
||||
assertThat(_bad,Matchers.notNullValue());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testBadHeaderEncoding() throws Exception
|
||||
{
|
||||
ByteBuffer buffer= BufferUtil.toBuffer(
|
||||
"GET / HTTP/1.0\r\nH\u00e6der0: value0\r\n\n\n");
|
||||
|
||||
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
|
||||
HttpParser parser= new HttpParser(handler);
|
||||
parseAll(parser,buffer);
|
||||
assertThat(_bad,Matchers.notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonStrict() throws Exception
|
||||
{
|
||||
ByteBuffer buffer= BufferUtil.toBuffer(
|
||||
"get / http/1.0\015\012" +
|
||||
"HOST: localhost\015\012" +
|
||||
"cOnNeCtIoN: ClOsE\015\012"+
|
||||
"\015\012");
|
||||
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
|
||||
HttpParser parser= new HttpParser(handler,-1,false);
|
||||
parseAll(parser,buffer);
|
||||
|
||||
assertEquals("GET", _methodOrVersion);
|
||||
assertEquals("/", _uriOrStatus);
|
||||
assertEquals("HTTP/1.0", _versionOrReason);
|
||||
assertEquals("Host", _hdr[0]);
|
||||
assertEquals("localhost", _val[0]);
|
||||
assertEquals("Connection", _hdr[1]);
|
||||
assertEquals("close", _val[1]);
|
||||
assertEquals(1, _h);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStrict() throws Exception
|
||||
{
|
||||
ByteBuffer buffer= BufferUtil.toBuffer(
|
||||
"gEt / http/1.0\015\012" +
|
||||
"HOST: localhost\015\012" +
|
||||
"cOnNeCtIoN: ClOsE\015\012"+
|
||||
"\015\012");
|
||||
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
|
||||
HttpParser parser= new HttpParser(handler,-1,true);
|
||||
parseAll(parser,buffer);
|
||||
|
||||
assertEquals("gEt", _methodOrVersion);
|
||||
assertEquals("/", _uriOrStatus);
|
||||
assertEquals("HTTP/1.0", _versionOrReason);
|
||||
assertEquals("HOST", _hdr[0]);
|
||||
assertEquals("localhost", _val[0]);
|
||||
assertEquals("cOnNeCtIoN", _hdr[1]);
|
||||
assertEquals("ClOsE", _val[1]);
|
||||
assertEquals(1, _h);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSplitHeaderParse() throws Exception
|
||||
{
|
||||
|
@ -426,7 +538,6 @@ public class HttpParserTest
|
|||
+ "\015\012"
|
||||
+ "0123456789\015\012");
|
||||
|
||||
|
||||
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
|
||||
HttpParser parser= new HttpParser(handler);
|
||||
parser.parseNext(buffer);
|
||||
|
@ -1125,7 +1236,7 @@ public class HttpParserTest
|
|||
@Override
|
||||
public void badMessage(int status, String reason)
|
||||
{
|
||||
_bad=reason;
|
||||
_bad=reason==null?(""+status):reason;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -57,7 +57,7 @@ public class NetworkTrafficSelectChannelEndPoint extends SelectChannelEndPoint
|
|||
if (b.hasRemaining())
|
||||
{
|
||||
int position = b.position();
|
||||
flushed|=super.flush(b);
|
||||
flushed&=super.flush(b);
|
||||
int l=b.position()-position;
|
||||
notifyOutgoing(b, position, l);
|
||||
if (!flushed)
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
package org.eclipse.jetty.io.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.SocketException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.util.Arrays;
|
||||
|
@ -39,7 +38,6 @@ import org.eclipse.jetty.io.EofException;
|
|||
import org.eclipse.jetty.io.FillInterest;
|
||||
import org.eclipse.jetty.io.RuntimeIOException;
|
||||
import org.eclipse.jetty.io.SelectChannelEndPoint;
|
||||
import org.eclipse.jetty.io.SocketBased;
|
||||
import org.eclipse.jetty.io.WriteFlusher;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
|
@ -110,17 +108,20 @@ public class SslConnection extends AbstractConnection
|
|||
this._sslEngine = sslEngine;
|
||||
this._decryptedEndPoint = newDecryptedEndPoint();
|
||||
|
||||
if (endPoint instanceof SocketBased)
|
||||
{
|
||||
try
|
||||
{
|
||||
((SocketBased)endPoint).getSocket().setSoLinger(true, 30000);
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
throw new RuntimeIOException(e);
|
||||
}
|
||||
}
|
||||
// commented out for now as it might cause native code being stuck in preClose0.
|
||||
// See: https://java.net/jira/browse/GRIZZLY-547
|
||||
|
||||
// if (endPoint instanceof SocketBased)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// ((SocketBased)endPoint).getSocket().setSoLinger(true, 30000);
|
||||
// }
|
||||
// catch (SocketException e)
|
||||
// {
|
||||
// throw new RuntimeIOException(e);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
protected DecryptedEndPoint newDecryptedEndPoint()
|
||||
|
|
|
@ -20,10 +20,10 @@ package org.eclipse.jetty.jaas.spi;
|
|||
|
||||
import java.security.Principal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
|
@ -45,7 +45,7 @@ public class PropertyFileLoginModule extends AbstractLoginModule
|
|||
|
||||
private static final Logger LOG = Log.getLogger(PropertyFileLoginModule.class);
|
||||
|
||||
private static Map<String, PropertyUserStore> _propertyUserStores = new HashMap<String, PropertyUserStore>();
|
||||
private static ConcurrentHashMap<String, PropertyUserStore> _propertyUserStores = new ConcurrentHashMap<String, PropertyUserStore>();
|
||||
|
||||
private int _refreshInterval = 0;
|
||||
private String _filename = DEFAULT_FILENAME;
|
||||
|
@ -68,33 +68,37 @@ public class PropertyFileLoginModule extends AbstractLoginModule
|
|||
|
||||
private void setupPropertyUserStore(Map<String, ?> options)
|
||||
{
|
||||
parseConfig(options);
|
||||
|
||||
if (_propertyUserStores.get(_filename) == null)
|
||||
{
|
||||
parseConfig(options);
|
||||
PropertyUserStore propertyUserStore = new PropertyUserStore();
|
||||
propertyUserStore.setConfig(_filename);
|
||||
propertyUserStore.setRefreshInterval(_refreshInterval);
|
||||
|
||||
PropertyUserStore _propertyUserStore = new PropertyUserStore();
|
||||
_propertyUserStore.setConfig(_filename);
|
||||
_propertyUserStore.setRefreshInterval(_refreshInterval);
|
||||
LOG.debug("setupPropertyUserStore: Starting new PropertyUserStore. PropertiesFile: " + _filename + " refreshInterval: " + _refreshInterval);
|
||||
|
||||
try
|
||||
PropertyUserStore prev = _propertyUserStores.putIfAbsent(_filename, propertyUserStore);
|
||||
if (prev == null)
|
||||
{
|
||||
_propertyUserStore.start();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn("Exception while starting propertyUserStore: ",e);
|
||||
}
|
||||
LOG.debug("setupPropertyUserStore: Starting new PropertyUserStore. PropertiesFile: " + _filename + " refreshInterval: " + _refreshInterval);
|
||||
|
||||
_propertyUserStores.put(_filename,_propertyUserStore);
|
||||
try
|
||||
{
|
||||
propertyUserStore.start();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn("Exception while starting propertyUserStore: ",e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parseConfig(Map<String, ?> options)
|
||||
{
|
||||
_filename = (String)options.get("file") != null?(String)options.get("file"):DEFAULT_FILENAME;
|
||||
String refreshIntervalString = (String)options.get("refreshInterval");
|
||||
_refreshInterval = refreshIntervalString == null?_refreshInterval:Integer.parseInt(refreshIntervalString);
|
||||
String tmp = (String)options.get("file");
|
||||
_filename = (tmp == null? DEFAULT_FILENAME : tmp);
|
||||
tmp = (String)options.get("refreshInterval");
|
||||
_refreshInterval = (tmp == null?_refreshInterval:Integer.parseInt(tmp));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -75,7 +75,14 @@ public class ContextFactory implements ObjectFactory
|
|||
* Threadlocal for injecting a context to use
|
||||
* instead of looking up the map.
|
||||
*/
|
||||
private static final ThreadLocal __threadContext = new ThreadLocal();
|
||||
private static final ThreadLocal<Context> __threadContext = new ThreadLocal<Context>();
|
||||
|
||||
/**
|
||||
* Threadlocal for setting a classloader which must be used
|
||||
* when finding the comp context.
|
||||
*/
|
||||
private static final ThreadLocal<ClassLoader> __threadClassLoader = new ThreadLocal<ClassLoader>();
|
||||
|
||||
|
||||
|
||||
/**
|
||||
|
@ -107,10 +114,25 @@ public class ContextFactory implements ObjectFactory
|
|||
return ctx;
|
||||
}
|
||||
|
||||
//See if there is a classloader to use for finding the comp context
|
||||
//Don't use its parent hierarchy if set.
|
||||
ClassLoader loader = (ClassLoader)__threadClassLoader.get();
|
||||
if (loader != null)
|
||||
{
|
||||
if (__log.isDebugEnabled() && loader != null) __log.debug("Using threadlocal classloader");
|
||||
ctx = getContextForClassLoader(loader);
|
||||
if (ctx == null)
|
||||
{
|
||||
ctx = newNamingContext(obj, loader, env, name, nameCtx);
|
||||
__contextMap.put (loader, ctx);
|
||||
if(__log.isDebugEnabled())__log.debug("Made context "+name.get(0)+" for classloader: "+loader);
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
ClassLoader tccl = Thread.currentThread().getContextClassLoader();
|
||||
ClassLoader loader = tccl;
|
||||
//If the thread context classloader is set, then try its hierarchy to find a matching context
|
||||
ClassLoader tccl = Thread.currentThread().getContextClassLoader();
|
||||
loader = tccl;
|
||||
if (loader != null)
|
||||
{
|
||||
if (__log.isDebugEnabled() && loader != null) __log.debug("Trying thread context classloader");
|
||||
|
@ -194,25 +216,34 @@ public class ContextFactory implements ObjectFactory
|
|||
|
||||
/**
|
||||
* Associate the given Context with the current thread.
|
||||
* resetComponentContext method should be called to reset the context.
|
||||
* disassociate method should be called to reset the context.
|
||||
* @param ctx the context to associate to the current thread.
|
||||
* @return the previous context associated on the thread (can be null)
|
||||
*/
|
||||
public static Context setComponentContext(final Context ctx)
|
||||
public static Context associateContext(final Context ctx)
|
||||
{
|
||||
Context previous = (Context)__threadContext.get();
|
||||
__threadContext.set(ctx);
|
||||
return previous;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set back the context with the given value.
|
||||
* Don't return the previous context, use setComponentContext() method for this.
|
||||
* @param ctx the context to associate to the current thread.
|
||||
*/
|
||||
public static void resetComponentContext(final Context ctx)
|
||||
public static void disassociateContext(final Context ctx)
|
||||
{
|
||||
__threadContext.set(ctx);
|
||||
__threadContext.remove();
|
||||
}
|
||||
|
||||
|
||||
public static ClassLoader associateClassLoader(final ClassLoader loader)
|
||||
{
|
||||
ClassLoader prev = (ClassLoader)__threadClassLoader.get();
|
||||
__threadClassLoader.set(loader);
|
||||
return prev;
|
||||
}
|
||||
|
||||
|
||||
public static void disassociateClassLoader ()
|
||||
{
|
||||
__threadClassLoader.remove();
|
||||
}
|
||||
|
||||
public static void dump(Appendable out, String indent) throws IOException
|
||||
|
|
|
@ -120,6 +120,11 @@
|
|||
<artifactId>jetty-jsp</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-continuation</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<reporting>
|
||||
<plugins>
|
||||
|
|
|
@ -203,6 +203,12 @@ public abstract class AbstractJettyMojo extends AbstractMojo
|
|||
*/
|
||||
protected String stopKey;
|
||||
|
||||
/**
|
||||
* Use the dump() facility of jetty to print out the server configuration to logging
|
||||
*
|
||||
* @parameter expression"${dumponStart}" default-value="false"
|
||||
*/
|
||||
protected boolean dumpOnStart;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
|
@ -272,6 +278,7 @@ public abstract class AbstractJettyMojo extends AbstractMojo
|
|||
*/
|
||||
protected List pluginArtifacts;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A ServerConnector to use.
|
||||
|
@ -558,6 +565,11 @@ public abstract class AbstractJettyMojo extends AbstractMojo
|
|||
getLog().info("Started Jetty Server");
|
||||
|
||||
|
||||
if ( dumpOnStart )
|
||||
{
|
||||
getLog().info(this.server.dump());
|
||||
}
|
||||
|
||||
// start the scanner thread (if necessary) on the main webapp
|
||||
configureScanner ();
|
||||
startScanner();
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
package org.eclipse.jetty.maven.plugin;
|
||||
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.LineNumberReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.ConnectException;
|
||||
import java.net.InetAddress;
|
||||
|
@ -54,6 +56,13 @@ public class JettyStopMojo extends AbstractMojo
|
|||
* @required
|
||||
*/
|
||||
protected String stopKey;
|
||||
|
||||
/**
|
||||
* Max time in seconds that the plugin will wait for confirmation that jetty has stopped.
|
||||
* @parameter
|
||||
*/
|
||||
protected int stopWait;
|
||||
|
||||
|
||||
public void execute() throws MojoExecutionException, MojoFailureException
|
||||
{
|
||||
|
@ -66,10 +75,29 @@ public class JettyStopMojo extends AbstractMojo
|
|||
{
|
||||
Socket s=new Socket(InetAddress.getByName("127.0.0.1"),stopPort);
|
||||
s.setSoLinger(false, 0);
|
||||
|
||||
|
||||
OutputStream out=s.getOutputStream();
|
||||
out.write((stopKey+"\r\nstop\r\n").getBytes());
|
||||
out.flush();
|
||||
|
||||
if (stopWait > 0)
|
||||
{
|
||||
s.setSoTimeout(stopWait * 1000);
|
||||
s.getInputStream();
|
||||
|
||||
System.err.printf("Waiting %d seconds for jetty to stop%n",stopWait);
|
||||
LineNumberReader lin = new LineNumberReader(new InputStreamReader(s.getInputStream()));
|
||||
String response;
|
||||
boolean stopped = false;
|
||||
while (!stopped && ((response = lin.readLine()) != null))
|
||||
{
|
||||
if ("Stopped".equals(response))
|
||||
{
|
||||
stopped = true;
|
||||
System.err.println("Server reports itself as Stopped");
|
||||
}
|
||||
}
|
||||
}
|
||||
s.close();
|
||||
}
|
||||
catch (ConnectException e)
|
||||
|
|
|
@ -450,6 +450,7 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
|
|||
/**
|
||||
* is the session id known to mongo, and is it valid
|
||||
*/
|
||||
@Override
|
||||
public boolean idInUse(String sessionId)
|
||||
{
|
||||
/*
|
||||
|
@ -473,6 +474,7 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public void addSession(HttpSession session)
|
||||
{
|
||||
if (session == null)
|
||||
|
@ -494,6 +496,7 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public void removeSession(HttpSession session)
|
||||
{
|
||||
if (session == null)
|
||||
|
@ -508,6 +511,7 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public void invalidateAll(String sessionId)
|
||||
{
|
||||
synchronized (_sessionsIds)
|
||||
|
@ -520,7 +524,7 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
|
|||
Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
|
||||
for (int i=0; contexts!=null && i<contexts.length; i++)
|
||||
{
|
||||
SessionHandler sessionHandler = (SessionHandler)((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
|
||||
SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
|
||||
if (sessionHandler != null)
|
||||
{
|
||||
SessionManager manager = sessionHandler.getSessionManager();
|
||||
|
@ -532,26 +536,7 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
// TODO not sure if this is correct
|
||||
public String getClusterId(String nodeId)
|
||||
{
|
||||
int dot=nodeId.lastIndexOf('.');
|
||||
return (dot>0)?nodeId.substring(0,dot):nodeId;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
// TODO not sure if this is correct
|
||||
public String getNodeId(String clusterId, HttpServletRequest request)
|
||||
{
|
||||
if (_workerName!=null)
|
||||
return clusterId+'.'+_workerName;
|
||||
|
||||
return clusterId;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
|
@ -569,7 +554,7 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
|
|||
Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
|
||||
for (int i=0; contexts!=null && i<contexts.length; i++)
|
||||
{
|
||||
SessionHandler sessionHandler = (SessionHandler)((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
|
||||
SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
|
||||
if (sessionHandler != null)
|
||||
{
|
||||
SessionManager manager = sessionHandler.getSessionManager();
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 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.plus.servlet;
|
||||
|
||||
import org.eclipse.jetty.plus.annotation.InjectionCollection;
|
||||
import org.eclipse.jetty.plus.annotation.LifeCycleCallbackCollection;
|
||||
|
||||
/**
|
||||
* ServletHandler
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class ServletHandler extends org.eclipse.jetty.servlet.ServletHandler
|
||||
{
|
||||
|
||||
private InjectionCollection _injections = null;
|
||||
private LifeCycleCallbackCollection _callbacks = null;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return the callbacks
|
||||
*/
|
||||
public LifeCycleCallbackCollection getCallbacks()
|
||||
{
|
||||
return _callbacks;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param callbacks the callbacks to set
|
||||
*/
|
||||
public void setCallbacks(LifeCycleCallbackCollection callbacks)
|
||||
{
|
||||
this._callbacks = callbacks;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return the injections
|
||||
*/
|
||||
public InjectionCollection getInjections()
|
||||
{
|
||||
return _injections;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param injections the injections to set
|
||||
*/
|
||||
public void setInjections(InjectionCollection injections)
|
||||
{
|
||||
this._injections = injections;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 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.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
/**
|
||||
* Jetty Plus : Servlet Handler for Limited Additional JEE support
|
||||
*/
|
||||
package org.eclipse.jetty.plus.servlet;
|
||||
|
|
@ -31,6 +31,7 @@ import javax.naming.Name;
|
|||
import javax.naming.NameNotFoundException;
|
||||
import javax.naming.NamingException;
|
||||
|
||||
import org.eclipse.jetty.jndi.ContextFactory;
|
||||
import org.eclipse.jetty.jndi.NamingContext;
|
||||
import org.eclipse.jetty.jndi.NamingUtil;
|
||||
import org.eclipse.jetty.jndi.local.localContextRoot;
|
||||
|
@ -147,6 +148,7 @@ public class EnvConfiguration extends AbstractConfiguration
|
|||
//get rid of any bindings for comp/env for webapp
|
||||
ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
|
||||
Thread.currentThread().setContextClassLoader(context.getClassLoader());
|
||||
ContextFactory.associateClassLoader(context.getClassLoader());
|
||||
try
|
||||
{
|
||||
Context ic = new InitialContext();
|
||||
|
@ -170,6 +172,7 @@ public class EnvConfiguration extends AbstractConfiguration
|
|||
}
|
||||
finally
|
||||
{
|
||||
ContextFactory.disassociateClassLoader();
|
||||
Thread.currentThread().setContextClassLoader(oldLoader);
|
||||
}
|
||||
}
|
||||
|
@ -251,6 +254,7 @@ public class EnvConfiguration extends AbstractConfiguration
|
|||
{
|
||||
ClassLoader old_loader = Thread.currentThread().getContextClassLoader();
|
||||
Thread.currentThread().setContextClassLoader(wac.getClassLoader());
|
||||
ContextFactory.associateClassLoader(wac.getClassLoader());
|
||||
try
|
||||
{
|
||||
Context context = new InitialContext();
|
||||
|
@ -259,8 +263,9 @@ public class EnvConfiguration extends AbstractConfiguration
|
|||
}
|
||||
finally
|
||||
{
|
||||
Thread.currentThread().setContextClassLoader(old_loader);
|
||||
}
|
||||
ContextFactory.disassociateClassLoader();
|
||||
Thread.currentThread().setContextClassLoader(old_loader);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Bound
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.eclipse.jetty.client.api.ContentResponse;
|
|||
import org.eclipse.jetty.server.NetworkConnector;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.session.AbstractSessionIdManager;
|
||||
import org.eclipse.jetty.server.session.HashSessionIdManager;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
|
@ -99,7 +100,7 @@ public class BalancerServletTest
|
|||
|
||||
if (nodeName != null)
|
||||
{
|
||||
HashSessionIdManager sessionIdManager = new HashSessionIdManager();
|
||||
AbstractSessionIdManager sessionIdManager = new HashSessionIdManager();
|
||||
sessionIdManager.setWorkerName(nodeName);
|
||||
server.setSessionIdManager(sessionIdManager);
|
||||
}
|
||||
|
|
|
@ -20,11 +20,13 @@ package org.eclipse.jetty.proxy;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.Socket;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
@ -55,6 +57,8 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
|
|||
import org.hamcrest.Matchers;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -116,14 +120,20 @@ public class ProxyTunnellingTest
|
|||
|
||||
protected void stopServer() throws Exception
|
||||
{
|
||||
server.stop();
|
||||
server.join();
|
||||
if (server != null)
|
||||
{
|
||||
server.stop();
|
||||
server.join();
|
||||
}
|
||||
}
|
||||
|
||||
protected void stopProxy() throws Exception
|
||||
{
|
||||
proxy.stop();
|
||||
proxy.join();
|
||||
if (proxy != null)
|
||||
{
|
||||
proxy.stop();
|
||||
proxy.join();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -364,6 +374,43 @@ public class ProxyTunnellingTest
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore // to delicate to rely on external proxy.
|
||||
public void testExternalProxy() throws Exception
|
||||
{
|
||||
// Free proxy server obtained from http://hidemyass.com/proxy-list/
|
||||
String proxyHost = "81.208.25.53";
|
||||
int proxyPort = 3128;
|
||||
try
|
||||
{
|
||||
new Socket(proxyHost, proxyPort).close();
|
||||
}
|
||||
catch (IOException x)
|
||||
{
|
||||
Assume.assumeNoException(x);
|
||||
}
|
||||
|
||||
SslContextFactory sslContextFactory = new SslContextFactory();
|
||||
sslContextFactory.start();
|
||||
|
||||
HttpClient httpClient = new HttpClient(sslContextFactory);
|
||||
httpClient.setProxyConfiguration(new ProxyConfiguration(proxyHost, proxyPort));
|
||||
httpClient.start();
|
||||
|
||||
try
|
||||
{
|
||||
ContentResponse response = httpClient.newRequest("https://www.google.com")
|
||||
// Use a longer timeout, sometimes the proxy takes a while to answer
|
||||
.timeout(20, TimeUnit.SECONDS)
|
||||
.send();
|
||||
assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
}
|
||||
finally
|
||||
{
|
||||
httpClient.stop();
|
||||
}
|
||||
}
|
||||
|
||||
private static class ServerHandler extends AbstractHandler
|
||||
{
|
||||
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
|
||||
|
|
|
@ -19,20 +19,21 @@
|
|||
package org.eclipse.jetty.rewrite.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.HttpURI;
|
||||
import org.eclipse.jetty.http.PathMap;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
|
||||
/**
|
||||
* Rewrite the URI by replacing the matched {@link PathMap} path with a fixed string.
|
||||
* Rewrite the URI by replacing the matched {@link PathMap} path with a fixed string.
|
||||
*/
|
||||
public class RewritePatternRule extends PatternRule implements Rule.ApplyURI
|
||||
{
|
||||
private String _replacement;
|
||||
private String _query;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public RewritePatternRule()
|
||||
|
@ -44,32 +45,63 @@ public class RewritePatternRule extends PatternRule implements Rule.ApplyURI
|
|||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Whenever a match is found, it replaces with this value.
|
||||
*
|
||||
* @param value the replacement string.
|
||||
*
|
||||
* @param replacement the replacement string.
|
||||
*/
|
||||
public void setReplacement(String value)
|
||||
public void setReplacement(String replacement)
|
||||
{
|
||||
_replacement = value;
|
||||
String[] split = replacement.split("\\?", 2);
|
||||
_replacement = split[0];
|
||||
_query = split.length == 2 ? split[1] : null;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.eclipse.jetty.server.handler.rules.RuleBase#apply(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
|
||||
*
|
||||
* @see org.eclipse.jetty.server.handler.rules.RuleBase#apply(javax.servlet.http.HttpServletRequest,
|
||||
* javax.servlet.http.HttpServletResponse)
|
||||
*/
|
||||
@Override
|
||||
public String apply(String target, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
target = URIUtil.addPaths(_replacement, PathMap.pathInfo(_pattern,target));
|
||||
target = URIUtil.addPaths(_replacement, PathMap.pathInfo(_pattern, target));
|
||||
return target;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* This method will add _query to the requests's queryString and also combine it with existing queryStrings in
|
||||
* the request. However it won't take care for duplicate. E.g. if request.getQueryString contains a parameter
|
||||
* "param1 = true" and _query will contain "param1=false" the result will be param1=true¶m1=false.
|
||||
* To cover this use case some more complex pattern matching is necessary. We can implement this if there's use
|
||||
* cases.
|
||||
*
|
||||
* @param request
|
||||
* @param oldTarget
|
||||
* @param newTarget
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
public void applyURI(Request request, String oldTarget, String newTarget) throws IOException
|
||||
public void applyURI(Request request, String oldTarget, String newTarget) throws IOException
|
||||
{
|
||||
String uri = URIUtil.addPaths(_replacement, PathMap.pathInfo(_pattern,request.getRequestURI()));
|
||||
request.setRequestURI(uri);
|
||||
if (_query == null)
|
||||
{
|
||||
request.setRequestURI(newTarget);
|
||||
}
|
||||
else
|
||||
{
|
||||
String queryString = request.getQueryString();
|
||||
if (queryString != null)
|
||||
queryString = queryString + "&" + _query;
|
||||
else
|
||||
queryString = _query;
|
||||
HttpURI uri = new HttpURI(newTarget + "?" + queryString);
|
||||
request.setUri(uri);
|
||||
request.setRequestURI(newTarget);
|
||||
request.setQueryString(queryString);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
|
@ -18,23 +18,25 @@
|
|||
|
||||
package org.eclipse.jetty.rewrite.handler;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.jetty.http.HttpURI;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class RewritePatternRuleTest extends AbstractRuleTestCase
|
||||
{
|
||||
private String[][] _tests=
|
||||
{
|
||||
{"/foo/bar","/","/replace"},
|
||||
{"/foo/bar","/*","/replace/foo/bar"},
|
||||
{"/foo/bar","/foo/*","/replace/bar"},
|
||||
{"/foo/bar","/foo/bar","/replace"},
|
||||
{"/foo/bar.txt","*.txt","/replace"},
|
||||
};
|
||||
private String[][] _tests =
|
||||
{
|
||||
{"/foo/bar", "/", "/replace"},
|
||||
{"/foo/bar", "/*", "/replace/foo/bar"},
|
||||
{"/foo/bar", "/foo/*", "/replace/bar"},
|
||||
{"/foo/bar", "/foo/bar", "/replace"},
|
||||
{"/foo/bar.txt", "*.txt", "/replace"},
|
||||
};
|
||||
private RewritePatternRule _rule;
|
||||
|
||||
@Before
|
||||
|
@ -46,13 +48,82 @@ public class RewritePatternRuleTest extends AbstractRuleTestCase
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testRequestUriEnabled() throws IOException
|
||||
public void testMatchAndApplyAndApplyURI() throws IOException
|
||||
{
|
||||
for (String[] test : _tests)
|
||||
{
|
||||
_rule.setPattern(test[1]);
|
||||
String result = _rule.matchAndApply(test[0], _request, _response);
|
||||
assertEquals(test[1], test[2], result);
|
||||
assertThat(test[1], test[2], is(result));
|
||||
|
||||
_rule.applyURI(_request, null, result);
|
||||
assertThat(_request.getRequestURI(), is(test[2]));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplacementWithQueryString() throws IOException
|
||||
{
|
||||
String replacement = "/replace?given=param";
|
||||
String[] split = replacement.split("\\?", 2);
|
||||
String path = split[0];
|
||||
String queryString = split[1];
|
||||
|
||||
RewritePatternRule rewritePatternRule = new RewritePatternRule();
|
||||
rewritePatternRule.setPattern("/old/context");
|
||||
rewritePatternRule.setReplacement(replacement);
|
||||
|
||||
String result = rewritePatternRule.matchAndApply("/old/context", _request, _response);
|
||||
assertThat(result, is(path));
|
||||
|
||||
rewritePatternRule.applyURI(_request, null, result);
|
||||
assertThat("queryString matches expected", _request.getQueryString(), is(queryString));
|
||||
assertThat("request URI matches expected", _request.getRequestURI(), is(path));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestWithQueryString() throws IOException
|
||||
{
|
||||
String replacement = "/replace";
|
||||
String queryString = "request=parameter";
|
||||
_request.setUri(new HttpURI("/old/context"));
|
||||
_request.setQueryString(queryString);
|
||||
|
||||
RewritePatternRule rewritePatternRule = new RewritePatternRule();
|
||||
rewritePatternRule.setPattern("/old/context");
|
||||
rewritePatternRule.setReplacement(replacement);
|
||||
|
||||
String result = rewritePatternRule.matchAndApply("/old/context", _request, _response);
|
||||
assertThat("result matches expected", result, is(replacement));
|
||||
|
||||
rewritePatternRule.applyURI(_request, null, result);
|
||||
assertThat("queryString matches expected", _request.getQueryString(), is(queryString));
|
||||
assertThat("request URI matches expected", _request.getRequestURI(), is(replacement));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestAndReplacementWithQueryString() throws IOException
|
||||
{
|
||||
String requestQueryString = "request=parameter";
|
||||
String replacement = "/replace?given=param";
|
||||
String[] split = replacement.split("\\?", 2);
|
||||
String path = split[0];
|
||||
String queryString = split[1];
|
||||
_request.setUri(new HttpURI("/old/context"));
|
||||
_request.setQueryString(requestQueryString);
|
||||
|
||||
RewritePatternRule rewritePatternRule = new RewritePatternRule();
|
||||
rewritePatternRule.setPattern("/old/context");
|
||||
rewritePatternRule.setReplacement(replacement);
|
||||
|
||||
String result = rewritePatternRule.matchAndApply("/old/context", _request, _response);
|
||||
assertThat(result, is(path));
|
||||
|
||||
rewritePatternRule.applyURI(_request, null, result);
|
||||
assertThat("queryString matches expected", _request.getQueryString(),
|
||||
is(requestQueryString + "&" + queryString));
|
||||
assertThat("request URI matches expected", _request.getRequestURI(), is(path));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -54,13 +54,19 @@ public class DefaultUserIdentity implements UserIdentity
|
|||
}
|
||||
|
||||
public boolean isUserInRole(String role, Scope scope)
|
||||
{
|
||||
{
|
||||
if (scope!=null && scope.getRoleRefMap()!=null)
|
||||
role=scope.getRoleRefMap().get(role);
|
||||
|
||||
{
|
||||
String mappedRole = scope.getRoleRefMap().get(role);
|
||||
if (mappedRole != null)
|
||||
role = mappedRole;
|
||||
}
|
||||
|
||||
for (String r :_roles)
|
||||
{
|
||||
if (r.equals(role))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -212,6 +212,9 @@ public abstract class MappedLoginService extends AbstractLifeCycle implements Lo
|
|||
*/
|
||||
public UserIdentity login(String username, Object credentials)
|
||||
{
|
||||
if (username == null)
|
||||
return null;
|
||||
|
||||
UserIdentity user = _users.get(username);
|
||||
|
||||
if (user==null)
|
||||
|
|
|
@ -116,6 +116,9 @@ public class DeferredAuthentication implements Authentication.Deferred
|
|||
@Override
|
||||
public Authentication login(String username, Object password, ServletRequest request)
|
||||
{
|
||||
if (username == null)
|
||||
return null;
|
||||
|
||||
UserIdentity identity = _authenticator.login(username, password, request);
|
||||
if (identity != null)
|
||||
{
|
||||
|
|
|
@ -25,6 +25,7 @@ import javax.servlet.http.Cookie;
|
|||
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.PathMap;
|
||||
import org.eclipse.jetty.server.handler.StatisticsHandler;
|
||||
import org.eclipse.jetty.util.DateCache;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
|
@ -59,7 +60,6 @@ public abstract class AbstractNCSARequestLog extends AbstractLifeCycle implement
|
|||
private boolean _logLatency = false;
|
||||
private boolean _logCookies = false;
|
||||
private boolean _logServer = false;
|
||||
private boolean _logDispatch = false;
|
||||
private boolean _preferProxiedForAddress;
|
||||
private transient DateCache _logDateCache;
|
||||
private String _logDateFormat = "dd/MMM/yyyy:HH:mm:ss Z";
|
||||
|
@ -193,17 +193,10 @@ public abstract class AbstractNCSARequestLog extends AbstractLifeCycle implement
|
|||
}
|
||||
}
|
||||
|
||||
if (_logDispatch || _logLatency)
|
||||
if (_logLatency)
|
||||
{
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
if (_logDispatch)
|
||||
{
|
||||
long d = request.getDispatchTime();
|
||||
buf.append(' ');
|
||||
buf.append(now - (d==0 ? request.getTimeStamp():d));
|
||||
}
|
||||
|
||||
if (_logLatency)
|
||||
{
|
||||
buf.append(' ');
|
||||
|
@ -340,24 +333,18 @@ public abstract class AbstractNCSARequestLog extends AbstractLifeCycle implement
|
|||
}
|
||||
|
||||
/**
|
||||
* Controls logging of the request dispatch time
|
||||
*
|
||||
* @param value true - request dispatch time will be logged
|
||||
* false - request dispatch time will not be logged
|
||||
* @deprecated use {@link StatisticsHandler}
|
||||
*/
|
||||
public void setLogDispatch(boolean value)
|
||||
{
|
||||
_logDispatch = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve request dispatch time logging flag
|
||||
*
|
||||
* @return value of the flag
|
||||
* @deprecated use {@link StatisticsHandler}
|
||||
*/
|
||||
public boolean isLogDispatch()
|
||||
{
|
||||
return _logDispatch;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -38,7 +38,7 @@ public class AsyncContextState implements AsyncContext
|
|||
_state=state;
|
||||
}
|
||||
|
||||
private HttpChannelState state()
|
||||
HttpChannelState state()
|
||||
{
|
||||
HttpChannelState state=_state;
|
||||
if (state==null)
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.net.InetSocketAddress;
|
|||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.RequestDispatcher;
|
||||
|
||||
|
@ -330,16 +331,11 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
|
|||
}
|
||||
finally
|
||||
{
|
||||
next=Next.RECYCLE;
|
||||
_request.setHandled(true);
|
||||
_transport.completed();
|
||||
}
|
||||
}
|
||||
|
||||
if (next==Next.RECYCLE)
|
||||
{
|
||||
_request.setHandled(true);
|
||||
_transport.completed();
|
||||
}
|
||||
|
||||
LOG.debug("{} handle exit, result {}", this, next);
|
||||
|
||||
return next!=Next.WAIT;
|
||||
|
|
|
@ -71,13 +71,12 @@ public class HttpChannelState
|
|||
COMPLETING, // Request is completable
|
||||
COMPLETED // Request is complete
|
||||
}
|
||||
|
||||
|
||||
public enum Next
|
||||
{
|
||||
CONTINUE, // Continue handling the channel
|
||||
WAIT, // Wait for further events
|
||||
COMPLETE, // Complete the channel
|
||||
RECYCLE, // Channel is completed
|
||||
COMPLETE // Complete the channel
|
||||
}
|
||||
|
||||
private final HttpChannel<?> _channel;
|
||||
|
@ -190,12 +189,12 @@ public class HttpChannelState
|
|||
|
||||
case COMPLETING:
|
||||
return Next.COMPLETE;
|
||||
|
||||
|
||||
case ASYNCWAIT:
|
||||
return Next.WAIT;
|
||||
|
||||
|
||||
case COMPLETED:
|
||||
return Next.RECYCLE;
|
||||
return Next.WAIT;
|
||||
|
||||
case REDISPATCH:
|
||||
_state=State.REDISPATCHED;
|
||||
|
@ -325,7 +324,7 @@ public class HttpChannelState
|
|||
_event.setDispatchTarget(context,path);
|
||||
_dispatched=true;
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
throw new IllegalStateException(this.getStatusString());
|
||||
}
|
||||
|
@ -393,7 +392,7 @@ public class HttpChannelState
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
scheduleDispatch();
|
||||
}
|
||||
|
||||
|
@ -528,7 +527,7 @@ public class HttpChannelState
|
|||
return _expired;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean isInitial()
|
||||
{
|
||||
synchronized(this)
|
||||
|
@ -563,13 +562,23 @@ public class HttpChannelState
|
|||
}
|
||||
}
|
||||
|
||||
boolean isCompleted()
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
return _state == State.COMPLETED;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAsyncStarted()
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
switch(_state)
|
||||
{
|
||||
case ASYNCSTARTED:
|
||||
case ASYNCSTARTED: // Suspend called, but not yet returned to container
|
||||
case REDISPATCHING: // resumed while dispatched
|
||||
case COMPLETECALLED: // complete called
|
||||
case ASYNCWAIT:
|
||||
return true;
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.util.List;
|
|||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import org.eclipse.jetty.http.HttpScheme;
|
||||
import org.eclipse.jetty.util.Jetty;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
|
||||
|
@ -40,6 +41,8 @@ import org.eclipse.jetty.util.annotation.ManagedObject;
|
|||
@ManagedObject("HTTP Configuration")
|
||||
public class HttpConfiguration
|
||||
{
|
||||
public static final String SERVER_VERSION = "Jetty(" + Jetty.VERSION + ")";
|
||||
|
||||
private List<Customizer> _customizers=new CopyOnWriteArrayList<>();
|
||||
private int _outputBufferSize=32*1024;
|
||||
private int _requestHeaderSize=8*1024;
|
||||
|
@ -48,8 +51,8 @@ public class HttpConfiguration
|
|||
private int _securePort;
|
||||
private String _secureScheme = HttpScheme.HTTPS.asString();
|
||||
private boolean _sendServerVersion = true; //send Server: header
|
||||
private boolean _sendXPoweredBy = false; //send X-Powered-By: header
|
||||
private boolean _sendDateHeader = false; //send Date: header
|
||||
|
||||
|
||||
public interface Customizer
|
||||
{
|
||||
|
@ -150,11 +153,22 @@ public class HttpConfiguration
|
|||
_sendServerVersion = sendServerVersion;
|
||||
}
|
||||
|
||||
@ManagedAttribute("if true, include the server version in HTTP headers")
|
||||
@ManagedAttribute("if true, send the Server header in responses")
|
||||
public boolean getSendServerVersion()
|
||||
{
|
||||
return _sendServerVersion;
|
||||
}
|
||||
|
||||
public void setSendXPoweredBy (boolean sendXPoweredBy)
|
||||
{
|
||||
_sendXPoweredBy=sendXPoweredBy;
|
||||
}
|
||||
|
||||
@ManagedAttribute("if true, send the X-Powered-By header in responses")
|
||||
public boolean getSendXPoweredBy()
|
||||
{
|
||||
return _sendXPoweredBy;
|
||||
}
|
||||
|
||||
public void setSendDateHeader(boolean sendDateHeader)
|
||||
{
|
||||
|
|
|
@ -91,8 +91,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
|||
_config = config;
|
||||
_connector = connector;
|
||||
_bufferPool = _connector.getByteBufferPool();
|
||||
_generator = new HttpGenerator();
|
||||
_generator.setSendServerVersion(_config.getSendServerVersion());
|
||||
_generator = new HttpGenerator(_config.getSendServerVersion(),_config.getSendXPoweredBy());
|
||||
_channel = new HttpChannelOverHttp(connector, config, endPoint, this, new Input());
|
||||
_parser = newHttpParser();
|
||||
|
||||
|
@ -133,14 +132,20 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
|||
_parser.reset();
|
||||
// close to seek EOF
|
||||
_parser.close();
|
||||
if (getEndPoint().isOpen())
|
||||
fillInterested();
|
||||
}
|
||||
// else if we are persistent
|
||||
else if (_generator.isPersistent())
|
||||
// reset to seek next request
|
||||
_parser.reset();
|
||||
else
|
||||
{
|
||||
// else seek EOF
|
||||
_parser.close();
|
||||
if (getEndPoint().isOpen())
|
||||
fillInterested();
|
||||
}
|
||||
|
||||
_generator.reset();
|
||||
_channel.reset();
|
||||
|
@ -235,7 +240,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
|||
int filled = getEndPoint().fill(_requestBuffer);
|
||||
if (filled==0) // Do a retry on fill 0 (optimisation for SSL connections)
|
||||
filled = getEndPoint().fill(_requestBuffer);
|
||||
|
||||
|
||||
LOG.debug("{} filled {}", this, filled);
|
||||
|
||||
// If we failed to fill
|
||||
|
|
|
@ -373,7 +373,8 @@ public class HttpOutput extends ServletOutputStream
|
|||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Asynchronous send of content.
|
||||
* @param in The content to send
|
||||
* @param in The content to send as a stream. The stream will be closed
|
||||
* after reading all content.
|
||||
* @param callback The callback to use to notify success or failure
|
||||
*/
|
||||
public void sendContent(InputStream in, Callback callback)
|
||||
|
@ -383,7 +384,8 @@ public class HttpOutput extends ServletOutputStream
|
|||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Asynchronous send of content.
|
||||
* @param in The content to send
|
||||
* @param in The content to send as a channel. The channel will be closed
|
||||
* after reading all content.
|
||||
* @param callback The callback to use to notify success or failure
|
||||
*/
|
||||
public void sendContent(ReadableByteChannel in, Callback callback)
|
||||
|
@ -472,28 +474,38 @@ public class HttpOutput extends ServletOutputStream
|
|||
@Override
|
||||
protected boolean process() throws Exception
|
||||
{
|
||||
int len=_in.read(_buffer.array(),0,_buffer.capacity());
|
||||
if (len==-1)
|
||||
{
|
||||
closed();
|
||||
_channel.getByteBufferPool().release(_buffer);
|
||||
return true;
|
||||
}
|
||||
boolean eof=false;
|
||||
|
||||
// if we read less than a buffer, are we at EOF?
|
||||
if (len<_buffer.capacity())
|
||||
int len=_in.read(_buffer.array(),0,_buffer.capacity());
|
||||
|
||||
if (len<0)
|
||||
{
|
||||
eof=true;
|
||||
len=0;
|
||||
_in.close();
|
||||
}
|
||||
else if (len<_buffer.capacity())
|
||||
{
|
||||
// read ahead for EOF to try for single commit
|
||||
int len2=_in.read(_buffer.array(),len,_buffer.capacity()-len);
|
||||
if (len2<0)
|
||||
eof=true;
|
||||
else
|
||||
len+=len2;
|
||||
}
|
||||
|
||||
|
||||
// write what we have
|
||||
_buffer.position(0);
|
||||
_buffer.limit(len);
|
||||
_channel.write(_buffer,eof,this);
|
||||
|
||||
// Handle EOF
|
||||
if (eof)
|
||||
{
|
||||
closed();
|
||||
_channel.getByteBufferPool().release(_buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -502,6 +514,14 @@ public class HttpOutput extends ServletOutputStream
|
|||
{
|
||||
super.failed(x);
|
||||
_channel.getByteBufferPool().release(_buffer);
|
||||
try
|
||||
{
|
||||
_in.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOG.ignore(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -531,30 +551,38 @@ public class HttpOutput extends ServletOutputStream
|
|||
protected boolean process() throws Exception
|
||||
{
|
||||
_buffer.clear();
|
||||
int len=_in.read(_buffer);
|
||||
if (len==-1)
|
||||
{
|
||||
closed();
|
||||
_channel.getByteBufferPool().release(_buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean eof=false;
|
||||
|
||||
// if we read less than a buffer, are we at EOF?
|
||||
if (len<_buffer.capacity())
|
||||
int len=_in.read(_buffer);
|
||||
|
||||
if (len<0)
|
||||
{
|
||||
eof=true;
|
||||
len=0;
|
||||
_in.close();
|
||||
}
|
||||
else if (len<_buffer.capacity())
|
||||
{
|
||||
// read ahead for EOF to try for single commit
|
||||
int len2=_in.read(_buffer);
|
||||
if (len2<0)
|
||||
eof=true;
|
||||
else
|
||||
len+=len2;
|
||||
}
|
||||
|
||||
|
||||
// write what we have
|
||||
_buffer.flip();
|
||||
_channel.write(_buffer,eof,this);
|
||||
|
||||
// Handle EOF
|
||||
if (eof)
|
||||
{
|
||||
closed();
|
||||
_channel.getByteBufferPool().release(_buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -562,6 +590,14 @@ public class HttpOutput extends ServletOutputStream
|
|||
{
|
||||
super.failed(x);
|
||||
_channel.getByteBufferPool().release(_buffer);
|
||||
try
|
||||
{
|
||||
_in.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOG.ignore(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import java.io.UnsupportedEncodingException;
|
|||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.UnsupportedCharsetException;
|
||||
import java.security.Principal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
@ -38,7 +39,6 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.AsyncContext;
|
||||
import javax.servlet.AsyncListener;
|
||||
import javax.servlet.DispatcherType;
|
||||
|
@ -63,7 +63,6 @@ import org.eclipse.jetty.http.HttpCookie;
|
|||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpScheme;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.HttpURI;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
|
@ -203,7 +202,6 @@ public class Request implements HttpServletRequest
|
|||
private HttpSession _session;
|
||||
private SessionManager _sessionManager;
|
||||
private long _timeStamp;
|
||||
private long _dispatchTime;
|
||||
private HttpURI _uri;
|
||||
private MultiPartInputStreamParser _multiPartInputStream; //if the request is a multi-part mime
|
||||
private AsyncContextState _async;
|
||||
|
@ -1395,16 +1393,6 @@ public class Request implements HttpServletRequest
|
|||
return null;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Get timestamp of the request dispatch
|
||||
*
|
||||
* @return timestamp
|
||||
*/
|
||||
public long getDispatchTime()
|
||||
{
|
||||
return _dispatchTime;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public boolean isHandled()
|
||||
|
@ -1714,7 +1702,16 @@ public class Request implements HttpServletRequest
|
|||
|
||||
// check encoding is supported
|
||||
if (!StringUtil.isUTF8(encoding))
|
||||
Charset.forName(encoding);
|
||||
{
|
||||
try
|
||||
{
|
||||
Charset.forName(encoding);
|
||||
}
|
||||
catch (UnsupportedCharsetException e)
|
||||
{
|
||||
throw new UnsupportedEncodingException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -1996,18 +1993,6 @@ public class Request implements HttpServletRequest
|
|||
_scope = scope;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Set timetstamp of request dispatch
|
||||
*
|
||||
* @param value
|
||||
* timestamp
|
||||
*/
|
||||
public void setDispatchTime(long value)
|
||||
{
|
||||
_dispatchTime = value;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public AsyncContext startAsync() throws IllegalStateException
|
||||
|
|
|
@ -281,8 +281,8 @@ public class Server extends HandlerWrapper implements Attributes
|
|||
|
||||
ShutdownMonitor.getInstance().start(); // initialize
|
||||
|
||||
LOG.info("jetty-"+getVersion());
|
||||
HttpGenerator.setServerVersion(getVersion());
|
||||
LOG.info("jetty-" + getVersion());
|
||||
HttpGenerator.setJettyVersion(HttpConfiguration.SERVER_VERSION);
|
||||
MultiException mex=new MultiException();
|
||||
|
||||
try
|
||||
|
|
|
@ -98,6 +98,23 @@ public class ServerConnector extends AbstractNetworkConnector
|
|||
{
|
||||
this(server,null,null,null,0,0,new HttpConnectionFactory());
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** HTTP Server Connection.
|
||||
* <p>Construct a ServerConnector with a private instance of {@link HttpConnectionFactory} as the only factory.</p>
|
||||
* @param server The {@link Server} this connector will accept connection for.
|
||||
* @param acceptors
|
||||
* the number of acceptor threads to use, or 0 for a default value. Acceptors accept new TCP/IP connections.
|
||||
* @param selectors
|
||||
* the number of selector threads, or 0 for a default value. Selectors notice and schedule established connection that can make IO progress.
|
||||
*/
|
||||
public ServerConnector(
|
||||
@Name("server") Server server,
|
||||
@Name("acceptors") int acceptors,
|
||||
@Name("selectors") int selectors)
|
||||
{
|
||||
this(server,null,null,null,acceptors,selectors,new HttpConnectionFactory());
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Generic Server Connection with default configuration.
|
||||
|
|
|
@ -102,6 +102,13 @@ public class ShutdownMonitor
|
|||
// Graceful Shutdown
|
||||
debug("Issuing graceful shutdown..");
|
||||
ShutdownThread.getInstance().run();
|
||||
|
||||
//Stop accepting any more
|
||||
close(serverSocket);
|
||||
serverSocket = null;
|
||||
|
||||
//Shutdown input from client
|
||||
shutdownInput(socket);
|
||||
|
||||
// Reply to client
|
||||
debug("Informing client that we are stopped.");
|
||||
|
@ -109,11 +116,10 @@ public class ShutdownMonitor
|
|||
out.flush();
|
||||
|
||||
// Shutdown Monitor
|
||||
debug("Shutting down monitor");
|
||||
socket.shutdownOutput();
|
||||
close(socket);
|
||||
socket = null;
|
||||
close(serverSocket);
|
||||
serverSocket = null;
|
||||
socket = null;
|
||||
debug("Shutting down monitor");
|
||||
|
||||
if (exitVm)
|
||||
{
|
||||
|
@ -248,7 +254,7 @@ public class ShutdownMonitor
|
|||
}
|
||||
catch (IOException ignore)
|
||||
{
|
||||
/* ignore */
|
||||
debug(ignore);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -265,10 +271,27 @@ public class ShutdownMonitor
|
|||
}
|
||||
catch (IOException ignore)
|
||||
{
|
||||
/* ignore */
|
||||
debug(ignore);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void shutdownInput(Socket socket)
|
||||
{
|
||||
if (socket == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
socket.shutdownInput();
|
||||
}
|
||||
catch (IOException ignore)
|
||||
{
|
||||
debug(ignore);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void debug(String format, Object... args)
|
||||
{
|
||||
if (DEBUG)
|
||||
|
|
|
@ -101,8 +101,8 @@ import org.eclipse.jetty.util.resource.Resource;
|
|||
@ManagedObject("URI Context")
|
||||
public class ContextHandler extends ScopedHandler implements Attributes, Graceful
|
||||
{
|
||||
public static int SERVLET_MAJOR_VERSION=3;
|
||||
public static int SERVLET_MINOR_VERSION=0;
|
||||
public final static int SERVLET_MAJOR_VERSION=3;
|
||||
public final static int SERVLET_MINOR_VERSION=0;
|
||||
|
||||
final private static String __unimplmented="Unimplemented - use org.eclipse.jetty.servlet.ServletContextHandler";
|
||||
|
||||
|
@ -884,8 +884,10 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
String vhost = normalizeHostname(baseRequest.getServerName());
|
||||
|
||||
boolean match = false;
|
||||
boolean connectorName = false;
|
||||
boolean connectorMatch = false;
|
||||
|
||||
loop: for (String contextVhost:_vhosts)
|
||||
for (String contextVhost:_vhosts)
|
||||
{
|
||||
if (contextVhost == null || contextVhost.length()==0)
|
||||
continue;
|
||||
|
@ -895,21 +897,21 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
case '*':
|
||||
if (contextVhost.startsWith("*."))
|
||||
// wildcard only at the beginning, and only for one additional subdomain level
|
||||
match = contextVhost.regionMatches(true,2,vhost,vhost.indexOf(".") + 1,contextVhost.length() - 2);
|
||||
match = match || contextVhost.regionMatches(true,2,vhost,vhost.indexOf(".") + 1,contextVhost.length() - 2);
|
||||
break;
|
||||
case '@':
|
||||
connectorName=true;
|
||||
String name=baseRequest.getHttpChannel().getConnector().getName();
|
||||
match=name!=null && contextVhost.length()==name.length()+1 && contextVhost.endsWith(name);
|
||||
boolean m=name!=null && contextVhost.length()==name.length()+1 && contextVhost.endsWith(name);
|
||||
match = match || m;
|
||||
connectorMatch = connectorMatch || m;
|
||||
break;
|
||||
default:
|
||||
match = contextVhost.equalsIgnoreCase(vhost);
|
||||
match = match || contextVhost.equalsIgnoreCase(vhost);
|
||||
}
|
||||
|
||||
if (match)
|
||||
break loop;
|
||||
|
||||
}
|
||||
if (!match)
|
||||
if (!match || connectorName && !connectorMatch)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1287,8 +1289,26 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
*/
|
||||
public void setContextPath(String contextPath)
|
||||
{
|
||||
if (contextPath != null && contextPath.length() > 1 && contextPath.endsWith("/"))
|
||||
throw new IllegalArgumentException("ends with /");
|
||||
if (contextPath == null)
|
||||
throw new IllegalArgumentException("null contextPath");
|
||||
|
||||
if (contextPath.endsWith("/*"))
|
||||
{
|
||||
LOG.warn(this+" contextPath ends with /*");
|
||||
contextPath=contextPath.substring(0,contextPath.length()-2);
|
||||
}
|
||||
else if (contextPath.endsWith("/"))
|
||||
{
|
||||
LOG.warn(this+" contextPath ends with /");
|
||||
contextPath=contextPath.substring(0,contextPath.length()-1);
|
||||
}
|
||||
|
||||
if (contextPath.length()==0)
|
||||
{
|
||||
LOG.warn("Empty contextPath");
|
||||
contextPath="/";
|
||||
}
|
||||
|
||||
_contextPath = contextPath;
|
||||
|
||||
if (getServer() != null && (getServer().isStarting() || getServer().isStarted()))
|
||||
|
@ -2576,7 +2596,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
if (dot<0)
|
||||
return false;
|
||||
String suffix=path.substring(dot);
|
||||
return resource.getAlias().toString().endsWith(suffix);
|
||||
return resource.toString().endsWith(suffix);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2592,10 +2612,10 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
public boolean check(String path, Resource resource)
|
||||
{
|
||||
int slash = path.lastIndexOf('/');
|
||||
if (slash<0)
|
||||
if (slash<0 || slash==path.length()-1)
|
||||
return false;
|
||||
String suffix=path.substring(slash);
|
||||
return resource.getAlias().toString().endsWith(suffix);
|
||||
return resource.toString().endsWith(suffix);
|
||||
}
|
||||
}
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -2609,7 +2629,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
public boolean check(String path, Resource resource)
|
||||
{
|
||||
int slash = path.lastIndexOf('/');
|
||||
if (slash<0)
|
||||
if (slash<0 || resource.exists())
|
||||
return false;
|
||||
String suffix=path.substring(slash);
|
||||
return resource.getAlias().toString().endsWith(suffix);
|
||||
|
|
|
@ -19,19 +19,19 @@
|
|||
package org.eclipse.jetty.server.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.PathMap;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.HandlerContainer;
|
||||
import org.eclipse.jetty.server.HttpChannelState;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.util.LazyList;
|
||||
import org.eclipse.jetty.util.ArrayTernaryTrie;
|
||||
import org.eclipse.jetty.util.ArrayUtil;
|
||||
import org.eclipse.jetty.util.Trie;
|
||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
import org.eclipse.jetty.util.annotation.ManagedOperation;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
|
@ -53,7 +53,7 @@ public class ContextHandlerCollection extends HandlerCollection
|
|||
{
|
||||
private static final Logger LOG = Log.getLogger(ContextHandlerCollection.class);
|
||||
|
||||
private volatile PathMap<Object> _contextMap;
|
||||
private volatile Trie<ContextHandler[]> _contexts;
|
||||
private Class<? extends ContextHandler> _contextClass = ContextHandler.class;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -70,90 +70,71 @@ public class ContextHandlerCollection extends HandlerCollection
|
|||
@ManagedOperation("update the mapping of context path to context")
|
||||
public void mapContexts()
|
||||
{
|
||||
PathMap<Object> contextMap = new PathMap<Object>();
|
||||
Handler[] branches = getHandlers();
|
||||
|
||||
|
||||
for (int b=0;branches!=null && b<branches.length;b++)
|
||||
int capacity=512;
|
||||
|
||||
// Loop until we have a big enough trie to hold all the context paths
|
||||
Trie<ContextHandler[]> trie;
|
||||
loop: while(true)
|
||||
{
|
||||
Handler[] handlers=null;
|
||||
trie=new ArrayTernaryTrie<>(false,capacity);
|
||||
|
||||
if (branches[b] instanceof ContextHandler)
|
||||
Handler[] branches = getHandlers();
|
||||
|
||||
// loop over each group of contexts
|
||||
for (int b=0;branches!=null && b<branches.length;b++)
|
||||
{
|
||||
handlers = new Handler[]{ branches[b] };
|
||||
}
|
||||
else if (branches[b] instanceof HandlerContainer)
|
||||
{
|
||||
handlers = ((HandlerContainer)branches[b]).getChildHandlersByClass(ContextHandler.class);
|
||||
}
|
||||
else
|
||||
continue;
|
||||
Handler[] handlers=null;
|
||||
|
||||
for (int i=0;i<handlers.length;i++)
|
||||
{
|
||||
ContextHandler handler=(ContextHandler)handlers[i];
|
||||
|
||||
String contextPath=handler.getContextPath();
|
||||
|
||||
if (contextPath==null || contextPath.indexOf(',')>=0 || contextPath.startsWith("*"))
|
||||
throw new IllegalArgumentException ("Illegal context spec:"+contextPath);
|
||||
|
||||
if(!contextPath.startsWith("/"))
|
||||
contextPath='/'+contextPath;
|
||||
|
||||
if (contextPath.length()>1)
|
||||
if (branches[b] instanceof ContextHandler)
|
||||
{
|
||||
if (contextPath.endsWith("/"))
|
||||
contextPath+="*";
|
||||
else if (!contextPath.endsWith("/*"))
|
||||
contextPath+="/*";
|
||||
handlers = new Handler[]{ branches[b] };
|
||||
}
|
||||
|
||||
Object contexts=contextMap.get(contextPath);
|
||||
String[] vhosts=handler.getVirtualHosts();
|
||||
|
||||
|
||||
if (vhosts!=null && vhosts.length>0)
|
||||
else if (branches[b] instanceof HandlerContainer)
|
||||
{
|
||||
Map<String, Object> hosts;
|
||||
|
||||
if (contexts instanceof Map)
|
||||
hosts=(Map<String, Object>)contexts;
|
||||
else
|
||||
{
|
||||
hosts=new HashMap<String, Object>();
|
||||
hosts.put("*",contexts);
|
||||
contextMap.put(contextPath, hosts);
|
||||
}
|
||||
|
||||
for (int j=0;j<vhosts.length;j++)
|
||||
{
|
||||
String vhost=vhosts[j];
|
||||
contexts=hosts.get(vhost);
|
||||
contexts=LazyList.add(contexts,branches[b]);
|
||||
hosts.put(vhost,contexts);
|
||||
}
|
||||
}
|
||||
else if (contexts instanceof Map)
|
||||
{
|
||||
Map<String, Object> hosts=(Map<String, Object>)contexts;
|
||||
contexts=hosts.get("*");
|
||||
contexts= LazyList.add(contexts, branches[b]);
|
||||
hosts.put("*",contexts);
|
||||
handlers = ((HandlerContainer)branches[b]).getChildHandlersByClass(ContextHandler.class);
|
||||
}
|
||||
else
|
||||
continue;
|
||||
|
||||
// for each context handler in a group
|
||||
for (int i=0;handlers!=null && i<handlers.length;i++)
|
||||
{
|
||||
contexts= LazyList.add(contexts, branches[b]);
|
||||
contextMap.put(contextPath, contexts);
|
||||
ContextHandler handler=(ContextHandler)handlers[i];
|
||||
String contextPath=handler.getContextPath().substring(1);
|
||||
ContextHandler[] contexts=trie.get(contextPath);
|
||||
|
||||
if (!trie.put(contextPath,ArrayUtil.addToArray(contexts,handler,ContextHandler.class)))
|
||||
{
|
||||
capacity+=512;
|
||||
continue loop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Sort the contexts so those with virtual hosts are considered before those without
|
||||
for (String ctx : trie.keySet())
|
||||
{
|
||||
ContextHandler[] contexts=trie.get(ctx);
|
||||
ContextHandler[] sorted=new ContextHandler[contexts.length];
|
||||
int i=0;
|
||||
for (ContextHandler handler:contexts)
|
||||
if (handler.getVirtualHosts()!=null && handler.getVirtualHosts().length>0)
|
||||
sorted[i++]=handler;
|
||||
for (ContextHandler handler:contexts)
|
||||
if (handler.getVirtualHosts()==null || handler.getVirtualHosts().length==0)
|
||||
sorted[i++]=handler;
|
||||
trie.put(ctx,sorted);
|
||||
}
|
||||
_contextMap=contextMap;
|
||||
|
||||
//for (String ctx : trie.keySet())
|
||||
// System.err.printf("'%s'->%s%n",ctx,Arrays.asList(trie.get(ctx)));
|
||||
_contexts=trie;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/*
|
||||
* @see org.eclipse.jetty.server.server.handler.HandlerCollection#setHandlers(org.eclipse.jetty.server.server.Handler[])
|
||||
|
@ -161,7 +142,7 @@ public class ContextHandlerCollection extends HandlerCollection
|
|||
@Override
|
||||
public void setHandlers(Handler[] handlers)
|
||||
{
|
||||
_contextMap=null;
|
||||
_contexts=null;
|
||||
super.setHandlers(handlers);
|
||||
if (isStarted())
|
||||
mapContexts();
|
||||
|
@ -199,67 +180,31 @@ public class ContextHandlerCollection extends HandlerCollection
|
|||
}
|
||||
|
||||
// data structure which maps a request to a context; first-best match wins
|
||||
// { context path =>
|
||||
// { virtual host => context }
|
||||
// { context path => [ context ] }
|
||||
// }
|
||||
PathMap<Object> map = _contextMap;
|
||||
if (map!=null && target!=null && target.startsWith("/"))
|
||||
if (target.startsWith("/"))
|
||||
{
|
||||
// first, get all contexts matched by context path
|
||||
Object contexts = map.getLazyMatches(target);
|
||||
int limit = target.length()-1;
|
||||
|
||||
for (int i=0; i<LazyList.size(contexts); i++)
|
||||
{
|
||||
// then, match against the virtualhost of each context
|
||||
Map.Entry entry = (Map.Entry)LazyList.get(contexts, i);
|
||||
Object list = entry.getValue();
|
||||
while (limit>=0)
|
||||
{
|
||||
// Get best match
|
||||
ContextHandler[] contexts = _contexts.getBest(target,1,limit);
|
||||
if (contexts==null)
|
||||
break;
|
||||
|
||||
if (list instanceof Map)
|
||||
{
|
||||
Map hosts = (Map)list;
|
||||
String host = normalizeHostname(request.getServerName());
|
||||
|
||||
// explicitly-defined virtual hosts, most specific
|
||||
list=hosts.get(host);
|
||||
for (int j=0; j<LazyList.size(list); j++)
|
||||
{
|
||||
Handler handler = (Handler)LazyList.get(list,j);
|
||||
handler.handle(target,baseRequest, request, response);
|
||||
if (baseRequest.isHandled())
|
||||
return;
|
||||
}
|
||||
|
||||
// wildcard for one level of names
|
||||
list=hosts.get("*."+host.substring(host.indexOf(".")+1));
|
||||
for (int j=0; j<LazyList.size(list); j++)
|
||||
{
|
||||
Handler handler = (Handler)LazyList.get(list,j);
|
||||
handler.handle(target,baseRequest, request, response);
|
||||
if (baseRequest.isHandled())
|
||||
return;
|
||||
}
|
||||
|
||||
// no virtualhosts defined for the context, least specific
|
||||
// will handle any request that does not match to a specific virtual host above
|
||||
list=hosts.get("*");
|
||||
for (int j=0; j<LazyList.size(list); j++)
|
||||
{
|
||||
Handler handler = (Handler)LazyList.get(list,j);
|
||||
handler.handle(target,baseRequest, request, response);
|
||||
if (baseRequest.isHandled())
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int j=0; j<LazyList.size(list); j++)
|
||||
{
|
||||
Handler handler = (Handler)LazyList.get(list,j);
|
||||
handler.handle(target,baseRequest, request, response);
|
||||
if (baseRequest.isHandled())
|
||||
return;
|
||||
}
|
||||
}
|
||||
int l=contexts[0].getContextPath().length();
|
||||
if (l==1 || target.length()==l || target.charAt(l)=='/')
|
||||
{
|
||||
for (ContextHandler handler : contexts)
|
||||
{
|
||||
handler.handle(target,baseRequest, request, response);
|
||||
if (baseRequest.isHandled())
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
limit=l-2;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -320,16 +265,5 @@ public class ContextHandlerCollection extends HandlerCollection
|
|||
_contextClass = contextClass;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
private String normalizeHostname( String host )
|
||||
{
|
||||
if ( host == null )
|
||||
return null;
|
||||
|
||||
if ( host.endsWith( "." ) )
|
||||
return host.substring( 0, host.length() -1);
|
||||
|
||||
return host;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,15 +20,18 @@ package org.eclipse.jetty.server.handler;
|
|||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.AsyncEvent;
|
||||
import javax.servlet.AsyncListener;
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.server.HttpChannelState;
|
||||
import org.eclipse.jetty.server.AsyncContextState;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.RequestLog;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
|
@ -44,8 +47,37 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
public class RequestLogHandler extends HandlerWrapper
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(RequestLogHandler.class);
|
||||
|
||||
private RequestLog _requestLog;
|
||||
private final AsyncListener _listener = new AsyncListener()
|
||||
{
|
||||
|
||||
@Override
|
||||
public void onTimeout(AsyncEvent event) throws IOException
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartAsync(AsyncEvent event) throws IOException
|
||||
{
|
||||
event.getAsyncContext().addListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(AsyncEvent event) throws IOException
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete(AsyncEvent event) throws IOException
|
||||
{
|
||||
AsyncContextState context = (AsyncContextState)event.getAsyncContext();
|
||||
Request request=context.getHttpChannelState().getBaseRequest();
|
||||
Response response=request.getResponse();
|
||||
_requestLog.log(request,response);
|
||||
}
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/*
|
||||
|
@ -55,21 +87,21 @@ public class RequestLogHandler extends HandlerWrapper
|
|||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
|
||||
throws IOException, ServletException
|
||||
{
|
||||
HttpChannelState continuation = baseRequest.getHttpChannelState();
|
||||
if (!continuation.isInitial())
|
||||
{
|
||||
baseRequest.setDispatchTime(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
super.handle(target, baseRequest, request, response);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_requestLog != null && DispatcherType.REQUEST.equals(baseRequest.getDispatcherType()))
|
||||
if (_requestLog != null && baseRequest.getDispatcherType().equals(DispatcherType.REQUEST))
|
||||
{
|
||||
_requestLog.log(baseRequest, (Response)response);
|
||||
if (baseRequest.getHttpChannelState().isAsync())
|
||||
{
|
||||
if (baseRequest.getHttpChannelState().isInitial())
|
||||
baseRequest.getAsyncContext().addListener(_listener);
|
||||
}
|
||||
else
|
||||
_requestLog.log(baseRequest, (Response)response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -86,5 +118,36 @@ public class RequestLogHandler extends HandlerWrapper
|
|||
{
|
||||
return _requestLog;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
protected void doStart() throws Exception
|
||||
{
|
||||
if (_requestLog==null)
|
||||
{
|
||||
LOG.warn("!RequestLog");
|
||||
_requestLog=new NullRequestLog();
|
||||
}
|
||||
super.doStart();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
protected void doStop() throws Exception
|
||||
{
|
||||
super.doStop();
|
||||
if (_requestLog instanceof NullRequestLog)
|
||||
_requestLog=null;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* ------------------------------------------------------------ */
|
||||
/* ------------------------------------------------------------ */
|
||||
private static class NullRequestLog extends AbstractLifeCycle implements RequestLog
|
||||
{
|
||||
@Override
|
||||
public void log(Request request, Response response)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ public abstract class AbstractSessionIdManager extends AbstractLifeCycle impleme
|
|||
protected Random _random;
|
||||
protected boolean _weakRandom;
|
||||
protected String _workerName;
|
||||
protected String _workerAttr;
|
||||
protected long _reseed=100000L;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -58,6 +59,7 @@ public abstract class AbstractSessionIdManager extends AbstractLifeCycle impleme
|
|||
*
|
||||
* @return String or null
|
||||
*/
|
||||
@Override
|
||||
public String getWorkerName()
|
||||
{
|
||||
return _workerName;
|
||||
|
@ -67,11 +69,16 @@ public abstract class AbstractSessionIdManager extends AbstractLifeCycle impleme
|
|||
/**
|
||||
* Set the workname. If set, the workername is dot appended to the session
|
||||
* ID and can be used to assist session affinity in a load balancer.
|
||||
* A worker name starting with $ is used as a request attribute name to
|
||||
* lookup the worker name that can be dynamically set by a request
|
||||
* customiser.
|
||||
*
|
||||
* @param workerName
|
||||
*/
|
||||
public void setWorkerName(String workerName)
|
||||
{
|
||||
if (isRunning())
|
||||
throw new IllegalStateException(getState());
|
||||
if (workerName.contains("."))
|
||||
throw new IllegalArgumentException("Name cannot contain '.'");
|
||||
_workerName=workerName;
|
||||
|
@ -114,27 +121,28 @@ public abstract class AbstractSessionIdManager extends AbstractLifeCycle impleme
|
|||
*
|
||||
* @see org.eclipse.jetty.server.SessionIdManager#newSessionId(javax.servlet.http.HttpServletRequest, long)
|
||||
*/
|
||||
@Override
|
||||
public String newSessionId(HttpServletRequest request, long created)
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
if (request!=null)
|
||||
{
|
||||
// A requested session ID can only be used if it is in use already.
|
||||
String requested_id=request.getRequestedSessionId();
|
||||
if (requested_id!=null)
|
||||
{
|
||||
String cluster_id=getClusterId(requested_id);
|
||||
if (idInUse(cluster_id))
|
||||
return cluster_id;
|
||||
}
|
||||
if (request==null)
|
||||
return newSessionId(created);
|
||||
|
||||
// Else reuse any new session ID already defined for this request.
|
||||
String new_id=(String)request.getAttribute(__NEW_SESSION_ID);
|
||||
if (new_id!=null&&idInUse(new_id))
|
||||
return new_id;
|
||||
// A requested session ID can only be used if it is in use already.
|
||||
String requested_id=request.getRequestedSessionId();
|
||||
if (requested_id!=null)
|
||||
{
|
||||
String cluster_id=getClusterId(requested_id);
|
||||
if (idInUse(cluster_id))
|
||||
return cluster_id;
|
||||
}
|
||||
|
||||
// Else reuse any new session ID already defined for this request.
|
||||
String new_id=(String)request.getAttribute(__NEW_SESSION_ID);
|
||||
if (new_id!=null&&idInUse(new_id))
|
||||
return new_id;
|
||||
|
||||
// pick a new unique ID!
|
||||
String id = newSessionId(request.hashCode());
|
||||
|
||||
|
@ -190,15 +198,16 @@ public abstract class AbstractSessionIdManager extends AbstractLifeCycle impleme
|
|||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public abstract void renewSessionId(String oldClusterId, String oldNodeId, HttpServletRequest request);
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
protected void doStart() throws Exception
|
||||
{
|
||||
initRandom();
|
||||
_workerAttr=(_workerName!=null && _workerName.startsWith("$"))?_workerName.substring(1):null;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -232,5 +241,39 @@ public abstract class AbstractSessionIdManager extends AbstractLifeCycle impleme
|
|||
_random.setSeed(_random.nextLong()^System.currentTimeMillis()^hashCode()^Runtime.getRuntime().freeMemory());
|
||||
}
|
||||
|
||||
/** Get the session ID with any worker ID.
|
||||
*
|
||||
* @param clusterId
|
||||
* @param request
|
||||
* @return sessionId plus any worker ID.
|
||||
*/
|
||||
@Override
|
||||
public String getNodeId(String clusterId, HttpServletRequest request)
|
||||
{
|
||||
if (_workerName!=null)
|
||||
{
|
||||
if (_workerAttr==null)
|
||||
return clusterId+'.'+_workerName;
|
||||
|
||||
String worker=(String)request.getAttribute(_workerAttr);
|
||||
if (worker!=null)
|
||||
return clusterId+'.'+worker;
|
||||
}
|
||||
|
||||
return clusterId;
|
||||
}
|
||||
|
||||
/** Get the session ID without any worker ID.
|
||||
*
|
||||
* @param nodeId the node id
|
||||
* @return sessionId without any worker ID.
|
||||
*/
|
||||
@Override
|
||||
public String getClusterId(String nodeId)
|
||||
{
|
||||
int dot=nodeId.lastIndexOf('.');
|
||||
return (dot>0)?nodeId.substring(0,dot):nodeId;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -459,16 +459,16 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement
|
|||
{
|
||||
if (isUsingCookies())
|
||||
{
|
||||
String sessionPath = (_sessionPath==null) ? contextPath : _sessionPath;
|
||||
String sessionPath = (_cookieConfig.getPath()==null) ? contextPath : _cookieConfig.getPath();
|
||||
sessionPath = (sessionPath==null||sessionPath.length()==0) ? "/" : sessionPath;
|
||||
String id = getNodeId(session);
|
||||
HttpCookie cookie = null;
|
||||
if (_sessionComment == null)
|
||||
{
|
||||
cookie = new HttpCookie(
|
||||
_sessionCookie,
|
||||
_cookieConfig.getName(),
|
||||
id,
|
||||
_sessionDomain,
|
||||
_cookieConfig.getDomain(),
|
||||
sessionPath,
|
||||
_cookieConfig.getMaxAge(),
|
||||
_cookieConfig.isHttpOnly(),
|
||||
|
@ -477,9 +477,9 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement
|
|||
else
|
||||
{
|
||||
cookie = new HttpCookie(
|
||||
_sessionCookie,
|
||||
_cookieConfig.getName(),
|
||||
id,
|
||||
_sessionDomain,
|
||||
_cookieConfig.getDomain(),
|
||||
sessionPath,
|
||||
_cookieConfig.getMaxAge(),
|
||||
_cookieConfig.isHttpOnly(),
|
||||
|
@ -796,9 +796,11 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement
|
|||
|
||||
if (invalidate && _sessionListeners!=null)
|
||||
{
|
||||
HttpSessionEvent event=new HttpSessionEvent(session);
|
||||
for (HttpSessionListener listener : _sessionListeners)
|
||||
listener.sessionDestroyed(event);
|
||||
HttpSessionEvent event=new HttpSessionEvent(session);
|
||||
for (int i = _sessionListeners.size()-1; i>=0; i--)
|
||||
{
|
||||
_sessionListeners.get(i).sessionDestroyed(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -899,43 +901,57 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement
|
|||
|
||||
@Override
|
||||
public void setComment(String comment)
|
||||
{
|
||||
{
|
||||
if (_context != null && _context.getContextHandler().isAvailable())
|
||||
throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
|
||||
_sessionComment = comment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDomain(String domain)
|
||||
{
|
||||
if (_context != null && _context.getContextHandler().isAvailable())
|
||||
throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
|
||||
_sessionDomain=domain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHttpOnly(boolean httpOnly)
|
||||
{
|
||||
{
|
||||
if (_context != null && _context.getContextHandler().isAvailable())
|
||||
throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
|
||||
_httpOnly=httpOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxAge(int maxAge)
|
||||
{
|
||||
{
|
||||
if (_context != null && _context.getContextHandler().isAvailable())
|
||||
throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
|
||||
_maxCookieAge=maxAge;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name)
|
||||
{
|
||||
{
|
||||
if (_context != null && _context.getContextHandler().isAvailable())
|
||||
throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
|
||||
_sessionCookie=name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPath(String path)
|
||||
{
|
||||
if (_context != null && _context.getContextHandler().isAvailable())
|
||||
throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
|
||||
_sessionPath=path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSecure(boolean secure)
|
||||
{
|
||||
if (_context != null && _context.getContextHandler().isAvailable())
|
||||
throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started");
|
||||
_secureCookies=secure;
|
||||
}
|
||||
|
||||
|
|
|
@ -81,38 +81,7 @@ public class HashSessionIdManager extends AbstractSessionIdManager
|
|||
}
|
||||
return sessions;
|
||||
}
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Get the session ID with any worker ID.
|
||||
*
|
||||
* @param clusterId
|
||||
* @param request
|
||||
* @return sessionId plus any worker ID.
|
||||
*/
|
||||
public String getNodeId(String clusterId,HttpServletRequest request)
|
||||
{
|
||||
// used in Ajp13Parser
|
||||
String worker=request==null?null:(String)request.getAttribute("org.eclipse.jetty.ajp.JVMRoute");
|
||||
if (worker!=null)
|
||||
return clusterId+'.'+worker;
|
||||
|
||||
if (_workerName!=null)
|
||||
return clusterId+'.'+_workerName;
|
||||
|
||||
return clusterId;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Get the session ID without any worker ID.
|
||||
*
|
||||
* @param nodeId the node id
|
||||
* @return sessionId without any worker ID.
|
||||
*/
|
||||
public String getClusterId(String nodeId)
|
||||
{
|
||||
int dot=nodeId.lastIndexOf('.');
|
||||
return (dot>0)?nodeId.substring(0,dot):nodeId;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
protected void doStart() throws Exception
|
||||
|
@ -132,6 +101,7 @@ public class HashSessionIdManager extends AbstractSessionIdManager
|
|||
/**
|
||||
* @see SessionIdManager#idInUse(String)
|
||||
*/
|
||||
@Override
|
||||
public boolean idInUse(String id)
|
||||
{
|
||||
synchronized (this)
|
||||
|
@ -144,6 +114,7 @@ public class HashSessionIdManager extends AbstractSessionIdManager
|
|||
/**
|
||||
* @see SessionIdManager#addSession(HttpSession)
|
||||
*/
|
||||
@Override
|
||||
public void addSession(HttpSession session)
|
||||
{
|
||||
String id = getClusterId(session.getId());
|
||||
|
@ -165,6 +136,7 @@ public class HashSessionIdManager extends AbstractSessionIdManager
|
|||
/**
|
||||
* @see SessionIdManager#removeSession(HttpSession)
|
||||
*/
|
||||
@Override
|
||||
public void removeSession(HttpSession session)
|
||||
{
|
||||
String id = getClusterId(session.getId());
|
||||
|
@ -199,6 +171,7 @@ public class HashSessionIdManager extends AbstractSessionIdManager
|
|||
/**
|
||||
* @see SessionIdManager#invalidateAll(String)
|
||||
*/
|
||||
@Override
|
||||
public void invalidateAll(String id)
|
||||
{
|
||||
Collection<WeakReference<HttpSession>> sessions;
|
||||
|
@ -221,6 +194,7 @@ public class HashSessionIdManager extends AbstractSessionIdManager
|
|||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public void renewSessionId (String oldClusterId, String oldNodeId, HttpServletRequest request)
|
||||
{
|
||||
//generate a new id
|
||||
|
|
|
@ -29,11 +29,7 @@ import java.sql.PreparedStatement;
|
|||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
@ -380,6 +376,7 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
|
|||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void addSession(HttpSession session)
|
||||
{
|
||||
if (session == null)
|
||||
|
@ -422,6 +419,7 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
|
|||
|
||||
|
||||
|
||||
@Override
|
||||
public void removeSession(HttpSession session)
|
||||
{
|
||||
if (session == null)
|
||||
|
@ -456,32 +454,7 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the session id without any node identifier suffix.
|
||||
*
|
||||
* @see org.eclipse.jetty.server.SessionIdManager#getClusterId(java.lang.String)
|
||||
*/
|
||||
public String getClusterId(String nodeId)
|
||||
{
|
||||
int dot=nodeId.lastIndexOf('.');
|
||||
return (dot>0)?nodeId.substring(0,dot):nodeId;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the session id, including this node's id as a suffix.
|
||||
*
|
||||
* @see org.eclipse.jetty.server.SessionIdManager#getNodeId(java.lang.String, javax.servlet.http.HttpServletRequest)
|
||||
*/
|
||||
public String getNodeId(String clusterId, HttpServletRequest request)
|
||||
{
|
||||
if (_workerName!=null)
|
||||
return clusterId+'.'+_workerName;
|
||||
|
||||
return clusterId;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean idInUse(String id)
|
||||
{
|
||||
if (id == null)
|
||||
|
@ -515,6 +488,7 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
|
|||
*
|
||||
* @see org.eclipse.jetty.server.SessionIdManager#invalidateAll(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void invalidateAll(String id)
|
||||
{
|
||||
//take the id out of the list of known sessionids for this node
|
||||
|
@ -527,7 +501,7 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
|
|||
Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
|
||||
for (int i=0; contexts!=null && i<contexts.length; i++)
|
||||
{
|
||||
SessionHandler sessionHandler = (SessionHandler)((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
|
||||
SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
|
||||
if (sessionHandler != null)
|
||||
{
|
||||
SessionManager manager = sessionHandler.getSessionManager();
|
||||
|
@ -542,6 +516,7 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
|
|||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void renewSessionId (String oldClusterId, String oldNodeId, HttpServletRequest request)
|
||||
{
|
||||
//generate a new id
|
||||
|
@ -556,7 +531,7 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
|
|||
Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
|
||||
for (int i=0; contexts!=null && i<contexts.length; i++)
|
||||
{
|
||||
SessionHandler sessionHandler = (SessionHandler)((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
|
||||
SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
|
||||
if (sessionHandler != null)
|
||||
{
|
||||
SessionManager manager = sessionHandler.getSessionManager();
|
||||
|
@ -971,7 +946,7 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
|
|||
Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
|
||||
for (int i=0; contexts!=null && i<contexts.length; i++)
|
||||
{
|
||||
SessionHandler sessionHandler = (SessionHandler)((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
|
||||
SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
|
||||
if (sessionHandler != null)
|
||||
{
|
||||
SessionManager manager = sessionHandler.getSessionManager();
|
||||
|
|
|
@ -0,0 +1,226 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 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.containsString;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class HttpOutputTest
|
||||
{
|
||||
private Server _server;
|
||||
private LocalConnector _connector;
|
||||
private ContentHandler _handler;
|
||||
|
||||
@Before
|
||||
public void init() throws Exception
|
||||
{
|
||||
_server = new Server();
|
||||
|
||||
HttpConnectionFactory http = new HttpConnectionFactory();
|
||||
http.getHttpConfiguration().setRequestHeaderSize(1024);
|
||||
http.getHttpConfiguration().setResponseHeaderSize(1024);
|
||||
http.getHttpConfiguration().setOutputBufferSize(4096);
|
||||
|
||||
_connector = new LocalConnector(_server,http,null);
|
||||
_server.addConnector(_connector);
|
||||
_handler=new ContentHandler();
|
||||
_server.setHandler(_handler);
|
||||
_server.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void destroy() throws Exception
|
||||
{
|
||||
_server.stop();
|
||||
_server.join();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimple() throws Exception
|
||||
{
|
||||
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
|
||||
assertThat(response,containsString("HTTP/1.1 200 OK"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendInputStreamSimple() throws Exception
|
||||
{
|
||||
Resource simple = Resource.newClassPathResource("simple/simple.txt");
|
||||
_handler._contentInputStream=simple.getInputStream();
|
||||
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
|
||||
assertThat(response,containsString("HTTP/1.1 200 OK"));
|
||||
assertThat(response,containsString("Content-Length: 11"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendInputStreamBig() throws Exception
|
||||
{
|
||||
Resource big = Resource.newClassPathResource("simple/big.txt");
|
||||
_handler._contentInputStream=big.getInputStream();
|
||||
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
|
||||
assertThat(response,containsString("HTTP/1.1 200 OK"));
|
||||
assertThat(response,Matchers.not(containsString("Content-Length")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendInputStreamBigChunked() throws Exception
|
||||
{
|
||||
Resource big = Resource.newClassPathResource("simple/big.txt");
|
||||
_handler._contentInputStream= new FilterInputStream(big.getInputStream())
|
||||
{
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException
|
||||
{
|
||||
int filled= super.read(b,off,len>2000?2000:len);
|
||||
return filled;
|
||||
}
|
||||
};
|
||||
String response=_connector.getResponses(
|
||||
"GET / HTTP/1.1\nHost: localhost:80\n\n"+
|
||||
"GET / HTTP/1.1\nHost: localhost:80\nConnection: close\n\n"
|
||||
);
|
||||
response=response.substring(0,response.lastIndexOf("HTTP/1.1 200 OK"));
|
||||
|
||||
assertThat(response,containsString("HTTP/1.1 200 OK"));
|
||||
assertThat(response,containsString("Transfer-Encoding: chunked"));
|
||||
assertThat(response,containsString("\r\n0\r\n"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSendChannelSimple() throws Exception
|
||||
{
|
||||
Resource simple = Resource.newClassPathResource("simple/simple.txt");
|
||||
_handler._contentChannel=simple.getReadableByteChannel();
|
||||
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
|
||||
assertThat(response,containsString("HTTP/1.1 200 OK"));
|
||||
assertThat(response,containsString("Content-Length: 11"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendChannelBig() throws Exception
|
||||
{
|
||||
Resource big = Resource.newClassPathResource("simple/big.txt");
|
||||
_handler._contentChannel=big.getReadableByteChannel();
|
||||
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
|
||||
assertThat(response,containsString("HTTP/1.1 200 OK"));
|
||||
assertThat(response,Matchers.not(containsString("Content-Length")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendChannelBigChunked() throws Exception
|
||||
{
|
||||
Resource big = Resource.newClassPathResource("simple/big.txt");
|
||||
final ReadableByteChannel channel = big.getReadableByteChannel();
|
||||
_handler._contentChannel=new ReadableByteChannel()
|
||||
{
|
||||
|
||||
@Override
|
||||
public boolean isOpen()
|
||||
{
|
||||
return channel.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException
|
||||
{
|
||||
channel.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(ByteBuffer dst) throws IOException
|
||||
{
|
||||
int filled=0;
|
||||
if (dst.position()==0 && dst.limit()>2000)
|
||||
{
|
||||
int limit=dst.limit();
|
||||
dst.limit(2000);
|
||||
filled=channel.read(dst);
|
||||
dst.limit(limit);
|
||||
}
|
||||
else
|
||||
filled=channel.read(dst);
|
||||
return filled;
|
||||
}
|
||||
};
|
||||
|
||||
String response=_connector.getResponses(
|
||||
"GET / HTTP/1.1\nHost: localhost:80\n\n"+
|
||||
"GET / HTTP/1.1\nHost: localhost:80\nConnection: close\n\n"
|
||||
);
|
||||
response=response.substring(0,response.lastIndexOf("HTTP/1.1 200 OK"));
|
||||
|
||||
assertThat(response,containsString("HTTP/1.1 200 OK"));
|
||||
assertThat(response,containsString("Transfer-Encoding: chunked"));
|
||||
assertThat(response,containsString("\r\n0\r\n"));
|
||||
}
|
||||
|
||||
static class ContentHandler extends AbstractHandler
|
||||
{
|
||||
InputStream _contentInputStream;
|
||||
ReadableByteChannel _contentChannel;
|
||||
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
response.setContentType("text/plain");
|
||||
|
||||
HttpOutput out = (HttpOutput) response.getOutputStream();
|
||||
|
||||
if (_contentInputStream!=null)
|
||||
{
|
||||
out.sendContent(_contentInputStream);
|
||||
_contentInputStream=null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_contentChannel!=null)
|
||||
{
|
||||
out.sendContent(_contentChannel);
|
||||
_contentChannel=null;
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -48,6 +48,7 @@ import javax.servlet.http.HttpServletResponse;
|
|||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.io.EofException;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.eclipse.jetty.http.HttpURI;
|
|||
import org.eclipse.jetty.util.MultiMap;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.TypeUtil;
|
||||
import org.eclipse.jetty.util.Utf8Appendable;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -322,29 +323,15 @@ public class HttpURITest
|
|||
{
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
HttpURI huri=new HttpURI(uri);
|
||||
MultiMap<String> params = new MultiMap<>();
|
||||
huri.decodeQueryTo(params);
|
||||
System.err.println(params);
|
||||
Assert.assertTrue(false);
|
||||
}
|
||||
catch (IllegalArgumentException e)
|
||||
{
|
||||
}
|
||||
HttpURI huri=new HttpURI(uri);
|
||||
MultiMap<String> params = new MultiMap<>();
|
||||
huri.decodeQueryTo(params);
|
||||
assertEquals("data"+Utf8Appendable.REPLACEMENT+"here"+Utf8Appendable.REPLACEMENT,params.getValue("invalid",0));
|
||||
|
||||
try
|
||||
{
|
||||
HttpURI huri=new HttpURI(uri);
|
||||
MultiMap<String> params = new MultiMap<>();
|
||||
huri.decodeQueryTo(params,"UTF-8");
|
||||
System.err.println(params);
|
||||
Assert.assertTrue(false);
|
||||
}
|
||||
catch (IllegalArgumentException e)
|
||||
{
|
||||
}
|
||||
huri=new HttpURI(uri);
|
||||
params = new MultiMap<>();
|
||||
huri.decodeQueryTo(params,"UTF-8");
|
||||
assertEquals("data"+Utf8Appendable.REPLACEMENT+"here"+Utf8Appendable.REPLACEMENT,params.getValue("invalid",0));
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -18,26 +18,17 @@
|
|||
|
||||
package org.eclipse.jetty.server;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.servlet.MultipartConfigElement;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequestEvent;
|
||||
|
@ -52,15 +43,23 @@ import org.eclipse.jetty.server.handler.ContextHandler;
|
|||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.MultiPartInputStreamParser;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.Utf8Appendable;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.log.StdErrLog;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class RequestTest
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(RequestTest.class);
|
||||
|
@ -100,24 +99,11 @@ public class RequestTest
|
|||
@Override
|
||||
public boolean check(HttpServletRequest request,HttpServletResponse response)
|
||||
{
|
||||
Map map = null;
|
||||
try
|
||||
{
|
||||
//do the parse
|
||||
request.getParameterMap();
|
||||
Assert.fail("Expected parsing failure");
|
||||
return false;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
//catch the error and check the param map is not null
|
||||
map = request.getParameterMap();
|
||||
assertFalse(map == null);
|
||||
assertTrue(map.isEmpty());
|
||||
|
||||
Enumeration names = request.getParameterNames();
|
||||
assertFalse(names.hasMoreElements());
|
||||
}
|
||||
Map<String,String[]> map = null;
|
||||
//do the parse
|
||||
map = request.getParameterMap();
|
||||
assertEquals("aaa"+Utf8Appendable.REPLACEMENT+"bbb",map.get("param")[0]);
|
||||
assertEquals("value",map.get("other")[0]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -125,7 +111,7 @@ public class RequestTest
|
|||
|
||||
//Send a request with query string with illegal hex code to cause
|
||||
//an exception parsing the params
|
||||
String request="GET /?param=%ZZaaa HTTP/1.1\r\n"+
|
||||
String request="GET /?param=aaa%ZZbbb&other=value HTTP/1.1\r\n"+
|
||||
"Host: whatever\r\n"+
|
||||
"Content-Type: text/html;charset=utf8\n"+
|
||||
"Connection: close\n"+
|
||||
|
@ -237,6 +223,7 @@ public class RequestTest
|
|||
"Host: whatever\r\n"+
|
||||
"Content-Type: multipart/form-data; boundary=\"AaB03x\"\r\n"+
|
||||
"Content-Length: "+multipart.getBytes().length+"\r\n"+
|
||||
"Connection: close\r\n"+
|
||||
"\r\n"+
|
||||
multipart;
|
||||
|
||||
|
@ -363,12 +350,13 @@ public class RequestTest
|
|||
};
|
||||
|
||||
results.clear();
|
||||
_connector.getResponses(
|
||||
String response=_connector.getResponses(
|
||||
"GET / HTTP/1.1\n"+
|
||||
"Host: myhost\n"+
|
||||
"Connection: close\n"+
|
||||
"\n");
|
||||
int i=0;
|
||||
assertThat(response,Matchers.containsString("200 OK"));
|
||||
assertEquals("http://myhost/",results.get(i++));
|
||||
assertEquals("0.0.0.0",results.get(i++));
|
||||
assertEquals("myhost",results.get(i++));
|
||||
|
@ -376,12 +364,13 @@ public class RequestTest
|
|||
|
||||
|
||||
results.clear();
|
||||
_connector.getResponses(
|
||||
response=_connector.getResponses(
|
||||
"GET / HTTP/1.1\n"+
|
||||
"Host: myhost:8888\n"+
|
||||
"Connection: close\n"+
|
||||
"\n");
|
||||
i=0;
|
||||
assertThat(response,Matchers.containsString("200 OK"));
|
||||
assertEquals("http://myhost:8888/",results.get(i++));
|
||||
assertEquals("0.0.0.0",results.get(i++));
|
||||
assertEquals("myhost",results.get(i++));
|
||||
|
@ -389,13 +378,14 @@ public class RequestTest
|
|||
|
||||
|
||||
results.clear();
|
||||
_connector.getResponses(
|
||||
response=_connector.getResponses(
|
||||
"GET / HTTP/1.1\n"+
|
||||
"Host: 1.2.3.4\n"+
|
||||
"Connection: close\n"+
|
||||
"\n");
|
||||
i=0;
|
||||
|
||||
|
||||
assertThat(response,Matchers.containsString("200 OK"));
|
||||
assertEquals("http://1.2.3.4/",results.get(i++));
|
||||
assertEquals("0.0.0.0",results.get(i++));
|
||||
assertEquals("1.2.3.4",results.get(i++));
|
||||
|
@ -403,12 +393,13 @@ public class RequestTest
|
|||
|
||||
|
||||
results.clear();
|
||||
_connector.getResponses(
|
||||
response=_connector.getResponses(
|
||||
"GET / HTTP/1.1\n"+
|
||||
"Host: 1.2.3.4:8888\n"+
|
||||
"Connection: close\n"+
|
||||
"\n");
|
||||
i=0;
|
||||
assertThat(response,Matchers.containsString("200 OK"));
|
||||
assertEquals("http://1.2.3.4:8888/",results.get(i++));
|
||||
assertEquals("0.0.0.0",results.get(i++));
|
||||
assertEquals("1.2.3.4",results.get(i++));
|
||||
|
@ -416,12 +407,13 @@ public class RequestTest
|
|||
|
||||
|
||||
results.clear();
|
||||
_connector.getResponses(
|
||||
response=_connector.getResponses(
|
||||
"GET / HTTP/1.1\n"+
|
||||
"Host: [::1]\n"+
|
||||
"Connection: close\n"+
|
||||
"\n");
|
||||
i=0;
|
||||
assertThat(response,Matchers.containsString("200 OK"));
|
||||
assertEquals("http://[::1]/",results.get(i++));
|
||||
assertEquals("0.0.0.0",results.get(i++));
|
||||
assertEquals("::1",results.get(i++));
|
||||
|
@ -429,12 +421,13 @@ public class RequestTest
|
|||
|
||||
|
||||
results.clear();
|
||||
_connector.getResponses(
|
||||
response=_connector.getResponses(
|
||||
"GET / HTTP/1.1\n"+
|
||||
"Host: [::1]:8888\n"+
|
||||
"Connection: close\n"+
|
||||
"\n");
|
||||
i=0;
|
||||
assertThat(response,Matchers.containsString("200 OK"));
|
||||
assertEquals("http://[::1]:8888/",results.get(i++));
|
||||
assertEquals("0.0.0.0",results.get(i++));
|
||||
assertEquals("::1",results.get(i++));
|
||||
|
@ -442,7 +435,7 @@ public class RequestTest
|
|||
|
||||
|
||||
results.clear();
|
||||
_connector.getResponses(
|
||||
response=_connector.getResponses(
|
||||
"GET / HTTP/1.1\n"+
|
||||
"Host: [::1]\n"+
|
||||
"x-forwarded-for: remote\n"+
|
||||
|
@ -450,6 +443,7 @@ public class RequestTest
|
|||
"Connection: close\n"+
|
||||
"\n");
|
||||
i=0;
|
||||
assertThat(response,Matchers.containsString("200 OK"));
|
||||
assertEquals("https://[::1]/",results.get(i++));
|
||||
assertEquals("remote",results.get(i++));
|
||||
assertEquals("::1",results.get(i++));
|
||||
|
@ -457,7 +451,7 @@ public class RequestTest
|
|||
|
||||
|
||||
results.clear();
|
||||
_connector.getResponses(
|
||||
response=_connector.getResponses(
|
||||
"GET / HTTP/1.1\n"+
|
||||
"Host: [::1]:8888\n"+
|
||||
"Connection: close\n"+
|
||||
|
@ -465,18 +459,11 @@ public class RequestTest
|
|||
"x-forwarded-proto: https\n"+
|
||||
"\n");
|
||||
i=0;
|
||||
|
||||
assertThat(response,Matchers.containsString("200 OK"));
|
||||
assertEquals("https://[::1]:8888/",results.get(i++));
|
||||
assertEquals("remote",results.get(i++));
|
||||
assertEquals("::1",results.get(i++));
|
||||
assertEquals("8888",results.get(i++));
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1046,6 +1033,12 @@ public class RequestTest
|
|||
}
|
||||
}
|
||||
|
||||
@Test(expected = UnsupportedEncodingException.class)
|
||||
public void testNotSupportedCharacterEncoding() throws UnsupportedEncodingException
|
||||
{
|
||||
Request request = new Request(null, null);
|
||||
request.setCharacterEncoding("doesNotExist");
|
||||
}
|
||||
|
||||
interface RequestTester
|
||||
{
|
||||
|
|
|
@ -601,7 +601,7 @@ public class ResponseTest
|
|||
|
||||
String set = response.getHttpFields().getStringField("Set-Cookie");
|
||||
|
||||
assertEquals("name=value;Path=/path;Domain=domain;Secure;HttpOnly;Comment=comment", set);
|
||||
assertEquals("name=value;Version=1;Path=/path;Domain=domain;Secure;HttpOnly;Comment=comment", set);
|
||||
}
|
||||
|
||||
|
||||
|
@ -630,7 +630,7 @@ public class ResponseTest
|
|||
assertNotNull(set);
|
||||
ArrayList<String> list = Collections.list(set);
|
||||
assertEquals(2, list.size());
|
||||
assertTrue(list.contains("name=value;Path=/path;Domain=domain;Secure;HttpOnly;Comment=comment"));
|
||||
assertTrue(list.contains("name=value;Version=1;Path=/path;Domain=domain;Secure;HttpOnly;Comment=comment"));
|
||||
assertTrue(list.contains("name2=value2;Path=/path;Domain=domain"));
|
||||
|
||||
//get rid of the cookies
|
||||
|
|
|
@ -23,6 +23,8 @@ import static org.junit.Assert.assertFalse;
|
|||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
@ -32,60 +34,121 @@ import org.eclipse.jetty.server.Connector;
|
|||
import org.eclipse.jetty.server.LocalConnector;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.util.ArrayUtil;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ContextHandlerCollectionTest
|
||||
{
|
||||
@Test
|
||||
public void testVirtualHostNormalization() throws Exception
|
||||
public void testVirtualHosts() throws Exception
|
||||
{
|
||||
Server server = new Server();
|
||||
LocalConnector connector = new LocalConnector(server);
|
||||
LocalConnector connector0 = new LocalConnector(server);
|
||||
LocalConnector connector1 = new LocalConnector(server);
|
||||
connector1.setName("connector1");
|
||||
|
||||
server.setConnectors(new Connector[]
|
||||
{ connector });
|
||||
{ connector0,connector1});
|
||||
|
||||
ContextHandler contextA = new ContextHandler("/");
|
||||
ContextHandler contextA = new ContextHandler("/ctx");
|
||||
contextA.setVirtualHosts(new String[]
|
||||
{ "www.example.com" });
|
||||
IsHandledHandler handlerA = new IsHandledHandler();
|
||||
{ "www.example.com", "alias.example.com" });
|
||||
IsHandledHandler handlerA = new IsHandledHandler("A");
|
||||
contextA.setHandler(handlerA);
|
||||
contextA.setAllowNullPathInfo(true);
|
||||
|
||||
ContextHandler contextB = new ContextHandler("/");
|
||||
IsHandledHandler handlerB = new IsHandledHandler();
|
||||
ContextHandler contextB = new ContextHandler("/ctx");
|
||||
IsHandledHandler handlerB = new IsHandledHandler("B");
|
||||
contextB.setHandler(handlerB);
|
||||
contextB.setVirtualHosts(new String[]
|
||||
{ "www.example2.com." });
|
||||
{ "*.other.com" , "@connector1"});
|
||||
|
||||
ContextHandler contextC = new ContextHandler("/");
|
||||
IsHandledHandler handlerC = new IsHandledHandler();
|
||||
ContextHandler contextC = new ContextHandler("/ctx");
|
||||
IsHandledHandler handlerC = new IsHandledHandler("C");
|
||||
contextC.setHandler(handlerC);
|
||||
|
||||
ContextHandlerCollection c = new ContextHandlerCollection();
|
||||
ContextHandler contextD = new ContextHandler("/");
|
||||
IsHandledHandler handlerD = new IsHandledHandler("D");
|
||||
contextD.setHandler(handlerD);
|
||||
|
||||
ContextHandler contextE = new ContextHandler("/ctx/foo");
|
||||
IsHandledHandler handlerE = new IsHandledHandler("E");
|
||||
contextE.setHandler(handlerE);
|
||||
|
||||
ContextHandler contextF = new ContextHandler("/ctxlong");
|
||||
IsHandledHandler handlerF = new IsHandledHandler("F");
|
||||
contextF.setHandler(handlerF);
|
||||
|
||||
ContextHandlerCollection c = new ContextHandlerCollection();
|
||||
c.addHandler(contextA);
|
||||
c.addHandler(contextB);
|
||||
c.addHandler(contextC);
|
||||
|
||||
|
||||
HandlerList list = new HandlerList();
|
||||
list.addHandler(contextD);
|
||||
list.addHandler(contextE);
|
||||
list.addHandler(contextF);
|
||||
c.addHandler(list);
|
||||
|
||||
server.setHandler(c);
|
||||
|
||||
try
|
||||
{
|
||||
server.start();
|
||||
connector.getResponses("GET / HTTP/1.0\n" + "Host: www.example.com.\n\n");
|
||||
|
||||
Object[][] tests = new Object[][] {
|
||||
{connector0,"www.example.com.", "/ctx", handlerA},
|
||||
{connector0,"www.example.com.", "/ctx/", handlerA},
|
||||
{connector0,"www.example.com.", "/ctx/info", handlerA},
|
||||
{connector0,"www.example.com", "/ctx/info", handlerA},
|
||||
{connector0,"alias.example.com", "/ctx/info", handlerA},
|
||||
{connector1,"www.example.com.", "/ctx/info", handlerA},
|
||||
{connector1,"www.example.com", "/ctx/info", handlerA},
|
||||
{connector1,"alias.example.com", "/ctx/info", handlerA},
|
||||
|
||||
assertTrue(handlerA.isHandled());
|
||||
assertFalse(handlerB.isHandled());
|
||||
assertFalse(handlerC.isHandled());
|
||||
{connector1,"www.other.com", "/ctx", null},
|
||||
{connector1,"www.other.com", "/ctx/", handlerB},
|
||||
{connector1,"www.other.com", "/ctx/info", handlerB},
|
||||
{connector0,"www.other.com", "/ctx/info", handlerC},
|
||||
|
||||
{connector0,"www.example.com", "/ctxinfo", handlerD},
|
||||
{connector1,"unknown.com", "/unknown", handlerD},
|
||||
|
||||
{connector0,"alias.example.com", "/ctx/foo/info", handlerE},
|
||||
{connector0,"alias.example.com", "/ctxlong/info", handlerF},
|
||||
};
|
||||
|
||||
for (int i=0;i<tests.length;i++)
|
||||
{
|
||||
Object[] test=tests[i];
|
||||
LocalConnector connector = (LocalConnector)test[0];
|
||||
String host=(String)test[1];
|
||||
String uri=(String)test[2];
|
||||
IsHandledHandler handler = (IsHandledHandler)test[3];
|
||||
|
||||
handlerA.reset();
|
||||
handlerB.reset();
|
||||
handlerC.reset();
|
||||
handlerA.reset();
|
||||
handlerB.reset();
|
||||
handlerC.reset();
|
||||
handlerD.reset();
|
||||
handlerE.reset();
|
||||
handlerF.reset();
|
||||
|
||||
connector.getResponses("GET / HTTP/1.0\n" + "Host: www.example2.com\n\n");
|
||||
|
||||
assertFalse(handlerA.isHandled());
|
||||
assertTrue(handlerB.isHandled());
|
||||
assertFalse(handlerC.isHandled());
|
||||
// System.err.printf("test %d %s@%s --> %s | %s%n",i,uri,host,connector.getName(),handler);
|
||||
String response = connector.getResponses("GET "+uri+" HTTP/1.0\nHost: "+host+"\n\n");
|
||||
|
||||
if (handler==null)
|
||||
{
|
||||
Assert.assertThat(response,Matchers.containsString(" 302 "));
|
||||
}
|
||||
else if (!handler.isHandled())
|
||||
{
|
||||
System.err.printf("FAILED %d %s@%s --> %s | %s%n",i,uri,host,connector.getName(),handler);
|
||||
System.err.println(response);
|
||||
Assert.fail();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
finally
|
||||
|
@ -103,7 +166,7 @@ public class ContextHandlerCollectionTest
|
|||
|
||||
ContextHandler context = new ContextHandler("/");
|
||||
|
||||
IsHandledHandler handler = new IsHandledHandler();
|
||||
IsHandledHandler handler = new IsHandledHandler("H");
|
||||
context.setHandler(handler);
|
||||
|
||||
ContextHandlerCollection c = new ContextHandlerCollection();
|
||||
|
@ -149,7 +212,10 @@ public class ContextHandlerCollectionTest
|
|||
|
||||
for(String host : requestHosts)
|
||||
{
|
||||
connector.getResponses("GET / HTTP/1.0\n" + "Host: "+host+"\nConnection:close\n\n");
|
||||
// System.err.printf("host=%s in %s%n",host,contextHosts==null?Collections.emptyList():Arrays.asList(contextHosts));
|
||||
|
||||
String response=connector.getResponses("GET / HTTP/1.0\n" + "Host: "+host+"\nConnection:close\n\n");
|
||||
// System.err.println(response);
|
||||
if(succeed)
|
||||
assertTrue("'"+host+"' should have been handled.",handler.isHandled());
|
||||
else
|
||||
|
@ -166,17 +232,17 @@ public class ContextHandlerCollectionTest
|
|||
Server server = new Server();
|
||||
|
||||
ContextHandler contextA = new ContextHandler("/a");
|
||||
IsHandledHandler handlerA = new IsHandledHandler();
|
||||
IsHandledHandler handlerA = new IsHandledHandler("A");
|
||||
contextA.setHandler(handlerA);
|
||||
|
||||
ContextHandler contextB = new ContextHandler("/b");
|
||||
IsHandledHandler handlerB = new IsHandledHandler();
|
||||
IsHandledHandler handlerB = new IsHandledHandler("B");
|
||||
HandlerWrapper wrapperB = new HandlerWrapper();
|
||||
wrapperB.setHandler(handlerB);
|
||||
contextB.setHandler(wrapperB);
|
||||
|
||||
ContextHandler contextC = new ContextHandler("/c");
|
||||
IsHandledHandler handlerC = new IsHandledHandler();
|
||||
IsHandledHandler handlerC = new IsHandledHandler("C");
|
||||
contextC.setHandler(handlerC);
|
||||
|
||||
ContextHandlerCollection collection = new ContextHandlerCollection();
|
||||
|
@ -202,22 +268,36 @@ public class ContextHandlerCollectionTest
|
|||
private static final class IsHandledHandler extends AbstractHandler
|
||||
{
|
||||
private boolean handled;
|
||||
private final String name;
|
||||
|
||||
public IsHandledHandler(String string)
|
||||
{
|
||||
name=string;
|
||||
}
|
||||
|
||||
public boolean isHandled()
|
||||
{
|
||||
return handled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(String s, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
this.handled = true;
|
||||
response.getWriter().print(name);
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
handled = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -102,7 +102,7 @@ public class HashSessionManagerTest
|
|||
Assert.assertTrue(testDir.exists());
|
||||
Assert.assertTrue(testDir.canWrite());
|
||||
|
||||
HashSessionIdManager idManager = new HashSessionIdManager();
|
||||
AbstractSessionIdManager idManager = new HashSessionIdManager();
|
||||
idManager.setWorkerName("foo");
|
||||
manager.setSessionIdManager(idManager);
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@ public class SessionCookieTest
|
|||
/**
|
||||
* @see org.eclipse.jetty.server.SessionIdManager#idInUse(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public boolean idInUse(String id)
|
||||
{
|
||||
return false;
|
||||
|
@ -66,6 +67,7 @@ public class SessionCookieTest
|
|||
/**
|
||||
* @see org.eclipse.jetty.server.SessionIdManager#addSession(javax.servlet.http.HttpSession)
|
||||
*/
|
||||
@Override
|
||||
public void addSession(HttpSession session)
|
||||
{
|
||||
|
||||
|
@ -74,6 +76,7 @@ public class SessionCookieTest
|
|||
/**
|
||||
* @see org.eclipse.jetty.server.SessionIdManager#removeSession(javax.servlet.http.HttpSession)
|
||||
*/
|
||||
@Override
|
||||
public void removeSession(HttpSession session)
|
||||
{
|
||||
|
||||
|
@ -82,28 +85,12 @@ public class SessionCookieTest
|
|||
/**
|
||||
* @see org.eclipse.jetty.server.SessionIdManager#invalidateAll(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void invalidateAll(String id)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.eclipse.jetty.server.SessionIdManager#getClusterId(java.lang.String)
|
||||
*/
|
||||
public String getClusterId(String nodeId)
|
||||
{
|
||||
int dot=nodeId.lastIndexOf('.');
|
||||
return (dot>0)?nodeId.substring(0,dot):nodeId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.eclipse.jetty.server.SessionIdManager#getNodeId(java.lang.String, javax.servlet.http.HttpServletRequest)
|
||||
*/
|
||||
public String getNodeId(String clusterId, HttpServletRequest request)
|
||||
{
|
||||
return clusterId+'.'+_workerName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renewSessionId(String oldClusterId, String oldNodeId, HttpServletRequest request)
|
||||
{
|
||||
|
@ -119,6 +106,7 @@ public class SessionCookieTest
|
|||
/**
|
||||
* @see org.eclipse.jetty.server.session.AbstractSessionManager#addSession(org.eclipse.jetty.server.session.AbstractSession)
|
||||
*/
|
||||
@Override
|
||||
protected void addSession(AbstractSession session)
|
||||
{
|
||||
|
||||
|
@ -127,6 +115,7 @@ public class SessionCookieTest
|
|||
/**
|
||||
* @see org.eclipse.jetty.server.session.AbstractSessionManager#getSession(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public AbstractSession getSession(String idInCluster)
|
||||
{
|
||||
return null;
|
||||
|
@ -135,6 +124,7 @@ public class SessionCookieTest
|
|||
/**
|
||||
* @see org.eclipse.jetty.server.session.AbstractSessionManager#invalidateSessions()
|
||||
*/
|
||||
@Override
|
||||
protected void invalidateSessions() throws Exception
|
||||
{
|
||||
|
||||
|
@ -143,6 +133,7 @@ public class SessionCookieTest
|
|||
/**
|
||||
* @see org.eclipse.jetty.server.session.AbstractSessionManager#newSession(javax.servlet.http.HttpServletRequest)
|
||||
*/
|
||||
@Override
|
||||
protected AbstractSession newSession(HttpServletRequest request)
|
||||
{
|
||||
return null;
|
||||
|
@ -151,6 +142,7 @@ public class SessionCookieTest
|
|||
/**
|
||||
* @see org.eclipse.jetty.server.session.AbstractSessionManager#removeSession(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
protected boolean removeSession(String idInCluster)
|
||||
{
|
||||
return false;
|
||||
|
|
|
@ -21,6 +21,8 @@ package org.eclipse.jetty.servlet;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.Socket;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import javax.servlet.AsyncContext;
|
||||
|
@ -33,9 +35,14 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.RequestLog;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.handler.RequestLogHandler;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
|
@ -53,15 +60,28 @@ public class AsyncServletTest
|
|||
protected Server _server = new Server();
|
||||
protected ServletHandler _servletHandler;
|
||||
protected ServerConnector _connector;
|
||||
protected List<String> _log;
|
||||
protected int _expectedLogs;
|
||||
protected String _expectedCode;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception
|
||||
{
|
||||
_connector = new ServerConnector(_server);
|
||||
_server.setConnectors(new Connector[]{ _connector });
|
||||
|
||||
_log=new ArrayList<>();
|
||||
RequestLog log=new Log();
|
||||
RequestLogHandler logHandler = new RequestLogHandler();
|
||||
logHandler.setRequestLog(log);
|
||||
_server.setHandler(logHandler);
|
||||
_expectedLogs=1;
|
||||
_expectedCode="200 ";
|
||||
|
||||
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
|
||||
context.setContextPath("/ctx");
|
||||
_server.setHandler(context);
|
||||
logHandler.setHandler(context);
|
||||
|
||||
_servletHandler=context.getServletHandler();
|
||||
ServletHolder holder=new ServletHolder(_servlet);
|
||||
holder.setAsyncSupported(true);
|
||||
|
@ -73,6 +93,8 @@ public class AsyncServletTest
|
|||
@After
|
||||
public void tearDown() throws Exception
|
||||
{
|
||||
assertEquals(_expectedLogs,_log.size());
|
||||
Assert.assertThat(_log.get(0),Matchers.containsString(_expectedCode));
|
||||
_server.stop();
|
||||
}
|
||||
|
||||
|
@ -105,6 +127,7 @@ public class AsyncServletTest
|
|||
@Test
|
||||
public void testSuspend() throws Exception
|
||||
{
|
||||
_expectedCode="500 ";
|
||||
String response=process("suspend=200",null);
|
||||
assertEquals("HTTP/1.1 500 Async Timeout",response.substring(0,26));
|
||||
assertContains(
|
||||
|
@ -258,6 +281,7 @@ public class AsyncServletTest
|
|||
@Test
|
||||
public void testSuspendWaitResumeSuspend() throws Exception
|
||||
{
|
||||
_expectedCode="500 ";
|
||||
String response=process("suspend=1000&resume=10&suspend2=10",null);
|
||||
assertEquals("HTTP/1.1 500 Async Timeout",response.substring(0,26));
|
||||
assertContains(
|
||||
|
@ -316,6 +340,7 @@ public class AsyncServletTest
|
|||
@Test
|
||||
public void testSuspendTimeoutSuspend() throws Exception
|
||||
{
|
||||
_expectedCode="500 ";
|
||||
String response=process("suspend=10&suspend2=10",null);
|
||||
assertContains(
|
||||
"history: REQUEST\r\n"+
|
||||
|
@ -335,6 +360,7 @@ public class AsyncServletTest
|
|||
@Test
|
||||
public void testAsyncRead() throws Exception
|
||||
{
|
||||
_expectedLogs=2;
|
||||
String header="GET /ctx/path/info?suspend=2000&resume=1500 HTTP/1.1\r\n"+
|
||||
"Host: localhost\r\n"+
|
||||
"Content-Length: 10\r\n"+
|
||||
|
@ -691,4 +717,13 @@ public class AsyncServletTest
|
|||
((HttpServletResponse)event.getSuppliedResponse()).addHeader("history","onComplete");
|
||||
}
|
||||
};
|
||||
|
||||
class Log extends AbstractLifeCycle implements RequestLog
|
||||
{
|
||||
@Override
|
||||
public void log(Request request, Response response)
|
||||
{
|
||||
_log.add(response.getStatus()+" "+response.getContentCount()+" "+request.getRequestURI());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
package org.eclipse.jetty.servlets;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
@ -1066,8 +1067,10 @@ public class DoSFilter implements Filter
|
|||
* A RateTracker is associated with a connection, and stores request rate
|
||||
* data.
|
||||
*/
|
||||
class RateTracker extends Timeout.Task implements HttpSessionBindingListener, HttpSessionActivationListener
|
||||
class RateTracker extends Timeout.Task implements HttpSessionBindingListener, HttpSessionActivationListener, Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 3534663738034577872L;
|
||||
|
||||
transient protected final String _id;
|
||||
transient protected final int _type;
|
||||
transient protected final long[] _timestamps;
|
||||
|
|
|
@ -45,10 +45,12 @@ import javax.servlet.http.HttpServletRequestWrapper;
|
|||
import javax.servlet.http.Part;
|
||||
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.http.MimeTypes;
|
||||
import org.eclipse.jetty.util.LazyList;
|
||||
import org.eclipse.jetty.util.MultiMap;
|
||||
import org.eclipse.jetty.util.MultiPartInputStreamParser;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.TypeUtil;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
|
@ -259,11 +261,11 @@ public class MultiPartFilter implements Filter
|
|||
{
|
||||
try
|
||||
{
|
||||
return new String((byte[])o,_encoding);
|
||||
return getParameterBytesAsString(name, (byte[])o);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
LOG.warn(e);
|
||||
}
|
||||
}
|
||||
else if (o!=null)
|
||||
|
@ -282,9 +284,7 @@ public class MultiPartFilter implements Filter
|
|||
|
||||
for ( Object key : _params.keySet() )
|
||||
{
|
||||
String[] a = LazyList.toStringArray(getParameter((String)key));
|
||||
cmap.put((String)key,a);
|
||||
|
||||
cmap.put((String)key,getParameterValues((String)key));
|
||||
}
|
||||
|
||||
return Collections.unmodifiableMap(cmap);
|
||||
|
@ -318,7 +318,7 @@ public class MultiPartFilter implements Filter
|
|||
{
|
||||
try
|
||||
{
|
||||
v[i]=new String((byte[])o,_encoding);
|
||||
v[i]=getParameterBytesAsString(name, (byte[])o);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
|
@ -341,5 +341,23 @@ public class MultiPartFilter implements Filter
|
|||
{
|
||||
_encoding=enc;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
private String getParameterBytesAsString (String name, byte[] bytes)
|
||||
throws UnsupportedEncodingException
|
||||
{
|
||||
//check if there is a specific encoding for the parameter
|
||||
Object ct = _params.getValue(name+CONTENT_TYPE_SUFFIX,0);
|
||||
//use default if not
|
||||
String contentType = _encoding;
|
||||
if (ct != null)
|
||||
{
|
||||
String tmp = MimeTypes.getCharsetFromContentType((String)ct);
|
||||
contentType = (tmp == null?_encoding:tmp);
|
||||
}
|
||||
|
||||
return new String(bytes,contentType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -243,7 +243,7 @@ public abstract class CompressedResponseWrapper extends HttpServletResponseWrapp
|
|||
if (_writer!=null)
|
||||
_writer.flush();
|
||||
if (_compressedStream!=null)
|
||||
_compressedStream.finish();
|
||||
_compressedStream.flush();
|
||||
else
|
||||
getResponse().flushBuffer();
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.eclipse.jetty.servlets.gzip.GzipTester;
|
|||
import org.eclipse.jetty.servlets.gzip.TestServletLengthStreamTypeWrite;
|
||||
import org.eclipse.jetty.servlets.gzip.TestServletLengthTypeStreamWrite;
|
||||
import org.eclipse.jetty.servlets.gzip.TestServletStreamLengthTypeWrite;
|
||||
import org.eclipse.jetty.servlets.gzip.TestServletStreamLengthTypeWriteWithFlush;
|
||||
import org.eclipse.jetty.servlets.gzip.TestServletStreamTypeLengthWrite;
|
||||
import org.eclipse.jetty.servlets.gzip.TestServletTypeLengthStreamWrite;
|
||||
import org.eclipse.jetty.servlets.gzip.TestServletTypeStreamLengthWrite;
|
||||
|
@ -73,12 +74,14 @@ public class GzipFilterContentLengthTest
|
|||
{ TestServletLengthStreamTypeWrite.class, GzipFilter.GZIP },
|
||||
{ TestServletLengthTypeStreamWrite.class, GzipFilter.GZIP },
|
||||
{ TestServletStreamLengthTypeWrite.class, GzipFilter.GZIP },
|
||||
{ TestServletStreamLengthTypeWriteWithFlush.class, GzipFilter.GZIP },
|
||||
{ TestServletStreamTypeLengthWrite.class, GzipFilter.GZIP },
|
||||
{ TestServletTypeLengthStreamWrite.class, GzipFilter.GZIP },
|
||||
{ TestServletTypeStreamLengthWrite.class, GzipFilter.GZIP },
|
||||
{ TestServletLengthStreamTypeWrite.class, GzipFilter.DEFLATE },
|
||||
{ TestServletLengthTypeStreamWrite.class, GzipFilter.DEFLATE },
|
||||
{ TestServletStreamLengthTypeWrite.class, GzipFilter.DEFLATE },
|
||||
{ TestServletStreamLengthTypeWriteWithFlush.class, GzipFilter.DEFLATE },
|
||||
{ TestServletStreamTypeLengthWrite.class, GzipFilter.DEFLATE },
|
||||
{ TestServletTypeLengthStreamWrite.class, GzipFilter.DEFLATE },
|
||||
{ TestServletTypeStreamLengthWrite.class, GzipFilter.DEFLATE }
|
||||
|
|
|
@ -26,11 +26,13 @@ import static org.junit.Assert.assertNotNull;
|
|||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.ServletException;
|
||||
|
@ -38,10 +40,12 @@ import javax.servlet.http.HttpServlet;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
||||
import org.eclipse.jetty.http.HttpTester;
|
||||
import org.eclipse.jetty.servlet.FilterHolder;
|
||||
import org.eclipse.jetty.servlet.ServletTester;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
@ -105,6 +109,7 @@ public class MultipartFilterTest
|
|||
public void tearDown() throws Exception
|
||||
{
|
||||
tester.stop();
|
||||
tester=null;
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -698,8 +703,6 @@ public class MultipartFilterTest
|
|||
assertTrue(response.getContent().contains("aaaa,bbbbb"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testContentTypeWithCharSet() throws Exception
|
||||
{
|
||||
// generated and parsed test
|
||||
|
@ -730,6 +733,39 @@ public class MultipartFilterTest
|
|||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testBufferOverflowNoCRLF () throws Exception
|
||||
{
|
||||
String boundary="XyXyXy";
|
||||
// generated and parsed test
|
||||
HttpTester.Request request = HttpTester.newRequest();
|
||||
HttpTester.Response response;
|
||||
tester.addServlet(BoundaryServlet.class,"/testb");
|
||||
tester.setAttribute("fileName", "abc");
|
||||
tester.setAttribute("desc", "123");
|
||||
tester.setAttribute("title", "ttt");
|
||||
request.setMethod("POST");
|
||||
request.setVersion("HTTP/1.0");
|
||||
request.setHeader("Host","tester");
|
||||
request.setURI("/context/testb");
|
||||
request.setHeader("Content-Type","multipart/form-data; boundary="+boundary);
|
||||
|
||||
String content = "--XyXyXy";
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
baos.write(content.getBytes());
|
||||
|
||||
for (int i=0; i< 8500; i++) //create content that will overrun default buffer size of BufferedInputStream
|
||||
{
|
||||
baos.write('a');
|
||||
}
|
||||
request.setContent(baos.toString());
|
||||
|
||||
response = HttpTester.parseResponse(tester.getResponses(request.generate()));
|
||||
assertTrue(response.getContent().contains("Buffer size exceeded"));
|
||||
assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, response.getStatus());
|
||||
}
|
||||
|
||||
/*
|
||||
* see the testParameterMap test
|
||||
*
|
||||
|
@ -786,6 +822,59 @@ public class MultipartFilterTest
|
|||
assertTrue(response.getContent().indexOf("brown cow")>=0);
|
||||
}
|
||||
|
||||
public static class TestServletCharSet extends HttpServlet
|
||||
{
|
||||
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
|
||||
{
|
||||
//test that the multipart content bytes were converted correctly from their charset to unicode
|
||||
String content = (String)req.getParameter("ttt");
|
||||
assertNotNull(content);
|
||||
assertEquals("ttt\u01FCzzz",content);
|
||||
assertEquals("application/octet-stream; charset=UTF-8",req.getParameter("ttt"+MultiPartFilter.CONTENT_TYPE_SUFFIX));
|
||||
|
||||
|
||||
//test that the parameter map retrieves values as String[]
|
||||
Map map = req.getParameterMap();
|
||||
Object o = map.get("ttt");
|
||||
assertTrue(o.getClass().isArray());
|
||||
super.doPost(req, resp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testWithCharSet()
|
||||
throws Exception
|
||||
{
|
||||
// generated and parsed test
|
||||
HttpTester.Request request = HttpTester.newRequest();
|
||||
HttpTester.Response response;
|
||||
tester.addServlet(TestServletCharSet.class,"/test3");
|
||||
|
||||
// test GET
|
||||
request.setMethod("POST");
|
||||
request.setVersion("HTTP/1.0");
|
||||
request.setHeader("Host","tester");
|
||||
request.setURI("/context/test3");
|
||||
|
||||
String boundary="XyXyXy";
|
||||
request.setHeader("Content-Type","multipart/form-data; boundary="+boundary);
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
baos.write(("--" + boundary + "\r\n"+
|
||||
"Content-Disposition: form-data; name=\"ttt\"\r\n"+
|
||||
"Content-Type: application/octet-stream; charset=UTF-8\r\n\r\n").getBytes());
|
||||
baos.write("ttt\u01FCzzz".getBytes(StringUtil.__UTF8));
|
||||
baos.write(("\r\n--" + boundary + "--\r\n\r\n").getBytes());
|
||||
|
||||
|
||||
request.setContent(baos.toByteArray());
|
||||
response = HttpTester.parseResponse(tester.getResponses(request.generate()));
|
||||
}
|
||||
|
||||
public static class DumpServlet extends HttpServlet
|
||||
{
|
||||
private static final long serialVersionUID = 201012011130L;
|
||||
|
|
|
@ -18,13 +18,8 @@
|
|||
|
||||
package org.eclipse.jetty.servlets.gzip;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
@ -113,7 +108,20 @@ public class GzipTester
|
|||
|
||||
// Assert the response headers
|
||||
// Assert.assertThat("Response.status",response.getStatus(),is(HttpServletResponse.SC_OK));
|
||||
Assert.assertThat("Response.header[Content-Length]",response.get("Content-Length"),notNullValue());
|
||||
|
||||
// Response headers should have either a Transfer-Encoding indicating chunked OR a Content-Length
|
||||
String contentLength = response.get("Content-Length");
|
||||
String transferEncoding = response.get("Transfer-Encoding");
|
||||
|
||||
/* TODO need to check for the 3rd option of EOF content. To do this properly you might need to look at both HTTP/1.1 and HTTP/1.0 requests
|
||||
boolean chunked = (transferEncoding != null) && (transferEncoding.indexOf("chunk") >= 0);
|
||||
if(!chunked) {
|
||||
Assert.assertThat("Response.header[Content-Length]",contentLength,notNullValue());
|
||||
} else {
|
||||
Assert.assertThat("Response.header[Transfer-Encoding]",transferEncoding,notNullValue());
|
||||
}
|
||||
*/
|
||||
|
||||
int qindex = compressionType.indexOf(";");
|
||||
if (qindex < 0)
|
||||
Assert.assertThat("Response.header[Content-Encoding]",response.get("Content-Encoding"),containsString(compressionType));
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 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.servlets.gzip;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.servlets.GzipFilter;
|
||||
|
||||
/**
|
||||
* A sample servlet to serve static content, using a order of construction that has caused problems for
|
||||
* {@link GzipFilter} in the past.
|
||||
*
|
||||
* Using a real-world pattern of:
|
||||
*
|
||||
* <pre>
|
||||
* 1) get stream
|
||||
* 2) set content length
|
||||
* 3) set content type
|
||||
* 4) write and flush
|
||||
* </pre>
|
||||
*
|
||||
* @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class TestServletStreamLengthTypeWriteWithFlush extends TestDirContentServlet
|
||||
{
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
String fileName = request.getServletPath();
|
||||
byte[] dataBytes = loadContentFileBytes(fileName);
|
||||
|
||||
ServletOutputStream out = response.getOutputStream();
|
||||
|
||||
// set content-length of uncompressed content (GzipFilter should handle this)
|
||||
response.setContentLength(dataBytes.length);
|
||||
|
||||
if (fileName.endsWith("txt"))
|
||||
response.setContentType("text/plain");
|
||||
else if (fileName.endsWith("mp3"))
|
||||
response.setContentType("audio/mpeg");
|
||||
response.setHeader("ETag","W/etag-"+fileName);
|
||||
|
||||
for ( int i = 0 ; i < dataBytes.length ; i++)
|
||||
{
|
||||
out.write(dataBytes[i]);
|
||||
// flush using response object (not the stream itself)
|
||||
response.flushBuffer();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -218,11 +218,22 @@ public class StandardSession implements ISession, Parser.Listener, Dumpable
|
|||
if (stream != null)
|
||||
{
|
||||
stream.process(frame);
|
||||
removeFrameBytesFromQueue(stream);
|
||||
removeStream(stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removeFrameBytesFromQueue(Stream stream)
|
||||
{
|
||||
synchronized (queue)
|
||||
{
|
||||
for (FrameBytes frameBytes : queue)
|
||||
if (frameBytes.getStream() == stream)
|
||||
queue.remove(frameBytes);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void settings(SettingsInfo settingsInfo) throws ExecutionException, InterruptedException, TimeoutException
|
||||
{
|
||||
|
@ -492,7 +503,8 @@ public class StandardSession implements ISession, Parser.Listener, Dumpable
|
|||
|
||||
private void onSyn(final SynStreamFrame frame)
|
||||
{
|
||||
IStream stream = createStream(frame, null, false, new Promise.Adapter<Stream>(){
|
||||
IStream stream = createStream(frame, null, false, new Promise.Adapter<Stream>()
|
||||
{
|
||||
@Override
|
||||
public void failed(Throwable x)
|
||||
{
|
||||
|
@ -1054,7 +1066,7 @@ public class StandardSession implements ISession, Parser.Listener, Dumpable
|
|||
}
|
||||
}
|
||||
|
||||
private void append(FrameBytes frameBytes)
|
||||
void append(FrameBytes frameBytes)
|
||||
{
|
||||
Throwable failure;
|
||||
synchronized (queue)
|
||||
|
@ -1215,7 +1227,7 @@ public class StandardSession implements ISession, Parser.Listener, Dumpable
|
|||
public abstract void fail(Throwable throwable);
|
||||
}
|
||||
|
||||
private abstract class AbstractFrameBytes implements FrameBytes, Runnable
|
||||
abstract class AbstractFrameBytes implements FrameBytes, Runnable
|
||||
{
|
||||
private final IStream stream;
|
||||
private final Callback callback;
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 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.spdy.generator;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Random;
|
||||
|
||||
import org.eclipse.jetty.io.ArrayByteBufferPool;
|
||||
import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
|
||||
import org.eclipse.jetty.spdy.api.DataInfo;
|
||||
import org.eclipse.jetty.spdy.frames.DataFrame;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
@RunWith(JUnit4.class)
|
||||
public class DataFrameGeneratorTest
|
||||
{
|
||||
private int increment = 1024;
|
||||
private int streamId = 1;
|
||||
private ArrayByteBufferPool bufferPool;
|
||||
private DataFrameGenerator dataFrameGenerator;
|
||||
private ByteBuffer headerBuffer = ByteBuffer.allocate(DataFrame.HEADER_LENGTH);
|
||||
|
||||
@Before
|
||||
public void setUp()
|
||||
{
|
||||
bufferPool = new ArrayByteBufferPool(64, increment, 8192);
|
||||
dataFrameGenerator = new DataFrameGenerator(bufferPool);
|
||||
headerBuffer.putInt(0, streamId & 0x7F_FF_FF_FF);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateSmallFrame()
|
||||
{
|
||||
int bufferSize = 256;
|
||||
generateFrame(bufferSize);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateFrameWithBufferThatEqualsBucketSize()
|
||||
{
|
||||
int bufferSize = increment;
|
||||
generateFrame(bufferSize);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateFrameWithBufferThatEqualsBucketSizeMinusHeaderLength()
|
||||
{
|
||||
int bufferSize = increment - DataFrame.HEADER_LENGTH;
|
||||
generateFrame(bufferSize);
|
||||
}
|
||||
|
||||
private void generateFrame(int bufferSize)
|
||||
{
|
||||
ByteBuffer byteBuffer = createByteBuffer(bufferSize);
|
||||
ByteBufferDataInfo dataInfo = new ByteBufferDataInfo(byteBuffer, true);
|
||||
fillHeaderBuffer(bufferSize);
|
||||
ByteBuffer dataFrameBuffer = dataFrameGenerator.generate(streamId, bufferSize, dataInfo);
|
||||
|
||||
assertThat("The content size in dataFrameBuffer matches the buffersize + header length",
|
||||
dataFrameBuffer.limit(),
|
||||
is(bufferSize + DataFrame.HEADER_LENGTH));
|
||||
|
||||
byte[] headerBytes = new byte[DataFrame.HEADER_LENGTH];
|
||||
dataFrameBuffer.get(headerBytes, 0, DataFrame.HEADER_LENGTH);
|
||||
|
||||
assertThat("Header bytes are prepended", headerBytes, is(headerBuffer.array()));
|
||||
}
|
||||
|
||||
private ByteBuffer createByteBuffer(int bufferSize)
|
||||
{
|
||||
byte[] bytes = new byte[bufferSize];
|
||||
new Random().nextBytes(bytes);
|
||||
ByteBuffer byteBuffer = bufferPool.acquire(bufferSize, false);
|
||||
BufferUtil.flipToFill(byteBuffer);
|
||||
byteBuffer.put(bytes);
|
||||
BufferUtil.flipToFlush(byteBuffer, 0);
|
||||
return byteBuffer;
|
||||
}
|
||||
|
||||
private void fillHeaderBuffer(int bufferSize)
|
||||
{
|
||||
headerBuffer.putInt(4, bufferSize & 0x00_FF_FF_FF);
|
||||
headerBuffer.put(4, DataInfo.FLAG_CLOSE);
|
||||
}
|
||||
|
||||
}
|
|
@ -105,7 +105,8 @@ public class HTTPSPDYServerConnectionFactory extends SPDYServerConnectionFactory
|
|||
if (!(headers.get("accept-encoding") != null && headers.get("accept-encoding").value().contains
|
||||
("gzip")))
|
||||
headers.add("accept-encoding", "gzip");
|
||||
HttpTransportOverSPDY transport = new HttpTransportOverSPDY(connector, httpConfiguration, endPoint, pushStrategy, stream, headers);
|
||||
HttpTransportOverSPDY transport = new HttpTransportOverSPDY(connector, httpConfiguration, endPoint,
|
||||
pushStrategy, stream, headers);
|
||||
HttpInputOverSPDY input = new HttpInputOverSPDY();
|
||||
HttpChannelOverSPDY channel = new HttpChannelOverSPDY(connector, httpConfiguration, endPoint, transport, input, stream);
|
||||
stream.setAttribute(CHANNEL_ATTRIBUTE, channel);
|
||||
|
|
|
@ -28,6 +28,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.eclipse.jetty.http.HttpGenerator;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
|
@ -41,6 +43,7 @@ import org.eclipse.jetty.spdy.api.HeadersInfo;
|
|||
import org.eclipse.jetty.spdy.api.PushInfo;
|
||||
import org.eclipse.jetty.spdy.api.ReplyInfo;
|
||||
import org.eclipse.jetty.spdy.api.SPDY;
|
||||
import org.eclipse.jetty.spdy.api.Session;
|
||||
import org.eclipse.jetty.spdy.api.Stream;
|
||||
import org.eclipse.jetty.spdy.api.StreamStatus;
|
||||
import org.eclipse.jetty.util.BlockingCallback;
|
||||
|
@ -61,6 +64,7 @@ public class HttpTransportOverSPDY implements HttpTransport
|
|||
private final EndPoint endPoint;
|
||||
private final PushStrategy pushStrategy;
|
||||
private final Stream stream;
|
||||
private final short version;
|
||||
private final Fields requestHeaders;
|
||||
private final BlockingCallback streamBlocker = new BlockingCallback();
|
||||
private final AtomicBoolean committed = new AtomicBoolean();
|
||||
|
@ -73,6 +77,8 @@ public class HttpTransportOverSPDY implements HttpTransport
|
|||
this.pushStrategy = pushStrategy == null ? new PushStrategy.None() : pushStrategy;
|
||||
this.stream = stream;
|
||||
this.requestHeaders = requestHeaders;
|
||||
Session session = stream.getSession();
|
||||
this.version = session.getVersion();
|
||||
}
|
||||
|
||||
protected Stream getStream()
|
||||
|
@ -94,7 +100,7 @@ public class HttpTransportOverSPDY implements HttpTransport
|
|||
}
|
||||
|
||||
@Override
|
||||
public void send(HttpGenerator.ResponseInfo info, ByteBuffer content, boolean lastContent, Callback callback)
|
||||
public void send(HttpGenerator.ResponseInfo info, ByteBuffer content, boolean lastContent, final Callback callback)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Sending {} {} {} {} last={}", this, stream, info, BufferUtil.toDetailString(content), lastContent);
|
||||
|
@ -115,7 +121,9 @@ public class HttpTransportOverSPDY implements HttpTransport
|
|||
// info!=null content!=null lastContent==false reply, commit with content
|
||||
// info!=null content!=null lastContent==true reply, commit with content and complete
|
||||
|
||||
boolean hasContent = BufferUtil.hasContent(content);
|
||||
boolean isHeadRequest = HttpMethod.HEAD.name().equalsIgnoreCase(requestHeaders.get(HTTPSPDYHeader.METHOD.name(version)).value());
|
||||
boolean hasContent = BufferUtil.hasContent(content) && !isHeadRequest;
|
||||
boolean close = !hasContent && lastContent;
|
||||
|
||||
if (info != null)
|
||||
{
|
||||
|
@ -127,68 +135,77 @@ public class HttpTransportOverSPDY implements HttpTransport
|
|||
LOG.warn("Committed response twice.", exception);
|
||||
return;
|
||||
}
|
||||
short version = stream.getSession().getVersion();
|
||||
Fields headers = new Fields();
|
||||
|
||||
HttpVersion httpVersion = HttpVersion.HTTP_1_1;
|
||||
headers.put(HTTPSPDYHeader.VERSION.name(version), httpVersion.asString());
|
||||
|
||||
int status = info.getStatus();
|
||||
StringBuilder httpStatus = new StringBuilder().append(status);
|
||||
String reason = info.getReason();
|
||||
if (reason == null)
|
||||
reason = HttpStatus.getMessage(status);
|
||||
if (reason != null)
|
||||
httpStatus.append(" ").append(reason);
|
||||
headers.put(HTTPSPDYHeader.STATUS.name(version), httpStatus.toString());
|
||||
LOG.debug("HTTP < {} {}", httpVersion, httpStatus);
|
||||
|
||||
// TODO merge the two Field classes into one
|
||||
HttpFields fields = info.getHttpFields();
|
||||
if (fields != null)
|
||||
sendReply(info, !hasContent ? callback : new Callback.Adapter()
|
||||
{
|
||||
for (int i = 0; i < fields.size(); ++i)
|
||||
@Override
|
||||
public void failed(Throwable x)
|
||||
{
|
||||
HttpField field = fields.getField(i);
|
||||
String name = field.getName();
|
||||
String value = field.getValue();
|
||||
headers.add(name, value);
|
||||
LOG.debug("HTTP < {}: {}", name, value);
|
||||
callback.failed(x);
|
||||
}
|
||||
}
|
||||
|
||||
boolean close = !hasContent && lastContent;
|
||||
ReplyInfo reply = new ReplyInfo(headers, close);
|
||||
reply(stream, reply);
|
||||
}, close);
|
||||
}
|
||||
|
||||
// Do we have some content to send as well
|
||||
if (hasContent)
|
||||
{
|
||||
// Is the stream still open?
|
||||
if (stream.isClosed() || stream.isReset())
|
||||
// tell the callback about the EOF
|
||||
callback.failed(new EofException("stream closed"));
|
||||
else
|
||||
// send the data and let it call the callback
|
||||
stream.data(new ByteBufferDataInfo(endPoint.getIdleTimeout(), TimeUnit.MILLISECONDS, content, lastContent
|
||||
), callback);
|
||||
// send the data and let it call the callback
|
||||
LOG.debug("Send content: {} on stream: {} lastContent={}", BufferUtil.toDetailString(content), stream,
|
||||
lastContent);
|
||||
stream.data(new ByteBufferDataInfo(endPoint.getIdleTimeout(), TimeUnit.MILLISECONDS, content, lastContent
|
||||
), callback);
|
||||
}
|
||||
// else do we need to close
|
||||
else if (lastContent)
|
||||
else if (lastContent && info == null)
|
||||
{
|
||||
// Are we closed ?
|
||||
if (stream.isClosed() || stream.isReset())
|
||||
// already closed by reply, so just tell callback we are complete
|
||||
callback.succeeded();
|
||||
else
|
||||
// send empty data to close and let the send call the callback
|
||||
stream.data(new ByteBufferDataInfo(endPoint.getIdleTimeout(), TimeUnit.MILLISECONDS,
|
||||
BufferUtil.EMPTY_BUFFER, lastContent), callback);
|
||||
// send empty data to close and let the send call the callback
|
||||
LOG.debug("No content and lastContent=true. Sending empty ByteBuffer to close stream: {}", stream);
|
||||
stream.data(new ByteBufferDataInfo(endPoint.getIdleTimeout(), TimeUnit.MILLISECONDS,
|
||||
BufferUtil.EMPTY_BUFFER, lastContent), callback);
|
||||
}
|
||||
else
|
||||
// No data and no close so tell callback we are completed
|
||||
callback.succeeded();
|
||||
else if (!lastContent && !hasContent && info == null)
|
||||
throw new IllegalStateException("not lastContent, no content and no responseInfo!");
|
||||
|
||||
}
|
||||
|
||||
private void sendReply(HttpGenerator.ResponseInfo info, Callback callback, boolean close)
|
||||
{
|
||||
Fields headers = new Fields();
|
||||
|
||||
HttpVersion httpVersion = HttpVersion.HTTP_1_1;
|
||||
headers.put(HTTPSPDYHeader.VERSION.name(version), httpVersion.asString());
|
||||
|
||||
int status = info.getStatus();
|
||||
StringBuilder httpStatus = new StringBuilder().append(status);
|
||||
String reason = info.getReason();
|
||||
if (reason == null)
|
||||
reason = HttpStatus.getMessage(status);
|
||||
if (reason != null)
|
||||
httpStatus.append(" ").append(reason);
|
||||
headers.put(HTTPSPDYHeader.STATUS.name(version), httpStatus.toString());
|
||||
LOG.debug("HTTP < {} {}", httpVersion, httpStatus);
|
||||
|
||||
// TODO merge the two Field classes into one
|
||||
HttpFields fields = info.getHttpFields();
|
||||
if (fields != null)
|
||||
{
|
||||
for (int i = 0; i < fields.size(); ++i)
|
||||
{
|
||||
HttpField field = fields.getField(i);
|
||||
String name = field.getName();
|
||||
String value = field.getValue();
|
||||
headers.add(name, value);
|
||||
LOG.debug("HTTP < {}: {}", name, value);
|
||||
}
|
||||
}
|
||||
|
||||
if (configuration.getSendServerVersion())
|
||||
headers.add(HttpHeader.SERVER.asString(), HttpConfiguration.SERVER_VERSION);
|
||||
if (configuration.getSendXPoweredBy())
|
||||
headers.add(HttpHeader.X_POWERED_BY.asString(), HttpConfiguration.SERVER_VERSION);
|
||||
|
||||
ReplyInfo reply = new ReplyInfo(headers, close);
|
||||
LOG.debug("Sending reply: {} on stream: {}", reply, stream);
|
||||
reply(stream, reply, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -208,18 +225,17 @@ public class HttpTransportOverSPDY implements HttpTransport
|
|||
@Override
|
||||
public void completed()
|
||||
{
|
||||
LOG.debug("Completed");
|
||||
LOG.debug("Completed {}", this);
|
||||
}
|
||||
|
||||
private void reply(Stream stream, ReplyInfo replyInfo)
|
||||
private void reply(Stream stream, ReplyInfo replyInfo, Callback callback)
|
||||
{
|
||||
if (!stream.isUnidirectional())
|
||||
stream.reply(replyInfo, new Callback.Adapter());
|
||||
stream.reply(replyInfo, callback);
|
||||
else
|
||||
stream.headers(new HeadersInfo(replyInfo.getHeaders(), replyInfo.isClose()), new Callback.Adapter());
|
||||
stream.headers(new HeadersInfo(replyInfo.getHeaders(), replyInfo.isClose()), callback);
|
||||
|
||||
Fields responseHeaders = replyInfo.getHeaders();
|
||||
short version = stream.getSession().getVersion();
|
||||
if (responseHeaders.get(HTTPSPDYHeader.STATUS.name(version)).value().startsWith("200") && !stream.isClosed())
|
||||
{
|
||||
Set<String> pushResources = pushStrategy.apply(stream, requestHeaders, responseHeaders);
|
||||
|
@ -234,13 +250,15 @@ public class HttpTransportOverSPDY implements HttpTransport
|
|||
private static class PushHttpTransportOverSPDY extends HttpTransportOverSPDY
|
||||
{
|
||||
private final PushResourceCoordinator coordinator;
|
||||
private final short version;
|
||||
|
||||
private PushHttpTransportOverSPDY(Connector connector, HttpConfiguration configuration, EndPoint endPoint,
|
||||
PushStrategy pushStrategy, Stream stream, Fields requestHeaders,
|
||||
PushResourceCoordinator coordinator)
|
||||
PushResourceCoordinator coordinator, short version)
|
||||
{
|
||||
super(connector, configuration, endPoint, pushStrategy, stream, requestHeaders);
|
||||
this.coordinator = coordinator;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -248,7 +266,7 @@ public class HttpTransportOverSPDY implements HttpTransport
|
|||
{
|
||||
Stream stream = getStream();
|
||||
LOG.debug("Resource pushed for {} on {}",
|
||||
getRequestHeaders().get(HTTPSPDYHeader.URI.name(stream.getSession().getVersion())), stream);
|
||||
getRequestHeaders().get(HTTPSPDYHeader.URI.name(version)), stream);
|
||||
coordinator.complete();
|
||||
}
|
||||
}
|
||||
|
@ -257,7 +275,7 @@ public class HttpTransportOverSPDY implements HttpTransport
|
|||
{
|
||||
private final Queue<PushResource> queue = new ConcurrentArrayQueue<>();
|
||||
private final Set<String> resources;
|
||||
private boolean active;
|
||||
private AtomicBoolean active = new AtomicBoolean(false);
|
||||
|
||||
private PushResourceCoordinator(Set<String> resources)
|
||||
{
|
||||
|
@ -266,6 +284,7 @@ public class HttpTransportOverSPDY implements HttpTransport
|
|||
|
||||
private void coordinate()
|
||||
{
|
||||
LOG.debug("Pushing resources: {}", resources);
|
||||
// Must send all push frames to the client at once before we
|
||||
// return from this method and send the main resource data
|
||||
for (String pushResource : resources)
|
||||
|
@ -274,31 +293,40 @@ public class HttpTransportOverSPDY implements HttpTransport
|
|||
|
||||
private void sendNextResourceData()
|
||||
{
|
||||
PushResource resource;
|
||||
synchronized (this)
|
||||
LOG.debug("{} sendNextResourceData active: {}", hashCode(), active.get());
|
||||
if (active.compareAndSet(false, true))
|
||||
{
|
||||
if (active)
|
||||
PushResource resource = queue.poll();
|
||||
if (resource != null)
|
||||
{
|
||||
LOG.debug("Opening new push channel for: {}", resource);
|
||||
HttpChannelOverSPDY pushChannel = newHttpChannelOverSPDY(resource.getPushStream(), resource.getPushRequestHeaders());
|
||||
pushChannel.requestStart(resource.getPushRequestHeaders(), true);
|
||||
return;
|
||||
resource = queue.poll();
|
||||
if (resource == null)
|
||||
return;
|
||||
active = true;
|
||||
}
|
||||
|
||||
if (active.compareAndSet(true, false))
|
||||
{
|
||||
if (queue.peek() != null)
|
||||
sendNextResourceData();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalStateException("active must not be false here! Concurrency bug!");
|
||||
}
|
||||
}
|
||||
HttpChannelOverSPDY pushChannel = newHttpChannelOverSPDY(resource.getPushStream(), resource.getPushRequestHeaders());
|
||||
pushChannel.requestStart(resource.getPushRequestHeaders(), true);
|
||||
}
|
||||
|
||||
private HttpChannelOverSPDY newHttpChannelOverSPDY(Stream pushStream, Fields pushRequestHeaders)
|
||||
{
|
||||
HttpTransport transport = new PushHttpTransportOverSPDY(connector, configuration, endPoint, pushStrategy,
|
||||
pushStream, pushRequestHeaders, this);
|
||||
pushStream, pushRequestHeaders, this, version);
|
||||
HttpInputOverSPDY input = new HttpInputOverSPDY();
|
||||
return new HttpChannelOverSPDY(connector, configuration, endPoint, transport, input, pushStream);
|
||||
}
|
||||
|
||||
private void pushResource(String pushResource)
|
||||
{
|
||||
final short version = stream.getSession().getVersion();
|
||||
Fields.Field scheme = requestHeaders.get(HTTPSPDYHeader.SCHEME.name(version));
|
||||
Fields.Field host = requestHeaders.get(HTTPSPDYHeader.HOST.name(version));
|
||||
Fields.Field uri = requestHeaders.get(HTTPSPDYHeader.URI.name(version));
|
||||
|
@ -319,14 +347,21 @@ public class HttpTransportOverSPDY implements HttpTransport
|
|||
public void failed(Throwable x)
|
||||
{
|
||||
LOG.debug("Creating push stream failed.", x);
|
||||
sendNextResourceData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void complete()
|
||||
{
|
||||
if (!active.compareAndSet(true, false))
|
||||
throw new IllegalStateException();
|
||||
sendNextResourceData();
|
||||
}
|
||||
|
||||
private Fields createRequestHeaders(Fields.Field scheme, Fields.Field host, Fields.Field uri, String pushResourcePath)
|
||||
{
|
||||
final Fields newRequestHeaders = new Fields(requestHeaders, false);
|
||||
short version = stream.getSession().getVersion();
|
||||
newRequestHeaders.put(HTTPSPDYHeader.METHOD.name(version), "GET");
|
||||
newRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
|
||||
newRequestHeaders.put(scheme);
|
||||
|
@ -341,7 +376,6 @@ public class HttpTransportOverSPDY implements HttpTransport
|
|||
private Fields createPushHeaders(Fields.Field scheme, Fields.Field host, String pushResourcePath)
|
||||
{
|
||||
final Fields pushHeaders = new Fields();
|
||||
short version = stream.getSession().getVersion();
|
||||
if (version == SPDY.V2)
|
||||
pushHeaders.put(HTTPSPDYHeader.URI.name(version), scheme.value() + "://" + host.value() + pushResourcePath);
|
||||
else
|
||||
|
@ -352,15 +386,6 @@ public class HttpTransportOverSPDY implements HttpTransport
|
|||
}
|
||||
return pushHeaders;
|
||||
}
|
||||
|
||||
private void complete()
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
active = false;
|
||||
}
|
||||
sendNextResourceData();
|
||||
}
|
||||
}
|
||||
|
||||
private static class PushResource
|
||||
|
@ -383,5 +408,14 @@ public class HttpTransportOverSPDY implements HttpTransport
|
|||
{
|
||||
return pushRequestHeaders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "PushResource{" +
|
||||
"pushStream=" + pushStream +
|
||||
", pushRequestHeaders=" + pushRequestHeaders +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -141,6 +141,7 @@ public class HTTPProxyEngine extends ProxyEngine
|
|||
{
|
||||
LOG.debug("onHeaders called with response: {}. Sending replyInfo to client.", response);
|
||||
Fields responseHeaders = createResponseHeaders(clientStream, response);
|
||||
removeHopHeaders(responseHeaders);
|
||||
ReplyInfo replyInfo = new ReplyInfo(responseHeaders, false);
|
||||
clientStream.reply(replyInfo, new Callback.Adapter()
|
||||
{
|
||||
|
|
|
@ -87,6 +87,8 @@ public abstract class ProxyEngine
|
|||
|
||||
protected void removeHopHeaders(Fields headers)
|
||||
{
|
||||
// Header names are case-insensitive (RFC2616) and oej.util.Fields.add converts the names to lowercase. So we
|
||||
// need to compare with the lowercase values only
|
||||
for (String hopHeader : HOP_HEADERS)
|
||||
headers.remove(hopHeader);
|
||||
}
|
||||
|
|
|
@ -99,7 +99,10 @@ public abstract class AbstractHTTPSPDYTest
|
|||
protected HTTPSPDYServerConnector newHTTPSPDYServerConnector(short version)
|
||||
{
|
||||
// For these tests, we need the connector to speak HTTP over SPDY even in non-SSL
|
||||
HTTPSPDYServerConnector connector = new HTTPSPDYServerConnector(server,version,new HttpConfiguration(), new PushStrategy.None());
|
||||
HttpConfiguration httpConfiguration = new HttpConfiguration();
|
||||
httpConfiguration.setSendServerVersion(true);
|
||||
httpConfiguration.setSendXPoweredBy(true);
|
||||
HTTPSPDYServerConnector connector = new HTTPSPDYServerConnector(server,version, httpConfiguration, new PushStrategy.None());
|
||||
return connector;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,13 +18,6 @@
|
|||
|
||||
package org.eclipse.jetty.spdy.server.http;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Random;
|
||||
|
@ -52,6 +45,13 @@ import org.mockito.ArgumentCaptor;
|
|||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class HttpTransportOverSPDYTest
|
||||
{
|
||||
|
@ -72,19 +72,22 @@ public class HttpTransportOverSPDYTest
|
|||
@Mock
|
||||
HttpGenerator.ResponseInfo responseInfo;
|
||||
|
||||
private Random random = new Random();
|
||||
Random random = new Random();
|
||||
short version = SPDY.V3;
|
||||
|
||||
HttpTransportOverSPDY httpTransportOverSPDY;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception
|
||||
{
|
||||
httpTransportOverSPDY = new HttpTransportOverSPDY(connector, httpConfiguration, endPoint, pushStrategy,
|
||||
stream, new Fields());
|
||||
Fields requestHeaders = new Fields();
|
||||
requestHeaders.add(HTTPSPDYHeader.METHOD.name(version), "GET");
|
||||
when(responseInfo.getStatus()).thenReturn(HttpStatus.OK_200);
|
||||
when(stream.getSession()).thenReturn(session);
|
||||
when(session.getVersion()).thenReturn(SPDY.V3);
|
||||
when(stream.isClosed()).thenReturn(false);
|
||||
httpTransportOverSPDY = new HttpTransportOverSPDY(connector, httpConfiguration, endPoint, pushStrategy,
|
||||
stream, requestHeaders);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -95,7 +98,10 @@ public class HttpTransportOverSPDYTest
|
|||
|
||||
httpTransportOverSPDY.send(null, content, lastContent, callback);
|
||||
ArgumentCaptor<ByteBufferDataInfo> dataInfoCaptor = ArgumentCaptor.forClass(ByteBufferDataInfo.class);
|
||||
verify(stream, times(1)).data(dataInfoCaptor.capture(), any(Callback.class));
|
||||
ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
|
||||
verify(stream, times(1)).data(dataInfoCaptor.capture(), callbackCaptor.capture());
|
||||
callbackCaptor.getValue().succeeded();
|
||||
verify(callback, times(1)).succeeded();
|
||||
assertThat("lastContent is true", dataInfoCaptor.getValue().isClose(), is(true));
|
||||
assertThat("ByteBuffer is empty", dataInfoCaptor.getValue().length(), is(0));
|
||||
}
|
||||
|
@ -109,7 +115,10 @@ public class HttpTransportOverSPDYTest
|
|||
|
||||
httpTransportOverSPDY.send(null, content, lastContent, callback);
|
||||
ArgumentCaptor<ByteBufferDataInfo> dataInfoCaptor = ArgumentCaptor.forClass(ByteBufferDataInfo.class);
|
||||
verify(stream, times(1)).data(dataInfoCaptor.capture(), any(Callback.class));
|
||||
ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
|
||||
verify(stream, times(1)).data(dataInfoCaptor.capture(), callbackCaptor.capture());
|
||||
callbackCaptor.getValue().succeeded();
|
||||
verify(callback, times(1)).succeeded();
|
||||
assertThat("lastContent is true", dataInfoCaptor.getValue().isClose(), is(true));
|
||||
assertThat("ByteBuffer length is 4096", dataInfoCaptor.getValue().length(), is(4096));
|
||||
}
|
||||
|
@ -119,48 +128,62 @@ public class HttpTransportOverSPDYTest
|
|||
{
|
||||
ByteBuffer content = BufferUtil.EMPTY_BUFFER;
|
||||
boolean lastContent = true;
|
||||
|
||||
|
||||
httpTransportOverSPDY.send(null, content, lastContent, callback);
|
||||
ArgumentCaptor<ByteBufferDataInfo> dataInfoCaptor = ArgumentCaptor.forClass(ByteBufferDataInfo.class);
|
||||
verify(stream, times(1)).data(dataInfoCaptor.capture(), any(Callback.class));
|
||||
ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
|
||||
verify(stream, times(1)).data(dataInfoCaptor.capture(), callbackCaptor.capture());
|
||||
callbackCaptor.getValue().succeeded();
|
||||
verify(callback, times(1)).succeeded();
|
||||
assertThat("lastContent is true", dataInfoCaptor.getValue().isClose(), is(true));
|
||||
assertThat("ByteBuffer is empty", dataInfoCaptor.getValue().length(), is(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendWithResponseInfoNullAndContentNullAndLastContentFalse() throws Exception
|
||||
{
|
||||
ByteBuffer content = null;
|
||||
boolean lastContent = false;
|
||||
|
||||
|
||||
httpTransportOverSPDY.send(null, content, lastContent, callback);
|
||||
verify(stream, times(0)).data(any(ByteBufferDataInfo.class), any(Callback.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendWithResponseInfoNullAndContentAndLastContentFalse() throws Exception
|
||||
{
|
||||
ByteBuffer content = createRandomByteBuffer();
|
||||
boolean lastContent = false;
|
||||
|
||||
|
||||
httpTransportOverSPDY.send(null, content, lastContent, callback);
|
||||
ArgumentCaptor<ByteBufferDataInfo> dataInfoCaptor = ArgumentCaptor.forClass(ByteBufferDataInfo.class);
|
||||
verify(stream, times(1)).data(dataInfoCaptor.capture(), any(Callback.class));
|
||||
ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
|
||||
verify(stream, times(1)).data(dataInfoCaptor.capture(), callbackCaptor.capture());
|
||||
callbackCaptor.getValue().succeeded();
|
||||
verify(callback, times(1)).succeeded();
|
||||
assertThat("lastContent is false", dataInfoCaptor.getValue().isClose(), is(false));
|
||||
assertThat("ByteBuffer is empty", dataInfoCaptor.getValue().length(), is(4096));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testSendWithResponseInfoNullAndContentNullAndLastContentFalse() throws Exception
|
||||
{
|
||||
ByteBuffer content = null;
|
||||
boolean lastContent = false;
|
||||
httpTransportOverSPDY.send(null, content, lastContent, callback);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testSendWithResponseInfoNullAndEmptyContentAndLastContentFalse() throws Exception
|
||||
{
|
||||
ByteBuffer content = BufferUtil.EMPTY_BUFFER;
|
||||
boolean lastContent = false;
|
||||
|
||||
|
||||
httpTransportOverSPDY.send(null, content, lastContent, callback);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendWithResponseInfoAndContentNullAndLastContentFalse() throws Exception
|
||||
{
|
||||
ByteBuffer content = null;
|
||||
boolean lastContent = false;
|
||||
|
||||
httpTransportOverSPDY.send(responseInfo, content, lastContent, callback);
|
||||
ArgumentCaptor<ReplyInfo> replyInfoCaptor = ArgumentCaptor.forClass(ReplyInfo.class);
|
||||
ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
|
||||
verify(stream, times(1)).reply(replyInfoCaptor.capture(), callbackCaptor.capture());
|
||||
callbackCaptor.getValue().succeeded();
|
||||
assertThat("ReplyInfo close is true", replyInfoCaptor.getValue().isClose(), is(false));
|
||||
|
||||
verify(stream, times(0)).data(any(ByteBufferDataInfo.class), any(Callback.class));
|
||||
verify(callback, times(1)).succeeded();
|
||||
}
|
||||
|
@ -170,13 +193,15 @@ public class HttpTransportOverSPDYTest
|
|||
{
|
||||
ByteBuffer content = null;
|
||||
boolean lastContent = true;
|
||||
|
||||
|
||||
// when stream.isClosed() is called a 2nd time, the reply has closed the stream already
|
||||
when(stream.isClosed()).thenReturn(false).thenReturn(true);
|
||||
|
||||
httpTransportOverSPDY.send(responseInfo, content, lastContent, callback);
|
||||
ArgumentCaptor<ReplyInfo> replyInfoCaptor = ArgumentCaptor.forClass(ReplyInfo.class);
|
||||
verify(stream, times(1)).reply(replyInfoCaptor.capture(), any(Callback.class));
|
||||
ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
|
||||
verify(stream, times(1)).reply(replyInfoCaptor.capture(), callbackCaptor.capture());
|
||||
callbackCaptor.getValue().succeeded();
|
||||
assertThat("ReplyInfo close is true", replyInfoCaptor.getValue().isClose(), is(true));
|
||||
|
||||
verify(callback, times(1)).succeeded();
|
||||
|
@ -186,54 +211,42 @@ public class HttpTransportOverSPDYTest
|
|||
public void testSendWithResponseInfoAndContentAndLastContentTrue() throws Exception
|
||||
{
|
||||
ByteBuffer content = createRandomByteBuffer();
|
||||
|
||||
boolean lastContent = true;
|
||||
|
||||
|
||||
httpTransportOverSPDY.send(responseInfo, content, lastContent, callback);
|
||||
|
||||
ArgumentCaptor<ReplyInfo> replyInfoCaptor = ArgumentCaptor.forClass(ReplyInfo.class);
|
||||
verify(stream, times(1)).reply(replyInfoCaptor.capture(), any(Callback.class));
|
||||
ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
|
||||
verify(stream, times(1)).reply(replyInfoCaptor.capture(), callbackCaptor.capture());
|
||||
callbackCaptor.getValue().succeeded();
|
||||
assertThat("ReplyInfo close is false", replyInfoCaptor.getValue().isClose(), is(false));
|
||||
|
||||
ArgumentCaptor<ByteBufferDataInfo> dataInfoCaptor = ArgumentCaptor.forClass(ByteBufferDataInfo.class);
|
||||
verify(stream, times(1)).data(dataInfoCaptor.capture(), any(Callback.class));
|
||||
verify(stream, times(1)).data(dataInfoCaptor.capture(), callbackCaptor.capture());
|
||||
callbackCaptor.getValue().succeeded();
|
||||
assertThat("lastContent is true", dataInfoCaptor.getValue().isClose(), is(true));
|
||||
assertThat("ByteBuffer length is 4096", dataInfoCaptor.getValue().length(), is(4096));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendWithResponseInfoAndContentNullAndLastContentFalse() throws Exception
|
||||
{
|
||||
ByteBuffer content = null;
|
||||
boolean lastContent = false;
|
||||
|
||||
|
||||
httpTransportOverSPDY.send(responseInfo, content, lastContent, callback);
|
||||
ArgumentCaptor<ReplyInfo> replyInfoCaptor = ArgumentCaptor.forClass(ReplyInfo.class);
|
||||
verify(stream, times(1)).reply(replyInfoCaptor.capture(), any(Callback.class));
|
||||
assertThat("ReplyInfo close is true", replyInfoCaptor.getValue().isClose(), is(false));
|
||||
|
||||
verify(stream, times(0)).data(any(ByteBufferDataInfo.class), any(Callback.class));
|
||||
verify(callback, times(1)).succeeded();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendWithResponseInfoAndContentAndLastContentFalse() throws Exception
|
||||
{
|
||||
ByteBuffer content = createRandomByteBuffer();
|
||||
|
||||
boolean lastContent = false;
|
||||
|
||||
|
||||
httpTransportOverSPDY.send(responseInfo, content, lastContent, callback);
|
||||
ArgumentCaptor<ReplyInfo> replyInfoCaptor = ArgumentCaptor.forClass(ReplyInfo.class);
|
||||
verify(stream, times(1)).reply(replyInfoCaptor.capture(), any(Callback.class));
|
||||
ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
|
||||
verify(stream, times(1)).reply(replyInfoCaptor.capture(), callbackCaptor.capture());
|
||||
callbackCaptor.getValue().succeeded();
|
||||
assertThat("ReplyInfo close is false", replyInfoCaptor.getValue().isClose(), is(false));
|
||||
|
||||
ArgumentCaptor<ByteBufferDataInfo> dataInfoCaptor = ArgumentCaptor.forClass(ByteBufferDataInfo.class);
|
||||
verify(stream, times(1)).data(dataInfoCaptor.capture(), any(Callback.class));
|
||||
verify(stream, times(1)).data(dataInfoCaptor.capture(), callbackCaptor.capture());
|
||||
callbackCaptor.getValue().succeeded();
|
||||
assertThat("lastContent is false", dataInfoCaptor.getValue().isClose(), is(false));
|
||||
assertThat("ByteBuffer length is 4096", dataInfoCaptor.getValue().length(), is(4096));
|
||||
|
||||
verify(callback, times(1)).succeeded();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -60,7 +60,6 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
import org.eclipse.jetty.util.log.StdErrLog;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
|
@ -338,7 +337,6 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
|
|||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testPushResourceAreSentNonInterleaved() throws Exception
|
||||
{
|
||||
final CountDownLatch allExpectedPushesReceivedLatch = new CountDownLatch(4);
|
||||
|
|
|
@ -29,7 +29,6 @@ import java.util.Arrays;
|
|||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.Cookie;
|
||||
|
@ -38,6 +37,7 @@ import javax.servlet.http.HttpServletResponse;
|
|||
|
||||
import org.eclipse.jetty.continuation.Continuation;
|
||||
import org.eclipse.jetty.continuation.ContinuationSupport;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.server.HttpChannel;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
|
@ -57,10 +57,12 @@ import org.junit.Test;
|
|||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
||||
{
|
||||
|
@ -100,7 +102,9 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
{
|
||||
assertTrue(replyInfo.isClose());
|
||||
Fields replyHeaders = replyInfo.getHeaders();
|
||||
assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).value().contains("200"));
|
||||
assertThat(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).value().contains("200"), is(true));
|
||||
assertThat(replyHeaders.get(HttpHeader.SERVER.asString()), is(notNullValue()));
|
||||
assertThat(replyHeaders.get(HttpHeader.X_POWERED_BY.asString()), is(notNullValue()));
|
||||
replyLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
@ -185,9 +189,9 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
assertThat("response code is 200 OK", replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).value()
|
||||
.contains("200"), is(true));
|
||||
assertThat(replyInfo.getHeaders().get("Set-Cookie").values()[0], is(cookie1 + "=\"" + cookie1Value +
|
||||
"\""));
|
||||
"\";Version=1"));
|
||||
assertThat(replyInfo.getHeaders().get("Set-Cookie").values()[1], is(cookie2 + "=\"" + cookie2Value +
|
||||
"\""));
|
||||
"\";Version=1"));
|
||||
replyLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
@ -210,6 +214,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
assertEquals("HEAD", httpRequest.getMethod());
|
||||
assertEquals(path, target);
|
||||
assertEquals(path, httpRequest.getRequestURI());
|
||||
httpResponse.getWriter().write("body that shouldn't be sent on a HEAD request");
|
||||
handlerLatch.countDown();
|
||||
}
|
||||
}), null);
|
||||
|
@ -226,11 +231,55 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).value().contains("200"));
|
||||
replyLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
fail("HEAD request shouldn't send any data");
|
||||
}
|
||||
});
|
||||
assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
|
||||
assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPOSTWithDelayedContentBody() throws Exception
|
||||
{
|
||||
final String path = "/foo";
|
||||
final CountDownLatch handlerLatch = new CountDownLatch(1);
|
||||
Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
|
||||
throws IOException, ServletException
|
||||
{
|
||||
// don't read the request body, reply immediately
|
||||
request.setHandled(true);
|
||||
handlerLatch.countDown();
|
||||
}
|
||||
}), null);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "POST", path);
|
||||
headers.put("content-type", "application/x-www-form-urlencoded");
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, headers, false, (byte)0),
|
||||
new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onReply(Stream stream, ReplyInfo replyInfo)
|
||||
{
|
||||
assertTrue(replyInfo.isClose());
|
||||
Fields replyHeaders = replyInfo.getHeaders();
|
||||
assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version)).value().contains("200"));
|
||||
replyLatch.countDown();
|
||||
}
|
||||
});
|
||||
stream.data(new StringDataInfo("a", false));
|
||||
assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
|
||||
assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
|
||||
stream.data(new StringDataInfo("b", true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPOSTWithParameters() throws Exception
|
||||
{
|
||||
|
|
|
@ -219,6 +219,8 @@ public class ProxySPDYToHTTPTest
|
|||
public void onReply(Stream stream, ReplyInfo replyInfo)
|
||||
{
|
||||
Fields headers = replyInfo.getHeaders();
|
||||
assertThat("Trailer header has been filtered by proxy", headers.get("trailer"),
|
||||
is(nullValue()));
|
||||
assertThat("custom header exists in response", headers.get(header), is(notNullValue()));
|
||||
replyLatch.countDown();
|
||||
}
|
||||
|
@ -545,6 +547,8 @@ public class ProxySPDYToHTTPTest
|
|||
while ((read = bufferedReader.read()) != -1)
|
||||
response.getOutputStream().write(read);
|
||||
|
||||
// add some hop header to be removed on the proxy
|
||||
response.addHeader("Trailer", "bla");
|
||||
if (responseHeader != null)
|
||||
response.addHeader(responseHeader, "bar");
|
||||
if (responseData != null)
|
||||
|
|
|
@ -36,13 +36,14 @@ import org.eclipse.jetty.spdy.api.SynInfo;
|
|||
import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Fields;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
@Ignore("Fails the build too often. Runs fine always when run alone.")
|
||||
@RunWith(JUnit4.class)
|
||||
public class MaxConcurrentStreamTest extends AbstractTest
|
||||
{
|
||||
@Test
|
||||
|
@ -116,7 +117,5 @@ public class MaxConcurrentStreamTest extends AbstractTest
|
|||
|
||||
stream.data(new ByteBufferDataInfo(BufferUtil.EMPTY_BUFFER, true));
|
||||
assertThat("Data has been received on first stream.", dataReceivedLatch.await(5, TimeUnit.SECONDS), is(true));
|
||||
|
||||
session.syn(synInfo, null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,8 +39,7 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
|
||||
/**
|
||||
* JSON Parser and Generator.
|
||||
*
|
||||
* <p>
|
||||
* <p />
|
||||
* This class provides some static methods to convert POJOs to and from JSON
|
||||
* notation. The mapping from JSON to java is:
|
||||
*
|
||||
|
@ -52,9 +51,7 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
* null ==> null
|
||||
* bool ==> Boolean
|
||||
* </pre>
|
||||
*
|
||||
* </p>
|
||||
* <p>
|
||||
|
||||
* The java to JSON mapping is:
|
||||
*
|
||||
* <pre>
|
||||
|
@ -68,30 +65,27 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
* Object --> string (dubious!)
|
||||
* </pre>
|
||||
*
|
||||
* </p>
|
||||
* <p>
|
||||
* The interface {@link JSON.Convertible} may be implemented by classes that
|
||||
* wish to externalize and initialize specific fields to and from JSON objects.
|
||||
* Only directed acyclic graphs of objects are supported.
|
||||
* </p>
|
||||
* <p>
|
||||
* <p />
|
||||
* The interface {@link JSON.Generator} may be implemented by classes that know
|
||||
* how to render themselves as JSON and the {@link #toString(Object)} method
|
||||
* will use {@link JSON.Generator#addJSON(Appendable)} to generate the JSON.
|
||||
* The class {@link JSON.Literal} may be used to hold pre-generated JSON object.
|
||||
* <p>
|
||||
* <p />
|
||||
* The interface {@link JSON.Convertor} may be implemented to provide static
|
||||
* convertors for objects that may be registered with
|
||||
* {@link #registerConvertor(Class, org.eclipse.jetty.util.ajax.JSON.Convertor)}
|
||||
* . These convertors are looked up by class, interface and super class by
|
||||
* converters for objects that may be registered with
|
||||
* {@link #registerConvertor(Class, Convertor)}.
|
||||
* These converters are looked up by class, interface and super class by
|
||||
* {@link #getConvertor(Class)}.
|
||||
* </p>
|
||||
* <p>If a JSON object has a "class" field, then a java class for that name is
|
||||
* looked up and the method {@link convertTo(Class,Map)} is used to find a
|
||||
* Convertor for that class. If a JSON object has a "x-class" field then a
|
||||
* direct lookup for a Convertor for that named x-class is done, so that none
|
||||
* java classes may be converted.
|
||||
* </p>
|
||||
* <p />
|
||||
* If a JSON object has a "class" field, then a java class for that name is
|
||||
* loaded and the method {@link #convertTo(Class,Map)} is used to find a
|
||||
* {@link JSON.Convertor} for that class.
|
||||
* <p />
|
||||
* If a JSON object has a "x-class" field then a direct lookup for a
|
||||
* {@link JSON.Convertor} for that class name is done (without loading the class).
|
||||
*/
|
||||
public class JSON
|
||||
{
|
||||
|
@ -105,7 +99,6 @@ public class JSON
|
|||
{
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @return the initial stringBuffer size to use when creating JSON strings
|
||||
* (default 1024)
|
||||
|
@ -115,7 +108,6 @@ public class JSON
|
|||
return _stringBufferSize;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @param stringBufferSize
|
||||
* the initial stringBuffer size to use when creating JSON
|
||||
|
@ -126,7 +118,6 @@ public class JSON
|
|||
_stringBufferSize = stringBufferSize;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Register a {@link Convertor} for a class or interface.
|
||||
*
|
||||
|
@ -140,19 +131,16 @@ public class JSON
|
|||
DEFAULT.addConvertor(forClass,convertor);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public static JSON getDefault()
|
||||
{
|
||||
return DEFAULT;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Deprecated
|
||||
public static void setDefault(JSON json)
|
||||
{
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public static String toString(Object object)
|
||||
{
|
||||
StringBuilder buffer = new StringBuilder(DEFAULT.getStringBufferSize());
|
||||
|
@ -160,7 +148,6 @@ public class JSON
|
|||
return buffer.toString();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public static String toString(Map object)
|
||||
{
|
||||
StringBuilder buffer = new StringBuilder(DEFAULT.getStringBufferSize());
|
||||
|
@ -168,7 +155,6 @@ public class JSON
|
|||
return buffer.toString();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public static String toString(Object[] array)
|
||||
{
|
||||
StringBuilder buffer = new StringBuilder(DEFAULT.getStringBufferSize());
|
||||
|
@ -176,7 +162,6 @@ public class JSON
|
|||
return buffer.toString();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @param s
|
||||
* String containing JSON object or array.
|
||||
|
@ -187,7 +172,6 @@ public class JSON
|
|||
return DEFAULT.parse(new StringSource(s),false);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @param s
|
||||
* String containing JSON object or array.
|
||||
|
@ -200,7 +184,6 @@ public class JSON
|
|||
return DEFAULT.parse(new StringSource(s),stripOuterComment);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @param in
|
||||
* Reader containing JSON object or array.
|
||||
|
@ -211,7 +194,6 @@ public class JSON
|
|||
return DEFAULT.parse(new ReaderSource(in),false);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @param in
|
||||
* Reader containing JSON object or array.
|
||||
|
@ -224,7 +206,6 @@ public class JSON
|
|||
return DEFAULT.parse(new ReaderSource(in),stripOuterComment);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @deprecated use {@link #parse(Reader)}
|
||||
* @param in
|
||||
|
@ -237,7 +218,6 @@ public class JSON
|
|||
return DEFAULT.parse(new StringSource(IO.toString(in)),false);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @deprecated use {@link #parse(Reader, boolean)}
|
||||
* @param in
|
||||
|
@ -252,7 +232,6 @@ public class JSON
|
|||
return DEFAULT.parse(new StringSource(IO.toString(in)),stripOuterComment);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Convert Object to JSON
|
||||
*
|
||||
|
@ -267,7 +246,6 @@ public class JSON
|
|||
return buffer.toString();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Convert JSON to Object
|
||||
*
|
||||
|
@ -287,7 +265,6 @@ public class JSON
|
|||
append((Appendable)buffer,object);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Append object as JSON to string buffer.
|
||||
*
|
||||
|
@ -301,32 +278,58 @@ public class JSON
|
|||
try
|
||||
{
|
||||
if (object == null)
|
||||
{
|
||||
buffer.append("null");
|
||||
else if (object instanceof Convertible)
|
||||
appendJSON(buffer,(Convertible)object);
|
||||
else if (object instanceof Generator)
|
||||
appendJSON(buffer,(Generator)object);
|
||||
}
|
||||
// Most likely first
|
||||
else if (object instanceof Map)
|
||||
{
|
||||
appendMap(buffer,(Map)object);
|
||||
else if (object instanceof Collection)
|
||||
appendArray(buffer,(Collection)object);
|
||||
else if (object.getClass().isArray())
|
||||
appendArray(buffer,object);
|
||||
else if (object instanceof Number)
|
||||
appendNumber(buffer,(Number)object);
|
||||
else if (object instanceof Boolean)
|
||||
appendBoolean(buffer,(Boolean)object);
|
||||
else if (object instanceof Character)
|
||||
appendString(buffer,object.toString());
|
||||
}
|
||||
else if (object instanceof String)
|
||||
{
|
||||
appendString(buffer,(String)object);
|
||||
}
|
||||
else if (object instanceof Number)
|
||||
{
|
||||
appendNumber(buffer,(Number)object);
|
||||
}
|
||||
else if (object instanceof Boolean)
|
||||
{
|
||||
appendBoolean(buffer,(Boolean)object);
|
||||
}
|
||||
else if (object.getClass().isArray())
|
||||
{
|
||||
appendArray(buffer,object);
|
||||
}
|
||||
else if (object instanceof Character)
|
||||
{
|
||||
appendString(buffer,object.toString());
|
||||
}
|
||||
else if (object instanceof Convertible)
|
||||
{
|
||||
appendJSON(buffer,(Convertible)object);
|
||||
}
|
||||
else if (object instanceof Generator)
|
||||
{
|
||||
appendJSON(buffer,(Generator)object);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check Convertor before Collection to support JSONCollectionConvertor
|
||||
Convertor convertor = getConvertor(object.getClass());
|
||||
if (convertor != null)
|
||||
{
|
||||
appendJSON(buffer,convertor,object);
|
||||
}
|
||||
else if (object instanceof Collection)
|
||||
{
|
||||
appendArray(buffer,(Collection)object);
|
||||
}
|
||||
else
|
||||
{
|
||||
appendString(buffer,object.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
|
@ -335,14 +338,12 @@ public class JSON
|
|||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Deprecated
|
||||
public void appendNull(StringBuffer buffer)
|
||||
{
|
||||
appendNull((Appendable)buffer);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void appendNull(Appendable buffer)
|
||||
{
|
||||
try
|
||||
|
@ -355,14 +356,12 @@ public class JSON
|
|||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Deprecated
|
||||
public void appendJSON(final StringBuffer buffer, final Convertor convertor, final Object object)
|
||||
{
|
||||
appendJSON((Appendable)buffer,convertor,object);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void appendJSON(final Appendable buffer, final Convertor convertor, final Object object)
|
||||
{
|
||||
appendJSON(buffer,new Convertible()
|
||||
|
@ -378,14 +377,12 @@ public class JSON
|
|||
});
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Deprecated
|
||||
public void appendJSON(final StringBuffer buffer, Convertible converter)
|
||||
{
|
||||
appendJSON((Appendable)buffer,converter);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void appendJSON(final Appendable buffer, Convertible converter)
|
||||
{
|
||||
ConvertableOutput out=new ConvertableOutput(buffer);
|
||||
|
@ -393,27 +390,23 @@ public class JSON
|
|||
out.complete();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Deprecated
|
||||
public void appendJSON(StringBuffer buffer, Generator generator)
|
||||
{
|
||||
generator.addJSON(buffer);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void appendJSON(Appendable buffer, Generator generator)
|
||||
{
|
||||
generator.addJSON(buffer);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Deprecated
|
||||
public void appendMap(StringBuffer buffer, Map<?,?> map)
|
||||
{
|
||||
appendMap((Appendable)buffer,map);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void appendMap(Appendable buffer, Map<?,?> map)
|
||||
{
|
||||
try
|
||||
|
@ -444,14 +437,12 @@ public class JSON
|
|||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Deprecated
|
||||
public void appendArray(StringBuffer buffer, Collection collection)
|
||||
{
|
||||
appendArray((Appendable)buffer,collection);
|
||||
appendArray((Appendable)buffer,collection);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void appendArray(Appendable buffer, Collection collection)
|
||||
{
|
||||
try
|
||||
|
@ -482,14 +473,12 @@ public class JSON
|
|||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Deprecated
|
||||
public void appendArray(StringBuffer buffer, Object array)
|
||||
{
|
||||
appendArray((Appendable)buffer,array);
|
||||
appendArray((Appendable)buffer,array);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void appendArray(Appendable buffer, Object array)
|
||||
{
|
||||
try
|
||||
|
@ -518,14 +507,12 @@ public class JSON
|
|||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Deprecated
|
||||
public void appendBoolean(StringBuffer buffer, Boolean b)
|
||||
{
|
||||
appendBoolean((Appendable)buffer,b);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void appendBoolean(Appendable buffer, Boolean b)
|
||||
{
|
||||
try
|
||||
|
@ -535,7 +522,7 @@ public class JSON
|
|||
appendNull(buffer);
|
||||
return;
|
||||
}
|
||||
buffer.append(b.booleanValue()?"true":"false");
|
||||
buffer.append(b?"true":"false");
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
|
@ -543,14 +530,12 @@ public class JSON
|
|||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Deprecated
|
||||
public void appendNumber(StringBuffer buffer, Number number)
|
||||
{
|
||||
appendNumber((Appendable)buffer,number);
|
||||
appendNumber((Appendable)buffer,number);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void appendNumber(Appendable buffer, Number number)
|
||||
{
|
||||
try
|
||||
|
@ -568,14 +553,12 @@ public class JSON
|
|||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Deprecated
|
||||
public void appendString(StringBuffer buffer, String string)
|
||||
{
|
||||
appendString((Appendable)buffer,string);
|
||||
appendString((Appendable)buffer,string);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void appendString(Appendable buffer, String string)
|
||||
{
|
||||
if (string == null)
|
||||
|
@ -589,37 +572,31 @@ public class JSON
|
|||
|
||||
// Parsing utilities
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected String toString(char[] buffer, int offset, int length)
|
||||
{
|
||||
return new String(buffer,offset,length);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected Map<String, Object> newMap()
|
||||
{
|
||||
return new HashMap<String, Object>();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected Object[] newArray(int size)
|
||||
{
|
||||
return new Object[size];
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected JSON contextForArray()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected JSON contextFor(String field)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected Object convertTo(Class type, Map map)
|
||||
{
|
||||
if (type != null && Convertible.class.isAssignableFrom(type))
|
||||
|
@ -644,7 +621,6 @@ public class JSON
|
|||
return map;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Register a {@link Convertor} for a class or interface.
|
||||
*
|
||||
|
@ -658,7 +634,6 @@ public class JSON
|
|||
_convertors.put(forClass.getName(),convertor);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Lookup a convertor for a class.
|
||||
* <p>
|
||||
|
@ -677,7 +652,7 @@ public class JSON
|
|||
if (convertor == null && this != DEFAULT)
|
||||
convertor = DEFAULT.getConvertor(cls);
|
||||
|
||||
while (convertor == null && cls != null && cls != Object.class)
|
||||
while (convertor == null && cls != Object.class)
|
||||
{
|
||||
Class[] ifs = cls.getInterfaces();
|
||||
int i = 0;
|
||||
|
@ -692,7 +667,6 @@ public class JSON
|
|||
return convertor;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Register a {@link JSON.Convertor} for a named class or interface.
|
||||
*
|
||||
|
@ -706,7 +680,6 @@ public class JSON
|
|||
_convertors.put(name,convertor);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Lookup a convertor for a named class.
|
||||
*
|
||||
|
@ -716,14 +689,12 @@ public class JSON
|
|||
*/
|
||||
public Convertor getConvertorFor(String name)
|
||||
{
|
||||
String clsName = name;
|
||||
Convertor convertor = _convertors.get(clsName);
|
||||
Convertor convertor = _convertors.get(name);
|
||||
if (convertor == null && this != DEFAULT)
|
||||
convertor = DEFAULT.getConvertorFor(clsName);
|
||||
convertor = DEFAULT.getConvertorFor(name);
|
||||
return convertor;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public Object parse(Source source, boolean stripOuterComment)
|
||||
{
|
||||
int comment_state = 0; // 0=no comment, 1="/", 2="/*", 3="/* *" -1="//"
|
||||
|
@ -811,7 +782,6 @@ public class JSON
|
|||
return o;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public Object parse(Source source)
|
||||
{
|
||||
int comment_state = 0; // 0=no comment, 1="/", 2="/*", 3="/* *" -1="//"
|
||||
|
@ -911,13 +881,11 @@ public class JSON
|
|||
return null;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected Object handleUnknown(Source source, char c)
|
||||
{
|
||||
throw new IllegalStateException("unknown char '" + c + "'(" + (int)c + ") in " + source);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected Object parseObject(Source source)
|
||||
{
|
||||
if (source.next() != '{')
|
||||
|
@ -952,10 +920,10 @@ public class JSON
|
|||
String xclassname = (String)map.get("x-class");
|
||||
if (xclassname != null)
|
||||
{
|
||||
Convertor c = getConvertorFor(xclassname);
|
||||
if (c != null)
|
||||
return c.fromJSON(map);
|
||||
LOG.warn("no Convertor for xclassname '%s'", xclassname);
|
||||
Convertor c = getConvertorFor(xclassname);
|
||||
if (c != null)
|
||||
return c.fromJSON(map);
|
||||
LOG.warn("No Convertor for x-class '{}'", xclassname);
|
||||
}
|
||||
|
||||
String classname = (String)map.get("class");
|
||||
|
@ -968,14 +936,13 @@ public class JSON
|
|||
}
|
||||
catch (ClassNotFoundException e)
|
||||
{
|
||||
LOG.warn("no Class for classname '%s'", classname);
|
||||
LOG.warn("No Class for '{}'", classname);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected Object parseArray(Source source)
|
||||
{
|
||||
if (source.next() != '[')
|
||||
|
@ -1042,7 +1009,6 @@ public class JSON
|
|||
throw new IllegalStateException("unexpected end of array");
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected String parseString(Source source)
|
||||
{
|
||||
if (source.next() != '"')
|
||||
|
@ -1110,7 +1076,6 @@ public class JSON
|
|||
else if (c == '\\')
|
||||
{
|
||||
escape = true;
|
||||
continue;
|
||||
}
|
||||
else if (c == '\"')
|
||||
{
|
||||
|
@ -1118,7 +1083,9 @@ public class JSON
|
|||
return toString(scratch,0,i);
|
||||
}
|
||||
else
|
||||
{
|
||||
scratch[i++] = c;
|
||||
}
|
||||
}
|
||||
|
||||
// Missing end quote, but return string anyway ?
|
||||
|
@ -1175,17 +1142,19 @@ public class JSON
|
|||
else if (c == '\\')
|
||||
{
|
||||
escape = true;
|
||||
continue;
|
||||
}
|
||||
else if (c == '\"')
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.append(c);
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public Number parseNumber(Source source)
|
||||
{
|
||||
boolean minus = false;
|
||||
|
@ -1270,7 +1239,6 @@ public class JSON
|
|||
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected void seekTo(char seek, Source source)
|
||||
{
|
||||
while (source.hasNext())
|
||||
|
@ -1287,7 +1255,6 @@ public class JSON
|
|||
throw new IllegalStateException("Expected '" + seek + "'");
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected char seekTo(String seek, Source source)
|
||||
{
|
||||
while (source.hasNext())
|
||||
|
@ -1306,7 +1273,6 @@ public class JSON
|
|||
throw new IllegalStateException("Expected one of '" + seek + "'");
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected static void complete(String seek, Source source)
|
||||
{
|
||||
int i = 0;
|
||||
|
@ -1398,7 +1364,7 @@ public class JSON
|
|||
_buffer.append(c);
|
||||
QuotedStringTokenizer.quote(_buffer,name);
|
||||
_buffer.append(':');
|
||||
appendNumber(_buffer,new Double(value));
|
||||
appendNumber(_buffer, value);
|
||||
c = ',';
|
||||
}
|
||||
catch (IOException e)
|
||||
|
@ -1444,7 +1410,6 @@ public class JSON
|
|||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public interface Source
|
||||
{
|
||||
boolean hasNext();
|
||||
|
@ -1456,7 +1421,6 @@ public class JSON
|
|||
char[] scratchBuffer();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public static class StringSource implements Source
|
||||
{
|
||||
private final String string;
|
||||
|
@ -1500,7 +1464,6 @@ public class JSON
|
|||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public static class ReaderSource implements Source
|
||||
{
|
||||
private Reader _reader;
|
||||
|
@ -1567,7 +1530,6 @@ public class JSON
|
|||
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* JSON Output class for use by {@link Convertible}.
|
||||
*/
|
||||
|
@ -1586,7 +1548,6 @@ public class JSON
|
|||
public void add(String name, boolean value);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* JSON Convertible object. Object can implement this interface in a similar
|
||||
|
@ -1607,7 +1568,6 @@ public class JSON
|
|||
public void fromJSON(Map object);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Static JSON Convertor.
|
||||
* <p>
|
||||
|
@ -1626,7 +1586,6 @@ public class JSON
|
|||
public Object fromJSON(Map object);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* JSON Generator. A class that can add it's JSON representation directly to
|
||||
* a StringBuffer. This is useful for object instances that are frequently
|
||||
|
@ -1637,7 +1596,6 @@ public class JSON
|
|||
public void addJSON(Appendable buffer);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* A Literal JSON generator A utility instance of {@link JSON.Generator}
|
||||
* that holds a pre-generated string on JSON text.
|
||||
|
@ -1646,7 +1604,6 @@ public class JSON
|
|||
{
|
||||
private String _json;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Construct a literal JSON instance for use by
|
||||
* {@link JSON#toString(Object)}. If {@link Log#isDebugEnabled()} is
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 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.util.ajax;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jetty.util.Loader;
|
||||
|
||||
public class JSONCollectionConvertor implements JSON.Convertor
|
||||
{
|
||||
public void toJSON(Object obj, JSON.Output out)
|
||||
{
|
||||
out.addClass(obj.getClass());
|
||||
out.add("list", ((Collection)obj).toArray());
|
||||
}
|
||||
|
||||
public Object fromJSON(Map object)
|
||||
{
|
||||
try
|
||||
{
|
||||
Collection result = (Collection)Loader.loadClass(getClass(), (String)object.get("class")).newInstance();
|
||||
Collections.addAll(result, (Object[])object.get("list"));
|
||||
return result;
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
if (x instanceof RuntimeException)
|
||||
throw (RuntimeException)x;
|
||||
throw new RuntimeException(x);
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue