Merge remote-tracking branch 'origin/master' into jetty-8

Conflicts:
	VERSION.txt
This commit is contained in:
Jan Bartel 2011-08-16 11:35:46 +10:00
commit 6e8afd6795
21 changed files with 414 additions and 130 deletions

View File

@ -11,7 +11,7 @@ jetty-8.0.0.M3 27 May 2011
+ 346180 jsp-2.2 support + 346180 jsp-2.2 support
+ Updated to jetty-7.4.2.v20110526 + Updated to jetty-7.4.2.v20110526
jetty-7.5.0-SNAPSHOT jetty-7.5.0.RC0 August 15th 2011
+ 298502 Handle 200 Connect responses with no content-length + 298502 Handle 200 Connect responses with no content-length
+ 351516 Refactored sessions to better support nosql session managers + 351516 Refactored sessions to better support nosql session managers
+ 351576 Do not use deprecated method File.toURL() + 351576 Do not use deprecated method File.toURL()
@ -30,6 +30,7 @@ jetty-7.5.0-SNAPSHOT
+ 353862 Improve performance of QuotedStringTokenizer.quote() + 353862 Improve performance of QuotedStringTokenizer.quote()
+ 354014 Content-Length is passed to wrapped response in GZipFilter + 354014 Content-Length is passed to wrapped response in GZipFilter
+ 354204 Charset encodings property file not used + 354204 Charset encodings property file not used
+ 354397 RewriteRegexRule handles special characters in regex group
+ 354466 Typo in example config of jetty-plus.xml + 354466 Typo in example config of jetty-plus.xml
jetty-7.4.4.v20110707 July 7th 2011 jetty-7.4.4.v20110707 July 7th 2011
@ -2357,7 +2358,7 @@ jetty-6.0.0rc0 - 7 July 2006
+ fixed HttpGenerator convertion of non UTF-8: JETTY-82 + fixed HttpGenerator convertion of non UTF-8: JETTY-82
+ added html module from jetty 5 - but deprecated until maintainer found + added html module from jetty 5 - but deprecated until maintainer found
jetty-6.0.0beta17 - 1/6/2006 jetty-6.0.0beta17 - 1 Jun 2006
+ Added config to disable file memory mapped buffers for windows + Added config to disable file memory mapped buffers for windows
+ Added Request.isHandled() + Added Request.isHandled()
+ Refactored Synchronization of SelectChannelConnector + Refactored Synchronization of SelectChannelConnector
@ -2373,11 +2374,11 @@ jetty-6.0.0beta17 - 1/6/2006
+ don't reset headers during forward + don't reset headers during forward
+ BoundedThreadPool.doStop waits for threads to complete + BoundedThreadPool.doStop waits for threads to complete
jetty-6.0.0beta16 - 12/5/2006 jetty-6.0.0beta16 - 12 May 2006
+ remove a couple of System.err.printlns + remove a couple of System.err.printlns
+ replace backwards compativle API in UrlEncoded + replace backwards compativle API in UrlEncoded
jetty-6.0.0beta15 - 11/5/2006 jetty-6.0.0beta15 - 11 May 2006
+ Added Server attribute org.mortbay.jetty.Request.maxFormContentSize + Added Server attribute org.mortbay.jetty.Request.maxFormContentSize
+ Renamed NotFoundHandler to DefaultHandler + Renamed NotFoundHandler to DefaultHandler
+ Added automatic scan of all WEB-INF/jetty-*.xml files for plugin + Added automatic scan of all WEB-INF/jetty-*.xml files for plugin
@ -2404,7 +2405,7 @@ jetty-6.0.0beta15 - 11/5/2006
+ don't accept partial authority in request line. + don't accept partial authority in request line.
+ enforce 204 and 304 have no content + enforce 204 and 304 have no content
jetty-6.0.0beta14 - 9/4/2006 jetty-6.0.0beta14 - 9 Apr 2006
+ ignore dirs and files that don't exist in plugin scanner + ignore dirs and files that don't exist in plugin scanner
+ added support for stopping jetty using "java -jar start.jar --stop" + added support for stopping jetty using "java -jar start.jar --stop"
+ added configurability for webdefault.xml in maven plugin + added configurability for webdefault.xml in maven plugin
@ -2428,7 +2429,7 @@ jetty-6.0.0beta14 - 9/4/2006
+ added reset to Continuation + added reset to Continuation
jetty-6.0.0beta12 - 16/3/2006 jetty-6.0.0beta12 - 16 Mar 2006
+ Fixed maven plugin JNDI for redeploys + Fixed maven plugin JNDI for redeploys
+ Fixed tld discovery for plugin (search dependencies) + Fixed tld discovery for plugin (search dependencies)
+ Fixed JettyPlus for root contexts + Fixed JettyPlus for root contexts
@ -2438,7 +2439,7 @@ jetty-6.0.0beta12 - 16/3/2006
+ Added provider support to SslListener + Added provider support to SslListener
+ Log ERROR for runtimeExceptions + Log ERROR for runtimeExceptions
jetty-6.0.0beta11 - 14/3/2006 jetty-6.0.0beta11 - 14 Mar 2006
+ added JAAS + added JAAS
+ added webapp-specific JNDI entries + added webapp-specific JNDI entries
+ added missing Configurations for maven plugin + added missing Configurations for maven plugin
@ -2451,7 +2452,7 @@ jetty-6.0.0beta11 - 14/3/2006
+ Added HttpURI and improved UTF-8 parsing. + Added HttpURI and improved UTF-8 parsing.
+ refactored writers and improved UTF-8 generation. + refactored writers and improved UTF-8 generation.
jetty-6.0.0beta10 25/2/2006 jetty-6.0.0beta10 25 Feb 2006
+ Added support for java:comp/env + Added support for java:comp/env
+ Added support for pluggable transaction manager + Added support for pluggable transaction manager
+ Forward masks include attributes and vice versa + Forward masks include attributes and vice versa
@ -2465,7 +2466,7 @@ jetty-6.0.0beta10 25/2/2006
+ Fix http://jira.codehaus.org/browse/JETTY-6. hi byte reader + Fix http://jira.codehaus.org/browse/JETTY-6. hi byte reader
+ Updates javax to MR2 release + Updates javax to MR2 release
jetty-6.0.0beta9 9/2/2006 jetty-6.0.0beta9 9 Feb 2006
+ PathMap for direct context mapping. + PathMap for direct context mapping.
+ Refactored chat demo and upgraded prototype.js + Refactored chat demo and upgraded prototype.js
+ Continuation cleanup + Continuation cleanup
@ -2479,7 +2480,7 @@ jetty-6.0.0beta9 9/2/2006
+ fixed setLocale bug sf1426940 + fixed setLocale bug sf1426940
+ Added TLD tag listener handling. + Added TLD tag listener handling.
jetty-6.0.0beta8 24/1/2006 jetty-6.0.0beta8 24 Jan 2006
+ fixed dispatch of new session problem. sf:1407090 + fixed dispatch of new session problem. sf:1407090
+ reinstated rfc2616 test harness + reinstated rfc2616 test harness
+ Handle pipeline requests without hangs + Handle pipeline requests without hangs
@ -3248,7 +3249,7 @@ Jetty-4.2.0 - 16 November 2002
+ Added upload demo to dump servlet. + Added upload demo to dump servlet.
+ Many more optimizations. + Many more optimizations.
Jetty-4.1.4 - 16 November Jetty-4.1.4 - 16 November 2002
+ Fixed ContextLoader parent delegation bug + Fixed ContextLoader parent delegation bug
+ Fixed remove SocketListener bug. + Fixed remove SocketListener bug.
+ Fixed Invoker servlet for RD.include + Fixed Invoker servlet for RD.include
@ -3277,7 +3278,7 @@ Jetty-4.2.0rc0 - 24 October 2002
+ Added authenticator to admin.xml + Added authenticator to admin.xml
+ Fixed Session timeout NPE. + Fixed Session timeout NPE.
Jetty-4.1.3 - 24 October 2002 Jetty-4.1.3 - 24 October 2002
+ Fixed RolloverFileOutputStream without date. + Fixed RolloverFileOutputStream without date.
+ Fixed SessionManager initialization + Fixed SessionManager initialization
+ Added authenticator to admin.xml + Added authenticator to admin.xml

View File

@ -0,0 +1,50 @@
package org.eclipse.jetty.http;
import org.eclipse.jetty.io.Buffer;
import org.junit.Assert;
import org.junit.Test;
public class MimeTypesTest
{
@Test
public void testGetMimeByExtension_Gzip()
{
assertMimeTypeByExtension("application/gzip","test.gz");
}
@Test
public void testGetMimeByExtension_Png()
{
assertMimeTypeByExtension("image/png","test.png");
assertMimeTypeByExtension("image/png","TEST.PNG");
assertMimeTypeByExtension("image/png","Test.Png");
}
@Test
public void testGetMimeByExtension_Png_MultiDot()
{
assertMimeTypeByExtension("image/png","org.eclipse.jetty.Logo.png");
}
@Test
public void testGetMimeByExtension_Png_DeepPath()
{
assertMimeTypeByExtension("image/png","/org/eclipse/jetty/Logo.png");
}
@Test
public void testGetMimeByExtension_Text()
{
assertMimeTypeByExtension("text/plain","test.txt");
assertMimeTypeByExtension("text/plain","TEST.TXT");
}
private void assertMimeTypeByExtension(String expectedMimeType, String filename)
{
MimeTypes mimetypes = new MimeTypes();
Buffer contentType = mimetypes.getMimeByExtension(filename);
String prefix = "MimeTypes.getMimeByExtension(" + filename + ")";
Assert.assertNotNull(prefix,contentType);
Assert.assertEquals(prefix,expectedMimeType,contentType.toString());
}
}

View File

@ -119,6 +119,12 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo
_manager.endPointUpgraded(this,old); _manager.endPointUpgraded(this,old);
} }
/* ------------------------------------------------------------ */
public long getIdleTimestamp()
{
return _idleTimestamp;
}
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/** Called by selectSet to schedule handling /** Called by selectSet to schedule handling
* *

View File

@ -1,5 +1,4 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>

View File

@ -56,7 +56,7 @@ public class RewriteRegexRule extends RegexRule implements Rule.ApplyURI
target=_replacement; target=_replacement;
for (int g=1;g<=matcher.groupCount();g++) for (int g=1;g<=matcher.groupCount();g++)
{ {
String group = matcher.group(g); String group = Matcher.quoteReplacement(matcher.group(g));
target=target.replaceAll("\\$"+g,group); target=target.replaceAll("\\$"+g,group);
} }
@ -73,7 +73,7 @@ public class RewriteRegexRule extends RegexRule implements Rule.ApplyURI
String uri=_replacement; String uri=_replacement;
for (int g=1;g<=matcher.groupCount();g++) for (int g=1;g<=matcher.groupCount();g++)
{ {
String group = matcher.group(g); String group = Matcher.quoteReplacement(matcher.group(g));
uri=uri.replaceAll("\\$"+g,group); uri=uri.replaceAll("\\$"+g,group);
} }
request.setRequestURI(uri); request.setRequestURI(uri);

View File

@ -26,6 +26,8 @@ public class RewriteRegexRuleTest extends AbstractRuleTestCase
{"/foo/bar",".*","/replace","/replace"}, {"/foo/bar",".*","/replace","/replace"},
{"/foo/bar","/xxx.*","/replace",null}, {"/foo/bar","/xxx.*","/replace",null},
{"/foo/bar","/(.*)/(.*)","/$2/$1/xxx","/bar/foo/xxx"}, {"/foo/bar","/(.*)/(.*)","/$2/$1/xxx","/bar/foo/xxx"},
{"/foo/$bar",".*","/$replace","/$replace"},
{"/foo/$bar","/foo/(.*)","/$1/replace","/$bar/replace"},
}; };
private RewriteRegexRule _rule; private RewriteRegexRule _rule;

View File

@ -6,12 +6,15 @@ import java.util.List;
import javax.servlet.Servlet; import javax.servlet.Servlet;
import org.eclipse.jetty.http.gzip.GzipResponseWrapper; import org.eclipse.jetty.http.gzip.GzipResponseWrapper;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlets.gzip.GzipTester; 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.TestServletLengthTypeStreamWrite;
import org.eclipse.jetty.servlets.gzip.TestServletStreamLengthTypeWrite; import org.eclipse.jetty.servlets.gzip.TestServletStreamLengthTypeWrite;
import org.eclipse.jetty.servlets.gzip.TestServletStreamTypeLengthWrite; import org.eclipse.jetty.servlets.gzip.TestServletStreamTypeLengthWrite;
import org.eclipse.jetty.servlets.gzip.TestServletTypeLengthStreamWrite; import org.eclipse.jetty.servlets.gzip.TestServletTypeLengthStreamWrite;
import org.eclipse.jetty.servlets.gzip.TestServletTypeStreamLengthWrite;
import org.eclipse.jetty.toolchain.test.TestingDir; import org.eclipse.jetty.toolchain.test.TestingDir;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
@ -30,11 +33,13 @@ public class GzipFilterContentLengthTest
/** /**
* These are the junit parameters for running this test. * These are the junit parameters for running this test.
* <p> * <p>
* We have 4 test servlets, that arrange the content-length/content-type/get stream in different orders so as to * In addition to Jetty's DefaultServlet we have multiple test
* simulate the real world scenario that caused the bug in <a * servlets that arrange content-length/content-type/get stream
* href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a> * in different order so as to simulate the real world scenario
* that caused the bug in Eclipse <a href="Bug 354014">http://bugs.eclipse.org/354014</a>
* <p> * <p>
* This test case will be run with each entry in the array below as setup parameters for the test case. * This test case will be run with each of the entries in
* the array below as setup parameters for the test case.
* *
* @return the junit parameters * @return the junit parameters
*/ */
@ -43,15 +48,19 @@ public class GzipFilterContentLengthTest
{ {
return Arrays.asList(new Object[][] return Arrays.asList(new Object[][]
{ {
{ DefaultServlet.class },
{ TestServletLengthStreamTypeWrite.class },
{ TestServletLengthTypeStreamWrite.class }, { TestServletLengthTypeStreamWrite.class },
{ TestServletStreamLengthTypeWrite.class }, { TestServletStreamLengthTypeWrite.class },
{ TestServletStreamTypeLengthWrite.class }, { TestServletStreamTypeLengthWrite.class },
{ TestServletTypeLengthStreamWrite.class } }); { TestServletTypeLengthStreamWrite.class },
{ TestServletTypeStreamLengthWrite.class } });
} }
private static final int LARGE = GzipResponseWrapper.DEFAULT_BUFFER_SIZE * 8; private static final int LARGE = GzipResponseWrapper.DEFAULT_BUFFER_SIZE * 8;
private static final int MEDIUM = GzipResponseWrapper.DEFAULT_BUFFER_SIZE; private static final int MEDIUM = GzipResponseWrapper.DEFAULT_BUFFER_SIZE;
private static final int SMALL = GzipResponseWrapper.DEFAULT_BUFFER_SIZE / 4; private static final int SMALL = GzipResponseWrapper.DEFAULT_BUFFER_SIZE / 4;
private static final int TINY = GzipResponseWrapper.DEFAULT_MIN_GZIP_SIZE / 2;
@Rule @Rule
public TestingDir testingdir = new TestingDir(); public TestingDir testingdir = new TestingDir();
@ -63,20 +72,19 @@ public class GzipFilterContentLengthTest
this.testServlet = testServlet; this.testServlet = testServlet;
} }
private void assertIsGzipCompressed(Class<? extends Servlet> servletClass, int filesize) throws Exception private void assertIsGzipCompressed(String filename, int filesize) throws Exception
{ {
GzipTester tester = new GzipTester(testingdir); GzipTester tester = new GzipTester(testingdir);
// Test content that is smaller than the buffer. tester.prepareServerFile(filename,filesize);
tester.prepareServerFile("file.txt",filesize);
FilterHolder holder = tester.setContentServlet(servletClass); FilterHolder holder = tester.setContentServlet(testServlet);
holder.setInitParameter("mimeTypes","text/plain"); holder.setInitParameter("mimeTypes","text/plain");
try try
{ {
tester.start(); tester.start();
tester.assertIsResponseGzipCompressed("file.txt"); tester.assertIsResponseGzipCompressed(filename);
} }
finally finally
{ {
@ -84,20 +92,19 @@ public class GzipFilterContentLengthTest
} }
} }
private void assertIsNotGzipCompressed(Class<? extends Servlet> servletClass, int filesize) throws Exception private void assertIsNotGzipCompressed(String filename, int filesize) throws Exception
{ {
GzipTester tester = new GzipTester(testingdir); GzipTester tester = new GzipTester(testingdir);
// Test content that is smaller than the buffer. tester.prepareServerFile(filename,filesize);
tester.prepareServerFile("file.mp3",filesize);
FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class); FilterHolder holder = tester.setContentServlet(testServlet);
holder.setInitParameter("mimeTypes","text/plain"); holder.setInitParameter("mimeTypes","text/plain");
try try
{ {
tester.start(); tester.start();
tester.assertIsResponseNotGzipCompressed("file.mp3",filesize); tester.assertIsResponseNotGzipCompressed(filename,filesize);
} }
finally finally
{ {
@ -105,42 +112,78 @@ public class GzipFilterContentLengthTest
} }
} }
@Test
public void testIsGzipCompressedTiny() throws Exception
{
assertIsGzipCompressed(testServlet,SMALL);
}
/** /**
* Tests for Length>Type>Stream>Write problems encountered in GzipFilter * Tests gzip compression of a small size file
* */
* @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a> @Test
public void testIsGzipCompressedSmall() throws Exception
{
assertIsGzipCompressed("file.txt",SMALL);
}
/**
* Tests gzip compression of a medium size file
*/ */
@Test @Test
public void testIsGzipCompressedMedium() throws Exception public void testIsGzipCompressedMedium() throws Exception
{ {
assertIsGzipCompressed(testServlet,MEDIUM); assertIsGzipCompressed("file.txt",MEDIUM);
} }
/** /**
* Tests for Length>Type>Stream>Write problems encountered in GzipFilter * Tests gzip compression of a large size file
*
* @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
*/ */
@Test @Test
public void testIsGzipCompressedLarge() throws Exception public void testIsGzipCompressedLarge() throws Exception
{ {
assertIsGzipCompressed(testServlet,LARGE); assertIsGzipCompressed("file.txt",LARGE);
} }
/** /**
* Tests for Length>Type>Stream>Write problems encountered in GzipFilter * Tests for problems with Content-Length header on small size files
* that are not being compressed encountered when using GzipFilter
* *
* @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a> * @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
*/ */
@Test @Test
public void testIsNotGzipCompressed() throws Exception public void testIsNotGzipCompressedTiny() throws Exception
{ {
assertIsNotGzipCompressed(TestServletLengthTypeStreamWrite.class,LARGE); assertIsNotGzipCompressed("file.txt",TINY);
}
/**
* Tests for problems with Content-Length header on small size files
* that are not being compressed encountered when using GzipFilter
*
* @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
*/
@Test
public void testIsNotGzipCompressedSmall() throws Exception
{
assertIsNotGzipCompressed("file.mp3",SMALL);
}
/**
* Tests for problems with Content-Length header on medium size files
* that are not being compressed encountered when using GzipFilter
*
* @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
*/
@Test
public void testIsNotGzipCompressedMedium() throws Exception
{
assertIsNotGzipCompressed("file.mp3",MEDIUM);
}
/**
* Tests for problems with Content-Length header on large size files
* that were not being compressed encountered when using GzipFilter
*
* @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
*/
@Test
public void testIsNotGzipCompressedLarge() throws Exception
{
assertIsNotGzipCompressed("file.mp3",LARGE);
} }
} }

View File

@ -8,10 +8,10 @@ import java.util.List;
import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlets.gzip.GzipTester; import org.eclipse.jetty.servlets.gzip.GzipTester;
import org.eclipse.jetty.servlets.gzip.TestStaticMimeTypeServlet;
import org.eclipse.jetty.toolchain.test.IO; import org.eclipse.jetty.toolchain.test.IO;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.TestingDir; import org.eclipse.jetty.toolchain.test.TestingDir;
import org.junit.Ignore;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -19,9 +19,8 @@ import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters; import org.junit.runners.Parameterized.Parameters;
/** /**
* Tests {@link GzipFilter} in combination with {@link DefaultServlet} for * Tests {@link GzipFilter} in combination with {@link DefaultServlet} for ability to configure {@link GzipFilter} to
* ability to configure {@link GzipFilter} to ignore recompress situations * ignore recompress situations from upstream.
* from upstream.
*/ */
@RunWith(Parameterized.class) @RunWith(Parameterized.class)
public class GzipFilterDefaultNoRecompressTest public class GzipFilterDefaultNoRecompressTest
@ -32,59 +31,52 @@ public class GzipFilterDefaultNoRecompressTest
return Arrays.asList(new Object[][] return Arrays.asList(new Object[][]
{ {
// Some already compressed files // Some already compressed files
{ "test_quotes.gz" }, { "test_quotes.gz", "application/gzip" },
{ "test_quotes.bz2" }, { "test_quotes.bz2", "application/bzip2" },
{ "test_quotes.zip" }, { "test_quotes.zip", "application/zip" },
{ "test_quotes.rar" }, { "test_quotes.rar", "application/octet-stream" },
// Some images (common first) // Some images (common first)
{ "jetty_logo.png" }, { "jetty_logo.png", "image/png" },
{ "jetty_logo.gif" }, { "jetty_logo.gif", "image/gif" },
{ "jetty_logo.jpeg" }, { "jetty_logo.jpeg", "image/jpeg" },
{ "jetty_logo.jpg" }, { "jetty_logo.jpg", "image/jpeg" },
// Lesser encountered images (usually found being requested from non-browser clients) // Lesser encountered images (usually found being requested from non-browser clients)
{ "jetty_logo.bmp" }, { "jetty_logo.bmp", "image/bmp" },
{ "jetty_logo.tga" }, { "jetty_logo.tga", "application/tga" },
{ "jetty_logo.tif" }, { "jetty_logo.tif", "image/tiff" },
{ "jetty_logo.tiff" }, { "jetty_logo.tiff", "image/tiff" },
{ "jetty_logo.xcf" }, { "jetty_logo.xcf", "image/xcf" },
{ "jetty_logo.jp2" } }); { "jetty_logo.jp2", "image/jpeg2000" } });
} }
@Rule @Rule
public TestingDir testingdir = new TestingDir(); public TestingDir testingdir = new TestingDir();
private String alreadyCompressedFilename; private String alreadyCompressedFilename;
private String expectedContentType;
public GzipFilterDefaultNoRecompressTest(String testFilename) {
public GzipFilterDefaultNoRecompressTest(String testFilename, String expectedContentType)
{
this.alreadyCompressedFilename = testFilename; this.alreadyCompressedFilename = testFilename;
this.expectedContentType = expectedContentType;
} }
@Test @Test
@Ignore("Cannot find a configuration that would allow this to pass")
public void testNotGzipFiltered_Default_AlreadyCompressed() throws Exception public void testNotGzipFiltered_Default_AlreadyCompressed() throws Exception
{ {
GzipTester tester = new GzipTester(testingdir); GzipTester tester = new GzipTester(testingdir);
copyTestFileToServer(alreadyCompressedFilename); copyTestFileToServer(alreadyCompressedFilename);
// Using DefaultServlet, with default GzipFilter setup FilterHolder holder = tester.setContentServlet(TestStaticMimeTypeServlet.class);
FilterHolder holder = tester.setContentServlet(DefaultServlet.class);
// TODO: find a configuration of the GzipFilter to allow
// each of these test cases to pass.
StringBuilder mimeTypes = new StringBuilder(); StringBuilder mimeTypes = new StringBuilder();
mimeTypes.append("images/png"); mimeTypes.append("text/plain");
mimeTypes.append(",images/jpeg"); holder.setInitParameter("mimeTypes",mimeTypes.toString());
mimeTypes.append(",images/gif");
mimeTypes.append(",images/jp2");
holder.setInitParameter("mimeTypes", mimeTypes.toString());
try try
{ {
tester.start(); tester.start();
tester.assertIsResponseNotGzipFiltered(alreadyCompressedFilename, tester.assertIsResponseNotGzipFiltered(alreadyCompressedFilename,alreadyCompressedFilename + ".sha1",expectedContentType);
alreadyCompressedFilename + ".sha1");
} }
finally finally
{ {

View File

@ -113,8 +113,9 @@ public class GzipTester
* @param testResourceSha1Sum * @param testResourceSha1Sum
* the sha1sum file that contains the SHA1SUM checksum that will be used to verify that the response * the sha1sum file that contains the SHA1SUM checksum that will be used to verify that the response
* contents are what is intended. * contents are what is intended.
* @param expectedContentType
*/ */
public void assertIsResponseNotGzipFiltered(String requestedFilename, String testResourceSha1Sum) throws Exception public void assertIsResponseNotGzipFiltered(String requestedFilename, String testResourceSha1Sum, String expectedContentType) throws Exception
{ {
System.err.printf("[GzipTester] requesting /context/%s%n",requestedFilename); System.err.printf("[GzipTester] requesting /context/%s%n",requestedFilename);
HttpTester request = new HttpTester(); HttpTester request = new HttpTester();
@ -135,13 +136,13 @@ public class GzipTester
dumpHeaders(requestedFilename + " / Response Headers",response); dumpHeaders(requestedFilename + " / Response Headers",response);
// Assert the response headers // Assert the response headers
Assert.assertThat(requestedFilename + " / Response.method",response.getMethod(),nullValue()); String prefix = requestedFilename + " / Response";
Assert.assertThat(requestedFilename + " / Response.status",response.getStatus(),is(HttpServletResponse.SC_OK)); Assert.assertThat(prefix + ".method",response.getMethod(),nullValue());
Assert.assertThat(requestedFilename + " / Response.header[Content-Length]",response.getHeader("Content-Length"),notNullValue()); Assert.assertThat(prefix + ".status",response.getStatus(),is(HttpServletResponse.SC_OK));
Assert.assertThat(requestedFilename + " / Response.header[Content-Encoding] (should not be recompressed by GzipFilter)", Assert.assertThat(prefix + ".header[Content-Length]",response.getHeader("Content-Length"),notNullValue());
response.getHeader("Content-Encoding"),nullValue()); Assert.assertThat(prefix + ".header[Content-Encoding] (should not be recompressed by GzipFilter)",response.getHeader("Content-Encoding"),nullValue());
Assert.assertThat(requestedFilename + " / Response.header[Content-Type] (should have a Content-Type associated with it)", Assert.assertThat(prefix + ".header[Content-Type] (should have a Content-Type associated with it)",response.getHeader("Content-Type"),notNullValue());
response.getHeader("Content-Type"),notNullValue()); Assert.assertThat(prefix + ".header[Content-Type]",response.getHeader("Content-Type"),is(expectedContentType));
ByteArrayInputStream bais = null; ByteArrayInputStream bais = null;
DigestOutputStream digester = null; DigestOutputStream digester = null;

View File

@ -0,0 +1,47 @@
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) set content length
* 2) get stream
* 3) set content type
* 4) write
* </pre>
*
* @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
*/
@SuppressWarnings("serial")
public class TestServletLengthStreamTypeWrite extends TestDirContentServlet
{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
String fileName = request.getServletPath();
byte[] dataBytes = loadContentFileBytes(fileName);
response.setContentLength(dataBytes.length);
ServletOutputStream out = response.getOutputStream();
if (fileName.endsWith("txt"))
response.setContentType("text/plain");
else if (fileName.endsWith("mp3"))
response.setContentType("audio/mpeg");
out.write(dataBytes);
}
}

View File

@ -0,0 +1,47 @@
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) set content type
* 2) get stream
* 3) set content length
* 4) write
* </pre>
*
* @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
*/
@SuppressWarnings("serial")
public class TestServletTypeStreamLengthWrite extends TestDirContentServlet
{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
String fileName = request.getServletPath();
byte[] dataBytes = loadContentFileBytes(fileName);
if (fileName.endsWith("txt"))
response.setContentType("text/plain");
else if (fileName.endsWith("mp3"))
response.setContentType("audio/mpeg");
ServletOutputStream out = response.getOutputStream();
response.setContentLength(dataBytes.length);
out.write(dataBytes);
}
}

View File

@ -0,0 +1,67 @@
package org.eclipse.jetty.servlets.gzip;
import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.io.Buffer;
/**
* Test servlet for testing against unusual MimeTypes and Content-Types.
*/
@SuppressWarnings("serial")
public class TestStaticMimeTypeServlet extends TestDirContentServlet
{
private MimeTypes mimeTypes;
@Override
public void init(ServletConfig config) throws ServletException
{
super.init(config);
mimeTypes = new MimeTypes();
// Some real world, yet not terribly common, mime type mappings.
mimeTypes.addMimeMapping("bz2","application/bzip2");
mimeTypes.addMimeMapping("bmp","image/bmp");
mimeTypes.addMimeMapping("tga","application/tga");
mimeTypes.addMimeMapping("xcf","image/xcf");
mimeTypes.addMimeMapping("jp2","image/jpeg2000");
// Some of the other gzip mime-types seen in the wild.
// NOTE: Using oddball extensions just so that the calling request can specify
// which strange mime type to use.
mimeTypes.addMimeMapping("x-gzip","application/x-gzip");
mimeTypes.addMimeMapping("x-gunzip","application/x-gunzip");
mimeTypes.addMimeMapping("gzipped","application/gzippped");
mimeTypes.addMimeMapping("gzip-compressed","application/gzip-compressed");
mimeTypes.addMimeMapping("x-compressed","application/x-compressed");
mimeTypes.addMimeMapping("x-compress","application/x-compress");
mimeTypes.addMimeMapping("gzipdoc","gzip/document");
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
String fileName = request.getServletPath();
byte[] dataBytes = loadContentFileBytes(fileName);
response.setContentLength(dataBytes.length);
Buffer buf = mimeTypes.getMimeByExtension(fileName);
if (buf == null)
{
response.setContentType("application/octet-stream");
}
else
{
response.setContentType(buf.toString());
}
ServletOutputStream out = response.getOutputStream();
out.write(dataBytes);
}
}

View File

@ -228,7 +228,7 @@ public class TestClient implements WebSocket.OnFrame
__start=System.currentTimeMillis(); __start=System.currentTimeMillis();
for (int i=0;i<clients;i++) for (int i=0;i<clients;i++)
{ {
client[i]=new TestClient(host,port,protocol==null?null:protocol,10000); client[i]=new TestClient(host,port,protocol==null?null:protocol,60000);
client[i].open(); client[i].open();
} }

View File

@ -14,7 +14,7 @@ public interface WebSocketConnection extends Connection
{ {
void fillBuffersFrom(Buffer buffer); void fillBuffersFrom(Buffer buffer);
void handshake(HttpServletRequest request, HttpServletResponse response, String origin, String subprotocol) throws IOException; void handshake(HttpServletRequest request, HttpServletResponse response, String subprotocol) throws IOException;
List<Extension> getExtensions(); List<Extension> getExtensions();

View File

@ -362,7 +362,7 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc
void access(EndPoint endp); void access(EndPoint endp);
} }
public void handshake(HttpServletRequest request, HttpServletResponse response, String origin, String subprotocol) throws IOException public void handshake(HttpServletRequest request, HttpServletResponse response, String subprotocol) throws IOException
{ {
String uri=request.getRequestURI(); String uri=request.getRequestURI();
String query=request.getQueryString(); String query=request.getQueryString();
@ -370,7 +370,9 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc
uri+="?"+query; uri+="?"+query;
String host=request.getHeader("Host"); String host=request.getHeader("Host");
String origin=request.getHeader("Host");
String key1 = request.getHeader("Sec-WebSocket-Key1"); String key1 = request.getHeader("Sec-WebSocket-Key1");
if (key1!=null) if (key1!=null)
{ {
String key2 = request.getHeader("Sec-WebSocket-Key2"); String key2 = request.getHeader("Sec-WebSocket-Key2");

View File

@ -710,7 +710,7 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
public void handshake(HttpServletRequest request, HttpServletResponse response, String origin, String subprotocol) throws IOException public void handshake(HttpServletRequest request, HttpServletResponse response, String subprotocol) throws IOException
{ {
String uri=request.getRequestURI(); String uri=request.getRequestURI();
String query=request.getQueryString(); String query=request.getQueryString();

View File

@ -90,7 +90,8 @@ public class WebSocketConnectionD10 extends AbstractConnection implements WebSoc
private final String _protocol; private final String _protocol;
private final int _draft; private final int _draft;
private final ClassLoader _context; private final ClassLoader _context;
private int _close; private volatile int _closeCode;
private volatile String _closeMessage;
private volatile boolean _closedIn; private volatile boolean _closedIn;
private volatile boolean _closedOut; private volatile boolean _closedOut;
private int _maxTextMessageSize; private int _maxTextMessageSize;
@ -275,7 +276,8 @@ public class WebSocketConnectionD10 extends AbstractConnection implements WebSoc
@Override @Override
public void idleExpired() public void idleExpired()
{ {
closeOut(WebSocketConnectionD10.CLOSE_NORMAL,"Idle"); long idle = System.currentTimeMillis()-((SelectChannelEndPoint)_endp).getIdleTimestamp();
closeOut(WebSocketConnectionD10.CLOSE_NORMAL,"Idle for "+idle+"ms");
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
@ -290,9 +292,9 @@ public class WebSocketConnectionD10 extends AbstractConnection implements WebSoc
final boolean closed; final boolean closed;
synchronized (this) synchronized (this)
{ {
closed=_close==0; closed=_closeCode==0;
if (closed) if (closed)
_close=WebSocketConnectionD10.CLOSE_NOCLOSE; _closeCode=WebSocketConnectionD10.CLOSE_NOCLOSE;
} }
if (closed) if (closed)
_webSocket.onClose(WebSocketConnectionD10.CLOSE_NOCLOSE,"closed"); _webSocket.onClose(WebSocketConnectionD10.CLOSE_NOCLOSE,"closed");
@ -309,9 +311,12 @@ public class WebSocketConnectionD10 extends AbstractConnection implements WebSoc
{ {
closedOut=_closedOut; closedOut=_closedOut;
_closedIn=true; _closedIn=true;
closed=_close==0; closed=_closeCode==0;
if (closed) if (closed)
_close=code; {
_closeCode=code;
_closeMessage=message;
}
} }
try try
@ -346,9 +351,12 @@ public class WebSocketConnectionD10 extends AbstractConnection implements WebSoc
{ {
close=_closedIn || _closedOut; close=_closedIn || _closedOut;
_closedOut=true; _closedOut=true;
closed=_close==0; closed=_closeCode==0;
if (closed) if (closed)
_close=code; {
_closeCode=code;
_closeMessage=message;
}
} }
try try
@ -409,7 +417,7 @@ public class WebSocketConnectionD10 extends AbstractConnection implements WebSoc
public void sendMessage(String content) throws IOException public void sendMessage(String content) throws IOException
{ {
if (_closedOut) if (_closedOut)
throw new IOException("closing"); throw new IOException("closedOut "+_closeCode+":"+_closeMessage);
byte[] data = content.getBytes(StringUtil.__UTF8); byte[] data = content.getBytes(StringUtil.__UTF8);
_outbound.addFrame((byte)FLAG_FIN,WebSocketConnectionD10.OP_TEXT,data,0,data.length); _outbound.addFrame((byte)FLAG_FIN,WebSocketConnectionD10.OP_TEXT,data,0,data.length);
checkWriteable(); checkWriteable();
@ -420,7 +428,7 @@ public class WebSocketConnectionD10 extends AbstractConnection implements WebSoc
public void sendMessage(byte[] content, int offset, int length) throws IOException public void sendMessage(byte[] content, int offset, int length) throws IOException
{ {
if (_closedOut) if (_closedOut)
throw new IOException("closing"); throw new IOException("closedOut "+_closeCode+":"+_closeMessage);
_outbound.addFrame((byte)FLAG_FIN,WebSocketConnectionD10.OP_BINARY,content,offset,length); _outbound.addFrame((byte)FLAG_FIN,WebSocketConnectionD10.OP_BINARY,content,offset,length);
checkWriteable(); checkWriteable();
_idle.access(_endp); _idle.access(_endp);
@ -430,7 +438,7 @@ public class WebSocketConnectionD10 extends AbstractConnection implements WebSoc
public void sendFrame(byte flags,byte opcode, byte[] content, int offset, int length) throws IOException public void sendFrame(byte flags,byte opcode, byte[] content, int offset, int length) throws IOException
{ {
if (_closedOut) if (_closedOut)
throw new IOException("closing"); throw new IOException("closedOut "+_closeCode+":"+_closeMessage);
_outbound.addFrame(flags,opcode,content,offset,length); _outbound.addFrame(flags,opcode,content,offset,length);
checkWriteable(); checkWriteable();
_idle.access(_endp); _idle.access(_endp);
@ -440,7 +448,7 @@ public class WebSocketConnectionD10 extends AbstractConnection implements WebSoc
public void sendControl(byte ctrl, byte[] data, int offset, int length) throws IOException public void sendControl(byte ctrl, byte[] data, int offset, int length) throws IOException
{ {
if (_closedOut) if (_closedOut)
throw new IOException("closing"); throw new IOException("closedOut "+_closeCode+":"+_closeMessage);
_outbound.addFrame((byte)FLAG_FIN,ctrl,data,offset,length); _outbound.addFrame((byte)FLAG_FIN,ctrl,data,offset,length);
checkWriteable(); checkWriteable();
_idle.access(_endp); _idle.access(_endp);
@ -798,7 +806,7 @@ public class WebSocketConnectionD10 extends AbstractConnection implements WebSoc
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
public void handshake(HttpServletRequest request, HttpServletResponse response, String origin, String subprotocol) throws IOException public void handshake(HttpServletRequest request, HttpServletResponse response, String subprotocol) throws IOException
{ {
String uri=request.getRequestURI(); String uri=request.getRequestURI();
String query=request.getQueryString(); String query=request.getQueryString();

View File

@ -38,9 +38,21 @@ public class WebSocketFactory
{ {
public interface Acceptor public interface Acceptor
{ {
/* ------------------------------------------------------------ */
/**
* @param request
* @param protocol
* @return
*/
WebSocket doWebSocketConnect(HttpServletRequest request, String protocol); WebSocket doWebSocketConnect(HttpServletRequest request, String protocol);
String checkOrigin(HttpServletRequest request, String host, String origin); /* ------------------------------------------------------------ */
/** Check the origin of an incoming WebSocket handshake request
* @param request
* @param origin
* @return boolean to indicate that the origin is acceptable.
*/
boolean checkOrigin(HttpServletRequest request, String origin);
} }
private final Map<String,Class<? extends Extension>> _extensionClasses = new HashMap<String, Class<? extends Extension>>(); private final Map<String,Class<? extends Extension>> _extensionClasses = new HashMap<String, Class<? extends Extension>>();
@ -128,7 +140,7 @@ public class WebSocketFactory
* @param protocol The websocket protocol * @param protocol The websocket protocol
* @throws IOException in case of I/O errors * @throws IOException in case of I/O errors
*/ */
public void upgrade(HttpServletRequest request, HttpServletResponse response, WebSocket websocket, String origin, String protocol) public void upgrade(HttpServletRequest request, HttpServletResponse response, WebSocket websocket, String protocol)
throws IOException throws IOException
{ {
if (!"websocket".equalsIgnoreCase(request.getHeader("Upgrade"))) if (!"websocket".equalsIgnoreCase(request.getHeader("Upgrade")))
@ -176,7 +188,7 @@ public class WebSocketFactory
} }
// Let the connection finish processing the handshake // Let the connection finish processing the handshake
connection.handshake(request, response, origin, protocol); connection.handshake(request, response, protocol);
response.flushBuffer(); response.flushBuffer();
// Give the connection any unused data from the HTTP connection. // Give the connection any unused data from the HTTP connection.
@ -205,11 +217,20 @@ public class WebSocketFactory
{ {
if ("websocket".equalsIgnoreCase(request.getHeader("Upgrade"))) if ("websocket".equalsIgnoreCase(request.getHeader("Upgrade")))
{ {
String origin = request.getHeader("Sec-WebSocket-Origin");
if (origin==null)
origin = request.getHeader("Origin");
if (!_acceptor.checkOrigin(request,origin))
{
response.sendError(HttpServletResponse.SC_FORBIDDEN);
return false;
}
// Try each requested protocol
WebSocket websocket = null;
String protocol = request.getHeader("Sec-WebSocket-Protocol"); String protocol = request.getHeader("Sec-WebSocket-Protocol");
if (protocol == null) // TODO remove once draft period is over if (protocol == null) // TODO remove once draft period is over
protocol = request.getHeader("WebSocket-Protocol"); protocol = request.getHeader("WebSocket-Protocol");
WebSocket websocket = null;
for (String p : parseProtocols(protocol)) for (String p : parseProtocols(protocol))
{ {
websocket = _acceptor.doWebSocketConnect(request, p); websocket = _acceptor.doWebSocketConnect(request, p);
@ -220,17 +241,16 @@ public class WebSocketFactory
} }
} }
String host = request.getHeader("Host"); // Did we get a websocket?
String origin = request.getHeader("Origin"); if (websocket == null)
origin = _acceptor.checkOrigin(request, host, origin);
if (websocket != null)
{ {
upgrade(request, response, websocket, origin, protocol); response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
return true; return false;
} }
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); // Send the upgrade
upgrade(request, response, websocket, protocol);
return true;
} }
return false; return false;

View File

@ -100,11 +100,9 @@ public abstract class WebSocketHandler extends HandlerWrapper implements WebSock
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
public String checkOrigin(HttpServletRequest request, String host, String origin) public boolean checkOrigin(HttpServletRequest request, String origin)
{ {
if (origin==null) return true;
origin=host;
return origin;
} }
} }

View File

@ -14,6 +14,8 @@
package org.eclipse.jetty.websocket; package org.eclipse.jetty.websocket;
import java.io.IOException; import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServlet;
@ -65,11 +67,10 @@ public abstract class WebSocketServlet extends HttpServlet implements WebSocketF
super.service(request,response); super.service(request,response);
} }
public String checkOrigin(HttpServletRequest request, String host, String origin) /* ------------------------------------------------------------ */
public boolean checkOrigin(HttpServletRequest request, String origin)
{ {
if (origin==null) return true;
origin=host;
return origin;
} }

View File

@ -745,7 +745,7 @@ public class WebSocketMessageD10Test
lookFor("sent on connect",input); lookFor("sent on connect",input);
assertEquals((byte)0x88,(byte)input.read()); assertEquals((byte)0x88,(byte)input.read());
assertEquals(0x06,input.read()); assertEquals(17,input.read());
assertEquals(1000/0x100,input.read()); assertEquals(1000/0x100,input.read());
assertEquals(1000%0x100,input.read()); assertEquals(1000%0x100,input.read());
lookFor("Idle",input); lookFor("Idle",input);