diff --git a/.gitignore b/.gitignore index 11c793e1afe..cd084872657 100644 --- a/.gitignore +++ b/.gitignore @@ -20,7 +20,7 @@ target/ .idea/ # Mac filesystem dust -/.DS_Store +.DS_Store # pmd .pmdruleset @@ -31,3 +31,6 @@ target/ # vim .*.sw[a-p] + +# merge tooling +*.orig diff --git a/VERSION.txt b/VERSION.txt index 47a4668db46..97a124abfa9 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1,11 +1,70 @@ -jetty-7.5.4-SNAPSHOT +jetty-7.6.0-SNAPSHOT + +jetty-7.6.0.RC1 - 04 December 2011 + + 352565 cookie httponly flag ignored + + 353285 ServletSecurity annotation ignored + + 357163 jetty 8 ought to proxy jetty8 javadocs + + 357209 JSP tag listeners not called + + 360051 SocketConnectionTest.testServerClosedConnection is excluded. + + 361135 Allow session cookies to NEVER be marked as secure, even on HTTPS + requests. + + 362249 update shell scripts to jetty8 + + 363878 Add ecj compiler to jetty-8 for jsp + + 364283 can't parse the servlet multipart-config for the web.xml + + 364430 Support web.xml enabled state for servlets + + 365370 ServletHandler can fall through to nested handler + +jetty-7.6.0.RC0 - 29 November 2011 + + Refactored NIO layer for better half close handling + + 349110 fixed bypass chunk handling + + 360546 handle set count exceeding max integer + + 362111 StdErrLog.isDebugEnabled() returns true too often + + 362113 Improve Test Coverage of org.eclipse.jetty.util.log classes + + 362407 setTrustStore(Resource) -> setTrustStoreResource(R) + + 362447 add setMaxNonceAge() to DigestAuthenticator + + 362468 NPE at line org.eclipse.jetty.io.BufferUtil.putHexInt + + 362614 NPE in accepting connection + + 362626 IllegalStateException thrown when SslContextFactory preconfigured + with SSLContext + + 362696 expand virtual host configuration options to ContextHandler and add + associated test case for new behavior + + 362742 improved UTF8 exception reason + + 363124 improved websocket close handling + + 363381 Throw IllegalStateException if Request uri is null on getServerName + + 363408 GzipFilter should not attempt to compress HTTP status 204 + + 363488 ShutdownHandler use stopper thread + + 363718 Setting java.rmi.server.hostname in jetty-jmx.xml + + 363757 partial fix + + 363785 StdErrLog must use system-dependent EOL. + + 363943 ignore null attribute values + + 363993 EOFException parsing HEAD response in HttpTester + + 364638 SCEP does idle timestamp checking. New setCheckForIdle method + controls onIdleExpired callback. 364921 a second onIdleExpired callback will + result in close rather than a shutdown output. + + 364657 Support HTTP only cookies from standard API + + JETTY-1442 add _hostHeader setter for ProxyRule + +jetty-7.5.4.v20111024 - 24 October 2011 + + 358263 JDBCSessionIdManager add setDatasource(DataSource) method + + 358649 Replace existing StdErrLog system properties for DEBUG/IGNORED with + LEVEL instead. + + 360836 Accept parameters with bad UTF-8. Use replacement character + + 360912 CrossOriginFilter does not send Access-Control-Allow-Origin on + responses. 355103 Make allowCredentials default to true in + CrossOriginFilter. + + 360938 Connections closed after a while. + + 361319 Log initialization does not catch correct exceptions on all jvms + + 361325 359292 Allow KeyStore to be set + + 361456 release timer task on connection failed + + 361655 ExecutorThreadPool.isLowOnThreads() returns wrong value. + + JETTY-1444 start threadpool before selector manager jetty-7.5.3.v20111011 - 11 October 2011 + 348978 migrate jetty-http-spi + 358649 StdErrLog system properties for package/class logging LEVEL. jetty-7.5.2.v20111006 - 06 October 2011 - + 336443 add missing comma in DigestAuthenticator string + + 336443 check nonce count is increasing + 342161 ScannerTest fails intermittently on Mac OS X + 346419 testing HttpClient FDs + 353267 Request._parameters initialization bug diff --git a/example-jetty-embedded/pom.xml b/example-jetty-embedded/pom.xml index 93f8ad5ff4e..3eb95b0ea27 100644 --- a/example-jetty-embedded/pom.xml +++ b/example-jetty-embedded/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 example-jetty-embedded diff --git a/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java b/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java index 99b954fdbb9..899c682bc08 100644 --- a/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java +++ b/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java @@ -15,11 +15,9 @@ package org.eclipse.jetty.embedded; import java.lang.management.ManagementFactory; -import org.eclipse.jetty.ajp.Ajp13SocketConnector; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.providers.ContextProvider; import org.eclipse.jetty.deploy.providers.WebAppProvider; -import org.eclipse.jetty.http.ssl.SslContextFactory; import org.eclipse.jetty.jmx.MBeanContainer; import org.eclipse.jetty.security.HashLoginService; import org.eclipse.jetty.server.Connector; @@ -35,6 +33,7 @@ import org.eclipse.jetty.server.nio.SelectChannelConnector; import org.eclipse.jetty.server.ssl.SslSelectChannelConnector; import org.eclipse.jetty.server.ssl.SslSocketConnector; import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.QueuedThreadPool; public class LikeJettyXml @@ -52,12 +51,12 @@ public class LikeJettyXml MBeanContainer mbContainer=new MBeanContainer(ManagementFactory.getPlatformMBeanServer()); server.getContainer().addEventListener(mbContainer); server.addBean(mbContainer); - mbContainer.addBean(Log.getLog()); + mbContainer.addBean(Log.getRootLogger()); // Setup Threadpool QueuedThreadPool threadPool = new QueuedThreadPool(); - threadPool.setMaxThreads(100); + threadPool.setMaxThreads(500); server.setThreadPool(threadPool); // Setup Connectors @@ -65,7 +64,7 @@ public class LikeJettyXml connector.setPort(8080); connector.setMaxIdleTime(30000); connector.setConfidentialPort(8443); - connector.setStatsOn(true); + connector.setStatsOn(false); server.setConnectors(new Connector[] { connector }); @@ -88,19 +87,25 @@ public class LikeJettyXml "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA" }); - cf.setProtocol("TLSv1.1"); - cf.addExcludeProtocols(new String[]{"TLSv1","SSLv3"}); - ssl_connector.setStatsOn(true); + ssl_connector.setStatsOn(false); server.addConnector(ssl_connector); ssl_connector.open(); + SslSocketConnector ssl2_connector = new SslSocketConnector(cf); + ssl2_connector.setPort(8444); + ssl2_connector.setStatsOn(false); + server.addConnector(ssl2_connector); + ssl2_connector.open(); - + + /* Ajp13SocketConnector ajp = new Ajp13SocketConnector(); ajp.setPort(8009); server.addConnector(ajp); + */ + HandlerCollection handlers = new HandlerCollection(); ContextHandlerCollection contexts = new ContextHandlerCollection(); RequestLogHandler requestLogHandler = new RequestLogHandler(); @@ -143,10 +148,9 @@ public class LikeJettyXml server.setStopAtShutdown(true); server.setSendServerVersion(true); - + server.start(); server.join(); } - } diff --git a/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java b/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java index 1059b945026..b60b8c1483c 100644 --- a/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java +++ b/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java @@ -13,11 +13,11 @@ package org.eclipse.jetty.embedded; -import org.eclipse.jetty.http.ssl.SslContextFactory; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.nio.SelectChannelConnector; import org.eclipse.jetty.server.ssl.SslSelectChannelConnector; +import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.QueuedThreadPool; /* ------------------------------------------------------------ */ diff --git a/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/SecuredHelloHandler.java b/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/SecuredHelloHandler.java index 33c00e7a69b..d02754041b0 100644 --- a/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/SecuredHelloHandler.java +++ b/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/SecuredHelloHandler.java @@ -17,13 +17,13 @@ import java.util.Collections; import java.util.HashSet; import java.util.Set; -import org.eclipse.jetty.http.security.Constraint; import org.eclipse.jetty.security.ConstraintMapping; import org.eclipse.jetty.security.ConstraintSecurityHandler; import org.eclipse.jetty.security.HashLoginService; import org.eclipse.jetty.security.LoginService; import org.eclipse.jetty.security.authentication.BasicAuthenticator; import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.security.Constraint; public class SecuredHelloHandler { diff --git a/jetty-aggregate/jetty-all-server/pom.xml b/jetty-aggregate/jetty-all-server/pom.xml index 29966c6ac59..072ff23321e 100644 --- a/jetty-aggregate/jetty-all-server/pom.xml +++ b/jetty-aggregate/jetty-all-server/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty.aggregate jetty-aggregate-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 jetty-all-server @@ -145,12 +145,6 @@ ${project.version} provided - - org.eclipse.jetty - jetty-jsp-2.1 - ${project.version} - provided - org.eclipse.jetty jetty-plus diff --git a/jetty-aggregate/jetty-all/pom.xml b/jetty-aggregate/jetty-all/pom.xml index 5a77460bc34..3a6eee3ac51 100644 --- a/jetty-aggregate/jetty-all/pom.xml +++ b/jetty-aggregate/jetty-all/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty.aggregate jetty-aggregate-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 jetty-all @@ -129,12 +129,6 @@ ${project.version} provided - - org.eclipse.jetty - jetty-jsp-2.1 - ${project.version} - provided - org.eclipse.jetty jetty-plus diff --git a/jetty-aggregate/jetty-client/pom.xml b/jetty-aggregate/jetty-client/pom.xml index d4752f3ddb3..e30ee5b160a 100644 --- a/jetty-aggregate/jetty-client/pom.xml +++ b/jetty-aggregate/jetty-client/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty.aggregate jetty-aggregate-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 jetty-client diff --git a/jetty-aggregate/jetty-plus/pom.xml b/jetty-aggregate/jetty-plus/pom.xml index ed64961b5b1..ff9bf1f74c9 100644 --- a/jetty-aggregate/jetty-plus/pom.xml +++ b/jetty-aggregate/jetty-plus/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty.aggregate jetty-aggregate-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 jetty-plus diff --git a/jetty-aggregate/jetty-server/pom.xml b/jetty-aggregate/jetty-server/pom.xml index 39d9aeb15cb..8d3cdddcb2f 100644 --- a/jetty-aggregate/jetty-server/pom.xml +++ b/jetty-aggregate/jetty-server/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty.aggregate jetty-aggregate-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 jetty-server diff --git a/jetty-aggregate/jetty-servlet/pom.xml b/jetty-aggregate/jetty-servlet/pom.xml index 3545f13e718..2e4f9bd47ee 100644 --- a/jetty-aggregate/jetty-servlet/pom.xml +++ b/jetty-aggregate/jetty-servlet/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty.aggregate jetty-aggregate-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 jetty-servlet diff --git a/jetty-aggregate/jetty-webapp/pom.xml b/jetty-aggregate/jetty-webapp/pom.xml index 0b0e218f3f8..47090b07d8f 100644 --- a/jetty-aggregate/jetty-webapp/pom.xml +++ b/jetty-aggregate/jetty-webapp/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty.aggregate jetty-aggregate-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 jetty-webapp @@ -95,11 +95,5 @@ servlet-api compile - - org.eclipse.jetty - jetty-jsp-2.1 - ${project.version} - provided - diff --git a/jetty-aggregate/jetty-websocket/pom.xml b/jetty-aggregate/jetty-websocket/pom.xml new file mode 100644 index 00000000000..c650ef55103 --- /dev/null +++ b/jetty-aggregate/jetty-websocket/pom.xml @@ -0,0 +1,88 @@ + + + org.eclipse.jetty.aggregate + jetty-aggregate-project + 7.6.0-SNAPSHOT + + 4.0.0 + jetty-websocket + Jetty :: Aggregate :: Websocket + + + ${project.build.directory}/sources + + + org.apache.maven.plugins + maven-dependency-plugin + + + unpack-dependencies + + unpack-dependencies + + + META-INF/**,org/eclipse/** + **/MANIFEST.MF,javax/**,about.html + ${project.build.directory}/classes + false + true + + + + unpack-source + generate-sources + + unpack-dependencies + + + sources + **/* + META-INF/** + org.eclipse.jetty + ${project.build.directory}/sources + true + true + + + + + + org.apache.maven.plugins + + maven-jar-plugin + + + package + package + + jar + + + + + + + development + http://eclipse.org/jetty + ${user.name} + org.eclipse.jetty + http://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk/NOTICE.txt + Jetty HTTP Server + + + + + + + + + + + + org.eclipse.jetty + jetty-websocket + ${project.version} + provided + + + diff --git a/jetty-aggregate/pom.xml b/jetty-aggregate/pom.xml index 95709419d10..7de1ac8d379 100644 --- a/jetty-aggregate/pom.xml +++ b/jetty-aggregate/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT org.eclipse.jetty.aggregate jetty-aggregate-project @@ -35,6 +35,7 @@ jetty-client jetty-servlet jetty-webapp + jetty-websocket jetty-plus jetty-all-server jetty-all diff --git a/jetty-ajp/pom.xml b/jetty-ajp/pom.xml index a6272a6b9a6..c02155bea81 100644 --- a/jetty-ajp/pom.xml +++ b/jetty-ajp/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 jetty-ajp diff --git a/jetty-ajp/src/main/java/org/eclipse/jetty/ajp/Ajp13Connection.java b/jetty-ajp/src/main/java/org/eclipse/jetty/ajp/Ajp13Connection.java index 1afc64facfb..f29159b870a 100644 --- a/jetty-ajp/src/main/java/org/eclipse/jetty/ajp/Ajp13Connection.java +++ b/jetty-ajp/src/main/java/org/eclipse/jetty/ajp/Ajp13Connection.java @@ -216,7 +216,10 @@ public class Ajp13Connection extends BlockingHttpConnection public void parsedRequestAttribute(String key, Buffer value) throws IOException { - _request.setAttribute(key, value.toString()); + if (value==null) + _request.removeAttribute(key); + else + _request.setAttribute(key,value.toString()); } public void parsedRequestAttribute(String key, int value) throws IOException diff --git a/jetty-ajp/src/main/java/org/eclipse/jetty/ajp/Ajp13Generator.java b/jetty-ajp/src/main/java/org/eclipse/jetty/ajp/Ajp13Generator.java index dd05df0df91..77d17e6db04 100644 --- a/jetty-ajp/src/main/java/org/eclipse/jetty/ajp/Ajp13Generator.java +++ b/jetty-ajp/src/main/java/org/eclipse/jetty/ajp/Ajp13Generator.java @@ -133,9 +133,9 @@ public class Ajp13Generator extends AbstractGenerator /* ------------------------------------------------------------ */ @Override - public void reset(boolean returnBuffers) + public void reset() { - super.reset(returnBuffers); + super.reset(); _needEOC = false; _needMore = false; @@ -487,7 +487,7 @@ public class Ajp13Generator extends AbstractGenerator /* ------------------------------------------------------------ */ @Override - public long flushBuffer() throws IOException + public int flushBuffer() throws IOException { try { @@ -819,7 +819,7 @@ public class Ajp13Generator extends AbstractGenerator while (buff.length() > 0); _buffers.returnBuffer(buff); - reset(true); + reset(); } diff --git a/jetty-ajp/src/main/java/org/eclipse/jetty/ajp/Ajp13Parser.java b/jetty-ajp/src/main/java/org/eclipse/jetty/ajp/Ajp13Parser.java index 02aa9e8fdd9..21c13c3e053 100644 --- a/jetty-ajp/src/main/java/org/eclipse/jetty/ajp/Ajp13Parser.java +++ b/jetty-ajp/src/main/java/org/eclipse/jetty/ajp/Ajp13Parser.java @@ -148,21 +148,16 @@ public class Ajp13Parser implements Parser } /* ------------------------------------------------------------------------------- */ - public int parseAvailable() throws IOException + public boolean parseAvailable() throws IOException { - int len = parseNext(); - int total = len > 0 ? len : 0; - + boolean progress=parseNext()>0; + // continue parsing - while (!isComplete() && _buffer != null && _buffer.length() > 0) + while (!isComplete() && _buffer!=null && _buffer.length()>0) { - len = parseNext(); - if (len > 0) - total += len; - else - break; + progress |= parseNext()>0; } - return total; + return progress; } /* ------------------------------------------------------------------------------- */ @@ -876,6 +871,15 @@ public class Ajp13Parser implements Parser return _content.length() > 0; } + } + public boolean isPersistent() + { + return true; + } + + public void setPersistent(boolean persistent) + { + LOG.warn("AJP13.setPersistent is not IMPLEMENTED!"); } } diff --git a/jetty-ajp/src/main/java/org/eclipse/jetty/ajp/Ajp13Request.java b/jetty-ajp/src/main/java/org/eclipse/jetty/ajp/Ajp13Request.java index bde26f7a2a3..df92aec440c 100644 --- a/jetty-ajp/src/main/java/org/eclipse/jetty/ajp/Ajp13Request.java +++ b/jetty-ajp/src/main/java/org/eclipse/jetty/ajp/Ajp13Request.java @@ -13,7 +13,7 @@ package org.eclipse.jetty.ajp; -import org.eclipse.jetty.server.HttpConnection; +import org.eclipse.jetty.server.AbstractHttpConnection; import org.eclipse.jetty.server.Request; public class Ajp13Request extends Request @@ -24,7 +24,7 @@ public class Ajp13Request extends Request protected boolean _sslSecure; /* ------------------------------------------------------------ */ - public Ajp13Request(HttpConnection connection) + public Ajp13Request(AbstractHttpConnection connection) { super(connection); } diff --git a/jetty-ajp/src/test/java/org/eclipse/jetty/ajp/Ajp13ConnectionTest.java b/jetty-ajp/src/test/java/org/eclipse/jetty/ajp/Ajp13ConnectionTest.java index 39662b28915..d4e89323296 100644 --- a/jetty-ajp/src/test/java/org/eclipse/jetty/ajp/Ajp13ConnectionTest.java +++ b/jetty-ajp/src/test/java/org/eclipse/jetty/ajp/Ajp13ConnectionTest.java @@ -13,10 +13,10 @@ package org.eclipse.jetty.ajp; -import java.io.BufferedReader; +import static org.junit.Assert.assertTrue; + import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.InputStreamReader; import java.io.OutputStream; import java.net.Socket; import java.net.SocketTimeoutException; @@ -39,8 +39,6 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; -import static org.junit.Assert.assertTrue; - public class Ajp13ConnectionTest { private static final Logger LOG = Log.getLogger(Ajp13ConnectionTest.class); diff --git a/jetty-ajp/src/test/java/org/eclipse/jetty/ajp/TestAjpParser.java b/jetty-ajp/src/test/java/org/eclipse/jetty/ajp/TestAjpParser.java index 460e1f911bd..f6993b20133 100644 --- a/jetty-ajp/src/test/java/org/eclipse/jetty/ajp/TestAjpParser.java +++ b/jetty-ajp/src/test/java/org/eclipse/jetty/ajp/TestAjpParser.java @@ -13,6 +13,9 @@ package org.eclipse.jetty.ajp; +import static junit.framework.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import java.io.IOException; import org.eclipse.jetty.io.Buffer; @@ -23,9 +26,6 @@ import org.eclipse.jetty.io.SimpleBuffers; import org.eclipse.jetty.util.TypeUtil; import org.junit.Test; -import static junit.framework.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - public class TestAjpParser { @Test diff --git a/jetty-annotations/pom.xml b/jetty-annotations/pom.xml index 730fb6b6a41..a4b6c219f44 100644 --- a/jetty-annotations/pom.xml +++ b/jetty-annotations/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 jetty-annotations diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java index 84a8c00954a..bfe0031a442 100644 --- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java @@ -454,7 +454,10 @@ public class AnnotationParser className = className.replace('.', '/')+".class"; URL resource = Loader.getResource(this.getClass(), className, false); if (resource!= null) - scanClass(resource.openStream()); + { + Resource r = Resource.newResource(resource); + scanClass(r.getInputStream()); + } } } } @@ -472,7 +475,10 @@ public class AnnotationParser String nameAsResource = cz.getName().replace('.', '/')+".class"; URL resource = Loader.getResource(this.getClass(), nameAsResource, false); if (resource!= null) - scanClass(resource.openStream()); + { + Resource r = Resource.newResource(resource); + scanClass(r.getInputStream()); + } } } if (visitSuperClasses) @@ -501,7 +507,10 @@ public class AnnotationParser s = s.replace('.', '/')+".class"; URL resource = Loader.getResource(this.getClass(), s, false); if (resource!= null) - scanClass(resource.openStream()); + { + Resource r = Resource.newResource(resource); + scanClass(r.getInputStream()); + } } } } @@ -525,7 +534,10 @@ public class AnnotationParser if (name.endsWith(".class")) { if ((resolver == null)|| (!resolver.isExcluded(name) && (!isParsed(name) || resolver.shouldOverride(name)))) - scanClass(res.getURL().openStream()); + { + Resource r = Resource.newResource(res.getURL()); + scanClass(r.getInputStream()); + } } } diff --git a/jetty-client/pom.xml b/jetty-client/pom.xml index 976b5cd7797..02a0320bdd9 100644 --- a/jetty-client/pom.xml +++ b/jetty-client/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java b/jetty-client/src/main/java/org/eclipse/jetty/client/AbstractHttpConnection.java similarity index 50% rename from jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java rename to jetty-client/src/main/java/org/eclipse/jetty/client/AbstractHttpConnection.java index 94262a24662..20a5d04aa63 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/AbstractHttpConnection.java @@ -1,5 +1,5 @@ // ======================================================================== -// Copyright (c) 2006-2009 Mort Bay Consulting Pty. Ltd. +// Copyright (c) 2006-2011 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 @@ -13,10 +13,8 @@ package org.eclipse.jetty.client; -import java.io.EOFException; import java.io.IOException; import java.io.InputStream; -import java.io.InterruptedIOException; import java.util.Collections; import java.util.concurrent.atomic.AtomicBoolean; @@ -31,7 +29,6 @@ import org.eclipse.jetty.http.HttpSchemes; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpVersions; import org.eclipse.jetty.io.AbstractConnection; -import org.eclipse.jetty.io.AsyncEndPoint; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.Buffers; import org.eclipse.jetty.io.ByteArrayBuffer; @@ -39,7 +36,6 @@ import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EofException; import org.eclipse.jetty.io.View; -import org.eclipse.jetty.io.nio.SslSelectChannelEndPoint; import org.eclipse.jetty.util.component.AggregateLifeCycle; import org.eclipse.jetty.util.component.Dumpable; import org.eclipse.jetty.util.log.Log; @@ -50,28 +46,26 @@ import org.eclipse.jetty.util.thread.Timeout; * * @version $Revision: 879 $ $Date: 2009-09-11 16:13:28 +0200 (Fri, 11 Sep 2009) $ */ -public class HttpConnection extends AbstractConnection implements Dumpable +public abstract class AbstractHttpConnection extends AbstractConnection implements Dumpable { - private static final Logger LOG = Log.getLogger(HttpConnection.class); + private static final Logger LOG = Log.getLogger(AbstractHttpConnection.class); - private HttpDestination _destination; - private HttpGenerator _generator; - private HttpParser _parser; - private boolean _http11 = true; - private int _status; - private Buffer _connectionHeader; - private Buffer _requestContentChunk; - private boolean _requestComplete; - private boolean _reserved; + protected HttpDestination _destination; + protected HttpGenerator _generator; + protected HttpParser _parser; + protected boolean _http11 = true; + protected int _status; + protected Buffer _connectionHeader; + protected boolean _reserved; // The current exchange waiting for a response - private volatile HttpExchange _exchange; - private HttpExchange _pipeline; + protected volatile HttpExchange _exchange; + protected HttpExchange _pipeline; private final Timeout.Task _idleTimeout = new ConnectionIdleTask(); private AtomicBoolean _idle = new AtomicBoolean(false); - HttpConnection(Buffers requestBuffers, Buffers responseBuffers, EndPoint endp) + AbstractHttpConnection(Buffers requestBuffers, Buffers responseBuffers, EndPoint endp) { super(endp); @@ -101,6 +95,7 @@ public class HttpConnection extends AbstractConnection implements Dumpable public boolean send(HttpExchange ex) throws IOException { + LOG.debug("Send {} on {}",ex,this); synchronized (this) { if (_exchange != null) @@ -124,16 +119,6 @@ public class HttpConnection extends AbstractConnection implements Dumpable _exchange.setStatus(HttpExchange.STATUS_WAITING_FOR_COMMIT); - if (_endp.isBlocking()) - { - this.notify(); - } - else - { - AsyncEndPoint scep = (AsyncEndPoint)_endp; - scep.scheduleWrite(); - } - adjustIdleTimeout(); return true; @@ -162,308 +147,8 @@ public class HttpConnection extends AbstractConnection implements Dumpable } } - public Connection handle() throws IOException - { - try - { - int no_progress = 0; + public abstract Connection handle() throws IOException; - boolean failed = false; - while (_endp.isBufferingInput() || _endp.isOpen()) - { - synchronized (this) - { - while (_exchange == null) - { - if (_endp.isBlocking()) - { - try - { - this.wait(); - } - catch (InterruptedException e) - { - throw new InterruptedIOException(); - } - } - else - { - long filled = _parser.fill(); - if (filled < 0) - { - close(); - } - else - { - // Hopefully just space? - _parser.skipCRLF(); - if (_parser.isMoreInBuffer()) - { - LOG.warn("Unexpected data received but no request sent"); - close(); - } - } - return this; - } - } - } - - try - { - if (_exchange.getStatus() == HttpExchange.STATUS_WAITING_FOR_COMMIT) - { - no_progress = 0; - commitRequest(); - } - - long io = 0; - _endp.flush(); - - if (_generator.isComplete()) - { - if (!_requestComplete) - { - _requestComplete = true; - _exchange.getEventListener().onRequestComplete(); - } - } - else - { - // Write as much of the request as possible - synchronized (this) - { - if (_exchange == null) - continue; - } - - long flushed = _generator.flushBuffer(); - io += flushed; - - if (!_generator.isComplete()) - { - if (_exchange!=null) - { - InputStream in = _exchange.getRequestContentSource(); - if (in != null) - { - if (_requestContentChunk == null || _requestContentChunk.length() == 0) - { - _requestContentChunk = _exchange.getRequestContentChunk(); - - if (_requestContentChunk != null) - _generator.addContent(_requestContentChunk,false); - else - _generator.complete(); - - flushed = _generator.flushBuffer(); - io += flushed; - } - } - else - _generator.complete(); - } - else - _generator.complete(); - } - } - - if (_generator.isComplete() && !_requestComplete) - { - _requestComplete = true; - _exchange.getEventListener().onRequestComplete(); - } - - // If we are not ended then parse available - if (!_parser.isComplete() && (_generator.isComplete() || _generator.isCommitted() && !_endp.isBlocking())) - { - long filled = _parser.parseAvailable(); - io += filled; - - if (_parser.isIdle() && (_endp.isInputShutdown() || !_endp.isOpen())) - throw new EofException(); - } - - if (io > 0) - no_progress = 0; - else if (no_progress++ >= 1 && !_endp.isBlocking()) - { - // SSL may need an extra flush as it may have made "no progress" while actually doing a handshake. - if (_endp instanceof SslSelectChannelEndPoint && !_generator.isComplete() && !_generator.isEmpty()) - { - long flushed = _generator.flushBuffer(); - if (flushed>0) - continue; - } - return this; - } - } - catch (Throwable e) - { - LOG.debug("Failure on " + _exchange, e); - - if (e instanceof ThreadDeath) - throw (ThreadDeath)e; - - failed = true; - - synchronized (this) - { - if (_exchange != null) - { - // Cancelling the exchange causes an exception as we close the connection, - // but we don't report it as it is normal cancelling operation - if (_exchange.getStatus() != HttpExchange.STATUS_CANCELLING && - _exchange.getStatus() != HttpExchange.STATUS_CANCELLED) - { - _exchange.setStatus(HttpExchange.STATUS_EXCEPTED); - _exchange.getEventListener().onException(e); - } - } - else - { - if (e instanceof IOException) - throw (IOException)e; - - if (e instanceof Error) - throw (Error)e; - - if (e instanceof RuntimeException) - throw (RuntimeException)e; - - throw new RuntimeException(e); - } - } - } - finally - { - boolean complete = false; - boolean close = failed; // always close the connection on error - if (!failed) - { - // are we complete? - if (_generator.isComplete()) - { - if (!_requestComplete) - { - _requestComplete = true; - _exchange.getEventListener().onRequestComplete(); - } - - // we need to return the HttpConnection to a state that - // it can be reused or closed out - if (_parser.isComplete()) - { - _exchange.cancelTimeout(_destination.getHttpClient()); - complete = true; - } - } - - // if the endpoint is closed, but the parser incomplete - if (!_endp.isOpen() && !(_parser.isComplete()||_parser.isIdle())) - { - // we wont be called again so let the parser see the close - complete=true; - _parser.parseAvailable(); - // TODO should not need this - if (!(_parser.isComplete()||_parser.isIdle())) - { - LOG.warn("Incomplete {} {}",_parser,_endp); - if (_exchange!=null && !_exchange.isDone()) - { - _exchange.setStatus(HttpExchange.STATUS_EXCEPTED); - _exchange.getEventListener().onException(new EOFException("Incomplete")); - } - } - } - } - - if (_endp.isInputShutdown() && !_parser.isComplete() && !_parser.isIdle()) - { - if (_exchange!=null && !_exchange.isDone()) - { - _exchange.setStatus(HttpExchange.STATUS_EXCEPTED); - _exchange.getEventListener().onException(new EOFException("Incomplete")); - } - _endp.close(); - } - - if (complete || failed) - { - synchronized (this) - { - if (!close) - close = shouldClose(); - - reset(true); - - no_progress = 0; - if (_exchange != null) - { - HttpExchange exchange=_exchange; - _exchange = null; - - // Reset the maxIdleTime because it may have been changed - if (!close) - _endp.setMaxIdleTime((int)_destination.getHttpClient().getIdleTimeout()); - - if (_status==HttpStatus.SWITCHING_PROTOCOLS_101) - { - Connection switched=exchange.onSwitchProtocol(_endp); - if (switched!=null) - { - // switched protocol! - exchange = _pipeline; - _pipeline = null; - if (exchange!=null) - _destination.send(exchange); - - return switched; - } - } - - if (_pipeline == null) - { - if (!isReserved()) - _destination.returnConnection(this, close); - } - else - { - if (close) - { - if (!isReserved()) - _destination.returnConnection(this,close); - - exchange = _pipeline; - _pipeline = null; - _destination.send(exchange); - } - else - { - exchange = _pipeline; - _pipeline = null; - send(exchange); - } - } - } - } - } - } - } - } - finally - { - _parser.returnBuffers(); - - // Do we have more stuff to write? - if (!_generator.isComplete() && _generator.getBytesBuffered()>0 && _endp.isOpen() && _endp instanceof AsyncEndPoint) - { - // Assume we are write blocked! - ((AsyncEndPoint)_endp).scheduleWrite(); - } - } - - return this; - } public boolean isIdle() { @@ -478,11 +163,11 @@ public class HttpConnection extends AbstractConnection implements Dumpable return false; } - public void closed() + public void onClose() { } - private void commitRequest() throws IOException + protected void commitRequest() throws IOException { synchronized (this) { @@ -558,30 +243,14 @@ public class HttpConnection extends AbstractConnection implements Dumpable } } - protected void reset(boolean returnBuffers) throws IOException + protected void reset() throws IOException { - _requestComplete = false; _connectionHeader = null; _parser.reset(); - if (returnBuffers) - _parser.returnBuffers(); - _generator.reset(returnBuffers); + _generator.reset(); _http11 = true; } - private boolean shouldClose() - { - if (_endp.isInputShutdown()) - return true; - if (_connectionHeader!=null) - { - if (HttpHeaderValues.CLOSE_BUFFER.equals(_connectionHeader)) - return true; - if (HttpHeaderValues.KEEP_ALIVE_BUFFER.equals(_connectionHeader)) - return false; - } - return !_http11; - } private class Handler extends HttpParser.EventHandler { @@ -599,28 +268,33 @@ public class HttpConnection extends AbstractConnection implements Dumpable public void startResponse(Buffer version, int status, Buffer reason) throws IOException { HttpExchange exchange = _exchange; - if (exchange!=null) + if (exchange==null) { - switch(status) - { - case HttpStatus.CONTINUE_100: - case HttpStatus.PROCESSING_102: - // TODO check if appropriate expect was sent in the request. - exchange.setEventListener(new NonFinalResponseListener(exchange)); - break; - - case HttpStatus.OK_200: - // handle special case for CONNECT 200 responses - if (HttpMethods.CONNECT.equalsIgnoreCase(exchange.getMethod())) - _parser.setHeadResponse(true); - break; - } - - _http11 = HttpVersions.HTTP_1_1_BUFFER.equals(version); - _status=status; - exchange.getEventListener().onResponseStatus(version,status,reason); - exchange.setStatus(HttpExchange.STATUS_PARSING_HEADERS); + LOG.warn("No exchange for response"); + _endp.close(); + return; } + + switch(status) + { + case HttpStatus.CONTINUE_100: + case HttpStatus.PROCESSING_102: + // TODO check if appropriate expect was sent in the request. + exchange.setEventListener(new NonFinalResponseListener(exchange)); + break; + + case HttpStatus.OK_200: + // handle special case for CONNECT 200 responses + if (HttpMethods.CONNECT.equalsIgnoreCase(exchange.getMethod())) + _parser.setHeadResponse(true); + break; + } + + _http11 = HttpVersions.HTTP_1_1_BUFFER.equals(version); + _status=status; + exchange.getEventListener().onResponseStatus(version,status,reason); + exchange.setStatus(HttpExchange.STATUS_PARSING_HEADERS); + } @Override @@ -640,8 +314,6 @@ public class HttpConnection extends AbstractConnection implements Dumpable @Override public void headerComplete() throws IOException { - if (_endp instanceof AsyncEndPoint) - ((AsyncEndPoint)_endp).scheduleIdle(); HttpExchange exchange = _exchange; if (exchange!=null) exchange.setStatus(HttpExchange.STATUS_PARSING_CONTENT); @@ -650,8 +322,6 @@ public class HttpConnection extends AbstractConnection implements Dumpable @Override public void content(Buffer ref) throws IOException { - if (_endp instanceof AsyncEndPoint) - ((AsyncEndPoint)_endp).scheduleIdle(); HttpExchange exchange = _exchange; if (exchange!=null) exchange.getEventListener().onResponseContent(ref); @@ -664,12 +334,33 @@ public class HttpConnection extends AbstractConnection implements Dumpable if (exchange!=null) exchange.setStatus(HttpExchange.STATUS_COMPLETED); } + + @Override + public void earlyEOF() + { + HttpExchange exchange = _exchange; + if (exchange!=null) + { + if (!exchange.isDone()) + { + if (exchange.setStatus(HttpExchange.STATUS_EXCEPTED)) + exchange.getEventListener().onException(new EofException("early EOF")); + } + } + } + + } @Override public String toString() { - return "HttpConnection@" + hashCode() + "//" + _destination.getAddress().getHost() + ":" + _destination.getAddress().getPort(); + return String.format("%s@%x//%s,g=%s,p=%s", + getClass().getSimpleName(), + hashCode(), + _destination == null ? "?.?.?.?:??" : _destination.getAddress(), + _generator, + _parser); } public String toDetailString() @@ -699,8 +390,8 @@ public class HttpConnection extends AbstractConnection implements Dumpable default: String exch= exchange.toString(); String reason = _endp.isOpen()?(_endp.isInputShutdown()?"half closed: ":"local close: "):"closed: "; - exchange.setStatus(HttpExchange.STATUS_EXCEPTED); - exchange.getEventListener().onException(new EOFException(reason+exch)); + if (exchange.setStatus(HttpExchange.STATUS_EXCEPTED)) + exchange.getEventListener().onException(new EofException(reason+exch)); } } @@ -784,7 +475,7 @@ public class HttpConnection extends AbstractConnection implements Dumpable // Connection idle, close it if (_idle.compareAndSet(true, false)) { - _destination.returnIdleConnection(HttpConnection.this); + _destination.returnIdleConnection(AbstractHttpConnection.this); } } } diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/Address.java b/jetty-client/src/main/java/org/eclipse/jetty/client/Address.java index f6a84649bb7..447b0628c46 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/Address.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/Address.java @@ -43,6 +43,9 @@ public class Address public Address(String host, int port) { + if (host == null) + throw new IllegalArgumentException("Host is null"); + this.host = host.trim(); this.port = port; } diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/AsyncHttpConnection.java b/jetty-client/src/main/java/org/eclipse/jetty/client/AsyncHttpConnection.java new file mode 100644 index 00000000000..455edfbeb92 --- /dev/null +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/AsyncHttpConnection.java @@ -0,0 +1,260 @@ +// ======================================================================== +// Copyright (c) 2006-2011 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.client; + +import java.io.IOException; + +import org.eclipse.jetty.http.AbstractGenerator; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.io.AsyncEndPoint; +import org.eclipse.jetty.io.Buffer; +import org.eclipse.jetty.io.Buffers; +import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.nio.AsyncConnection; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; + + + +/* ------------------------------------------------------------ */ +/** Asynchronous Client HTTP Connection + */ +public class AsyncHttpConnection extends AbstractHttpConnection implements AsyncConnection +{ + private static final Logger LOG = Log.getLogger(AsyncHttpConnection.class); + + private boolean _requestComplete; + private Buffer _requestContentChunk; + private final AsyncEndPoint _asyncEndp; + + AsyncHttpConnection(Buffers requestBuffers, Buffers responseBuffers, EndPoint endp) + { + super(requestBuffers,responseBuffers,endp); + _asyncEndp=(AsyncEndPoint)endp; + } + + protected void reset() throws IOException + { + _requestComplete = false; + super.reset(); + } + + public Connection handle() throws IOException + { + Connection connection = this; + boolean progress=true; + + try + { + boolean failed = false; + + // While we are making progress and have not changed connection + while (progress && connection==this) + { + LOG.debug("while open={} more={} progress={}",_endp.isOpen(),_parser.isMoreInBuffer(),progress); + + progress=false; + HttpExchange exchange=_exchange; + + LOG.debug("exchange {} on {}",exchange,this); + + try + { + // Should we commit the request? + if (!_generator.isCommitted() && exchange!=null && exchange.getStatus() == HttpExchange.STATUS_WAITING_FOR_COMMIT) + { + LOG.debug("commit {}",exchange); + progress=true; + commitRequest(); + } + + // Generate output + if (_generator.isCommitted() && !_generator.isComplete()) + { + if (_generator.flushBuffer()>0) + { + LOG.debug("flushed"); + progress=true; + } + + // Is there more content to send or should we complete the generator + if (_generator.isState(AbstractGenerator.STATE_CONTENT)) + { + // Look for more content to send. + if (_requestContentChunk==null) + _requestContentChunk = exchange.getRequestContentChunk(null); + + if (_requestContentChunk==null) + { + LOG.debug("complete {}",exchange); + progress=true; + _generator.complete(); + } + else if (_generator.isEmpty()) + { + LOG.debug("addChunk"); + progress=true; + Buffer chunk=_requestContentChunk; + _requestContentChunk=exchange.getRequestContentChunk(null); + _generator.addContent(chunk,_requestContentChunk==null); + } + } + } + + // Signal request completion + if (_generator.isComplete() && !_requestComplete) + { + LOG.debug("requestComplete {}",exchange); + progress=true; + _requestComplete = true; + exchange.getEventListener().onRequestComplete(); + } + + // Read any input that is available + if (!_parser.isComplete() && _parser.parseAvailable()) + { + LOG.debug("parsed {}",exchange); + progress=true; + } + + // Flush output + _endp.flush(); + + // Has any IO been done by the endpoint itself since last loop + if (_asyncEndp.hasProgressed()) + { + LOG.debug("hasProgressed {}",exchange); + progress=true; + } + } + catch (Throwable e) + { + LOG.debug("Failure on " + _exchange, e); + + failed = true; + + synchronized (this) + { + if (exchange != null) + { + // Cancelling the exchange causes an exception as we close the connection, + // but we don't report it as it is normal cancelling operation + if (exchange.getStatus() != HttpExchange.STATUS_CANCELLING && + exchange.getStatus() != HttpExchange.STATUS_CANCELLED && + !exchange.isDone()) + { + if (exchange.setStatus(HttpExchange.STATUS_EXCEPTED)) + exchange.getEventListener().onException(e); + } + } + else + { + if (e instanceof IOException) + throw (IOException)e; + if (e instanceof Error) + throw (Error)e; + if (e instanceof RuntimeException) + throw (RuntimeException)e; + throw new RuntimeException(e); + } + } + } + finally + { + LOG.debug("finally {} on {} progress={} {}",exchange,this,progress,_endp); + + boolean complete = failed || _generator.isComplete() && _parser.isComplete(); + + if (complete) + { + boolean persistent = !failed && _parser.isPersistent() && _generator.isPersistent(); + _generator.setPersistent(persistent); + reset(); + if (persistent) + _endp.setMaxIdleTime((int)_destination.getHttpClient().getIdleTimeout()); + + synchronized (this) + { + exchange=_exchange; + _exchange = null; + + // Cancel the exchange + if (exchange!=null) + { + exchange.cancelTimeout(_destination.getHttpClient()); + + // TODO should we check the exchange is done? + } + + // handle switched protocols + if (_status==HttpStatus.SWITCHING_PROTOCOLS_101) + { + Connection switched=exchange.onSwitchProtocol(_endp); + if (switched!=null) + connection=switched; + { + // switched protocol! + _pipeline = null; + if (_pipeline!=null) + _destination.send(_pipeline); + _pipeline = null; + + connection=switched; + } + } + + // handle pipelined requests + if (_pipeline!=null) + { + if (!persistent || connection!=this) + _destination.send(_pipeline); + else + _exchange=_pipeline; + _pipeline=null; + } + + if (_exchange==null && !isReserved()) // TODO how do we return switched connections? + _destination.returnConnection(this, !persistent); + } + + } + } + } + } + finally + { + _parser.returnBuffers(); + _generator.returnBuffers(); + LOG.debug("unhandle {} on {}",_exchange,_endp); + } + + return connection; + } + + public void onInputShutdown() throws IOException + { + if (_generator.isIdle()) + _endp.shutdownOutput(); + } + + @Override + public boolean send(HttpExchange ex) throws IOException + { + boolean sent=super.send(ex); + if (sent) + _asyncEndp.asyncDispatch(); + return sent; + } +} diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/BlockingHttpConnection.java b/jetty-client/src/main/java/org/eclipse/jetty/client/BlockingHttpConnection.java new file mode 100644 index 00000000000..d241699bdac --- /dev/null +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/BlockingHttpConnection.java @@ -0,0 +1,257 @@ +// ======================================================================== +// Copyright (c) 2006-2011 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.client; + +import java.io.IOException; +import java.io.InterruptedIOException; + +import org.eclipse.jetty.http.AbstractGenerator; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.io.Buffer; +import org.eclipse.jetty.io.Buffers; +import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; + + +/* ------------------------------------------------------------ */ +/** Blocking HTTP Connection + */ +public class BlockingHttpConnection extends AbstractHttpConnection +{ + private static final Logger LOG = Log.getLogger(BlockingHttpConnection.class); + + private boolean _requestComplete; + private Buffer _requestContentChunk; + + BlockingHttpConnection(Buffers requestBuffers, Buffers responseBuffers, EndPoint endPoint) + { + super(requestBuffers, responseBuffers, endPoint); + } + + protected void reset() throws IOException + { + _requestComplete = false; + super.reset(); + } + + @Override + public Connection handle() throws IOException + { + Connection connection = this; + + try + { + boolean failed = false; + + + // While we are making progress and have not changed connection + while (_endp.isOpen() && connection==this) + { + LOG.debug("open={} more={}",_endp.isOpen(),_parser.isMoreInBuffer()); + + HttpExchange exchange; + synchronized (this) + { + exchange=_exchange; + + while (exchange == null) + { + try + { + this.wait(); + exchange=_exchange; + } + catch (InterruptedException e) + { + throw new InterruptedIOException(); + } + } + } + LOG.debug("exchange {}",exchange); + + try + { + // Should we commit the request? + if (!_generator.isCommitted() && exchange!=null && exchange.getStatus() == HttpExchange.STATUS_WAITING_FOR_COMMIT) + { + LOG.debug("commit"); + commitRequest(); + } + + // Generate output + while (_generator.isCommitted() && !_generator.isComplete()) + { + if (_generator.flushBuffer()>0) + { + LOG.debug("flushed"); + } + + // Is there more content to send or should we complete the generator + if (_generator.isState(AbstractGenerator.STATE_CONTENT)) + { + // Look for more content to send. + if (_requestContentChunk==null) + _requestContentChunk = exchange.getRequestContentChunk(null); + + if (_requestContentChunk==null) + { + LOG.debug("complete"); + _generator.complete(); + } + else if (_generator.isEmpty()) + { + LOG.debug("addChunk"); + Buffer chunk=_requestContentChunk; + _requestContentChunk=exchange.getRequestContentChunk(null); + _generator.addContent(chunk,_requestContentChunk==null); + } + } + } + + // Signal request completion + if (_generator.isComplete() && !_requestComplete) + { + LOG.debug("requestComplete"); + _requestComplete = true; + exchange.getEventListener().onRequestComplete(); + } + + // Read any input that is available + if (!_parser.isComplete() && _parser.parseAvailable()) + { + LOG.debug("parsed"); + } + + // Flush output + _endp.flush(); + } + catch (Throwable e) + { + LOG.debug("Failure on " + _exchange, e); + + failed = true; + + synchronized (this) + { + if (exchange != null) + { + // Cancelling the exchange causes an exception as we close the connection, + // but we don't report it as it is normal cancelling operation + if (exchange.getStatus() != HttpExchange.STATUS_CANCELLING && + exchange.getStatus() != HttpExchange.STATUS_CANCELLED && + !exchange.isDone()) + { + if(exchange.setStatus(HttpExchange.STATUS_EXCEPTED)) + exchange.getEventListener().onException(e); + } + } + else + { + if (e instanceof IOException) + throw (IOException)e; + if (e instanceof Error) + throw (Error)e; + if (e instanceof RuntimeException) + throw (RuntimeException)e; + throw new RuntimeException(e); + } + } + } + finally + { + LOG.debug("{} {}",_generator, _parser); + LOG.debug("{}",_endp); + + boolean complete = failed || _generator.isComplete() && _parser.isComplete(); + + if (complete) + { + boolean persistent = !failed && _parser.isPersistent() && _generator.isPersistent(); + _generator.setPersistent(persistent); + reset(); + if (persistent) + _endp.setMaxIdleTime((int)_destination.getHttpClient().getIdleTimeout()); + + synchronized (this) + { + exchange=_exchange; + _exchange = null; + + // Cancel the exchange + if (exchange!=null) + { + exchange.cancelTimeout(_destination.getHttpClient()); + + // TODO should we check the exchange is done? + } + + // handle switched protocols + if (_status==HttpStatus.SWITCHING_PROTOCOLS_101) + { + Connection switched=exchange.onSwitchProtocol(_endp); + if (switched!=null) + connection=switched; + { + // switched protocol! + _pipeline = null; + if (_pipeline!=null) + _destination.send(_pipeline); + _pipeline = null; + + connection=switched; + } + } + + // handle pipelined requests + if (_pipeline!=null) + { + if (!persistent || connection!=this) + _destination.send(_pipeline); + else + _exchange=_pipeline; + _pipeline=null; + } + + if (_exchange==null && !isReserved()) // TODO how do we return switched connections? + _destination.returnConnection(this, !persistent); + } + } + } + } + } + finally + { + _parser.returnBuffers(); + _generator.returnBuffers(); + } + + return connection; + } + + @Override + public boolean send(HttpExchange ex) throws IOException + { + boolean sent=super.send(ex); + if (sent) + { + synchronized (this) + { + notifyAll(); + } + } + return sent; + } +} diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java index dd75edeb743..21e8b0f89e9 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java @@ -16,6 +16,7 @@ package org.eclipse.jetty.client; import java.io.IOException; import java.io.InputStream; import java.net.UnknownHostException; +import java.util.Arrays; import java.util.Enumeration; import java.util.LinkedList; import java.util.Set; @@ -29,13 +30,13 @@ import org.eclipse.jetty.client.security.RealmResolver; import org.eclipse.jetty.client.security.SecurityListener; import org.eclipse.jetty.http.HttpBuffers; import org.eclipse.jetty.http.HttpSchemes; -import org.eclipse.jetty.http.ssl.SslContextFactory; import org.eclipse.jetty.io.Buffers.Type; import org.eclipse.jetty.util.Attributes; import org.eclipse.jetty.util.AttributesMap; import org.eclipse.jetty.util.component.AggregateLifeCycle; import org.eclipse.jetty.util.component.Dumpable; import org.eclipse.jetty.util.component.LifeCycle; +import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.util.thread.ThreadPool; import org.eclipse.jetty.util.thread.Timeout; @@ -52,11 +53,11 @@ import org.eclipse.jetty.util.thread.Timeout; * The an instance of {@link HttpExchange} is passed to the {@link #send(HttpExchange)} method * to send a request. The exchange contains both the headers and content (source) of the request * plus the callbacks to handle responses. A HttpClient can have many exchanges outstanding - * and they may be queued on the {@link HttpDestination} waiting for a {@link HttpConnection}, - * queued in the {@link HttpConnection} waiting to be transmitted or pipelined on the actual + * and they may be queued on the {@link HttpDestination} waiting for a {@link AbstractHttpConnection}, + * queued in the {@link AbstractHttpConnection} waiting to be transmitted or pipelined on the actual * TCP/IP connection waiting for a response. *

- * The {@link HttpDestination} class is an aggregation of {@link HttpConnection}s for the + * The {@link HttpDestination} class is an aggregation of {@link AbstractHttpConnection}s for the * same host, port and protocol. A destination may limit the number of connections * open and they provide a pool of open connections that may be reused. Connections may also * be allocated from a destination, so that multiple request sources are not multiplexed @@ -164,7 +165,7 @@ public class HttpClient extends HttpBuffers implements Attributes, Dumpable public void dump(Appendable out, String indent) throws IOException { out.append(String.valueOf(this)).append("\n"); - AggregateLifeCycle.dump(out,indent,_destinations.values()); + AggregateLifeCycle.dump(out,indent,Arrays.asList(_threadPool,_connector),_destinations.values()); } /* ------------------------------------------------------------------------------- */ @@ -190,7 +191,7 @@ public class HttpClient extends HttpBuffers implements Attributes, Dumpable pool.setName("HttpClient"); _threadPool = pool; } - + return _threadPool; } @@ -526,7 +527,7 @@ public class HttpClient extends HttpBuffers implements Attributes, Dumpable /* ------------------------------------------------------------ */ /** - * @return the period in milliseconds a {@link HttpConnection} can be idle for before it is closed. + * @return the period in milliseconds a {@link AbstractHttpConnection} can be idle for before it is closed. */ public long getIdleTimeout() { @@ -535,7 +536,7 @@ public class HttpClient extends HttpBuffers implements Attributes, Dumpable /* ------------------------------------------------------------ */ /** - * @param ms the period in milliseconds a {@link HttpConnection} can be idle for before it is closed. + * @param ms the period in milliseconds a {@link AbstractHttpConnection} can be idle for before it is closed. */ public void setIdleTimeout(long ms) { diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java index 9d7723b2b1b..18a197ced6a 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java @@ -15,7 +15,7 @@ package org.eclipse.jetty.client; import java.io.IOException; import java.lang.reflect.Constructor; -import java.net.ConnectException; +import java.net.ProtocolException; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; @@ -48,9 +48,9 @@ public class HttpDestination implements Dumpable private static final Logger LOG = Log.getLogger(HttpDestination.class); private final List _queue = new LinkedList(); - private final List _connections = new LinkedList(); + private final List _connections = new LinkedList(); private final BlockingQueue _newQueue = new ArrayBlockingQueue(10, true); - private final List _idle = new ArrayList(); + private final List _idle = new ArrayList(); private final HttpClient _client; private final Address _address; private final boolean _ssl; @@ -65,7 +65,7 @@ public class HttpDestination implements Dumpable private List _cookies; - + HttpDestination(HttpClient client, Address address, boolean ssl) { _client = client; @@ -168,9 +168,9 @@ public class HttpDestination implements Dumpable * @return a HttpConnection for this destination * @throws IOException if an I/O error occurs */ - private HttpConnection getConnection(long timeout) throws IOException + private AbstractHttpConnection getConnection(long timeout) throws IOException { - HttpConnection connection = null; + AbstractHttpConnection connection = null; while ((connection == null) && (connection = getIdleConnection()) == null && timeout > 0) { @@ -191,9 +191,9 @@ public class HttpDestination implements Dumpable try { Object o = _newQueue.take(); - if (o instanceof HttpConnection) + if (o instanceof AbstractHttpConnection) { - connection = (HttpConnection)o; + connection = (AbstractHttpConnection)o; } else throw (IOException)o; @@ -220,17 +220,17 @@ public class HttpDestination implements Dumpable return connection; } - public HttpConnection reserveConnection(long timeout) throws IOException + public AbstractHttpConnection reserveConnection(long timeout) throws IOException { - HttpConnection connection = getConnection(timeout); + AbstractHttpConnection connection = getConnection(timeout); if (connection != null) connection.setReserved(true); return connection; } - public HttpConnection getIdleConnection() throws IOException + public AbstractHttpConnection getIdleConnection() throws IOException { - HttpConnection connection = null; + AbstractHttpConnection connection = null; while (true) { synchronized (this) @@ -246,12 +246,16 @@ public class HttpDestination implements Dumpable } if (connection == null) + { return null; - + } + // Check if the connection was idle, // but it expired just a moment ago if (connection.cancelIdleTimeout()) + { return connection; + } } } @@ -290,8 +294,8 @@ public class HttpDestination implements Dumpable else if (_queue.size() > 0) { HttpExchange ex = _queue.remove(0); - ex.setStatus(HttpExchange.STATUS_EXCEPTED); - ex.getEventListener().onConnectionFailed(throwable); + if (ex.setStatus(HttpExchange.STATUS_EXCEPTED)) + ex.getEventListener().onConnectionFailed(throwable); // Since an existing connection had failed, we need to create a // connection if the queue is not empty and client is running. @@ -324,13 +328,13 @@ public class HttpDestination implements Dumpable if (_queue.size() > 0) { HttpExchange ex = _queue.remove(0); - ex.setStatus(HttpExchange.STATUS_EXCEPTED); - ex.getEventListener().onException(throwable); + if(ex.setStatus(HttpExchange.STATUS_EXCEPTED)) + ex.getEventListener().onException(throwable); } } } - public void onNewConnection(final HttpConnection connection) throws IOException + public void onNewConnection(final AbstractHttpConnection connection) throws IOException { Connection q_connection = null; @@ -352,9 +356,9 @@ public class HttpDestination implements Dumpable else { EndPoint endPoint = connection.getEndPoint(); - if (isProxied() && endPoint instanceof SelectConnector.ProxySelectChannelEndPoint) + if (isProxied() && endPoint instanceof SelectConnector.UpgradableEndPoint) { - SelectConnector.ProxySelectChannelEndPoint proxyEndPoint = (SelectConnector.ProxySelectChannelEndPoint)endPoint; + SelectConnector.UpgradableEndPoint proxyEndPoint = (SelectConnector.UpgradableEndPoint)endPoint; HttpExchange exchange = _queue.get(0); ConnectExchange connect = new ConnectExchange(getAddress(), proxyEndPoint, exchange); connect.setAddress(getProxy()); @@ -381,7 +385,7 @@ public class HttpDestination implements Dumpable } } - public void returnConnection(HttpConnection connection, boolean close) throws IOException + public void returnConnection(AbstractHttpConnection connection, boolean close) throws IOException { if (connection.isReserved()) connection.setReserved(false); @@ -433,16 +437,11 @@ public class HttpDestination implements Dumpable } } - public void returnIdleConnection(HttpConnection connection) + public void returnIdleConnection(AbstractHttpConnection connection) { - try - { - connection.close(); - } - catch (IOException e) - { - LOG.ignore(e); - } + // TODO work out the real idle time; + long idleForMs=connection!=null&&connection.getEndPoint()!=null?connection.getEndPoint().getMaxIdleTime():-1; + connection.onIdleExpired(idleForMs); boolean startConnection = false; synchronized (this) @@ -524,7 +523,7 @@ public class HttpDestination implements Dumpable // Add any known authorizations if (_authorizations != null) { - Authentication auth = (Authentication)_authorizations.match(ex.getURI()); + Authentication auth = (Authentication)_authorizations.match(ex.getRequestURI()); if (auth != null) (auth).setCredentials(ex); } @@ -533,7 +532,7 @@ public class HttpDestination implements Dumpable // so that we count also the queue time in the timeout ex.scheduleTimeout(this); - HttpConnection connection = getIdleConnection(); + AbstractHttpConnection connection = getIdleConnection(); if (connection != null) { send(connection, ex); @@ -566,7 +565,7 @@ public class HttpDestination implements Dumpable } } - protected void send(HttpConnection connection, HttpExchange exchange) throws IOException + protected void send(AbstractHttpConnection connection, HttpExchange exchange) throws IOException { synchronized (this) { @@ -594,7 +593,7 @@ public class HttpDestination implements Dumpable b.append('\n'); synchronized (this) { - for (HttpConnection connection : _connections) + for (AbstractHttpConnection connection : _connections) { b.append(connection.toDetailString()); if (_idle.contains(connection)) @@ -637,7 +636,7 @@ public class HttpDestination implements Dumpable { synchronized (this) { - for (HttpConnection connection : _connections) + for (AbstractHttpConnection connection : _connections) { connection.close(); } @@ -665,13 +664,13 @@ public class HttpDestination implements Dumpable AggregateLifeCycle.dump(out,indent,_connections); } } - + private class ConnectExchange extends ContentExchange { - private final SelectConnector.ProxySelectChannelEndPoint proxyEndPoint; + private final SelectConnector.UpgradableEndPoint proxyEndPoint; private final HttpExchange exchange; - public ConnectExchange(Address serverAddress, SelectConnector.ProxySelectChannelEndPoint proxyEndPoint, HttpExchange exchange) + public ConnectExchange(Address serverAddress, SelectConnector.UpgradableEndPoint proxyEndPoint, HttpExchange exchange) { this.proxyEndPoint = proxyEndPoint; this.exchange = exchange; @@ -687,13 +686,18 @@ public class HttpDestination implements Dumpable @Override protected void onResponseComplete() throws IOException { - if (getResponseStatus() == HttpStatus.OK_200) + int responseStatus = getResponseStatus(); + if (responseStatus == HttpStatus.OK_200) { proxyEndPoint.upgrade(); } + else if(responseStatus == HttpStatus.GATEWAY_TIMEOUT_504) + { + onExpire(); + } else { - onConnectionFailed(new ConnectException(exchange.getAddress().toString())); + onException(new ProtocolException("Proxy: " + proxyEndPoint.getRemoteAddr() +":" + proxyEndPoint.getRemotePort() + " didn't return http return code 200, but " + responseStatus + " while trying to request: " + exchange.getAddress().toString())); } } @@ -702,5 +706,22 @@ public class HttpDestination implements Dumpable { HttpDestination.this.onConnectionFailed(x); } + + @Override + protected void onException(Throwable x) + { + _queue.remove(exchange); + if (exchange.setStatus(STATUS_EXCEPTED)) + exchange.getEventListener().onException(x); + } + + @Override + protected void onExpire() + { + _queue.remove(exchange); + if (exchange.setStatus(STATUS_EXPIRED)) + exchange.getEventListener().onExpire(); + } + } } diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpExchange.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpExchange.java index 6840adcabd2..27b28aa0cc8 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpExchange.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpExchange.java @@ -59,7 +59,7 @@ import org.eclipse.jetty.util.thread.Timeout; * *

* Typically the HttpExchange is passed to the {@link HttpClient#send(HttpExchange)} method, which in turn selects a {@link HttpDestination} and calls its - * {@link HttpDestination#send(HttpExchange)}, which then creates or selects a {@link HttpConnection} and calls its {@link HttpConnection#send(HttpExchange)}. A + * {@link HttpDestination#send(HttpExchange)}, which then creates or selects a {@link AbstractHttpConnection} and calls its {@link AbstractHttpConnection#send(HttpExchange)}. A * developer may wish to directly call send on the destination or connection if they wish to bypass some handling provided (eg Cookie handling in the * HttpDestination). *

@@ -72,7 +72,7 @@ import org.eclipse.jetty.util.thread.Timeout; */ public class HttpExchange { - private static final Logger LOG = Log.getLogger(HttpExchange.class); + static final Logger LOG = Log.getLogger(HttpExchange.class); public static final int STATUS_START = 0; public static final int STATUS_WAITING_FOR_CONNECTION = 1; @@ -98,12 +98,11 @@ public class HttpExchange private InputStream _requestContentSource; private AtomicInteger _status = new AtomicInteger(STATUS_START); - private Buffer _requestContentChunk; private boolean _retryStatus = false; // controls if the exchange will have listeners autoconfigured by the destination private boolean _configureListeners = true; private HttpEventListener _listener = new Listener(); - private volatile HttpConnection _connection; + private volatile AbstractHttpConnection _connection; private Address _localAddress = null; @@ -123,9 +122,8 @@ public class HttpExchange { if (getStatus() < HttpExchange.STATUS_COMPLETED) setStatus(HttpExchange.STATUS_EXPIRED); - destination.exchangeExpired(this); - HttpConnection connection = _connection; + AbstractHttpConnection connection = _connection; if (connection != null) connection.exchangeExpired(this); } @@ -183,12 +181,18 @@ public class HttpExchange } } - void setStatus(int newStatus) + /* ------------------------------------------------------------ */ + /** + * @param newStatus + * @return True if the status was actually set. + */ + boolean setStatus(int newStatus) { + boolean set = false; try { int oldStatus = _status.get(); - boolean set = false; + boolean ignored = false; if (oldStatus != newStatus) { long now = System.currentTimeMillis(); @@ -198,7 +202,7 @@ public class HttpExchange if (newStatus==STATUS_SENDING_REQUEST) _sent=_lastStateChange; } - + // State machine: from which old status you can go into which new status switch (oldStatus) { @@ -315,7 +319,7 @@ public class HttpExchange case STATUS_CANCELLING: case STATUS_EXPIRED: // Don't change the status, it's too late - set = true; + ignored = true; break; } break; @@ -329,7 +333,7 @@ public class HttpExchange break; default: // Ignore other statuses, we're cancelling - set = true; + ignored = true; break; } break; @@ -341,8 +345,14 @@ public class HttpExchange case STATUS_START: set = _status.compareAndSet(oldStatus,newStatus); break; + + case STATUS_COMPLETED: + ignored = true; + done(); + break; + default: - set = true; + ignored = true; break; } break; @@ -351,13 +361,15 @@ public class HttpExchange throw new AssertionError(oldStatus + " => " + newStatus); } - if (!set) + if (!set && !ignored) throw new IllegalStateException(toState(oldStatus) + " => " + toState(newStatus)); + LOG.debug("setStatus {} {}",newStatus,this); } catch (IOException x) { LOG.warn(x); } + return set; } private boolean setStatusExpired(int newStatus, int oldStatus) @@ -593,7 +605,8 @@ public class HttpExchange if (uri.isOpaque()) throw new IllegalArgumentException("Opaque URI: " + uri); - LOG.debug("URI = {}",uri.toASCIIString()); + if (LOG.isDebugEnabled()) + LOG.debug("URI = {}",uri.toASCIIString()); String scheme = uri.getScheme(); int port = uri.getPort(); @@ -705,25 +718,22 @@ public class HttpExchange return _requestContentSource; } - public Buffer getRequestContentChunk() throws IOException + public Buffer getRequestContentChunk(Buffer buffer) throws IOException { synchronized (this) { - if (_requestContentChunk == null) - _requestContentChunk = new ByteArrayBuffer(4096); // TODO configure - else + if (_requestContentSource!=null) { - if (_requestContentChunk.hasContent()) - throw new IllegalStateException(); - _requestContentChunk.clear(); - } + if (buffer == null) + buffer = new ByteArrayBuffer(8192); // TODO configure - int read = _requestContentChunk.capacity(); - int length = _requestContentSource.read(_requestContentChunk.array(),0,read); - if (length >= 0) - { - _requestContentChunk.setPutIndex(length); - return _requestContentChunk; + int space = buffer.space(); + int length = _requestContentSource.read(buffer.array(),buffer.putIndex(),space); + if (length >= 0) + { + buffer.setPutIndex(buffer.putIndex()+length); + return buffer; + } } return null; } @@ -778,7 +788,7 @@ public class HttpExchange private void abort() { - HttpConnection httpConnection = _connection; + AbstractHttpConnection httpConnection = _connection; if (httpConnection != null) { try @@ -798,7 +808,7 @@ public class HttpExchange } } - void associate(HttpConnection connection) + void associate(AbstractHttpConnection connection) { if (connection.getEndPoint().getLocalHost() != null) _localAddress = new Address(connection.getEndPoint().getLocalHost(),connection.getEndPoint().getLocalPort()); @@ -813,9 +823,9 @@ public class HttpExchange return this._connection != null; } - HttpConnection disassociate() + AbstractHttpConnection disassociate() { - HttpConnection result = _connection; + AbstractHttpConnection result = _connection; this._connection = null; if (getStatus() == STATUS_CANCELLING) setStatus(STATUS_CANCELLED); diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/SelectConnector.java b/jetty-client/src/main/java/org/eclipse/jetty/client/SelectConnector.java index 853e7e70f4d..b3c93a7dee8 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/SelectConnector.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/SelectConnector.java @@ -15,43 +15,39 @@ package org.eclipse.jetty.client; import java.io.IOException; import java.net.SocketTimeoutException; -import java.net.UnknownHostException; import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; import java.nio.channels.UnresolvedAddressException; +import java.util.Arrays; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLSession; -import org.eclipse.jetty.http.HttpGenerator; -import org.eclipse.jetty.http.HttpParser; -import org.eclipse.jetty.http.ssl.SslContextFactory; +import org.eclipse.jetty.io.AsyncEndPoint; import org.eclipse.jetty.io.Buffer; -import org.eclipse.jetty.io.Buffers; -import org.eclipse.jetty.io.Buffers.Type; -import org.eclipse.jetty.io.BuffersFactory; import org.eclipse.jetty.io.ConnectedEndPoint; import org.eclipse.jetty.io.Connection; -import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.nio.AsyncConnection; import org.eclipse.jetty.io.nio.SelectChannelEndPoint; import org.eclipse.jetty.io.nio.SelectorManager; -import org.eclipse.jetty.io.nio.SslSelectChannelEndPoint; +import org.eclipse.jetty.io.nio.SslConnection; import org.eclipse.jetty.util.component.AbstractLifeCycle; +import org.eclipse.jetty.util.component.AggregateLifeCycle; +import org.eclipse.jetty.util.component.Dumpable; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.Timeout; +import org.eclipse.jetty.util.thread.Timeout.Task; -class SelectConnector extends AbstractLifeCycle implements HttpClient.Connector +class SelectConnector extends AbstractLifeCycle implements HttpClient.Connector, Dumpable { private static final Logger LOG = Log.getLogger(SelectConnector.class); private final HttpClient _httpClient; private final Manager _selectorManager=new Manager(); private final Map _connectingChannels = new ConcurrentHashMap(); - private Buffers _sslBuffers; /** * @param httpClient the HttpClient this connector is associated to @@ -67,16 +63,6 @@ class SelectConnector extends AbstractLifeCycle implements HttpClient.Connector { super.doStart(); - - final boolean direct=_httpClient.getUseDirectBuffers(); - - SSLEngine sslEngine=_selectorManager.newSslEngine(null); - final SSLSession ssl_session=sslEngine.getSession(); - _sslBuffers = BuffersFactory.newBuffers( - direct?Type.DIRECT:Type.INDIRECT,ssl_session.getApplicationBufferSize(), - direct?Type.DIRECT:Type.INDIRECT,ssl_session.getApplicationBufferSize(), - direct?Type.DIRECT:Type.INDIRECT,1024); - _selectorManager.start(); } @@ -87,6 +73,17 @@ class SelectConnector extends AbstractLifeCycle implements HttpClient.Connector _selectorManager.stop(); } + public String dump() + { + return AggregateLifeCycle.dump(this); + } + + public void dump(Appendable out, String indent) throws IOException + { + out.append(String.valueOf(this)).append("\n"); + AggregateLifeCycle.dump(out, indent, Arrays.asList(_selectorManager)); + } + /* ------------------------------------------------------------ */ public void startConnection( HttpDestination destination ) throws IOException @@ -107,7 +104,7 @@ class SelectConnector extends AbstractLifeCycle implements HttpClient.Connector else { channel.configureBlocking(false); - channel.connect(address.toSocketAddress()); + channel.connect(address.toSocketAddress()); _selectorManager.register(channel,destination); ConnectTimeout connectTimeout = new ConnectTimeout(channel,destination); _httpClient.schedule(connectTimeout,_httpClient.getConnectTimeout()); @@ -131,6 +128,8 @@ class SelectConnector extends AbstractLifeCycle implements HttpClient.Connector /* ------------------------------------------------------------ */ class Manager extends SelectorManager { + Logger LOG = SelectConnector.LOG; + @Override public boolean dispatch(Runnable task) { @@ -153,12 +152,9 @@ class SelectConnector extends AbstractLifeCycle implements HttpClient.Connector } @Override - protected Connection newConnection(SocketChannel channel, SelectChannelEndPoint endpoint) + public AsyncConnection newConnection(SocketChannel channel, AsyncEndPoint endpoint, Object attachment) { - if (endpoint instanceof SslSelectChannelEndPoint) - return new HttpConnection(_sslBuffers,_sslBuffers,endpoint); - - return new HttpConnection(_httpClient.getRequestBuffers(),_httpClient.getResponseBuffers(),endpoint); + return new AsyncHttpConnection(_httpClient.getRequestBuffers(),_httpClient.getResponseBuffers(),endpoint); } @Override @@ -168,37 +164,33 @@ class SelectConnector extends AbstractLifeCycle implements HttpClient.Connector Timeout.Task connectTimeout = _connectingChannels.remove(channel); if (connectTimeout != null) connectTimeout.cancel(); - LOG.debug("Channels with connection pending: {}", _connectingChannels.size()); + if (LOG.isDebugEnabled()) + LOG.debug("Channels with connection pending: {}", _connectingChannels.size()); // key should have destination at this point (will be replaced by endpoint after this call) HttpDestination dest=(HttpDestination)key.attachment(); - SelectChannelEndPoint ep=null; + SelectChannelEndPoint scep = new SelectChannelEndPoint(channel, selectSet, key, (int)_httpClient.getIdleTimeout()); + AsyncEndPoint ep = scep; if (dest.isSecure()) { - if (dest.isProxied()) - { - SSLEngine engine=newSslEngine(channel); - ep = new ProxySelectChannelEndPoint(channel, selectSet, key, _sslBuffers, engine, (int)_httpClient.getIdleTimeout()); - } - else - { - SSLEngine engine=newSslEngine(channel); - SslSelectChannelEndPoint sslEp = new SslSelectChannelEndPoint(_sslBuffers, channel, selectSet, key, engine, (int)_httpClient.getIdleTimeout()); - sslEp.setAllowRenegotiate(_httpClient.getSslContextFactory().isAllowRenegotiate()); - ep = sslEp; - } - } - else - { - ep = new SelectChannelEndPoint(channel, selectSet, key, (int)_httpClient.getIdleTimeout()); + LOG.debug("secure to {}, proxied={}",channel,dest.isProxied()); + ep = new UpgradableEndPoint(ep,newSslEngine(channel)); } - HttpConnection connection=(HttpConnection)ep.getConnection(); - connection.setDestination(dest); - dest.onNewConnection(connection); - return ep; + AsyncConnection connection = selectSet.getManager().newConnection(channel,ep, key.attachment()); + ep.setConnection(connection); + + AbstractHttpConnection httpConnection=(AbstractHttpConnection)connection; + httpConnection.setDestination(dest); + + if (dest.isSecure() && !dest.isProxied()) + ((UpgradableEndPoint)ep).upgrade(); + + dest.onNewConnection(httpConnection); + + return scep; } private synchronized SSLEngine newSslEngine(SocketChannel channel) throws IOException @@ -231,7 +223,7 @@ class SelectConnector extends AbstractLifeCycle implements HttpClient.Connector Timeout.Task connectTimeout = _connectingChannels.remove(channel); if (connectTimeout != null) connectTimeout.cancel(); - + if (attachment instanceof HttpDestination) ((HttpDestination)attachment).onConnectionFailed(ex); else @@ -270,203 +262,200 @@ class SelectConnector extends AbstractLifeCycle implements HttpClient.Connector } } - /** - * An endpoint that is able to "upgrade" from a normal endpoint to a SSL endpoint. - * Since {@link HttpParser} and {@link HttpGenerator} only depend on the {@link EndPoint} - * interface, this class overrides all methods of {@link EndPoint} to provide the right - * behavior depending on the fact that it has been upgraded or not. - */ - public static class ProxySelectChannelEndPoint extends SslSelectChannelEndPoint + public static class UpgradableEndPoint implements AsyncEndPoint { - private final SelectChannelEndPoint plainEndPoint; - private volatile boolean upgraded = false; + AsyncEndPoint _endp; + SSLEngine _engine; - public ProxySelectChannelEndPoint(SocketChannel channel, SelectorManager.SelectSet selectSet, SelectionKey key, Buffers sslBuffers, SSLEngine engine, int maxIdleTimeout) throws IOException + public UpgradableEndPoint(AsyncEndPoint endp, SSLEngine engine) throws IOException { - super(sslBuffers, channel, selectSet, key, engine, maxIdleTimeout); - this.plainEndPoint = new SelectChannelEndPoint(channel, selectSet, key, maxIdleTimeout); + _engine=engine; + _endp=endp; } public void upgrade() { - upgraded = true; + AsyncHttpConnection connection = (AsyncHttpConnection)_endp.getConnection(); + + SslConnection sslConnection = new SslConnection(_engine,_endp); + _endp.setConnection(sslConnection); + + _endp=sslConnection.getSslEndPoint(); + sslConnection.getSslEndPoint().setConnection(connection); + + LOG.debug("upgrade {} to {} for {}",this,sslConnection,connection); + } + + + public Connection getConnection() + { + return _endp.getConnection(); + } + + public void setConnection(Connection connection) + { + _endp.setConnection(connection); } public void shutdownOutput() throws IOException { - if (upgraded) - super.shutdownOutput(); - else - plainEndPoint.shutdownOutput(); + _endp.shutdownOutput(); + } + + public void asyncDispatch() + { + _endp.asyncDispatch(); + } + + public boolean isOutputShutdown() + { + return _endp.isOutputShutdown(); + } + + public void shutdownInput() throws IOException + { + _endp.shutdownInput(); + } + + public void scheduleWrite() + { + _endp.scheduleWrite(); + } + + public boolean isInputShutdown() + { + return _endp.isInputShutdown(); } public void close() throws IOException { - if (upgraded) - super.close(); - else - plainEndPoint.close(); + _endp.close(); } public int fill(Buffer buffer) throws IOException { - if (upgraded) - return super.fill(buffer); - else - return plainEndPoint.fill(buffer); + return _endp.fill(buffer); + } + + public boolean isWritable() + { + return _endp.isWritable(); + } + + public boolean hasProgressed() + { + return _endp.hasProgressed(); } public int flush(Buffer buffer) throws IOException { - if (upgraded) - return super.flush(buffer); - else - return plainEndPoint.flush(buffer); + return _endp.flush(buffer); + } + + public void scheduleTimeout(Task task, long timeoutMs) + { + _endp.scheduleTimeout(task,timeoutMs); + } + + public void cancelTimeout(Task task) + { + _endp.cancelTimeout(task); } public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException { - if (upgraded) - return super.flush(header, buffer, trailer); - else - return plainEndPoint.flush(header, buffer, trailer); + return _endp.flush(header,buffer,trailer); } public String getLocalAddr() { - if (upgraded) - return super.getLocalAddr(); - else - return plainEndPoint.getLocalAddr(); + return _endp.getLocalAddr(); } public String getLocalHost() { - if (upgraded) - return super.getLocalHost(); - else - return plainEndPoint.getLocalHost(); + return _endp.getLocalHost(); } public int getLocalPort() { - if (upgraded) - return super.getLocalPort(); - else - return plainEndPoint.getLocalPort(); + return _endp.getLocalPort(); } public String getRemoteAddr() { - if (upgraded) - return super.getRemoteAddr(); - else - return plainEndPoint.getRemoteAddr(); + return _endp.getRemoteAddr(); } public String getRemoteHost() { - if (upgraded) - return super.getRemoteHost(); - else - return plainEndPoint.getRemoteHost(); + return _endp.getRemoteHost(); } public int getRemotePort() { - if (upgraded) - return super.getRemotePort(); - else - return plainEndPoint.getRemotePort(); + return _endp.getRemotePort(); } public boolean isBlocking() { - if (upgraded) - return super.isBlocking(); - else - return plainEndPoint.isBlocking(); - } - - public boolean isBufferred() - { - if (upgraded) - return super.isBufferred(); - else - return plainEndPoint.isBufferred(); + return _endp.isBlocking(); } public boolean blockReadable(long millisecs) throws IOException { - if (upgraded) - return super.blockReadable(millisecs); - else - return plainEndPoint.blockReadable(millisecs); + return _endp.blockReadable(millisecs); } public boolean blockWritable(long millisecs) throws IOException { - if (upgraded) - return super.blockWritable(millisecs); - else - return plainEndPoint.blockWritable(millisecs); + return _endp.blockWritable(millisecs); } public boolean isOpen() { - if (upgraded) - return super.isOpen(); - else - return plainEndPoint.isOpen(); + return _endp.isOpen(); } public Object getTransport() { - if (upgraded) - return super.getTransport(); - else - return plainEndPoint.getTransport(); - } - - public boolean isBufferingInput() - { - if (upgraded) - return super.isBufferingInput(); - else - return plainEndPoint.isBufferingInput(); - } - - public boolean isBufferingOutput() - { - if (upgraded) - return super.isBufferingOutput(); - else - return plainEndPoint.isBufferingOutput(); + return _endp.getTransport(); } public void flush() throws IOException { - if (upgraded) - super.flush(); - else - plainEndPoint.flush(); - + _endp.flush(); } public int getMaxIdleTime() { - if (upgraded) - return super.getMaxIdleTime(); - else - return plainEndPoint.getMaxIdleTime(); + return _endp.getMaxIdleTime(); } public void setMaxIdleTime(int timeMs) throws IOException { - if (upgraded) - super.setMaxIdleTime(timeMs); - else - plainEndPoint.setMaxIdleTime(timeMs); + _endp.setMaxIdleTime(timeMs); } + + public void onIdleExpired(long idleForMs) + { + _endp.onIdleExpired(idleForMs); + } + + public void setCheckForIdle(boolean check) + { + _endp.setCheckForIdle(check); + } + + public boolean isCheckForIdle() + { + return _endp.isCheckForIdle(); + } + + public String toString() + { + return "Upgradable:"+_endp.toString(); + } + } } diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/SocketConnector.java b/jetty-client/src/main/java/org/eclipse/jetty/client/SocketConnector.java index 031c0cb5bb3..fbcb9c28dcf 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/SocketConnector.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/SocketConnector.java @@ -17,7 +17,6 @@ import java.io.InterruptedIOException; import java.net.Socket; import javax.net.SocketFactory; -import javax.net.ssl.SSLContext; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; @@ -55,9 +54,9 @@ class SocketConnector extends AbstractLifeCycle implements HttpClient.Connector Address address = destination.isProxied() ? destination.getProxy() : destination.getAddress(); socket.connect(address.toSocketAddress(), _httpClient.getConnectTimeout()); - EndPoint endpoint=new SocketEndPoint(socket); + final EndPoint endpoint=new SocketEndPoint(socket); - final HttpConnection connection=new HttpConnection(_httpClient.getRequestBuffers(),_httpClient.getResponseBuffers(),endpoint); + final AbstractHttpConnection connection=new BlockingHttpConnection(_httpClient.getRequestBuffers(),_httpClient.getResponseBuffers(),endpoint); connection.setDestination(destination); destination.onNewConnection(connection); _httpClient.getThreadPool().dispatch(new Runnable() diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/AbstractConnectionTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/AbstractConnectionTest.java index 9f5bbaa95cb..e0618b1268e 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/AbstractConnectionTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/AbstractConnectionTest.java @@ -14,7 +14,11 @@ package org.eclipse.jetty.client; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import java.io.BufferedReader; +import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; @@ -25,9 +29,6 @@ import java.util.concurrent.TimeUnit; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - /** * @version $Revision$ $Date$ */ @@ -41,6 +42,14 @@ public abstract class AbstractConnectionTest return httpClient; } + + protected ServerSocket newServerSocket() throws IOException + { + ServerSocket serverSocket=new ServerSocket(); + serverSocket.bind(null); + return serverSocket; + } + @Test public void testServerClosedConnection() throws Exception { @@ -119,11 +128,15 @@ public abstract class AbstractConnectionTest } } + protected String getScheme() + { + return "http"; + } + @Test public void testServerClosedIncomplete() throws Exception { - ServerSocket serverSocket = new ServerSocket(); - serverSocket.bind(null); + ServerSocket serverSocket = newServerSocket(); int port=serverSocket.getLocalPort(); HttpClient httpClient = newHttpClient(); @@ -133,6 +146,7 @@ public abstract class AbstractConnectionTest { CountDownLatch latch = new CountDownLatch(1); HttpExchange exchange = new ConnectionExchange(latch); + exchange.setScheme(getScheme()); exchange.setAddress(new Address("localhost", port)); exchange.setRequestURI("/"); httpClient.send(exchange); @@ -159,7 +173,9 @@ public abstract class AbstractConnectionTest remote.close(); - assertEquals(HttpExchange.STATUS_EXCEPTED, exchange.waitForDone()); + int status = exchange.waitForDone(); + + assertEquals(HttpExchange.STATUS_EXCEPTED, status); } finally @@ -346,27 +362,33 @@ public abstract class AbstractConnectionTest HttpDestination dest = httpClient.getDestination(new Address("localhost", port),false); httpClient.send(exchange); - Socket s = serverSocket.accept(); + Socket server = serverSocket.accept(); + server.setSoTimeout(5000); byte[] buf = new byte[4096]; - s.getInputStream().read(buf); + + int len=server.getInputStream().read(buf); assertEquals(1,dest.getConnections()); assertEquals(0,dest.getIdleConnections()); - s.getOutputStream().write("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n".getBytes()); - - Thread.sleep(300); + server.getOutputStream().write("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n".getBytes()); + + assertEquals(HttpExchange.STATUS_COMPLETED,exchange.waitForDone()); + Thread.sleep(200); // TODO get rid of this assertEquals(1,dest.getConnections()); assertEquals(1,dest.getIdleConnections()); exchange = new ConnectionExchange(); exchange.setAddress(new Address("localhost", port)); exchange.setRequestURI("/"); - httpClient.send(exchange); - s.getInputStream().read(buf); assertEquals(1,dest.getConnections()); assertEquals(0,dest.getIdleConnections()); - s.getOutputStream().write("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n".getBytes()); + + + len=server.getInputStream().read(buf); + assertEquals(1,dest.getConnections()); + assertEquals(0,dest.getIdleConnections()); + server.getOutputStream().write("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n".getBytes()); Thread.sleep(500); diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/AbstractHttpExchangeCancelTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/AbstractHttpExchangeCancelTest.java index c1d5c3da2c1..923d6867664 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/AbstractHttpExchangeCancelTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/AbstractHttpExchangeCancelTest.java @@ -14,7 +14,11 @@ package org.eclipse.jetty.client; -import static org.junit.Assert.*; +import static org.hamcrest.Matchers.is; +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.io.IOException; import java.net.SocketTimeoutException; @@ -25,23 +29,25 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.io.Buffer; +import org.eclipse.jetty.server.AbstractHttpConnection; import org.eclipse.jetty.server.Connector; -import org.eclipse.jetty.server.HttpConnection; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.AbstractHandler; 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.log.StdErrLog; import org.junit.After; import org.junit.Before; import org.junit.Test; /** - * @version $Revision$ $Date$ */ public abstract class AbstractHttpExchangeCancelTest { + private static final Logger LOG = Log.getLogger(AbstractHttpExchangeCancelTest.TestHttpExchange.class); + private Server server; private Connector connector; @@ -73,13 +79,13 @@ public abstract class AbstractHttpExchangeCancelTest TestHttpExchange exchange = new TestHttpExchange() { @Override - void setStatus(int status) + boolean setStatus(int status) { // Cancel before setting the new status if (getStatus() == HttpExchange.STATUS_START && status == STATUS_WAITING_FOR_CONNECTION) cancel(); - super.setStatus(status); + return super.setStatus(status); } }; exchange.setAddress(newAddress()); @@ -110,14 +116,15 @@ public abstract class AbstractHttpExchangeCancelTest TestHttpExchange exchange = new TestHttpExchange() { @Override - void setStatus(int status) + boolean setStatus(int status) { // Cancel after setting the new status int oldStatus = getStatus(); - super.setStatus(status); + boolean set = super.setStatus(status); if (oldStatus == STATUS_START && getStatus() == HttpExchange.STATUS_WAITING_FOR_CONNECTION) cancel(); + return set; } }; exchange.setAddress(newAddress()); @@ -181,10 +188,10 @@ public abstract class AbstractHttpExchangeCancelTest getHttpClient().send(exchange); int status = exchange.waitForDone(); - assertEquals(HttpExchange.STATUS_CANCELLED, status); - assertFalse(exchange.isResponseCompleted()); - assertFalse(exchange.isFailed()); - assertFalse(exchange.isAssociated()); + assertThat("Exchange Status", status, is(HttpExchange.STATUS_CANCELLED)); + assertThat("Exchange.isResponseCompleted", exchange.isResponseCompleted(), is(false)); + assertThat("Exchange.isFailed", exchange.isFailed(), is(false)); + assertThat("Exchange.isAssociated", exchange.isAssociated(), is(false)); } /* ------------------------------------------------------------ */ @@ -318,7 +325,7 @@ public abstract class AbstractHttpExchangeCancelTest { try { - ((StdErrLog)Log.getLogger(HttpConnection.class)).setHideStacks(true); + ((StdErrLog)Log.getLogger(AbstractHttpConnection.class)).setHideStacks(true); TestHttpExchange exchange = new TestHttpExchange(); exchange.setAddress(newAddress()); exchange.setRequestURI("/?action=throw"); @@ -333,7 +340,7 @@ public abstract class AbstractHttpExchangeCancelTest } finally { - ((StdErrLog)Log.getLogger(HttpConnection.class)).setHideStacks(false); + ((StdErrLog)Log.getLogger(AbstractHttpConnection.class)).setHideStacks(false); } } @@ -442,6 +449,7 @@ public abstract class AbstractHttpExchangeCancelTest @Override protected synchronized void onException(Throwable ex) { + LOG.debug(ex); if (ex instanceof SocketTimeoutException || ex.getCause() instanceof SocketTimeoutException) expired=true; diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/AsyncSelectConnectionTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/AsyncSelectConnectionTest.java index 7dbbefd7e5b..20a6d492e4f 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/AsyncSelectConnectionTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/AsyncSelectConnectionTest.java @@ -13,6 +13,13 @@ package org.eclipse.jetty.client; +import java.io.IOException; +import java.net.ServerSocket; + +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.junit.BeforeClass; + public class AsyncSelectConnectionTest extends AbstractConnectionTest { protected HttpClient newHttpClient() @@ -23,10 +30,38 @@ public class AsyncSelectConnectionTest extends AbstractConnectionTest return httpClient; } + static SslContextFactory ctx = new SslContextFactory(MavenTestingUtils.getTestResourceFile("keystore").getAbsolutePath()); + + @BeforeClass + public static void initKS() throws Exception + { + ctx.setKeyStorePassword("storepwd"); + ctx.setKeyManagerPassword("keypwd"); + ctx.start(); + } + + @Override + protected String getScheme() + { + return "https"; + } + + @Override + protected ServerSocket newServerSocket() throws IOException + { + return ctx.newSslServerSocket(null,0,100); + } + @Override public void testServerHalfClosedIncomplete() throws Exception { - super.testServerHalfClosedIncomplete(); + // SSL doesn't do half closes + } + + @Override + public void testServerClosedIncomplete() throws Exception + { + super.testServerClosedIncomplete(); } diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/AsyncSslHttpExchangeTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/AsyncSslHttpExchangeTest.java index 2c878e273d5..875e224594c 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/AsyncSslHttpExchangeTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/AsyncSslHttpExchangeTest.java @@ -16,7 +16,6 @@ package org.eclipse.jetty.client; import org.eclipse.jetty.client.helperClasses.AsyncSslServerAndClientCreator; import org.eclipse.jetty.client.helperClasses.ServerAndClientCreator; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; public class AsyncSslHttpExchangeTest extends SslHttpExchangeTest @@ -32,4 +31,18 @@ public class AsyncSslHttpExchangeTest extends SslHttpExchangeTest _port = _server.getConnectors()[0].getLocalPort(); } + + @Test + public void testPerf1() throws Exception + { + sender(1,true); + } + + + @Override + public void testBigPostWithContentExchange() throws Exception + { + super.testBigPostWithContentExchange(); + } + } diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/Curl.java b/jetty-client/src/test/java/org/eclipse/jetty/client/Curl.java index 1e7c1da3913..6c4d5102709 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/Curl.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/Curl.java @@ -33,11 +33,26 @@ public class Curl client.start(); boolean async=true; boolean dump= false; + boolean verbose= false; - final CountDownLatch latch = new CountDownLatch(args.length); + + int urls=0; + for (String arg : args) + { + if (!arg.startsWith("-")) + urls++; + } + + final CountDownLatch latch = new CountDownLatch(urls); for (String arg : args) { + if ("--verbose".equals(arg)) + { + verbose=true; + continue; + } + if ("--sync".equals(arg)) { async=false; @@ -63,6 +78,7 @@ public class Curl } final boolean d = dump; + final boolean v = verbose; HttpExchange ex = new HttpExchange() { AtomicBoolean counted=new AtomicBoolean(false); @@ -105,7 +121,8 @@ public class Curl super.onResponseContent(content); if (d) System.out.print(content.toString()); - System.err.println("got "+content.length()); + if (v) + System.err.println("got "+content.length()); } /* ------------------------------------------------------------ */ @@ -116,7 +133,8 @@ public class Curl protected void onResponseHeader(Buffer name, Buffer value) throws IOException { super.onResponseHeader(name,value); - System.err.println(name+": "+value); + if (v) + System.err.println(name+": "+value); } /* ------------------------------------------------------------ */ @@ -127,7 +145,8 @@ public class Curl protected void onResponseHeaderComplete() throws IOException { super.onResponseHeaderComplete(); - System.err.println(); + if (v) + System.err.println(); } /* ------------------------------------------------------------ */ @@ -138,7 +157,8 @@ public class Curl protected void onResponseStatus(Buffer version, int status, Buffer reason) throws IOException { super.onResponseStatus(version,status,reason); - System.err.println(version+" "+status+" "+reason); + if (v) + System.err.println(version+" "+status+" "+reason); } }; diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/ExternalKeyStoreAsyncSslHttpExchangeTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/ExternalKeyStoreAsyncSslHttpExchangeTest.java index 27af22c21ed..c61f767e5c6 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/ExternalKeyStoreAsyncSslHttpExchangeTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/ExternalKeyStoreAsyncSslHttpExchangeTest.java @@ -16,8 +16,6 @@ package org.eclipse.jetty.client; import org.eclipse.jetty.client.helperClasses.ExternalKeyStoreAsyncSslServerAndClientCreator; import org.eclipse.jetty.client.helperClasses.ServerAndClientCreator; import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; public class ExternalKeyStoreAsyncSslHttpExchangeTest extends SslHttpExchangeTest { @@ -31,12 +29,4 @@ public class ExternalKeyStoreAsyncSslHttpExchangeTest extends SslHttpExchangeTes _httpClient = serverAndClientCreator.createClient(3000L,3500L,2000); _port = _server.getConnectors()[0].getLocalPort(); } - - @Override - @Test - public void testBigPostWithContentExchange() throws Exception - { - super.testBigPostWithContentExchange(); - } - } diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpExchangeTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpExchangeTest.java index 3b84bfec975..d4fbf0fdd4b 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpExchangeTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpExchangeTest.java @@ -13,8 +13,12 @@ package org.eclipse.jetty.client; -import static org.junit.Assert.*; -import static org.junit.matchers.JUnitMatchers.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.matchers.JUnitMatchers.containsString; import java.io.IOException; import java.io.InputStream; @@ -40,9 +44,7 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.toolchain.test.Stress; import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.junit.After; -import org.junit.AfterClass; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; /* ------------------------------------------------------------ */ @@ -51,7 +53,7 @@ import org.junit.Test; */ public class HttpExchangeTest { - final static boolean verbose=false; + final static boolean verbose=HttpExchange.LOG.isDebugEnabled(); protected static int _maxConnectionsPerAddress = 2; protected static String _scheme = "http"; protected static Server _server; @@ -59,7 +61,7 @@ public class HttpExchangeTest protected static HttpClient _httpClient; protected static AtomicInteger _count = new AtomicInteger(); protected static ServerAndClientCreator serverAndClientCreator = new HttpServerAndClientCreator(); - + protected static URI getBaseURI() { return URI.create(_scheme + "://localhost:" + _port + "/"); @@ -120,11 +122,12 @@ public class HttpExchangeTest /* ------------------------------------------------------------ */ /** * Test sending data through the exchange. - * + * * @throws IOException */ public void sender(final int nb, final boolean close) throws Exception { + // System.err.printf("%nSENDER %d %s%n",nb,close); _count.set(0); final CountDownLatch complete = new CountDownLatch(nb); final AtomicInteger allcontent = new AtomicInteger(nb); @@ -144,7 +147,7 @@ public class HttpExchangeTest protected void onRequestCommitted() { if (verbose) - System.err.println("[ "); + System.err.println(n+" [ "+this); result = "committed"; } @@ -153,7 +156,7 @@ public class HttpExchangeTest protected void onRequestComplete() throws IOException { if (verbose) - System.err.println("[ =="); + System.err.println(n+" [ =="); result = "sent"; } @@ -162,7 +165,7 @@ public class HttpExchangeTest protected void onResponseStatus(Buffer version, int status, Buffer reason) { if (verbose) - System.err.println("] "+version+" "+status+" "+reason); + System.err.println(n+" ] "+version+" "+status+" "+reason); result = "status"; } @@ -171,7 +174,7 @@ public class HttpExchangeTest protected void onResponseHeader(Buffer name, Buffer value) { if (verbose) - System.err.println("] "+name+": "+value); + System.err.println(n+" ] "+name+": "+value); } /* ------------------------------------------------------------ */ @@ -179,7 +182,7 @@ public class HttpExchangeTest protected void onResponseHeaderComplete() throws IOException { if (verbose) - System.err.println("] -"); + System.err.println(n+" ] -"); result = "content"; super.onResponseHeaderComplete(); } @@ -190,7 +193,7 @@ public class HttpExchangeTest { len += content.length(); if (verbose) - System.err.println("] "+content.length()+" -> "+len); + System.err.println(n+" ] "+content.length()+" -> "+len); } /* ------------------------------------------------------------ */ @@ -198,12 +201,12 @@ public class HttpExchangeTest protected void onResponseComplete() { if (verbose) - System.err.println("] == "+len+" "+complete.getCount()+"/"+nb); + System.err.println(n+" ] == "+len+" "+complete.getCount()+"/"+nb); result = "complete"; if (len == 2009) allcontent.decrementAndGet(); else - System.err.println(n + " ONLY " + len+ "/2009"); + System.err.println(n+ " ONLY " + len+ "/2009"); complete.countDown(); } @@ -212,10 +215,10 @@ public class HttpExchangeTest protected void onConnectionFailed(Throwable ex) { if (verbose) - System.err.println("] "+ex); + System.err.println(n+" ] "+ex); complete.countDown(); result = "failed"; - System.err.println(n + " FAILED " + ex); + System.err.println(n+ " FAILED " + ex); super.onConnectionFailed(ex); } @@ -224,10 +227,10 @@ public class HttpExchangeTest protected void onException(Throwable ex) { if (verbose) - System.err.println("] "+ex); + System.err.println(n+" ] "+ex); complete.countDown(); result = "excepted"; - System.err.println(n + " EXCEPTED " + ex); + System.err.println(n+ " EXCEPTED " + ex); super.onException(ex); } @@ -236,7 +239,7 @@ public class HttpExchangeTest protected void onExpire() { if (verbose) - System.err.println("] expired"); + System.err.println(n+" ] expired"); complete.countDown(); result = "expired"; System.err.println(n + " EXPIRED " + len); @@ -258,12 +261,12 @@ public class HttpExchangeTest _httpClient.send(httpExchange[n]); } - + if (!complete.await(2,TimeUnit.SECONDS)) System.err.println(_httpClient.dump()); - + assertTrue(complete.await(20,TimeUnit.SECONDS)); - + assertEquals("nb="+nb+" close="+close,0,allcontent.get()); } @@ -307,7 +310,7 @@ public class HttpExchangeTest Thread.sleep(5); } } - + /* ------------------------------------------------------------ */ @Test public void testLocalAddressAvailabilityWithContentExchange() throws Exception @@ -320,9 +323,9 @@ public class HttpExchangeTest httpExchange.setMethod(HttpMethods.GET); _httpClient.send(httpExchange); int status = httpExchange.waitForDone(); - + assertNotNull(httpExchange.getLocalAddress()); - + String result=httpExchange.getResponseContent(); assertNotNull("Should have received response content", result); assertEquals("i="+i,0,result.indexOf("")); @@ -331,13 +334,13 @@ public class HttpExchangeTest Thread.sleep(5); } } - + /* ------------------------------------------------------------ */ @Test public void testShutdownWithExchange() throws Exception { final AtomicReference throwable=new AtomicReference(); - + HttpExchange httpExchange=new HttpExchange() { @@ -369,8 +372,8 @@ public class HttpExchangeTest @Override public void run() { - try { - Thread.sleep(500); + try { + Thread.sleep(500); _httpClient.stop(); } catch(Exception e) {e.printStackTrace();} } @@ -386,12 +389,12 @@ public class HttpExchangeTest /* ------------------------------------------------------------ */ @Test public void testBigPostWithContentExchange() throws Exception - { + { int size =32; ContentExchange httpExchange=new ContentExchange() { int total; - + @Override protected synchronized void onResponseStatus(Buffer version, int status, Buffer reason) throws IOException { @@ -434,7 +437,7 @@ public class HttpExchangeTest System.err.println("] --"); super.onResponseHeaderComplete(); } - + }; Buffer babuf = new ByteArrayBuffer(size*36*1024); @@ -447,13 +450,28 @@ public class HttpExchangeTest babuf.put(bytes); niobuf.put(bytes); } - + httpExchange.setURI(getBaseURI()); httpExchange.setMethod(HttpMethods.POST); httpExchange.setRequestContentType("application/data"); httpExchange.setRequestContent(babuf); - _httpClient.send(httpExchange); + + long start=System.currentTimeMillis(); + while(!httpExchange.isDone()) + { + long now=System.currentTimeMillis(); + if ((now-start)>=10000) + { + System.err.println("TEST IS TAKING TOOOOO LONG!!!!!!!!!!!!!!!!!!!!"); + System.err.println("CLIENT:"); + System.err.println(_httpClient.dump()); + System.err.println("SERVER:"); + _server.dumpStdErr(); + break; + } + Thread.sleep(100); + } int status = httpExchange.waitForDone(); assertEquals(HttpExchange.STATUS_COMPLETED,status); String result=httpExchange.getResponseContent(); @@ -465,6 +483,22 @@ public class HttpExchangeTest httpExchange.setRequestContentType("application/data"); httpExchange.setRequestContent(niobuf); _httpClient.send(httpExchange); + + start=System.currentTimeMillis(); + while(!httpExchange.isDone()) + { + long now=System.currentTimeMillis(); + if ((now-start)>=10000) + { + System.err.println("TEST IS TAKING TOOOOO LONG!!!!!!!!!!!!!!!!!!!!"); + System.err.println("CLIENT:"); + System.err.println(_httpClient.dump()); + System.err.println("SERVER:"); + _server.dumpStdErr(); + break; + } + Thread.sleep(100); + } status = httpExchange.waitForDone(); assertEquals(HttpExchange.STATUS_COMPLETED, status); result=httpExchange.getResponseContent(); @@ -475,10 +509,7 @@ public class HttpExchangeTest @Test public void testSlowPost() throws Exception { - ContentExchange httpExchange=new ContentExchange() - { - - }; + ContentExchange httpExchange=new ContentExchange(); httpExchange.setURI(getBaseURI()); httpExchange.setMethod(HttpMethods.POST); @@ -491,15 +522,27 @@ public class HttpExchangeTest @Override public int read() throws IOException { + // System.err.printf("reading 1 of %d/%d%n",_index,data.length()); if (_index>=data.length()) return -1; + try + { + Thread.sleep(5); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + + // System.err.printf("read 1%n"); return data.charAt(_index++); } @Override public int read(byte[] b, int off, int len) throws IOException { + // System.err.printf("reading %d of %d/%d%n",len,_index,data.length()); if (_index >= data.length()) return -1; @@ -516,18 +559,17 @@ public class HttpExchangeTest while (l < 5 && _index < data.length() && l < len) b[off + l++] = (byte)data.charAt(_index++); + // System.err.printf("read %d%n",l); return l; } }; httpExchange.setRequestContentSource(content); - // httpExchange.setRequestContent(new ByteArrayBuffer(data)); _httpClient.send(httpExchange); int status = httpExchange.waitForDone(); - // httpExchange.waitForStatus(HttpExchange.STATUS_COMPLETED); String result = httpExchange.getResponseContent(); assertEquals(HttpExchange.STATUS_COMPLETED,status); assertEquals(data,result); @@ -571,7 +613,7 @@ public class HttpExchangeTest { _httpClient = serverAndClientCreator.createClient(3000L,3500L,2000); final HttpDestination destination = _httpClient.getDestination(new Address("localhost",_port),_scheme.equalsIgnoreCase("https")); - final org.eclipse.jetty.client.HttpConnection[] connections = new org.eclipse.jetty.client.HttpConnection[_maxConnectionsPerAddress]; + final org.eclipse.jetty.client.AbstractHttpConnection[] connections = new org.eclipse.jetty.client.AbstractHttpConnection[_maxConnectionsPerAddress]; for (int i = 0; i < _maxConnectionsPerAddress; i++) { connections[i] = destination.reserveConnection(200); @@ -593,13 +635,13 @@ public class HttpExchangeTest // reserving one should now work c = destination.reserveConnection(500); assertNotNull(c); - + // release connections - for (HttpConnection httpConnection : connections){ + for (AbstractHttpConnection httpConnection : connections){ destination.returnConnection(httpConnection,false); } } - + @Test public void testOptionsWithExchange() throws Exception { @@ -609,15 +651,15 @@ public class HttpExchangeTest httpExchange.setMethod(HttpMethods.OPTIONS); // httpExchange.setRequestHeader("Connection","close"); _httpClient.send(httpExchange); - + int state = httpExchange.waitForDone(); assertEquals(HttpExchange.STATUS_COMPLETED, state); assertEquals(HttpStatus.OK_200,httpExchange.getResponseStatus()); - + HttpFields headers = httpExchange.getResponseFields(); HttpAsserts.assertContainsHeaderKey("Content-Length", headers); assertEquals("Content-Length header value", 0, headers.getLongField("Content-Length")); - + HttpAsserts.assertContainsHeaderKey("Allow",headers); String allow = headers.getStringField("Allow"); String expectedMethods[] = diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpsViaBrokenHttpProxyTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpsViaBrokenHttpProxyTest.java new file mode 100644 index 00000000000..91d241f0b0d --- /dev/null +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpsViaBrokenHttpProxyTest.java @@ -0,0 +1,136 @@ +// ======================================================================== +// Copyright (c) 2009-2009 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.client; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; +import java.net.ProtocolException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.AbstractHttpConnection; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ConnectHandler; +import org.eclipse.jetty.server.nio.SelectChannelConnector; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/* ------------------------------------------------------------ */ +/** + * This UnitTest class executes two tests. Both will send a http request to https://google.com through a misbehaving proxy server. + *

+ * The first test runs against a proxy which simply closes the connection (as nginx does) for a connect request. The second proxy server always responds with a + * 500 error. + *

+ * The expected result for both tests is an exception and the HttpExchange should have status HttpExchange.STATUS_EXCEPTED. + */ +public class HttpsViaBrokenHttpProxyTest +{ + private Server _proxy = new Server(); + private HttpClient _client = new HttpClient(); + + @Before + public void init() throws Exception + { + // setup proxies with different behaviour + _proxy.addConnector(new SelectChannelConnector()); + _proxy.setHandler(new BadBehavingConnectHandler()); + _proxy.start(); + int proxyClosingConnectionPort = _proxy.getConnectors()[0].getLocalPort(); + + _client.setProxy(new Address("localhost", proxyClosingConnectionPort)); + _client.start(); + } + + @After + public void destroy() throws Exception + { + _client.stop(); + _proxy.stop(); + } + + @Test + public void httpsViaProxyThatClosesConnectionOnConnectRequestTest() throws Exception + { + sendRequestThroughProxy(new ContentExchange() + { + + @Override + protected void onException(Throwable x) + { + + } + + }, "close", 9); + } + + @Test + public void httpsViaProxyThatReturns500ErrorTest() throws Exception + { + HttpExchange exchange = new ContentExchange() + { + @Override + protected void onException(Throwable x) + { + // Suppress logging for expected exception + if (!(x instanceof ProtocolException)) + super.onException(x); + } + }; + sendRequestThroughProxy(exchange, "error500", 9); + } + + @Test + public void httpsViaProxyThatReturns504ErrorTest() throws Exception + { + sendRequestThroughProxy(new ContentExchange(), "error504", 8); + } + + private void sendRequestThroughProxy(HttpExchange exchange, String desiredBehaviour, int exptectedStatus) throws Exception + { + String url = "https://" + desiredBehaviour + ".com/"; + exchange.setURL(url); + exchange.addRequestHeader("behaviour", desiredBehaviour); + _client.send(exchange); + assertEquals(HttpExchange.toState(exptectedStatus) + " status awaited", exptectedStatus, exchange.waitForDone()); + } + + private class BadBehavingConnectHandler extends ConnectHandler + { + @Override + protected void handleConnect(Request baseRequest, HttpServletRequest request, HttpServletResponse response, String serverAddress) + throws ServletException, IOException + { + if (serverAddress.contains("close")) + { + AbstractHttpConnection.getCurrentConnection().getEndPoint().close(); + } + else if (serverAddress.contains("error500")) + { + response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR_500); + } + else if (serverAddress.contains("error504")) + { + response.setStatus(HttpStatus.GATEWAY_TIMEOUT_504); + } + baseRequest.setHandled(true); + } + } +} diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/NonBlockingHttpExchangeCancelTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/NonBlockingHttpExchangeCancelTest.java index 7fdde5b2a2c..9950d6710fb 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/NonBlockingHttpExchangeCancelTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/NonBlockingHttpExchangeCancelTest.java @@ -51,4 +51,10 @@ public class NonBlockingHttpExchangeCancelTest extends AbstractHttpExchangeCance { return httpClient; } + + /* ------------------------------------------------------------ */ + public void testHttpExchangeCancelOnRequestComplete() throws Exception + { + super.testHttpExchangeCancelOnRequestComplete(); + } } diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/ProxyTunnellingTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/ProxyTunnellingTest.java index 441f9dfac7d..89a55f510e0 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/ProxyTunnellingTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/ProxyTunnellingTest.java @@ -1,9 +1,13 @@ package org.eclipse.jetty.client; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import java.io.IOException; import java.net.URLEncoder; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; + import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; @@ -12,7 +16,6 @@ import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.http.HttpHeaders; import org.eclipse.jetty.http.HttpMethods; import org.eclipse.jetty.http.MimeTypes; -import org.eclipse.jetty.http.ssl.SslContextFactory; import org.eclipse.jetty.io.ByteArrayBuffer; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Handler; @@ -23,12 +26,10 @@ import org.eclipse.jetty.server.handler.ConnectHandler; import org.eclipse.jetty.server.nio.SelectChannelConnector; import org.eclipse.jetty.server.ssl.SslSelectChannelConnector; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.ssl.SslContextFactory; import org.junit.After; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - public class ProxyTunnellingTest { private Server server; @@ -41,7 +42,7 @@ public class ProxyTunnellingTest { return proxyConnector.getLocalPort(); } - + protected void startSSLServer(Handler handler) throws Exception { SslSelectChannelConnector connector = new SslSelectChannelConnector(); @@ -217,11 +218,11 @@ public class ProxyTunnellingTest ContentExchange exchange = new ContentExchange(true) { @Override - protected void onConnectionFailed(Throwable x) + protected void onException(Throwable x) { latch.countDown(); } - + }; exchange.setMethod(HttpMethods.GET); String body = "BODY"; diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/SecuredContentExchangeTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/SecuredContentExchangeTest.java index b7883b229ab..9016b50a8f2 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/SecuredContentExchangeTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/SecuredContentExchangeTest.java @@ -19,7 +19,6 @@ import java.util.HashSet; import java.util.Set; import org.eclipse.jetty.client.security.Realm; -import org.eclipse.jetty.http.security.Constraint; import org.eclipse.jetty.security.ConstraintMapping; import org.eclipse.jetty.security.ConstraintSecurityHandler; import org.eclipse.jetty.security.HashLoginService; @@ -33,6 +32,7 @@ import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.security.Constraint; public class SecuredContentExchangeTest extends ContentExchangeTest diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/SecuredErrorStatusTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/SecuredErrorStatusTest.java index 7af4be89281..00a99a8f1fd 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/SecuredErrorStatusTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/SecuredErrorStatusTest.java @@ -20,7 +20,6 @@ import java.util.Set; import org.eclipse.jetty.client.security.Realm; import org.eclipse.jetty.http.HttpStatus; -import org.eclipse.jetty.http.security.Constraint; import org.eclipse.jetty.security.ConstraintMapping; import org.eclipse.jetty.security.ConstraintSecurityHandler; import org.eclipse.jetty.security.HashLoginService; @@ -34,6 +33,7 @@ import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.security.Constraint; import org.junit.Test; public class SecuredErrorStatusTest diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/SecurityListenerTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/SecurityListenerTest.java index 133cf476c92..8a1e8a50e94 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/SecurityListenerTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/SecurityListenerTest.java @@ -31,7 +31,6 @@ import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.client.security.Realm; import org.eclipse.jetty.client.security.SimpleRealmResolver; import org.eclipse.jetty.http.HttpMethods; -import org.eclipse.jetty.http.security.Constraint; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.EofException; import org.eclipse.jetty.security.ConstraintMapping; @@ -46,6 +45,7 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.server.nio.SelectChannelConnector; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.security.Constraint; import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/Siege.java b/jetty-client/src/test/java/org/eclipse/jetty/client/Siege.java new file mode 100644 index 00000000000..bc476c27fc7 --- /dev/null +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/Siege.java @@ -0,0 +1,211 @@ +package org.eclipse.jetty.client; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.eclipse.jetty.http.HttpMethods; +import org.eclipse.jetty.io.Buffer; +import org.eclipse.jetty.util.thread.QueuedThreadPool; + + +/* ------------------------------------------------------------ */ +/** + */ +public class Siege +{ + private static final class ConcurrentExchange extends HttpExchange + { + private final long _start=System.currentTimeMillis(); + private final HttpClient _client; + private final CountDownLatch _latch; + volatile int _status; + volatile int _count; + volatile long _bytes; + final List _uris; + final int _repeats; + int _u; + int _r; + + AtomicBoolean counted=new AtomicBoolean(false); + + public ConcurrentExchange(HttpClient client,CountDownLatch latch, List uris, int repeats) + { + _client = client; + _latch = latch; + _uris = uris; + _repeats = repeats; + } + + @Override + protected void onConnectionFailed(Throwable ex) + { + if (!counted.getAndSet(true)) + _latch.countDown(); + super.onConnectionFailed(ex); + } + + @Override + protected void onException(Throwable ex) + { + if (!counted.getAndSet(true)) + _latch.countDown(); + super.onException(ex); + } + + @Override + protected void onExpire() + { + if (!counted.getAndSet(true)) + _latch.countDown(); + super.onExpire(); + } + + @Override + protected void onResponseComplete() throws IOException + { + if (_status==200) + _count++; + if (!next() && !counted.getAndSet(true)) + { + _latch.countDown(); + long duration=System.currentTimeMillis()-_start; + System.err.printf("Got %d/%d with %dB in %dms %d%n",_count,_uris.size()*_repeats,_bytes,duration,_latch.getCount()); + } + } + + + /* ------------------------------------------------------------ */ + @Override + protected void onResponseContent(Buffer content) throws IOException + { + _bytes+=content.length(); + super.onResponseContent(content); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.client.HttpExchange#onResponseHeader(org.eclipse.jetty.io.Buffer, org.eclipse.jetty.io.Buffer) + */ + @Override + protected void onResponseHeader(Buffer name, Buffer value) throws IOException + { + super.onResponseHeader(name,value); + if ("Set-Cookie".equalsIgnoreCase(name.toString())) + { + String v=value.toString(); + int c = v.indexOf(';'); + if (c>=0) + v=v.substring(0,c); + addRequestHeader("Cookie",v); + } + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.client.HttpExchange#onResponseHeaderComplete() + */ + @Override + protected void onResponseHeaderComplete() throws IOException + { + super.onResponseHeaderComplete(); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.client.HttpExchange#onResponseStatus(org.eclipse.jetty.io.Buffer, int, org.eclipse.jetty.io.Buffer) + */ + @Override + protected void onResponseStatus(Buffer version, int status, Buffer reason) throws IOException + { + _status=status; + super.onResponseStatus(version,status,reason); + } + + public boolean next() + { + if (_u>=_uris.size()) + { + _u=0; + _r++; + if (_r>=_repeats) + return false; + } + + String uri=_uris.get(_u++); + + reset(); + setMethod(HttpMethods.GET); + setURL(uri); + + try + { + _client.send(this); + } + catch(IOException e) + { + e.printStackTrace(); + return false; + } + return true; + } + } + + public static void main(String[] args) + throws Exception + { + if (args.length==0) + args=new String[] + { "-c", "2", "-r", "2", "http://localhost:8080/dump", "http://localhost:8080/d.txt"}; + + int concurrent=1; + int repeats=1; + final List uris = new ArrayList(); + + for (int i=0; i accumulatedRequest; + + public SluggishHandler(int requestSize) + { + accumulatedRequest = new ArrayList(requestSize); + } + + public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + { + accumulatedRequest.clear(); + ServletInputStream input = request.getInputStream(); + byte[] buffer = new byte[16384]; + int bytesAvailable; + while ((bytesAvailable = input.read(buffer,0,buffer.length)) > 0) + { + //System.err.println("AVAILABLE FOR READ = " + bytesAvailable); + for (int n = 0; n < bytesAvailable; ++n) + { + accumulatedRequest.add(buffer[n]); + } + try + { + Thread.sleep(READ_DELAY); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + response.setStatus(HttpServletResponse.SC_OK); + baseRequest.setHandled(true); + //System.err.println("HANDLED"); + } + + public byte[] getAccumulatedRequest() + { + byte[] buffer = new byte[accumulatedRequest.size()]; + int pos = 0; + for (Byte b : accumulatedRequest) + { + buffer[pos++] = b; + } + return buffer; + } + } + + private static boolean compareBuffers(byte[] sent, byte[] received) + { + if (sent.length != received.length) + { + System.err.format("Mismatch in sent/received lengths: sent=%d received=%d\n",sent.length,received.length); + return false; + } + else + { + for (int n = 0; n < sent.length; ++n) + { + if (sent[n] != received[n]) + { + System.err.format("Mismatch at offset %d: request=%d response=%d\n",n,sent[n],received[n]); + return false; + } + } + } + return true; + } + + @Test + public void test0() throws Exception + { + goSlow(20000,10); + } + + @Test + public void test1() throws Exception + { + goSlow(200000,5); + } + + @Test + public void test2() throws Exception + { + goSlow(2000000,2); + } + + void goSlow(int requestSize,int iterations) throws Exception + { + Server server = new Server(); + SocketConnector connector = new SocketConnector(); + server.addConnector(connector); + SluggishHandler handler = new SluggishHandler(requestSize); + server.setHandler(handler); + server.start(); + int port = connector.getLocalPort(); + + HttpClient client = new HttpClient(); + client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL); + client.setConnectTimeout(5000); + client.setIdleTimeout(60000); + client.start(); + + try + { + for (int i = 0; i < iterations; ++i) + { + //System.err.format("-------------- ITERATION %d ------------------\n",i); + SluggishExchange exchange = new SluggishExchange(port,requestSize); + long startTime = System.currentTimeMillis(); + client.send(exchange); + exchange.waitForDone(); + long endTime = System.currentTimeMillis(); + //System.err.println("EXCHANGE STATUS = " + exchange); + //System.err.println("ELAPSED MSEC = " + (endTime - startTime)); + Assert.assertTrue(compareBuffers(exchange.getRequestBody(),handler.getAccumulatedRequest())); + } + } + finally + { + server.stop(); + server.join(); + } + } +} diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/SocketConnectionTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/SocketConnectionTest.java index 4438463ce2d..adcaab849a5 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/SocketConnectionTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/SocketConnectionTest.java @@ -13,6 +13,8 @@ package org.eclipse.jetty.client; +import static org.junit.Assert.assertEquals; + import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; @@ -21,8 +23,6 @@ import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.CountDownLatch; -import static org.junit.Assert.assertEquals; - public class SocketConnectionTest extends AbstractConnectionTest { protected HttpClient newHttpClient() @@ -88,4 +88,9 @@ public class SocketConnectionTest extends AbstractConnectionTest httpClient.stop(); } } + + public void testIdleConnection() throws Exception + { + super.testIdleConnection(); + } } diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/SslBytesClientTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/SslBytesClientTest.java new file mode 100644 index 00000000000..a2f041b7d0e --- /dev/null +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/SslBytesClientTest.java @@ -0,0 +1,148 @@ +package org.eclipse.jetty.client; + +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLSocket; + +import org.eclipse.jetty.http.HttpMethods; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class SslBytesClientTest extends SslBytesTest +{ + private ExecutorService threadPool; + private HttpClient client; + private SimpleProxy proxy; + private SSLServerSocket acceptor; + + @Before + public void init() throws Exception + { + threadPool = Executors.newCachedThreadPool(); + + client = new HttpClient(); + client.setMaxConnectionsPerAddress(1); + client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL); + File keyStore = MavenTestingUtils.getTestResourceFile("keystore"); + SslContextFactory cf = client.getSslContextFactory(); + cf.setKeyStorePath(keyStore.getAbsolutePath()); + cf.setKeyStorePassword("storepwd"); + cf.setKeyManagerPassword("keypwd"); + client.start(); + + SSLContext sslContext = cf.getSslContext(); + acceptor = (SSLServerSocket)sslContext.getServerSocketFactory().createServerSocket(0); + + int serverPort = acceptor.getLocalPort(); + + proxy = new SimpleProxy(threadPool, "localhost", serverPort); + proxy.start(); + logger.debug(":{} <==> :{}", proxy.getPort(), serverPort); + } + + @After + public void destroy() throws Exception + { + if (acceptor != null) + acceptor.close(); + if (proxy != null) + proxy.stop(); + if (client != null) + client.stop(); + if (threadPool != null) + threadPool.shutdownNow(); + } + + @Test + public void testHandshake() throws Exception + { + ContentExchange exchange = new ContentExchange(true); + exchange.setURL("https://localhost:" + proxy.getPort()); + String method = HttpMethods.GET; + exchange.setMethod(method); + client.send(exchange); + Assert.assertTrue(proxy.awaitClient(5, TimeUnit.SECONDS)); + + final SSLSocket server = (SSLSocket)acceptor.accept(); + server.setUseClientMode(false); + + Future handshake = threadPool.submit(new Callable() + { + public Object call() throws Exception + { + server.startHandshake(); + return null; + } + }); + + // Client Hello + TLSRecord record = proxy.readFromClient(); + Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); + proxy.flushToServer(record); + + // Server Hello + Certificate + Server Done + record = proxy.readFromServer(); + Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); + proxy.flushToClient(record); + + // Client Key Exchange + record = proxy.readFromClient(); + Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); + proxy.flushToServer(record); + + // Change Cipher Spec + record = proxy.readFromClient(); + Assert.assertEquals(TLSRecord.Type.CHANGE_CIPHER_SPEC, record.getType()); + proxy.flushToServer(record); + + // Client Done + record = proxy.readFromClient(); + Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); + proxy.flushToServer(record); + + // Change Cipher Spec + record = proxy.readFromServer(); + Assert.assertEquals(TLSRecord.Type.CHANGE_CIPHER_SPEC, record.getType()); + proxy.flushToClient(record); + + // Server Done + record = proxy.readFromServer(); + Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); + proxy.flushToClient(record); + + Assert.assertNull(handshake.get(5, TimeUnit.SECONDS)); + + SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); + // Read request + BufferedReader reader = new BufferedReader(new InputStreamReader(server.getInputStream(), "UTF-8")); + String line = reader.readLine(); + Assert.assertTrue(line.startsWith(method)); + while (line.length() > 0) + line = reader.readLine(); + // Write response + OutputStream output = server.getOutputStream(); + output.write(("HTTP/1.1 200 OK\r\n" + + "Content-Length: 0\r\n" + + "\r\n").getBytes("UTF-8")); + output.flush(); + Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); + + Assert.assertEquals(HttpExchange.STATUS_COMPLETED, exchange.waitForDone()); + Assert.assertEquals(HttpStatus.OK_200, exchange.getResponseStatus()); + } +} diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/SslBytesServerTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/SslBytesServerTest.java new file mode 100644 index 00000000000..f6b919ed64f --- /dev/null +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/SslBytesServerTest.java @@ -0,0 +1,1227 @@ +package org.eclipse.jetty.client; + +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.lessThan; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.SocketTimeoutException; +import java.nio.channels.SocketChannel; +import java.util.Arrays; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLSocket; +import javax.servlet.ServletException; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.http.HttpParser; +import org.eclipse.jetty.io.AsyncEndPoint; +import org.eclipse.jetty.io.Buffers; +import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.nio.AsyncConnection; +import org.eclipse.jetty.io.nio.SslConnection; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.eclipse.jetty.server.ssl.SslSelectChannelConnector; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.junit.After; +import org.junit.Assert; +import org.junit.Assume; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +public class SslBytesServerTest extends SslBytesTest +{ + private final AtomicInteger sslHandles = new AtomicInteger(); + private final AtomicInteger httpParses = new AtomicInteger(); + private ExecutorService threadPool; + private Server server; + private SSLContext sslContext; + private SimpleProxy proxy; + + @Before + public void init() throws Exception + { + threadPool = Executors.newCachedThreadPool(); + server = new Server(); + + SslSelectChannelConnector connector = new SslSelectChannelConnector() + { + @Override + protected SslConnection newSslConnection(AsyncEndPoint endPoint, SSLEngine engine) + { + return new SslConnection(engine, endPoint) + { + @Override + public Connection handle() throws IOException + { + sslHandles.incrementAndGet(); + return super.handle(); + } + }; + } + + @Override + protected AsyncConnection newPlainConnection(SocketChannel channel, AsyncEndPoint endPoint) + { + return new org.eclipse.jetty.server.AsyncHttpConnection(this, endPoint, getServer()) + { + @Override + protected HttpParser newHttpParser(Buffers requestBuffers, EndPoint endPoint, HttpParser.EventHandler requestHandler) + { + return new HttpParser(requestBuffers, endPoint, requestHandler) + { + @Override + public int parseNext() throws IOException + { + httpParses.incrementAndGet(); + return super.parseNext(); + } + }; + } + }; + } + }; + +// connector.setPort(5870); + connector.setPort(0); + + File keyStore = MavenTestingUtils.getTestResourceFile("keystore"); + SslContextFactory cf = connector.getSslContextFactory(); + cf.setKeyStorePath(keyStore.getAbsolutePath()); + cf.setKeyStorePassword("storepwd"); + cf.setKeyManagerPassword("keypwd"); + server.addConnector(connector); + server.setHandler(new AbstractHandler() + { + public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException + { + request.setHandled(true); + String contentLength = request.getHeader("Content-Length"); + if (contentLength != null) + { + int length = Integer.parseInt(contentLength); + ServletInputStream input = request.getInputStream(); + for (int i = 0; i < length; ++i) + input.read(); + } + } + }); + server.start(); + int serverPort = connector.getLocalPort(); + + sslContext = cf.getSslContext(); + + proxy = new SimpleProxy(threadPool, "localhost", serverPort); + proxy.start(); + logger.debug(":{} <==> :{}", proxy.getPort(), serverPort); + } + + @After + public void destroy() throws Exception + { + if (proxy != null) + proxy.stop(); + if (server != null) + server.stop(); + if (threadPool != null) + threadPool.shutdownNow(); + } + + @Test + public void testHandshake() throws Exception + { + final SSLSocket client = newClient(); + + Future handshake = threadPool.submit(new Callable() + { + public Object call() throws Exception + { + client.startHandshake(); + return null; + } + }); + + // Client Hello + TLSRecord record = proxy.readFromClient(); + Assert.assertNotNull(record); + proxy.flushToServer(record); + + // Server Hello + Certificate + Server Done + record = proxy.readFromServer(); + Assert.assertNotNull(record); + proxy.flushToClient(record); + + // Client Key Exchange + record = proxy.readFromClient(); + Assert.assertNotNull(record); + proxy.flushToServer(record); + + // Change Cipher Spec + record = proxy.readFromClient(); + Assert.assertNotNull(record); + proxy.flushToServer(record); + + // Client Done + record = proxy.readFromClient(); + Assert.assertNotNull(record); + proxy.flushToServer(record); + + // Change Cipher Spec + record = proxy.readFromServer(); + Assert.assertNotNull(record); + proxy.flushToClient(record); + + // Server Done + record = proxy.readFromServer(); + Assert.assertNotNull(record); + proxy.flushToClient(record); + + Assert.assertNull(handshake.get(5, TimeUnit.SECONDS)); + + // Check that we did not spin + Assert.assertThat(sslHandles.get(), lessThan(20)); + Assert.assertThat(httpParses.get(), lessThan(50)); + + closeClient(client); + } + + @Test + public void testHandshakeWithSplitBoundary() throws Exception + { + final SSLSocket client = newClient(); + + Future handshake = threadPool.submit(new Callable() + { + public Object call() throws Exception + { + client.startHandshake(); + return null; + } + }); + + // Client Hello + TLSRecord record = proxy.readFromClient(); + byte[] bytes = record.getBytes(); + byte[] chunk1 = new byte[2 * bytes.length / 3]; + System.arraycopy(bytes, 0, chunk1, 0, chunk1.length); + byte[] chunk2 = new byte[bytes.length - chunk1.length]; + System.arraycopy(bytes, chunk1.length, chunk2, 0, chunk2.length); + proxy.flushToServer(chunk1); + TimeUnit.MILLISECONDS.sleep(100); + proxy.flushToServer(chunk2); + TimeUnit.MILLISECONDS.sleep(100); + + // Server Hello + Certificate + Server Done + record = proxy.readFromServer(); + proxy.flushToClient(record); + + // Client Key Exchange + record = proxy.readFromClient(); + bytes = record.getBytes(); + chunk1 = new byte[2 * bytes.length / 3]; + System.arraycopy(bytes, 0, chunk1, 0, chunk1.length); + chunk2 = new byte[bytes.length - chunk1.length]; + System.arraycopy(bytes, chunk1.length, chunk2, 0, chunk2.length); + proxy.flushToServer(chunk1); + TimeUnit.MILLISECONDS.sleep(100); + proxy.flushToServer(chunk2); + TimeUnit.MILLISECONDS.sleep(100); + + // Change Cipher Spec + record = proxy.readFromClient(); + bytes = record.getBytes(); + chunk1 = new byte[2 * bytes.length / 3]; + System.arraycopy(bytes, 0, chunk1, 0, chunk1.length); + chunk2 = new byte[bytes.length - chunk1.length]; + System.arraycopy(bytes, chunk1.length, chunk2, 0, chunk2.length); + proxy.flushToServer(chunk1); + TimeUnit.MILLISECONDS.sleep(100); + proxy.flushToServer(chunk2); + TimeUnit.MILLISECONDS.sleep(100); + + // Client Done + record = proxy.readFromClient(); + bytes = record.getBytes(); + chunk1 = new byte[2 * bytes.length / 3]; + System.arraycopy(bytes, 0, chunk1, 0, chunk1.length); + chunk2 = new byte[bytes.length - chunk1.length]; + System.arraycopy(bytes, chunk1.length, chunk2, 0, chunk2.length); + proxy.flushToServer(chunk1); + TimeUnit.MILLISECONDS.sleep(100); + proxy.flushToServer(chunk2); + TimeUnit.MILLISECONDS.sleep(100); + + // Change Cipher Spec + record = proxy.readFromServer(); + Assert.assertNotNull(record); + proxy.flushToClient(record); + + // Server Done + record = proxy.readFromServer(); + Assert.assertNotNull(record); + proxy.flushToClient(record); + + Assert.assertNull(handshake.get(5, TimeUnit.SECONDS)); + + // Check that we did not spin + Assert.assertThat(sslHandles.get(), lessThan(20)); + Assert.assertThat(httpParses.get(), lessThan(50)); + + client.close(); + + // Close Alert + record = proxy.readFromClient(); + bytes = record.getBytes(); + chunk1 = new byte[2 * bytes.length / 3]; + System.arraycopy(bytes, 0, chunk1, 0, chunk1.length); + chunk2 = new byte[bytes.length - chunk1.length]; + System.arraycopy(bytes, chunk1.length, chunk2, 0, chunk2.length); + proxy.flushToServer(chunk1); + TimeUnit.MILLISECONDS.sleep(100); + proxy.flushToServer(chunk2); + TimeUnit.MILLISECONDS.sleep(100); + // Socket close + record = proxy.readFromClient(); + Assert.assertNull(String.valueOf(record), record); + proxy.flushToServer(record); + + // Close Alert + record = proxy.readFromServer(); + proxy.flushToClient(record); + // Socket close + record = proxy.readFromServer(); + Assert.assertNull(String.valueOf(record), record); + proxy.flushToClient(record); + } + + @Test + public void testRequestResponse() throws Exception + { + final SSLSocket client = newClient(); + + SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); + client.startHandshake(); + Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); + + Future request = threadPool.submit(new Callable() + { + public Object call() throws Exception + { + OutputStream clientOutput = client.getOutputStream(); + clientOutput.write(("" + + "GET / HTTP/1.1\r\n" + + "Host: localhost\r\n" + + "\r\n").getBytes("UTF-8")); + clientOutput.flush(); + return null; + } + }); + + // Application data + TLSRecord record = proxy.readFromClient(); + proxy.flushToServer(record); + Assert.assertNull(request.get(5, TimeUnit.SECONDS)); + + // Application data + record = proxy.readFromServer(); + Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType()); + proxy.flushToClient(record); + + BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream(), "UTF-8")); + String line = reader.readLine(); + Assert.assertNotNull(line); + Assert.assertTrue(line.startsWith("HTTP/1.1 200 ")); + while ((line = reader.readLine()) != null) + { + if (line.trim().length() == 0) + break; + } + + // Check that we did not spin + Assert.assertThat(sslHandles.get(), lessThan(20)); + Assert.assertThat(httpParses.get(), lessThan(50)); + + closeClient(client); + } + + @Test + public void testHandshakeAndRequestOneByteAtATime() throws Exception + { + final SSLSocket client = newClient(); + + Future handshake = threadPool.submit(new Callable() + { + public Object call() throws Exception + { + client.startHandshake(); + return null; + } + }); + + // Client Hello + TLSRecord record = proxy.readFromClient(); + for (byte b : record.getBytes()) + { + proxy.flushToServer(b); + TimeUnit.MILLISECONDS.sleep(50); + } + + // Server Hello + Certificate + Server Done + record = proxy.readFromServer(); + proxy.flushToClient(record); + + // Client Key Exchange + record = proxy.readFromClient(); + for (byte b : record.getBytes()) + { + proxy.flushToServer(b); + TimeUnit.MILLISECONDS.sleep(50); + } + + // Change Cipher Spec + record = proxy.readFromClient(); + for (byte b : record.getBytes()) + { + proxy.flushToServer(b); + TimeUnit.MILLISECONDS.sleep(50); + } + + // Client Done + record = proxy.readFromClient(); + for (byte b : record.getBytes()) + { + proxy.flushToServer(b); + TimeUnit.MILLISECONDS.sleep(50); + } + + // Change Cipher Spec + record = proxy.readFromServer(); + proxy.flushToClient(record); + + // Server Done + record = proxy.readFromServer(); + proxy.flushToClient(record); + + Assert.assertNull(handshake.get(5, TimeUnit.SECONDS)); + + Future request = threadPool.submit(new Callable() + { + public Object call() throws Exception + { + OutputStream clientOutput = client.getOutputStream(); + clientOutput.write(("" + + "GET / HTTP/1.1\r\n" + + "Host: localhost\r\n" + + "\r\n").getBytes("UTF-8")); + clientOutput.flush(); + return null; + } + }); + + // Application data + record = proxy.readFromClient(); + for (byte b : record.getBytes()) + { + proxy.flushToServer(b); + TimeUnit.MILLISECONDS.sleep(50); + } + Assert.assertNull(request.get(5, TimeUnit.SECONDS)); + + // Application data + record = proxy.readFromServer(); + Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType()); + proxy.flushToClient(record); + + BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream(), "UTF-8")); + String line = reader.readLine(); + Assert.assertNotNull(line); + Assert.assertTrue(line.startsWith("HTTP/1.1 200 ")); + while ((line = reader.readLine()) != null) + { + if (line.trim().length() == 0) + break; + } + + // Check that we did not spin + Assert.assertThat(sslHandles.get(), lessThan(750)); + Assert.assertThat(httpParses.get(), lessThan(150)); + + client.close(); + + // Close Alert + record = proxy.readFromClient(); + for (byte b : record.getBytes()) + { + proxy.flushToServer(b); + TimeUnit.MILLISECONDS.sleep(50); + } + // Socket close + record = proxy.readFromClient(); + Assert.assertNull(String.valueOf(record), record); + proxy.flushToServer(record); + + // Close Alert + record = proxy.readFromServer(); + proxy.flushToClient(record); + // Socket close + record = proxy.readFromServer(); + Assert.assertNull(String.valueOf(record), record); + proxy.flushToClient(record); + } + + /** + * TODO + * Currently this test does not pass. + * The problem is a mix of Java not being able to perform SSL half closes + * (but SSL supporting it), and the current implementation in Jetty. + * See the test below, that passes and whose only difference is that we + * delay the output shutdown from the client. + * + * @throws Exception if the test fails + */ + @Ignore + @Test + public void testRequestWithCloseAlertAndShutdown() throws Exception + { + final SSLSocket client = newClient(); + + SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); + client.startHandshake(); + Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); + + Future request = threadPool.submit(new Callable() + { + public Object call() throws Exception + { + OutputStream clientOutput = client.getOutputStream(); + clientOutput.write(("" + + "GET / HTTP/1.1\r\n" + + "Host: localhost\r\n" + + "\r\n").getBytes("UTF-8")); + clientOutput.flush(); + return null; + } + }); + + // Application data + TLSRecord record = proxy.readFromClient(); + proxy.flushToServer(record); + Assert.assertNull(request.get(5, TimeUnit.SECONDS)); + + client.close(); + + // Close Alert + record = proxy.readFromClient(); + proxy.flushToServer(record); + // Socket close + record = proxy.readFromClient(); + Assert.assertNull(String.valueOf(record), record); + proxy.flushToServer(record); + + // Expect response from server + // SSLSocket is limited and we cannot read the response, but we make sure + // it is application data and not a close alert + record = proxy.readFromServer(); + Assert.assertNotNull(record); + Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType()); + proxy.flushToClient(record); + + // Close Alert + record = proxy.readFromServer(); + Assert.assertNotNull(record); + Assert.assertEquals(TLSRecord.Type.ALERT, record.getType()); + // We can't forward to the client, its socket is already closed + + // Socket close + record = proxy.readFromClient(); + Assert.assertNull(String.valueOf(record), record); + proxy.flushToServer(record); + + // Socket close + record = proxy.readFromServer(); + Assert.assertNull(String.valueOf(record), record); + proxy.flushToClient(record); + } + + @Test + public void testRequestWithCloseAlert() throws Exception + { + final SSLSocket client = newClient(); + + SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); + client.startHandshake(); + Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); + + Future request = threadPool.submit(new Callable() + { + public Object call() throws Exception + { + OutputStream clientOutput = client.getOutputStream(); + clientOutput.write(("" + + "GET / HTTP/1.1\r\n" + + "Host: localhost\r\n" + + "\r\n").getBytes("UTF-8")); + clientOutput.flush(); + return null; + } + }); + + // Application data + TLSRecord record = proxy.readFromClient(); + proxy.flushToServer(record); + Assert.assertNull(request.get(5, TimeUnit.SECONDS)); + + client.close(); + + // Close Alert + record = proxy.readFromClient(); + proxy.flushToServer(record); + + // Do not close the raw socket yet + + // Expect response from server + // SSLSocket is limited and we cannot read the response, but we make sure + // it is application data and not a close alert + record = proxy.readFromServer(); + Assert.assertNotNull(record); + Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType()); + proxy.flushToClient(record); + + // Close Alert + record = proxy.readFromServer(); + Assert.assertNotNull(record); + Assert.assertEquals(TLSRecord.Type.ALERT, record.getType()); + // We can't forward to the client, its socket is already closed + + // Check that we did not spin + Assert.assertThat(sslHandles.get(), lessThan(20)); + Assert.assertThat(httpParses.get(), lessThan(50)); + + // Socket close + record = proxy.readFromClient(); + Assert.assertNull(String.valueOf(record), record); + proxy.flushToServer(record); + + // Socket close + record = proxy.readFromServer(); + Assert.assertNull(String.valueOf(record), record); + proxy.flushToClient(record); + } + + @Test + public void testRequestWithRawClose() throws Exception + { + final SSLSocket client = newClient(); + + SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); + client.startHandshake(); + Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); + + Future request = threadPool.submit(new Callable() + { + public Object call() throws Exception + { + OutputStream clientOutput = client.getOutputStream(); + clientOutput.write(("" + + "GET / HTTP/1.1\r\n" + + "Host: localhost\r\n" + + "\r\n").getBytes("UTF-8")); + clientOutput.flush(); + return null; + } + }); + + // Application data + TLSRecord record = proxy.readFromClient(); + proxy.flushToServer(record); + Assert.assertNull(request.get(5, TimeUnit.SECONDS)); + + // Application data + record = proxy.readFromServer(); + Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType()); + proxy.flushToClient(record); + + // Close the raw socket, this generates a truncation attack + proxy.flushToServer((TLSRecord)null); + + // Expect raw close from server + record = proxy.readFromServer(); + Assert.assertNull(String.valueOf(record), record); + proxy.flushToClient(record); + + // Check that we did not spin + Assert.assertThat(sslHandles.get(), lessThan(20)); + Assert.assertThat(httpParses.get(), lessThan(50)); + + client.close(); + } + + @Test + public void testRequestWithCloseAlertWithSplitBoundary() throws Exception + { + final SSLSocket client = newClient(); + + SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); + client.startHandshake(); + Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); + + Future request = threadPool.submit(new Callable() + { + public Object call() throws Exception + { + OutputStream clientOutput = client.getOutputStream(); + clientOutput.write(("" + + "GET / HTTP/1.1\r\n" + + "Host: localhost\r\n" + + "\r\n").getBytes("UTF-8")); + clientOutput.flush(); + return null; + } + }); + + // Application data + TLSRecord dataRecord = proxy.readFromClient(); + Assert.assertNull(request.get(5, TimeUnit.SECONDS)); + + client.close(); + + // Close Alert + TLSRecord closeRecord = proxy.readFromClient(); + + // Send request and half of the close alert bytes + byte[] dataBytes = dataRecord.getBytes(); + byte[] closeBytes = closeRecord.getBytes(); + byte[] bytes = new byte[dataBytes.length + closeBytes.length / 2]; + System.arraycopy(dataBytes, 0, bytes, 0, dataBytes.length); + System.arraycopy(closeBytes, 0, bytes, dataBytes.length, closeBytes.length / 2); + proxy.flushToServer(bytes); + + TimeUnit.MILLISECONDS.sleep(100); + + bytes = new byte[closeBytes.length - closeBytes.length / 2]; + System.arraycopy(closeBytes, closeBytes.length / 2, bytes, 0, bytes.length); + proxy.flushToServer(bytes); + + // Do not close the raw socket yet + + // Expect response from server + // SSLSocket is limited and we cannot read the response, but we make sure + // it is application data and not a close alert + TLSRecord record = proxy.readFromServer(); + Assert.assertNotNull(record); + Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType()); + proxy.flushToClient(record); + + // Close Alert + record = proxy.readFromServer(); + Assert.assertNotNull(record); + Assert.assertEquals(TLSRecord.Type.ALERT, record.getType()); + // We can't forward to the client, its socket is already closed + + // Check that we did not spin + Assert.assertThat(sslHandles.get(), lessThan(20)); + Assert.assertThat(httpParses.get(), lessThan(50)); + + // Socket close + record = proxy.readFromClient(); + Assert.assertNull(String.valueOf(record), record); + proxy.flushToServer(record); + + // Socket close + record = proxy.readFromServer(); + Assert.assertNull(String.valueOf(record), record); + proxy.flushToClient(record); + } + + @Test + public void testRequestWithContentWithSplitBoundary() throws Exception + { + final SSLSocket client = newClient(); + + SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); + client.startHandshake(); + Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); + + final String content = "0123456789ABCDEF"; + + Future request = threadPool.submit(new Callable() + { + public Object call() throws Exception + { + OutputStream clientOutput = client.getOutputStream(); + clientOutput.write(("" + + "POST / HTTP/1.1\r\n" + + "Host: localhost\r\n" + + "Content-Type: text/plain\r\n" + + "Content-Length: " + content.length() + "\r\n" + + "\r\n" + + content).getBytes("UTF-8")); + clientOutput.flush(); + return null; + } + }); + + // Application data + TLSRecord record = proxy.readFromClient(); + Assert.assertNull(request.get(5, TimeUnit.SECONDS)); + byte[] chunk1 = new byte[2 * record.getBytes().length / 3]; + System.arraycopy(record.getBytes(), 0, chunk1, 0, chunk1.length); + proxy.flushToServer(chunk1); + + TimeUnit.MILLISECONDS.sleep(100); + + byte[] chunk2 = new byte[record.getBytes().length - chunk1.length]; + System.arraycopy(record.getBytes(), chunk1.length, chunk2, 0, chunk2.length); + proxy.flushToServer(chunk2); + + record = proxy.readFromServer(); + Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType()); + proxy.flushToClient(record); + + BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream(), "UTF-8")); + String line = reader.readLine(); + Assert.assertNotNull(line); + Assert.assertTrue(line.startsWith("HTTP/1.1 200 ")); + while ((line = reader.readLine()) != null) + { + if (line.trim().length() == 0) + break; + } + + // Check that we did not spin + Assert.assertThat(sslHandles.get(), lessThan(20)); + Assert.assertThat(httpParses.get(), lessThan(50)); + + closeClient(client); + } + + @Test + public void testRequestWithBigContentWithSplitBoundary() throws Exception + { + final SSLSocket client = newClient(); + + SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); + client.startHandshake(); + Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); + + // Use a content that is larger than the TLS record which is 2^14 (around 16k) + byte[] data = new byte[128 * 1024]; + Arrays.fill(data, (byte)'X'); + final String content = new String(data, "UTF-8"); + + Future request = threadPool.submit(new Callable() + { + public Object call() throws Exception + { + OutputStream clientOutput = client.getOutputStream(); + clientOutput.write(("" + + "POST / HTTP/1.1\r\n" + + "Host: localhost\r\n" + + "Content-Type: text/plain\r\n" + + "Content-Length: " + content.length() + "\r\n" + + "\r\n" + + content).getBytes("UTF-8")); + clientOutput.flush(); + return null; + } + }); + + // Nine TLSRecords will be generated for the request + for (int i = 0; i < 9; ++i) + { + // Application data + TLSRecord record = proxy.readFromClient(); + byte[] bytes = record.getBytes(); + byte[] chunk1 = new byte[2 * bytes.length / 3]; + System.arraycopy(bytes, 0, chunk1, 0, chunk1.length); + byte[] chunk2 = new byte[bytes.length - chunk1.length]; + System.arraycopy(bytes, chunk1.length, chunk2, 0, chunk2.length); + proxy.flushToServer(chunk1); + TimeUnit.MILLISECONDS.sleep(100); + proxy.flushToServer(chunk2); + TimeUnit.MILLISECONDS.sleep(100); + } + + // Check that we did not spin + Assert.assertThat(sslHandles.get(), lessThan(20)); + Assert.assertThat(httpParses.get(), lessThan(150)); + + Assert.assertNull(request.get(5, TimeUnit.SECONDS)); + + TLSRecord record = proxy.readFromServer(); + Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType()); + proxy.flushToClient(record); + + BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream(), "UTF-8")); + String line = reader.readLine(); + Assert.assertNotNull(line); + Assert.assertTrue(line.startsWith("HTTP/1.1 200 ")); + while ((line = reader.readLine()) != null) + { + if (line.trim().length() == 0) + break; + } + + // Check that we did not spin + Assert.assertThat(sslHandles.get(), lessThan(20)); + Assert.assertThat(httpParses.get(), lessThan(150)); + + closeClient(client); + } + + @Test + public void testRequestWithBigContentWithRenegotiationInMiddleOfContent() throws Exception + { + assumeJavaVersionSupportsTLSRenegotiations(); + + final SSLSocket client = newClient(); + final OutputStream clientOutput = client.getOutputStream(); + + SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); + client.startHandshake(); + Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); + + // Use a content that is larger than the TLS record which is 2^14 (around 16k) + byte[] data1 = new byte[80 * 1024]; + Arrays.fill(data1, (byte)'X'); + String content1 = new String(data1, "UTF-8"); + byte[] data2 = new byte[48 * 1024]; + Arrays.fill(data2, (byte)'Y'); + final String content2 = new String(data2, "UTF-8"); + + // Write only part of the body + automaticProxyFlow = proxy.startAutomaticFlow(); + clientOutput.write(("" + + "POST / HTTP/1.1\r\n" + + "Host: localhost\r\n" + + "Content-Type: text/plain\r\n" + + "Content-Length: " + (content1.length() + content2.length()) + "\r\n" + + "\r\n" + + content1).getBytes("UTF-8")); + clientOutput.flush(); + Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); + + // Renegotiate + Future renegotiation = threadPool.submit(new Callable() + { + public Object call() throws Exception + { + client.startHandshake(); + return null; + } + }); + + // Renegotiation Handshake + TLSRecord record = proxy.readFromClient(); + Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); + proxy.flushToServer(record); + + // Renegotiation Handshake + record = proxy.readFromServer(); + Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); + proxy.flushToClient(record); + + // Renegotiation Change Cipher + record = proxy.readFromServer(); + Assert.assertEquals(TLSRecord.Type.CHANGE_CIPHER_SPEC, record.getType()); + proxy.flushToClient(record); + + // Renegotiation Handshake + record = proxy.readFromServer(); + Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); + proxy.flushToClient(record); + + // Trigger a read to have the client write the final renegotiation steps + client.setSoTimeout(100); + try + { + client.getInputStream().read(); + Assert.fail(); + } + catch (SocketTimeoutException x) + { + // Expected + } + + // Renegotiation Change Cipher + record = proxy.readFromClient(); + Assert.assertEquals(TLSRecord.Type.CHANGE_CIPHER_SPEC, record.getType()); + proxy.flushToServer(record); + + // Renegotiation Handshake + record = proxy.readFromClient(); + Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); + proxy.flushToServer(record); + + Assert.assertNull(renegotiation.get(5, TimeUnit.SECONDS)); + + // Write the rest of the request + Future request = threadPool.submit(new Callable() + { + public Object call() throws Exception + { + clientOutput.write(content2.getBytes("UTF-8")); + clientOutput.flush(); + return null; + } + }); + + // Three TLSRecords will be generated for the remainder of the content + for (int i = 0; i < 3; ++i) + { + // Application data + record = proxy.readFromClient(); + proxy.flushToServer(record); + } + + Assert.assertNull(request.get(5, TimeUnit.SECONDS)); + + // Read response + // Application Data + record = proxy.readFromServer(); + Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType()); + proxy.flushToClient(record); + + BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream(), "UTF-8")); + String line = reader.readLine(); + Assert.assertNotNull(line); + Assert.assertTrue(line.startsWith("HTTP/1.1 200 ")); + while ((line = reader.readLine()) != null) + { + if (line.trim().length() == 0) + break; + } + + // Check that we did not spin + Assert.assertThat(sslHandles.get(), lessThan(20)); + Assert.assertThat(httpParses.get(), lessThan(50)); + + closeClient(client); + } + + @Test + public void testRequestWithBigContentWithRenegotiationInMiddleOfContentWithSplitBoundary() throws Exception + { + assumeJavaVersionSupportsTLSRenegotiations(); + + final SSLSocket client = newClient(); + final OutputStream clientOutput = client.getOutputStream(); + + SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); + client.startHandshake(); + Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); + + // Use a content that is larger than the TLS record which is 2^14 (around 16k) + byte[] data1 = new byte[80 * 1024]; + Arrays.fill(data1, (byte)'X'); + String content1 = new String(data1, "UTF-8"); + byte[] data2 = new byte[48 * 1024]; + Arrays.fill(data2, (byte)'Y'); + final String content2 = new String(data2, "UTF-8"); + + // Write only part of the body + automaticProxyFlow = proxy.startAutomaticFlow(); + clientOutput.write(("" + + "POST / HTTP/1.1\r\n" + + "Host: localhost\r\n" + + "Content-Type: text/plain\r\n" + + "Content-Length: " + (content1.length() + content2.length()) + "\r\n" + + "\r\n" + + content1).getBytes("UTF-8")); + clientOutput.flush(); + Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); + + // Renegotiate + Future renegotiation = threadPool.submit(new Callable() + { + public Object call() throws Exception + { + client.startHandshake(); + return null; + } + }); + + // Renegotiation Handshake + TLSRecord record = proxy.readFromClient(); + Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); + byte[] bytes = record.getBytes(); + byte[] chunk1 = new byte[2 * bytes.length / 3]; + System.arraycopy(bytes, 0, chunk1, 0, chunk1.length); + byte[] chunk2 = new byte[bytes.length - chunk1.length]; + System.arraycopy(bytes, chunk1.length, chunk2, 0, chunk2.length); + proxy.flushToServer(chunk1); + TimeUnit.MILLISECONDS.sleep(100); + proxy.flushToServer(chunk2); + TimeUnit.MILLISECONDS.sleep(100); + + // Renegotiation Handshake + record = proxy.readFromServer(); + Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); + proxy.flushToClient(record); + + // Renegotiation Change Cipher + record = proxy.readFromServer(); + Assert.assertEquals(TLSRecord.Type.CHANGE_CIPHER_SPEC, record.getType()); + proxy.flushToClient(record); + + // Renegotiation Handshake + record = proxy.readFromServer(); + Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); + proxy.flushToClient(record); + + // Trigger a read to have the client write the final renegotiation steps + client.setSoTimeout(100); + try + { + client.getInputStream().read(); + Assert.fail(); + } + catch (SocketTimeoutException x) + { + // Expected + } + + // Renegotiation Change Cipher + record = proxy.readFromClient(); + Assert.assertEquals(TLSRecord.Type.CHANGE_CIPHER_SPEC, record.getType()); + bytes = record.getBytes(); + chunk1 = new byte[2 * bytes.length / 3]; + System.arraycopy(bytes, 0, chunk1, 0, chunk1.length); + chunk2 = new byte[bytes.length - chunk1.length]; + System.arraycopy(bytes, chunk1.length, chunk2, 0, chunk2.length); + proxy.flushToServer(chunk1); + TimeUnit.MILLISECONDS.sleep(100); + proxy.flushToServer(chunk2); + TimeUnit.MILLISECONDS.sleep(100); + + // Renegotiation Handshake + record = proxy.readFromClient(); + Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); + bytes = record.getBytes(); + chunk1 = new byte[2 * bytes.length / 3]; + System.arraycopy(bytes, 0, chunk1, 0, chunk1.length); + chunk2 = new byte[bytes.length - chunk1.length]; + System.arraycopy(bytes, chunk1.length, chunk2, 0, chunk2.length); + proxy.flushToServer(chunk1); + TimeUnit.MILLISECONDS.sleep(100); + // Do not write the second chunk now, but merge it with content, see below + + Assert.assertNull(renegotiation.get(5, TimeUnit.SECONDS)); + + // Write the rest of the request + Future request = threadPool.submit(new Callable() + { + public Object call() throws Exception + { + clientOutput.write(content2.getBytes("UTF-8")); + clientOutput.flush(); + return null; + } + }); + + // Three TLSRecords will be generated for the remainder of the content + // Merge the last chunk of the renegotiation with the first data record + record = proxy.readFromClient(); + Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType()); + byte[] dataBytes = record.getBytes(); + byte[] mergedBytes = new byte[chunk2.length + dataBytes.length]; + System.arraycopy(chunk2, 0, mergedBytes, 0, chunk2.length); + System.arraycopy(dataBytes, 0, mergedBytes, chunk2.length, dataBytes.length); + proxy.flushToServer(mergedBytes); + // Write the remaining 2 TLS records + for (int i = 0; i < 2; ++i) + { + // Application data + record = proxy.readFromClient(); + Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType()); + proxy.flushToServer(record); + } + + Assert.assertNull(request.get(5, TimeUnit.SECONDS)); + + // Read response + // Application Data + record = proxy.readFromServer(); + Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType()); + proxy.flushToClient(record); + + BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream(), "UTF-8")); + String line = reader.readLine(); + Assert.assertNotNull(line); + Assert.assertTrue(line.startsWith("HTTP/1.1 200 ")); + while ((line = reader.readLine()) != null) + { + if (line.trim().length() == 0) + break; + } + + // Check that we did not spin + Assert.assertThat(sslHandles.get(), lessThan(20)); + Assert.assertThat(httpParses.get(), lessThan(100)); + + closeClient(client); + } + + private void assumeJavaVersionSupportsTLSRenegotiations() + { + // Due to a security bug, TLS renegotiations were disabled in JDK 1.6.0_19-21 + // so we check the java version in order to avoid to fail the test. + String javaVersion = System.getProperty("java.version"); + Pattern regexp = Pattern.compile("1\\.6\\.0_(\\d{2})"); + Matcher matcher = regexp.matcher(javaVersion); + if (matcher.matches()) + { + String nano = matcher.group(1); + Assume.assumeThat(Integer.parseInt(nano), greaterThan(21)); + } + } + + private SSLSocket newClient() throws IOException, InterruptedException + { + SSLSocket client = (SSLSocket)sslContext.getSocketFactory().createSocket("localhost", proxy.getPort()); + client.setUseClientMode(true); + Assert.assertTrue(proxy.awaitClient(5, TimeUnit.SECONDS)); + return client; + } + + private void closeClient(SSLSocket client) throws IOException + { + client.close(); + + // Close Alert + TLSRecord record = proxy.readFromClient(); + proxy.flushToServer(record); + // Socket close + record = proxy.readFromClient(); + Assert.assertNull(String.valueOf(record), record); + proxy.flushToServer(record); + + // Close Alert + record = proxy.readFromServer(); + proxy.flushToClient(record); + // Socket close + record = proxy.readFromServer(); + Assert.assertNull(String.valueOf(record), record); + proxy.flushToClient(record); + } + +} diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/SslBytesTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/SslBytesTest.java new file mode 100644 index 00000000000..4ad5c68c56f --- /dev/null +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/SslBytesTest.java @@ -0,0 +1,337 @@ +package org.eclipse.jetty.client; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InterruptedIOException; +import java.io.OutputStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketTimeoutException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; +import org.junit.Assert; + +public abstract class SslBytesTest +{ + protected final Logger logger = Log.getLogger(getClass()); + + public static class TLSRecord + { + private final SslBytesServerTest.TLSRecord.Type type; + private final byte[] bytes; + + public TLSRecord(SslBytesServerTest.TLSRecord.Type type, byte[] bytes) + { + this.type = type; + this.bytes = bytes; + } + + public SslBytesServerTest.TLSRecord.Type getType() + { + return type; + } + + public byte[] getBytes() + { + return bytes; + } + + @Override + public String toString() + { + return "TLSRecord [" + type + "] " + bytes.length + " bytes"; + } + + public enum Type + { + CHANGE_CIPHER_SPEC(20), ALERT(21), HANDSHAKE(22), APPLICATION(23); + + private int code; + + private Type(int code) + { + this.code = code; + SslBytesServerTest.TLSRecord.Type.Mapper.codes.put(this.code, this); + } + + public static SslBytesServerTest.TLSRecord.Type from(int code) + { + SslBytesServerTest.TLSRecord.Type result = SslBytesServerTest.TLSRecord.Type.Mapper.codes.get(code); + if (result == null) + throw new IllegalArgumentException("Invalid TLSRecord.Type " + code); + return result; + } + + private static class Mapper + { + private static final Map codes = new HashMap(); + } + } + } + + public class SimpleProxy implements Runnable + { + private final CountDownLatch latch = new CountDownLatch(1); + private final ExecutorService threadPool; + private final String serverHost; + private final int serverPort; + private volatile ServerSocket serverSocket; + private volatile Socket server; + private volatile Socket client; + + public SimpleProxy(ExecutorService threadPool, String serverHost, int serverPort) + { + this.threadPool = threadPool; + this.serverHost = serverHost; + this.serverPort = serverPort; + } + + public void start() throws Exception + { +// serverSocket = new ServerSocket(5871); + serverSocket = new ServerSocket(0); + Thread acceptor = new Thread(this); + acceptor.start(); + server = new Socket(serverHost, serverPort); + } + + public void stop() throws Exception + { + serverSocket.close(); + } + + public void run() + { + try + { + client = serverSocket.accept(); + latch.countDown(); + } + catch (IOException x) + { + x.printStackTrace(); + } + } + + public int getPort() + { + return serverSocket.getLocalPort(); + } + + public TLSRecord readFromClient() throws IOException + { + TLSRecord record = read(client); + logger.debug("C --> P {}", record); + return record; + } + + private TLSRecord read(Socket socket) throws IOException + { + InputStream input = socket.getInputStream(); + int first = -2; + while (true) + { + try + { + socket.setSoTimeout(500); + first = input.read(); + break; + } + catch (SocketTimeoutException x) + { + if (Thread.currentThread().isInterrupted()) + break; + } + } + if (first == -2) + throw new InterruptedIOException(); + else if (first == -1) + return null; + + if (first >= 0x80) + { + // SSLv2 Record + int hiLength = first & 0x3F; + int loLength = input.read(); + int length = (hiLength << 8) + loLength; + byte[] bytes = new byte[2 + length]; + bytes[0] = (byte)first; + bytes[1] = (byte)loLength; + return read(TLSRecord.Type.HANDSHAKE, input, bytes, 2, length); + } + else + { + // TLS Record + int major = input.read(); + int minor = input.read(); + int hiLength = input.read(); + int loLength = input.read(); + int length = (hiLength << 8) + loLength; + byte[] bytes = new byte[1 + 2 + 2 + length]; + bytes[0] = (byte)first; + bytes[1] = (byte)major; + bytes[2] = (byte)minor; + bytes[3] = (byte)hiLength; + bytes[4] = (byte)loLength; + return read(TLSRecord.Type.from(first), input, bytes, 5, length); + } + } + + private TLSRecord read(SslBytesServerTest.TLSRecord.Type type, InputStream input, byte[] bytes, int offset, int length) throws IOException + { + while (length > 0) + { + int read = input.read(bytes, offset, length); + if (read < 0) + throw new EOFException(); + offset += read; + length -= read; + } + return new TLSRecord(type, bytes); + } + + public void flushToServer(TLSRecord record) throws IOException + { + if (record == null) + { + server.shutdownOutput(); + if (client.isOutputShutdown()) + { + client.close(); + server.close(); + } + } + else + { + flush(server, record.getBytes()); + } + } + + public void flushToServer(byte... bytes) throws IOException + { + flush(server, bytes); + } + + private void flush(Socket socket, byte... bytes) throws IOException + { + OutputStream output = socket.getOutputStream(); + output.write(bytes); + output.flush(); + } + + public TLSRecord readFromServer() throws IOException + { + TLSRecord record = read(server); + logger.debug("P <-- S {}", record); + return record; + } + + public void flushToClient(TLSRecord record) throws IOException + { + if (record == null) + { + client.shutdownOutput(); + if (server.isOutputShutdown()) + { + server.close(); + client.close(); + } + } + else + { + flush(client, record.getBytes()); + } + } + + public SslBytesServerTest.SimpleProxy.AutomaticFlow startAutomaticFlow() throws InterruptedException + { + final CountDownLatch startLatch = new CountDownLatch(2); + final CountDownLatch stopLatch = new CountDownLatch(2); + Future clientToServer = threadPool.submit(new Callable() + { + public Object call() throws Exception + { + startLatch.countDown(); + logger.debug("Automatic flow C --> S started"); + try + { + while (true) + { + flushToServer(readFromClient()); + } + } + catch (InterruptedIOException x) + { + return null; + } + finally + { + stopLatch.countDown(); + logger.debug("Automatic flow C --> S finished"); + } + } + }); + Future serverToClient = threadPool.submit(new Callable() + { + public Object call() throws Exception + { + startLatch.countDown(); + logger.debug("Automatic flow C <-- S started"); + try + { + while (true) + { + flushToClient(readFromServer()); + } + } + catch (InterruptedIOException x) + { + return null; + } + finally + { + stopLatch.countDown(); + logger.debug("Automatic flow C <-- S finished"); + } + } + }); + Assert.assertTrue(startLatch.await(5, TimeUnit.SECONDS)); + return new SslBytesServerTest.SimpleProxy.AutomaticFlow(stopLatch, clientToServer, serverToClient); + } + + public boolean awaitClient(int time, TimeUnit unit) throws InterruptedException + { + return latch.await(time, unit); + } + + public class AutomaticFlow + { + private final CountDownLatch stopLatch; + private final Future clientToServer; + private final Future serverToClient; + + public AutomaticFlow(CountDownLatch stopLatch, Future clientToServer, Future serverToClient) + { + this.stopLatch = stopLatch; + this.clientToServer = clientToServer; + this.serverToClient = serverToClient; + } + + public boolean stop(long time, TimeUnit unit) throws InterruptedException + { + clientToServer.cancel(true); + serverToClient.cancel(true); + return stopLatch.await(time, unit); + } + } + } +} diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/SslCertSecuredExchangeTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/SslCertSecuredExchangeTest.java index 5c41ee335d5..0b07a42403d 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/SslCertSecuredExchangeTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/SslCertSecuredExchangeTest.java @@ -20,9 +20,6 @@ import java.util.Set; import javax.security.auth.Subject; -import org.eclipse.jetty.http.security.Constraint; -import org.eclipse.jetty.http.security.Credential; -import org.eclipse.jetty.http.ssl.SslContextFactory; import org.eclipse.jetty.security.ConstraintMapping; import org.eclipse.jetty.security.ConstraintSecurityHandler; import org.eclipse.jetty.security.IdentityService; @@ -38,6 +35,9 @@ import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.security.Constraint; +import org.eclipse.jetty.util.security.Credential; +import org.eclipse.jetty.util.ssl.SslContextFactory; public class SslCertSecuredExchangeTest extends ContentExchangeTest { diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/SslContentExchangeTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/SslContentExchangeTest.java index 0a97a9e64e3..510ca711563 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/SslContentExchangeTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/SslContentExchangeTest.java @@ -15,7 +15,6 @@ package org.eclipse.jetty.client; import java.io.File; -import org.eclipse.jetty.http.ssl.SslContextFactory; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.HandlerCollection; @@ -24,6 +23,7 @@ import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.ssl.SslContextFactory; public class SslContentExchangeTest extends ContentExchangeTest diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/SslHttpExchangeTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/SslHttpExchangeTest.java index 8ec13c1ef21..2493a1b2f5c 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/SslHttpExchangeTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/SslHttpExchangeTest.java @@ -13,16 +13,14 @@ package org.eclipse.jetty.client; -import static org.hamcrest.Matchers.*; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.not; import org.eclipse.jetty.client.helperClasses.ServerAndClientCreator; import org.eclipse.jetty.client.helperClasses.SslServerAndClientCreator; import org.eclipse.jetty.server.Connector; -import org.eclipse.jetty.toolchain.test.OS; -import org.eclipse.jetty.toolchain.test.Stress; import org.junit.Assume; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; /** @@ -72,9 +70,6 @@ public class SslHttpExchangeTest extends HttpExchangeTest @Override public void testPerf() throws Exception { - // TODO needs to be further investigated - Assume.assumeTrue(!OS.IS_OSX || Stress.isEnabled()); - // TODO Resolve problems on IBM JVM https://bugs.eclipse.org/bugs/show_bug.cgi?id=304532 IgnoreTestOnBuggyIBM(); super.testPerf(); diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/SslSecuredContentExchangeTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/SslSecuredContentExchangeTest.java index f85641998f8..30112af7305 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/SslSecuredContentExchangeTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/SslSecuredContentExchangeTest.java @@ -19,8 +19,6 @@ import java.util.HashSet; import java.util.Set; import org.eclipse.jetty.client.security.Realm; -import org.eclipse.jetty.http.security.Constraint; -import org.eclipse.jetty.http.ssl.SslContextFactory; import org.eclipse.jetty.security.ConstraintMapping; import org.eclipse.jetty.security.ConstraintSecurityHandler; import org.eclipse.jetty.security.HashLoginService; @@ -34,6 +32,8 @@ import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.security.Constraint; +import org.eclipse.jetty.util.ssl.SslContextFactory; public class SslSecuredContentExchangeTest extends ContentExchangeTest diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/SslSecuredErrorStatusTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/SslSecuredErrorStatusTest.java index 4b4c4dcf19f..91c5da40963 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/SslSecuredErrorStatusTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/SslSecuredErrorStatusTest.java @@ -20,7 +20,6 @@ import java.util.Set; import org.eclipse.jetty.client.security.Realm; import org.eclipse.jetty.http.HttpStatus; -import org.eclipse.jetty.http.security.Constraint; import org.eclipse.jetty.security.ConstraintMapping; import org.eclipse.jetty.security.ConstraintSecurityHandler; import org.eclipse.jetty.security.HashLoginService; @@ -34,6 +33,7 @@ import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.security.Constraint; import org.junit.Test; /* ------------------------------------------------------------ */ diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/SslSecurityListenerTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/SslSecurityListenerTest.java index 9a1d593dc45..1e9c18ead65 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/SslSecurityListenerTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/SslSecurityListenerTest.java @@ -34,8 +34,6 @@ import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.client.security.HashRealmResolver; import org.eclipse.jetty.client.security.Realm; import org.eclipse.jetty.http.HttpMethods; -import org.eclipse.jetty.http.security.Constraint; -import org.eclipse.jetty.http.ssl.SslContextFactory; import org.eclipse.jetty.io.EofException; import org.eclipse.jetty.security.ConstraintMapping; import org.eclipse.jetty.security.ConstraintSecurityHandler; @@ -51,6 +49,8 @@ import org.eclipse.jetty.server.ssl.SslSocketConnector; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.security.Constraint; +import org.eclipse.jetty.util.ssl.SslContextFactory; import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/SslValidationTestBase.java b/jetty-client/src/test/java/org/eclipse/jetty/client/SslValidationTestBase.java index 99862dae2e5..375020a71a1 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/SslValidationTestBase.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/SslValidationTestBase.java @@ -6,7 +6,6 @@ import java.security.KeyStore; import java.security.cert.CRL; import java.util.Collection; -import org.eclipse.jetty.http.ssl.SslContextFactory; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.HandlerCollection; @@ -16,6 +15,7 @@ import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.util.security.CertificateUtils; +import org.eclipse.jetty.util.ssl.SslContextFactory; public abstract class SslValidationTestBase extends ContentExchangeTest { diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/UnexpectedDataTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/UnexpectedDataTest.java index f4b3d82168c..74dd69d43d4 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/UnexpectedDataTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/UnexpectedDataTest.java @@ -16,7 +16,6 @@ package org.eclipse.jetty.client; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.InputStream; import java.io.OutputStream; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -26,17 +25,17 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import junit.framework.TestCase; - -import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.http.HttpMethods; -import org.eclipse.jetty.io.EofException; +import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.server.nio.SelectChannelConnector; import org.eclipse.jetty.util.IO; - +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; /** * Functional testing for HttpExchange. @@ -44,91 +43,71 @@ import org.eclipse.jetty.util.IO; * @author Matthew Purland * @author Greg Wilkins */ -public class UnexpectedDataTest extends TestCase +public class UnexpectedDataTest { - protected int _maxConnectionsPerAddress = 1; - protected String _scheme = "http://"; - protected Server _server; - protected int _port; - protected HttpClient _httpClient; - protected Connector _connector; - protected AtomicInteger _count = new AtomicInteger(); + private Server _server; + private int _port; + private HttpClient _httpClient; + private Connector _connector; + private AtomicInteger _count = new AtomicInteger(); - protected void setUp() throws Exception + @Before + public void setUp() throws Exception { startServer(); - _httpClient=new HttpClient(); + _httpClient = new HttpClient(); _httpClient.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL); - _httpClient.setMaxConnectionsPerAddress(_maxConnectionsPerAddress); + _httpClient.setMaxConnectionsPerAddress(1); _httpClient.start(); } - protected void tearDown() throws Exception + @After + public void tearDown() throws Exception { _httpClient.stop(); Thread.sleep(500); stopServer(); } + @Test public void testUnexpectedData() throws Exception { - for (int i=0; i<4; i++) + for (int i = 0; i < 4; ++i) { - final CountDownLatch done=new CountDownLatch(1); - ContentExchange httpExchange=new ContentExchange() + final CountDownLatch done = new CountDownLatch(1); + ContentExchange httpExchange = new ContentExchange() { protected void onResponseComplete() throws IOException { super.onResponseComplete(); - done.countDown(); } }; - httpExchange.setURL(_scheme+"localhost:"+_port+"/?i="+i); + httpExchange.setURL("http://localhost:" + _port + "/?i=" + i); httpExchange.setMethod(HttpMethods.GET); _httpClient.send(httpExchange); - - done.await(1,TimeUnit.SECONDS); - - int status = httpExchange.getStatus(); - String result=httpExchange.getResponseContent(); - assertEquals("i="+i,0,result.indexOf("")); - assertEquals("i="+i,result.length()-10,result.indexOf("")); - assertEquals(HttpExchange.STATUS_COMPLETED, status); - - Thread.sleep(5); - } - } - public static void copyStream(InputStream in, OutputStream out) - { - try - { - byte[] buffer=new byte[1024]; - int len; - while ((len=in.read(buffer))>=0) - { - out.write(buffer,0,len); - } - } - catch (EofException e) - { - System.err.println(e); - } - catch (IOException e) - { - e.printStackTrace(); + Assert.assertTrue(done.await(1000, TimeUnit.SECONDS)); + + int status = httpExchange.getStatus(); + String result = httpExchange.getResponseContent(); + Assert.assertEquals("i=" + i, 0, result.indexOf("")); + Assert.assertEquals("i=" + i, result.length() - 10, result.indexOf("")); + Assert.assertEquals(HttpExchange.STATUS_COMPLETED, status); + + // Give the client the time to read -1 from server before issuing the next request + // There is currently no simple way to be notified of connection closed. + Thread.sleep(500); } } protected void newServer() throws Exception { - _server=new Server(); + _server = new Server(); _server.setGracefulShutdown(500); - _connector=new SelectChannelConnector(); - + _connector = new SelectChannelConnector(); _connector.setPort(0); - _server.setConnectors(new Connector[] { _connector }); + _server.setConnectors(new Connector[]{_connector}); } protected void startServer() throws Exception @@ -137,28 +116,28 @@ public class UnexpectedDataTest extends TestCase _server.setHandler(new AbstractHandler() { public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) - throws IOException, ServletException + throws IOException, ServletException { - int i=0; + int i = 0; try { baseRequest.setHandled(true); response.setStatus(200); _count.incrementAndGet(); - + if (request.getMethod().equalsIgnoreCase("GET")) { - StringBuffer buffer = new StringBuffer(); + StringBuilder buffer = new StringBuilder(); buffer.append("\r\n"); - for (; i<100; i++) + for (; i < 100; i++) { - buffer.append(" "+i+"\r\n"); + buffer.append(" ").append(i).append("\r\n"); } buffer.append("\r\n"); - + byte[] buff = buffer.toString().getBytes(); response.setContentLength(buff.length); - + buffer.append("extra data"); buff = buffer.toString().getBytes(); @@ -169,35 +148,30 @@ public class UnexpectedDataTest extends TestCase else { response.setContentType(request.getContentType()); - int size=request.getContentLength(); - ByteArrayOutputStream bout = new ByteArrayOutputStream(size>0?size:32768); - IO.copy(request.getInputStream(),bout); + int size = request.getContentLength(); + ByteArrayOutputStream bout = new ByteArrayOutputStream(size > 0 ? size : 32768); + IO.copy(request.getInputStream(), bout); response.getOutputStream().write(bout.toByteArray()); } } - catch(IOException e) + catch (IOException e) { e.printStackTrace(); throw e; } - catch(Throwable e) + catch (Throwable e) { e.printStackTrace(); throw new ServletException(e); } - finally - { - // System.err.println("HANDLED "+i); - } } }); _server.start(); - _port=_connector.getLocalPort(); + _port = _connector.getLocalPort(); } private void stopServer() throws Exception { _server.stop(); } - } diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/WebSocketUpgradeTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/WebSocketUpgradeTest.java index 07480e128d3..d282544e8e7 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/WebSocketUpgradeTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/WebSocketUpgradeTest.java @@ -162,8 +162,6 @@ public class WebSocketUpgradeTest int status = httpExchange.waitForDone(); assertEquals(HttpExchange.STATUS_COMPLETED, status); - // System.err.println("results="+_results); - assertEquals("serverWS.onConnect", _results.poll(1,TimeUnit.SECONDS)); TestWebSocket serverWS = (TestWebSocket)_results.poll(1,TimeUnit.SECONDS); diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/helperClasses/AbstractSslServerAndClientCreator.java b/jetty-client/src/test/java/org/eclipse/jetty/client/helperClasses/AbstractSslServerAndClientCreator.java index fdb2ebf0d75..f7d13cb4c73 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/helperClasses/AbstractSslServerAndClientCreator.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/helperClasses/AbstractSslServerAndClientCreator.java @@ -14,14 +14,13 @@ package org.eclipse.jetty.client.helperClasses; -import org.eclipse.jetty.http.ssl.SslContextFactory; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.ssl.SslSelectChannelConnector; import org.eclipse.jetty.server.ssl.SslSocketConnector; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.ssl.SslContextFactory; /* ------------------------------------------------------------ */ @@ -35,7 +34,7 @@ public abstract class AbstractSslServerAndClientCreator implements ServerAndClie public Server createServer() throws Exception { Server server = new Server(); - //SslSelectChannelConnector connector = new SslSelectChannelConnector(); + // SslSelectChannelConnector connector = new SslSelectChannelConnector(); SslSocketConnector connector = new SslSocketConnector(); String keystore = MavenTestingUtils.getTestResourceFile("keystore").getAbsolutePath(); @@ -45,7 +44,6 @@ public abstract class AbstractSslServerAndClientCreator implements ServerAndClie cf.setKeyStorePath(keystore); cf.setKeyStorePassword("storepwd"); cf.setKeyManagerPassword("keypwd"); - connector.setAllowRenegotiate(true); server.setConnectors(new Connector[]{ connector }); server.setHandler(new GenericServerHandler()); diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/helperClasses/AsyncSslServerAndClientCreator.java b/jetty-client/src/test/java/org/eclipse/jetty/client/helperClasses/AsyncSslServerAndClientCreator.java index 6d08d216f90..b9627e47dba 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/helperClasses/AsyncSslServerAndClientCreator.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/helperClasses/AsyncSslServerAndClientCreator.java @@ -1,7 +1,5 @@ package org.eclipse.jetty.client.helperClasses; -import java.io.FileInputStream; - import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; @@ -16,9 +14,9 @@ public class AsyncSslServerAndClientCreator extends AbstractSslServerAndClientCr httpClient.setMaxConnectionsPerAddress(2); String keystore = MavenTestingUtils.getTestResourceFile("keystore").getAbsolutePath(); - httpClient.setKeyStoreInputStream(new FileInputStream(keystore)); - httpClient.setKeyStorePassword("storepwd"); - httpClient.setKeyManagerPassword("keypwd"); + httpClient.getSslContextFactory().setKeyStorePath(keystore); + httpClient.getSslContextFactory().setKeyStorePassword("storepwd"); + httpClient.getSslContextFactory().setKeyManagerPassword("keypwd"); httpClient.start(); return httpClient; } diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/helperClasses/GenericServerHandler.java b/jetty-client/src/test/java/org/eclipse/jetty/client/helperClasses/GenericServerHandler.java index 82dbdb35c47..cc9ac055454 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/helperClasses/GenericServerHandler.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/helperClasses/GenericServerHandler.java @@ -8,6 +8,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.http.HttpHeaders; +import org.eclipse.jetty.io.EofException; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.util.IO; @@ -70,6 +71,12 @@ public class GenericServerHandler extends AbstractHandler { LOG.debug(e); } + catch (EofException e) + { + LOG.info(e.toString()); + LOG.debug(e); + throw e; + } catch (IOException e) { LOG.warn(e); diff --git a/jetty-continuation/pom.xml b/jetty-continuation/pom.xml index dcd09df4cf0..6c4c47b893a 100644 --- a/jetty-continuation/pom.xml +++ b/jetty-continuation/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 jetty-continuation diff --git a/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/ContinuationFilter.java b/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/ContinuationFilter.java index 639ae2e1819..e5bd75144a5 100644 --- a/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/ContinuationFilter.java +++ b/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/ContinuationFilter.java @@ -1,6 +1,7 @@ package org.eclipse.jetty.continuation; import java.io.IOException; + import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; diff --git a/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/Jetty6Continuation.java b/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/Jetty6Continuation.java index da0c080ea36..142b675ec2d 100644 --- a/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/Jetty6Continuation.java +++ b/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/Jetty6Continuation.java @@ -23,7 +23,7 @@ public class Jetty6Continuation implements ContinuationFilter.FilteredContinuati // Exception reused for all continuations // Turn on debug in ContinuationFilter to see real stack trace. private final static ContinuationThrowable __exception = new ContinuationThrowable(); - + private final ServletRequest _request; private ServletResponse _response; private final org.mortbay.util.ajax.Continuation _j6Continuation; @@ -66,7 +66,7 @@ public class Jetty6Continuation implements ContinuationFilter.FilteredContinuati _j6Continuation.resume(); } } - + /* ------------------------------------------------------------ */ /** * @see org.eclipse.jetty.continuation.Continuation#getAttribute(java.lang.String) @@ -233,8 +233,6 @@ public class Jetty6Continuation implements ContinuationFilter.FilteredContinuati Throwable th=_retry; _retry=null; - if (th instanceof ThreadDeath) - throw (ThreadDeath)th; if (th instanceof Error) throw (Error)th; if (th instanceof RuntimeException) @@ -245,7 +243,7 @@ public class Jetty6Continuation implements ContinuationFilter.FilteredContinuati for (ContinuationListener l: _listeners) l.onComplete(this); } - + return true; } } diff --git a/jetty-deploy/pom.xml b/jetty-deploy/pom.xml index 7cadea26837..528e7ce46c0 100644 --- a/jetty-deploy/pom.xml +++ b/jetty-deploy/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 jetty-deploy diff --git a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java index 0cb29ccc7c1..fb8e1a5d36b 100644 --- a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java +++ b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java @@ -481,7 +481,7 @@ public class DeploymentManager extends AggregateLifeCycle while (it.hasNext()) { Node node = it.next(); - LOG.debug("Executing Node: " + node); + LOG.debug("Executing Node {}",node); _lifecycle.runBindings(node,appentry.app,this); appentry.setLifeCycleNode(node); } diff --git a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/WebAppDeployer.java b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/WebAppDeployer.java index 81e641cbfdf..6cbb2d6fc39 100644 --- a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/WebAppDeployer.java +++ b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/WebAppDeployer.java @@ -257,7 +257,7 @@ public class WebAppDeployer extends AbstractLifeCycle if (path != null && path.equals(app.getFile().getCanonicalPath())) { - LOG.debug("Already deployed:"+path); + LOG.debug("Already deployed: {}",path); continue files; } } diff --git a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardUndeployer.java b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardUndeployer.java index 0a5c08c6bdf..c7119dafd33 100644 --- a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardUndeployer.java +++ b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardUndeployer.java @@ -51,13 +51,14 @@ public class StandardUndeployer implements AppLifeCycle.Binding for (int i = 0, n = children.length; i < n; i++) { Handler child = children[i]; - LOG.debug("Child handler: " + child); + LOG.debug("Child handler {}",child); if (child.equals(context)) { - LOG.debug("Removing handler: " + child); + LOG.debug("Removing handler {}",child); coll.removeHandler(child); child.destroy(); - LOG.debug(String.format("After removal: %d (originally %d)",coll.getHandlers().length,originalCount)); + if (LOG.isDebugEnabled()) + LOG.debug("After removal: {} (originally {})",coll.getHandlers().length,originalCount); } else if (child instanceof HandlerCollection) { diff --git a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java index 629f13f92ec..8bf03f30a12 100644 --- a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java +++ b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java @@ -99,7 +99,8 @@ public abstract class ScanningAppProvider extends AbstractLifeCycle implements A @Override protected void doStart() throws Exception { - if (LOG.isDebugEnabled()) LOG.debug(this.getClass().getSimpleName() + ".doStart()"); + if (LOG.isDebugEnabled()) + LOG.debug(this.getClass().getSimpleName() + ".doStart()"); if (_monitoredDir == null) { throw new IllegalStateException("No configuration dir specified"); @@ -132,7 +133,8 @@ public abstract class ScanningAppProvider extends AbstractLifeCycle implements A /* ------------------------------------------------------------ */ protected void fileAdded(String filename) throws Exception { - if (LOG.isDebugEnabled()) LOG.debug("added ",filename); + if (LOG.isDebugEnabled()) + LOG.debug("added {}",filename); App app = ScanningAppProvider.this.createApp(filename); if (app != null) { @@ -144,7 +146,8 @@ public abstract class ScanningAppProvider extends AbstractLifeCycle implements A /* ------------------------------------------------------------ */ protected void fileChanged(String filename) throws Exception { - if (LOG.isDebugEnabled()) LOG.debug("changed ",filename); + if (LOG.isDebugEnabled()) + LOG.debug("changed {}",filename); App app = _appMap.remove(filename); if (app != null) { @@ -161,7 +164,8 @@ public abstract class ScanningAppProvider extends AbstractLifeCycle implements A /* ------------------------------------------------------------ */ protected void fileRemoved(String filename) throws Exception { - if (LOG.isDebugEnabled()) LOG.debug("removed ",filename); + if (LOG.isDebugEnabled()) + LOG.debug("removed {}",filename); App app = _appMap.remove(filename); if (app != null) _deploymentManager.removeApp(app); diff --git a/jetty-distribution/pom.xml b/jetty-distribution/pom.xml index 35dd919cf00..173d546d426 100644 --- a/jetty-distribution/pom.xml +++ b/jetty-distribution/pom.xml @@ -3,7 +3,7 @@ org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT jetty-distribution Jetty :: Distribution Assemblies @@ -20,8 +20,7 @@ 2.1.0.v201004190952 1.2.0.v201004190952 1.0.0.v201004190952 - - 2.1.3-b10 + 2.1.0.v201110031002 1.2.0.v201004190952 3.1.0.v200803061910 1.1.1.v201004190952 @@ -77,7 +76,7 @@ - + @@ -207,24 +206,11 @@ org.eclipse.jetty - jetty-start,jetty-monitor,jetty-jsp-2.1 + jetty-start,jetty-monitor jar ${assembly-directory}/lib - - copy-lib-jsp-deps - generate-resources - - copy-dependencies - - - org.eclipse.jetty,org.glassfish.web - jetty-jsp-2.1,jsp-impl - jar - ${assembly-directory}/lib/jsp - - copy-lib-monitor-deps generate-resources @@ -388,16 +374,6 @@ jetty-overlay-deployer ${project.version} - - org.eclipse.jetty - jetty-jsp-2.1 - ${project.version} - - - org.glassfish.web - jsp-impl - ${central-jsp-version} - org.eclipse.jetty.aggregate jetty-all diff --git a/jetty-distribution/src/main/resources/start.ini b/jetty-distribution/src/main/resources/start.ini index 52e0a52d1cc..0ada4c02051 100644 --- a/jetty-distribution/src/main/resources/start.ini +++ b/jetty-distribution/src/main/resources/start.ini @@ -18,9 +18,10 @@ # Below are some recommended options for Sun's JRE #----------------------------------------------------------- # --exec +# -Dorg.apache.jasper.compiler.disablejsr199=true # -Dcom.sun.management.jmxremote # -Dorg.eclipse.jetty.util.log.IGNORED=true -# -Dorg.eclipse.jetty.util.log.stderr.DEBUG=true +# -Dorg.eclipse.jetty.LEVEL=DEBUG # -Dorg.eclipse.jetty.util.log.stderr.SOURCE=true # -Xmx2000m # -Xmn512m diff --git a/jetty-http-spi/pom.xml b/jetty-http-spi/pom.xml index f4204154f1b..856673b86eb 100644 --- a/jetty-http-spi/pom.xml +++ b/jetty-http-spi/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 jetty-http-spi @@ -69,4 +69,4 @@ - \ No newline at end of file + diff --git a/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/HttpSpiContextHandler.java b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/HttpSpiContextHandler.java index cb59e31dcee..62c30cac169 100644 --- a/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/HttpSpiContextHandler.java +++ b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/HttpSpiContextHandler.java @@ -1,4 +1,5 @@ package org.eclipse.jetty.http.spi; + //======================================================================== //Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd. //------------------------------------------------------------------------ @@ -12,20 +13,22 @@ package org.eclipse.jetty.http.spi; //You may elect to redistribute this code under either of these licenses. //======================================================================== -import com.sun.net.httpserver.Authenticator; -import com.sun.net.httpserver.Authenticator.Result; -import com.sun.net.httpserver.HttpContext; -import com.sun.net.httpserver.HttpHandler; -import com.sun.net.httpserver.HttpPrincipal; -import org.eclipse.jetty.server.HttpConnection; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.handler.ContextHandler; +import java.io.IOException; +import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.PrintWriter; + +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.handler.ContextHandler; + +import com.sun.net.httpserver.Authenticator; +import com.sun.net.httpserver.Authenticator.Result; +import com.sun.net.httpserver.HttpContext; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpPrincipal; /** * Jetty handler that bridges requests to {@link HttpHandler}. @@ -37,7 +40,6 @@ public class HttpSpiContextHandler extends ContextHandler private HttpHandler _httpHandler; - public HttpSpiContextHandler(HttpContext httpContext, HttpHandler httpHandler) { this._httpContext = httpContext; @@ -47,9 +49,20 @@ public class HttpSpiContextHandler extends ContextHandler @Override public void doScope(String target, Request baseRequest, HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { - if (!target.startsWith(getContextPath())) return; + if (!target.startsWith(getContextPath())) + { + return; + } - JettyHttpExchange jettyHttpExchange = new JettyHttpExchange(_httpContext, req, resp); + HttpExchange jettyHttpExchange; + if (baseRequest.isSecure()) + { + jettyHttpExchange = new JettyHttpsExchange(_httpContext,req,resp); + } + else + { + jettyHttpExchange = new JettyHttpExchange(_httpContext,req,resp); + } // TODO: add filters processing @@ -57,39 +70,41 @@ public class HttpSpiContextHandler extends ContextHandler { Authenticator auth = _httpContext.getAuthenticator(); if (auth != null) - handleAuthentication(resp, jettyHttpExchange, auth); + { + handleAuthentication(resp,jettyHttpExchange,auth); + } else + { _httpHandler.handle(jettyHttpExchange); + } } - catch(Exception ex) + catch (Exception ex) { PrintWriter writer = new PrintWriter(jettyHttpExchange.getResponseBody()); - + resp.setStatus(500); writer.println("

HTTP ERROR: 500

"); writer.println("
INTERNAL_SERVER_ERROR
"); writer.println("

RequestURI=" + req.getRequestURI() + "

"); - + writer.println("
");
             ex.printStackTrace(writer);
             writer.println("
"); - + writer.println("

Powered by jetty://

"); - + writer.close(); } finally { - Request base_request = (req instanceof Request) ? (Request)req:HttpConnection.getCurrentConnection().getRequest(); - base_request.setHandled(true); + baseRequest.setHandled(true); } - + } - - private void handleAuthentication(HttpServletResponse resp, JettyHttpExchange jettyHttpExchange, Authenticator auth) throws IOException + private void handleAuthentication(HttpServletResponse resp, HttpExchange httpExchange, Authenticator auth) throws IOException { - Result result = auth.authenticate(jettyHttpExchange); + Result result = auth.authenticate(httpExchange); if (result instanceof Authenticator.Failure) { int rc = ((Authenticator.Failure)result).getResponseCode(); @@ -103,8 +118,8 @@ public class HttpSpiContextHandler extends ContextHandler else if (result instanceof Authenticator.Success) { HttpPrincipal principal = ((Authenticator.Success)result).getPrincipal(); - jettyHttpExchange.setPrincipal(principal); - _httpHandler.handle(jettyHttpExchange); + ((JettyExchange)httpExchange).setPrincipal(principal); + _httpHandler.handle(httpExchange); } } diff --git a/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyExchange.java b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyExchange.java new file mode 100644 index 00000000000..f448c118185 --- /dev/null +++ b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyExchange.java @@ -0,0 +1,28 @@ +// ======================================================================== +// Copyright (c) 2009-2009 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 com.sun.net.httpserver.HttpPrincipal; + +/* ------------------------------------------------------------ */ +/** + */ +public interface JettyExchange +{ + + HttpPrincipal getPrincipal(); + + void setPrincipal(HttpPrincipal principal); + +} \ No newline at end of file diff --git a/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpExchange.java b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpExchange.java index 9ce8e959d83..41809d036e3 100644 --- a/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpExchange.java +++ b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpExchange.java @@ -1,227 +1,252 @@ -package org.eclipse.jetty.http.spi; - -//======================================================================== -//Copyright (c) 2004-2009 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. -//======================================================================== - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.InetSocketAddress; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Enumeration; -import java.util.List; -import java.util.Map; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import com.sun.net.httpserver.Headers; -import com.sun.net.httpserver.HttpContext; -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpPrincipal; - -/** - * Jetty implementation of {@link com.sun.net.httpserver.HttpExchange} - */ -public class JettyHttpExchange extends HttpExchange -{ - - private HttpContext _httpContext; - - private HttpServletRequest _req; - - private HttpServletResponse _resp; - - private Headers _responseHeaders = new Headers(); - - private int _responseCode = 0; - - private InputStream _is; - - private OutputStream _os; - - private HttpPrincipal _httpPrincipal; - - - public JettyHttpExchange(HttpContext jaxWsContext , HttpServletRequest req, - HttpServletResponse resp) - { - this._httpContext = jaxWsContext; - this._req = req; - this._resp = resp; - try - { - this._is = req.getInputStream(); - this._os = resp.getOutputStream(); - } - catch (IOException ex) - { - throw new RuntimeException(ex); - } - } - - @Override - public Headers getRequestHeaders() - { - Headers headers = new Headers(); - Enumeration en = _req.getHeaderNames(); - while (en.hasMoreElements()) - { - String name = (String) en.nextElement(); - Enumeration en2 = _req.getHeaders(name); - while (en2.hasMoreElements()) - { - String value = (String) en2.nextElement(); - headers.add(name, value); - } - } - return headers; - } - - @Override - public Headers getResponseHeaders() - { - return _responseHeaders; - } - - @Override - public URI getRequestURI() - { - try - { - String uriAsString = _req.getRequestURI(); - if (_req.getQueryString() != null) - uriAsString += "?" + _req.getQueryString(); - - return new URI(uriAsString); - } - catch (URISyntaxException ex) - { - throw new RuntimeException(ex); - } - } - - @Override - public String getRequestMethod() - { - return _req.getMethod(); - } - - @Override - public HttpContext getHttpContext() - { - return _httpContext; - } - - @Override - public void close() - { - try - { - _resp.getOutputStream().close(); - } - catch (IOException ex) - { - throw new RuntimeException(ex); - } - } - - @Override - public InputStream getRequestBody() - { - return _is; - } - - @Override - public OutputStream getResponseBody() - { - return _os; - } - - @Override - public void sendResponseHeaders(int rCode, long responseLength) - throws IOException - { - this._responseCode = rCode; - - for (Map.Entry> stringListEntry : _responseHeaders.entrySet()) - { - String name = stringListEntry.getKey(); - List values = stringListEntry.getValue(); - - for (String value : values) - { - _resp.setHeader(name, value); - } - } - if (responseLength > 0) - _resp.setHeader("content-length", "" + responseLength); - _resp.setStatus(rCode); - } - - @Override - public InetSocketAddress getRemoteAddress() - { - return new InetSocketAddress(_req.getRemoteAddr(), _req.getRemotePort()); - } - - @Override - public int getResponseCode() - { - return _responseCode; - } - - @Override - public InetSocketAddress getLocalAddress() - { - return new InetSocketAddress(_req.getLocalAddr(), _req.getLocalPort()); - } - - @Override - public String getProtocol() - { - return _req.getProtocol(); - } - - @Override - public Object getAttribute(String name) - { - return _req.getAttribute(name); - } - - @Override - public void setAttribute(String name, Object value) - { - _req.setAttribute(name, value); - } - - @Override - public void setStreams(InputStream i, OutputStream o) - { - _is = i; - _os = o; - } - - @Override - public HttpPrincipal getPrincipal() - { - return _httpPrincipal; - } - - public void setPrincipal(HttpPrincipal principal) - { - this._httpPrincipal = principal; - } - -} +// ======================================================================== +// Copyright (c) 2009-2009 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.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.URI; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpContext; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpPrincipal; + +/* ------------------------------------------------------------ */ +/** + */ +public class JettyHttpExchange extends HttpExchange implements JettyExchange +{ + private JettyHttpExchangeDelegate _delegate; + + public JettyHttpExchange(HttpContext jaxWsContext, HttpServletRequest req, HttpServletResponse resp) + { + super(); + _delegate = new JettyHttpExchangeDelegate(jaxWsContext,req,resp); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.http.spi.JettyExchange#hashCode() + */ + @Override + public int hashCode() + { + return _delegate.hashCode(); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.http.spi.JettyExchange#getRequestHeaders() + */ + @Override + public Headers getRequestHeaders() + { + return _delegate.getRequestHeaders(); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.http.spi.JettyExchange#getResponseHeaders() + */ + @Override + public Headers getResponseHeaders() + { + return _delegate.getResponseHeaders(); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.http.spi.JettyExchange#getRequestURI() + */ + @Override + public URI getRequestURI() + { + return _delegate.getRequestURI(); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.http.spi.JettyExchange#getRequestMethod() + */ + @Override + public String getRequestMethod() + { + return _delegate.getRequestMethod(); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.http.spi.JettyExchange#getHttpContext() + */ + @Override + public HttpContext getHttpContext() + { + return _delegate.getHttpContext(); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.http.spi.JettyExchange#close() + */ + @Override + public void close() + { + _delegate.close(); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.http.spi.JettyExchange#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) + { + return _delegate.equals(obj); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.http.spi.JettyExchange#getRequestBody() + */ + @Override + public InputStream getRequestBody() + { + return _delegate.getRequestBody(); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.http.spi.JettyExchange#getResponseBody() + */ + @Override + public OutputStream getResponseBody() + { + return _delegate.getResponseBody(); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.http.spi.JettyExchange#sendResponseHeaders(int, long) + */ + @Override + public void sendResponseHeaders(int rCode, long responseLength) throws IOException + { + _delegate.sendResponseHeaders(rCode,responseLength); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.http.spi.JettyExchange#getRemoteAddress() + */ + @Override + public InetSocketAddress getRemoteAddress() + { + return _delegate.getRemoteAddress(); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.http.spi.JettyExchange#getResponseCode() + */ + @Override + public int getResponseCode() + { + return _delegate.getResponseCode(); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.http.spi.JettyExchange#getLocalAddress() + */ + @Override + public InetSocketAddress getLocalAddress() + { + return _delegate.getLocalAddress(); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.http.spi.JettyExchange#getProtocol() + */ + @Override + public String getProtocol() + { + return _delegate.getProtocol(); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.http.spi.JettyExchange#getAttribute(java.lang.String) + */ + @Override + public Object getAttribute(String name) + { + return _delegate.getAttribute(name); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.http.spi.JettyExchange#setAttribute(java.lang.String, java.lang.Object) + */ + @Override + public void setAttribute(String name, Object value) + { + _delegate.setAttribute(name,value); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.http.spi.JettyExchange#setStreams(java.io.InputStream, java.io.OutputStream) + */ + @Override + public void setStreams(InputStream i, OutputStream o) + { + _delegate.setStreams(i,o); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.http.spi.JettyExchange#getPrincipal() + */ + @Override + public HttpPrincipal getPrincipal() + { + return _delegate.getPrincipal(); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.http.spi.JettyExchange#setPrincipal(com.sun.net.httpserver.HttpPrincipal) + */ + public void setPrincipal(HttpPrincipal principal) + { + _delegate.setPrincipal(principal); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.http.spi.JettyExchange#toString() + */ + @Override + public String toString() + { + return _delegate.toString(); + } + +} diff --git a/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpExchangeDelegate.java b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpExchangeDelegate.java new file mode 100644 index 00000000000..b772afbe598 --- /dev/null +++ b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpExchangeDelegate.java @@ -0,0 +1,228 @@ +package org.eclipse.jetty.http.spi; + +//======================================================================== +//Copyright (c) 2004-2009 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. +//======================================================================== + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Enumeration; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpContext; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpPrincipal; + +/** + * Jetty implementation of {@link com.sun.net.httpserver.HttpExchange} + */ +public class JettyHttpExchangeDelegate extends HttpExchange +{ + + private HttpContext _httpContext; + + private HttpServletRequest _req; + + private HttpServletResponse _resp; + + private Headers _responseHeaders = new Headers(); + + private int _responseCode = 0; + + private InputStream _is; + + private OutputStream _os; + + private HttpPrincipal _httpPrincipal; + + JettyHttpExchangeDelegate(HttpContext jaxWsContext, HttpServletRequest req, HttpServletResponse resp) + { + this._httpContext = jaxWsContext; + this._req = req; + this._resp = resp; + try + { + this._is = req.getInputStream(); + this._os = resp.getOutputStream(); + } + catch (IOException ex) + { + throw new RuntimeException(ex); + } + } + + @Override + public Headers getRequestHeaders() + { + Headers headers = new Headers(); + Enumeration en = _req.getHeaderNames(); + while (en.hasMoreElements()) + { + String name = (String)en.nextElement(); + Enumeration en2 = _req.getHeaders(name); + while (en2.hasMoreElements()) + { + String value = (String)en2.nextElement(); + headers.add(name,value); + } + } + return headers; + } + + @Override + public Headers getResponseHeaders() + { + return _responseHeaders; + } + + @Override + public URI getRequestURI() + { + try + { + String uriAsString = _req.getRequestURI(); + if (_req.getQueryString() != null) + { + uriAsString += "?" + _req.getQueryString(); + } + + return new URI(uriAsString); + } + catch (URISyntaxException ex) + { + throw new RuntimeException(ex); + } + } + + @Override + public String getRequestMethod() + { + return _req.getMethod(); + } + + @Override + public HttpContext getHttpContext() + { + return _httpContext; + } + + @Override + public void close() + { + try + { + _resp.getOutputStream().close(); + } + catch (IOException ex) + { + throw new RuntimeException(ex); + } + } + + @Override + public InputStream getRequestBody() + { + return _is; + } + + @Override + public OutputStream getResponseBody() + { + return _os; + } + + @Override + public void sendResponseHeaders(int rCode, long responseLength) throws IOException + { + this._responseCode = rCode; + + for (Map.Entry> stringListEntry : _responseHeaders.entrySet()) + { + String name = stringListEntry.getKey(); + List values = stringListEntry.getValue(); + + for (String value : values) + { + _resp.setHeader(name,value); + } + } + if (responseLength > 0) + { + _resp.setHeader("content-length","" + responseLength); + } + _resp.setStatus(rCode); + } + + @Override + public InetSocketAddress getRemoteAddress() + { + return new InetSocketAddress(_req.getRemoteAddr(),_req.getRemotePort()); + } + + @Override + public int getResponseCode() + { + return _responseCode; + } + + @Override + public InetSocketAddress getLocalAddress() + { + return new InetSocketAddress(_req.getLocalAddr(),_req.getLocalPort()); + } + + @Override + public String getProtocol() + { + return _req.getProtocol(); + } + + @Override + public Object getAttribute(String name) + { + return _req.getAttribute(name); + } + + @Override + public void setAttribute(String name, Object value) + { + _req.setAttribute(name,value); + } + + @Override + public void setStreams(InputStream i, OutputStream o) + { + _is = i; + _os = o; + } + + @Override + public HttpPrincipal getPrincipal() + { + return _httpPrincipal; + } + + public void setPrincipal(HttpPrincipal principal) + { + this._httpPrincipal = principal; + } + +} diff --git a/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpsExchange.java b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpsExchange.java new file mode 100644 index 00000000000..f4af2b99ce9 --- /dev/null +++ b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpsExchange.java @@ -0,0 +1,179 @@ +// ======================================================================== +// Copyright (c) 2009-2009 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.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.URI; + +import javax.net.ssl.SSLSession; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpContext; +import com.sun.net.httpserver.HttpPrincipal; +import com.sun.net.httpserver.HttpsExchange; + +/* ------------------------------------------------------------ */ +/** + */ +public class JettyHttpsExchange extends HttpsExchange implements JettyExchange +{ + private JettyHttpExchangeDelegate _delegate; + + public JettyHttpsExchange(HttpContext jaxWsContext, HttpServletRequest req, HttpServletResponse resp) + { + super(); + _delegate = new JettyHttpExchangeDelegate(jaxWsContext,req,resp); + } + + @Override + public int hashCode() + { + return _delegate.hashCode(); + } + + @Override + public Headers getRequestHeaders() + { + return _delegate.getRequestHeaders(); + } + + @Override + public Headers getResponseHeaders() + { + return _delegate.getResponseHeaders(); + } + + @Override + public URI getRequestURI() + { + return _delegate.getRequestURI(); + } + + @Override + public String getRequestMethod() + { + return _delegate.getRequestMethod(); + } + + @Override + public HttpContext getHttpContext() + { + return _delegate.getHttpContext(); + } + + @Override + public void close() + { + _delegate.close(); + } + + @Override + public boolean equals(Object obj) + { + return _delegate.equals(obj); + } + + @Override + public InputStream getRequestBody() + { + return _delegate.getRequestBody(); + } + + @Override + public OutputStream getResponseBody() + { + return _delegate.getResponseBody(); + } + + @Override + public void sendResponseHeaders(int rCode, long responseLength) throws IOException + { + _delegate.sendResponseHeaders(rCode,responseLength); + } + + @Override + public InetSocketAddress getRemoteAddress() + { + return _delegate.getRemoteAddress(); + } + + @Override + public int getResponseCode() + { + return _delegate.getResponseCode(); + } + + @Override + public InetSocketAddress getLocalAddress() + { + return _delegate.getLocalAddress(); + } + + @Override + public String getProtocol() + { + return _delegate.getProtocol(); + } + + @Override + public Object getAttribute(String name) + { + return _delegate.getAttribute(name); + } + + @Override + public void setAttribute(String name, Object value) + { + _delegate.setAttribute(name,value); + } + + @Override + public void setStreams(InputStream i, OutputStream o) + { + _delegate.setStreams(i,o); + } + + @Override + public HttpPrincipal getPrincipal() + { + return _delegate.getPrincipal(); + } + + public void setPrincipal(HttpPrincipal principal) + { + _delegate.setPrincipal(principal); + } + + @Override + public String toString() + { + return _delegate.toString(); + } + + /* ------------------------------------------------------------ */ + /** + * @see com.sun.net.httpserver.HttpsExchange#getSSLSession() + */ + @Override + public SSLSession getSSLSession() + { + return null; + } + +} diff --git a/jetty-http/pom.xml b/jetty-http/pom.xml index ad9abfd0446..53d5c6e9641 100644 --- a/jetty-http/pom.xml +++ b/jetty-http/pom.xml @@ -2,7 +2,7 @@ jetty-project org.eclipse.jetty - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 org.eclipse.jetty diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/AbstractGenerator.java b/jetty-http/src/main/java/org/eclipse/jetty/http/AbstractGenerator.java index 6901fe1f63f..53df952595a 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/AbstractGenerator.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/AbstractGenerator.java @@ -4,11 +4,11 @@ // 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 +// 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. +// You may elect to redistribute this code under either of these licenses. // ======================================================================== package org.eclipse.jetty.http; @@ -27,11 +27,11 @@ import org.eclipse.jetty.util.log.Logger; /* ------------------------------------------------------------ */ /** * Abstract Generator. Builds HTTP Messages. - * + * * Currently this class uses a system parameter "jetty.direct.writers" to control - * two optional writer to byte conversions. buffer.writers=true will probably be + * two optional writer to byte conversions. buffer.writers=true will probably be * faster, but will consume more memory. This option is just for testing and tuning. - * + * */ public abstract class AbstractGenerator implements Generator { @@ -42,16 +42,16 @@ public abstract class AbstractGenerator implements Generator public final static int STATE_CONTENT = 2; public final static int STATE_FLUSHING = 3; public final static int STATE_END = 4; - + public static final byte[] NO_BYTES = {}; // data protected final Buffers _buffers; // source of buffers protected final EndPoint _endp; - + protected int _state = STATE_HEADER; - + protected int _status = 0; protected int _version = HttpVersions.HTTP_1_1_ORDINAL; protected Buffer _reason; @@ -64,20 +64,20 @@ public abstract class AbstractGenerator implements Generator protected boolean _head = false; protected boolean _noContent = false; protected Boolean _persistent = null; - + protected Buffer _header; // Buffer for HTTP header (and maybe small _content) protected Buffer _buffer; // Buffer for copy of passed _content protected Buffer _content; // Buffer passed to addContent - + protected Buffer _date; - + private boolean _sendServerVersion; - + /* ------------------------------------------------------------------------------- */ /** * Constructor. - * + * * @param buffers buffer pool * @param io the end point */ @@ -89,18 +89,18 @@ public abstract class AbstractGenerator implements Generator /* ------------------------------------------------------------------------------- */ public abstract boolean isRequest(); - + /* ------------------------------------------------------------------------------- */ public abstract boolean isResponse(); - + /* ------------------------------------------------------------------------------- */ public boolean isOpen() { return _endp.isOpen(); } - + /* ------------------------------------------------------------------------------- */ - public void reset(boolean returnBuffers) + public void reset() { _state = STATE_HEADER; _status = 0; @@ -114,27 +114,13 @@ public abstract class AbstractGenerator implements Generator _contentLength = HttpTokens.UNKNOWN_CONTENT; _date = null; - // always return the body buffer - if (_buffer!=null) - _buffers.returnBuffer(_buffer); - _buffer=null; - - if (returnBuffers) - { - if (_header!=null) - _buffers.returnBuffer(_header); - _header=null; - } - else if (_header != null) - _header.clear(); - _content = null; _method=null; } /* ------------------------------------------------------------------------------- */ public void returnBuffers() - { + { if (_buffer!=null && _buffer.length()==0) { _buffers.returnBuffer(_buffer); @@ -145,22 +131,22 @@ public abstract class AbstractGenerator implements Generator { _buffers.returnBuffer(_header); _header=null; - } + } } - + /* ------------------------------------------------------------------------------- */ public void resetBuffer() - { + { if(_state>=STATE_FLUSHING) throw new IllegalStateException("Flushed"); - + _last = false; _persistent=null; _contentWritten = 0; _contentLength = HttpTokens.UNKNOWN_CONTENT; _content=null; if (_buffer!=null) - _buffer.clear(); + _buffer.clear(); } /* ------------------------------------------------------------ */ @@ -190,25 +176,25 @@ public abstract class AbstractGenerator implements Generator _buffer = nb; } } - - /* ------------------------------------------------------------ */ + + /* ------------------------------------------------------------ */ public Buffer getUncheckedBuffer() { return _buffer; } - - /* ------------------------------------------------------------ */ + + /* ------------------------------------------------------------ */ public boolean getSendServerVersion () { return _sendServerVersion; } - - /* ------------------------------------------------------------ */ + + /* ------------------------------------------------------------ */ public void setSendServerVersion (boolean sendServerVersion) { _sendServerVersion = sendServerVersion; } - + /* ------------------------------------------------------------ */ public int getState() { @@ -256,7 +242,7 @@ public abstract class AbstractGenerator implements Generator else _contentLength=value; } - + /* ------------------------------------------------------------ */ /** * @param head The head to set. @@ -277,7 +263,7 @@ public abstract class AbstractGenerator implements Generator ?_persistent.booleanValue() :(isRequest()?true:_version>HttpVersions.HTTP_1_0_ORDINAL); } - + /* ------------------------------------------------------------ */ public void setPersistent(boolean persistent) { @@ -291,7 +277,7 @@ public abstract class AbstractGenerator implements Generator */ public void setVersion(int version) { - if (_state != STATE_HEADER) + if (_state != STATE_HEADER) throw new IllegalStateException("STATE!=START "+_state); _version = version; if (_version==HttpVersions.HTTP_0_9_ORDINAL && _method!=null) @@ -303,7 +289,7 @@ public abstract class AbstractGenerator implements Generator { return _version; } - + /* ------------------------------------------------------------ */ /** * @see org.eclipse.jetty.http.Generator#setDate(org.eclipse.jetty.io.Buffer) @@ -340,7 +326,7 @@ public abstract class AbstractGenerator implements Generator if (reason!=null) { int len=reason.length(); - + // TODO don't hard code if (len>1024) len=1024; @@ -378,14 +364,14 @@ public abstract class AbstractGenerator implements Generator if(_buffer!=null) _buffer.clear(); } - else + else { _contentWritten+=_buffer.length(); if (_head) _buffer.clear(); } } - + /* ------------------------------------------------------------ */ public boolean isBufferFull() { @@ -404,20 +390,20 @@ public abstract class AbstractGenerator implements Generator { return _contentWritten>0; } - + /* ------------------------------------------------------------ */ public boolean isAllContentWritten() { return _contentLength>=0 && _contentWritten>=_contentLength; } - + /* ------------------------------------------------------------ */ public abstract void completeHeader(HttpFields fields, boolean allContentAdded) throws IOException; - + /* ------------------------------------------------------------ */ /** * Complete the message. - * + * * @throws IOException */ public void complete() throws IOException @@ -436,9 +422,9 @@ public abstract class AbstractGenerator implements Generator } /* ------------------------------------------------------------ */ - public abstract long flushBuffer() throws IOException; + public abstract int flushBuffer() throws IOException; + - /* ------------------------------------------------------------ */ public void flush(long maxIdleTime) throws IOException { @@ -450,29 +436,20 @@ public abstract class AbstractGenerator implements Generator if (content!=null && content.length()>0 || buffer!=null && buffer.length()>0 || isBufferFull()) { flushBuffer(); - + while (now0 ||buffer!=null && buffer.length()>0) && _endp.isOpen()&& !_endp.isOutputShutdown()) { blockForOutput(end-now); now=System.currentTimeMillis(); } } - - // make sure buffered data is also flushed - while (now0 || _bufferChunked) { - if (!_endp.isOpen()) + if (_endp.isOutputShutdown()) throw new EofException(); flushBuffer(); if (_content != null && _content.length()>0) @@ -242,7 +257,7 @@ public class HttpGenerator extends AbstractGenerator if (_last || _state==STATE_END) { - LOG.debug("Ignoring extra content {}",Byte.valueOf(b)); + LOG.warn("Ignoring extra content {}",Byte.valueOf(b)); return false; } @@ -399,7 +414,7 @@ public class HttpGenerator extends AbstractGenerator _contentLength = HttpTokens.NO_CONTENT; _header.put(_method); _header.put((byte)' '); - _header.put(_uri.getBytes("utf-8")); // TODO WRONG! + _header.put(_uri.getBytes("UTF-8")); // TODO check _header.put(HttpTokens.CRLF); _state = STATE_FLUSHING; _noContent=true; @@ -409,7 +424,7 @@ public class HttpGenerator extends AbstractGenerator { _header.put(_method); _header.put((byte)' '); - _header.put(_uri.getBytes("utf-8")); // TODO WRONG! + _header.put(_uri.getBytes("UTF-8")); // TODO check _header.put((byte)' '); _header.put(_version==HttpVersions.HTTP_1_0_ORDINAL?HttpVersions.HTTP_1_0_BUFFER:HttpVersions.HTTP_1_1_BUFFER); _header.put(HttpTokens.CRLF); @@ -418,7 +433,6 @@ public class HttpGenerator extends AbstractGenerator else { // Responses - if (_version == HttpVersions.HTTP_0_9_ORDINAL) { _persistent = false; @@ -677,7 +691,7 @@ public class HttpGenerator extends AbstractGenerator { // we have seen all the _content there is _contentLength = _contentWritten; - if (content_length == null && (isResponse() || _contentLength>0 || content_type )) + if (content_length == null && (isResponse() || _contentLength>0 || content_type ) && !_noContent) { // known length but not actually set. _header.put(HttpHeaders.CONTENT_LENGTH_BUFFER); @@ -776,7 +790,6 @@ public class HttpGenerator extends AbstractGenerator // end the header. _header.put(HttpTokens.CRLF); - _state = STATE_CONTENT; } @@ -786,8 +799,6 @@ public class HttpGenerator extends AbstractGenerator } } - - /* ------------------------------------------------------------ */ /** * Complete the message. @@ -814,10 +825,11 @@ public class HttpGenerator extends AbstractGenerator /* ------------------------------------------------------------ */ @Override - public long flushBuffer() throws IOException + public int flushBuffer() throws IOException { try { + if (_state == STATE_HEADER) throw new IllegalStateException("State==HEADER"); @@ -837,79 +849,87 @@ public class HttpGenerator extends AbstractGenerator int total= 0; int len = -1; - int to_flush = ((_header != null && _header.length() > 0)?4:0) | ((_buffer != null && _buffer.length() > 0)?2:0) | ((_bypass && _content != null && _content.length() > 0)?1:0); - switch (to_flush) + int to_flush = flushMask(); + int last_flush; + + do { - case 7: - throw new IllegalStateException(); // should never happen! - case 6: - len = _endp.flush(_header, _buffer, null); - break; - case 5: - len = _endp.flush(_header, _content, null); - break; - case 4: - len = _endp.flush(_header); - break; - case 3: - len = _endp.flush(_buffer, _content, null); - break; - case 2: - len = _endp.flush(_buffer); - break; - case 1: - len = _endp.flush(_content); - break; - case 0: + last_flush=to_flush; + switch (to_flush) { - // Nothing more we can write now. - if (_header != null) - _header.clear(); - - _bypass = false; - _bufferChunked = false; - - if (_buffer != null) + case 7: + throw new IllegalStateException(); // should never happen! + case 6: + len = _endp.flush(_header, _buffer, null); + break; + case 5: + len = _endp.flush(_header, _content, null); + break; + case 4: + len = _endp.flush(_header); + break; + case 3: + len = _endp.flush(_buffer, _content, null); + break; + case 2: + len = _endp.flush(_buffer); + break; + case 1: + len = _endp.flush(_content); + break; + case 0: { - _buffer.clear(); - if (_contentLength == HttpTokens.CHUNKED_CONTENT) - { - // reserve some space for the chunk header - _buffer.setPutIndex(CHUNK_SPACE); - _buffer.setGetIndex(CHUNK_SPACE); + len=0; + // Nothing more we can write now. + if (_header != null) + _header.clear(); - // Special case handling for small left over buffer from - // an addContent that caused a buffer flush. - if (_content != null && _content.length() < _buffer.space() && _state != STATE_FLUSHING) + _bypass = false; + _bufferChunked = false; + + if (_buffer != null) + { + _buffer.clear(); + if (_contentLength == HttpTokens.CHUNKED_CONTENT) { - _buffer.put(_content); - _content.clear(); - _content=null; + // reserve some space for the chunk header + _buffer.setPutIndex(CHUNK_SPACE); + _buffer.setGetIndex(CHUNK_SPACE); + + // Special case handling for small left over buffer from + // an addContent that caused a buffer flush. + if (_content != null && _content.length() < _buffer.space() && _state != STATE_FLUSHING) + { + _buffer.put(_content); + _content.clear(); + _content=null; + } } } + + // Are we completely finished for now? + if (!_needCRLF && !_needEOC && (_content==null || _content.length()==0)) + { + if (_state == STATE_FLUSHING) + _state = STATE_END; + + if (_state==STATE_END && _persistent != null && !_persistent && _status!=100 && _method==null) + _endp.shutdownOutput(); + } + else + // Try to prepare more to write. + prepareBuffers(); } - // Are we completely finished for now? - if (!_needCRLF && !_needEOC && (_content==null || _content.length()==0)) - { - if (_state == STATE_FLUSHING) - { - _state = STATE_END; - } - - if (_state==STATE_END && _persistent != null && !_persistent && _status!=100 && _method==null) - { - _endp.shutdownOutput(); - } - } - else - // Try to prepare more to write. - prepareBuffers(); } - } - if (len > 0) - total+=len; + if (len > 0) + total+=len; + + to_flush = flushMask(); + } + // loop while progress is being made (OR we have prepared some buffers that might make progress) + while (len>0 || (to_flush!=0 && last_flush==0)); return total; } @@ -920,6 +940,14 @@ public class HttpGenerator extends AbstractGenerator } } + /* ------------------------------------------------------------ */ + private int flushMask() + { + return ((_header != null && _header.length() > 0)?4:0) + | ((_buffer != null && _buffer.length() > 0)?2:0) + | ((_bypass && _content != null && _content.length() > 0)?1:0); + } + /* ------------------------------------------------------------ */ private void prepareBuffers() { @@ -934,16 +962,19 @@ public class HttpGenerator extends AbstractGenerator if (_content.length() == 0) _content = null; } - + // Chunk buffer if need be if (_contentLength == HttpTokens.CHUNKED_CONTENT) { - if ((_buffer==null||_buffer.length()==0) && _content!=null) + if (_bypass && (_buffer==null||_buffer.length()==0) && _content!=null) { // this is a bypass write int size = _content.length(); _bufferChunked = true; + if (_header == null) + _header = _buffers.getHeader(); + // if we need CRLF add this to header if (_needCRLF) { @@ -954,7 +985,7 @@ public class HttpGenerator extends AbstractGenerator // Add the chunk size to the header BufferUtil.putHexInt(_header, size); _header.put(HttpTokens.CRLF); - + // Need a CRLF after the content _needCRLF=true; } @@ -984,7 +1015,10 @@ public class HttpGenerator extends AbstractGenerator } else { - // No space so lets use the header buffer. + // No space so lets use a header buffer. + if (_header == null) + _header = _buffers.getHeader(); + if (_needCRLF) { if (_header.length() > 0) throw new IllegalStateException("EOC"); @@ -1067,9 +1101,11 @@ public class HttpGenerator extends AbstractGenerator @Override public String toString() { - return "HttpGenerator s="+_state+ - " h="+(_header==null?"null":_header.length())+ - " b="+(_buffer==null?"null":_buffer.length())+ - " c="+(_content==null?"null":_content.length()); + return String.format("%s{s=%d,h=%d,b=%d,c=%d}", + getClass().getSimpleName(), + _state, + _header == null ? -1 : _header.length(), + _buffer == null ? -1 : _buffer.length(), + _content == null ? -1 : _content.length()); } } diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeaderValues.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeaderValues.java index b4e77338cda..ec933743bbf 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeaderValues.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeaderValues.java @@ -13,14 +13,9 @@ package org.eclipse.jetty.http; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.LineNumberReader; - import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.BufferCache; import org.eclipse.jetty.io.ByteArrayBuffer; -import org.eclipse.jetty.util.log.Log; /** * Cached HTTP Header values. diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java index 23acc031e91..950d439036c 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java @@ -13,7 +13,6 @@ package org.eclipse.jetty.http; -import java.io.EOFException; import java.io.IOException; import org.eclipse.jetty.io.Buffer; @@ -55,6 +54,7 @@ public class HttpParser implements Parser public static final int STATE_CHUNK_SIZE=4; public static final int STATE_CHUNK_PARAMS=5; public static final int STATE_CHUNK=6; + public static final int STATE_SEEKING_EOF=7; private final EventHandler _handler; private final Buffers _buffers; // source of buffers @@ -68,6 +68,7 @@ public class HttpParser implements Parser private String _multiLineValue; private int _responseStatus; // If >0 then we are parsing a response private boolean _forceContentBuffer; + private boolean _persistent; /* ------------------------------------------------------------------------------- */ protected final View _contentView=new View(); // View of the content in the buffer for {@link Input} @@ -186,6 +187,20 @@ public class HttpParser implements Parser return _state == state; } + /* ------------------------------------------------------------------------------- */ + public boolean isPersistent() + { + return _persistent; + } + + /* ------------------------------------------------------------------------------- */ + public void setPersistent(boolean persistent) + { + _persistent = persistent; + if (_state==STATE_END) + _state=STATE_SEEKING_EOF; + } + /* ------------------------------------------------------------------------------- */ /** * Parse until {@link #STATE_END END} state. @@ -213,19 +228,16 @@ public class HttpParser implements Parser * @see #parse * @see #parseNext */ - public int parseAvailable() throws IOException + public boolean parseAvailable() throws IOException { - int progress = parseNext(); - int total=progress>0?1:0; + boolean progress=parseNext()>0; // continue parsing while (!isComplete() && _buffer!=null && _buffer.length()>0) { - progress = parseNext(); - if (progress>0) - total++; + progress |= parseNext()>0; } - return total; + return progress; } @@ -236,203 +248,182 @@ public class HttpParser implements Parser */ public int parseNext() throws IOException { - int progress=0; - - if (_state == STATE_END) - return 0; - - if (_buffer==null) + try { - if (_header == null) + int progress=0; + + if (_state == STATE_END) + return 0; + + if (_buffer==null) { - _header=_buffers.getHeader(); - } - _buffer=_header; - _tok0=new View.CaseInsensitive(_header); - _tok1=new View.CaseInsensitive(_header); - _tok0.setPutIndex(_tok0.getIndex()); - _tok1.setPutIndex(_tok1.getIndex()); - } - - - if (_state == STATE_CONTENT && _contentPosition == _contentLength) - { - _state=STATE_END; - _handler.messageComplete(_contentPosition); - return 1; - } - - int length=_buffer.length(); - - // Fill buffer if we can - if (length == 0) - { - long filled=-1; - IOException ex=null; - try - { - filled=fill(); - } - catch(IOException e) - { - LOG.debug(this.toString(),e); - ex=e; - } - - if (filled < 0 || _endp.isInputShutdown()) - { - if (_headResponse && _state>STATE_END) + if (_header == null) { - _state=STATE_END; - _handler.messageComplete(_contentPosition); - return 1; + _header=_buffers.getHeader(); } - if ( _state == STATE_EOF_CONTENT) + _buffer=_header; + _tok0=new View.CaseInsensitive(_header); + _tok1=new View.CaseInsensitive(_header); + _tok0.setPutIndex(_tok0.getIndex()); + _tok1.setPutIndex(_tok1.getIndex()); + } + + + if (_state == STATE_CONTENT && _contentPosition == _contentLength) + { + _state=STATE_END; + _handler.messageComplete(_contentPosition); + returnBuffers(); + return 1; + } + + int length=_buffer.length(); + + // Fill buffer if we can + if (length == 0) + { + int filled=-1; + IOException ex=null; + try { - if (_buffer.length()>0) - { - // TODO should we do this here or fall down to main loop? - Buffer chunk=_buffer.get(_buffer.length()); - _contentPosition += chunk.length(); - _contentView.update(chunk); - _handler.content(chunk); // May recurse here - } - _state=STATE_END; - _handler.messageComplete(_contentPosition); - return 1; + filled=fill(); + LOG.debug("filled {}/{}",filled,_buffer.length()); + } + catch(IOException e) + { + LOG.debug(this.toString(),e); + ex=e; } - if (ex!=null) - throw ex; + if (filled > 0 ) + progress++; + else if (filled < 0 ) + { + _persistent=false; - if (!isComplete() && !isIdle()) - throw new EofException(); - - return -1; - } - length=_buffer.length(); - } - - - // EventHandler header - byte ch; - byte[] array=_buffer.array(); - int last=_state; - while (_state0) - { - if (last!=_state) - { - progress++; - last=_state; - } - - ch=_buffer.get(); - - if (_eol == HttpTokens.CARRIAGE_RETURN && ch == HttpTokens.LINE_FEED) - { - _eol=HttpTokens.LINE_FEED; - continue; - } - _eol=0; - - switch (_state) - { - case STATE_START: - _contentLength=HttpTokens.UNKNOWN_CONTENT; - _cached=null; - if (ch > HttpTokens.SPACE || ch<0) + // do we have content to deliver? + if (_state>STATE_END) { - _buffer.mark(); - _state=STATE_FIELD0; - } - break; - - case STATE_FIELD0: - if (ch == HttpTokens.SPACE) - { - _tok0.update(_buffer.markIndex(), _buffer.getIndex() - 1); - _responseStatus=HttpVersions.CACHE.get(_tok0)==null?-1:0; - _state=STATE_SPACE1; - continue; - } - else if (ch < HttpTokens.SPACE && ch>=0) - { - throw new HttpException(HttpStatus.BAD_REQUEST_400); - } - break; - - case STATE_SPACE1: - if (ch > HttpTokens.SPACE || ch<0) - { - _buffer.mark(); - if (_responseStatus>=0) + if (_buffer.length()>0 && !_headResponse) { - _state=STATE_STATUS; - _responseStatus=ch-'0'; + Buffer chunk=_buffer.get(_buffer.length()); + _contentPosition += chunk.length(); + _contentView.update(chunk); + _handler.content(chunk); // May recurse here } - else - _state=STATE_URI; } - else if (ch < HttpTokens.SPACE) - { - throw new HttpException(HttpStatus.BAD_REQUEST_400); - } - break; - case STATE_STATUS: - if (ch == HttpTokens.SPACE) + // was this unexpected? + switch(_state) { - _tok1.update(_buffer.markIndex(), _buffer.getIndex() - 1); - _state=STATE_SPACE2; - continue; - } - else if (ch>='0' && ch<='9') - { - _responseStatus=_responseStatus*10+(ch-'0'); - continue; - } - else if (ch < HttpTokens.SPACE && ch>=0) - { - _handler.startResponse(HttpMethods.CACHE.lookup(_tok0), _responseStatus, null); - _eol=ch; - _state=STATE_HEADER; - _tok0.setPutIndex(_tok0.getIndex()); - _tok1.setPutIndex(_tok1.getIndex()); - _multiLineValue=null; - continue; - } - // not a digit, so must be a URI - _state=STATE_URI; - _responseStatus=-1; - break; + case STATE_END: + case STATE_SEEKING_EOF: + _state=STATE_END; + break; - case STATE_URI: - if (ch == HttpTokens.SPACE) - { - _tok1.update(_buffer.markIndex(), _buffer.getIndex() - 1); - _state=STATE_SPACE2; - continue; - } - else if (ch < HttpTokens.SPACE && ch>=0) - { - // HTTP/0.9 - _handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _buffer.sliceFromMark(), null); - _state=STATE_END; - _handler.headerComplete(); - _handler.messageComplete(_contentPosition); - return 1; - } - break; + case STATE_EOF_CONTENT: + _state=STATE_END; + _handler.messageComplete(_contentPosition); + break; - case STATE_SPACE2: - if (ch > HttpTokens.SPACE || ch<0) - { - _buffer.mark(); - _state=STATE_FIELD2; + default: + _state=STATE_END; + if (!_headResponse) + _handler.earlyEOF(); + _handler.messageComplete(_contentPosition); } - else if (ch < HttpTokens.SPACE) - { - if (_responseStatus>0) + + if (ex!=null) + throw ex; + + if (!isComplete() && !isIdle()) + throw new EofException(); + + returnBuffers(); + return -1; + } + length=_buffer.length(); + } + + + // Handle header states + byte ch; + byte[] array=_buffer.array(); + int last=_state; + while (_state0) + { + if (last!=_state) + { + progress++; + last=_state; + } + + ch=_buffer.get(); + + if (_eol == HttpTokens.CARRIAGE_RETURN && ch == HttpTokens.LINE_FEED) + { + _eol=HttpTokens.LINE_FEED; + continue; + } + _eol=0; + + switch (_state) + { + case STATE_START: + _contentLength=HttpTokens.UNKNOWN_CONTENT; + _cached=null; + if (ch > HttpTokens.SPACE || ch<0) + { + _buffer.mark(); + _state=STATE_FIELD0; + } + break; + + case STATE_FIELD0: + if (ch == HttpTokens.SPACE) + { + _tok0.update(_buffer.markIndex(), _buffer.getIndex() - 1); + _responseStatus=HttpVersions.CACHE.get(_tok0)==null?-1:0; + _state=STATE_SPACE1; + continue; + } + else if (ch < HttpTokens.SPACE && ch>=0) + { + throw new HttpException(HttpStatus.BAD_REQUEST_400); + } + break; + + case STATE_SPACE1: + if (ch > HttpTokens.SPACE || ch<0) + { + _buffer.mark(); + if (_responseStatus>=0) + { + _state=STATE_STATUS; + _responseStatus=ch-'0'; + } + else + _state=STATE_URI; + } + else if (ch < HttpTokens.SPACE) + { + throw new HttpException(HttpStatus.BAD_REQUEST_400); + } + break; + + case STATE_STATUS: + if (ch == HttpTokens.SPACE) + { + _tok1.update(_buffer.markIndex(), _buffer.getIndex() - 1); + _state=STATE_SPACE2; + continue; + } + else if (ch>='0' && ch<='9') + { + _responseStatus=_responseStatus*10+(ch-'0'); + continue; + } + else if (ch < HttpTokens.SPACE && ch>=0) { _handler.startResponse(HttpMethods.CACHE.lookup(_tok0), _responseStatus, null); _eol=ch; @@ -440,473 +431,568 @@ public class HttpParser implements Parser _tok0.setPutIndex(_tok0.getIndex()); _tok1.setPutIndex(_tok1.getIndex()); _multiLineValue=null; + continue; } - else + // not a digit, so must be a URI + _state=STATE_URI; + _responseStatus=-1; + break; + + case STATE_URI: + if (ch == HttpTokens.SPACE) + { + _tok1.update(_buffer.markIndex(), _buffer.getIndex() - 1); + _state=STATE_SPACE2; + continue; + } + else if (ch < HttpTokens.SPACE && ch>=0) { // HTTP/0.9 - _handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _tok1, null); - _state=STATE_END; + _handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _buffer.sliceFromMark(), null); + _persistent=false; + _state=STATE_SEEKING_EOF; _handler.headerComplete(); _handler.messageComplete(_contentPosition); + returnBuffers(); return 1; } - } - break; + break; - case STATE_FIELD2: - if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED) - { - if (_responseStatus>0) - _handler.startResponse(HttpVersions.CACHE.lookup(_tok0), _responseStatus,_buffer.sliceFromMark()); - else - _handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _tok1, HttpVersions.CACHE.lookup(_buffer.sliceFromMark())); - _eol=ch; - _state=STATE_HEADER; - _tok0.setPutIndex(_tok0.getIndex()); - _tok1.setPutIndex(_tok1.getIndex()); - _multiLineValue=null; - continue; - } - break; - - case STATE_HEADER: - switch(ch) - { - case HttpTokens.COLON: - case HttpTokens.SPACE: - case HttpTokens.TAB: + case STATE_SPACE2: + if (ch > HttpTokens.SPACE || ch<0) { - // header value without name - continuation? - _length=-1; - _state=STATE_HEADER_VALUE; - break; + _buffer.mark(); + _state=STATE_FIELD2; } - - default: + else if (ch < HttpTokens.SPACE) { - // handler last header if any - if (_cached!=null || _tok0.length() > 0 || _tok1.length() > 0 || _multiLineValue != null) + if (_responseStatus>0) { - - Buffer header=_cached!=null?_cached:HttpHeaders.CACHE.lookup(_tok0); - _cached=null; - Buffer value=_multiLineValue == null ? _tok1 : new ByteArrayBuffer(_multiLineValue); - - int ho=HttpHeaders.CACHE.getOrdinal(header); - if (ho >= 0) - { - int vo; - - switch (ho) - { - case HttpHeaders.CONTENT_LENGTH_ORDINAL: - if (_contentLength != HttpTokens.CHUNKED_CONTENT && _responseStatus!=304 && _responseStatus!=204 && (_responseStatus<100 || _responseStatus>=200)) - { - try - { - _contentLength=BufferUtil.toLong(value); - } - catch(NumberFormatException e) - { - LOG.ignore(e); - throw new HttpException(HttpStatus.BAD_REQUEST_400); - } - if (_contentLength <= 0) - _contentLength=HttpTokens.NO_CONTENT; - } - break; - - case HttpHeaders.TRANSFER_ENCODING_ORDINAL: - value=HttpHeaderValues.CACHE.lookup(value); - vo=HttpHeaderValues.CACHE.getOrdinal(value); - if (HttpHeaderValues.CHUNKED_ORDINAL == vo) - _contentLength=HttpTokens.CHUNKED_CONTENT; - else - { - String c=value.toString(StringUtil.__ISO_8859_1); - if (c.endsWith(HttpHeaderValues.CHUNKED)) - _contentLength=HttpTokens.CHUNKED_CONTENT; - - else if (c.indexOf(HttpHeaderValues.CHUNKED) >= 0) - throw new HttpException(400,null); - } - break; - } - } - - _handler.parsedHeader(header, value); + _handler.startResponse(HttpMethods.CACHE.lookup(_tok0), _responseStatus, null); + _eol=ch; + _state=STATE_HEADER; _tok0.setPutIndex(_tok0.getIndex()); _tok1.setPutIndex(_tok1.getIndex()); _multiLineValue=null; } - _buffer.setMarkIndex(-1); - - - // now handle ch - if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED) - { - // work out the _content demarcation - if (_contentLength == HttpTokens.UNKNOWN_CONTENT) - { - if (_responseStatus == 0 // request - || _responseStatus == 304 // not-modified response - || _responseStatus == 204 // no-content response - || _responseStatus < 200) // 1xx response - _contentLength=HttpTokens.NO_CONTENT; - else - _contentLength=HttpTokens.EOF_CONTENT; - } - - _contentPosition=0; - _eol=ch; - if (_eol==HttpTokens.CARRIAGE_RETURN && _buffer.hasContent() && _buffer.peek()==HttpTokens.LINE_FEED) - _eol=_buffer.get(); - - // We convert _contentLength to an int for this switch statement because - // we don't care about the amount of data available just whether there is some. - switch (_contentLength > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) _contentLength) - { - case HttpTokens.EOF_CONTENT: - _state=STATE_EOF_CONTENT; - _handler.headerComplete(); // May recurse here ! - break; - - case HttpTokens.CHUNKED_CONTENT: - _state=STATE_CHUNKED_CONTENT; - _handler.headerComplete(); // May recurse here ! - break; - - case HttpTokens.NO_CONTENT: - _state=STATE_END; - returnBuffers(); - _handler.headerComplete(); - _handler.messageComplete(_contentPosition); - break; - - default: - _state=STATE_CONTENT; - _handler.headerComplete(); // May recurse here ! - break; - } - return 1; - } else { - // New header - _length=1; - _buffer.mark(); - _state=STATE_HEADER_NAME; + // HTTP/0.9 + _handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _tok1, null); + _persistent=false; + _state=STATE_SEEKING_EOF; + _handler.headerComplete(); + _handler.messageComplete(_contentPosition); + returnBuffers(); + return 1; + } + } + break; - // try cached name! - if (array!=null) + case STATE_FIELD2: + if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED) + { + Buffer version; + if (_responseStatus>0) + _handler.startResponse(version=HttpVersions.CACHE.lookup(_tok0), _responseStatus,_buffer.sliceFromMark()); + else + _handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _tok1, version=HttpVersions.CACHE.lookup(_buffer.sliceFromMark())); + _eol=ch; + _persistent=HttpVersions.CACHE.getOrdinal(version)>=HttpVersions.HTTP_1_1_ORDINAL; + _state=STATE_HEADER; + _tok0.setPutIndex(_tok0.getIndex()); + _tok1.setPutIndex(_tok1.getIndex()); + _multiLineValue=null; + continue; + } + break; + + case STATE_HEADER: + switch(ch) + { + case HttpTokens.COLON: + case HttpTokens.SPACE: + case HttpTokens.TAB: + { + // header value without name - continuation? + _length=-1; + _state=STATE_HEADER_VALUE; + break; + } + + default: + { + // handler last header if any + if (_cached!=null || _tok0.length() > 0 || _tok1.length() > 0 || _multiLineValue != null) { - _cached=HttpHeaders.CACHE.getBest(array, _buffer.markIndex(), length+1); + Buffer header=_cached!=null?_cached:HttpHeaders.CACHE.lookup(_tok0); + _cached=null; + Buffer value=_multiLineValue == null ? _tok1 : new ByteArrayBuffer(_multiLineValue); - if (_cached!=null) + int ho=HttpHeaders.CACHE.getOrdinal(header); + if (ho >= 0) { - _length=_cached.length(); - _buffer.setGetIndex(_buffer.markIndex()+_length); - length=_buffer.length(); + int vo; + + switch (ho) + { + case HttpHeaders.CONTENT_LENGTH_ORDINAL: + if (_contentLength != HttpTokens.CHUNKED_CONTENT && _responseStatus!=304 && _responseStatus!=204 && (_responseStatus<100 || _responseStatus>=200)) + { + try + { + _contentLength=BufferUtil.toLong(value); + } + catch(NumberFormatException e) + { + LOG.ignore(e); + throw new HttpException(HttpStatus.BAD_REQUEST_400); + } + if (_contentLength <= 0) + _contentLength=HttpTokens.NO_CONTENT; + } + break; + + case HttpHeaders.TRANSFER_ENCODING_ORDINAL: + value=HttpHeaderValues.CACHE.lookup(value); + vo=HttpHeaderValues.CACHE.getOrdinal(value); + if (HttpHeaderValues.CHUNKED_ORDINAL == vo) + _contentLength=HttpTokens.CHUNKED_CONTENT; + else + { + String c=value.toString(StringUtil.__ISO_8859_1); + if (c.endsWith(HttpHeaderValues.CHUNKED)) + _contentLength=HttpTokens.CHUNKED_CONTENT; + + else if (c.indexOf(HttpHeaderValues.CHUNKED) >= 0) + throw new HttpException(400,null); + } + break; + + case HttpHeaders.CONNECTION_ORDINAL: + switch(HttpHeaderValues.CACHE.getOrdinal(value)) + { + case HttpHeaderValues.CLOSE_ORDINAL: + _persistent=false; + break; + + case HttpHeaderValues.KEEP_ALIVE_ORDINAL: + _persistent=true; + break; + + case -1: // No match, may be multi valued + { + for (String v : value.toString().split(",")) + { + switch(HttpHeaderValues.CACHE.getOrdinal(v.trim())) + { + case HttpHeaderValues.CLOSE_ORDINAL: + _persistent=false; + break; + + case HttpHeaderValues.KEEP_ALIVE_ORDINAL: + _persistent=true; + break; + } + } + break; + } + } + } + } + + _handler.parsedHeader(header, value); + _tok0.setPutIndex(_tok0.getIndex()); + _tok1.setPutIndex(_tok1.getIndex()); + _multiLineValue=null; + } + _buffer.setMarkIndex(-1); + + + // now handle ch + if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED) + { + // work out the _content demarcation + if (_contentLength == HttpTokens.UNKNOWN_CONTENT) + { + if (_responseStatus == 0 // request + || _responseStatus == 304 // not-modified response + || _responseStatus == 204 // no-content response + || _responseStatus < 200) // 1xx response + _contentLength=HttpTokens.NO_CONTENT; + else + _contentLength=HttpTokens.EOF_CONTENT; + } + + _contentPosition=0; + _eol=ch; + if (_eol==HttpTokens.CARRIAGE_RETURN && _buffer.hasContent() && _buffer.peek()==HttpTokens.LINE_FEED) + _eol=_buffer.get(); + + // We convert _contentLength to an int for this switch statement because + // we don't care about the amount of data available just whether there is some. + switch (_contentLength > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) _contentLength) + { + case HttpTokens.EOF_CONTENT: + _state=STATE_EOF_CONTENT; + _handler.headerComplete(); // May recurse here ! + break; + + case HttpTokens.CHUNKED_CONTENT: + _state=STATE_CHUNKED_CONTENT; + _handler.headerComplete(); // May recurse here ! + break; + + case HttpTokens.NO_CONTENT: + _handler.headerComplete(); + _state=_persistent||(_responseStatus>=100&&_responseStatus<200)?STATE_END:STATE_SEEKING_EOF; + _handler.messageComplete(_contentPosition); + returnBuffers(); + return 1; + + default: + _state=STATE_CONTENT; + _handler.headerComplete(); // May recurse here ! + break; + } + return 1; + } + else + { + // New header + _length=1; + _buffer.mark(); + _state=STATE_HEADER_NAME; + + // try cached name! + if (array!=null) + { + _cached=HttpHeaders.CACHE.getBest(array, _buffer.markIndex(), length+1); + + if (_cached!=null) + { + _length=_cached.length(); + _buffer.setGetIndex(_buffer.markIndex()+_length); + length=_buffer.length(); + } } } } } - } - break; + break; - case STATE_HEADER_NAME: - switch(ch) - { - case HttpTokens.CARRIAGE_RETURN: - case HttpTokens.LINE_FEED: - if (_length > 0) - _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length); - _eol=ch; - _state=STATE_HEADER; - break; - case HttpTokens.COLON: - if (_length > 0 && _cached==null) - _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length); - _length=-1; - _state=STATE_HEADER_VALUE; - break; - case HttpTokens.SPACE: - case HttpTokens.TAB: - break; - default: + case STATE_HEADER_NAME: + switch(ch) { - _cached=null; - if (_length == -1) - _buffer.mark(); - _length=_buffer.getIndex() - _buffer.markIndex(); - _state=STATE_HEADER_IN_NAME; - } - } - - break; - - case STATE_HEADER_IN_NAME: - switch(ch) - { - case HttpTokens.CARRIAGE_RETURN: - case HttpTokens.LINE_FEED: - if (_length > 0) - _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length); - _eol=ch; - _state=STATE_HEADER; - break; - case HttpTokens.COLON: - if (_length > 0 && _cached==null) - _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length); - _length=-1; - _state=STATE_HEADER_VALUE; - break; - case HttpTokens.SPACE: - case HttpTokens.TAB: - _state=STATE_HEADER_NAME; - break; - default: - { - _cached=null; - _length++; - } - } - break; - - case STATE_HEADER_VALUE: - switch(ch) - { - case HttpTokens.CARRIAGE_RETURN: - case HttpTokens.LINE_FEED: - if (_length > 0) + case HttpTokens.CARRIAGE_RETURN: + case HttpTokens.LINE_FEED: + if (_length > 0) + _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length); + _eol=ch; + _state=STATE_HEADER; + break; + case HttpTokens.COLON: + if (_length > 0 && _cached==null) + _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length); + _length=-1; + _state=STATE_HEADER_VALUE; + break; + case HttpTokens.SPACE: + case HttpTokens.TAB: + break; + default: { - if (_tok1.length() == 0) - _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length); - else - { - // Continuation line! - if (_multiLineValue == null) _multiLineValue=_tok1.toString(StringUtil.__ISO_8859_1); - _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length); - _multiLineValue += " " + _tok1.toString(StringUtil.__ISO_8859_1); - } + _cached=null; + if (_length == -1) + _buffer.mark(); + _length=_buffer.getIndex() - _buffer.markIndex(); + _state=STATE_HEADER_IN_NAME; } - _eol=ch; - _state=STATE_HEADER; - break; - case HttpTokens.SPACE: - case HttpTokens.TAB: - break; - default: - { - if (_length == -1) - _buffer.mark(); - _length=_buffer.getIndex() - _buffer.markIndex(); - _state=STATE_HEADER_IN_VALUE; } - } - break; - case STATE_HEADER_IN_VALUE: - switch(ch) - { - case HttpTokens.CARRIAGE_RETURN: - case HttpTokens.LINE_FEED: - if (_length > 0) + break; + + case STATE_HEADER_IN_NAME: + switch(ch) + { + case HttpTokens.CARRIAGE_RETURN: + case HttpTokens.LINE_FEED: + if (_length > 0) + _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length); + _eol=ch; + _state=STATE_HEADER; + break; + case HttpTokens.COLON: + if (_length > 0 && _cached==null) + _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length); + _length=-1; + _state=STATE_HEADER_VALUE; + break; + case HttpTokens.SPACE: + case HttpTokens.TAB: + _state=STATE_HEADER_NAME; + break; + default: { - if (_tok1.length() == 0) - _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length); - else - { - // Continuation line! - if (_multiLineValue == null) _multiLineValue=_tok1.toString(StringUtil.__ISO_8859_1); - _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length); - _multiLineValue += " " + _tok1.toString(StringUtil.__ISO_8859_1); - } + _cached=null; + _length++; } - _eol=ch; - _state=STATE_HEADER; - break; - case HttpTokens.SPACE: - case HttpTokens.TAB: - _state=STATE_HEADER_VALUE; - break; - default: - _length++; - } - break; - } - } // end of HEADER states loop + } + break; - // ========================== + case STATE_HEADER_VALUE: + switch(ch) + { + case HttpTokens.CARRIAGE_RETURN: + case HttpTokens.LINE_FEED: + if (_length > 0) + { + if (_tok1.length() == 0) + _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length); + else + { + // Continuation line! + if (_multiLineValue == null) _multiLineValue=_tok1.toString(StringUtil.__ISO_8859_1); + _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length); + _multiLineValue += " " + _tok1.toString(StringUtil.__ISO_8859_1); + } + } + _eol=ch; + _state=STATE_HEADER; + break; + case HttpTokens.SPACE: + case HttpTokens.TAB: + break; + default: + { + if (_length == -1) + _buffer.mark(); + _length=_buffer.getIndex() - _buffer.markIndex(); + _state=STATE_HEADER_IN_VALUE; + } + } + break; - // Handle HEAD response - if (_responseStatus>0 && _headResponse) - { - _state=STATE_END; - _handler.messageComplete(_contentLength); - } + case STATE_HEADER_IN_VALUE: + switch(ch) + { + case HttpTokens.CARRIAGE_RETURN: + case HttpTokens.LINE_FEED: + if (_length > 0) + { + if (_tok1.length() == 0) + _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length); + else + { + // Continuation line! + if (_multiLineValue == null) _multiLineValue=_tok1.toString(StringUtil.__ISO_8859_1); + _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length); + _multiLineValue += " " + _tok1.toString(StringUtil.__ISO_8859_1); + } + } + _eol=ch; + _state=STATE_HEADER; + break; + case HttpTokens.SPACE: + case HttpTokens.TAB: + _state=STATE_HEADER_VALUE; + break; + default: + _length++; + } + break; + } + } // end of HEADER states loop - // ========================== + // ========================== - // Handle _content - length=_buffer.length(); - Buffer chunk; - last=_state; - while (_state > STATE_END && length > 0) - { - if (last!=_state) + // Handle HEAD response + if (_responseStatus>0 && _headResponse) { - progress++; - last=_state; + _state=_persistent||(_responseStatus>=100&&_responseStatus<200)?STATE_END:STATE_SEEKING_EOF; + _handler.messageComplete(_contentLength); } - if (_eol == HttpTokens.CARRIAGE_RETURN && _buffer.peek() == HttpTokens.LINE_FEED) - { - _eol=_buffer.get(); - length=_buffer.length(); - continue; - } - _eol=0; - switch (_state) - { - case STATE_EOF_CONTENT: - chunk=_buffer.get(_buffer.length()); - _contentPosition += chunk.length(); - _contentView.update(chunk); - _handler.content(chunk); // May recurse here - // TODO adjust the _buffer to keep unconsumed content - return 1; - case STATE_CONTENT: + // ========================== + + // Handle _content + length=_buffer.length(); + Buffer chunk; + last=_state; + while (_state > STATE_END && length > 0) + { + if (last!=_state) { - long remaining=_contentLength - _contentPosition; - if (remaining == 0) + progress++; + last=_state; + } + + if (_eol == HttpTokens.CARRIAGE_RETURN && _buffer.peek() == HttpTokens.LINE_FEED) + { + _eol=_buffer.get(); + length=_buffer.length(); + continue; + } + _eol=0; + switch (_state) + { + case STATE_EOF_CONTENT: + chunk=_buffer.get(_buffer.length()); + _contentPosition += chunk.length(); + _contentView.update(chunk); + _handler.content(chunk); // May recurse here + // TODO adjust the _buffer to keep unconsumed content + return 1; + + case STATE_CONTENT: { - _state=STATE_END; - _handler.messageComplete(_contentPosition); + long remaining=_contentLength - _contentPosition; + if (remaining == 0) + { + _state=_persistent?STATE_END:STATE_SEEKING_EOF; + _handler.messageComplete(_contentPosition); + returnBuffers(); + return 1; + } + + if (length > remaining) + { + // We can cast reamining to an int as we know that it is smaller than + // or equal to length which is already an int. + length=(int)remaining; + } + + chunk=_buffer.get(length); + _contentPosition += chunk.length(); + _contentView.update(chunk); + _handler.content(chunk); // May recurse here + + if(_contentPosition == _contentLength) + { + _state=_persistent?STATE_END:STATE_SEEKING_EOF; + _handler.messageComplete(_contentPosition); + returnBuffers(); + } + // TODO adjust the _buffer to keep unconsumed content return 1; } - if (length > remaining) + case STATE_CHUNKED_CONTENT: { - // We can cast reamining to an int as we know that it is smaller than - // or equal to length which is already an int. - length=(int)remaining; - } - - chunk=_buffer.get(length); - _contentPosition += chunk.length(); - _contentView.update(chunk); - _handler.content(chunk); // May recurse here - - if(_contentPosition == _contentLength) - { - _state=STATE_END; - _handler.messageComplete(_contentPosition); - } - // TODO adjust the _buffer to keep unconsumed content - return 1; - } - - case STATE_CHUNKED_CONTENT: - { - ch=_buffer.peek(); - if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED) - _eol=_buffer.get(); - else if (ch <= HttpTokens.SPACE) - _buffer.get(); - else - { - _chunkLength=0; - _chunkPosition=0; - _state=STATE_CHUNK_SIZE; - } - break; - } - - case STATE_CHUNK_SIZE: - { - ch=_buffer.get(); - if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED) - { - _eol=ch; - - if (_chunkLength == 0) - { - if (_eol==HttpTokens.CARRIAGE_RETURN && _buffer.hasContent() && _buffer.peek()==HttpTokens.LINE_FEED) - _eol=_buffer.get(); - _state=STATE_END; - _handler.messageComplete(_contentPosition); - return 1; - } + ch=_buffer.peek(); + if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED) + _eol=_buffer.get(); + else if (ch <= HttpTokens.SPACE) + _buffer.get(); else - _state=STATE_CHUNK; - } - else if (ch <= HttpTokens.SPACE || ch == HttpTokens.SEMI_COLON) - _state=STATE_CHUNK_PARAMS; - else if (ch >= '0' && ch <= '9') - _chunkLength=_chunkLength * 16 + (ch - '0'); - else if (ch >= 'a' && ch <= 'f') - _chunkLength=_chunkLength * 16 + (10 + ch - 'a'); - else if (ch >= 'A' && ch <= 'F') - _chunkLength=_chunkLength * 16 + (10 + ch - 'A'); - else - throw new IOException("bad chunk char: " + ch); - break; - } - - case STATE_CHUNK_PARAMS: - { - ch=_buffer.get(); - if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED) - { - _eol=ch; - if (_chunkLength == 0) { - if (_eol==HttpTokens.CARRIAGE_RETURN && _buffer.hasContent() && _buffer.peek()==HttpTokens.LINE_FEED) - _eol=_buffer.get(); - _state=STATE_END; - _handler.messageComplete(_contentPosition); - return 1; + _chunkLength=0; + _chunkPosition=0; + _state=STATE_CHUNK_SIZE; } - else - _state=STATE_CHUNK; - } - break; - } - - case STATE_CHUNK: - { - int remaining=_chunkLength - _chunkPosition; - if (remaining == 0) - { - _state=STATE_CHUNKED_CONTENT; break; } - else if (length > remaining) - length=remaining; - chunk=_buffer.get(length); - _contentPosition += chunk.length(); - _chunkPosition += chunk.length(); - _contentView.update(chunk); - _handler.content(chunk); // May recurse here - // TODO adjust the _buffer to keep unconsumed content - return 1; + + case STATE_CHUNK_SIZE: + { + ch=_buffer.get(); + if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED) + { + _eol=ch; + + if (_chunkLength == 0) + { + if (_eol==HttpTokens.CARRIAGE_RETURN && _buffer.hasContent() && _buffer.peek()==HttpTokens.LINE_FEED) + _eol=_buffer.get(); + _state=_persistent?STATE_END:STATE_SEEKING_EOF; + _handler.messageComplete(_contentPosition); + returnBuffers(); + return 1; + } + else + _state=STATE_CHUNK; + } + else if (ch <= HttpTokens.SPACE || ch == HttpTokens.SEMI_COLON) + _state=STATE_CHUNK_PARAMS; + else if (ch >= '0' && ch <= '9') + _chunkLength=_chunkLength * 16 + (ch - '0'); + else if (ch >= 'a' && ch <= 'f') + _chunkLength=_chunkLength * 16 + (10 + ch - 'a'); + else if (ch >= 'A' && ch <= 'F') + _chunkLength=_chunkLength * 16 + (10 + ch - 'A'); + else + throw new IOException("bad chunk char: " + ch); + break; + } + + case STATE_CHUNK_PARAMS: + { + ch=_buffer.get(); + if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED) + { + _eol=ch; + if (_chunkLength == 0) + { + if (_eol==HttpTokens.CARRIAGE_RETURN && _buffer.hasContent() && _buffer.peek()==HttpTokens.LINE_FEED) + _eol=_buffer.get(); + _state=_persistent?STATE_END:STATE_SEEKING_EOF; + _handler.messageComplete(_contentPosition); + returnBuffers(); + return 1; + } + else + _state=STATE_CHUNK; + } + break; + } + + case STATE_CHUNK: + { + int remaining=_chunkLength - _chunkPosition; + if (remaining == 0) + { + _state=STATE_CHUNKED_CONTENT; + break; + } + else if (length > remaining) + length=remaining; + chunk=_buffer.get(length); + _contentPosition += chunk.length(); + _chunkPosition += chunk.length(); + _contentView.update(chunk); + _handler.content(chunk); // May recurse here + // TODO adjust the _buffer to keep unconsumed content + return 1; + } + + case STATE_SEEKING_EOF: + { + // Skip all data + _buffer.clear(); + break; + } } + + length=_buffer.length(); } - length=_buffer.length(); + return progress; + } + catch(HttpException e) + { + _persistent=false; + _state=STATE_SEEKING_EOF; + throw e; } - - return progress; } /* ------------------------------------------------------------------------------- */ /** fill the buffers from the endpoint * */ - public long fill() throws IOException + protected int fill() throws IOException { // Do we have a buffer? if (_buffer==null) @@ -942,11 +1028,15 @@ public class HttpParser implements Parser // Are we full? if (_buffer.space() == 0) + { + LOG.warn("Full {}",_buffer.toDetailString()); throw new HttpException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413, "FULL "+(_buffer==_body?"body":"head")); + } try { - return _endp.fill(_buffer); + int filled = _endp.fill(_buffer); + return filled; } catch(IOException e) { @@ -958,45 +1048,12 @@ public class HttpParser implements Parser return -1; } - /* ------------------------------------------------------------------------------- */ - /** Skip any CRLFs in buffers - * - */ - public void skipCRLF() - { - - while (_header!=null && _header.length()>0) - { - byte ch = _header.peek(); - if (ch==HttpTokens.CARRIAGE_RETURN || ch==HttpTokens.LINE_FEED) - { - _eol=ch; - _header.skip(1); - } - else - break; - } - - while (_body!=null && _body.length()>0) - { - byte ch = _body.peek(); - if (ch==HttpTokens.CARRIAGE_RETURN || ch==HttpTokens.LINE_FEED) - { - _eol=ch; - _body.skip(1); - } - else - break; - } - - } - /* ------------------------------------------------------------------------------- */ public void reset() { // reset state _contentView.setGetIndex(_contentView.putIndex()); - _state=STATE_START; + _state=_persistent?STATE_START:(_endp.isInputShutdown()?STATE_END:STATE_SEEKING_EOF); _contentLength=HttpTokens.UNKNOWN_CONTENT; _contentPosition=0; _length=0; @@ -1036,6 +1093,7 @@ public class HttpParser implements Parser _body.setMarkIndex(-1); _buffer=_header; + returnBuffers(); } @@ -1077,7 +1135,11 @@ public class HttpParser implements Parser @Override public String toString() { - return "state=" + _state + " length=" + _length + " len=" + _contentLength; + return String.format("%s{s=%d,l=%d,c=%d}", + getClass().getSimpleName(), + _state, + _length, + _contentLength); } /* ------------------------------------------------------------ */ @@ -1110,7 +1172,8 @@ public class HttpParser implements Parser { if (_contentView.length()>0) return _contentView; - if (getState() <= HttpParser.STATE_END) + + if (getState() <= STATE_END || isState(STATE_SEEKING_EOF)) return null; try @@ -1118,11 +1181,11 @@ public class HttpParser implements Parser parseNext(); // parse until some progress is made (or IOException thrown for timeout) - while(_contentView.length() == 0 && !isState(HttpParser.STATE_END) && _endp!=null && _endp.isOpen()) + while(_contentView.length() == 0 && !(isState(HttpParser.STATE_END)||isState(HttpParser.STATE_SEEKING_EOF)) && _endp!=null && _endp.isOpen()) { if (!_endp.isBlocking()) { - if (_endp.isBufferingInput() && parseNext()>0) + if (parseNext()>0) continue; if (!_endp.blockReadable(maxIdleTime)) @@ -1137,6 +1200,7 @@ public class HttpParser implements Parser } catch(IOException e) { + // TODO is this needed? _endp.close(); throw e; } @@ -1198,6 +1262,9 @@ public class HttpParser implements Parser */ public abstract void startResponse(Buffer version, int status, Buffer reason) throws IOException; + + public void earlyEOF() + {} } diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/Parser.java b/jetty-http/src/main/java/org/eclipse/jetty/http/Parser.java index c953c67c175..5817c315b4f 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/Parser.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/Parser.java @@ -26,13 +26,17 @@ public interface Parser boolean isComplete(); /** - * @return An indication of progress, typically the number of bytes filled plus the events parsed: -1 means EOF read, 0 no progress, >0 progress + * @return True if progress made * @throws IOException */ - int parseAvailable() throws IOException; + boolean parseAvailable() throws IOException; boolean isMoreInBuffer() throws IOException; boolean isIdle(); + + boolean isPersistent(); + + void setPersistent(boolean persistent); } diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/gzip/GzipResponseWrapper.java b/jetty-http/src/main/java/org/eclipse/jetty/http/gzip/GzipResponseWrapper.java index 5b331e149c2..12275fe9104 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/gzip/GzipResponseWrapper.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/gzip/GzipResponseWrapper.java @@ -22,7 +22,6 @@ import java.io.UnsupportedEncodingException; import java.util.Set; import javax.servlet.ServletOutputStream; -import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; @@ -121,7 +120,7 @@ public class GzipResponseWrapper extends HttpServletResponseWrapper public void setStatus(int sc, String sm) { super.setStatus(sc,sm); - if (sc<200||sc>=300) + if (sc<200 || sc==204 || sc==205 || sc>=300) noGzip(); } @@ -132,7 +131,7 @@ public class GzipResponseWrapper extends HttpServletResponseWrapper public void setStatus(int sc) { super.setStatus(sc); - if (sc<200||sc>=300) + if (sc<200 || sc==204 || sc==205 ||sc>=300) noGzip(); } diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/ssl/SslContextFactory.java b/jetty-http/src/main/java/org/eclipse/jetty/http/ssl/SslContextFactory.java index 86e53a270d7..7a5d156f0e1 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/ssl/SslContextFactory.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/ssl/SslContextFactory.java @@ -1,1525 +1,24 @@ -//======================================================================== -//Copyright (c) Webtide LLC -//------------------------------------------------------------------------ -//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.apache.org/licenses/LICENSE-2.0.txt -// -//You may elect to redistribute this code under either of these licenses. -//======================================================================== - package org.eclipse.jetty.http.ssl; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.InetAddress; -import java.security.InvalidParameterException; -import java.security.KeyStore; -import java.security.SecureRandom; -import java.security.Security; -import java.security.cert.CRL; -import java.security.cert.CertStore; -import java.security.cert.Certificate; -import java.security.cert.CollectionCertStoreParameters; -import java.security.cert.PKIXBuilderParameters; -import java.security.cert.X509CertSelector; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import javax.net.ssl.CertPathTrustManagerParameters; -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLServerSocket; -import javax.net.ssl.SSLServerSocketFactory; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509KeyManager; -import javax.net.ssl.X509TrustManager; - -import org.eclipse.jetty.http.security.Password; -import org.eclipse.jetty.util.IO; -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.resource.Resource; -import org.eclipse.jetty.util.security.CertificateUtils; -import org.eclipse.jetty.util.security.CertificateValidator; - /* ------------------------------------------------------------ */ /** - * SslContextFactory is used to configure SSL connectors - * as well as HttpClient. It holds all SSL parameters and - * creates SSL context based on these parameters to be - * used by the SSL connectors. + * @deprecated Use org.eclipse.jetty.util.ssl.SslContextFactory */ -public class SslContextFactory extends AbstractLifeCycle +public class SslContextFactory extends org.eclipse.jetty.util.ssl.SslContextFactory { - private static final Logger LOG = Log.getLogger(SslContextFactory.class); - - public static final String DEFAULT_KEYMANAGERFACTORY_ALGORITHM = - (Security.getProperty("ssl.KeyManagerFactory.algorithm") == null ? - "SunX509" : Security.getProperty("ssl.KeyManagerFactory.algorithm")); - public static final String DEFAULT_TRUSTMANAGERFACTORY_ALGORITHM = - (Security.getProperty("ssl.TrustManagerFactory.algorithm") == null ? - "SunX509" : Security.getProperty("ssl.TrustManagerFactory.algorithm")); - - /** Default value for the keystore location path. */ - public static final String DEFAULT_KEYSTORE_PATH = - System.getProperty("user.home") + File.separator + ".keystore"; - - /** String name of key password property. */ - public static final String KEYPASSWORD_PROPERTY = "org.eclipse.jetty.ssl.keypassword"; - - /** String name of keystore password property. */ - public static final String PASSWORD_PROPERTY = "org.eclipse.jetty.ssl.password"; - - /** Excluded protocols. */ - private final Set _excludeProtocols = new HashSet(); - // private final Set _excludeProtocols = new HashSet(Collections.singleton("SSLv2Hello")); - /** Included protocols. */ - private Set _includeProtocols = null; - - /** Excluded cipher suites. */ - private final Set _excludeCipherSuites = new HashSet(); - /** Included cipher suites. */ - private Set _includeCipherSuites = null; - - /** Keystore path. */ - private String _keyStorePath; - /** Keystore provider name */ - private String _keyStoreProvider; - /** Keystore type */ - private String _keyStoreType = "JKS"; - /** Keystore input stream */ - private InputStream _keyStoreInputStream; - - /** SSL certificate alias */ - private String _certAlias; - - /** Truststore path */ - private String _trustStorePath; - /** Truststore provider name */ - private String _trustStoreProvider; - /** Truststore type */ - private String _trustStoreType = "JKS"; - /** Truststore input stream */ - private InputStream _trustStoreInputStream; - - /** Set to true if client certificate authentication is required */ - private boolean _needClientAuth = false; - /** Set to true if client certificate authentication is desired */ - private boolean _wantClientAuth = false; - - /** Set to true if renegotiation is allowed */ - private boolean _allowRenegotiate = true; - - /** Keystore password */ - private transient Password _keyStorePassword; - /** Key manager password */ - private transient Password _keyManagerPassword; - /** Truststore password */ - private transient Password _trustStorePassword; - - /** SSL provider name */ - private String _sslProvider; - /** SSL protocol name */ - private String _sslProtocol = "TLS"; - - /** SecureRandom algorithm */ - private String _secureRandomAlgorithm; - /** KeyManager factory algorithm */ - private String _keyManagerFactoryAlgorithm = DEFAULT_KEYMANAGERFACTORY_ALGORITHM; - /** TrustManager factory algorithm */ - private String _trustManagerFactoryAlgorithm = DEFAULT_TRUSTMANAGERFACTORY_ALGORITHM; - - /** Set to true if SSL certificate validation is required */ - private boolean _validateCerts; - /** Set to true if SSL certificate of the peer validation is required */ - private boolean _validatePeerCerts; - /** Maximum certification path length (n - number of intermediate certs, -1 for unlimited) */ - private int _maxCertPathLength = -1; - /** Path to file that contains Certificate Revocation List */ - private String _crlPath; - /** Set to true to enable CRL Distribution Points (CRLDP) support */ - private boolean _enableCRLDP = false; - /** Set to true to enable On-Line Certificate Status Protocol (OCSP) support */ - private boolean _enableOCSP = false; - /** Location of OCSP Responder */ - private String _ocspResponderURL; - - /** SSL keystore */ - private KeyStore _keyStore; - /** SSL truststore */ - private KeyStore _trustStore; - /** Set to true to enable SSL Session caching */ - private boolean _sessionCachingEnabled = true; - /** SSL session cache size */ - private int _sslSessionCacheSize; - /** SSL session timeout */ - private int _sslSessionTimeout; - - /** SSL context */ - private SSLContext _context; - - private boolean _trustAll; - - /* ------------------------------------------------------------ */ - /** - * Construct an instance of SslContextFactory - * Default constructor for use in XmlConfiguration files - */ public SslContextFactory() { - _trustAll=true; + super(); } - /* ------------------------------------------------------------ */ - /** - * Construct an instance of SslContextFactory - * Default constructor for use in XmlConfiguration files - */ public SslContextFactory(boolean trustAll) { - _trustAll=trustAll; + super(trustAll); } - /* ------------------------------------------------------------ */ - /** - * Construct an instance of SslContextFactory - * @param keyStorePath default keystore location - */ public SslContextFactory(String keyStorePath) { - _keyStorePath = keyStorePath; + super(keyStorePath); } - - /* ------------------------------------------------------------ */ - /** - * Create the SSLContext object and start the lifecycle - * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart() - */ - @Override - protected void doStart() throws Exception - { - if (_context == null) - { - if (_keyStore==null && _keyStoreInputStream == null && _keyStorePath == null && - _trustStore==null && _trustStoreInputStream == null && _trustStorePath == null ) - { - TrustManager[] trust_managers=null; - - if (_trustAll) - { - LOG.info("No keystore or trust store configured. ACCEPTING UNTRUSTED CERTIFICATES!!!!!"); - // Create a trust manager that does not validate certificate chains - TrustManager trustAllCerts = new X509TrustManager() - { - public java.security.cert.X509Certificate[] getAcceptedIssuers() - { - return null; - } - - public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) - { - } - - public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) - { - } - }; - trust_managers = new TrustManager[] { trustAllCerts }; - } - - SecureRandom secureRandom = (_secureRandomAlgorithm == null)?null:SecureRandom.getInstance(_secureRandomAlgorithm); - _context = SSLContext.getInstance(_sslProtocol); - _context.init(null, trust_managers, secureRandom); - } - else - { - // verify that keystore and truststore - // parameters are set up correctly - try - { - checkKeyStore(); - } - catch(IllegalStateException e) - { - LOG.ignore(e); - } - - KeyStore keyStore = loadKeyStore(); - KeyStore trustStore = loadTrustStore(); - - Collection crls = loadCRL(_crlPath); - - if (_validateCerts && keyStore != null) - { - if (_certAlias == null) - { - List aliases = Collections.list(keyStore.aliases()); - _certAlias = aliases.size() == 1 ? aliases.get(0) : null; - } - - Certificate cert = _certAlias == null?null:keyStore.getCertificate(_certAlias); - if (cert == null) - { - throw new Exception("No certificate found in the keystore" + (_certAlias==null ? "":" for alias " + _certAlias)); - } - - CertificateValidator validator = new CertificateValidator(trustStore, crls); - validator.setMaxCertPathLength(_maxCertPathLength); - validator.setEnableCRLDP(_enableCRLDP); - validator.setEnableOCSP(_enableOCSP); - validator.setOcspResponderURL(_ocspResponderURL); - validator.validate(keyStore, cert); - } - - KeyManager[] keyManagers = getKeyManagers(keyStore); - TrustManager[] trustManagers = getTrustManagers(trustStore,crls); - - SecureRandom secureRandom = (_secureRandomAlgorithm == null)?null:SecureRandom.getInstance(_secureRandomAlgorithm); - _context = (_sslProvider == null)?SSLContext.getInstance(_sslProtocol):SSLContext.getInstance(_sslProtocol,_sslProvider); - _context.init(keyManagers,trustManagers,secureRandom); - - SSLEngine engine=newSslEngine(); - LOG.info("Enabled Protocols {} of {}",Arrays.asList(engine.getEnabledProtocols()),Arrays.asList(engine.getSupportedProtocols())); - LOG.debug("Enabled Ciphers {} of {}",Arrays.asList(engine.getEnabledCipherSuites()),Arrays.asList(engine.getSupportedCipherSuites())); - } - } - } - - /* ------------------------------------------------------------ */ - /** - * @return The array of protocol names to exclude from - * {@link SSLEngine#setEnabledProtocols(String[])} - */ - public String[] getExcludeProtocols() - { - return _excludeProtocols.toArray(new String[_excludeProtocols.size()]); - } - - /* ------------------------------------------------------------ */ - /** - * @param Protocols - * The array of protocol names to exclude from - * {@link SSLEngine#setEnabledProtocols(String[])} - */ - public void setExcludeProtocols(String... protocols) - { - checkNotStarted(); - - _excludeProtocols.clear(); - _excludeProtocols.addAll(Arrays.asList(protocols)); - } - - /* ------------------------------------------------------------ */ - /** - * @param protocol Protocol names to add to {@link SSLEngine#setEnabledProtocols(String[])} - */ - public void addExcludeProtocols(String... protocol) - { - checkNotStarted(); - _excludeProtocols.addAll(Arrays.asList(protocol)); - } - - /* ------------------------------------------------------------ */ - /** - * @return The array of protocol names to include in - * {@link SSLEngine#setEnabledProtocols(String[])} - */ - public String[] getIncludeProtocols() - { - return _includeProtocols.toArray(new String[_includeProtocols.size()]); - } - - /* ------------------------------------------------------------ */ - /** - * @param Protocols - * The array of protocol names to include in - * {@link SSLEngine#setEnabledProtocols(String[])} - */ - public void setIncludeProtocols(String... protocols) - { - checkNotStarted(); - - _includeProtocols = new HashSet(Arrays.asList(protocols)); - } - - /* ------------------------------------------------------------ */ - /** - * @return The array of cipher suite names to exclude from - * {@link SSLEngine#setEnabledCipherSuites(String[])} - */ - public String[] getExcludeCipherSuites() - { - return _excludeCipherSuites.toArray(new String[_excludeCipherSuites.size()]); - } - - /* ------------------------------------------------------------ */ - /** - * @param cipherSuites - * The array of cipher suite names to exclude from - * {@link SSLEngine#setEnabledCipherSuites(String[])} - */ - public void setExcludeCipherSuites(String... cipherSuites) - { - checkNotStarted(); - _excludeCipherSuites.clear(); - _excludeCipherSuites.addAll(Arrays.asList(cipherSuites)); - } - - /* ------------------------------------------------------------ */ - /** - * @param cipher Cipher names to add to {@link SSLEngine#setEnabledCipherSuites(String[])} - */ - public void addExcludeCipherSuites(String... cipher) - { - checkNotStarted(); - _excludeCipherSuites.addAll(Arrays.asList(cipher)); - } - - /* ------------------------------------------------------------ */ - /** - * @return The array of cipher suite names to include in - * {@link SSLEngine#setEnabledCipherSuites(String[])} - */ - public String[] getIncludeCipherSuites() - { - return _includeCipherSuites.toArray(new String[_includeCipherSuites.size()]); - } - - /* ------------------------------------------------------------ */ - /** - * @param cipherSuites - * The array of cipher suite names to include in - * {@link SSLEngine#setEnabledCipherSuites(String[])} - */ - public void setIncludeCipherSuites(String... cipherSuites) - { - checkNotStarted(); - - _includeCipherSuites = new HashSet(Arrays.asList(cipherSuites)); - } - - /* ------------------------------------------------------------ */ - /** - * @return The file or URL of the SSL Key store. - */ - public String getKeyStorePath() - { - return _keyStorePath; - } - - /* ------------------------------------------------------------ */ - @Deprecated - public String getKeyStore() - { - return _keyStorePath; - } - - /* ------------------------------------------------------------ */ - /** - * @param keyStorePath - * The file or URL of the SSL Key store. - */ - public void setKeyStorePath(String keyStorePath) - { - checkNotStarted(); - - _keyStorePath = keyStorePath; - } - - /* ------------------------------------------------------------ */ - /** - * @param keyStorePath - * @deprecated Use {@link #setKeyStorePath(String)} - */ - @Deprecated - public void setKeyStore(String keyStorePath) - { - checkNotStarted(); - - _keyStorePath = keyStorePath; - } - - /* ------------------------------------------------------------ */ - /** - * @return The provider of the key store - */ - public String getKeyStoreProvider() - { - return _keyStoreProvider; - } - - /* ------------------------------------------------------------ */ - /** - * @param keyStoreProvider - * The provider of the key store - */ - public void setKeyStoreProvider(String keyStoreProvider) - { - checkNotStarted(); - - _keyStoreProvider = keyStoreProvider; - } - - /* ------------------------------------------------------------ */ - /** - * @return The type of the key store (default "JKS") - */ - public String getKeyStoreType() - { - return (_keyStoreType); - } - - /* ------------------------------------------------------------ */ - /** - * @param keyStoreType - * The type of the key store (default "JKS") - */ - public void setKeyStoreType(String keyStoreType) - { - checkNotStarted(); - - _keyStoreType = keyStoreType; - } - - /* ------------------------------------------------------------ */ - /** Get the _keyStoreInputStream. - * @return the _keyStoreInputStream - * - * @deprecated - */ - @Deprecated - public InputStream getKeyStoreInputStream() - { - checkKeyStore(); - - return _keyStoreInputStream; - } - - /* ------------------------------------------------------------ */ - /** Set the keyStoreInputStream. - * @param keyStoreInputStream the InputStream to the KeyStore - * - * @deprecated Use {@link #setKeyStore(KeyStore)} - */ - @Deprecated - public void setKeyStoreInputStream(InputStream keyStoreInputStream) - { - checkNotStarted(); - - _keyStoreInputStream = keyStoreInputStream; - } - - /* ------------------------------------------------------------ */ - /** - * @return Alias of SSL certificate for the connector - */ - public String getCertAlias() - { - return _certAlias; - } - - /* ------------------------------------------------------------ */ - /** - * @param certAlias - * Alias of SSL certificate for the connector - */ - public void setCertAlias(String certAlias) - { - checkNotStarted(); - - _certAlias = certAlias; - } - - /* ------------------------------------------------------------ */ - /** - * @return The file name or URL of the trust store location - */ - public String getTrustStore() - { - return _trustStorePath; - } - - /* ------------------------------------------------------------ */ - /** - * @param trustStorePath - * The file name or URL of the trust store location - */ - public void setTrustStore(String trustStorePath) - { - checkNotStarted(); - - _trustStorePath = trustStorePath; - } - - /* ------------------------------------------------------------ */ - /** - * @return The provider of the trust store - */ - public String getTrustStoreProvider() - { - return _trustStoreProvider; - } - - /* ------------------------------------------------------------ */ - /** - * @param trustStoreProvider - * The provider of the trust store - */ - public void setTrustStoreProvider(String trustStoreProvider) - { - checkNotStarted(); - - _trustStoreProvider = trustStoreProvider; - } - - /* ------------------------------------------------------------ */ - /** - * @return The type of the trust store (default "JKS") - */ - public String getTrustStoreType() - { - return _trustStoreType; - } - - /* ------------------------------------------------------------ */ - /** - * @param trustStoreType - * The type of the trust store (default "JKS") - */ - public void setTrustStoreType(String trustStoreType) - { - checkNotStarted(); - - _trustStoreType = trustStoreType; - } - - /* ------------------------------------------------------------ */ - /** Get the _trustStoreInputStream. - * @return the _trustStoreInputStream - * - * @deprecated - */ - @Deprecated - public InputStream getTrustStoreInputStream() - { - checkKeyStore(); - - return _trustStoreInputStream; - } - - /* ------------------------------------------------------------ */ - /** Set the _trustStoreInputStream. - * @param trustStoreInputStream the InputStream to the TrustStore - * - * @deprecated - */ - @Deprecated - public void setTrustStoreInputStream(InputStream trustStoreInputStream) - { - checkNotStarted(); - - _trustStoreInputStream = trustStoreInputStream; - } - - /* ------------------------------------------------------------ */ - /** - * @return True if SSL needs client authentication. - * @see SSLEngine#getNeedClientAuth() - */ - public boolean getNeedClientAuth() - { - return _needClientAuth; - } - - /* ------------------------------------------------------------ */ - /** - * @param needClientAuth - * True if SSL needs client authentication. - * @see SSLEngine#getNeedClientAuth() - */ - public void setNeedClientAuth(boolean needClientAuth) - { - checkNotStarted(); - - _needClientAuth = needClientAuth; - } - - /* ------------------------------------------------------------ */ - /** - * @return True if SSL wants client authentication. - * @see SSLEngine#getWantClientAuth() - */ - public boolean getWantClientAuth() - { - return _wantClientAuth; - } - - /* ------------------------------------------------------------ */ - /** - * @param wantClientAuth - * True if SSL wants client authentication. - * @see SSLEngine#getWantClientAuth() - */ - public void setWantClientAuth(boolean wantClientAuth) - { - checkNotStarted(); - - _wantClientAuth = wantClientAuth; - } - - /* ------------------------------------------------------------ */ - /** - * @return true if SSL certificate has to be validated - * @deprecated - */ - @Deprecated - public boolean getValidateCerts() - { - return _validateCerts; - } - - /* ------------------------------------------------------------ */ - /** - * @return true if SSL certificate has to be validated - */ - public boolean isValidateCerts() - { - return _validateCerts; - } - - /* ------------------------------------------------------------ */ - /** - * @param validateCerts - * true if SSL certificates have to be validated - */ - public void setValidateCerts(boolean validateCerts) - { - checkNotStarted(); - - _validateCerts = validateCerts; - } - - /* ------------------------------------------------------------ */ - /** - * @return true if SSL certificates of the peer have to be validated - */ - public boolean isValidatePeerCerts() - { - return _validatePeerCerts; - } - - /* ------------------------------------------------------------ */ - /** - * @param validatePeerCerts - * true if SSL certificates of the peer have to be validated - */ - public void setValidatePeerCerts(boolean validatePeerCerts) - { - checkNotStarted(); - - _validatePeerCerts = validatePeerCerts; - } - - /* ------------------------------------------------------------ */ - /** - * @return True if SSL re-negotiation is allowed (default false) - */ - public boolean isAllowRenegotiate() - { - return _allowRenegotiate; - } - - /* ------------------------------------------------------------ */ - /** - * Set if SSL re-negotiation is allowed. CVE-2009-3555 discovered - * a vulnerability in SSL/TLS with re-negotiation. If your JVM - * does not have CVE-2009-3555 fixed, then re-negotiation should - * not be allowed. CVE-2009-3555 was fixed in Sun java 1.6 with a ban - * of renegotiates in u19 and with RFC5746 in u22. - * - * @param allowRenegotiate - * true if re-negotiation is allowed (default false) - */ - public void setAllowRenegotiate(boolean allowRenegotiate) - { - checkNotStarted(); - - _allowRenegotiate = allowRenegotiate; - } - - /* ------------------------------------------------------------ */ - /** - * @param password - * The password for the key store - */ - public void setKeyStorePassword(String password) - { - checkNotStarted(); - - _keyStorePassword = Password.getPassword(PASSWORD_PROPERTY,password,null); - } - - /* ------------------------------------------------------------ */ - /** - * @param password - * The password (if any) for the specific key within the key store - */ - public void setKeyManagerPassword(String password) - { - checkNotStarted(); - - _keyManagerPassword = Password.getPassword(KEYPASSWORD_PROPERTY,password,null); - } - - /* ------------------------------------------------------------ */ - /** - * @param password - * The password for the trust store - */ - public void setTrustStorePassword(String password) - { - checkNotStarted(); - - _trustStorePassword = Password.getPassword(PASSWORD_PROPERTY,password,null); - } - - /* ------------------------------------------------------------ */ - /** - * @return The SSL provider name, which if set is passed to - * {@link SSLContext#getInstance(String, String)} - */ - public String getProvider() - { - return _sslProvider; - } - - /* ------------------------------------------------------------ */ - /** - * @param provider - * The SSL provider name, which if set is passed to - * {@link SSLContext#getInstance(String, String)} - */ - public void setProvider(String provider) - { - checkNotStarted(); - - _sslProvider = provider; - } - - /* ------------------------------------------------------------ */ - /** - * @return The SSL protocol (default "TLS") passed to - * {@link SSLContext#getInstance(String, String)} - */ - public String getProtocol() - { - return _sslProtocol; - } - - /* ------------------------------------------------------------ */ - /** - * @param protocol - * The SSL protocol (default "TLS") passed to - * {@link SSLContext#getInstance(String, String)} - */ - public void setProtocol(String protocol) - { - checkNotStarted(); - - _sslProtocol = protocol; - } - - /* ------------------------------------------------------------ */ - /** - * @return The algorithm name, which if set is passed to - * {@link SecureRandom#getInstance(String)} to obtain the {@link SecureRandom} instance passed to - * {@link SSLContext#init(javax.net.ssl.KeyManager[], javax.net.ssl.TrustManager[], SecureRandom)} - */ - public String getSecureRandomAlgorithm() - { - return _secureRandomAlgorithm; - } - - /* ------------------------------------------------------------ */ - /** - * @param algorithm - * The algorithm name, which if set is passed to - * {@link SecureRandom#getInstance(String)} to obtain the {@link SecureRandom} instance passed to - * {@link SSLContext#init(javax.net.ssl.KeyManager[], javax.net.ssl.TrustManager[], SecureRandom)} - */ - public void setSecureRandomAlgorithm(String algorithm) - { - checkNotStarted(); - - _secureRandomAlgorithm = algorithm; - } - - /* ------------------------------------------------------------ */ - /** - * @return The algorithm name (default "SunX509") used by the {@link KeyManagerFactory} - */ - public String getSslKeyManagerFactoryAlgorithm() - { - return (_keyManagerFactoryAlgorithm); - } - - /* ------------------------------------------------------------ */ - /** - * @param algorithm - * The algorithm name (default "SunX509") used by the {@link KeyManagerFactory} - */ - public void setSslKeyManagerFactoryAlgorithm(String algorithm) - { - checkNotStarted(); - - _keyManagerFactoryAlgorithm = algorithm; - } - - /* ------------------------------------------------------------ */ - /** - * @return The algorithm name (default "SunX509") used by the {@link TrustManagerFactory} - */ - public String getTrustManagerFactoryAlgorithm() - { - return (_trustManagerFactoryAlgorithm); - } - - /* ------------------------------------------------------------ */ - /** - * @return True if all certificates should be trusted if there is no KeyStore or TrustStore - */ - public boolean isTrustAll() - { - return _trustAll; - } - - /* ------------------------------------------------------------ */ - /** - * @param trustAll True if all certificates should be trusted if there is no KeyStore or TrustStore - */ - public void setTrustAll(boolean trustAll) - { - _trustAll = trustAll; - } - - /* ------------------------------------------------------------ */ - /** - * @param algorithm - * The algorithm name (default "SunX509") used by the {@link TrustManagerFactory} - * Use the string "TrustAll" to install a trust manager that trusts all. - */ - public void setTrustManagerFactoryAlgorithm(String algorithm) - { - checkNotStarted(); - - _trustManagerFactoryAlgorithm = algorithm; - } - - /* ------------------------------------------------------------ */ - /** - * @return Path to file that contains Certificate Revocation List - */ - public String getCrlPath() - { - return _crlPath; - } - - /* ------------------------------------------------------------ */ - /** - * @param crlPath - * Path to file that contains Certificate Revocation List - */ - public void setCrlPath(String crlPath) - { - checkNotStarted(); - - _crlPath = crlPath; - } - - /* ------------------------------------------------------------ */ - /** - * @return Maximum number of intermediate certificates in - * the certification path (-1 for unlimited) - */ - public int getMaxCertPathLength() - { - return _maxCertPathLength; - } - - /* ------------------------------------------------------------ */ - /** - * @param maxCertPathLength - * maximum number of intermediate certificates in - * the certification path (-1 for unlimited) - */ - public void setMaxCertPathLength(int maxCertPathLength) - { - checkNotStarted(); - - _maxCertPathLength = maxCertPathLength; - } - - /* ------------------------------------------------------------ */ - /** - * @return The SSLContext - */ - public SSLContext getSslContext() - { - if (!isStarted()) - throw new IllegalStateException(getState()); - return _context; - } - - /* ------------------------------------------------------------ */ - /** - * @param sslContext - * Set a preconfigured SSLContext - */ - public void setSslContext(SSLContext sslContext) - { - checkNotStarted(); - - _context = sslContext; - } - - /* ------------------------------------------------------------ */ - /** - * Override this method to provide alternate way to load a keystore. - * - * @return the key store instance - * @throws Exception - */ - protected KeyStore loadKeyStore() throws Exception - { - return _keyStore != null ? _keyStore : getKeyStore(_keyStoreInputStream, - _keyStorePath, _keyStoreType, _keyStoreProvider, - _keyStorePassword==null? null: _keyStorePassword.toString()); - } - - /* ------------------------------------------------------------ */ - /** - * Override this method to provide alternate way to load a truststore. - * - * @return the key store instance - * @throws Exception - */ - protected KeyStore loadTrustStore() throws Exception - { - return _trustStore != null ? _trustStore : getKeyStore(_trustStoreInputStream, - _trustStorePath, _trustStoreType, _trustStoreProvider, - _trustStorePassword==null? null: _trustStorePassword.toString()); - } - - /* ------------------------------------------------------------ */ - /** - * Loads keystore using an input stream or a file path in the same - * order of precedence. - * - * Required for integrations to be able to override the mechanism - * used to load a keystore in order to provide their own implementation. - * - * @param storeStream keystore input stream - * @param storePath path of keystore file - * @param storeType keystore type - * @param storeProvider keystore provider - * @param storePassword keystore password - * @return created keystore - * @throws Exception - * - * @deprecated - */ - @Deprecated - protected KeyStore getKeyStore(InputStream storeStream, String storePath, String storeType, String storeProvider, String storePassword) throws Exception - { - return CertificateUtils.getKeyStore(storeStream, storePath, storeType, storeProvider, storePassword); - } - - /* ------------------------------------------------------------ */ - /** - * Loads certificate revocation list (CRL) from a file. - * - * Required for integrations to be able to override the mechanism used to - * load CRL in order to provide their own implementation. - * - * @param crlPath path of certificate revocation list file - * @return Collection of CRL's - * @throws Exception - */ - protected Collection loadCRL(String crlPath) throws Exception - { - return CertificateUtils.loadCRL(crlPath); - } - - /* ------------------------------------------------------------ */ - protected KeyManager[] getKeyManagers(KeyStore keyStore) throws Exception - { - KeyManager[] managers = null; - - if (keyStore != null) - { - KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(_keyManagerFactoryAlgorithm); - keyManagerFactory.init(keyStore,_keyManagerPassword == null?(_keyStorePassword == null?null:_keyStorePassword.toString().toCharArray()):_keyManagerPassword.toString().toCharArray()); - managers = keyManagerFactory.getKeyManagers(); - - if (_certAlias != null) - { - for (int idx = 0; idx < managers.length; idx++) - { - if (managers[idx] instanceof X509KeyManager) - { - managers[idx] = new AliasedX509ExtendedKeyManager(_certAlias,(X509KeyManager)managers[idx]); - } - } - } - } - - return managers; - } - - /* ------------------------------------------------------------ */ - protected TrustManager[] getTrustManagers(KeyStore trustStore, Collection crls) throws Exception - { - TrustManager[] managers = null; - if (trustStore != null) - { - // Revocation checking is only supported for PKIX algorithm - if (_validatePeerCerts && _trustManagerFactoryAlgorithm.equalsIgnoreCase("PKIX")) - { - PKIXBuilderParameters pbParams = new PKIXBuilderParameters(trustStore,new X509CertSelector()); - - // Set maximum certification path length - pbParams.setMaxPathLength(_maxCertPathLength); - - // Make sure revocation checking is enabled - pbParams.setRevocationEnabled(true); - - if (crls != null && !crls.isEmpty()) - { - pbParams.addCertStore(CertStore.getInstance("Collection",new CollectionCertStoreParameters(crls))); - } - - if (_enableCRLDP) - { - // Enable Certificate Revocation List Distribution Points (CRLDP) support - System.setProperty("com.sun.security.enableCRLDP","true"); - } - - if (_enableOCSP) - { - // Enable On-Line Certificate Status Protocol (OCSP) support - Security.setProperty("ocsp.enable","true"); - - if (_ocspResponderURL != null) - { - // Override location of OCSP Responder - Security.setProperty("ocsp.responderURL", _ocspResponderURL); - } - } - - TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(_trustManagerFactoryAlgorithm); - trustManagerFactory.init(new CertPathTrustManagerParameters(pbParams)); - - managers = trustManagerFactory.getTrustManagers(); - } - else - { - TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(_trustManagerFactoryAlgorithm); - trustManagerFactory.init(trustStore); - - managers = trustManagerFactory.getTrustManagers(); - } - } - - return managers; - } - - /* ------------------------------------------------------------ */ - /** - * Check KetyStore Configuration. Ensures that if keystore has been - * configured but there's no truststore, that keystore is - * used as truststore. - * @throws IllegalStateException if SslContextFactory configuration can't be used. - */ - public void checkKeyStore() - { - if (_keyStore == null && _keyStoreInputStream == null && _keyStorePath == null) - throw new IllegalStateException("SSL doesn't have a valid keystore"); - - // if the keystore has been configured but there is no - // truststore configured, use the keystore as the truststore - if (_trustStore == null && _trustStoreInputStream == null && _trustStorePath == null) - { - _trustStore = _keyStore; - _trustStorePath = _keyStorePath; - _trustStoreInputStream = _keyStoreInputStream; - _trustStoreType = _keyStoreType; - _trustStoreProvider = _keyStoreProvider; - _trustStorePassword = _keyStorePassword; - _trustManagerFactoryAlgorithm = _keyManagerFactoryAlgorithm; - } - - // It's the same stream we cannot read it twice, so read it once in memory - if (_keyStoreInputStream != null && _keyStoreInputStream == _trustStoreInputStream) - { - try - { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - IO.copy(_keyStoreInputStream, baos); - _keyStoreInputStream.close(); - - _keyStoreInputStream = new ByteArrayInputStream(baos.toByteArray()); - _trustStoreInputStream = new ByteArrayInputStream(baos.toByteArray()); - } - catch (Exception ex) - { - throw new IllegalStateException(ex); - } - } - } - - /* ------------------------------------------------------------ */ - /** - * Select cipher suites to be used by the connector - * based on configured inclusion and exclusion lists - * as well as enabled and supported cipher suite lists. - * @param enabledCipherSuites Array of enabled cipher suites - * @param supportedCipherSuites Array of supported cipher suites - * @return Array of cipher suites to enable - */ - public String[] selectProtocols(String[] enabledProtocols, String[] supportedProtocols) - { - Set selected_protocols = new HashSet(); - - // Set the starting protocols - either from the included or enabled list - if (_includeProtocols!=null) - { - // Use only the supported included protocols - for (String protocol : supportedProtocols) - if (_includeProtocols.contains(protocol)) - selected_protocols.add(protocol); - } - else - selected_protocols.addAll(Arrays.asList(enabledProtocols)); - - - // Remove any excluded protocols - if (_excludeProtocols != null) - selected_protocols.removeAll(_excludeProtocols); - - return selected_protocols.toArray(new String[selected_protocols.size()]); - } - - /* ------------------------------------------------------------ */ - /** - * Select cipher suites to be used by the connector - * based on configured inclusion and exclusion lists - * as well as enabled and supported cipher suite lists. - * @param enabledCipherSuites Array of enabled cipher suites - * @param supportedCipherSuites Array of supported cipher suites - * @return Array of cipher suites to enable - */ - public String[] selectCipherSuites(String[] enabledCipherSuites, String[] supportedCipherSuites) - { - Set selected_ciphers = new HashSet(); - - // Set the starting ciphers - either from the included or enabled list - if (_includeCipherSuites!=null) - { - // Use only the supported included ciphers - for (String cipherSuite : supportedCipherSuites) - if (_includeCipherSuites.contains(cipherSuite)) - selected_ciphers.add(cipherSuite); - } - else - selected_ciphers.addAll(Arrays.asList(enabledCipherSuites)); - - - // Remove any excluded ciphers - if (_excludeCipherSuites != null) - selected_ciphers.removeAll(_excludeCipherSuites); - return selected_ciphers.toArray(new String[selected_ciphers.size()]); - } - - /* ------------------------------------------------------------ */ - /** - * Check if the lifecycle has been started and throw runtime exception - */ - protected void checkNotStarted() - { - if (isStarted()) - throw new IllegalStateException("Cannot modify configuration when "+getState()); - } - - /* ------------------------------------------------------------ */ - /** - * @return true if CRL Distribution Points support is enabled - */ - public boolean isEnableCRLDP() - { - return _enableCRLDP; - } - - /* ------------------------------------------------------------ */ - /** Enables CRL Distribution Points Support - * @param enableCRLDP true - turn on, false - turns off - */ - public void setEnableCRLDP(boolean enableCRLDP) - { - checkNotStarted(); - - _enableCRLDP = enableCRLDP; - } - - /* ------------------------------------------------------------ */ - /** - * @return true if On-Line Certificate Status Protocol support is enabled - */ - public boolean isEnableOCSP() - { - return _enableOCSP; - } - - /* ------------------------------------------------------------ */ - /** Enables On-Line Certificate Status Protocol support - * @param enableOCSP true - turn on, false - turn off - */ - public void setEnableOCSP(boolean enableOCSP) - { - checkNotStarted(); - - _enableOCSP = enableOCSP; - } - - /* ------------------------------------------------------------ */ - /** - * @return Location of the OCSP Responder - */ - public String getOcspResponderURL() - { - return _ocspResponderURL; - } - - /* ------------------------------------------------------------ */ - /** Set the location of the OCSP Responder. - * @param ocspResponderURL location of the OCSP Responder - */ - public void setOcspResponderURL(String ocspResponderURL) - { - checkNotStarted(); - - _ocspResponderURL = ocspResponderURL; - } - - /* ------------------------------------------------------------ */ - /** Set the key store. - * @param keyStore the key store to set - */ - public void setKeyStore(KeyStore keyStore) - { - checkNotStarted(); - - _keyStore = keyStore; - } - - /* ------------------------------------------------------------ */ - /** Set the trust store. - * @param trustStore the trust store to set - */ - public void setTrustStore(KeyStore trustStore) - { - checkNotStarted(); - - _trustStore = trustStore; - } - - /* ------------------------------------------------------------ */ - /** Set the key store resource. - * @param resource the key store resource to set - */ - public void setKeyStoreResource(Resource resource) - { - checkNotStarted(); - - try - { - _keyStoreInputStream = resource.getInputStream(); - } - catch (IOException e) - { - throw new InvalidParameterException("Unable to get resource "+ - "input stream for resource "+resource.toString()); - } - } - - /* ------------------------------------------------------------ */ - /** Set the trust store resource. - * @param resource the trust store resource to set - */ - public void setTrustStore(Resource resource) - { - checkNotStarted(); - - try - { - _trustStoreInputStream = resource.getInputStream(); - } - catch (IOException e) - { - throw new InvalidParameterException("Unable to get resource "+ - "input stream for resource "+resource.toString()); - } - } - - /* ------------------------------------------------------------ */ - /** - * @return true if SSL Session caching is enabled - */ - public boolean isSessionCachingEnabled() - { - return _sessionCachingEnabled; - } - - /* ------------------------------------------------------------ */ - /** Set the flag to enable SSL Session caching. - * @param enableSessionCaching the value of the flag - */ - public void setSessionCachingEnabled(boolean enableSessionCaching) - { - _sessionCachingEnabled = enableSessionCaching; - } - - /* ------------------------------------------------------------ */ - /** Get SSL session cache size. - * @return SSL session cache size - */ - public int getSslSessionCacheSize() - { - return _sslSessionCacheSize; - } - - /* ------------------------------------------------------------ */ - /** SEt SSL session cache size. - * @param sslSessionCacheSize SSL session cache size to set - */ - public void setSslSessionCacheSize(int sslSessionCacheSize) - { - _sslSessionCacheSize = sslSessionCacheSize; - } - - /* ------------------------------------------------------------ */ - /** Get SSL session timeout. - * @return SSL session timeout - */ - public int getSslSessionTimeout() - { - return _sslSessionTimeout; - } - - /* ------------------------------------------------------------ */ - /** Set SSL session timeout. - * @param sslSessionTimeout SSL session timeout to set - */ - public void setSslSessionTimeout(int sslSessionTimeout) - { - _sslSessionTimeout = sslSessionTimeout; - } - - - /* ------------------------------------------------------------ */ - public SSLServerSocket newSslServerSocket(String host,int port,int backlog) throws IOException - { - SSLServerSocketFactory factory = _context.getServerSocketFactory(); - - SSLServerSocket socket = - (SSLServerSocket) (host==null ? - factory.createServerSocket(port,backlog): - factory.createServerSocket(port,backlog,InetAddress.getByName(host))); - - if (getWantClientAuth()) - socket.setWantClientAuth(getWantClientAuth()); - if (getNeedClientAuth()) - socket.setNeedClientAuth(getNeedClientAuth()); - - socket.setEnabledCipherSuites(selectCipherSuites( - socket.getEnabledCipherSuites(), - socket.getSupportedCipherSuites())); - socket.setEnabledProtocols(selectProtocols(socket.getEnabledProtocols(),socket.getSupportedProtocols())); - - return socket; - } - - /* ------------------------------------------------------------ */ - public SSLSocket newSslSocket() throws IOException - { - SSLSocketFactory factory = _context.getSocketFactory(); - - SSLSocket socket = (SSLSocket)factory.createSocket(); - - if (getWantClientAuth()) - socket.setWantClientAuth(getWantClientAuth()); - if (getNeedClientAuth()) - socket.setNeedClientAuth(getNeedClientAuth()); - - socket.setEnabledCipherSuites(selectCipherSuites( - socket.getEnabledCipherSuites(), - socket.getSupportedCipherSuites())); - socket.setEnabledProtocols(selectProtocols(socket.getEnabledProtocols(),socket.getSupportedProtocols())); - - return socket; - } - - /* ------------------------------------------------------------ */ - public SSLEngine newSslEngine(String host,int port) - { - SSLEngine sslEngine=isSessionCachingEnabled() - ?_context.createSSLEngine(host, port) - :_context.createSSLEngine(); - - customize(sslEngine); - return sslEngine; - } - - /* ------------------------------------------------------------ */ - public SSLEngine newSslEngine() - { - SSLEngine sslEngine=_context.createSSLEngine(); - customize(sslEngine); - return sslEngine; - } - - /* ------------------------------------------------------------ */ - public void customize(SSLEngine sslEngine) - { - if (getWantClientAuth()) - sslEngine.setWantClientAuth(getWantClientAuth()); - if (getNeedClientAuth()) - sslEngine.setNeedClientAuth(getNeedClientAuth()); - - sslEngine.setEnabledCipherSuites(selectCipherSuites( - sslEngine.getEnabledCipherSuites(), - sslEngine.getSupportedCipherSuites())); - - sslEngine.setEnabledProtocols(selectProtocols(sslEngine.getEnabledProtocols(),sslEngine.getSupportedProtocols())); - } - } diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java index 6120f942aec..3ae9a409fd0 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java @@ -14,10 +14,9 @@ package org.eclipse.jetty.http; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertFalse; import java.util.Enumeration; import java.util.HashSet; diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorClientTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorClientTest.java index 46841564daa..18012e950f5 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorClientTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorClientTest.java @@ -138,7 +138,7 @@ public class HttpGeneratorClientTest String t="v="+v+",r="+r+",chunks="+chunks+",c="+c+",tr="+tr[r]; // System.err.println(t); - hb.reset(true); + hb.reset(); endp.reset(); fields.clear(); @@ -193,7 +193,7 @@ public class HttpGeneratorClientTest } private static final String[] headers= { "Content-Type","Content-Length","Connection","Transfer-Encoding","Other"}; - private class TR + private static class TR { private String[] values=new String[headers.length]; private String body; diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorTest.java index 547d4647bb9..bceb1541e9d 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorTest.java @@ -86,7 +86,7 @@ public class HttpGeneratorTest String t="v="+v+",r="+r+",chunks="+chunks+",connect="+c+",tr="+tr[r]; // System.err.println(t); - hb.reset(true); + hb.reset(); endp.reset(); fields.clear(); @@ -138,7 +138,7 @@ public class HttpGeneratorTest } private static final String[] headers= { "Content-Type","Content-Length","Connection","Transfer-Encoding","Other"}; - private class TR + private static class TR { private int _code; private String _body; diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java index ac4ae8a1552..85cf15c202c 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java @@ -210,6 +210,7 @@ public class HttpParserTest StringEndPoint io=new StringEndPoint(); io.setInput( "GET /mp HTTP/1.0\015\012" + + "Connection: Keep-Alive\015\012" + "Header1: value1\015\012" + "Transfer-Encoding: chunked\015\012" + "\015\012" @@ -219,10 +220,12 @@ public class HttpParserTest + "ABCDEFGHIJKLMNOPQRSTUVWXYZ\015\012" + "0\015\012" + "POST /foo HTTP/1.0\015\012" + + "Connection: Keep-Alive\015\012" + "Header2: value2\015\012" + "Content-Length: 0\015\012" + "\015\012" + "PUT /doodle HTTP/1.0\015\012" + + "Connection: close\015\012" + "Header3: value3\015\012" + "Content-Length: 10\015\012" + "\015\012" @@ -238,27 +241,27 @@ public class HttpParserTest assertEquals("GET", f0); assertEquals("/mp", f1); assertEquals("HTTP/1.0", f2); - assertEquals(1, h); - assertEquals("Header1", hdr[0]); - assertEquals("value1", val[0]); + assertEquals(2, h); + assertEquals("Header1", hdr[1]); + assertEquals("value1", val[1]); assertEquals("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", _content); parser.parse(); assertEquals("POST", f0); assertEquals("/foo", f1); assertEquals("HTTP/1.0", f2); - assertEquals(1, h); - assertEquals("Header2", hdr[0]); - assertEquals("value2", val[0]); + assertEquals(2, h); + assertEquals("Header2", hdr[1]); + assertEquals("value2", val[1]); assertEquals(null, _content); parser.parse(); assertEquals("PUT", f0); assertEquals("/doodle", f1); assertEquals("HTTP/1.0", f2); - assertEquals(1, h); - assertEquals("Header3", hdr[0]); - assertEquals("value3", val[0]); + assertEquals(2, h); + assertEquals("Header3", hdr[1]); + assertEquals("value3", val[1]); assertEquals("0123456789", _content); } @@ -266,7 +269,8 @@ public class HttpParserTest public void testStreamParse() throws Exception { StringEndPoint io=new StringEndPoint(); - String http="GET / HTTP/1.0\015\012" + String http="GET / HTTP/1.1\015\012" + + "Host: test\015\012" + "Header1: value1\015\012" + "Transfer-Encoding: chunked\015\012" + "\015\012" @@ -275,11 +279,14 @@ public class HttpParserTest + "1a\015\012" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ\015\012" + "0\015\012" - + "POST /foo HTTP/1.0\015\012" + + "POST /foo HTTP/1.1\015\012" + + "Host: test\015\012" + "Header2: value2\015\012" + "Content-Length: 0\015\012" + "\015\012" - + "PUT /doodle HTTP/1.0\015\012" + + "PUT /doodle HTTP/1.1\015\012" + + "Host: test\015\012" + + "Connection: close\015\012" + "Header3: value3\015\012" + "Content-Length: 10\015\012" + "\015\012" @@ -296,15 +303,17 @@ public class HttpParserTest http.length() - 2, http.length() / 2, http.length() / 3, - 64, + 128, 32 }; for (int t= 0; t < tests.length; t++) { - String tst="t"+tests[t]; + String tst="t"+t+"="+tests[t]; try - { + { + f0=f1=f2=null; + h=0; ByteArrayBuffer buffer= new ByteArrayBuffer(tests[t]); ByteArrayBuffer content=new ByteArrayBuffer(8192); SimpleBuffers buffers=new SimpleBuffers(buffer,content); @@ -314,31 +323,32 @@ public class HttpParserTest io.setInput(http); + // System.err.println(tst); parser.parse(); assertEquals(tst,"GET", f0); assertEquals(tst,"/", f1); - assertEquals(tst,"HTTP/1.0", f2); - assertEquals(tst,1, h); - assertEquals(tst,"Header1", hdr[0]); - assertEquals(tst,"value1", val[0]); + assertEquals(tst,"HTTP/1.1", f2); + assertEquals(tst,2, h); + assertEquals(tst,"Header1", hdr[1]); + assertEquals(tst,"value1", val[1]); assertEquals(tst,"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", _content); parser.parse(); assertEquals(tst,"POST", f0); assertEquals(tst,"/foo", f1); - assertEquals(tst,"HTTP/1.0", f2); - assertEquals(tst,1, h); - assertEquals(tst,"Header2", hdr[0]); - assertEquals(tst,"value2", val[0]); + assertEquals(tst,"HTTP/1.1", f2); + assertEquals(tst,2, h); + assertEquals(tst,"Header2", hdr[1]); + assertEquals(tst,"value2", val[1]); assertEquals(tst,null, _content); parser.parse(); assertEquals(tst,"PUT", f0); assertEquals(tst,"/doodle", f1); - assertEquals(tst,"HTTP/1.0", f2); - assertEquals(tst,1, h); - assertEquals(tst,"Header3", hdr[0]); - assertEquals(tst,"value3", val[0]); + assertEquals(tst,"HTTP/1.1", f2); + assertEquals(tst,3, h); + assertEquals(tst,"Header3", hdr[2]); + assertEquals(tst,"value3", val[2]); assertEquals(tst,"0123456789", _content); } catch(Exception e) @@ -401,7 +411,7 @@ public class HttpParserTest StringEndPoint io=new StringEndPoint(); io.setInput( "HTTP/1.1 204 No-Content\015\012" - + "Connection: close\015\012" + + "Header: value\015\012" + "\015\012" + "HTTP/1.1 200 Correct\015\012" + "Content-Length: 10\015\012" diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/SslContextFactoryTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/SslContextFactoryTest.java deleted file mode 100644 index f7fce832c0e..00000000000 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/SslContextFactoryTest.java +++ /dev/null @@ -1,75 +0,0 @@ -package org.eclipse.jetty.http; - -import static junit.framework.Assert.assertTrue; - -import java.io.FileInputStream; -import java.security.KeyStore; - -import org.eclipse.jetty.http.ssl.SslContextFactory; -import org.junit.Test; - - -public class SslContextFactoryTest -{ - @Test - public void testNoTsFileKs() throws Exception - { - String keystorePath = System.getProperty("basedir",".") + "/src/test/resources/keystore"; - SslContextFactory cf = new SslContextFactory(keystorePath); - cf.setKeyStorePassword("storepwd"); - cf.setKeyManagerPassword("keypwd"); - - cf.start(); - - assertTrue(cf.getSslContext()!=null); - } - - @Test - public void testNoTsStreamKs() throws Exception - { - String keystorePath = System.getProperty("basedir",".") + "/src/test/resources/keystore"; - - SslContextFactory cf = new SslContextFactory(); - - cf.setKeyStoreInputStream(new FileInputStream(keystorePath)); - cf.setKeyStorePassword("storepwd"); - cf.setKeyManagerPassword("keypwd"); - - cf.start(); - - assertTrue(cf.getSslContext()!=null); - } - - @Test - public void testNoTsSetKs() throws Exception - { - String keystorePath = System.getProperty("basedir",".") + "/src/test/resources/keystore"; - - KeyStore ks = KeyStore.getInstance("JKS"); - ks.load(new FileInputStream(keystorePath),"storepwd".toCharArray()); - - SslContextFactory cf = new SslContextFactory(); - cf.setKeyStore(ks); - cf.setKeyManagerPassword("keypwd"); - - cf.start(); - - assertTrue(cf.getSslContext()!=null); - } - - @Test - public void testNoTsNoKs() throws Exception - { - SslContextFactory cf = new SslContextFactory(); - cf.start(); - assertTrue(cf.getSslContext()!=null); - } - - @Test - public void testTrustAll() throws Exception - { - SslContextFactory cf = new SslContextFactory(); - cf.start(); - assertTrue(cf.getSslContext()!=null); - } -} diff --git a/jetty-io/pom.xml b/jetty-io/pom.xml index f4c816d3f27..8a5f69852a6 100644 --- a/jetty-io/pom.xml +++ b/jetty-io/pom.xml @@ -2,7 +2,7 @@ jetty-project org.eclipse.jetty - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 org.eclipse.jetty diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractConnection.java index 04a6e26ec87..a9167eba32e 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractConnection.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractConnection.java @@ -15,13 +15,13 @@ public abstract class AbstractConnection implements Connection public AbstractConnection(EndPoint endp) { - _endp=endp; + _endp=(EndPoint)endp; _timeStamp = System.currentTimeMillis(); } - + public AbstractConnection(EndPoint endp,long timestamp) { - _endp=endp; + _endp=(EndPoint)endp; _timeStamp = timestamp; } @@ -29,17 +29,21 @@ public abstract class AbstractConnection implements Connection { return _timeStamp; } - + public EndPoint getEndPoint() { return _endp; } - public void idleExpired() + public void onIdleExpired(long idleForMs) { try { - _endp.shutdownOutput(); + LOG.debug("onIdleExpired {}ms {} {}",idleForMs,this,_endp); + if (_endp.isInputShutdown() || _endp.isOutputShutdown()) + _endp.close(); + else + _endp.shutdownOutput(); } catch(IOException e) { @@ -52,13 +56,18 @@ public abstract class AbstractConnection implements Connection catch(IOException e2) { LOG.ignore(e2); - } } } - + public String toString() { - return super.toString()+"@"+_endp.getLocalAddr()+":"+_endp.getLocalPort()+"<->"+_endp.getRemoteAddr()+":"+_endp.getRemotePort(); + return String.format("%s@%x//%s:%d<->%s:%d", + getClass().getSimpleName(), + hashCode(), + _endp.getLocalAddr(), + _endp.getLocalPort(), + _endp.getRemoteAddr(), + _endp.getRemotePort()); } } diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/AsyncEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/AsyncEndPoint.java index 5eebc36ad80..992ad7a195a 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/AsyncEndPoint.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/AsyncEndPoint.java @@ -13,36 +13,59 @@ package org.eclipse.jetty.io; -public interface AsyncEndPoint extends EndPoint +import org.eclipse.jetty.util.thread.Timeout; + +public interface AsyncEndPoint extends ConnectedEndPoint { /* ------------------------------------------------------------ */ /** * Dispatch the endpoint to a thread to attend to it. * */ - public void dispatch(); - - /** - * @return true if this endpoint can accept a dispatch. False if the - * endpoint cannot accept a dispatched (eg is suspended or already dispatched) - */ - public boolean isReadyForDispatch(); + public void asyncDispatch(); /* ------------------------------------------------------------ */ /** Schedule a write dispatch. * Set the endpoint to not be writable and schedule a dispatch when * it becomes writable. */ - public void scheduleWrite(); - - /* ------------------------------------------------------------ */ - /** Schedule a call to the idle timeout - */ - public void scheduleIdle(); - - /* ------------------------------------------------------------ */ - /** Cancel a call to the idle timeout - */ - public void cancelIdle(); + public void scheduleWrite(); + /* ------------------------------------------------------------ */ + /** Callback when idle. + *

An endpoint is idle if there has been no IO activity for + * {@link #getMaxIdleTime()} and {@link #isCheckForIdle()} is true. + * @param idleForMs TODO + */ + public void onIdleExpired(long idleForMs); + + /* ------------------------------------------------------------ */ + /** Set if the endpoint should be checked for idleness + */ + public void setCheckForIdle(boolean check); + + /* ------------------------------------------------------------ */ + /** Get if the endpoint should be checked for idleness + */ + public boolean isCheckForIdle(); + + + /* ------------------------------------------------------------ */ + public boolean isWritable(); + + /* ------------------------------------------------------------ */ + /** + * @return True if IO has been successfully performed since the last call to {@link #hasProgressed()} + */ + public boolean hasProgressed(); + + /* ------------------------------------------------------------ */ + /** + */ + public void scheduleTimeout(Timeout.Task task, long timeoutMs); + + /* ------------------------------------------------------------ */ + /** + */ + public void cancelTimeout(Timeout.Task task); } diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/BufferCache.java b/jetty-io/src/main/java/org/eclipse/jetty/io/BufferCache.java index 904961087dc..0abc1270182 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/BufferCache.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/BufferCache.java @@ -103,6 +103,12 @@ public class BufferCache return lookup(buffer).toString(); } + public int getOrdinal(String value) + { + CachedBuffer buffer = (CachedBuffer)_stringMap.get(value); + return buffer==null?-1:buffer.getOrdinal(); + } + public int getOrdinal(Buffer buffer) { if (buffer instanceof CachedBuffer) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/BufferDateCache.java b/jetty-io/src/main/java/org/eclipse/jetty/io/BufferDateCache.java index c1aebb88829..275d2f3b77d 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/BufferDateCache.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/BufferDateCache.java @@ -46,7 +46,6 @@ public class BufferDateCache extends DateCache public synchronized Buffer formatBuffer(long date) { String d = super.format(date); - //noinspection StringEquality if (d==_last) return _buffer; _last=d; diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java index fcae2f99fda..a0e4f8e96df 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java @@ -4,11 +4,11 @@ // 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 +// 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. +// You may elect to redistribute this code under either of these licenses. // ======================================================================== package org.eclipse.jetty.io; @@ -19,7 +19,7 @@ import java.io.IOException; /* ------------------------------------------------------------ */ /** ByteArrayEndPoint. - * + * * */ public class ByteArrayEndPoint implements ConnectedEndPoint @@ -36,12 +36,12 @@ public class ByteArrayEndPoint implements ConnectedEndPoint /* ------------------------------------------------------------ */ /** - * + * */ public ByteArrayEndPoint() { } - + /* ------------------------------------------------------------ */ /** * @see org.eclipse.jetty.io.ConnectedEndPoint#getConnection() @@ -80,7 +80,7 @@ public class ByteArrayEndPoint implements ConnectedEndPoint /* ------------------------------------------------------------ */ /** - * + * */ public ByteArrayEndPoint(byte[] input, int outputSize) { @@ -121,9 +121,9 @@ public class ByteArrayEndPoint implements ConnectedEndPoint { _out = out; } - + /* ------------------------------------------------------------ */ - /* + /* * @see org.eclipse.io.EndPoint#isOpen() */ public boolean isOpen() @@ -150,7 +150,7 @@ public class ByteArrayEndPoint implements ConnectedEndPoint } /* ------------------------------------------------------------ */ - /* + /* * @see org.eclipse.io.EndPoint#isBlocking() */ public boolean isBlocking() @@ -171,7 +171,7 @@ public class ByteArrayEndPoint implements ConnectedEndPoint } /* ------------------------------------------------------------ */ - /* + /* * @see org.eclipse.io.EndPoint#shutdownOutput() */ public void shutdownOutput() throws IOException @@ -180,16 +180,16 @@ public class ByteArrayEndPoint implements ConnectedEndPoint } /* ------------------------------------------------------------ */ - /* + /* * @see org.eclipse.io.EndPoint#shutdownInput() */ public void shutdownInput() throws IOException { close(); } - + /* ------------------------------------------------------------ */ - /* + /* * @see org.eclipse.io.EndPoint#close() */ public void close() throws IOException @@ -198,30 +198,30 @@ public class ByteArrayEndPoint implements ConnectedEndPoint } /* ------------------------------------------------------------ */ - /* + /* * @see org.eclipse.io.EndPoint#fill(org.eclipse.io.Buffer) */ public int fill(Buffer buffer) throws IOException { if (_closed) throw new IOException("CLOSED"); - + if (_in!=null && _in.length()>0) { int len = buffer.put(_in); _in.skip(len); return len; } - + if (_in!=null && _in.length()==0 && _nonBlocking) return 0; - + close(); return -1; } /* ------------------------------------------------------------ */ - /* + /* * @see org.eclipse.io.EndPoint#flush(org.eclipse.io.Buffer) */ public int flush(Buffer buffer) throws IOException @@ -252,24 +252,24 @@ public class ByteArrayEndPoint implements ConnectedEndPoint } /* ------------------------------------------------------------ */ - /* + /* * @see org.eclipse.io.EndPoint#flush(org.eclipse.io.Buffer, org.eclipse.io.Buffer, org.eclipse.io.Buffer) */ public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException { if (_closed) throw new IOException("CLOSED"); - + int flushed=0; - + if (header!=null && header.length()>0) flushed=flush(header); - + if (header==null || header.length()==0) { if (buffer!=null && buffer.length()>0) flushed+=flush(buffer); - + if (buffer==null || buffer.length()==0) { if (trailer!=null && trailer.length()>0) @@ -278,13 +278,13 @@ public class ByteArrayEndPoint implements ConnectedEndPoint } } } - + return flushed; } /* ------------------------------------------------------------ */ /** - * + * */ public void reset() { @@ -296,7 +296,7 @@ public class ByteArrayEndPoint implements ConnectedEndPoint } /* ------------------------------------------------------------ */ - /* + /* * @see org.eclipse.io.EndPoint#getLocalAddr() */ public String getLocalAddr() @@ -305,7 +305,7 @@ public class ByteArrayEndPoint implements ConnectedEndPoint } /* ------------------------------------------------------------ */ - /* + /* * @see org.eclipse.io.EndPoint#getLocalHost() */ public String getLocalHost() @@ -314,7 +314,7 @@ public class ByteArrayEndPoint implements ConnectedEndPoint } /* ------------------------------------------------------------ */ - /* + /* * @see org.eclipse.io.EndPoint#getLocalPort() */ public int getLocalPort() @@ -323,7 +323,7 @@ public class ByteArrayEndPoint implements ConnectedEndPoint } /* ------------------------------------------------------------ */ - /* + /* * @see org.eclipse.io.EndPoint#getRemoteAddr() */ public String getRemoteAddr() @@ -332,7 +332,7 @@ public class ByteArrayEndPoint implements ConnectedEndPoint } /* ------------------------------------------------------------ */ - /* + /* * @see org.eclipse.io.EndPoint#getRemoteHost() */ public String getRemoteHost() @@ -341,7 +341,7 @@ public class ByteArrayEndPoint implements ConnectedEndPoint } /* ------------------------------------------------------------ */ - /* + /* * @see org.eclipse.io.EndPoint#getRemotePort() */ public int getRemotePort() @@ -350,7 +350,7 @@ public class ByteArrayEndPoint implements ConnectedEndPoint } /* ------------------------------------------------------------ */ - /* + /* * @see org.eclipse.io.EndPoint#getConnection() */ public Object getTransport() @@ -360,27 +360,9 @@ public class ByteArrayEndPoint implements ConnectedEndPoint /* ------------------------------------------------------------ */ public void flush() throws IOException - { - } - - /* ------------------------------------------------------------ */ - public boolean isBufferingInput() { - return false; } - - /* ------------------------------------------------------------ */ - public boolean isBufferingOutput() - { - return false; - } - - /* ------------------------------------------------------------ */ - public boolean isBufferred() - { - return false; - } - + /* ------------------------------------------------------------ */ /** * @return the growOutput diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/Connection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/Connection.java index 431964afe8f..19cf3bee60e 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/Connection.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/Connection.java @@ -50,10 +50,11 @@ public interface Connection /** * Called when the connection is closed */ - void closed(); + void onClose(); /** * Called when the connection idle timeout expires + * @param idleForMs TODO */ - void idleExpired(); + void onIdleExpired(long idleForMs); } diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/EndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/EndPoint.java index 6940991f387..231b752b9ee 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/EndPoint.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/EndPoint.java @@ -4,11 +4,11 @@ // 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 +// 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. +// You may elect to redistribute this code under either of these licenses. // ======================================================================== package org.eclipse.jetty.io; @@ -17,7 +17,7 @@ import java.io.IOException; /** - * + * * A transport EndPoint */ public interface EndPoint @@ -28,36 +28,36 @@ public interface EndPoint void shutdownOutput() throws IOException; boolean isOutputShutdown(); - + /** * Shutdown any backing input stream associated with the endpoint */ void shutdownInput() throws IOException; - + boolean isInputShutdown(); - + /** * Close any backing stream associated with the endpoint */ void close() throws IOException; /** - * Fill the buffer from the current putIndex to it's capacity from whatever + * Fill the buffer from the current putIndex to it's capacity from whatever * byte source is backing the buffer. The putIndex is increased if bytes filled. * The buffer may chose to do a compact before filling. - * @return an int value indicating the number of bytes + * @return an int value indicating the number of bytes * filled or -1 if EOF is reached. * @throws EofException If input is shutdown or the endpoint is closed. */ int fill(Buffer buffer) throws IOException; - + /** * Flush the buffer from the current getIndex to it's putIndex using whatever byte * sink is backing the buffer. The getIndex is updated with the number of bytes flushed. * Any mark set is cleared. * If the entire contents of the buffer are flushed, then an implicit empty() is done. - * + * * @param buffer The buffer to flush. This buffers getIndex is updated. * @return the number of bytes written * @throws EofException If the endpoint is closed or output is shutdown. @@ -69,7 +69,7 @@ public interface EndPoint * sink is backing the buffer. The getIndex is updated with the number of bytes flushed. * Any mark set is cleared. * If the entire contents of the buffer are flushed, then an implicit empty() is done. - * The passed header/trailer buffers are written before/after the contents of this buffer. This may be done + * The passed header/trailer buffers are written before/after the contents of this buffer. This may be done * either as gather writes, as a poke into this buffer or as several writes. The implementation is free to * select the optimal mechanism. * @param header A buffer to write before flushing this buffer. This buffers getIndex is updated. @@ -79,14 +79,14 @@ public interface EndPoint */ int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException; - + /* ------------------------------------------------------------ */ /** * @return The local IP address to which this EndPoint is bound, or null * if this EndPoint does not represent a network connection. */ public String getLocalAddr(); - + /* ------------------------------------------------------------ */ /** * @return The local host name to which this EndPoint is bound, or null @@ -122,13 +122,9 @@ public interface EndPoint */ public int getRemotePort(); - /* ------------------------------------------------------------ */ public boolean isBlocking(); - - /* ------------------------------------------------------------ */ - public boolean isBufferred(); - + /* ------------------------------------------------------------ */ public boolean blockReadable(long millisecs) throws IOException; @@ -143,27 +139,14 @@ public interface EndPoint * @return The underlying transport object (socket, channel, etc.) */ public Object getTransport(); - - /* ------------------------------------------------------------ */ - /** - * @return True if the endpoint has some buffered input data - */ - public boolean isBufferingInput(); - - /* ------------------------------------------------------------ */ - /** - * @return True if the endpoint has some buffered output data - */ - public boolean isBufferingOutput(); - + /* ------------------------------------------------------------ */ /** Flush any buffered output. * May fail to write all data if endpoint is non-blocking * @throws EofException If the endpoint is closed or output is shutdown. */ public void flush() throws IOException; - - + /* ------------------------------------------------------------ */ /** Get the max idle time in ms. *

The max idle time is the time the endpoint can be idle before @@ -174,14 +157,14 @@ public interface EndPoint * @return the max idle time in ms or if ms <= 0 implies an infinite timeout */ public int getMaxIdleTime(); - + /* ------------------------------------------------------------ */ /** Set the max idle time. * @param timeMs the max idle time in MS. Timeout <= 0 implies an infinite timeout * @throws IOException if the timeout cannot be set. */ public void setMaxIdleTime(int timeMs) throws IOException; - - - + + + } diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/EofException.java b/jetty-io/src/main/java/org/eclipse/jetty/io/EofException.java index 9c864aa078a..03369e5a79d 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/EofException.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/EofException.java @@ -15,6 +15,13 @@ package org.eclipse.jetty.io; import java.io.EOFException; + +/* ------------------------------------------------------------ */ +/** A Jetty specialization of EOFException. + *

This is thrown by Jetty to distinguish between EOF received from + * the connection, vs and EOF thrown by some application talking to some other file/socket etc. + * The only difference in handling is that Jetty EOFs are logged less verbosely. + */ public class EofException extends EOFException { public EofException() diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/UncheckedPrintWriter.java b/jetty-io/src/main/java/org/eclipse/jetty/io/UncheckedPrintWriter.java index f114417e800..cfb05447a29 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/UncheckedPrintWriter.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/UncheckedPrintWriter.java @@ -35,7 +35,8 @@ public class UncheckedPrintWriter extends PrintWriter private static final Logger LOG = Log.getLogger(UncheckedPrintWriter.class); private boolean _autoFlush = false; - private boolean _throwUnchecked=true; + private IOException _ioException; + private boolean _isClosed = false; /* ------------------------------------------------------------ */ /** @@ -102,38 +103,45 @@ public class UncheckedPrintWriter extends PrintWriter this(new BufferedWriter(new OutputStreamWriter(out)),autoFlush); } + /* ------------------------------------------------------------ */ - private void setError(Throwable th) + public boolean checkError() { - setError(); - if (_throwUnchecked) - throw new RuntimeIOException(th); - LOG.debug(th); - } - - /* ------------------------------------------------------------ */ - /** Are unchecked exceptions thrown. - * @return True if {@link RuntimeIOException}s are thrown - */ - public boolean isUncheckedPrintWriter() - { - return _throwUnchecked; - } - - /* ------------------------------------------------------------ */ - /** Set if unchecked exceptions are thrown - * @param uncheckedPrintWriter True if {@link RuntimeIOException}s are to be thrown - */ - public void setUncheckedPrintWriter(boolean uncheckedPrintWriter) - { - _throwUnchecked = uncheckedPrintWriter; + return _ioException!=null || super.checkError(); } + /* ------------------------------------------------------------ */ + private void setError(Throwable th) + { + + super.setError(); + + if (th instanceof IOException) + _ioException=(IOException)th; + else + { + _ioException=new IOException(String.valueOf(th)); + _ioException.initCause(th); + } + + LOG.debug(th); + } + + + @Override + protected void setError() + { + setError(new IOException()); + } + /* ------------------------------------------------------------ */ /** Check to make sure that the stream has not been closed */ private void isOpen() throws IOException - { - if (super.out == null) + { + if (_ioException!=null) + throw new RuntimeIOException(_ioException); + + if (_isClosed) throw new IOException("Stream closed"); } @@ -170,6 +178,7 @@ public class UncheckedPrintWriter extends PrintWriter synchronized (lock) { out.close(); + _isClosed = true; } } catch (IOException ex) @@ -248,7 +257,7 @@ public class UncheckedPrintWriter extends PrintWriter */ @Override public void write(char buf[]) - { + { this.write(buf,0,buf.length); } diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/bio/SocketEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/bio/SocketEndPoint.java index f749f0ba417..67fb8ab214f 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/bio/SocketEndPoint.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/bio/SocketEndPoint.java @@ -17,17 +17,13 @@ import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; + import javax.net.ssl.SSLSocket; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; -/** - * - * To change the template for this generated type comment go to - * Window - Preferences - Java - Code Generation - Code and Comments - */ public class SocketEndPoint extends StreamEndPoint { private static final Logger LOG = Log.getLogger(SocketEndPoint.class); @@ -79,14 +75,34 @@ public class SocketEndPoint extends StreamEndPoint @Override public boolean isInputShutdown() { - return !isOpen() || super.isInputShutdown(); + if (_socket instanceof SSLSocket) + return super.isInputShutdown(); + return _socket.isClosed() || _socket.isInputShutdown(); } /* ------------------------------------------------------------ */ @Override public boolean isOutputShutdown() { - return !isOpen() || super.isOutputShutdown(); + if (_socket instanceof SSLSocket) + return super.isOutputShutdown(); + + return _socket.isClosed() || _socket.isOutputShutdown(); + } + + + /* ------------------------------------------------------------ */ + /* + */ + protected final void shutdownSocketOutput() throws IOException + { + if (!_socket.isClosed()) + { + if (!_socket.isOutputShutdown()) + _socket.shutdownOutput(); + if (_socket.isInputShutdown()) + _socket.close(); + } } /* ------------------------------------------------------------ */ @@ -96,15 +112,27 @@ public class SocketEndPoint extends StreamEndPoint @Override public void shutdownOutput() throws IOException { - if (!isOutputShutdown()) - { + if (_socket instanceof SSLSocket) super.shutdownOutput(); - if (!(_socket instanceof SSLSocket)) - _socket.shutdownOutput(); - } + else + shutdownSocketOutput(); } + /* ------------------------------------------------------------ */ + /* + */ + public void shutdownSocketInput() throws IOException + { + if (!_socket.isClosed()) + { + if (!_socket.isInputShutdown()) + _socket.shutdownInput(); + if (_socket.isOutputShutdown()) + _socket.close(); + } + } + /* ------------------------------------------------------------ */ /* * @see org.eclipse.jetty.io.bio.StreamEndPoint#shutdownOutput() @@ -112,12 +140,10 @@ public class SocketEndPoint extends StreamEndPoint @Override public void shutdownInput() throws IOException { - if (!isInputShutdown()) - { + if (_socket instanceof SSLSocket) super.shutdownInput(); - if (!(_socket instanceof SSLSocket)) - _socket.shutdownInput(); - } + else + shutdownSocketInput(); } /* ------------------------------------------------------------ */ @@ -247,4 +273,9 @@ public class SocketEndPoint extends StreamEndPoint } } + @Override + public String toString() + { + return _local + " <--> " + _remote; + } } diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/bio/StreamEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/bio/StreamEndPoint.java index 48dce3723df..0ebab0058d3 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/bio/StreamEndPoint.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/bio/StreamEndPoint.java @@ -72,10 +72,8 @@ public class StreamEndPoint implements EndPoint public void shutdownOutput() throws IOException { - if (_oshut) - return; _oshut = true; - if (_out!=null) + if (_ishut && _out!=null) _out.close(); } @@ -86,10 +84,8 @@ public class StreamEndPoint implements EndPoint public void shutdownInput() throws IOException { - if (_ishut) - return; _ishut = true; - if (_in!=null) + if (_oshut&&_in!=null) _in.close(); } @@ -122,6 +118,8 @@ public class StreamEndPoint implements EndPoint */ public int fill(Buffer buffer) throws IOException { + if (_ishut) + return -1; if (_in==null) return 0; @@ -135,15 +133,10 @@ public class StreamEndPoint implements EndPoint try { - int read=buffer.readFrom(_in, space); - if (read<0 && isOpen()) - { - if (!isInputShutdown()) - shutdownInput(); - else if (isOutputShutdown()) - close(); - } - return read; + int filled=buffer.readFrom(_in, space); + if (filled<0) + shutdownInput(); + return filled; } catch(SocketTimeoutException e) { @@ -157,8 +150,10 @@ public class StreamEndPoint implements EndPoint */ public int flush(Buffer buffer) throws IOException { - if (_out==null) + if (_oshut) return -1; + if (_out==null) + return 0; int length=buffer.length(); if (length>0) buffer.writeTo(_out); @@ -310,24 +305,6 @@ public class StreamEndPoint implements EndPoint _out.flush(); } - /* ------------------------------------------------------------ */ - public boolean isBufferingInput() - { - return false; - } - - /* ------------------------------------------------------------ */ - public boolean isBufferingOutput() - { - return false; - } - - /* ------------------------------------------------------------ */ - public boolean isBufferred() - { - return false; - } - /* ------------------------------------------------------------ */ public int getMaxIdleTime() { diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/bio/StringEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/bio/StringEndPoint.java index 1e8aad30a26..1698249d438 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/bio/StringEndPoint.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/bio/StringEndPoint.java @@ -53,6 +53,8 @@ public class StringEndPoint extends StreamEndPoint _in=_bin; _bout = new ByteArrayOutputStream(); _out=_bout; + _ishut=false; + _oshut=false; } catch(Exception e) { diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/AsyncConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/AsyncConnection.java new file mode 100644 index 00000000000..717d01294ab --- /dev/null +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/AsyncConnection.java @@ -0,0 +1,23 @@ +// ======================================================================== +// Copyright (c) 2010 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.io.nio; + +import java.io.IOException; + +import org.eclipse.jetty.io.Connection; + +public interface AsyncConnection extends Connection +{ + void onInputShutdown() throws IOException; +} diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/ChannelEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/ChannelEndPoint.java index 9aefa436cb9..fb3c09145a8 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/ChannelEndPoint.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/ChannelEndPoint.java @@ -43,7 +43,9 @@ public class ChannelEndPoint implements EndPoint protected final Socket _socket; protected final InetSocketAddress _local; protected final InetSocketAddress _remote; - protected int _maxIdleTime; + protected volatile int _maxIdleTime; + private volatile boolean _ishut; + private volatile boolean _oshut; public ChannelEndPoint(ByteChannel channel) throws IOException { @@ -102,20 +104,76 @@ public class ChannelEndPoint implements EndPoint return _channel.isOpen(); } + /** Shutdown the channel Input. + * Cannot be overridden. To override, see {@link #shutdownInput()} + * @throws IOException + */ + protected final void shutdownChannelInput() throws IOException + { + LOG.debug("ishut {}", this); + _ishut = true; + if (_channel.isOpen()) + { + if (_socket != null) + { + try + { + if (!_socket.isInputShutdown()) + { + _socket.shutdownInput(); + } + } + catch (SocketException e) + { + LOG.debug(e.toString()); + LOG.ignore(e); + } + finally + { + if (_oshut) + { + close(); + } + } + } + } + } + /* (non-Javadoc) * @see org.eclipse.io.EndPoint#close() */ public void shutdownInput() throws IOException { - if (_channel.isOpen() && _channel instanceof SocketChannel) + shutdownChannelInput(); + } + + protected final void shutdownChannelOutput() throws IOException + { + LOG.debug("oshut {}",this); + _oshut = true; + if (_channel.isOpen()) { - Socket socket= ((SocketChannel)_channel).socket(); - if (!socket.isClosed()) + if (_socket != null) { - if(socket.isOutputShutdown()) - socket.close(); - else if (!socket.isInputShutdown()) - socket.shutdownInput(); + try + { + if (!_socket.isOutputShutdown()) + { + _socket.shutdownOutput(); + } + } + catch (SocketException e) + { + LOG.debug(e.toString()); + LOG.ignore(e); + } + finally + { + if (_ishut) + { + close(); + } + } } } } @@ -125,34 +183,17 @@ public class ChannelEndPoint implements EndPoint */ public void shutdownOutput() throws IOException { - if (_channel.isOpen() && _channel instanceof SocketChannel) - { - Socket socket= ((SocketChannel)_channel).socket(); - if (!socket.isClosed()) - { - try - { - if (socket.isInputShutdown()) - socket.close(); - else if (!socket.isOutputShutdown()) - socket.shutdownOutput(); - } - catch(SocketException e) - { - LOG.ignore(e); - } - } - } + shutdownChannelOutput(); } public boolean isOutputShutdown() { - return _channel.isOpen() && _socket!=null && _socket.isOutputShutdown(); + return _oshut || !_channel.isOpen() || _socket != null && _socket.isOutputShutdown(); } public boolean isInputShutdown() { - return _channel.isOpen() && _socket!=null && _socket.isInputShutdown(); + return _ishut || !_channel.isOpen() || _socket != null && _socket.isInputShutdown(); } /* (non-Javadoc) @@ -160,6 +201,7 @@ public class ChannelEndPoint implements EndPoint */ public void close() throws IOException { + LOG.debug("close {}",this); _channel.close(); } @@ -168,13 +210,15 @@ public class ChannelEndPoint implements EndPoint */ public int fill(Buffer buffer) throws IOException { + if (_ishut) + return -1; Buffer buf = buffer.buffer(); int len=0; if (buf instanceof NIOBuffer) { final NIOBuffer nbuf = (NIOBuffer)buf; final ByteBuffer bbuf=nbuf.getByteBuffer(); - + //noinspection SynchronizationOnLocalVariableOrMethodParameter try { @@ -196,22 +240,24 @@ public class ChannelEndPoint implements EndPoint { if (!isInputShutdown()) shutdownInput(); - else if (isOutputShutdown()) + if (isOutputShutdown()) _channel.close(); } } catch (IOException x) { - LOG.debug(x); + LOG.debug(x.toString()); + LOG.ignore(x); try { - close(); + if (_channel.isOpen()) + _channel.close(); } - catch (IOException xx) + catch (Exception xx) { LOG.ignore(xx); } - + if (len>0) throw x; len=-1; @@ -221,7 +267,7 @@ public class ChannelEndPoint implements EndPoint { throw new IOException("Not Implemented"); } - + return len; } @@ -293,20 +339,6 @@ public class ChannelEndPoint implements EndPoint } else { - if (header!=null) - { - if (buffer!=null && buffer.length()>0 && header.space()>buffer.length()) - { - header.put(buffer); - buffer.clear(); - } - if (trailer!=null && trailer.length()>0 && header.space()>trailer.length()) - { - header.put(trailer); - trailer.clear(); - } - } - // flush header if (header!=null && header.length()>0) length=flush(header); @@ -478,24 +510,6 @@ public class ChannelEndPoint implements EndPoint { } - /* ------------------------------------------------------------ */ - public boolean isBufferingInput() - { - return false; - } - - /* ------------------------------------------------------------ */ - public boolean isBufferingOutput() - { - return false; - } - - /* ------------------------------------------------------------ */ - public boolean isBufferred() - { - return false; - } - /* ------------------------------------------------------------ */ public int getMaxIdleTime() { diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectChannelEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectChannelEndPoint.java index a318543f647..39cd2205dc3 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectChannelEndPoint.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectChannelEndPoint.java @@ -27,6 +27,7 @@ import org.eclipse.jetty.io.EofException; import org.eclipse.jetty.io.nio.SelectorManager.SelectSet; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.thread.Timeout.Task; /* ------------------------------------------------------------ */ /** @@ -35,7 +36,7 @@ import org.eclipse.jetty.util.log.Logger; public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPoint, ConnectedEndPoint { public static final Logger LOG=Log.getLogger("org.eclipse.jetty.io.nio"); - + private final SelectorManager.SelectSet _selectSet; private final SelectorManager _manager; private SelectionKey _key; @@ -46,36 +47,38 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo /** The desired value for {@link SelectionKey#interestOps()} */ private int _interestOps; - + /** * The connection instance is the handler for any IO activity on the endpoint. - * There is a different type of connection for HTTP, AJP, WebSocket and - * ProxyConnect. The connection may change for an SCEP as it is upgraded + * There is a different type of connection for HTTP, AJP, WebSocket and + * ProxyConnect. The connection may change for an SCEP as it is upgraded * from HTTP to proxy connect or websocket. */ - private volatile Connection _connection; - + private volatile AsyncConnection _connection; + /** true if a thread has been dispatched to handle this endpoint */ private boolean _dispatched = false; - + /** true if a non IO dispatch (eg async resume) is outstanding */ - private boolean _redispatched = false; - + private boolean _asyncDispatch = false; + /** true if the last write operation succeed and wrote all offered bytes */ private volatile boolean _writable = true; - + /** True if a thread has is blocked in {@link #blockReadable(long)} */ private boolean _readBlocked; /** True if a thread has is blocked in {@link #blockWritable(long)} */ private boolean _writeBlocked; - + /** true if {@link SelectSet#destroyEndPoint(SelectChannelEndPoint)} has not been called */ private boolean _open; - + private volatile long _idleTimestamp; - + + private boolean _ishut; + /* ------------------------------------------------------------ */ public SelectChannelEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey key, int maxIdleTime) throws IOException @@ -85,33 +88,13 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo _manager = selectSet.getManager(); _selectSet = selectSet; _dispatched = false; - _redispatched = false; + _asyncDispatch = false; _open=true; _key = key; - _connection = _manager.newConnection(channel,this); - - scheduleIdle(); + setCheckForIdle(true); } - /* ------------------------------------------------------------ */ - public SelectChannelEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey key) - throws IOException - { - super(channel); - - _manager = selectSet.getManager(); - _selectSet = selectSet; - _dispatched = false; - _redispatched = false; - _open=true; - _key = key; - - _connection = _manager.newConnection(channel,this); - - scheduleIdle(); - } - /* ------------------------------------------------------------ */ public SelectionKey getSelectionKey() { @@ -137,8 +120,9 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo public void setConnection(Connection connection) { Connection old=_connection; - _connection=connection; - _manager.endPointUpgraded(this,old); + _connection=(AsyncConnection)connection; + if (old!=null && old!=_connection) + _manager.endPointUpgraded(this,old); } /* ------------------------------------------------------------ */ @@ -146,7 +130,7 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo { return _idleTimestamp; } - + /* ------------------------------------------------------------ */ /** Called by selectSet to schedule handling * @@ -176,17 +160,10 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo // wake them up is as good as a dispatched. this.notifyAll(); - // we are not interested in further selecting - if (_dispatched) - _key.interestOps(0); - return; - } - - // Otherwise if we are still dispatched - if (!isReadyForDispatch()) - { // we are not interested in further selecting _key.interestOps(0); + if (!_dispatched) + updateKey(); return; } @@ -211,6 +188,18 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo } } + /* ------------------------------------------------------------ */ + public void asyncDispatch() + { + synchronized(this) + { + if (_dispatched) + _asyncDispatch=true; + else + dispatch(); + } + } + /* ------------------------------------------------------------ */ public void dispatch() { @@ -218,7 +207,7 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo { if (_dispatched) { - _redispatched=true; + throw new IllegalStateException("dispatched"); } else { @@ -245,9 +234,9 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo { synchronized (this) { - if (_redispatched) + if (_asyncDispatch) { - _redispatched=false; + _asyncDispatch=false; return false; } _dispatched = false; @@ -257,48 +246,74 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo } /* ------------------------------------------------------------ */ - public void scheduleIdle() + public void cancelTimeout(Task task) { - _idleTimestamp=System.currentTimeMillis(); + getSelectSet().cancelTimeout(task); } /* ------------------------------------------------------------ */ - public void cancelIdle() + public void scheduleTimeout(Task task, long timeoutMs) { - _idleTimestamp=0; + getSelectSet().scheduleTimeout(task,timeoutMs); + } + + /* ------------------------------------------------------------ */ + public void setCheckForIdle(boolean check) + { + _idleTimestamp=check?System.currentTimeMillis():0; + } + + /* ------------------------------------------------------------ */ + public boolean isCheckForIdle() + { + return _idleTimestamp!=0; + } + + /* ------------------------------------------------------------ */ + protected void notIdle() + { + if (_idleTimestamp!=0) + _idleTimestamp=System.currentTimeMillis(); } /* ------------------------------------------------------------ */ public void checkIdleTimestamp(long now) { long idleTimestamp=_idleTimestamp; - if (!getChannel().isOpen() || idleTimestamp!=0 && _maxIdleTime>0 && now>(idleTimestamp+_maxIdleTime)) - idleExpired(); + + if (idleTimestamp!=0 && _maxIdleTime>0) + { + long idleForMs=now-idleTimestamp; + if (idleForMs>_maxIdleTime) + { + onIdleExpired(idleForMs); + _idleTimestamp=now; + } + } } /* ------------------------------------------------------------ */ - protected void idleExpired() + public void onIdleExpired(long idleForMs) { - _connection.idleExpired(); + _connection.onIdleExpired(idleForMs); } /* ------------------------------------------------------------ */ - /** - * @return True if the endpoint has produced/consumed bytes itself (non application data). - */ - public boolean isProgressing() + @Override + public int fill(Buffer buffer) throws IOException { - return false; + int fill=super.fill(buffer); + if (fill>0) + notIdle(); + return fill; } - + /* ------------------------------------------------------------ */ - /* - */ @Override public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException { int l = super.flush(header, buffer, trailer); - + // If there was something to write and it wasn't written, then we are not writable. if (l==0 && ( header!=null && header.hasContent() || buffer!=null && buffer.hasContent() || trailer!=null && trailer.hasContent())) { @@ -309,8 +324,11 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo updateKey(); } } - else + else if (l>0) + { _writable=true; + notIdle(); + } return l; } @@ -321,7 +339,7 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo public int flush(Buffer buffer) throws IOException { int l = super.flush(buffer); - + // If there was something to write and it wasn't written, then we are not writable. if (l==0 && buffer!=null && buffer.hasContent()) { @@ -332,20 +350,13 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo updateKey(); } } - else - _writable=true; - - return l; - } - - /* ------------------------------------------------------------ */ - public boolean isReadyForDispatch() - { - synchronized (this) + else if (l>0) { - // Ready if not dispatched and not suspended - return !(_dispatched || getConnection().isSuspended()); + _writable=true; + notIdle(); } + + return l; } /* ------------------------------------------------------------ */ @@ -357,12 +368,17 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo { synchronized (this) { + if (isInputShutdown()) + throw new EofException(); + long now=_selectSet.getNow(); long end=now+timeoutMs; + boolean check=isCheckForIdle(); + setCheckForIdle(true); try { _readBlocked=true; - while (isOpen() && _readBlocked) + while (!isInputShutdown() && _readBlocked) { try { @@ -385,6 +401,7 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo finally { _readBlocked=false; + setCheckForIdle(check); } } return true; @@ -399,15 +416,17 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo { synchronized (this) { - if (!isOpen() || isOutputShutdown()) + if (isOutputShutdown()) throw new EofException(); - + long now=_selectSet.getNow(); long end=now+timeoutMs; + boolean check=isCheckForIdle(); + setCheckForIdle(true); try { _writeBlocked=true; - while (isOpen() && _writeBlocked && !isOutputShutdown()) + while (_writeBlocked && !isOutputShutdown()) { try { @@ -426,21 +445,10 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo return false; } } - catch(Throwable e) - { - // TODO remove this if it finds nothing - LOG.warn(e); - if (e instanceof RuntimeException) - throw (RuntimeException)e; - if (e instanceof Error) - throw (Error)e; - throw new RuntimeException(e); - } finally { _writeBlocked=false; - if (_idleTimestamp!=-1) - scheduleIdle(); + setCheckForIdle(check); } } return true; @@ -452,17 +460,32 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo { _writable=false; } - + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.io.AsyncEndPoint#scheduleWrite() + */ public void scheduleWrite() { if (_writable==true) LOG.debug("Required scheduleWrite {}",this); - + _writable=false; updateKey(); } + /* ------------------------------------------------------------ */ + public boolean isWritable() + { + return _writable; + } + + /* ------------------------------------------------------------ */ + public boolean hasProgressed() + { + return false; + } + /* ------------------------------------------------------------ */ /** * Updates selection key. Adds operations types to the selection key as needed. No operations @@ -471,17 +494,21 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo */ private void updateKey() { + final boolean changed; synchronized (this) { - int ops=-1; + int current_ops=-1; if (getChannel().isOpen()) { + boolean read_interest = _readBlocked || (!_dispatched && !_connection.isSuspended()); + boolean write_interest= _writeBlocked || (!_dispatched && !_writable); + _interestOps = - ((!_socket.isInputShutdown() && (!_dispatched || _readBlocked)) ? SelectionKey.OP_READ : 0) - | ((!_socket.isOutputShutdown()&& (!_writable || _writeBlocked)) ? SelectionKey.OP_WRITE : 0); + ((!_socket.isInputShutdown() && read_interest ) ? SelectionKey.OP_READ : 0) + | ((!_socket.isOutputShutdown()&& write_interest) ? SelectionKey.OP_WRITE : 0); try { - ops = ((_key!=null && _key.isValid())?_key.interestOps():-1); + current_ops = ((_key!=null && _key.isValid())?_key.interestOps():-1); } catch(Exception e) { @@ -489,14 +516,17 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo LOG.ignore(e); } } - - if(_interestOps == ops && getChannel().isOpen()) - return; + changed=_interestOps!=current_ops; + } + + if(changed) + { + _selectSet.addChange(this); + _selectSet.wakeup(); } - _selectSet.addChange(this); - _selectSet.wakeup(); } + /* ------------------------------------------------------------ */ /** * Synchronize the interestOps with the actual key. Call is scheduled by a call to updateKey @@ -529,7 +559,6 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo { _key.cancel(); } - cancelIdle(); if (_open) { @@ -558,7 +587,6 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo if (_key!=null && _key.isValid()) _key.cancel(); - cancelIdle(); if (_open) { _open=false; @@ -583,11 +611,13 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo { while(true) { - final Connection next = _connection.handle(); + final AsyncConnection next = (AsyncConnection)_connection.handle(); if (next!=_connection) { LOG.debug("{} replaced {}",next,_connection); + Connection old=_connection; _connection=next; + _manager.endPointUpgraded(this,old); continue; } break; @@ -600,23 +630,44 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo catch (EofException e) { LOG.debug("EOF", e); - try{getChannel().close();} + try{close();} catch(IOException e2){LOG.ignore(e2);} } catch (IOException e) { LOG.warn(e.toString()); LOG.debug(e); - try{getChannel().close();} + try{close();} catch(IOException e2){LOG.ignore(e2);} } catch (Throwable e) { LOG.warn("handle failed", e); - try{getChannel().close();} + try{close();} catch(IOException e2){LOG.ignore(e2);} } - dispatched=!undispatch(); + finally + { + if (!_ishut && isInputShutdown() && isOpen()) + { + _ishut=true; + try + { + _connection.onInputShutdown(); + } + catch(Throwable x) + { + LOG.warn("onInputShutdown failed", x); + try{close();} + catch(IOException e2){LOG.ignore(e2);} + } + finally + { + updateKey(); + } + } + dispatched=!undispatch(); + } } } finally @@ -660,9 +711,21 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo { synchronized(this) { - return "SCEP@" + hashCode() + _channel+ - "[o="+isOpen()+" d=" + _dispatched + ",io=" + _interestOps+ - ",w=" + _writable + ",rb=" + _readBlocked + ",wb=" + _writeBlocked + "]"; + return String.format("SCEP@%x{%s->%s,d=%b,open=%b,ishut=%b,oshut=%b,rb=%b,wb=%b,w=%b,i=%d%s%s%s}", + hashCode(), + _socket.getRemoteSocketAddress(), + _socket.getLocalSocketAddress(), + _dispatched, + isOpen(), + isInputShutdown(), + isOutputShutdown(), + _readBlocked, + _writeBlocked, + _writable, + _interestOps, + _key != null && _key.isValid() ? "" : "!", + _key != null && _key.isValid() && _key.isReadable() ? "r" : "", + _key != null && _key.isValid() && _key.isWritable() ? "w" : ""); } } diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectorManager.java b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectorManager.java index 1b8218832a4..eaeabc88624 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectorManager.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectorManager.java @@ -30,6 +30,7 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import org.eclipse.jetty.io.AsyncEndPoint; import org.eclipse.jetty.io.ConnectedEndPoint; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; @@ -54,7 +55,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa public static final Logger LOG=Log.getLogger("org.eclipse.jetty.io.nio"); private static final int __MONITOR_PERIOD=Integer.getInteger("org.eclipse.jetty.io.nio.MONITOR_PERIOD",1000).intValue(); - private static final int __MAX_SELECTS=Integer.getInteger("org.eclipse.jetty.io.nio.MAX_SELECTS",25000).intValue(); + private static final int __MAX_SELECTS=Integer.getInteger("org.eclipse.jetty.io.nio.MAX_SELECTS",100000).intValue(); private static final int __BUSY_PAUSE=Integer.getInteger("org.eclipse.jetty.io.nio.BUSY_PAUSE",50).intValue(); private static final int __IDLE_TICK=Integer.getInteger("org.eclipse.jetty.io.nio.IDLE_TICK",400).intValue(); @@ -63,7 +64,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa private long _lowResourcesConnections; private SelectSet[] _selectSet; private int _selectSets=1; - private volatile int _set; + private volatile int _set=0; private boolean _deferringInterestedOps0=true; private int _selectorPriorityDelta=0; @@ -128,6 +129,8 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa // be distributed over the available sets. int s=_set++; + if (s<0) + s=-s; s=s%_selectSets; SelectSet[] sets=_selectSet; if (sets!=null) @@ -150,6 +153,8 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa // be distributed over the available sets. int s=_set++; + if (s<0) + s=-s; s=s%_selectSets; SelectSet[] sets=_selectSet; if (sets!=null) @@ -167,6 +172,8 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa public void register(ServerSocketChannel acceptChannel) { int s=_set++; + if (s<0) + s=-s; s=s%_selectSets; SelectSet set=_selectSet[s]; set.addChange(acceptChannel); @@ -276,10 +283,6 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa { set.doSelect(); } - catch(ThreadDeath e) - { - throw e; - } catch(IOException e) { LOG.ignore(e); @@ -337,7 +340,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa protected abstract void endPointUpgraded(ConnectedEndPoint endpoint,Connection oldConnection); /* ------------------------------------------------------------------------------- */ - protected abstract Connection newConnection(SocketChannel channel, SelectChannelEndPoint endpoint); + public abstract AsyncConnection newConnection(SocketChannel channel, AsyncEndPoint endpoint, Object attachment); /* ------------------------------------------------------------ */ /** @@ -502,9 +505,6 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa } catch (Throwable e) { - if (e instanceof ThreadDeath) - throw (ThreadDeath)e; - if (isRunning()) LOG.warn(e); else @@ -570,21 +570,13 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa { // Start injecting pauses _pausing=true; - + // if this is the first pause if (!_paused) { // Log and dump some status _paused=true; LOG.warn("Selector {} is too busy, pausing!",this); - final SelectSet set = this; - SelectorManager.this.dispatch( - new Runnable(){ - public void run() - { - System.err.println(set+":\n"+set.dump()); - } - }); } } } @@ -713,17 +705,18 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa endp.checkIdleTimestamp(idle_now); } } + public String toString() {return "Idle-"+super.toString();} }); - + } - + // Reset busy select monitor counts if (__MONITOR_PERIOD>0 && now>_monitorNext) { _busySelects=0; _pausing=false; _monitorNext=now+__MONITOR_PERIOD; - + } } catch (ClosedSelectorException e) @@ -837,6 +830,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa private SelectChannelEndPoint createEndPoint(SocketChannel channel, SelectionKey sKey) throws IOException { SelectChannelEndPoint endp = newEndPoint(channel,this,sKey); + LOG.debug("created {}",endp); endPointOpened(endp); _endPoints.put(endp,this); return endp; @@ -975,11 +969,22 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa for (SelectionKey key: selector.keys()) { if (key.isValid()) - dumpto.add(key.attachment()+" "+key.interestOps()+" "+key.readyOps()); + dumpto.add(key.attachment()+" iOps="+key.interestOps()+" rOps="+key.readyOps()); else - dumpto.add(key.attachment()+" - - "); + dumpto.add(key.attachment()+" iOps=-1 rOps=-1"); } } + + /* ------------------------------------------------------------ */ + public String toString() + { + Selector selector=_selector; + return String.format("%s %s keys=%d selected=%d", + super.toString(), + SelectorManager.this.getState(), + selector != null && selector.isOpen() ? selector.keys().size() : -1, + selector != null && selector.isOpen() ? selector.selectedKeys().size() : -1); + } } /* ------------------------------------------------------------ */ diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SslConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SslConnection.java new file mode 100644 index 00000000000..d8cee6277f5 --- /dev/null +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SslConnection.java @@ -0,0 +1,812 @@ +// ======================================================================== +// Copyright (c) 2004-2011 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.io.nio; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.concurrent.atomic.AtomicBoolean; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLEngineResult; +import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLSession; + +import org.eclipse.jetty.io.AbstractConnection; +import org.eclipse.jetty.io.AsyncEndPoint; +import org.eclipse.jetty.io.Buffer; +import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.thread.Timeout.Task; + +/* ------------------------------------------------------------ */ +/** SSL Connection. + * An AysyncConnection that acts as an interceptor between and EndPoint and another + * Connection, that implements TLS encryption using an {@link SSLEngine}. + *

+ * The connector uses an {@link AsyncEndPoint} (like {@link SelectChannelEndPoint}) as + * it's source/sink of encrypted data. It then provides {@link #getSslEndPoint()} to + * expose a source/sink of unencrypted data to another connection (eg HttpConnection). + */ +public class SslConnection extends AbstractConnection implements AsyncConnection +{ + static final Logger LOG=Log.getLogger("org.eclipse.jetty.io.nio.ssl"); + + private static final NIOBuffer __ZERO_BUFFER=new IndirectNIOBuffer(0); + + private static final ThreadLocal __buffers = new ThreadLocal(); + private final SSLEngine _engine; + private final SSLSession _session; + private AsyncConnection _connection; + private final SslEndPoint _sslEndPoint = new SslEndPoint(); + private int _allocations; + private SslBuffers _buffers; + private NIOBuffer _inbound; + private NIOBuffer _unwrapBuf; + private NIOBuffer _outbound; + private AsyncEndPoint _aEndp; + private boolean _allowRenegotiate=true; + private boolean _handshook; + private boolean _ishut; + private boolean _oshut; + private final AtomicBoolean _progressed = new AtomicBoolean(); + + /* ------------------------------------------------------------ */ + /* this is a half baked buffer pool + */ + private static class SslBuffers + { + final NIOBuffer _in; + final NIOBuffer _out; + final NIOBuffer _unwrap; + + SslBuffers(int packetSize, int appSize) + { + _in=new IndirectNIOBuffer(packetSize); + _out=new IndirectNIOBuffer(packetSize); + _unwrap=new IndirectNIOBuffer(appSize); + } + } + + /* ------------------------------------------------------------ */ + public SslConnection(SSLEngine engine,EndPoint endp) + { + this(engine,endp,System.currentTimeMillis()); + } + + /* ------------------------------------------------------------ */ + public SslConnection(SSLEngine engine,EndPoint endp, long timeStamp) + { + super(endp,timeStamp); + _engine=engine; + _session=_engine.getSession(); + _aEndp=(AsyncEndPoint)endp; + } + + /* ------------------------------------------------------------ */ + /** + * @return True if SSL re-negotiation is allowed (default false) + */ + public boolean isAllowRenegotiate() + { + return _allowRenegotiate; + } + + /* ------------------------------------------------------------ */ + /** + * Set if SSL re-negotiation is allowed. CVE-2009-3555 discovered + * a vulnerability in SSL/TLS with re-negotiation. If your JVM + * does not have CVE-2009-3555 fixed, then re-negotiation should + * not be allowed. CVE-2009-3555 was fixed in Sun java 1.6 with a ban + * of renegotiates in u19 and with RFC5746 in u22. + * + * @param allowRenegotiate + * true if re-negotiation is allowed (default false) + */ + public void setAllowRenegotiate(boolean allowRenegotiate) + { + _allowRenegotiate = allowRenegotiate; + } + + /* ------------------------------------------------------------ */ + private void allocateBuffers() + { + synchronized (this) + { + if (_allocations++==0) + { + if (_buffers==null) + { + _buffers=__buffers.get(); + if (_buffers==null) + _buffers=new SslBuffers(_session.getPacketBufferSize()*2,_session.getApplicationBufferSize()*2); + _inbound=_buffers._in; + _outbound=_buffers._out; + _unwrapBuf=_buffers._unwrap; + __buffers.set(null); + } + } + } + } + + /* ------------------------------------------------------------ */ + private void releaseBuffers() + { + synchronized (this) + { + if (--_allocations==0) + { + if (_buffers!=null && + _inbound.length()==0 && + _outbound.length()==0 && + _unwrapBuf.length()==0) + { + _inbound=null; + _outbound=null; + _unwrapBuf=null; + __buffers.set(_buffers); + _buffers=null; + } + } + } + } + + /* ------------------------------------------------------------ */ + public Connection handle() throws IOException + { + try + { + allocateBuffers(); + + boolean progress=true; + + while (progress) + { + progress=false; + + // If we are handshook let the delegate connection + if (_engine.getHandshakeStatus()!=HandshakeStatus.NOT_HANDSHAKING) + { + progress=process(null,null); + } + else + { + // handle the delegate connection + AsyncConnection next = (AsyncConnection)_connection.handle(); + if (next!=_connection && next!=null) + { + _connection=next; + progress=true; + } + } + + LOG.debug("{} handle {} progress=",_session,this, progress); + } + } + finally + { + releaseBuffers(); + + if (!_ishut && _sslEndPoint.isInputShutdown() && _sslEndPoint.isOpen()) + { + _ishut=true; + try + { + _connection.onInputShutdown(); + } + catch(Throwable x) + { + LOG.warn("onInputShutdown failed", x); + try{_sslEndPoint.close();} + catch(IOException e2){LOG.ignore(e2);} + } + } + } + + return this; + } + + /* ------------------------------------------------------------ */ + public boolean isIdle() + { + return false; + } + + /* ------------------------------------------------------------ */ + public boolean isSuspended() + { + return false; + } + + /* ------------------------------------------------------------ */ + public void onClose() + { + } + + /* ------------------------------------------------------------ */ + @Override + public void onIdleExpired(long idleForMs) + { + try + { + LOG.debug("onIdleExpired {}ms on {}",idleForMs,this); + _sslEndPoint.shutdownOutput(); + } + catch (IOException e) + { + LOG.warn(e); + super.onIdleExpired(idleForMs); + } + } + + /* ------------------------------------------------------------ */ + public void onInputShutdown() throws IOException + { + + } + + /* ------------------------------------------------------------ */ + private synchronized boolean process(Buffer toFill, Buffer toFlush) throws IOException + { + boolean some_progress=false; + try + { + allocateBuffers(); + if (toFill==null) + { + _unwrapBuf.compact(); + toFill=_unwrapBuf; + } + else if (toFill.capacity()<_session.getApplicationBufferSize()) + { + boolean progress=process(null,toFlush); + if (_unwrapBuf!=null && _unwrapBuf.hasContent()) + { + _unwrapBuf.skip(toFill.put(_unwrapBuf)); + return true; + } + else + return progress; + } + else if (_unwrapBuf!=null && _unwrapBuf.hasContent()) + { + _unwrapBuf.skip(toFill.put(_unwrapBuf)); + return true; + } + + + if (toFlush==null) + toFlush=__ZERO_BUFFER; + + boolean progress=true; + + while (progress) + { + progress=false; + int filled=0,flushed=0; + + try + { + // Read any available data + if (_inbound.space()>0 && (filled=_endp.fill(_inbound))>0) + progress = true; + + // flush any output data + if (_outbound.hasContent() && (flushed=_endp.flush(_outbound))>0) + progress = true; + } + catch (Exception e) + { + LOG.debug(e.toString()); + LOG.ignore(e); + } + LOG.debug("{} {} {} filled={}/{} flushed={}/{}",_session,this,_engine.getHandshakeStatus(),filled,_inbound.length(),flushed,_outbound.length()); + + // handle the current hand share status + switch(_engine.getHandshakeStatus()) + { + case FINISHED: + throw new IllegalStateException(); + + case NOT_HANDSHAKING: + { + // Try wrapping some application data + if (toFlush.hasContent() && _outbound.space()>0 && wrap(toFlush)) + progress=true; + + // Try unwrapping some application data + if (toFill.space()>0 && _inbound.hasContent() && unwrap(toFill)) + progress=true; + } + break; + + case NEED_TASK: + { + // A task needs to be run, so run it! + Runnable task; + while ((task=_engine.getDelegatedTask())!=null) + { + progress=true; + task.run(); + } + + // Detect SUN JVM Bug!!! + /* TODO + if(initialStatus==HandshakeStatus.NOT_HANDSHAKING && + _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP && sent==0 ) + { + // This should be NEED_WRAP + // The fix simply detects the signature of the bug and then close the connection (fail-fast) so that ff3 will delegate to using SSL instead of TLS. + // This is a jvm bug on java1.6 where the SSLEngine expects more data from the initial handshake when the client(ff3-tls) already had given it. + // See http://jira.codehaus.org/browse/JETTY-567 for more details + LOG.warn("{} JETTY-567",_session); + _endp.close(); + return false; + } + */ + } + break; + + case NEED_WRAP: + { + // The SSL needs to send some handshake data to the other side + if (_handshook && !_allowRenegotiate) + _endp.close(); + else if (wrap(toFlush)) + progress=true; + } + break; + + case NEED_UNWRAP: + { + // The SSL needs to receive some handshake data from the other side + if (_handshook && !_allowRenegotiate) + _endp.close(); + else if (unwrap(toFill)) + progress=true; + } + break; + } + + // pass on ishut/oshut state + if (_endp.isOpen() && _endp.isInputShutdown() && !_inbound.hasContent()) + _engine.closeInbound(); + + if (_endp.isOpen() && _engine.isOutboundDone() && !_outbound.hasContent()) + _endp.shutdownOutput(); + + some_progress|=progress; + } + } + finally + { + releaseBuffers(); + if (some_progress) + _progressed.set(true); + } + return some_progress; + } + + private synchronized boolean wrap(final Buffer buffer) throws IOException + { + ByteBuffer bbuf=extractByteBuffer(buffer); + final SSLEngineResult result; + + synchronized(bbuf) + { + _outbound.compact(); + ByteBuffer out_buffer=_outbound.getByteBuffer(); + synchronized(out_buffer) + { + try + { + bbuf.position(buffer.getIndex()); + bbuf.limit(buffer.putIndex()); + out_buffer.position(_outbound.putIndex()); + out_buffer.limit(out_buffer.capacity()); + result=_engine.wrap(bbuf,out_buffer); + if (LOG.isDebugEnabled()) + LOG.debug("{} wrap {} {} consumed={} produced={}", + _session, + result.getStatus(), + result.getHandshakeStatus(), + result.bytesConsumed(), + result.bytesProduced()); + + + buffer.skip(result.bytesConsumed()); + _outbound.setPutIndex(_outbound.putIndex()+result.bytesProduced()); + } + catch(SSLException e) + { + LOG.warn(_endp+":",e); + _endp.close(); + throw e; + } + finally + { + out_buffer.position(0); + out_buffer.limit(out_buffer.capacity()); + bbuf.position(0); + bbuf.limit(bbuf.capacity()); + } + } + } + + switch(result.getStatus()) + { + case BUFFER_UNDERFLOW: + throw new IllegalStateException(); + + case BUFFER_OVERFLOW: + break; + + case OK: + if (result.getHandshakeStatus()==HandshakeStatus.FINISHED) + _handshook=true; + break; + + case CLOSED: + LOG.debug("wrap CLOSE {} {}",this,result); + if (result.getHandshakeStatus()==HandshakeStatus.FINISHED) + _endp.close(); + break; + + default: + LOG.warn("{} wrap default {}",_session,result); + throw new IOException(result.toString()); + } + + return result.bytesConsumed()>0 || result.bytesProduced()>0; + } + + private synchronized boolean unwrap(final Buffer buffer) throws IOException + { + if (!_inbound.hasContent()) + return false; + + ByteBuffer bbuf=extractByteBuffer(buffer); + final SSLEngineResult result; + + synchronized(bbuf) + { + ByteBuffer in_buffer=_inbound.getByteBuffer(); + synchronized(in_buffer) + { + try + { + bbuf.position(buffer.putIndex()); + bbuf.limit(buffer.capacity()); + in_buffer.position(_inbound.getIndex()); + in_buffer.limit(_inbound.putIndex()); + + result=_engine.unwrap(in_buffer,bbuf); + if (LOG.isDebugEnabled()) + LOG.debug("{} unwrap {} {} consumed={} produced={}", + _session, + result.getStatus(), + result.getHandshakeStatus(), + result.bytesConsumed(), + result.bytesProduced()); + + _inbound.skip(result.bytesConsumed()); + _inbound.compact(); + buffer.setPutIndex(buffer.putIndex()+result.bytesProduced()); + } + catch(SSLException e) + { + LOG.warn(_endp+":"+e); + LOG.debug(e); + if (_endp.isOpen()) + _endp.close(); + throw e; + } + finally + { + in_buffer.position(0); + in_buffer.limit(in_buffer.capacity()); + bbuf.position(0); + bbuf.limit(bbuf.capacity()); + } + } + } + + switch(result.getStatus()) + { + case BUFFER_UNDERFLOW: + break; + + case BUFFER_OVERFLOW: + LOG.debug("{} unwrap {} {}->{}",_session,result.getStatus(),_inbound.toDetailString(),buffer.toDetailString()); + break; + + case OK: + if (result.getHandshakeStatus()==HandshakeStatus.FINISHED) + _handshook=true; + break; + + case CLOSED: + LOG.debug("unwrap CLOSE {} {}",this,result); + if (result.getHandshakeStatus()==HandshakeStatus.FINISHED) + _endp.close(); + break; + + default: + LOG.warn("{} wrap default {}",_session,result); + throw new IOException(result.toString()); + } + + //if (LOG.isDebugEnabled() && result.bytesProduced()>0) + // LOG.debug("{} unwrapped '{}'",_session,buffer); + + return result.bytesConsumed()>0 || result.bytesProduced()>0; + } + + + /* ------------------------------------------------------------ */ + private ByteBuffer extractByteBuffer(Buffer buffer) + { + if (buffer.buffer() instanceof NIOBuffer) + return ((NIOBuffer)buffer.buffer()).getByteBuffer(); + return ByteBuffer.wrap(buffer.array()); + } + + /* ------------------------------------------------------------ */ + public AsyncEndPoint getSslEndPoint() + { + return _sslEndPoint; + } + + public String toString() + { + return String.format("%s | %s", super.toString(), _sslEndPoint); + } + + /* ------------------------------------------------------------ */ + /* ------------------------------------------------------------ */ + public class SslEndPoint implements AsyncEndPoint + { + public SSLEngine getSslEngine() + { + return _engine; + } + + public void shutdownOutput() throws IOException + { + synchronized (SslConnection.this) + { + LOG.debug("{} ssl endp.oshut {}",_session,this); + _engine.closeOutbound(); + _oshut=true; + } + flush(); + } + + public boolean isOutputShutdown() + { + synchronized (SslConnection.this) + { + return _oshut||!isOpen()||_engine.isOutboundDone(); + } + } + + public void shutdownInput() throws IOException + { + LOG.debug("{} ssl endp.ishut!",_session); + // We do not do a closeInput here, as SSL does not support half close. + // isInputShutdown works it out itself from buffer state and underlying endpoint state. + } + + public boolean isInputShutdown() + { + synchronized (SslConnection.this) + { + return _endp.isInputShutdown() && + !(_unwrapBuf!=null&&_unwrapBuf.hasContent()) && + !(_inbound!=null&&_inbound.hasContent()); + } + } + + public void close() throws IOException + { + LOG.debug("{} ssl endp.close",_session); + _endp.close(); + } + + public int fill(Buffer buffer) throws IOException + { + int size=buffer.length(); + process(buffer,null); + + int filled=buffer.length()-size; + + if (filled==0 && isInputShutdown()) + return -1; + return filled; + } + + public int flush(Buffer buffer) throws IOException + { + int size = buffer.length(); + process(null, buffer); + return size-buffer.length(); + } + + public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException + { + if (header!=null && header.hasContent()) + return flush(header); + if (buffer!=null && buffer.hasContent()) + return flush(buffer); + if (trailer!=null && trailer.hasContent()) + return flush(trailer); + return 0; + } + + public boolean blockReadable(long millisecs) throws IOException + { + long now = System.currentTimeMillis(); + long end=millisecs>0?(now+millisecs):Long.MAX_VALUE; + + while (now - * A SelectChannelEndPoint that uses an {@link SSLEngine} to handle an - * SSL connection. - *

- * There is a named logger "org.eclipse.jetty.http.ssl" - *

- */ -public class SslSelectChannelEndPoint extends SelectChannelEndPoint -{ - public static final Logger LOG=Log.getLogger("org.eclipse.jetty.io.nio").getLogger("ssl"); - - private static final Buffer __EMPTY_BUFFER=new DirectNIOBuffer(0); - private static final ByteBuffer __ZERO_BUFFER=ByteBuffer.allocate(0); - - private final Buffers _buffers; - - private final SSLEngine _engine; - private final SSLSession _session; - private volatile NIOBuffer _inNIOBuffer; - private volatile NIOBuffer _outNIOBuffer; - - private boolean _closing=false; - private SSLEngineResult _result; - - private volatile boolean _handshook=false; - private boolean _allowRenegotiate=true; - - private volatile boolean _debug = LOG.isDebugEnabled(); // snapshot debug status for optimizer - - /* ------------------------------------------------------------ */ - public SslSelectChannelEndPoint(Buffers buffers,SocketChannel channel, SelectorManager.SelectSet selectSet, SelectionKey key, SSLEngine engine, int maxIdleTime) - throws IOException - { - super(channel,selectSet,key, maxIdleTime); - _buffers=buffers; - - // ssl - _engine=engine; - _session=engine.getSession(); - - if (_debug) LOG.debug(_session+" channel="+channel); - } - - /* ------------------------------------------------------------ */ - public SslSelectChannelEndPoint(Buffers buffers,SocketChannel channel, SelectorManager.SelectSet selectSet, SelectionKey key, SSLEngine engine) - throws IOException - { - super(channel,selectSet,key); - _buffers=buffers; - - // ssl - _engine=engine; - _session=engine.getSession(); - - if (_debug) LOG.debug(_session+" channel="+channel); - } - - - /* ------------------------------------------------------------ */ - private void needOutBuffer() - { - synchronized (this) - { - if (_outNIOBuffer==null) - _outNIOBuffer=(NIOBuffer)_buffers.getBuffer(_session.getPacketBufferSize()); - } - } - - /* ------------------------------------------------------------ */ - private void freeOutBuffer() - { - synchronized (this) - { - if (_outNIOBuffer!=null && _outNIOBuffer.length()==0) - { - _buffers.returnBuffer(_outNIOBuffer); - _outNIOBuffer=null; - } - } - } - - /* ------------------------------------------------------------ */ - private void needInBuffer() - { - synchronized (this) - { - if(_inNIOBuffer==null) - _inNIOBuffer=(NIOBuffer)_buffers.getBuffer(_session.getPacketBufferSize()); - } - } - - /* ------------------------------------------------------------ */ - private void freeInBuffer() - { - synchronized (this) - { - if (_inNIOBuffer!=null && _inNIOBuffer.length()==0) - { - _buffers.returnBuffer(_inNIOBuffer); - _inNIOBuffer=null; - } - } - } - - /* ------------------------------------------------------------ */ - /** - * @return True if the endpoint has produced/consumed bytes itself (non application data). - */ - public boolean isProgressing() - { - SSLEngineResult result = _result; - _result=null; - return result!=null && (result.bytesConsumed()>0 || result.bytesProduced()>0); - } - - /* ------------------------------------------------------------ */ - /** - * @return True if SSL re-negotiation is allowed (default false) - */ - public boolean isAllowRenegotiate() - { - return _allowRenegotiate; - } - - /* ------------------------------------------------------------ */ - /** - * Set if SSL re-negotiation is allowed. CVE-2009-3555 discovered - * a vulnerability in SSL/TLS with re-negotiation. If your JVM - * does not have CVE-2009-3555 fixed, then re-negotiation should - * not be allowed. - * @param allowRenegotiate true if re-negotiation is allowed (default false) - */ - public void setAllowRenegotiate(boolean allowRenegotiate) - { - _allowRenegotiate = allowRenegotiate; - } - - - /* ------------------------------------------------------------ */ - @Override - public boolean isOutputShutdown() - { - return _engine!=null && _engine.isOutboundDone(); - } - - /* ------------------------------------------------------------ */ - @Override - public boolean isInputShutdown() - { - return _engine!=null && _engine.isInboundDone(); - } - - /* ------------------------------------------------------------ */ - @Override - public void shutdownOutput() throws IOException - { - LOG.debug("{} shutdownOutput",_session); - // All SSL closes should be graceful, as it is more secure. - // So normal SSL close can be used here. - close(); - } - - /* ------------------------------------------------------------ */ - private int process(ByteBuffer inBBuf, Buffer outBuf) throws IOException - { - if (_debug) - LOG.debug("{} process closing={} in={} out={}",_session,_closing,inBBuf,outBuf); - - // If there is no place to put incoming application data, - if (inBBuf==null) - { - // use ZERO buffer - inBBuf=__ZERO_BUFFER; - } - - int received=0; - int sent=0; - - - HandshakeStatus initialStatus = _engine.getHandshakeStatus(); - boolean progress=true; - - while (progress) - { - progress=false; - - // flush output data - int len=_outNIOBuffer==null?0:_outNIOBuffer.length(); - - // we must flush it, as the other end might be - // waiting for that outgoing data before sending - // more incoming data - flush(); - - // If we have written some bytes, then progress has been made. - progress|=(_outNIOBuffer==null?0:_outNIOBuffer.length())0||_result.bytesProduced()>0||_result.bytesConsumed()>0; - - if (c>0) - sent+=c; - else if (c<0 && sent==0) - sent=-1; - } - - // Try unwrapping some application data - if (inBBuf.remaining()>0 && _inNIOBuffer!=null && _inNIOBuffer.hasContent()) - { - int space=inBBuf.remaining(); - progress|=unwrap(inBBuf); - received+=space-inBBuf.remaining(); - } - break; - - - case NEED_TASK: - { - // A task needs to be run, so run it! - Runnable task; - while ((task=_engine.getDelegatedTask())!=null) - { - progress=true; - task.run(); - } - - // Detect SUN JVM Bug!!! - if(initialStatus==HandshakeStatus.NOT_HANDSHAKING && - _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP && sent==0) - { - // This should be NEED_WRAP - // The fix simply detects the signature of the bug and then close the connection (fail-fast) so that ff3 will delegate to using SSL instead of TLS. - // This is a jvm bug on java1.6 where the SSLEngine expects more data from the initial handshake when the client(ff3-tls) already had given it. - // See http://jira.codehaus.org/browse/JETTY-567 for more details - if (_debug) LOG.warn("{} JETTY-567",_session); - return -1; - } - break; - } - - case NEED_WRAP: - { - checkRenegotiate(); - - // The SSL needs to send some handshake data to the other side - int c=0; - if (outBuf!=null && outBuf.hasContent()) - c=wrap(outBuf); - else - c=wrap(__EMPTY_BUFFER); - - progress=_result.bytesProduced()>0||_result.bytesConsumed()>0; - if (c>0) - sent+=c; - else if (c<0 && sent==0) - sent=-1; - break; - } - - case NEED_UNWRAP: - { - checkRenegotiate(); - - // Need more data to be unwrapped so try another call to unwrap - progress|=unwrap(inBBuf); - if (_closing && inBBuf.hasRemaining()) - inBBuf.clear(); - break; - } - } - - if (_debug) LOG.debug("{} progress {}",_session,progress); - } - - if (_debug) LOG.debug("{} received {} sent {}",_session,received,sent); - - freeInBuffer(); - return (received<0||sent<0)?-1:(received+sent); - } - - - - /* ------------------------------------------------------------ */ - @Override - public void close() throws IOException - { - // For safety we always force a close calling super - try - { - if (!_closing) - { - _closing=true; - LOG.debug("{} close",_session); - _engine.closeOutbound(); - process(null,null); - } - } - catch (IOException e) - { - // We could not write the SSL close message because the - // socket was already closed, nothing more we can do. - LOG.ignore(e); - } - finally - { - super.close(); - } - } - - /* ------------------------------------------------------------ */ - /** Fill the buffer with unencrypted bytes. - * Called by a Http Parser when more data is - * needed to continue parsing a request or a response. - */ - @Override - public int fill(Buffer buffer) throws IOException - { - _debug=LOG.isDebugEnabled(); - LOG.debug("{} fill",_session); - // This end point only works on NIO buffer type (director - // or indirect), so extract the NIO buffer that is wrapped - // by the passed jetty Buffer. - ByteBuffer bbuf=((NIOBuffer)buffer).getByteBuffer(); - - - // remember the original size of the unencrypted buffer - int size=buffer.length(); - - - synchronized (bbuf) - { - bbuf.position(buffer.putIndex()); - try - { - // Call the SSLEngine unwrap method to process data in - // the inBuffer. If there is no data in the inBuffer, then - // super.fill is called to read encrypted bytes. - unwrap(bbuf); - process(bbuf,null); - } - finally - { - // reset the Buffers - buffer.setPutIndex(bbuf.position()); - bbuf.position(0); - } - } - // return the number of unencrypted bytes filled. - int filled=buffer.length()-size; - if (filled==0 && (isInputShutdown() || !isOpen())) - return -1; - - return filled; - } - - /* ------------------------------------------------------------ */ - @Override - public int flush(Buffer buffer) throws IOException - { - _debug=LOG.isDebugEnabled(); - LOG.debug("{} flush1",_session); - return process(null,buffer); - } - - - /* ------------------------------------------------------------ */ - /* - */ - @Override - public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException - { - _debug=LOG.isDebugEnabled(); - LOG.debug("{} flush3",_session); - - int len=0; - int flushed=0; - if (header!=null && header.hasContent()) - { - len=header.length(); - flushed=flush(header); - } - if (flushed==len && buffer!=null && buffer.hasContent()) - { - int f=flush(buffer); - if (f>=0) - flushed+=f; - else if (flushed==0) - flushed=-1; - } - - return flushed; - } - - /* ------------------------------------------------------------ */ - @Override - public void flush() throws IOException - { - LOG.debug("{} flush",_session); - if (!isOpen()) - throw new EofException(); - - if (isBufferingOutput()) - { - int flushed=super.flush(_outNIOBuffer); - if (_debug) - LOG.debug("{} flushed={} left={}",_session,flushed,_outNIOBuffer.length()); - } - else if (_engine.isOutboundDone() && super.isOpen()) - { - if (_debug) - LOG.debug("{} flush shutdownOutput",_session); - try - { - super.shutdownOutput(); - } - catch(IOException e) - { - LOG.ignore(e); - } - } - - freeOutBuffer(); - } - - /* ------------------------------------------------------------ */ - private void checkRenegotiate() throws IOException - { - if (_handshook && !_allowRenegotiate && _channel!=null && _channel.isOpen()) - { - LOG.warn("SSL renegotiate denied: {}",_channel); - super.close(); - } - } - - /* ------------------------------------------------------------ */ - /** - * @return true if progress is made - */ - private boolean unwrap(ByteBuffer buffer) throws IOException - { - needInBuffer(); - ByteBuffer in_buffer=_inNIOBuffer.getByteBuffer(); - - _inNIOBuffer.compact(); - - int total_filled=0; - boolean remoteClosed = false; - - LOG.debug("{} unwrap space={} open={}",_session,_inNIOBuffer.space(),super.isOpen()); - - // loop filling as much encrypted data as we can into the buffer - while (_inNIOBuffer.space()>0 && super.isOpen()) - { - int filled=super.fill(_inNIOBuffer); - if (_debug) LOG.debug("{} filled {}",_session,filled); - if (filled < 0) - remoteClosed = true; - // break the loop if no progress is made (we have read everything there is to read) - if (filled<=0) - break; - total_filled+=filled; - } - - // If we have no progress and no data - if (total_filled==0 && _inNIOBuffer.length()==0) - { - // Do we need to close? - if (isOpen() && remoteClosed) - { - try - { - _engine.closeInbound(); - } - catch (SSLException x) - { - // It may happen, for example, in case of truncation - // attacks, we close so that we do not spin forever - super.close(); - } - } - - if (!isOpen()) - throw new EofException(); - - return false; - } - - // We have some in data, so try to unwrap it. - try - { - // inBuffer is the NIO buffer inside the _inNIOBuffer, - // so update its position and limit from the inNIOBuffer. - in_buffer.position(_inNIOBuffer.getIndex()); - in_buffer.limit(_inNIOBuffer.putIndex()); - - // Do the unwrap - _result=_engine.unwrap(in_buffer,buffer); - if (!_handshook && _result.getHandshakeStatus()==SSLEngineResult.HandshakeStatus.FINISHED) - _handshook=true; - if (_debug) LOG.debug("{} unwrap {}",_session,_result); - - // skip the bytes consumed - _inNIOBuffer.skip(_result.bytesConsumed()); - } - catch(SSLException e) - { - LOG.debug(getRemoteAddr() + ":" + getRemotePort() + " ",e); - super.close(); - throw e; - } - finally - { - // reset the buffer so it can be managed by the _inNIOBuffer again. - in_buffer.position(0); - in_buffer.limit(in_buffer.capacity()); - } - - // handle the unwrap results - switch(_result.getStatus()) - { - case BUFFER_OVERFLOW: - LOG.debug("{} unwrap overflow",_session); - return false; - - case BUFFER_UNDERFLOW: - // Not enough data, - // If we are closed, we will never get more, so EOF - // else return and we will be tried again - // later when more data arriving causes another dispatch. - if (LOG.isDebugEnabled()) LOG.debug("{} unwrap {}",_session,_result); - if(!isOpen()) - { - _inNIOBuffer.clear(); - if (_outNIOBuffer!=null) - _outNIOBuffer.clear(); - throw new EofException(); - } - return (total_filled > 0); - - case CLOSED: - _closing=true; - // return true is some bytes somewhere were moved about. - return total_filled>0 ||_result.bytesConsumed()>0 || _result.bytesProduced()>0; - - case OK: - // return true is some bytes somewhere were moved about. - return total_filled>0 ||_result.bytesConsumed()>0 || _result.bytesProduced()>0; - - default: - LOG.warn("{} unwrap default: {}",_session,_result); - throw new IOException(_result.toString()); - } - } - - /* ------------------------------------------------------------ */ - private ByteBuffer extractOutputBuffer(Buffer buffer) - { - if (buffer.buffer() instanceof NIOBuffer) - return ((NIOBuffer)buffer.buffer()).getByteBuffer(); - - return ByteBuffer.wrap(buffer.array()); - } - - /* ------------------------------------------------------------ */ - private int wrap(final Buffer buffer) throws IOException - { - ByteBuffer bbuf=extractOutputBuffer(buffer); - synchronized(bbuf) - { - int consumed=0; - needOutBuffer(); - _outNIOBuffer.compact(); - ByteBuffer out_buffer=_outNIOBuffer.getByteBuffer(); - synchronized(out_buffer) - { - try - { - bbuf.position(buffer.getIndex()); - bbuf.limit(buffer.putIndex()); - out_buffer.position(_outNIOBuffer.putIndex()); - out_buffer.limit(out_buffer.capacity()); - _result=_engine.wrap(bbuf,out_buffer); - if (_debug) LOG.debug("{} wrap {}",_session,_result); - if (!_handshook && _result.getHandshakeStatus()==SSLEngineResult.HandshakeStatus.FINISHED) - _handshook=true; - _outNIOBuffer.setPutIndex(out_buffer.position()); - consumed=_result.bytesConsumed(); - } - catch(SSLException e) - { - LOG.warn(getRemoteAddr()+":"+getRemotePort()+" ",e); - if (getChannel().isOpen()) - getChannel().close(); - throw e; - } - finally - { - out_buffer.position(0); - bbuf.position(0); - bbuf.limit(bbuf.capacity()); - - if (consumed>0) - { - int len=consumed0?_result.bytesConsumed():-1; - - default: - LOG.warn("{} wrap default {}",_session,_result); - throw new IOException(_result.toString()); - } - } - - /* ------------------------------------------------------------ */ - @Override - public boolean isBufferingInput() - { - final Buffer in = _inNIOBuffer; - return in!=null && in.hasContent(); - } - - /* ------------------------------------------------------------ */ - @Override - public boolean isBufferingOutput() - { - final NIOBuffer out = _outNIOBuffer; - return out!=null && out.hasContent(); - } - - /* ------------------------------------------------------------ */ - @Override - public boolean isBufferred() - { - return true; - } - - /* ------------------------------------------------------------ */ - public SSLEngine getSSLEngine() - { - return _engine; - } - - /* ------------------------------------------------------------ */ - @Override - public String toString() - { - final NIOBuffer i=_inNIOBuffer; - final NIOBuffer o=_outNIOBuffer; - return "SSL"+super.toString()+","+(_engine==null?"-":_engine.getHandshakeStatus())+", in/out="+ - (i==null?0:i.length())+"/"+(o==null?0:o.length())+ - " bi/o="+isBufferingInput()+"/"+isBufferingOutput()+ - " "+_result; - } -} diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/EndPointTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/EndPointTest.java index 75922f50437..51be3cd751f 100644 --- a/jetty-io/src/test/java/org/eclipse/jetty/io/EndPointTest.java +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/EndPointTest.java @@ -1,62 +1,157 @@ package org.eclipse.jetty.io; -import java.net.ServerSocket; -import java.net.Socket; -import java.util.concurrent.Exchanger; -import java.util.concurrent.TimeUnit; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; -import org.eclipse.jetty.io.bio.SocketEndPoint; import org.eclipse.jetty.io.nio.IndirectNIOBuffer; -import org.junit.Assert; import org.junit.Test; -public class EndPointTest +public abstract class EndPointTest { + public static class EndPointPair + { + public T client; + public T server; + } + + protected abstract EndPointPair newConnection() throws Exception; + + @Test - public void testSocketEndPoints() throws Exception + public void testClientServerExchange() throws Exception { - final ServerSocket server = new ServerSocket(); - server.bind(null); + EndPointPair c = newConnection(); + Buffer buffer = new IndirectNIOBuffer(4096); - final Exchanger accepted = new Exchanger(); - new Thread(){ - public void run() - { - try - { - accepted.exchange(server.accept()); - } - catch(Exception e) - { - e.printStackTrace(); - } - } - }.start(); + // Client sends a request + c.client.flush(new ByteArrayBuffer("request")); - Socket s0 = new Socket(server.getInetAddress(),server.getLocalPort()); - Socket s1 = accepted.exchange(null,5,TimeUnit.SECONDS); + // Server receives the request + int len = c.server.fill(buffer); + assertEquals(7,len); + assertEquals("request",buffer.toString()); + + // Client and server are open + assertTrue(c.client.isOpen()); + assertFalse(c.client.isInputShutdown()); + assertFalse(c.client.isOutputShutdown()); + assertTrue(c.server.isOpen()); + assertFalse(c.server.isInputShutdown()); + assertFalse(c.server.isOutputShutdown()); - SocketEndPoint in = new SocketEndPoint(s0); - SocketEndPoint out = new SocketEndPoint(s1); + // Server sends response and closes output + c.server.flush(new ByteArrayBuffer("response")); + c.server.shutdownOutput(); + + // client server are open, server is oshut + assertTrue(c.client.isOpen()); + assertFalse(c.client.isInputShutdown()); + assertFalse(c.client.isOutputShutdown()); + assertTrue(c.server.isOpen()); + assertFalse(c.server.isInputShutdown()); + assertTrue(c.server.isOutputShutdown()); + + // Client reads response + buffer.clear(); + len = c.client.fill(buffer); + assertEquals(8,len); + assertEquals("response",buffer.toString()); + + // Client and server are open, server is oshut + assertTrue(c.client.isOpen()); + assertFalse(c.client.isInputShutdown()); + assertFalse(c.client.isOutputShutdown()); + assertTrue(c.server.isOpen()); + assertFalse(c.server.isInputShutdown()); + assertTrue(c.server.isOutputShutdown()); + + // Client reads -1 + buffer.clear(); + len = c.client.fill(buffer); + assertEquals(-1,len); + + // Client and server are open, server is oshut, client is ishut + assertTrue(c.client.isOpen()); + assertTrue(c.client.isInputShutdown()); + assertFalse(c.client.isOutputShutdown()); + assertTrue(c.server.isOpen()); + assertFalse(c.server.isInputShutdown()); + assertTrue(c.server.isOutputShutdown()); + + // Client shutsdown output, which is a close because already ishut + c.client.shutdownOutput(); + + // Client is closed. Server is open and oshut + assertFalse(c.client.isOpen()); + assertTrue(c.client.isInputShutdown()); + assertTrue(c.client.isOutputShutdown()); + assertTrue(c.server.isOpen()); + assertFalse(c.server.isInputShutdown()); + assertTrue(c.server.isOutputShutdown()); + + // Server reads close + buffer.clear(); + len = c.server.fill(buffer); + assertEquals(-1,len); + + // Client and Server are closed + assertFalse(c.client.isOpen()); + assertTrue(c.client.isInputShutdown()); + assertTrue(c.client.isOutputShutdown()); + assertFalse(c.server.isOpen()); + assertTrue(c.server.isInputShutdown()); + assertTrue(c.server.isOutputShutdown()); - check(in,out); } - - private void check(EndPoint in, EndPoint out) throws Exception + + + @Test + public void testClientClose() throws Exception { - String data="Now is the time for all good men to come to the aid of the party"; - Buffer send = new ByteArrayBuffer(data); - Buffer receive = new IndirectNIOBuffer(4096); + EndPointPair c = newConnection(); + Buffer buffer = new IndirectNIOBuffer(4096); - int lo=out.flush(send); - int li=in.fill(receive); + c.client.flush(new ByteArrayBuffer("request")); + int len = c.server.fill(buffer); + assertEquals(7,len); + assertEquals("request",buffer.toString()); + + assertTrue(c.client.isOpen()); + assertFalse(c.client.isInputShutdown()); + assertFalse(c.client.isOutputShutdown()); + assertTrue(c.server.isOpen()); + assertFalse(c.server.isInputShutdown()); + assertFalse(c.server.isOutputShutdown()); - Assert.assertEquals(data.length(),lo); - Assert.assertEquals(data.length(),li); - Assert.assertEquals(data,receive.toString()); + c.client.close(); + + assertFalse(c.client.isOpen()); + assertTrue(c.client.isInputShutdown()); + assertTrue(c.client.isOutputShutdown()); + assertTrue(c.server.isOpen()); + assertFalse(c.server.isInputShutdown()); + assertFalse(c.server.isOutputShutdown()); - in.close(); - out.close(); - } + len = c.server.fill(buffer); + assertEquals(-1,len); + + assertFalse(c.client.isOpen()); + assertTrue(c.client.isInputShutdown()); + assertTrue(c.client.isOutputShutdown()); + assertTrue(c.server.isOpen()); + assertTrue(c.server.isInputShutdown()); + assertFalse(c.server.isOutputShutdown()); + + c.server.shutdownOutput(); + + assertFalse(c.client.isOpen()); + assertTrue(c.client.isInputShutdown()); + assertTrue(c.client.isOutputShutdown()); + assertFalse(c.server.isOpen()); + assertTrue(c.server.isInputShutdown()); + assertTrue(c.server.isOutputShutdown()); + } + } diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/IOTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/IOTest.java index 1ca3b75fdfc..8575ab7f229 100644 --- a/jetty-io/src/test/java/org/eclipse/jetty/io/IOTest.java +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/IOTest.java @@ -14,15 +14,17 @@ package org.eclipse.jetty.io; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.io.Reader; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; import org.eclipse.jetty.util.IO; import org.junit.Test; @@ -50,14 +52,12 @@ public class IOTest } @Test - public void testHalfCloses() throws Exception + public void testHalfClose() throws Exception { ServerSocket connector = new ServerSocket(0); Socket client = new Socket("localhost",connector.getLocalPort()); - System.err.println(client); Socket server = connector.accept(); - System.err.println(server); // we can write both ways client.getOutputStream().write(1); @@ -76,7 +76,7 @@ public class IOTest assertEquals(-1,server.getInputStream().read()); // but cannot write - try { client.getOutputStream().write(1); assertTrue(false); } catch (SocketException e) {} + try { client.getOutputStream().write(1); fail("exception expected"); } catch (SocketException e) {} // but can still write in opposite direction. server.getOutputStream().write(1); @@ -87,7 +87,7 @@ public class IOTest server.shutdownInput(); // now we EOF instead of reading -1 - try { server.getInputStream().read(); assertTrue(false); } catch (SocketException e) {} + try { server.getInputStream().read(); fail("exception expected"); } catch (SocketException e) {} // but can still write in opposite direction. @@ -98,7 +98,7 @@ public class IOTest client.shutdownInput(); // now we EOF instead of reading -1 - try { client.getInputStream().read(); assertTrue(false); } catch (SocketException e) {} + try { client.getInputStream().read(); fail("exception expected"); } catch (SocketException e) {} // But we can still write at the server (data which will never be read) server.getOutputStream().write(1); @@ -110,7 +110,7 @@ public class IOTest server.shutdownOutput(); // and now we can't write - try { server.getOutputStream().write(1); assertTrue(false); } catch (SocketException e) {} + try { server.getOutputStream().write(1); fail("exception expected"); } catch (SocketException e) {} // but the sockets are still open assertFalse(client.isClosed()); @@ -128,8 +128,114 @@ public class IOTest // which has to be closed explictly server.close(); assertTrue(server.isClosed()); - - - + } + + + @Test + public void testHalfCloseClientServer() throws Exception + { + ServerSocketChannel connector = ServerSocketChannel.open(); + connector.socket().bind(null); + + Socket client = SocketChannel.open(connector.socket().getLocalSocketAddress()).socket(); + client.setSoTimeout(1000); + client.setSoLinger(false,-1); + Socket server = connector.accept().socket(); + server.setSoTimeout(1000); + server.setSoLinger(false,-1); + + // Write from client to server + client.getOutputStream().write(1); + + // Server reads + assertEquals(1,server.getInputStream().read()); + + // Write from server to client with oshut + server.getOutputStream().write(1); + System.err.println("OSHUT "+server); + server.shutdownOutput(); + + // Client reads response + assertEquals(1,client.getInputStream().read()); + + try + { + // Client reads -1 and does ishut + assertEquals(-1,client.getInputStream().read()); + assertFalse(client.isInputShutdown()); + System.err.println("ISHUT "+client); + client.shutdownInput(); + + // Client ??? + System.err.println("OSHUT "+client); + client.shutdownOutput(); + System.err.println("CLOSE "+client); + client.close(); + + // Server reads -1, does ishut and then close + assertEquals(-1,server.getInputStream().read()); + assertFalse(server.isInputShutdown()); + System.err.println("ISHUT "+server); + + try + { + server.shutdownInput(); + } + catch(SocketException e) + { + System.err.println(e); + } + System.err.println("CLOSE "+server); + server.close(); + + } + catch(Exception e) + { + // Dang OSX! + System.err.println(e); + } + } + + @Test + public void testReset() throws Exception + { + ServerSocket connector; + Socket client; + Socket server; + + connector = new ServerSocket(9123); + client = new Socket("127.0.0.1",connector.getLocalPort()); + server = connector.accept(); + client.setTcpNoDelay(true); + client.setSoLinger(true,0); + server.setTcpNoDelay(true); + server.setSoLinger(true,0); + + client.getOutputStream().write(1); + assertEquals(1,server.getInputStream().read()); + server.getOutputStream().write(1); + assertEquals(1,client.getInputStream().read()); + + // Server generator shutdowns output after non persistent sending response. + server.shutdownOutput(); + + // client endpoint reads EOF and shutdown input as result + assertEquals(-1,client.getInputStream().read()); + client.shutdownInput(); + + // client connection see's EOF and shutsdown output as no more requests to be sent. + client.shutdownOutput(); + + // Since input already shutdown, client also closes socket. + client.close(); + + // Server reads the EOF from client oshut and shut's down it's input + assertEquals(-1,server.getInputStream().read()); + server.shutdownInput(); + + // Since output was already shutdown, server closes + server.close(); + } + } diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/bio/SocketEndPointTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/bio/SocketEndPointTest.java new file mode 100644 index 00000000000..277c6c7ccba --- /dev/null +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/bio/SocketEndPointTest.java @@ -0,0 +1,38 @@ +package org.eclipse.jetty.io.bio; + +import java.net.ServerSocket; +import java.net.Socket; + +import org.eclipse.jetty.io.EndPointTest; +import org.junit.AfterClass; +import org.junit.BeforeClass; + +public class SocketEndPointTest extends EndPointTest +{ + static ServerSocket connector; + + @BeforeClass + public static void open() throws Exception + { + connector = new ServerSocket(); + connector.bind(null); + } + + @AfterClass + public static void close() throws Exception + { + connector.close(); + connector=null; + } + + @Override + protected EndPointPair newConnection() throws Exception + { + EndPointPair c = new EndPointPair(); + c.client=new SocketEndPoint(new Socket(connector.getInetAddress(),connector.getLocalPort())); + c.server=new SocketEndPoint(connector.accept()); + return c; + } + + +} diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/nio/ChannelEndPointTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/nio/ChannelEndPointTest.java new file mode 100644 index 00000000000..1d8d5b2c7eb --- /dev/null +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/nio/ChannelEndPointTest.java @@ -0,0 +1,43 @@ +package org.eclipse.jetty.io.nio; + +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; + +import org.eclipse.jetty.io.EndPointTest; +import org.junit.AfterClass; +import org.junit.BeforeClass; + +public class ChannelEndPointTest extends EndPointTest +{ + static ServerSocketChannel connector; + + @BeforeClass + public static void open() throws Exception + { + connector = ServerSocketChannel.open(); + connector.socket().bind(null); + } + + @AfterClass + public static void close() throws Exception + { + connector.close(); + connector=null; + } + + @Override + protected EndPointPair newConnection() throws Exception + { + EndPointPair c = new EndPointPair(); + + c.client=new ChannelEndPoint(SocketChannel.open(connector.socket().getLocalSocketAddress())); + c.server=new ChannelEndPoint(connector.accept()); + return c; + } + + @Override + public void testClientServerExchange() throws Exception + { + super.testClientServerExchange(); + } +} diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/nio/NIOTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/nio/NIOTest.java new file mode 100644 index 00000000000..9366d8ce843 --- /dev/null +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/nio/NIOTest.java @@ -0,0 +1,131 @@ +// ======================================================================== +// Copyright (c) 2004-2009 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.io.nio; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import java.net.ServerSocket; +import java.net.Socket; +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.SocketChannel; + +import org.junit.Test; + +/** + * + */ +public class NIOTest +{ + @Test + public void testSelector() throws Exception + { + ServerSocket acceptor = new ServerSocket(0); + + Selector selector = Selector.open(); + + // Create client server socket pair + SocketChannel client = SocketChannel.open(acceptor.getLocalSocketAddress()); + Socket server = acceptor.accept(); + server.setTcpNoDelay(true); + + // Make the client non blocking and register it with selector for reads + client.configureBlocking(false); + SelectionKey key = client.register(selector,SelectionKey.OP_READ); + + // assert it is not selected + assertTrue(key.isValid()); + assertFalse(key.isReadable()); + assertEquals(0,key.readyOps()); + + // try selecting and assert nothing selected + int selected = selector.selectNow(); + assertEquals(0,selected); + assertEquals(0,selector.selectedKeys().size()); + assertTrue(key.isValid()); + assertFalse(key.isReadable()); + assertEquals(0,key.readyOps()); + + // Write a byte from server to client + server.getOutputStream().write(42); + server.getOutputStream().flush(); + + // select again and assert selection found for read + selected = selector.select(1000); + assertEquals(1,selected); + assertEquals(1,selector.selectedKeys().size()); + assertTrue(key.isValid()); + assertTrue(key.isReadable()); + assertEquals(1,key.readyOps()); + + // select again and see that it is not reselect, but stays selected + selected = selector.select(100); + assertEquals(0,selected); + assertEquals(1,selector.selectedKeys().size()); + assertTrue(key.isValid()); + assertTrue(key.isReadable()); + assertEquals(1,key.readyOps()); + + // read the byte + ByteBuffer buf = ByteBuffer.allocate(1024); + int len=client.read(buf); + assertEquals(1,len); + buf.flip(); + assertEquals(42,buf.get()); + buf.clear(); + + // But this does not change the key + assertTrue(key.isValid()); + assertTrue(key.isReadable()); + assertEquals(1,key.readyOps()); + + // Even if we select again ? + selected = selector.select(100); + assertEquals(0,selected); + assertEquals(1,selector.selectedKeys().size()); + assertTrue(key.isValid()); + assertTrue(key.isReadable()); + assertEquals(1,key.readyOps()); + + // Unless we remove the key from the select set + // and then it is still flagged as isReadable() + selector.selectedKeys().clear(); + assertEquals(0,selector.selectedKeys().size()); + assertTrue(key.isValid()); + assertTrue(key.isReadable()); + assertEquals(1,key.readyOps()); + + // Now if we select again - it is still flagged as readable!!! + selected = selector.select(100); + assertEquals(0,selected); + assertEquals(0,selector.selectedKeys().size()); + assertTrue(key.isValid()); + assertTrue(key.isReadable()); + assertEquals(1,key.readyOps()); + + // Only when it is selected for something else does that state change. + key.interestOps(SelectionKey.OP_READ|SelectionKey.OP_WRITE); + selected = selector.select(1000); + assertEquals(1,selected); + assertEquals(1,selector.selectedKeys().size()); + assertTrue(key.isValid()); + assertTrue(key.isWritable()); + assertFalse(key.isReadable()); + assertEquals(SelectionKey.OP_WRITE,key.readyOps()); + } + +} diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/nio/SelectChannelEndPointSslTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/nio/SelectChannelEndPointSslTest.java new file mode 100644 index 00000000000..d6a6a46a06a --- /dev/null +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/nio/SelectChannelEndPointSslTest.java @@ -0,0 +1,187 @@ +package org.eclipse.jetty.io.nio; + +import java.io.File; +import java.io.IOException; +import java.net.Socket; +import java.nio.ByteBuffer; +import java.nio.channels.SocketChannel; + +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLEngineResult; +import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import javax.net.ssl.SSLSocket; + +import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + + +public class SelectChannelEndPointSslTest extends SelectChannelEndPointTest +{ + static SslContextFactory __sslCtxFactory=new SslContextFactory(); + + @BeforeClass + public static void initSslEngine() throws Exception + { + File keystore = MavenTestingUtils.getTestResourceFile("keystore"); + __sslCtxFactory.setKeyStorePath(keystore.getAbsolutePath()); + __sslCtxFactory.setKeyStorePassword("storepwd"); + __sslCtxFactory.setKeyManagerPassword("keypwd"); + __sslCtxFactory.start(); + } + + @Override + protected Socket newClient() throws IOException + { + SSLSocket socket = __sslCtxFactory.newSslSocket(); + socket.connect(_connector.socket().getLocalSocketAddress()); + return socket; + } + + @Override + protected AsyncConnection newConnection(SocketChannel channel, EndPoint endpoint) + { + SSLEngine engine = __sslCtxFactory.newSslEngine(); + engine.setUseClientMode(false); + SslConnection connection = new SslConnection(engine,endpoint); + + AsyncConnection delegate = super.newConnection(channel,connection.getSslEndPoint()); + connection.getSslEndPoint().setConnection(delegate); + return connection; + } + + @Test + @Override + public void testEcho() throws Exception + { + super.testEcho(); + } + + + @Test + @Override + public void testShutdown() throws Exception + { + // SSL does not do half closes + } + + @Test + public void testTcpClose() throws Exception + { + + // This test replaces SSLSocket() with a very manual SSL client + // so we can close TCP underneath SSL. + + SocketChannel client = SocketChannel.open(_connector.socket().getLocalSocketAddress()); + client.socket().setSoTimeout(500); + + SocketChannel server = _connector.accept(); + server.configureBlocking(false); + _manager.register(server); + + SSLEngine engine = __sslCtxFactory.newSslEngine(); + engine.setUseClientMode(true); + engine.beginHandshake(); + + ByteBuffer appOut = ByteBuffer.allocate(engine.getSession().getApplicationBufferSize()); + ByteBuffer sslOut = ByteBuffer.allocate(engine.getSession().getPacketBufferSize()*2); + ByteBuffer appIn = ByteBuffer.allocate(engine.getSession().getApplicationBufferSize()); + ByteBuffer sslIn = ByteBuffer.allocate(engine.getSession().getPacketBufferSize()*2); + + boolean debug=SslConnection.LOG.isDebugEnabled(); + + if (debug) System.err.println(engine.getHandshakeStatus()); + int loop=20; + while (engine.getHandshakeStatus()!=HandshakeStatus.NOT_HANDSHAKING) + { + if (--loop==0) + throw new IllegalStateException(); + + if (engine.getHandshakeStatus()==HandshakeStatus.NEED_WRAP) + { + if (debug) System.err.printf("sslOut %d-%d-%d%n",sslOut.position(),sslOut.limit(),sslOut.capacity()); + if (debug) System.err.printf("appOut %d-%d-%d%n",appOut.position(),appOut.limit(),appOut.capacity()); + SSLEngineResult result =engine.wrap(appOut,sslOut); + if (debug) System.err.println(result); + sslOut.flip(); + int flushed=client.write(sslOut); + if (debug) System.err.println("out="+flushed); + sslOut.clear(); + } + + if (engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP) + { + if (debug) System.err.printf("sslIn %d-%d-%d%n",sslIn.position(),sslIn.limit(),sslIn.capacity()); + if (sslIn.position()==0) + { + int filled=client.read(sslIn); + if (debug) System.err.println("in="+filled); + } + sslIn.flip(); + if (debug) System.err.printf("sslIn %d-%d-%d%n",sslIn.position(),sslIn.limit(),sslIn.capacity()); + SSLEngineResult result =engine.unwrap(sslIn,appIn); + if (debug) System.err.println(result); + if (debug) System.err.printf("sslIn %d-%d-%d%n",sslIn.position(),sslIn.limit(),sslIn.capacity()); + if (sslIn.hasRemaining()) + sslIn.compact(); + else + sslIn.clear(); + if (debug) System.err.printf("sslIn %d-%d-%d%n",sslIn.position(),sslIn.limit(),sslIn.capacity()); + } + + if (engine.getHandshakeStatus()==HandshakeStatus.NEED_TASK) + { + Runnable task; + while ((task=engine.getDelegatedTask())!=null) + task.run(); + if (debug) System.err.println(engine.getHandshakeStatus()); + } + } + + if (debug) System.err.println("\nSay Hello"); + + // write a message + appOut.put("HelloWorld".getBytes("UTF-8")); + appOut.flip(); + SSLEngineResult result =engine.wrap(appOut,sslOut); + if (debug) System.err.println(result); + sslOut.flip(); + int flushed=client.write(sslOut); + if (debug) System.err.println("out="+flushed); + sslOut.clear(); + appOut.clear(); + + // read the response + int filled=client.read(sslIn); + if (debug) System.err.println("in="+filled); + sslIn.flip(); + result =engine.unwrap(sslIn,appIn); + if (debug) System.err.println(result); + if (sslIn.hasRemaining()) + sslIn.compact(); + else + sslIn.clear(); + + appIn.flip(); + String reply= new String(appIn.array(),appIn.arrayOffset(),appIn.remaining()); + appIn.clear(); + + Assert.assertEquals("HelloWorld",reply); + + SelectorManager.LOG.info("javax.net.ssl.SSLException: Inbound closed... is expected soon"); + if (debug) System.err.println("\nSudden Death"); + client.socket().shutdownOutput(); + + filled=client.read(sslIn); + Assert.assertEquals(-1,filled); + } + + @Test + public void testStress() throws Exception + { + super.testStress(); + } +} diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/nio/SelectChannelEndPointTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/nio/SelectChannelEndPointTest.java new file mode 100644 index 00000000000..768f872258e --- /dev/null +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/nio/SelectChannelEndPointTest.java @@ -0,0 +1,434 @@ +package org.eclipse.jetty.io.nio; + +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.net.SocketTimeoutException; +import java.nio.channels.SelectionKey; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.io.AbstractConnection; +import org.eclipse.jetty.io.AsyncEndPoint; +import org.eclipse.jetty.io.ConnectedEndPoint; +import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class SelectChannelEndPointTest +{ + protected SelectChannelEndPoint _lastEndp; + protected ServerSocketChannel _connector; + protected QueuedThreadPool _threadPool = new QueuedThreadPool(); + protected SelectorManager _manager = new SelectorManager() + { + @Override + public boolean dispatch(Runnable task) + { + return _threadPool.dispatch(task); + } + + @Override + protected void endPointClosed(SelectChannelEndPoint endpoint) + { + } + + @Override + protected void endPointOpened(SelectChannelEndPoint endpoint) + { + } + + @Override + protected void endPointUpgraded(ConnectedEndPoint endpoint, org.eclipse.jetty.io.Connection oldConnection) + { + } + + @Override + public AsyncConnection newConnection(SocketChannel channel, AsyncEndPoint endpoint, Object attachment) + { + return SelectChannelEndPointTest.this.newConnection(channel,endpoint); + } + + @Override + protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey key) throws IOException + { + SelectChannelEndPoint endp = new SelectChannelEndPoint(channel,selectSet,key,2000); + endp.setConnection(selectSet.getManager().newConnection(channel,endp, key.attachment())); + _lastEndp=endp; + return endp; + } + }; + + // Must be volatile or the test may fail spuriously + private volatile int _blockAt=0; + + @Before + public void startManager() throws Exception + { + _connector = ServerSocketChannel.open(); + _connector.socket().bind(null); + _threadPool.start(); + _manager.start(); + } + + @After + public void stopManager() throws Exception + { + _manager.stop(); + _threadPool.stop(); + _connector.close(); + } + + protected Socket newClient() throws IOException + { + return new Socket(_connector.socket().getInetAddress(),_connector.socket().getLocalPort()); + } + + protected AsyncConnection newConnection(SocketChannel channel, EndPoint endpoint) + { + return new TestConnection(endpoint); + } + + public class TestConnection extends AbstractConnection implements AsyncConnection + { + NIOBuffer _in = new IndirectNIOBuffer(32*1024); + NIOBuffer _out = new IndirectNIOBuffer(32*1024); + + public TestConnection(EndPoint endp) + { + super(endp); + } + + public org.eclipse.jetty.io.Connection handle() throws IOException + { + boolean progress=true; + while(progress) + { + progress=false; + _in.compact(); + if (_in.space()>0 && _endp.fill(_in)>0) + progress=true; + + while (_blockAt>0 && _in.length()>0 && _in.length()<_blockAt) + { + _endp.blockReadable(10000); + if (_in.space()>0 && _endp.fill(_in)>0) + progress=true; + } + + if (_in.hasContent() && _in.skip(_out.put(_in))>0) + progress=true; + + if (_out.hasContent() && _endp.flush(_out)>0) + progress=true; + + _out.compact(); + + if (!_out.hasContent() && _endp.isInputShutdown()) + _endp.shutdownOutput(); + } + return this; + } + + public boolean isIdle() + { + return false; + } + + public boolean isSuspended() + { + return false; + } + + public void onClose() + { + // System.err.println("onClose"); + } + + public void onInputShutdown() throws IOException + { + // System.err.println("onInputShutdown"); + } + + } + + @Test + public void testEcho() throws Exception + { + Socket client = newClient(); + + client.setSoTimeout(500); + + SocketChannel server = _connector.accept(); + server.configureBlocking(false); + + _manager.register(server); + + // Write client to server + client.getOutputStream().write("HelloWorld".getBytes("UTF-8")); + + // Verify echo server to client + for (char c : "HelloWorld".toCharArray()) + { + int b = client.getInputStream().read(); + assertTrue(b>0); + assertEquals(c,(char)b); + } + + // wait for read timeout + long start=System.currentTimeMillis(); + try + { + client.getInputStream().read(); + Assert.fail(); + } + catch(SocketTimeoutException e) + { + assertTrue(System.currentTimeMillis()-start>=400); + } + + // write then shutdown + client.getOutputStream().write("Goodbye Cruel TLS".getBytes("UTF-8")); + + // Verify echo server to client + for (char c : "Goodbye Cruel TLS".toCharArray()) + { + int b = client.getInputStream().read(); + assertTrue(b>0); + assertEquals(c,(char)b); + } + client.close(); + + int i=0; + while (server.isOpen()) + { + assert(i++<10); + Thread.sleep(10); + } + + } + + + @Test + public void testShutdown() throws Exception + { + Socket client = newClient(); + + client.setSoTimeout(500); + + SocketChannel server = _connector.accept(); + server.configureBlocking(false); + + _manager.register(server); + + // Write client to server + client.getOutputStream().write("HelloWorld".getBytes("UTF-8")); + + // Verify echo server to client + for (char c : "HelloWorld".toCharArray()) + { + int b = client.getInputStream().read(); + assertTrue(b>0); + assertEquals(c,(char)b); + } + + // wait for read timeout + long start=System.currentTimeMillis(); + try + { + client.getInputStream().read(); + Assert.fail(); + } + catch(SocketTimeoutException e) + { + assertTrue(System.currentTimeMillis()-start>=400); + } + + // write then shutdown + client.getOutputStream().write("Goodbye Cruel TLS".getBytes("UTF-8")); + client.shutdownOutput(); + + + // Verify echo server to client + for (char c : "Goodbye Cruel TLS".toCharArray()) + { + int b = client.getInputStream().read(); + assertTrue(b>0); + assertEquals(c,(char)b); + } + + // Read close + assertEquals(-1,client.getInputStream().read()); + + } + + + + @Test + public void testBlockIn() throws Exception + { + Socket client = newClient(); + + SocketChannel server = _connector.accept(); + server.configureBlocking(false); + + _manager.register(server); + + OutputStream clientOutputStream = client.getOutputStream(); + InputStream clientInputStream = client.getInputStream(); + + int specifiedTimeout = 400; + client.setSoTimeout(specifiedTimeout); + + // Write 8 and cause block for 10 + _blockAt=10; + clientOutputStream.write("12345678".getBytes("UTF-8")); + clientOutputStream.flush(); + + Thread.sleep(2 * specifiedTimeout); + + // No echo as blocking for 10 + long start=System.currentTimeMillis(); + try + { + int b = clientInputStream.read(); + Assert.fail("Should have timed out waiting for a response, but read "+b); + } + catch(SocketTimeoutException e) + { + int elapsed = Long.valueOf(System.currentTimeMillis() - start).intValue(); + System.err.println("blocked for " + elapsed+ "ms"); + Assert.assertThat("Expected timeout", elapsed, greaterThanOrEqualTo(3*specifiedTimeout/4)); + } + + // write remaining characters + clientOutputStream.write("90ABCDEF".getBytes("UTF-8")); + clientOutputStream.flush(); + + // Verify echo server to client + for (char c : "1234567890ABCDEF".toCharArray()) + { + int b = clientInputStream.read(); + assertTrue(b>0); + assertEquals(c,(char)b); + } + } + + @Test + public void testIdle() throws Exception + { + Socket client = newClient(); + + client.setSoTimeout(3000); + + SocketChannel server = _connector.accept(); + server.configureBlocking(false); + + _manager.register(server); + + // Write client to server + client.getOutputStream().write("HelloWorld".getBytes("UTF-8")); + + // Verify echo server to client + for (char c : "HelloWorld".toCharArray()) + { + int b = client.getInputStream().read(); + assertTrue(b>0); + assertEquals(c,(char)b); + } + + // Set Max idle + _lastEndp.setMaxIdleTime(500); + + // read until idle shutdown received + long start=System.currentTimeMillis(); + int b=client.getInputStream().read(); + assertEquals(-1,b); + long idle=System.currentTimeMillis()-start; + assertTrue(idle>400); + assertTrue(idle<2000); + + // But endpoint is still open. + assertTrue(_lastEndp.isOpen()); + + + // Wait for another idle callback + Thread.sleep(2000); + // endpoint is closed. + + assertFalse(_lastEndp.isOpen()); + + } + + + + @Test + public void testStress() throws Exception + { + Socket client = newClient(); + client.setSoTimeout(30000); + + SocketChannel server = _connector.accept(); + server.configureBlocking(false); + + _manager.register(server); + int writes = 100000; + + final byte[] bytes="HelloWorld".getBytes("UTF-8"); + final CountDownLatch latch = new CountDownLatch(writes); + final InputStream in = new BufferedInputStream(client.getInputStream()); + final long start = System.currentTimeMillis(); + client.getOutputStream().write(bytes); + client.getOutputStream().flush(); + + new Thread() + { + public void run() + { + try + { + while (latch.getCount()>0) + { + // Verify echo server to client + for (byte b0 : bytes) + { + int b = in.read(); + assertTrue(b>0); + assertEquals(0xff&b0,b); + } + latch.countDown(); + } + } + catch(Throwable e) + { + System.err.println("latch="+latch.getCount()); + System.err.println("time="+(System.currentTimeMillis()-start)); + e.printStackTrace(); + } + } + }.start(); + + + // Write client to server + for (int i=1;i org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 jetty-jaspi diff --git a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/callback/CredentialValidationCallback.java b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/callback/CredentialValidationCallback.java index e22aaa32e60..bf2c82c8c33 100644 --- a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/callback/CredentialValidationCallback.java +++ b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/callback/CredentialValidationCallback.java @@ -17,7 +17,7 @@ package org.eclipse.jetty.security.jaspi.callback; import javax.security.auth.Subject; import javax.security.auth.callback.Callback; -import org.eclipse.jetty.http.security.Credential; +import org.eclipse.jetty.util.security.Credential; /** * CredentialValidationCallback diff --git a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BaseAuthModule.java b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BaseAuthModule.java index 7099661a4da..4eb1ad6e256 100644 --- a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BaseAuthModule.java +++ b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BaseAuthModule.java @@ -32,8 +32,8 @@ import javax.security.auth.message.module.ServerAuthModule; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.eclipse.jetty.http.security.Credential; -import org.eclipse.jetty.http.security.Password; +import org.eclipse.jetty.util.security.Credential; +import org.eclipse.jetty.util.security.Password; import org.eclipse.jetty.security.authentication.LoginCallbackImpl; import org.eclipse.jetty.security.jaspi.JaspiMessageInfo; import org.eclipse.jetty.security.jaspi.callback.CredentialValidationCallback; diff --git a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BasicAuthModule.java b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BasicAuthModule.java index d97c5df7152..50032d46b26 100644 --- a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BasicAuthModule.java +++ b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BasicAuthModule.java @@ -27,7 +27,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.http.HttpHeaders; -import org.eclipse.jetty.http.security.Constraint; +import org.eclipse.jetty.util.security.Constraint; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; diff --git a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/ClientCertAuthModule.java b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/ClientCertAuthModule.java index 94300672200..5e637c4dedf 100644 --- a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/ClientCertAuthModule.java +++ b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/ClientCertAuthModule.java @@ -25,8 +25,8 @@ import javax.security.auth.message.MessageInfo; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.eclipse.jetty.http.security.Constraint; -import org.eclipse.jetty.http.security.Password; +import org.eclipse.jetty.util.security.Constraint; +import org.eclipse.jetty.util.security.Password; import org.eclipse.jetty.util.B64Code; /** diff --git a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/DigestAuthModule.java b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/DigestAuthModule.java index 5702ee4892b..847b01b1c63 100644 --- a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/DigestAuthModule.java +++ b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/DigestAuthModule.java @@ -28,8 +28,8 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.http.HttpHeaders; -import org.eclipse.jetty.http.security.Constraint; -import org.eclipse.jetty.http.security.Credential; +import org.eclipse.jetty.util.security.Constraint; +import org.eclipse.jetty.util.security.Credential; import org.eclipse.jetty.util.B64Code; import org.eclipse.jetty.util.QuotedStringTokenizer; import org.eclipse.jetty.util.StringUtil; diff --git a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/FormAuthModule.java b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/FormAuthModule.java index 72fc9b7f55f..be390555742 100644 --- a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/FormAuthModule.java +++ b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/FormAuthModule.java @@ -33,8 +33,8 @@ import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; -import org.eclipse.jetty.http.security.Constraint; -import org.eclipse.jetty.http.security.Password; +import org.eclipse.jetty.util.security.Constraint; +import org.eclipse.jetty.util.security.Password; import org.eclipse.jetty.security.CrossContextPsuedoSession; import org.eclipse.jetty.security.authentication.DeferredAuthentication; import org.eclipse.jetty.security.authentication.LoginCallbackImpl; diff --git a/jetty-jmx/pom.xml b/jetty-jmx/pom.xml index 3b3f0b177b1..4b9a0dc2a9d 100644 --- a/jetty-jmx/pom.xml +++ b/jetty-jmx/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 jetty-jmx diff --git a/jetty-jmx/src/main/config/etc/jetty-jmx.xml b/jetty-jmx/src/main/config/etc/jetty-jmx.xml index bdce9d42981..b8c38a201d6 100644 --- a/jetty-jmx/src/main/config/etc/jetty-jmx.xml +++ b/jetty-jmx/src/main/config/etc/jetty-jmx.xml @@ -9,6 +9,17 @@ + + + + + + diff --git a/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/MBeanContainer.java b/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/MBeanContainer.java index 5371c8b60dc..d784442d9f4 100644 --- a/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/MBeanContainer.java +++ b/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/MBeanContainer.java @@ -22,11 +22,11 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.WeakHashMap; + import javax.management.MBeanServer; import javax.management.ObjectInstance; import javax.management.ObjectName; -import org.eclipse.jetty.util.MultiMap; import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.eclipse.jetty.util.component.AggregateLifeCycle; import org.eclipse.jetty.util.component.Container; @@ -41,7 +41,7 @@ import org.eclipse.jetty.util.thread.ShutdownThread; */ public class MBeanContainer extends AbstractLifeCycle implements Container.Listener, Dumpable { - private final static Logger __log = Log.getLogger(MBeanContainer.class.getName()); + private final static Logger LOG = Log.getLogger(MBeanContainer.class.getName()); private final MBeanServer _server; private final WeakHashMap _beans = new WeakHashMap(); @@ -93,7 +93,7 @@ public class MBeanContainer extends AbstractLifeCycle implements Container.Liste } catch (Exception e) { - __log.ignore(e); + LOG.ignore(e); } } @@ -134,7 +134,7 @@ public class MBeanContainer extends AbstractLifeCycle implements Container.Liste */ public synchronized void add(Relationship relationship) { - __log.debug("add {}",relationship); + LOG.debug("add {}",relationship); ObjectName parent = _beans.get(relationship.getParent()); if (parent == null) { @@ -168,7 +168,7 @@ public class MBeanContainer extends AbstractLifeCycle implements Container.Liste */ public synchronized void remove(Relationship relationship) { - __log.debug("remove {}",relationship); + LOG.debug("remove {}",relationship); ObjectName parent = _beans.get(relationship.getParent()); ObjectName child = _beans.get(relationship.getChild()); @@ -194,7 +194,7 @@ public class MBeanContainer extends AbstractLifeCycle implements Container.Liste */ public synchronized void removeBean(Object obj) { - __log.debug("removeBean {}",obj); + LOG.debug("removeBean {}",obj); ObjectName bean = _beans.remove(obj); if (bean != null) @@ -202,7 +202,7 @@ public class MBeanContainer extends AbstractLifeCycle implements Container.Liste List beanRelations= _relations.remove(bean); if (beanRelations != null) { - __log.debug("Unregister {}", beanRelations); + LOG.debug("Unregister {}", beanRelations); List removeList = new ArrayList(beanRelations); for (Object r : removeList) { @@ -214,15 +214,15 @@ public class MBeanContainer extends AbstractLifeCycle implements Container.Liste try { _server.unregisterMBean(bean); - __log.debug("Unregistered {}", bean); + LOG.debug("Unregistered {}", bean); } catch (javax.management.InstanceNotFoundException e) { - __log.ignore(e); + LOG.ignore(e); } catch (Exception e) { - __log.warn(e); + LOG.warn(e); } } } @@ -234,7 +234,7 @@ public class MBeanContainer extends AbstractLifeCycle implements Container.Liste */ public synchronized void addBean(Object obj) { - __log.debug("addBean {}",obj); + LOG.debug("addBean {}",obj); try { if (obj == null || _beans.containsKey(obj)) @@ -298,13 +298,13 @@ public class MBeanContainer extends AbstractLifeCycle implements Container.Liste } ObjectInstance oinstance = _server.registerMBean(mbean, oname); - __log.debug("Registered {}", oinstance.getObjectName()); + LOG.debug("Registered {}", oinstance.getObjectName()); _beans.put(obj, oinstance.getObjectName()); } catch (Exception e) { - __log.warn("bean: " + obj, e); + LOG.warn("bean: " + obj, e); } } diff --git a/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/ObjectMBean.java b/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/ObjectMBean.java index 08797aa7264..8b309d5ce67 100644 --- a/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/ObjectMBean.java +++ b/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/ObjectMBean.java @@ -28,8 +28,6 @@ import java.util.Map; import java.util.MissingResourceException; import java.util.ResourceBundle; import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import javax.management.Attribute; import javax.management.AttributeList; diff --git a/jetty-jmx/src/test/java/org/eclipse/jetty/jmx/ConnectorServerTest.java b/jetty-jmx/src/test/java/org/eclipse/jetty/jmx/ConnectorServerTest.java index 10ed7d822ac..1818567d25c 100644 --- a/jetty-jmx/src/test/java/org/eclipse/jetty/jmx/ConnectorServerTest.java +++ b/jetty-jmx/src/test/java/org/eclipse/jetty/jmx/ConnectorServerTest.java @@ -16,8 +16,6 @@ package org.eclipse.jetty.jmx; -import java.net.MalformedURLException; - import javax.management.remote.JMXServiceURL; import org.junit.Test; diff --git a/jetty-jmx/src/test/java/org/eclipse/jetty/jmx/ObjectMBeanTest.java b/jetty-jmx/src/test/java/org/eclipse/jetty/jmx/ObjectMBeanTest.java index 4b1d09f79d8..ebb5f63defb 100644 --- a/jetty-jmx/src/test/java/org/eclipse/jetty/jmx/ObjectMBeanTest.java +++ b/jetty-jmx/src/test/java/org/eclipse/jetty/jmx/ObjectMBeanTest.java @@ -13,10 +13,11 @@ package org.eclipse.jetty.jmx; -import com.acme.Derived; +import static org.junit.Assert.assertTrue; + import org.junit.Test; -import static org.junit.Assert.assertTrue; +import com.acme.Derived; public class ObjectMBeanTest { diff --git a/jetty-jndi/pom.xml b/jetty-jndi/pom.xml index e9b89cdb92c..5eaf6fef291 100644 --- a/jetty-jndi/pom.xml +++ b/jetty-jndi/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 jetty-jndi diff --git a/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/factories/MailSessionReference.java b/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/factories/MailSessionReference.java index e6ff384f548..210b52d2a0d 100644 --- a/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/factories/MailSessionReference.java +++ b/jetty-jndi/src/main/java/org/eclipse/jetty/jndi/factories/MailSessionReference.java @@ -30,7 +30,7 @@ import javax.naming.Reference; import javax.naming.StringRefAddr; import javax.naming.spi.ObjectFactory; -import org.eclipse.jetty.http.security.Password; +import org.eclipse.jetty.util.security.Password; /** * MailSessionReference diff --git a/jetty-jsp-2.1/pom.xml b/jetty-jsp-2.1/pom.xml deleted file mode 100644 index 9f4a419d79f..00000000000 --- a/jetty-jsp-2.1/pom.xml +++ /dev/null @@ -1,129 +0,0 @@ - - - org.eclipse.jetty - jetty-project - 7.5.4-SNAPSHOT - - 4.0.0 - jetty-jsp-2.1 - Jetty :: Jetty JSP Additions - Additions to Jasper implementation from Glassfish - - ${project.groupId}.jsp-2.1 - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - unpack - generate-sources - - unpack - - - - - org.glassfish.web - jsp-impl - 2.1.3-b10 - jar - sources - true - target/generated-sources - **/JDTJavaCompiler.java - - - - - - - - - org.codehaus.mojo - build-helper-maven-plugin - 1.7 - - - add-source - generate-sources - - add-source - - - - target/generated-sources - - - - - - - - org.apache.felix - maven-bundle-plugin - true - - - - manifest - - - - - - - org.apache.jasper;version="[2.1,3)";glassfish="split", - * - - org.apache.jasper*;version="${parsedVersion.osgiVersion}" - org.apache.jasper.glassfish - <_nouses>true - - - - - - org.apache.maven.plugins - maven-jar-plugin - - - ${project.build.outputDirectory}/META-INF/MANIFEST.MF - - - - - org.codehaus.mojo - findbugs-maven-plugin - - org.eclipse.jetty.jsp.* - - - - - - - org.glassfish.web - jsp-impl - 2.1.3-b10 - - - provided - - - javax.servlet - servlet-api - provided - - - org.eclipse.jdt.core.compiler - ecj - 3.7 - provided - - - diff --git a/jetty-monitor/pom.xml b/jetty-monitor/pom.xml index 8298960557d..bfff477df54 100644 --- a/jetty-monitor/pom.xml +++ b/jetty-monitor/pom.xml @@ -19,7 +19,7 @@ org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 jetty-monitor diff --git a/jetty-nested/pom.xml b/jetty-nested/pom.xml index 9a625784fcb..40da79b3cfe 100644 --- a/jetty-nested/pom.xml +++ b/jetty-nested/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT jetty-nested Jetty :: Nested diff --git a/jetty-nested/src/main/java/org/eclipse/jetty/nested/NestedConnection.java b/jetty-nested/src/main/java/org/eclipse/jetty/nested/NestedConnection.java index b377fc6f0bc..0d98785a526 100644 --- a/jetty-nested/src/main/java/org/eclipse/jetty/nested/NestedConnection.java +++ b/jetty-nested/src/main/java/org/eclipse/jetty/nested/NestedConnection.java @@ -15,7 +15,6 @@ package org.eclipse.jetty.nested; import java.io.IOException; import java.util.Enumeration; -import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; @@ -24,14 +23,11 @@ import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpURI; import org.eclipse.jetty.io.Connection; -import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.AbstractHttpConnection; import org.eclipse.jetty.server.DispatcherType; -import org.eclipse.jetty.server.HttpConnection; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.util.log.Log; -public class NestedConnection extends HttpConnection +public class NestedConnection extends AbstractHttpConnection { protected NestedConnection(final NestedConnector connector, final NestedEndPoint endp, final HttpServletRequest outerRequest, HttpServletResponse outerResponse,String nestedIn) throws IOException diff --git a/jetty-nested/src/main/java/org/eclipse/jetty/nested/NestedGenerator.java b/jetty-nested/src/main/java/org/eclipse/jetty/nested/NestedGenerator.java index b9c970207e8..47d74c3d1ee 100644 --- a/jetty-nested/src/main/java/org/eclipse/jetty/nested/NestedGenerator.java +++ b/jetty-nested/src/main/java/org/eclipse/jetty/nested/NestedGenerator.java @@ -252,7 +252,7 @@ public class NestedGenerator extends AbstractGenerator /* ------------------------------------------------------------ */ @Override - public long flushBuffer() throws IOException + public int flushBuffer() throws IOException { if (_state == STATE_HEADER) throw new IllegalStateException("State==HEADER"); diff --git a/jetty-nested/src/main/java/org/eclipse/jetty/nested/NestedParser.java b/jetty-nested/src/main/java/org/eclipse/jetty/nested/NestedParser.java index 1b6830ffbd4..9bac8831296 100644 --- a/jetty-nested/src/main/java/org/eclipse/jetty/nested/NestedParser.java +++ b/jetty-nested/src/main/java/org/eclipse/jetty/nested/NestedParser.java @@ -38,9 +38,9 @@ public class NestedParser implements Parser return false; } - public int parseAvailable() throws IOException + public boolean parseAvailable() throws IOException { - return 0; + return false; } public boolean isMoreInBuffer() throws IOException @@ -53,4 +53,13 @@ public class NestedParser implements Parser return false; } + public boolean isPersistent() + { + return false; + } + + public void setPersistent(boolean persistent) + { + } + } diff --git a/jetty-nosql/pom.xml b/jetty-nosql/pom.xml index 7660293e01b..75b77f019a9 100644 --- a/jetty-nosql/pom.xml +++ b/jetty-nosql/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 jetty-nosql diff --git a/jetty-osgi/jetty-osgi-boot-jsp/pom.xml b/jetty-osgi/jetty-osgi-boot-jsp/pom.xml index 70d3b2c02e1..76e530cde78 100644 --- a/jetty-osgi/jetty-osgi-boot-jsp/pom.xml +++ b/jetty-osgi/jetty-osgi-boot-jsp/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty.osgi jetty-osgi-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/jetty-osgi/jetty-osgi-boot-logback/pom.xml b/jetty-osgi/jetty-osgi-boot-logback/pom.xml index be3565e4f5b..595e77a4b24 100644 --- a/jetty-osgi/jetty-osgi-boot-logback/pom.xml +++ b/jetty-osgi/jetty-osgi-boot-logback/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty.osgi jetty-osgi-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/jetty-osgi/jetty-osgi-boot-warurl/pom.xml b/jetty-osgi/jetty-osgi-boot-warurl/pom.xml index 69f1d4f9178..a0463cc5643 100644 --- a/jetty-osgi/jetty-osgi-boot-warurl/pom.xml +++ b/jetty-osgi/jetty-osgi-boot-warurl/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty.osgi jetty-osgi-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/jetty-osgi/jetty-osgi-boot/pom.xml b/jetty-osgi/jetty-osgi-boot/pom.xml index b063f746195..87a7087125d 100644 --- a/jetty-osgi/jetty-osgi-boot/pom.xml +++ b/jetty-osgi/jetty-osgi-boot/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty.osgi jetty-osgi-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/ServerInstanceWrapper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/ServerInstanceWrapper.java index bb89bfe4889..21d0d6adb7d 100644 --- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/ServerInstanceWrapper.java +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/ServerInstanceWrapper.java @@ -271,7 +271,8 @@ public class ServerInstanceWrapper { try { // Execute a Jetty configuration file - is = jettyConfiguration.openStream(); + Resource r = Resource.newResource(jettyConfiguration); + is = r.getInputStream(); XmlConfiguration config = new XmlConfiguration(is); config.getIdMap().putAll(id_map); diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleDeployerHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleDeployerHelper.java index 27068cb1167..ea8fc5192b9 100644 --- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleDeployerHelper.java +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleDeployerHelper.java @@ -376,7 +376,8 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper URL contextURL = contributor.getEntry(contextFileRelativePath); if (contextURL != null) { - return registerContext(contributor,contextFileRelativePath,contextURL.openStream(),extraClasspath,overrideBundleInstallLocation,requireTldBundle,handler); + Resource r = Resource.newResource(contextURL); + return registerContext(contributor,contextFileRelativePath,r.getInputStream(),extraClasspath,overrideBundleInstallLocation,requireTldBundle,handler); } throw new IllegalArgumentException("Could not find the context " + "file " + contextFileRelativePath + " for the bundle " + contributor.getSymbolicName() + (overrideBundleInstallLocation != null?" using the install location " + overrideBundleInstallLocation:"")); diff --git a/jetty-osgi/jetty-osgi-equinoxtools/pom.xml b/jetty-osgi/jetty-osgi-equinoxtools/pom.xml index 1c0f1e535a9..fbb16968697 100644 --- a/jetty-osgi/jetty-osgi-equinoxtools/pom.xml +++ b/jetty-osgi/jetty-osgi-equinoxtools/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty.osgi jetty-osgi-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/jetty-osgi/jetty-osgi-httpservice/pom.xml b/jetty-osgi/jetty-osgi-httpservice/pom.xml index 88b815d322b..e389254295d 100644 --- a/jetty-osgi/jetty-osgi-httpservice/pom.xml +++ b/jetty-osgi/jetty-osgi-httpservice/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty.osgi jetty-osgi-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/jetty-osgi/pom.xml b/jetty-osgi/pom.xml index e06101a6774..ae30fdd313e 100644 --- a/jetty-osgi/pom.xml +++ b/jetty-osgi/pom.xml @@ -3,7 +3,7 @@ org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT ../pom.xml org.eclipse.jetty.osgi diff --git a/jetty-osgi/test-jetty-osgi/pom.xml b/jetty-osgi/test-jetty-osgi/pom.xml index c4b1a3e9888..f0ae38a1230 100644 --- a/jetty-osgi/test-jetty-osgi/pom.xml +++ b/jetty-osgi/test-jetty-osgi/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty.osgi jetty-osgi-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/jetty-overlay-deployer/pom.xml b/jetty-overlay-deployer/pom.xml index 94f302eb353..75b8349308e 100644 --- a/jetty-overlay-deployer/pom.xml +++ b/jetty-overlay-deployer/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 jetty-overlay-deployer diff --git a/jetty-plus/pom.xml b/jetty-plus/pom.xml index 145dd57640e..391eb9691c7 100644 --- a/jetty-plus/pom.xml +++ b/jetty-plus/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 jetty-plus diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/jaas/callback/DefaultCallbackHandler.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/jaas/callback/DefaultCallbackHandler.java index 35ed91e5d07..bf679cdc9b2 100644 --- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/jaas/callback/DefaultCallbackHandler.java +++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/jaas/callback/DefaultCallbackHandler.java @@ -21,7 +21,7 @@ import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.UnsupportedCallbackException; -import org.eclipse.jetty.http.security.Password; +import org.eclipse.jetty.util.security.Password; import org.eclipse.jetty.server.Request; diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/jaas/spi/AbstractDatabaseLoginModule.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/jaas/spi/AbstractDatabaseLoginModule.java index 1747463e850..77af2d1cce6 100644 --- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/jaas/spi/AbstractDatabaseLoginModule.java +++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/jaas/spi/AbstractDatabaseLoginModule.java @@ -24,7 +24,7 @@ import java.util.Map; import javax.security.auth.Subject; import javax.security.auth.callback.CallbackHandler; -import org.eclipse.jetty.http.security.Credential; +import org.eclipse.jetty.util.security.Credential; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/jaas/spi/LdapLoginModule.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/jaas/spi/LdapLoginModule.java index 5b1e7e92c39..5f7401724af 100644 --- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/jaas/spi/LdapLoginModule.java +++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/jaas/spi/LdapLoginModule.java @@ -35,7 +35,7 @@ import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.UnsupportedCallbackException; import javax.security.auth.login.LoginException; -import org.eclipse.jetty.http.security.Credential; +import org.eclipse.jetty.util.security.Credential; import org.eclipse.jetty.plus.jaas.callback.ObjectCallback; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/jaas/spi/PropertyFileLoginModule.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/jaas/spi/PropertyFileLoginModule.java index 1e36eb5f5fc..8258d68aa72 100644 --- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/jaas/spi/PropertyFileLoginModule.java +++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/jaas/spi/PropertyFileLoginModule.java @@ -24,7 +24,7 @@ import java.util.Set; import javax.security.auth.Subject; import javax.security.auth.callback.CallbackHandler; -import org.eclipse.jetty.http.security.Credential; +import org.eclipse.jetty.util.security.Credential; import org.eclipse.jetty.security.PropertyUserStore; import org.eclipse.jetty.server.UserIdentity; import org.eclipse.jetty.util.log.Log; @@ -123,4 +123,4 @@ public class PropertyFileLoginModule extends AbstractLoginModule return new UserInfo(userName, credential, roles); } -} \ No newline at end of file +} diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/jaas/spi/UserInfo.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/jaas/spi/UserInfo.java index 01e9e6c8355..861acb20fef 100644 --- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/jaas/spi/UserInfo.java +++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/jaas/spi/UserInfo.java @@ -16,7 +16,7 @@ package org.eclipse.jetty.plus.jaas.spi; import java.util.ArrayList; import java.util.List; -import org.eclipse.jetty.http.security.Credential; +import org.eclipse.jetty.util.security.Credential; /** * UserInfo diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/security/DataSourceLoginService.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/security/DataSourceLoginService.java index 48a38bebf42..fc269ccfab2 100644 --- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/security/DataSourceLoginService.java +++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/security/DataSourceLoginService.java @@ -27,7 +27,6 @@ import javax.naming.NameNotFoundException; import javax.naming.NamingException; import javax.sql.DataSource; -import org.eclipse.jetty.http.security.Password; import org.eclipse.jetty.plus.jndi.NamingEntryUtil; import org.eclipse.jetty.security.IdentityService; import org.eclipse.jetty.security.MappedLoginService; @@ -35,6 +34,7 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.UserIdentity; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.security.Password; /** diff --git a/jetty-policy/pom.xml b/jetty-policy/pom.xml index fc4e8a7e8b8..64548024678 100644 --- a/jetty-policy/pom.xml +++ b/jetty-policy/pom.xml @@ -3,7 +3,7 @@ org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT org.eclipse.jetty jetty-policy diff --git a/jetty-policy/src/main/java/org/eclipse/jetty/policy/entry/KeystoreEntry.java b/jetty-policy/src/main/java/org/eclipse/jetty/policy/entry/KeystoreEntry.java index a89b422bea4..b8d7a719ed4 100644 --- a/jetty-policy/src/main/java/org/eclipse/jetty/policy/entry/KeystoreEntry.java +++ b/jetty-policy/src/main/java/org/eclipse/jetty/policy/entry/KeystoreEntry.java @@ -21,6 +21,7 @@ import java.security.KeyStore; import org.eclipse.jetty.policy.PolicyContext; import org.eclipse.jetty.policy.PolicyException; +import org.eclipse.jetty.util.resource.Resource; public class KeystoreEntry extends AbstractEntry { @@ -49,7 +50,8 @@ public class KeystoreEntry extends AbstractEntry keystore = KeyStore.getInstance( type ); URL keyStoreLocation = new URL ( url ); - InputStream istream = keyStoreLocation.openStream(); + Resource r = Resource.newResource(keyStoreLocation); + InputStream istream = r.getInputStream(); keystore.load( istream, null ); diff --git a/jetty-rewrite/pom.xml b/jetty-rewrite/pom.xml index 5a1f026adc1..cdfe6d1c5b0 100644 --- a/jetty-rewrite/pom.xml +++ b/jetty-rewrite/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 jetty-rewrite diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/ProxyRule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/ProxyRule.java index 99c6877e52b..8e63b8c5a74 100644 --- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/ProxyRule.java +++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/ProxyRule.java @@ -280,7 +280,7 @@ public class ProxyRule extends PatternRule if (debug != 0) { - _log.debug(debug + " " + request.getMethod() + " " + url + " " + request.getProtocol()); + _log.debug("{} {} {} {}", debug ,request.getMethod(), url, request.getProtocol()); } boolean hasContent = createHeaders(request,debug,exchange); @@ -393,7 +393,7 @@ public class ProxyRule extends PatternRule if (val != null) { if (debug != 0) - _log.debug(debug + " " + hdr + ": " + val); + _log.debug("{} {} {}",debug,hdr,val); exchange.setRequestHeader(hdr,val); } @@ -483,5 +483,15 @@ public class ProxyRule extends PatternRule { _connectorType = connectorType; } + + public String getHostHeader() + { + return _hostHeader; + } + + public void setHostHeader(String hostHeader) + { + _hostHeader = hostHeader; + } } diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RuleContainer.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RuleContainer.java index 201d4fadab1..0321859b27b 100644 --- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RuleContainer.java +++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RuleContainer.java @@ -18,8 +18,7 @@ import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.eclipse.jetty.http.PathMap; -import org.eclipse.jetty.server.HttpConnection; +import org.eclipse.jetty.server.AbstractHttpConnection; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.util.LazyList; import org.eclipse.jetty.util.log.Log; @@ -224,7 +223,7 @@ public class RuleContainer extends Rule if (rule.isHandling()) { LOG.debug("handling {}",rule); - (request instanceof Request?(Request)request:HttpConnection.getCurrentConnection().getRequest()).setHandled(true); + (request instanceof Request?(Request)request:AbstractHttpConnection.getCurrentConnection().getRequest()).setHandled(true); } if (rule.isTerminating()) diff --git a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/AbstractRuleTestCase.java b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/AbstractRuleTestCase.java index 955391dbe52..16575c4dfca 100644 --- a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/AbstractRuleTestCase.java +++ b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/AbstractRuleTestCase.java @@ -15,7 +15,7 @@ package org.eclipse.jetty.rewrite.handler; import org.eclipse.jetty.io.bio.StringEndPoint; import org.eclipse.jetty.server.BlockingHttpConnection; import org.eclipse.jetty.server.Connector; -import org.eclipse.jetty.server.HttpConnection; +import org.eclipse.jetty.server.AbstractHttpConnection; import org.eclipse.jetty.server.LocalConnector; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Response; @@ -27,7 +27,7 @@ public abstract class AbstractRuleTestCase protected Server _server = new Server(); protected LocalConnector _connector; protected StringEndPoint _endpoint = new StringEndPoint(); - protected HttpConnection _connection; + protected AbstractHttpConnection _connection; protected Request _request; protected Response _response; protected boolean _isSecure = false; diff --git a/jetty-rewrite/src/test/resources/org.mortbay.jetty.rewrite.handler/jetty-rewrite.xml b/jetty-rewrite/src/test/resources/org.mortbay.jetty.rewrite.handler/jetty-rewrite.xml index 83f86b6d3e8..e97f217ff65 100644 --- a/jetty-rewrite/src/test/resources/org.mortbay.jetty.rewrite.handler/jetty-rewrite.xml +++ b/jetty-rewrite/src/test/resources/org.mortbay.jetty.rewrite.handler/jetty-rewrite.xml @@ -1,5 +1,5 @@ - + @@ -85,7 +85,7 @@ - + /* /test diff --git a/jetty-security/pom.xml b/jetty-security/pom.xml index f08e4e768a2..397991f6299 100644 --- a/jetty-security/pom.xml +++ b/jetty-security/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 jetty-security diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/Authenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/Authenticator.java index 7d65c482484..0f255300c8b 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/Authenticator.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/Authenticator.java @@ -20,9 +20,8 @@ import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import org.eclipse.jetty.server.Authentication; -import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Authentication.User; -import org.eclipse.jetty.server.SessionManager; +import org.eclipse.jetty.server.Server; /** * Authenticator Interface diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintMapping.java b/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintMapping.java index 378ff14d575..13361cf1e46 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintMapping.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintMapping.java @@ -13,7 +13,7 @@ package org.eclipse.jetty.security; -import org.eclipse.jetty.http.security.Constraint; +import org.eclipse.jetty.util.security.Constraint; public class ConstraintMapping { diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java b/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java index f8d92e8d279..44280872b43 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java @@ -25,14 +25,14 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArraySet; import org.eclipse.jetty.http.PathMap; -import org.eclipse.jetty.http.security.Constraint; +import org.eclipse.jetty.server.AbstractHttpConnection; import org.eclipse.jetty.server.Connector; -import org.eclipse.jetty.server.HttpConnection; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Response; import org.eclipse.jetty.server.UserIdentity; import org.eclipse.jetty.util.StringMap; import org.eclipse.jetty.util.TypeUtil; +import org.eclipse.jetty.util.security.Constraint; /* ------------------------------------------------------------ */ /** @@ -351,7 +351,7 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr { return true; } - HttpConnection connection = HttpConnection.getCurrentConnection(); + AbstractHttpConnection connection = AbstractHttpConnection.getCurrentConnection(); Connector connector = connection.getConnector(); if (dataConstraint == UserDataConstraint.Integral) diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/DefaultAuthenticatorFactory.java b/jetty-security/src/main/java/org/eclipse/jetty/security/DefaultAuthenticatorFactory.java index 0016be50b41..6c243a3a873 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/DefaultAuthenticatorFactory.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/DefaultAuthenticatorFactory.java @@ -15,7 +15,6 @@ package org.eclipse.jetty.security; import javax.servlet.ServletContext; -import org.eclipse.jetty.http.security.Constraint; import org.eclipse.jetty.security.Authenticator.AuthConfiguration; import org.eclipse.jetty.security.authentication.BasicAuthenticator; import org.eclipse.jetty.security.authentication.ClientCertAuthenticator; @@ -23,6 +22,7 @@ import org.eclipse.jetty.security.authentication.DigestAuthenticator; import org.eclipse.jetty.security.authentication.FormAuthenticator; import org.eclipse.jetty.security.authentication.SpnegoAuthenticator; import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.security.Constraint; /* ------------------------------------------------------------ */ /** diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/DefaultIdentityService.java b/jetty-security/src/main/java/org/eclipse/jetty/security/DefaultIdentityService.java index 472da9c6654..2b2b7462761 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/DefaultIdentityService.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/DefaultIdentityService.java @@ -17,9 +17,6 @@ import java.security.Principal; import javax.security.auth.Subject; -import org.eclipse.jetty.http.security.Credential; -import org.eclipse.jetty.security.MappedLoginService.KnownUser; -import org.eclipse.jetty.security.MappedLoginService.RolePrincipal; import org.eclipse.jetty.server.UserIdentity; diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/DefaultUserIdentity.java b/jetty-security/src/main/java/org/eclipse/jetty/security/DefaultUserIdentity.java index cf94075de0f..e9d2b9a7d37 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/DefaultUserIdentity.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/DefaultUserIdentity.java @@ -13,7 +13,6 @@ package org.eclipse.jetty.security; -import java.io.Serializable; import java.security.Principal; import javax.security.auth.Subject; diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/HashLoginService.java b/jetty-security/src/main/java/org/eclipse/jetty/security/HashLoginService.java index 12b51f51502..34f7a5cedef 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/HashLoginService.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/HashLoginService.java @@ -15,13 +15,13 @@ package org.eclipse.jetty.security; import java.io.IOException; -import org.eclipse.jetty.http.security.Credential; import org.eclipse.jetty.security.PropertyUserStore.UserListener; import org.eclipse.jetty.server.UserIdentity; import org.eclipse.jetty.util.Scanner; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.security.Credential; /* ------------------------------------------------------------ */ /** @@ -135,9 +135,8 @@ public class HashLoginService extends MappedLoginService implements UserListener if (_propertyUserStore == null) { if(LOG.isDebugEnabled()) - { LOG.debug("doStart: Starting new PropertyUserStore. PropertiesFile: " + _config + " refreshInterval: " + _refreshInterval); - } + _propertyUserStore = new PropertyUserStore(); _propertyUserStore.setRefreshInterval(_refreshInterval); _propertyUserStore.setConfig(_config); diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/IdentityService.java b/jetty-security/src/main/java/org/eclipse/jetty/security/IdentityService.java index 216ae818d9b..e05a000b2e0 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/IdentityService.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/IdentityService.java @@ -14,9 +14,9 @@ package org.eclipse.jetty.security; import java.security.Principal; + import javax.security.auth.Subject; -import org.eclipse.jetty.http.security.Credential; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.UserIdentity; diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/JDBCLoginService.java b/jetty-security/src/main/java/org/eclipse/jetty/security/JDBCLoginService.java index 03a4786efbb..44e7ef078e1 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/JDBCLoginService.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/JDBCLoginService.java @@ -23,12 +23,12 @@ import java.util.ArrayList; import java.util.List; import java.util.Properties; -import org.eclipse.jetty.http.security.Credential; import org.eclipse.jetty.server.UserIdentity; import org.eclipse.jetty.util.Loader; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.security.Credential; /* ------------------------------------------------------------ */ /** @@ -137,7 +137,7 @@ public class JDBCLoginService extends MappedLoginService || _password == null || _cacheTime < 0) { - if (LOG.isDebugEnabled()) LOG.debug("UserRealm " + getName() + " has not been properly configured"); + LOG.warn("UserRealm " + getName() + " has not been properly configured"); } _cacheTime *= 1000; _lastHashPurge = 0; diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/MappedLoginService.java b/jetty-security/src/main/java/org/eclipse/jetty/security/MappedLoginService.java index 8808be26dae..5ca896fb154 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/MappedLoginService.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/MappedLoginService.java @@ -23,11 +23,11 @@ import java.util.concurrent.ConcurrentMap; import javax.security.auth.Subject; -import org.eclipse.jetty.http.security.Credential; import org.eclipse.jetty.server.UserIdentity; 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.security.Credential; diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/PropertyUserStore.java b/jetty-security/src/main/java/org/eclipse/jetty/security/PropertyUserStore.java index b7e64ac123b..d3921bcab0a 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/PropertyUserStore.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/PropertyUserStore.java @@ -15,7 +15,6 @@ import java.util.Set; import javax.security.auth.Subject; -import org.eclipse.jetty.http.security.Credential; import org.eclipse.jetty.security.MappedLoginService.KnownUser; import org.eclipse.jetty.security.MappedLoginService.RolePrincipal; import org.eclipse.jetty.server.UserIdentity; @@ -25,6 +24,7 @@ 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.resource.Resource; +import org.eclipse.jetty.util.security.Credential; /** * PropertyUserStore diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/SpnegoLoginService.java b/jetty-security/src/main/java/org/eclipse/jetty/security/SpnegoLoginService.java index f4949b05d40..94321f03ae6 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/SpnegoLoginService.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/SpnegoLoginService.java @@ -15,17 +15,16 @@ package org.eclipse.jetty.security; //You may elect to redistribute this code under either of these licenses. //======================================================================== -import java.util.Collections; import java.util.Properties; import javax.security.auth.Subject; -import org.eclipse.jetty.http.security.B64Code; import org.eclipse.jetty.server.UserIdentity; 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.resource.Resource; +import org.eclipse.jetty.util.security.B64Code; import org.ietf.jgss.GSSContext; import org.ietf.jgss.GSSCredential; import org.ietf.jgss.GSSException; @@ -100,7 +99,7 @@ public class SpnegoLoginService extends AbstractLifeCycle implements LoginServic _targetName = properties.getProperty("targetName"); - LOG.debug("\n\nTarget Name\n\n" + _targetName); + LOG.debug("Target Name {}", _targetName); super.doStart(); } diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/SpnegoUserPrincipal.java b/jetty-security/src/main/java/org/eclipse/jetty/security/SpnegoUserPrincipal.java index bfadf400317..a30f60996f5 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/SpnegoUserPrincipal.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/SpnegoUserPrincipal.java @@ -2,7 +2,7 @@ package org.eclipse.jetty.security; import java.security.Principal; -import org.eclipse.jetty.http.security.B64Code; +import org.eclipse.jetty.util.security.B64Code; public class SpnegoUserPrincipal implements Principal { diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/UserAuthentication.java b/jetty-security/src/main/java/org/eclipse/jetty/security/UserAuthentication.java index cef019b14fe..da55e961c47 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/UserAuthentication.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/UserAuthentication.java @@ -13,9 +13,6 @@ package org.eclipse.jetty.security; -import java.io.Serializable; - -import org.eclipse.jetty.security.authentication.LoginAuthenticator; import org.eclipse.jetty.server.Authentication; import org.eclipse.jetty.server.UserIdentity; import org.eclipse.jetty.server.UserIdentity.Scope; diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/BasicAuthenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/BasicAuthenticator.java index 372f9b63ea0..7857b4d46ae 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/BasicAuthenticator.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/BasicAuthenticator.java @@ -21,14 +21,14 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.http.HttpHeaders; -import org.eclipse.jetty.http.security.Constraint; import org.eclipse.jetty.security.ServerAuthException; import org.eclipse.jetty.security.UserAuthentication; import org.eclipse.jetty.server.Authentication; -import org.eclipse.jetty.server.UserIdentity; import org.eclipse.jetty.server.Authentication.User; +import org.eclipse.jetty.server.UserIdentity; import org.eclipse.jetty.util.B64Code; import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.security.Constraint; /** * @version $Rev: 4793 $ $Date: 2009-03-19 00:00:01 +0100 (Thu, 19 Mar 2009) $ diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/ClientCertAuthenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/ClientCertAuthenticator.java index 9efd783b8a0..25220375506 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/ClientCertAuthenticator.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/ClientCertAuthenticator.java @@ -25,8 +25,6 @@ import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.eclipse.jetty.http.security.Constraint; -import org.eclipse.jetty.http.security.Password; import org.eclipse.jetty.security.ServerAuthException; import org.eclipse.jetty.security.UserAuthentication; import org.eclipse.jetty.server.Authentication; @@ -35,6 +33,8 @@ import org.eclipse.jetty.server.UserIdentity; import org.eclipse.jetty.util.B64Code; import org.eclipse.jetty.util.security.CertificateUtils; import org.eclipse.jetty.util.security.CertificateValidator; +import org.eclipse.jetty.util.security.Constraint; +import org.eclipse.jetty.util.security.Password; /** * @version $Rev: 4793 $ $Date: 2009-03-19 00:00:01 +0100 (Thu, 19 Mar 2009) $ diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DigestAuthenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DigestAuthenticator.java index 5a2ecf412e8..51833fad789 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DigestAuthenticator.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DigestAuthenticator.java @@ -28,22 +28,21 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.http.HttpHeaders; -import org.eclipse.jetty.http.security.Constraint; -import org.eclipse.jetty.http.security.Credential; -import org.eclipse.jetty.security.Authenticator.AuthConfiguration; import org.eclipse.jetty.security.SecurityHandler; import org.eclipse.jetty.security.ServerAuthException; import org.eclipse.jetty.security.UserAuthentication; import org.eclipse.jetty.server.Authentication; +import org.eclipse.jetty.server.Authentication.User; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.UserIdentity; -import org.eclipse.jetty.server.Authentication.User; import org.eclipse.jetty.util.B64Code; import org.eclipse.jetty.util.QuotedStringTokenizer; 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; +import org.eclipse.jetty.util.security.Constraint; +import org.eclipse.jetty.util.security.Credential; /** * @version $Rev: 4793 $ $Date: 2009-03-19 00:00:01 +0100 (Thu, 19 Mar 2009) $ @@ -87,7 +86,18 @@ public class DigestAuthenticator extends LoginAuthenticator String mna=configuration.getInitParameter("maxNonceAge"); if (mna!=null) - _maxNonceAgeMs=Long.valueOf(mna); + { + synchronized (this) + { + _maxNonceAgeMs=Long.valueOf(mna); + } + } + } + + /* ------------------------------------------------------------ */ + public synchronized void setMaxNonceAge(long maxNonceAgeInMillis) + { + _maxNonceAgeMs = maxNonceAgeInMillis; } /* ------------------------------------------------------------ */ @@ -117,7 +127,8 @@ public class DigestAuthenticator extends LoginAuthenticator boolean stale = false; if (credentials != null) { - if (LOG.isDebugEnabled()) LOG.debug("Credentials: " + credentials); + if (LOG.isDebugEnabled()) + LOG.debug("Credentials: " + credentials); QuotedStringTokenizer tokenizer = new QuotedStringTokenizer(credentials, "=, ", true, false); final Digest digest = new Digest(request.getMethod()); String last = null; @@ -234,7 +245,11 @@ public class DigestAuthenticator extends LoginAuthenticator private int checkNonce(Digest digest, Request request) { // firstly let's expire old nonces - long expired = request.getTimeStamp()-_maxNonceAgeMs; + long expired; + synchronized (this) + { + expired = request.getTimeStamp()-_maxNonceAgeMs; + } Nonce nonce=_nonceQueue.peek(); while (nonce!=null && nonce._ts(base_request.getParameters())); } diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/LoginAuthenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/LoginAuthenticator.java index 98cb1968b6d..c730a9bab5c 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/LoginAuthenticator.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/LoginAuthenticator.java @@ -24,7 +24,6 @@ import javax.servlet.http.HttpSession; import org.eclipse.jetty.security.Authenticator; import org.eclipse.jetty.security.IdentityService; import org.eclipse.jetty.security.LoginService; -import org.eclipse.jetty.server.SessionManager; public abstract class LoginAuthenticator implements Authenticator { diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionAuthentication.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionAuthentication.java index 0e28f3d4193..e63e597ccae 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionAuthentication.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionAuthentication.java @@ -16,20 +16,16 @@ package org.eclipse.jetty.security.authentication; import java.io.IOException; import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; import java.io.Serializable; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionActivationListener; -import javax.servlet.http.HttpSessionAttributeListener; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; import javax.servlet.http.HttpSessionEvent; -import org.eclipse.jetty.security.Authenticator; import org.eclipse.jetty.security.LoginService; import org.eclipse.jetty.security.SecurityHandler; -import org.eclipse.jetty.security.UserAuthentication; import org.eclipse.jetty.server.Authentication; import org.eclipse.jetty.server.UserIdentity; import org.eclipse.jetty.server.UserIdentity.Scope; diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SpnegoAuthenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SpnegoAuthenticator.java index 8ed9a790a0f..9df7448468a 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SpnegoAuthenticator.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SpnegoAuthenticator.java @@ -23,7 +23,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.http.HttpHeaders; -import org.eclipse.jetty.http.security.Constraint; import org.eclipse.jetty.security.ServerAuthException; import org.eclipse.jetty.security.UserAuthentication; import org.eclipse.jetty.server.Authentication; @@ -31,6 +30,7 @@ import org.eclipse.jetty.server.Authentication.User; import org.eclipse.jetty.server.UserIdentity; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.security.Constraint; public class SpnegoAuthenticator extends LoginAuthenticator { diff --git a/jetty-security/src/test/java/org/eclipse/jetty/security/ConstraintTest.java b/jetty-security/src/test/java/org/eclipse/jetty/security/ConstraintTest.java index 19eb3099011..47190675b63 100644 --- a/jetty-security/src/test/java/org/eclipse/jetty/security/ConstraintTest.java +++ b/jetty-security/src/test/java/org/eclipse/jetty/security/ConstraintTest.java @@ -27,8 +27,6 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.eclipse.jetty.http.security.Constraint; -import org.eclipse.jetty.http.security.Password; import org.eclipse.jetty.security.authentication.BasicAuthenticator; import org.eclipse.jetty.security.authentication.FormAuthenticator; import org.eclipse.jetty.server.Connector; @@ -41,7 +39,8 @@ import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.HandlerWrapper; import org.eclipse.jetty.server.session.SessionHandler; import org.eclipse.jetty.util.B64Code; -import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.security.Constraint; +import org.eclipse.jetty.util.security.Password; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; diff --git a/jetty-security/src/test/java/org/eclipse/jetty/security/PropertyUserStoreTest.java b/jetty-security/src/test/java/org/eclipse/jetty/security/PropertyUserStoreTest.java index 68220b8dbca..2e24c178c4d 100644 --- a/jetty-security/src/test/java/org/eclipse/jetty/security/PropertyUserStoreTest.java +++ b/jetty-security/src/test/java/org/eclipse/jetty/security/PropertyUserStoreTest.java @@ -9,7 +9,7 @@ import java.util.concurrent.atomic.AtomicInteger; import junit.framework.Assert; -import org.eclipse.jetty.http.security.Credential; +import org.eclipse.jetty.util.security.Credential; import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/jetty-server/pom.xml b/jetty-server/pom.xml index 9cd880908e3..8e71f15966d 100644 --- a/jetty-server/pom.xml +++ b/jetty-server/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 jetty-server diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java index 199d0e18e26..6f349ca4f8b 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java @@ -48,8 +48,8 @@ import org.eclipse.jetty.util.thread.ThreadPool; *
  • Base acceptor thread
  • *
  • Optional reverse proxy headers checking
  • * - * - * + * + * */ public abstract class AbstractConnector extends HttpBuffers implements Connector, Dumpable { @@ -181,7 +181,7 @@ public abstract class AbstractConnector extends HttpBuffers implements Connector *

    * Previously, Jetty supported separate idle timeouts and IO operation timeouts, however the expense of changing the value of soTimeout was significant, so * these timeouts were merged. With the advent of NIO, it may be possible to again differentiate these values (if there is demand). - * + * * @param maxIdleTime * The maxIdleTime to set. */ @@ -358,7 +358,11 @@ public abstract class AbstractConnector extends HttpBuffers implements Connector /* ------------------------------------------------------------ */ public void join() throws InterruptedException { - Thread[] threads = _acceptorThread; + Thread[] threads; + synchronized(this) + { + threads= _acceptorThread; + } if (threads != null) for (int i = 0; i < threads.length; i++) if (threads[i] != null) @@ -410,13 +414,13 @@ public abstract class AbstractConnector extends HttpBuffers implements Connector request.setScheme(HttpSchemes.HTTPS); } } - + // Retrieving headers from the request String forwardedHost = getLeftMostFieldValue(httpFields,getForwardedHostHeader()); String forwardedServer = getLeftMostFieldValue(httpFields,getForwardedServerHeader()); String forwardedFor = getLeftMostFieldValue(httpFields,getForwardedForHeader()); String forwardedProto = getLeftMostFieldValue(httpFields,getForwardedProtoHeader()); - + if (_hostHeader != null) { // Update host header @@ -458,7 +462,7 @@ public abstract class AbstractConnector extends HttpBuffers implements Connector request.setRemoteHost(inetAddress == null?forwardedFor:inetAddress.getHostName()); } - + if (forwardedProto != null) { request.setScheme(forwardedProto); @@ -611,7 +615,7 @@ public abstract class AbstractConnector extends HttpBuffers implements Connector /* ------------------------------------------------------------ */ /** * Is reverse proxy handling on? - * + * * @return true if this connector is checking the x-forwarded-for/host/server headers */ public boolean isForwarded() @@ -623,7 +627,7 @@ public abstract class AbstractConnector extends HttpBuffers implements Connector /** * Set reverse proxy handling. If set to true, then the X-Forwarded headers (or the headers set in their place) are looked for to set the request protocol, * host, server and client ip. - * + * * @param check * true if this connector is checking the x-forwarded-for/host/server headers * @set {@link #setForwardedForHeader(String)} @@ -634,7 +638,7 @@ public abstract class AbstractConnector extends HttpBuffers implements Connector public void setForwarded(boolean check) { if (check) - LOG.debug(this + " is forwarded"); + LOG.debug("{} is forwarded",this); _forwarded = check; } @@ -648,7 +652,7 @@ public abstract class AbstractConnector extends HttpBuffers implements Connector /** * Set a forced valued for the host header to control what is returned by {@link ServletRequest#getServerName()} and {@link ServletRequest#getServerPort()}. * This value is only used if {@link #isForwarded()} is true. - * + * * @param hostHeader * The value of the host header to force. */ @@ -659,7 +663,7 @@ public abstract class AbstractConnector extends HttpBuffers implements Connector /* ------------------------------------------------------------ */ /* - * + * * @see #setForwarded(boolean) */ public String getForwardedHostHeader() @@ -722,7 +726,7 @@ public abstract class AbstractConnector extends HttpBuffers implements Connector /* ------------------------------------------------------------ */ /** * Get the forwardedProtoHeader. - * + * * @return the forwardedProtoHeader (default X-Forwarded-For) * @see #setForwarded(boolean) */ @@ -734,7 +738,7 @@ public abstract class AbstractConnector extends HttpBuffers implements Connector /* ------------------------------------------------------------ */ /** * Set the forwardedProtoHeader. - * + * * @param forwardedProtoHeader * the forwardedProtoHeader to set (default X-Forwarded-For) * @see #setForwarded(boolean) @@ -845,10 +849,6 @@ public abstract class AbstractConnector extends HttpBuffers implements Connector // Connector has been stopped LOG.ignore(x); } - catch (ThreadDeath e) - { - throw e; - } catch (Throwable e) { LOG.warn(e); @@ -1001,7 +1001,8 @@ public abstract class AbstractConnector extends HttpBuffers implements Connector if (on && _statsStartedAt.get() != -1) return; - LOG.debug("Statistics on = " + on + " for " + this); + if (LOG.isDebugEnabled()) + LOG.debug("Statistics on = " + on + " for " + this); statsReset(); _statsStartedAt.set(on?System.currentTimeMillis():-1); @@ -1039,19 +1040,19 @@ public abstract class AbstractConnector extends HttpBuffers implements Connector /* ------------------------------------------------------------ */ protected void connectionUpgraded(Connection oldConnection, Connection newConnection) { - _requestStats.set((oldConnection instanceof HttpConnection)?((HttpConnection)oldConnection).getRequests():0); + _requestStats.set((oldConnection instanceof AbstractHttpConnection)?((AbstractHttpConnection)oldConnection).getRequests():0); } /* ------------------------------------------------------------ */ protected void connectionClosed(Connection connection) { - connection.closed(); + connection.onClose(); if (_statsStartedAt.get() == -1) return; long duration = System.currentTimeMillis() - connection.getTimeStamp(); - int requests = (connection instanceof HttpConnection)?((HttpConnection)connection).getRequests():0; + int requests = (connection instanceof AbstractHttpConnection)?((AbstractHttpConnection)connection).getRequests():0; _requestStats.set(requests); _connectionStats.decrement(); _connectionDurationStats.set(duration); @@ -1070,7 +1071,7 @@ public abstract class AbstractConnector extends HttpBuffers implements Connector /** * Set the priority offset of the acceptor threads. The priority is adjusted by this amount (default 0) to either favour the acceptance of new threads and * newly active connections or to favour the handling of already dispatched connections. - * + * * @param offset * the amount to alter the priority of the acceptor threads. */ diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractHttpConnection.java similarity index 90% rename from jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java rename to jetty-server/src/main/java/org/eclipse/jetty/server/AbstractHttpConnection.java index 3f1d327d75b..6869768901f 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractHttpConnection.java @@ -1,5 +1,5 @@ // ======================================================================== -// Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd. +// Copyright (c) 2004-2011 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 @@ -22,7 +22,6 @@ import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.continuation.ContinuationThrowable; -import org.eclipse.jetty.http.AbstractGenerator; import org.eclipse.jetty.http.EncodedHttpURI; import org.eclipse.jetty.http.Generator; import org.eclipse.jetty.http.HttpBuffers; @@ -40,9 +39,9 @@ import org.eclipse.jetty.http.HttpVersions; import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.http.Parser; import org.eclipse.jetty.io.AbstractConnection; -import org.eclipse.jetty.io.AsyncEndPoint; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.BufferCache.CachedBuffer; +import org.eclipse.jetty.io.Buffers; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EofException; @@ -56,7 +55,6 @@ import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; -import org.eclipse.jetty.util.thread.Timeout; /** *

    A HttpConnection represents the connection of a HTTP client to the server @@ -89,12 +87,12 @@ import org.eclipse.jetty.util.thread.Timeout; *

    * */ -public abstract class HttpConnection extends AbstractConnection +public abstract class AbstractHttpConnection extends AbstractConnection { - private static final Logger LOG = Log.getLogger(HttpConnection.class); + private static final Logger LOG = Log.getLogger(AbstractHttpConnection.class); private static final int UNKNOWN = -2; - private static final ThreadLocal __currentConnection = new ThreadLocal(); + private static final ThreadLocal __currentConnection = new ThreadLocal(); private int _requests; @@ -128,13 +126,13 @@ public abstract class HttpConnection extends AbstractConnection private boolean _delayedHandling=false; /* ------------------------------------------------------------ */ - public static HttpConnection getCurrentConnection() + public static AbstractHttpConnection getCurrentConnection() { return __currentConnection.get(); } /* ------------------------------------------------------------ */ - protected static void setCurrentConnection(HttpConnection connection) + protected static void setCurrentConnection(AbstractHttpConnection connection) { __currentConnection.set(connection); } @@ -143,13 +141,13 @@ public abstract class HttpConnection extends AbstractConnection /** Constructor * */ - public HttpConnection(Connector connector, EndPoint endpoint, Server server) + public AbstractHttpConnection(Connector connector, EndPoint endpoint, Server server) { super(endpoint); _uri = StringUtil.__UTF8.equals(URIUtil.__CHARSET)?new HttpURI():new EncodedHttpURI(URIUtil.__CHARSET); _connector = connector; HttpBuffers ab = (HttpBuffers)_connector; - _parser = new HttpParser(ab.getRequestBuffers(), endpoint, new RequestHandler()); + _parser = newHttpParser(ab.getRequestBuffers(), endpoint, new RequestHandler()); _requestFields = new HttpFields(); _responseFields = new HttpFields(server.getMaxCookieVersion()); _request = new Request(this); @@ -160,11 +158,11 @@ public abstract class HttpConnection extends AbstractConnection } /* ------------------------------------------------------------ */ - protected HttpConnection(Connector connector, EndPoint endpoint, Server server, + protected AbstractHttpConnection(Connector connector, EndPoint endpoint, Server server, Parser parser, Generator generator, Request request) { super(endpoint); - + _uri = URIUtil.__CHARSET.equals(StringUtil.__UTF8)?new HttpURI():new EncodedHttpURI(URIUtil.__CHARSET); _connector = connector; _parser = parser; @@ -177,6 +175,11 @@ public abstract class HttpConnection extends AbstractConnection _server = server; } + protected HttpParser newHttpParser(Buffers requestBuffers, EndPoint endpoint, HttpParser.EventHandler requestHandler) + { + return new HttpParser(requestBuffers, endpoint, requestHandler); + } + /* ------------------------------------------------------------ */ /** * @return the parser used by this connection @@ -194,13 +197,13 @@ public abstract class HttpConnection extends AbstractConnection { return _requests; } - + /* ------------------------------------------------------------ */ public Server getServer() { return _server; } - + /* ------------------------------------------------------------ */ /** * @return Returns the associatedObject. @@ -327,7 +330,7 @@ public abstract class HttpConnection extends AbstractConnection } if (_in == null) - _in = new HttpInput(HttpConnection.this); + _in = new HttpInput(AbstractHttpConnection.this); return _in; } @@ -353,7 +356,27 @@ public abstract class HttpConnection extends AbstractConnection if (_writer==null) { _writer=new OutputWriter(); - _printWriter=new UncheckedPrintWriter(_writer); + if (_server.isUncheckedPrintWriter()) + _printWriter=new UncheckedPrintWriter(_writer); + else + _printWriter = new PrintWriter(_writer) + { + public void close() + { + synchronized (lock) + { + try + { + out.close(); + } + catch (IOException e) + { + setError(); + } + } + } + }; + } _writer.setCharacterEncoding(encoding); return _printWriter; @@ -366,30 +389,16 @@ public abstract class HttpConnection extends AbstractConnection } /* ------------------------------------------------------------ */ - public void scheduleTimeout(Timeout.Task task, long timeoutMs) + public void reset() { - throw new UnsupportedOperationException(); - } - - /* ------------------------------------------------------------ */ - public void cancelTimeout(Timeout.Task task) - { - throw new UnsupportedOperationException(); - } - - /* ------------------------------------------------------------ */ - public void reset(boolean returnBuffers) - { - _parser.reset(); - if (returnBuffers) - _parser.returnBuffers(); + _parser.reset(); + _parser.returnBuffers(); // TODO maybe only on unhandle _requestFields.clear(); _request.recycle(); - - _generator.reset(returnBuffers); // TODO maybe only release when low on resources + _generator.reset(); + _generator.returnBuffers();// TODO maybe only on unhandle _responseFields.clear(); _response.recycle(); - _uri.clear(); } @@ -471,9 +480,6 @@ public abstract class HttpConnection extends AbstractConnection } catch (Throwable e) { - if (e instanceof ThreadDeath) - throw (ThreadDeath)e; - LOG.warn(String.valueOf(_uri),e); error=true; _request.setHandled(true); @@ -557,10 +563,10 @@ public abstract class HttpConnection extends AbstractConnection } catch(RuntimeException e) { - LOG.warn("header full: "+e); + LOG.warn("header full: " + e); _response.reset(); - _generator.reset(true); + _generator.reset(); _generator.setResponse(HttpStatus.INTERNAL_SERVER_ERROR_500,null); _generator.completeHeader(_responseFields,Generator.LAST); _generator.complete(); @@ -592,7 +598,7 @@ public abstract class HttpConnection extends AbstractConnection LOG.debug(e); _response.reset(); - _generator.reset(true); + _generator.reset(); _generator.setResponse(HttpStatus.INTERNAL_SERVER_ERROR_500,null); _generator.completeHeader(_responseFields,Generator.LAST); _generator.complete(); @@ -659,11 +665,11 @@ public abstract class HttpConnection extends AbstractConnection } /* ------------------------------------------------------------ */ - public void closed() + public void onClose() { LOG.debug("closed {}",this); } - + /* ------------------------------------------------------------ */ public boolean isExpecting100Continues() { @@ -686,6 +692,16 @@ public abstract class HttpConnection extends AbstractConnection return _connector.getMaxIdleTime(); } + /* ------------------------------------------------------------ */ + public String toString() + { + return String.format("%s,g=%s,p=%s,r=%d", + super.toString(), + _generator, + _parser, + _requests); + } + /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ @@ -702,7 +718,7 @@ public abstract class HttpConnection extends AbstractConnection public void startRequest(Buffer method, Buffer uri, Buffer version) throws IOException { uri=uri.asImmutableBuffer(); - + _host = false; _expect = false; _expect100Continue=false; @@ -725,7 +741,8 @@ public abstract class HttpConnection extends AbstractConnection case HttpMethods.HEAD_ORDINAL: _head=true; - // fall through + _uri.parse(uri.array(), uri.getIndex(), uri.length()); + break; default: _uri.parse(uri.array(), uri.getIndex(), uri.length()); @@ -817,46 +834,6 @@ public abstract class HttpConnection extends AbstractConnection value = MimeTypes.CACHE.lookup(value); _charset=MimeTypes.getCharsetFromContentType(value); break; - - case HttpHeaders.CONNECTION_ORDINAL: - //looks rather clumsy, but the idea is to optimize for a single valued header - switch(HttpHeaderValues.CACHE.getOrdinal(value)) - { - case -1: - { - String[] values = value.toString().split(","); - for (int i=0;values!=null && i _continuationListeners; /* ------------------------------------------------------------ */ @@ -90,7 +90,7 @@ public class AsyncContinuation implements AsyncContext, Continuation } /* ------------------------------------------------------------ */ - protected void setConnection(final HttpConnection connection) + protected void setConnection(final AbstractHttpConnection connection) { synchronized(this) { @@ -183,6 +183,26 @@ public class AsyncContinuation implements AsyncContext, Continuation } } } + + /* ------------------------------------------------------------ */ + /* (non-Javadoc) + * @see javax.servlet.ServletRequest#isSuspended() + */ + public boolean isSuspending() + { + synchronized(this) + { + switch(_state) + { + case __ASYNCSTARTED: + case __ASYNCWAIT: + return true; + + default: + return false; + } + } + } /* ------------------------------------------------------------ */ @Override @@ -539,7 +559,7 @@ public class AsyncContinuation implements AsyncContext, Continuation EndPoint endp=_connection.getEndPoint(); if (!endp.isBlocking()) { - ((AsyncEndPoint)endp).dispatch(); + ((AsyncEndPoint)endp).asyncDispatch(); } } @@ -576,7 +596,7 @@ public class AsyncContinuation implements AsyncContext, Continuation } else { - _connection.scheduleTimeout(_event,_timeoutMs); + ((AsyncEndPoint)endp).scheduleTimeout(_event,_timeoutMs); } } } @@ -597,7 +617,9 @@ public class AsyncContinuation implements AsyncContext, Continuation { final AsyncEventState event=_event; if (event!=null) - _connection.cancelTimeout(event); + { + ((AsyncEndPoint)endp).cancelTimeout(event); + } } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncHttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncHttpConnection.java index 4d72d8feb19..9d79c6cd78f 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncHttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncHttpConnection.java @@ -1,3 +1,16 @@ +// ======================================================================== +// Copyright (c) 2006-2011 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 java.io.IOException; @@ -7,21 +20,29 @@ import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.io.AsyncEndPoint; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.nio.AsyncConnection; import org.eclipse.jetty.io.nio.SelectChannelEndPoint; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; -public class AsyncHttpConnection extends HttpConnection + +/* ------------------------------------------------------------ */ +/** Asychronous Server HTTP connection + * + */ +public class AsyncHttpConnection extends AbstractHttpConnection implements AsyncConnection { private final static int NO_PROGRESS_INFO = Integer.getInteger("org.mortbay.jetty.NO_PROGRESS_INFO",100); private final static int NO_PROGRESS_CLOSE = Integer.getInteger("org.mortbay.jetty.NO_PROGRESS_CLOSE",200); private static final Logger LOG = Log.getLogger(AsyncHttpConnection.class); private int _total_no_progress; + private final AsyncEndPoint _asyncEndp; public AsyncHttpConnection(Connector connector, EndPoint endpoint, Server server) { super(connector,endpoint,server); + _asyncEndp=(AsyncEndPoint)endpoint; } public Connection handle() throws IOException @@ -30,39 +51,34 @@ public class AsyncHttpConnection extends HttpConnection boolean some_progress=false; boolean progress=true; - // Loop while more in buffer try { setCurrentConnection(this); - boolean more_in_buffer =false; - - while (_endp.isOpen() && (more_in_buffer || progress) && connection==this) + // While progress and the connection has not changed + while (progress && connection==this) { progress=false; try { - // Handle resumed request if (_request._async.isAsync() && !_request._async.isComplete()) handleRequest(); - // else Parse more input - else if (!_parser.isComplete() && _parser.parseAvailable()>0) + else if (!_parser.isComplete() && _parser.parseAvailable()) progress=true; // Generate more output - if (_generator.isCommitted() && !_generator.isComplete() && _generator.flushBuffer()>0) + if (_generator.isCommitted() && !_generator.isComplete() && !_endp.isOutputShutdown()) + if (_generator.flushBuffer()>0) + progress=true; + + // Flush output + _endp.flush(); + + // Has any IO been done by the endpoint itself since last loop + if (_asyncEndp.hasProgressed()) progress=true; - - // Flush output from buffering endpoint - if (_endp.isBufferingOutput()) - _endp.flush(); - - // Special case close handling. - // If we were dispatched and have made no progress, but io is shutdown, then close - if (!progress && !some_progress && (_endp.isInputShutdown()||_endp.isOutputShutdown())) - _endp.close(); } catch (HttpException e) { @@ -72,91 +88,81 @@ public class AsyncHttpConnection extends HttpConnection LOG.debug("fields="+_requestFields); LOG.debug(e); } + progress=true; _generator.sendError(e.getStatus(), e.getReason(), null, true); - _parser.reset(); - _endp.close(); } finally { - // Do we need to complete a half close? - if (_endp.isInputShutdown() && (_parser.isIdle() || _parser.isComplete())) + some_progress|=progress; + // Is this request/response round complete and are fully flushed? + if (_parser.isComplete() && _generator.isComplete()) { - LOG.debug("complete half close {}",this); - more_in_buffer=false; - _endp.close(); - reset(true); - } + // Reset the parser/generator + progress=true; - // else Is this request/response round complete? - else if (_parser.isComplete() && _generator.isComplete() && !_endp.isBufferingOutput()) - { // look for a switched connection instance? if (_response.getStatus()==HttpStatus.SWITCHING_PROTOCOLS_101) { Connection switched=(Connection)_request.getAttribute("org.eclipse.jetty.io.Connection"); if (switched!=null) - { - _parser.reset(); - _generator.reset(true); connection=switched; - } } - // Reset the parser/generator - // keep the buffers as we will cycle - progress=true; - reset(false); - more_in_buffer = _parser.isMoreInBuffer() || _endp.isBufferingInput(); - } - // else Are we suspended? - else if (_request.isAsyncStarted()) - { - LOG.debug("suspended {}",this); - more_in_buffer=false; - progress=false; - } - else - more_in_buffer = _parser.isMoreInBuffer() || _endp.isBufferingInput(); + reset(); - some_progress|=progress|((SelectChannelEndPoint)_endp).isProgressing(); + // TODO Is this still required? + if (!_generator.isPersistent() && !_endp.isOutputShutdown()) + { + LOG.warn("Safety net oshut!!! IF YOU SEE THIS, PLEASE RAISE BUGZILLA"); + _endp.shutdownOutput(); + } + } + else if (_request.getAsyncContinuation().isAsyncStarted()) + { + // The request is suspended, so even though progress has been made, break the while loop + LOG.debug("suspended {}",this); + // TODO: breaking inside finally blocks is bad: rethink how we should exit from here + break; + } } } } finally { setCurrentConnection(null); - _parser.returnBuffers(); - _generator.returnBuffers(); - - // Check if we are write blocked - if (_generator.isCommitted() && !_generator.isComplete() && _endp.isOpen() && !_endp.isOutputShutdown()) - ((AsyncEndPoint)_endp).scheduleWrite(); // TODO. This should not be required - - if (some_progress) + if (!_request.isAsyncStarted()) { - _total_no_progress=0; + _parser.returnBuffers(); + _generator.returnBuffers(); } + + // Safety net to catch spinning + if (some_progress) + _total_no_progress=0; else { - int totalNoProgress=++_total_no_progress; - - if (NO_PROGRESS_INFO>0 && totalNoProgress==NO_PROGRESS_INFO && (NO_PROGRESS_CLOSE<=0 || totalNoProgress0 && _total_no_progress%NO_PROGRESS_INFO==0 && (NO_PROGRESS_CLOSE<=0 || _total_no_progress< NO_PROGRESS_CLOSE)) + LOG.info("EndPoint making no progress: "+_total_no_progress+" "+_endp+" "+this); + if (NO_PROGRESS_CLOSE>0 && _total_no_progress==NO_PROGRESS_CLOSE) { - LOG.info("EndPoint making no progress: {} {}", totalNoProgress, _endp); - } - - if (NO_PROGRESS_CLOSE>0 && totalNoProgress==NO_PROGRESS_CLOSE) - { - LOG.warn("Closing EndPoint making no progress: {} {}", totalNoProgress, _endp); + LOG.warn("Closing EndPoint making no progress: "+_total_no_progress+" "+_endp+" "+this); if (_endp instanceof SelectChannelEndPoint) - { - System.err.println(((SelectChannelEndPoint)_endp).getSelectManager().dump()); ((SelectChannelEndPoint)_endp).getChannel().close(); - } } } } return connection; } + public void onInputShutdown() throws IOException + { + // If we don't have a committed response and we are not suspended + if (_generator.isIdle() && !_request.getAsyncContinuation().isSuspended()) + { + // then no more can happen, so close. + _endp.close(); + } + } + } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/BlockingHttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/BlockingHttpConnection.java index 975f942389e..ec15289f914 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/BlockingHttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/BlockingHttpConnection.java @@ -1,3 +1,15 @@ +// ======================================================================== +// Copyright (c) 2006-2011 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 java.io.IOException; @@ -6,66 +18,62 @@ import org.eclipse.jetty.http.Generator; import org.eclipse.jetty.http.HttpException; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.Parser; -import org.eclipse.jetty.io.AsyncEndPoint; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; -public class BlockingHttpConnection extends HttpConnection + +/* ------------------------------------------------------------ */ +/** Blocking Server HTTP Connection + */ +public class BlockingHttpConnection extends AbstractHttpConnection { private static final Logger LOG = Log.getLogger(BlockingHttpConnection.class); - private volatile boolean _handling; - public BlockingHttpConnection(Connector connector, EndPoint endpoint, Server server) { super(connector,endpoint,server); } - public BlockingHttpConnection(Connector connector, EndPoint endpoint, Server server, Parser parser, Generator generator, Request request) { super(connector,endpoint,server,parser,generator,request); } + @Override + protected void handleRequest() throws IOException + { + super.handleRequest(); + } public Connection handle() throws IOException { Connection connection = this; - // Loop while more in buffer - boolean more_in_buffer =true; // assume true until proven otherwise - try { setCurrentConnection(this); - while (more_in_buffer && _endp.isOpen()) + // do while the endpoint is open + // AND the connection has not changed + while (_endp.isOpen() && connection==this) { try { // If we are not ended then parse available - if (!_parser.isComplete()) + if (!_parser.isComplete() && !_endp.isInputShutdown()) _parser.parseAvailable(); // Do we have more generating to do? // Loop here because some writes may take multiple steps and // we need to flush them all before potentially blocking in the // next loop. - while (_generator.isCommitted() && !_generator.isComplete()) - { - long written=_generator.flushBuffer(); - if (written<=0) - break; - if (_endp.isBufferingOutput()) - _endp.flush(); - } + if (_generator.isCommitted() && !_generator.isComplete() && !_endp.isOutputShutdown()) + _generator.flushBuffer(); // Flush buffers - if (_endp.isBufferingOutput()) - _endp.flush(); - + _endp.flush(); } catch (HttpException e) { @@ -81,59 +89,37 @@ public class BlockingHttpConnection extends HttpConnection } finally { - more_in_buffer = _parser.isMoreInBuffer() || _endp.isBufferingInput(); - - // Is this request/response round complete? - if (_parser.isComplete() && _generator.isComplete() && !_endp.isBufferingOutput()) + // Is this request/response round complete and are fully flushed? + if (_parser.isComplete() && _generator.isComplete()) { + // Reset the parser/generator + reset(); + // look for a switched connection instance? - Connection switched=(_response.getStatus()==HttpStatus.SWITCHING_PROTOCOLS_101) - ?(Connection)_request.getAttribute("org.eclipse.jetty.io.Connection"):null; - - // have we switched? - if (switched!=null) + if (_response.getStatus()==HttpStatus.SWITCHING_PROTOCOLS_101) { - _parser.reset(); - _generator.reset(true); - connection=switched; + Connection switched=(Connection)_request.getAttribute("org.eclipse.jetty.io.Connection"); + if (switched!=null) + connection=switched; } - else - { - // No switch, so cleanup and reset - if (!_generator.isPersistent() || _endp.isInputShutdown()) - { - _parser.reset(); - more_in_buffer=false; - _endp.close(); - } - if (more_in_buffer) - { - reset(false); - more_in_buffer = _parser.isMoreInBuffer() || _endp.isBufferingInput(); - } - else - reset(true); + // TODO Is this required? + if (!_generator.isPersistent() && !_endp.isOutputShutdown()) + { + LOG.warn("Safety net oshut!!! Please open a bugzilla"); + _endp.shutdownOutput(); } } - else if (_parser.isIdle() && _endp.isInputShutdown()) - { - more_in_buffer=false; - _endp.close(); - } - - if (_request.isAsyncStarted()) - throw new IllegalStateException(); } } + + return connection; } finally { - _parser.returnBuffers(); setCurrentConnection(null); - _handling=false; + _parser.returnBuffers(); + _generator.returnBuffers(); } - return connection; } - } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Connector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Connector.java index ddad84d6aa3..95c30b15139 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Connector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Connector.java @@ -135,14 +135,14 @@ public interface Connector extends LifeCycle /* ------------------------------------------------------------ */ /** * @return The port to use when redirecting a request if a data constraint of integral is - * required. See {@link org.eclipse.jetty.http.security.Constraint#getDataConstraint()} + * required. See {@link org.eclipse.jetty.util.security.Constraint#getDataConstraint()} */ int getIntegralPort(); /* ------------------------------------------------------------ */ /** * @return The schema to use when redirecting a request if a data constraint of integral is - * required. See {@link org.eclipse.jetty.http.security.Constraint#getDataConstraint()} + * required. See {@link org.eclipse.jetty.util.security.Constraint#getDataConstraint()} */ String getIntegralScheme(); @@ -156,7 +156,7 @@ public interface Connector extends LifeCycle /* ------------------------------------------------------------ */ /** * @return The port to use when redirecting a request if a data constraint of confidential is - * required. See {@link org.eclipse.jetty.http.security.Constraint#getDataConstraint()} + * required. See {@link org.eclipse.jetty.util.security.Constraint#getDataConstraint()} */ int getConfidentialPort(); @@ -164,7 +164,7 @@ public interface Connector extends LifeCycle /* ------------------------------------------------------------ */ /** * @return The schema to use when redirecting a request if a data constraint of confidential is - * required. See {@link org.eclipse.jetty.http.security.Constraint#getDataConstraint()} + * required. See {@link org.eclipse.jetty.util.security.Constraint#getDataConstraint()} */ String getConfidentialScheme(); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java index 5422cb2662c..99c9ec33127 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java @@ -130,7 +130,7 @@ public class Dispatcher implements RequestDispatcher */ public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException { - Request baseRequest=(request instanceof Request)?((Request)request):HttpConnection.getCurrentConnection().getRequest(); + Request baseRequest=(request instanceof Request)?((Request)request):AbstractHttpConnection.getCurrentConnection().getRequest(); request.removeAttribute(__JSP_FILE); // TODO remove when glassfish 1044 is fixed if (!(request instanceof HttpServletRequest)) @@ -211,7 +211,7 @@ public class Dispatcher implements RequestDispatcher */ protected void forward(ServletRequest request, ServletResponse response, DispatcherType dispatch) throws ServletException, IOException { - Request baseRequest=(request instanceof Request)?((Request)request):HttpConnection.getCurrentConnection().getRequest(); + Request baseRequest=(request instanceof Request)?((Request)request):AbstractHttpConnection.getCurrentConnection().getRequest(); Response base_response=baseRequest.getResponse(); response.resetBuffer(); base_response.fwdReset(); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Handler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Handler.java index 519c8730c54..cf16c0b14a7 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Handler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Handler.java @@ -47,10 +47,10 @@ public interface Handler extends LifeCycle, Destroyable * @param target The target of the request - either a URI or a name. * @param baseRequest The original unwrapped request object. * @param request The request either as the {@link Request} - * object or a wrapper of that request. The {@link HttpConnection#getCurrentConnection()} + * object or a wrapper of that request. The {@link AbstractHttpConnection#getCurrentConnection()} * method can be used access the Request object if required. * @param response The response as the {@link Response} - * object or a wrapper of that request. The {@link HttpConnection#getCurrentConnection()} + * object or a wrapper of that request. The {@link AbstractHttpConnection#getCurrentConnection()} * method can be used access the Response object if required. * @throws IOException * @throws ServletException diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java index 670fa95c81a..abaec917d7e 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java @@ -22,11 +22,11 @@ import org.eclipse.jetty.io.Buffer; public class HttpInput extends ServletInputStream { - protected final HttpConnection _connection; + protected final AbstractHttpConnection _connection; protected final HttpParser _parser; /* ------------------------------------------------------------ */ - public HttpInput(HttpConnection connection) + public HttpInput(AbstractHttpConnection connection) { _connection=connection; _parser=(HttpParser)connection.getParser(); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java index 6d595877851..7f5d2628b4d 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java @@ -36,7 +36,7 @@ import org.eclipse.jetty.util.ByteArrayOutputStream2; */ public class HttpOutput extends ServletOutputStream { - protected final HttpConnection _connection; + protected final AbstractHttpConnection _connection; protected final AbstractGenerator _generator; private boolean _closed; @@ -47,7 +47,7 @@ public class HttpOutput extends ServletOutputStream ByteArrayOutputStream2 _bytes; /* ------------------------------------------------------------ */ - public HttpOutput(HttpConnection connection) + public HttpOutput(AbstractHttpConnection connection) { _connection=connection; _generator=(AbstractGenerator)connection.getGenerator(); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/LocalConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/LocalConnector.java index d4849acb122..149b1bfbc19 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/LocalConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/LocalConnector.java @@ -110,13 +110,14 @@ public class LocalConnector extends AbstractConnector @Override public void setConnection(Connection connection) { - connectionUpgraded(getConnection(),connection); + if (getConnection()!=null && connection!=getConnection()) + connectionUpgraded(getConnection(),connection); super.setConnection(connection); } }; endPoint.setGrowOutput(true); - HttpConnection connection = new BlockingHttpConnection(LocalConnector.this, endPoint, getServer()); + AbstractHttpConnection connection = new BlockingHttpConnection(LocalConnector.this, endPoint, getServer()); endPoint.setConnection(connection); connectionOpened(connection); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/NCSARequestLog.java b/jetty-server/src/main/java/org/eclipse/jetty/server/NCSARequestLog.java index 4382d46cc8d..7a8e8215ca4 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/NCSARequestLog.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/NCSARequestLog.java @@ -629,7 +629,7 @@ public class NCSARequestLog extends AbstractLifeCycle implements RequestLog * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart() */ @Override - protected void doStart() throws Exception + protected synchronized void doStart() throws Exception { if (_logDateFormat != null) { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java index 38f3b460d23..84ec0e24495 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java @@ -96,7 +96,7 @@ import org.eclipse.jetty.util.log.Logger; * against the servlet URL patterns and {@link Request#setServletPath(String)} called as a result. * * - * A request instance is created for each {@link HttpConnection} accepted by the server + * A request instance is created for each {@link AbstractHttpConnection} accepted by the server * and recycled for each HTTP request received via that connection. An effort is made * to avoid reparsing headers and cookies that are likely to be the same for * requests from the same connection. @@ -116,7 +116,7 @@ public class Request implements HttpServletRequest if (request instanceof Request) return (Request) request; - return HttpConnection.getCurrentConnection().getRequest(); + return AbstractHttpConnection.getCurrentConnection().getRequest(); } protected final AsyncContinuation _async = new AsyncContinuation(); private boolean _asyncSupported=true; @@ -124,7 +124,7 @@ public class Request implements HttpServletRequest private Authentication _authentication; private MultiMap _baseParameters; private String _characterEncoding; - protected HttpConnection _connection; + protected AbstractHttpConnection _connection; private ContextHandler.Context _context; private boolean _newContext; private String _contextPath; @@ -170,7 +170,7 @@ public class Request implements HttpServletRequest } /* ------------------------------------------------------------ */ - public Request(HttpConnection connection) + public Request(AbstractHttpConnection connection) { setConnection(connection); } @@ -382,7 +382,7 @@ public class Request implements HttpServletRequest /** * @return Returns the connection. */ - public HttpConnection getConnection() + public AbstractHttpConnection getConnection() { return _connection; } @@ -988,6 +988,9 @@ public class Request implements HttpServletRequest // Return already determined host if (_serverName != null) return _serverName; + + if (_uri == null) + throw new IllegalStateException("No uri"); // Return host from absolute URI _serverName = _uri.getHost(); @@ -1466,7 +1469,7 @@ public class Request implements HttpServletRequest { try { - ((HttpConnection.Output)getServletResponse().getOutputStream()).sendContent(value); + ((AbstractHttpConnection.Output)getServletResponse().getOutputStream()).sendContent(value); } catch (IOException e) { @@ -1483,7 +1486,7 @@ public class Request implements HttpServletRequest NIOBuffer buffer = byteBuffer.isDirect() ?new DirectNIOBuffer(byteBuffer,true) :new IndirectNIOBuffer(byteBuffer,true); - ((HttpConnection.Output)getServletResponse().getOutputStream()).sendResponse(buffer); + ((AbstractHttpConnection.Output)getServletResponse().getOutputStream()).sendResponse(buffer); } } catch (IOException e) @@ -1579,7 +1582,7 @@ public class Request implements HttpServletRequest /* ------------------------------------------------------------ */ //final so we can safely call this from constructor - protected final void setConnection(HttpConnection connection) + protected final void setConnection(AbstractHttpConnection connection) { _connection=connection; _async.setConnection(connection); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java index fb6196a2087..d5fa8321d62 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java @@ -53,6 +53,7 @@ public class Response implements HttpServletResponse { private static final Logger LOG = Log.getLogger(Response.class); + public static final int NONE=0, STREAM=1, @@ -65,7 +66,13 @@ public class Response implements HttpServletResponse */ public final static String SET_INCLUDE_HEADER_PREFIX = "org.eclipse.jetty.server.include."; - private final HttpConnection _connection; + /** + * If this string is found within the comment of a cookie added with {@link #addCookie(Cookie)}, then the cookie + * will be set as HTTP ONLY. + */ + public final static String HTTP_ONLY_COMMENT="__HTTP_ONLY__"; + + private final AbstractHttpConnection _connection; private int _status=SC_OK; private String _reason; private Locale _locale; @@ -81,7 +88,7 @@ public class Response implements HttpServletResponse /** * */ - public Response(HttpConnection connection) + public Response(AbstractHttpConnection connection) { _connection=connection; } @@ -120,14 +127,28 @@ public class Response implements HttpServletResponse */ public void addCookie(Cookie cookie) { + String comment=cookie.getComment(); + boolean http_only=false; + + if (comment!=null) + { + int i=comment.indexOf(HTTP_ONLY_COMMENT); + if (i>=0) + { + http_only=true; + comment=comment.substring(i,i+HTTP_ONLY_COMMENT.length()).trim(); + if (comment.length()==0) + comment=null; + } + } _connection.getResponseFields().addSetCookie(cookie.getName(), cookie.getValue(), cookie.getDomain(), cookie.getPath(), cookie.getMaxAge(), - cookie.getComment(), + comment, cookie.getSecure(), - false,//cookie.isHttpOnly(), + http_only,// || cookie.isHttpOnly(), cookie.getVersion()); } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java index 10cfd69efa3..98221fc2a65 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java @@ -76,6 +76,7 @@ public class Server extends HandlerWrapper implements Attributes private int _maxCookieVersion=1; private boolean _dumpAfterStart=false; private boolean _dumpBeforeStop=false; + private boolean _uncheckedPrintWriter=false; /* ------------------------------------------------------------ */ @@ -333,7 +334,7 @@ public class Server extends HandlerWrapper implements Attributes * or after the entire request has been received (for short requests of known length), or * on the dispatch of an async request. */ - public void handle(HttpConnection connection) throws IOException, ServletException + public void handle(AbstractHttpConnection connection) throws IOException, ServletException { final String target=connection.getRequest().getPathInfo(); final Request request=connection.getRequest(); @@ -355,7 +356,7 @@ public class Server extends HandlerWrapper implements Attributes * or after the entire request has been received (for short requests of known length), or * on the dispatch of an async request. */ - public void handleAsync(HttpConnection connection) throws IOException, ServletException + public void handleAsync(AbstractHttpConnection connection) throws IOException, ServletException { final AsyncContinuation async = connection.getRequest().getAsyncContinuation(); final AsyncContinuation.AsyncEventState state = async.getAsyncEventState(); @@ -614,6 +615,20 @@ public class Server extends HandlerWrapper implements Attributes dumpThis(out); dump(out,indent,TypeUtil.asList(getHandlers()),getBeans(),TypeUtil.asList(_connectors)); } + + + /* ------------------------------------------------------------ */ + public boolean isUncheckedPrintWriter() + { + return _uncheckedPrintWriter; + } + + /* ------------------------------------------------------------ */ + public void setUncheckedPrintWriter(boolean unchecked) + { + _uncheckedPrintWriter=unchecked; + } + /* ------------------------------------------------------------ */ /* A handler that can be gracefully shutdown. diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/bio/SocketConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/bio/SocketConnector.java index 9bff3cfa9ac..28efa1bef72 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/bio/SocketConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/bio/SocketConnector.java @@ -19,7 +19,6 @@ import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.util.HashSet; -import java.util.Iterator; import java.util.Set; import org.eclipse.jetty.http.HttpException; @@ -30,9 +29,10 @@ import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EofException; import org.eclipse.jetty.io.bio.SocketEndPoint; import org.eclipse.jetty.server.AbstractConnector; +import org.eclipse.jetty.server.AbstractHttpConnection; import org.eclipse.jetty.server.BlockingHttpConnection; -import org.eclipse.jetty.server.HttpConnection; import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.util.component.AggregateLifeCycle; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -156,21 +156,30 @@ public class SocketConnector extends AbstractConnector protected void doStop() throws Exception { super.doStop(); - Set set=null; - + Set set = new HashSet(); synchronized(_connections) { - set= new HashSet(_connections); + set.addAll(_connections); } - - Iterator iter=set.iterator(); - while(iter.hasNext()) + for (EndPoint endPoint : set) { - ConnectorEndPoint connection = (ConnectorEndPoint)iter.next(); + ConnectorEndPoint connection = (ConnectorEndPoint)endPoint; connection.close(); } } + @Override + public void dump(Appendable out, String indent) throws IOException + { + super.dump(out, indent); + Set connections = new HashSet(); + synchronized (_connections) + { + connections.addAll(_connections); + } + AggregateLifeCycle.dump(out, indent, connections); + } + /* ------------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------------- */ @@ -193,7 +202,7 @@ public class SocketConnector extends AbstractConnector public void setConnection(Connection connection) { - if (_connection!=connection) + if (_connection!=connection && _connection!=null) connectionUpgraded(_connection,connection); _connection=connection; } @@ -219,8 +228,8 @@ public class SocketConnector extends AbstractConnector @Override public void close() throws IOException { - if (_connection instanceof HttpConnection) - ((HttpConnection)_connection).getRequest().getAsyncContinuation().cancel(); + if (_connection instanceof AbstractHttpConnection) + ((AbstractHttpConnection)_connection).getRequest().getAsyncContinuation().cancel(); super.close(); } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ConnectHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ConnectHandler.java index 47c086b0170..9ddd15bb252 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ConnectHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ConnectHandler.java @@ -10,21 +10,24 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.http.HttpMethods; import org.eclipse.jetty.http.HttpParser; +import org.eclipse.jetty.io.AsyncEndPoint; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.ConnectedEndPoint; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.nio.AsyncConnection; import org.eclipse.jetty.io.nio.IndirectNIOBuffer; import org.eclipse.jetty.io.nio.SelectChannelEndPoint; import org.eclipse.jetty.io.nio.SelectorManager; +import org.eclipse.jetty.server.AbstractHttpConnection; import org.eclipse.jetty.server.Handler; -import org.eclipse.jetty.server.HttpConnection; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.HostMap; @@ -32,7 +35,6 @@ import org.eclipse.jetty.util.TypeUtil; 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.log.Logger; import org.eclipse.jetty.util.thread.ThreadPool; /** @@ -231,7 +233,7 @@ public class ConnectHandler extends HandlerWrapper // 1. when this unread data is written and the server replies before the clientToProxy // connection is installed (it is only installed after returning from this method) // 2. when the client sends data before this unread data has been written. - HttpConnection httpConnection = HttpConnection.getCurrentConnection(); + AbstractHttpConnection httpConnection = AbstractHttpConnection.getCurrentConnection(); Buffer headerBuffer = ((HttpParser)httpConnection.getParser()).getHeaderBuffer(); Buffer bodyBuffer = ((HttpParser)httpConnection.getParser()).getBodyBuffer(); int length = headerBuffer == null ? 0 : headerBuffer.length(); @@ -271,7 +273,7 @@ public class ConnectHandler extends HandlerWrapper private ClientToProxyConnection prepareConnections(ConcurrentMap context, SocketChannel channel, Buffer buffer) { - HttpConnection httpConnection = HttpConnection.getCurrentConnection(); + AbstractHttpConnection httpConnection = AbstractHttpConnection.getCurrentConnection(); ProxyToServerConnection proxyToServer = newProxyToServerConnection(context, buffer); ClientToProxyConnection clientToProxy = newClientToProxyConnection(context, channel, httpConnection.getEndPoint(), httpConnection.getTimeStamp()); clientToProxy.setConnection(proxyToServer); @@ -421,17 +423,18 @@ public class ConnectHandler extends HandlerWrapper private class Manager extends SelectorManager { @Override - protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey selectionKey) throws IOException + protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey key) throws IOException { - SelectChannelEndPoint endp = new SelectChannelEndPoint(channel, selectSet, selectionKey); + SelectChannelEndPoint endp = new SelectChannelEndPoint(channel, selectSet, key, channel.socket().getSoTimeout()); + endp.setConnection(selectSet.getManager().newConnection(channel,endp, key.attachment())); endp.setMaxIdleTime(_writeTimeout); return endp; } @Override - protected Connection newConnection(SocketChannel channel, SelectChannelEndPoint endpoint) + public AsyncConnection newConnection(SocketChannel channel, AsyncEndPoint endpoint, Object attachment) { - ProxyToServerConnection proxyToServer = (ProxyToServerConnection)endpoint.getSelectionKey().attachment(); + ProxyToServerConnection proxyToServer = (ProxyToServerConnection)attachment; proxyToServer.setTimeStamp(System.currentTimeMillis()); proxyToServer.setEndPoint(endpoint); return proxyToServer; @@ -461,7 +464,9 @@ public class ConnectHandler extends HandlerWrapper } } - public class ProxyToServerConnection implements Connection + + + public class ProxyToServerConnection implements AsyncConnection { private final CountDownLatch _ready = new CountDownLatch(1); private final Buffer _buffer = new IndirectNIOBuffer(1024); @@ -469,7 +474,7 @@ public class ConnectHandler extends HandlerWrapper private volatile Buffer _data; private volatile ClientToProxyConnection _toClient; private volatile long _timestamp; - private volatile SelectChannelEndPoint _endPoint; + private volatile AsyncEndPoint _endPoint; public ProxyToServerConnection(ConcurrentMap context, Buffer data) { @@ -541,6 +546,11 @@ public class ConnectHandler extends HandlerWrapper } } + public void onInputShutdown() throws IOException + { + // TODO + } + private void writeData() throws IOException { // This method is called from handle() and closeServer() @@ -581,7 +591,7 @@ public class ConnectHandler extends HandlerWrapper _timestamp = timestamp; } - public void setEndPoint(SelectChannelEndPoint endpoint) + public void setEndPoint(AsyncEndPoint endpoint) { _endPoint = endpoint; } @@ -596,7 +606,7 @@ public class ConnectHandler extends HandlerWrapper return false; } - public void closed() + public void onClose() { } @@ -657,7 +667,7 @@ public class ConnectHandler extends HandlerWrapper _endPoint.shutdownOutput(); } - public void idleExpired() + public void onIdleExpired(long idleForMs) { try { @@ -671,7 +681,7 @@ public class ConnectHandler extends HandlerWrapper } } - public class ClientToProxyConnection implements Connection + public class ClientToProxyConnection implements AsyncConnection { private final Buffer _buffer = new IndirectNIOBuffer(1024); private final ConcurrentMap _context; @@ -758,6 +768,11 @@ public class ConnectHandler extends HandlerWrapper _logger.debug("{}: end reading from client", this); } } + + public void onInputShutdown() throws IOException + { + // TODO + } public long getTimeStamp() { @@ -774,7 +789,7 @@ public class ConnectHandler extends HandlerWrapper return false; } - public void closed() + public void onClose() { } @@ -819,7 +834,7 @@ public class ConnectHandler extends HandlerWrapper _endPoint.shutdownOutput(); } - public void idleExpired() + public void onIdleExpired(long idleForMs) { try { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java index 46554a844bc..8af31771943 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java @@ -48,11 +48,11 @@ import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.http.HttpException; import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.io.Buffer; +import org.eclipse.jetty.server.AbstractHttpConnection; import org.eclipse.jetty.server.Dispatcher; import org.eclipse.jetty.server.DispatcherType; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.HandlerContainer; -import org.eclipse.jetty.server.HttpConnection; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.Attributes; @@ -65,7 +65,6 @@ import org.eclipse.jetty.util.component.AggregateLifeCycle; import org.eclipse.jetty.util.component.Dumpable; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; -import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; /* ------------------------------------------------------------ */ @@ -264,6 +263,85 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } } + /* ------------------------------------------------------------ */ + /** Either set virtual hosts or add to an existing set of virtual hosts. + * + * @param virtualHosts + * Array of virtual hosts that this context responds to. A null host name or null/empty array means any hostname is acceptable. Host names may be + * String representation of IP addresses. Host names may start with '*.' to wildcard one level of names. + */ + public void addVirtualHosts(String[] virtualHosts) + { + if (virtualHosts == null) // since this is add, we don't null the old ones + { + return; + } + else + { + List currentVirtualHosts = null; + if (_vhosts != null) + { + currentVirtualHosts = new ArrayList(Arrays.asList(_vhosts)); + } + else + { + currentVirtualHosts = new ArrayList(); + } + + for (int i = 0; i < virtualHosts.length; i++) + { + String normVhost = normalizeHostname(virtualHosts[i]); + if (!currentVirtualHosts.contains(normVhost)) + { + currentVirtualHosts.add(normVhost); + } + } + _vhosts = currentVirtualHosts.toArray(new String[0]); + } + } + + /* ------------------------------------------------------------ */ + /** + * Removes an array of virtual host entries, if this removes all entries the _vhosts will be set to null + * + * @param virtualHosts + * Array of virtual hosts that this context responds to. A null host name or null/empty array means any hostname is acceptable. Host names may be + * String representation of IP addresses. Host names may start with '*.' to wildcard one level of names. + */ + public void removeVirtualHosts(String[] virtualHosts) + { + if (virtualHosts == null) + { + return; // do nothing + } + else if ( _vhosts == null || _vhosts.length == 0) + { + return; // do nothing + } + else + { + List existingVirtualHosts = new ArrayList(Arrays.asList(_vhosts)); + + for (int i = 0; i < virtualHosts.length; i++) + { + String toRemoveVirtualHost = normalizeHostname(virtualHosts[i]); + if (existingVirtualHosts.contains(toRemoveVirtualHost)) + { + existingVirtualHosts.remove(toRemoveVirtualHost); + } + } + + if (existingVirtualHosts.isEmpty()) + { + _vhosts = null; // if we ended up removing them all, just null out _vhosts + } + else + { + _vhosts = existingVirtualHosts.toArray(new String[0]); + } + } + } + /* ------------------------------------------------------------ */ /** * Get the virtual hosts for the context. Only requests that have a matching host header or fully qualified URL will be passed to that context with a @@ -498,7 +576,10 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. */ public boolean isShutdown() { - return !_shutdown; + synchronized (this) + { + return !_shutdown; + } } /* ------------------------------------------------------------ */ @@ -524,7 +605,10 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. */ public boolean isAvailable() { - return _available; + synchronized (this) + { + return _available; + } } /* ------------------------------------------------------------ */ @@ -588,7 +672,10 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. // defers the calling of super.doStart() startContext(); - _availability = _shutdown?__SHUTDOWN:_available?__AVAILABLE:__UNAVAILABLE; + synchronized(this) + { + _availability = _shutdown?__SHUTDOWN:_available?__AVAILABLE:__UNAVAILABLE; + } } finally { @@ -755,7 +842,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. // Check the connector if (_connectors != null && _connectors.size() > 0) { - String connector = HttpConnection.getCurrentConnection().getConnector().getName(); + String connector = AbstractHttpConnection.getCurrentConnection().getConnector().getName(); if (connector == null || !_connectors.contains(connector)) return false; } @@ -793,7 +880,8 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. @Override public void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { - LOG.debug("scope {} @ {}",baseRequest.getContextPath() + "|" + baseRequest.getServletPath() + "|" + baseRequest.getPathInfo(),this); + if (LOG.isDebugEnabled()) + LOG.debug("scope {}|{}|{} @ {}",baseRequest.getContextPath(),baseRequest.getServletPath(),baseRequest.getPathInfo(),this); Context old_context = null; String old_context_path = null; @@ -865,7 +953,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } if (LOG.isDebugEnabled()) - LOG.debug("context={} @ {}",baseRequest.getContextPath() + "|" + baseRequest.getServletPath() + "|" + baseRequest.getPathInfo(),this); + LOG.debug("context={}|{}|{} @ {}",baseRequest.getContextPath(),baseRequest.getServletPath(), baseRequest.getPathInfo(),this); // start manual inline of nextScope(target,baseRequest,request,response); if (never()) @@ -1699,7 +1787,8 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. URL url = getResource(path); if (url == null) return null; - return url.openStream(); + Resource r = Resource.newResource(url); + return r.getInputStream(); } catch (Exception e) { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DefaultHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DefaultHandler.java index 5f424466636..6ac3dcc2ff2 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DefaultHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DefaultHandler.java @@ -29,9 +29,9 @@ import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.ByteArrayISO8859Writer; import org.eclipse.jetty.util.IO; -import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.resource.Resource; /* ------------------------------------------------------------ */ @@ -61,7 +61,10 @@ public class DefaultHandler extends AbstractHandler { URL fav = this.getClass().getClassLoader().getResource("org/eclipse/jetty/favicon.ico"); if (fav!=null) - _favicon=IO.readBytes(fav.openStream()); + { + Resource r = Resource.newResource(fav); + _favicon=IO.readBytes(r.getInputStream()); + } } catch(Exception e) { @@ -110,10 +113,6 @@ public class DefaultHandler extends AbstractHandler response.setContentType(MimeTypes.TEXT_HTML); ByteArrayISO8859Writer writer = new ByteArrayISO8859Writer(1500); - - String uri=request.getRequestURI(); - uri=StringUtil.replace(uri,"<","<"); - uri=StringUtil.replace(uri,">",">"); writer.write("\n\nError 404 - Not Found"); writer.write("\n\n

    Error 404 - Not Found.

    \n"); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorHandler.java index 6a422e9be84..b43e34b9cef 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorHandler.java @@ -23,7 +23,7 @@ import org.eclipse.jetty.http.HttpHeaders; import org.eclipse.jetty.http.HttpMethods; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.MimeTypes; -import org.eclipse.jetty.server.HttpConnection; +import org.eclipse.jetty.server.AbstractHttpConnection; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.util.ByteArrayISO8859Writer; @@ -45,7 +45,7 @@ public class ErrorHandler extends AbstractHandler */ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException { - HttpConnection connection = HttpConnection.getCurrentConnection(); + AbstractHttpConnection connection = AbstractHttpConnection.getCurrentConnection(); connection.getRequest().setHandled(true); String method = request.getMethod(); if(!method.equals(HttpMethods.GET) && !method.equals(HttpMethods.POST) && !method.equals(HttpMethods.HEAD)) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/GzipHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/GzipHandler.java index 29a9a833932..81198f5299d 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/GzipHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/GzipHandler.java @@ -281,9 +281,9 @@ public class GzipHandler extends HandlerWrapper return new GzipResponseWrapper(request, response) { { - setMimeTypes(GzipHandler.this._mimeTypes); - setBufferSize(GzipHandler.this._bufferSize); - setMinGzipSize(GzipHandler.this._minGzipSize); + super.setMimeTypes(GzipHandler.this._mimeTypes); + super.setBufferSize(GzipHandler.this._bufferSize); + super.setMinGzipSize(GzipHandler.this._minGzipSize); } @Override diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerWrapper.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerWrapper.java index 2d597ed5c30..de8c0e4a2e0 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerWrapper.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerWrapper.java @@ -14,6 +14,7 @@ package org.eclipse.jetty.server.handler; import java.io.IOException; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IPAccessHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IPAccessHandler.java index 060e3288323..4e6e50b52c6 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IPAccessHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IPAccessHandler.java @@ -25,7 +25,7 @@ import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.PathMap; import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.server.HttpConnection; +import org.eclipse.jetty.server.AbstractHttpConnection; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.util.IPAddressMap; import org.eclipse.jetty.util.log.Log; @@ -179,7 +179,7 @@ public class IPAccessHandler extends HandlerWrapper public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // Get the real remote IP (not the one set by the forwarded headers (which may be forged)) - HttpConnection connection = baseRequest.getConnection(); + AbstractHttpConnection connection = baseRequest.getConnection(); if (connection!=null) { EndPoint endp=connection.getEndPoint(); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/MovedContextHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/MovedContextHandler.java index 8650b80c9a1..6ff990c9c04 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/MovedContextHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/MovedContextHandler.java @@ -100,12 +100,6 @@ public class MovedContextHandler extends ContextHandler { if (_newContextURL==null) return; - - String url = _newContextURL; - if (!_discardPathInfo && request.getPathInfo()!=null) - url=URIUtil.addPaths(url, request.getPathInfo()); - if (!_discardQuery && request.getQueryString()!=null) - url+="?"+request.getQueryString(); String path=_newContextURL; if (!_discardPathInfo && request.getPathInfo()!=null) @@ -117,7 +111,9 @@ public class MovedContextHandler extends ContextHandler if (!_discardQuery && request.getQueryString()!=null) { location.append('?'); - location.append(request.getQueryString()); + String q=request.getQueryString(); + q=q.replaceAll("\r\n?&=","!"); + location.append(q); } response.setHeader(HttpHeaders.LOCATION,location.toString()); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java index 04a6ff9566e..f4978c67494 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java @@ -29,8 +29,8 @@ import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.ByteArrayBuffer; import org.eclipse.jetty.io.WriterOutputStream; +import org.eclipse.jetty.server.AbstractHttpConnection; import org.eclipse.jetty.server.Dispatcher; -import org.eclipse.jetty.server.HttpConnection; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Response; import org.eclipse.jetty.server.handler.ContextHandler.Context; @@ -51,7 +51,7 @@ import org.eclipse.jetty.util.resource.Resource; * * @org.apache.xbean.XBean */ -public class ResourceHandler extends AbstractHandler +public class ResourceHandler extends HandlerWrapper { private static final Logger LOG = Log.getLogger(ResourceHandler.class); @@ -309,7 +309,6 @@ public class ResourceHandler extends AbstractHandler } else { - included = Boolean.FALSE; servletPath = request.getServletPath(); pathInfo = request.getPathInfo(); } @@ -359,6 +358,8 @@ public class ResourceHandler extends AbstractHandler { if(!HttpMethods.HEAD.equals(request.getMethod())) { + //try another handler + super.handle(target, baseRequest, request, response); return; } skipContentBody = true; @@ -369,12 +370,18 @@ public class ResourceHandler extends AbstractHandler if (resource==null || !resource.exists()) { if (target.endsWith("/jetty-dir.css")) - { - response.setContentType("text/css"); + { resource = getStylesheet(); + if (resource==null) + return; + response.setContentType("text/css"); } else + { + //no resource - try other handlers + super.handle(target, baseRequest, request, response); return; + } } if (!_aliases && resource.getAlias()!=null) @@ -432,10 +439,10 @@ public class ResourceHandler extends AbstractHandler catch(IllegalStateException e) {out = new WriterOutputStream(response.getWriter());} // See if a short direct method can be used? - if (out instanceof HttpConnection.Output) + if (out instanceof AbstractHttpConnection.Output) { // TODO file mapped buffers - ((HttpConnection.Output)out).sendContent(resource.getInputStream()); + ((AbstractHttpConnection.Output)out).sendContent(resource.getInputStream()); } else { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ShutdownHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ShutdownHandler.java index 701d774b557..aa4edeb7f61 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ShutdownHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ShutdownHandler.java @@ -68,6 +68,8 @@ public class ShutdownHandler extends AbstractHandler private final Server _server; private boolean _exitJvm = false; + + /** * Creates a listener that lets the server be shut down remotely (but only from localhost). @@ -110,14 +112,24 @@ public class ShutdownHandler extends AbstractHandler LOG.info("Shutting down by request from " + getRemoteAddr(request)); - try + new Thread() { - shutdownServer(); - } - catch (Exception e) - { - throw new RuntimeException("Shutting down server",e); - } + public void run () + { + try + { + shutdownServer(); + } + catch (InterruptedException e) + { + LOG.ignore(e); + } + catch (Exception e) + { + throw new RuntimeException("Shutting down server",e); + } + } + }.start(); } private boolean requestFromLocalhost(HttpServletRequest request) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/nio/BlockingChannelConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/nio/BlockingChannelConnector.java index 69639a9d631..98716285b71 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/nio/BlockingChannelConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/nio/BlockingChannelConnector.java @@ -4,13 +4,13 @@ // 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 +// 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. +// You may elect to redistribute this code under either of these licenses. // ======================================================================== - + package org.eclipse.jetty.server.nio; import java.io.IOException; @@ -29,7 +29,6 @@ import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EofException; import org.eclipse.jetty.io.nio.ChannelEndPoint; import org.eclipse.jetty.server.BlockingHttpConnection; -import org.eclipse.jetty.server.HttpConnection; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.util.ConcurrentHashSet; import org.eclipse.jetty.util.log.Log; @@ -40,25 +39,25 @@ import org.eclipse.jetty.util.log.Logger; /** Blocking NIO connector. * This connector uses efficient NIO buffers with a traditional blocking thread model. * Direct NIO buffers are used and a thread is allocated per connections. - * + * * This connector is best used when there are a few very active connections. - * + * * @org.apache.xbean.XBean element="blockingNioConnector" description="Creates a blocking NIO based socket connector" - * - * + * + * * */ -public class BlockingChannelConnector extends AbstractNIOConnector +public class BlockingChannelConnector extends AbstractNIOConnector { private static final Logger LOG = Log.getLogger(BlockingChannelConnector.class); private transient ServerSocketChannel _acceptChannel; private final Set _endpoints = new ConcurrentHashSet(); - - + + /* ------------------------------------------------------------ */ /** Constructor. - * + * */ public BlockingChannelConnector() { @@ -104,12 +103,12 @@ public class BlockingChannelConnector extends AbstractNIOConnector } } } - + }); - + } - + /* ------------------------------------------------------------ */ public void open() throws IOException { @@ -129,12 +128,12 @@ public class BlockingChannelConnector extends AbstractNIOConnector _acceptChannel.close(); _acceptChannel=null; } - + /* ------------------------------------------------------------ */ @Override public void accept(int acceptorID) throws IOException, InterruptedException - { + { SocketChannel channel = _acceptChannel.accept(); channel.configureBlocking(true); Socket socket=channel.socket(); @@ -143,7 +142,7 @@ public class BlockingChannelConnector extends AbstractNIOConnector BlockingChannelEndPoint connection=new BlockingChannelEndPoint(channel); connection.dispatch(); } - + /* ------------------------------------------------------------------------------- */ @Override public void customize(EndPoint endpoint, Request request) @@ -162,7 +161,7 @@ public class BlockingChannelConnector extends AbstractNIOConnector return -1; return _acceptChannel.socket().getLocalPort(); } - + /* ------------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------------- */ @@ -171,14 +170,14 @@ public class BlockingChannelConnector extends AbstractNIOConnector private Connection _connection; private int _timeout; private volatile long _idleTimestamp; - - BlockingChannelEndPoint(ByteChannel channel) + + BlockingChannelEndPoint(ByteChannel channel) throws IOException { super(channel,BlockingChannelConnector.this._maxIdleTime); _connection = new BlockingHttpConnection(BlockingChannelConnector.this,this,getServer()); } - + /* ------------------------------------------------------------ */ /** Get the connection. * @return the connection @@ -187,7 +186,7 @@ public class BlockingChannelConnector extends AbstractNIOConnector { return _connection; } - + /* ------------------------------------------------------------ */ public void setConnection(Connection connection) { @@ -208,24 +207,24 @@ public class BlockingChannelConnector extends AbstractNIOConnector { try { - close(); + super.close(); } catch (IOException e) { LOG.ignore(e); } } - + /* ------------------------------------------------------------ */ void dispatch() throws IOException { if (!getThreadPool().dispatch(this)) { LOG.warn("dispatch failed for {}",_connection); - BlockingChannelEndPoint.this.close(); + super.close(); } } - + /* ------------------------------------------------------------ */ /** * @see org.eclipse.jetty.io.nio.ChannelEndPoint#fill(org.eclipse.jetty.io.Buffer) @@ -289,9 +288,9 @@ public class BlockingChannelConnector extends AbstractNIOConnector _timeout=getMaxIdleTime(); } } - + _connection = _connection.handle(); - + } } catch (EofException e) @@ -303,27 +302,27 @@ public class BlockingChannelConnector extends AbstractNIOConnector catch (HttpException e) { LOG.debug("BAD", e); - try{BlockingChannelEndPoint.this.close();} + try{super.close();} catch(IOException e2){LOG.ignore(e2);} } catch(Throwable e) { LOG.warn("handle failed",e); - try{BlockingChannelEndPoint.this.close();} + try{super.close();} catch(IOException e2){LOG.ignore(e2);} } finally { connectionClosed(_connection); _endpoints.remove(this); - + // wait for client to close, but if not, close ourselves. try { if (!_socket.isClosed()) { long timestamp=System.currentTimeMillis(); - int max_idle=getMaxIdleTime(); + int max_idle=getMaxIdleTime(); _socket.setSoTimeout(getMaxIdleTime()); int c=0; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/nio/NetworkTrafficSelectChannelConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/nio/NetworkTrafficSelectChannelConnector.java index efea044745b..072de4cf778 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/nio/NetworkTrafficSelectChannelConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/nio/NetworkTrafficSelectChannelConnector.java @@ -54,6 +54,7 @@ public class NetworkTrafficSelectChannelConnector extends SelectChannelConnector protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectorManager.SelectSet selectSet, SelectionKey key) throws IOException { NetworkTrafficSelectChannelEndPoint endPoint = new NetworkTrafficSelectChannelEndPoint(channel, selectSet, key, _maxIdleTime, listeners); + endPoint.setConnection(selectSet.getManager().newConnection(channel,endPoint, key.attachment())); endPoint.notifyOpened(); return endPoint; } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/nio/SelectChannelConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/nio/SelectChannelConnector.java index b6c73f478a5..998dc76638d 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/nio/SelectChannelConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/nio/SelectChannelConnector.java @@ -22,22 +22,20 @@ import java.nio.channels.SocketChannel; import java.util.Arrays; import org.eclipse.jetty.continuation.Continuation; +import org.eclipse.jetty.io.AsyncEndPoint; import org.eclipse.jetty.io.ConnectedEndPoint; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.nio.AsyncConnection; import org.eclipse.jetty.io.nio.SelectChannelEndPoint; import org.eclipse.jetty.io.nio.SelectorManager; import org.eclipse.jetty.io.nio.SelectorManager.SelectSet; import org.eclipse.jetty.server.AsyncHttpConnection; -import org.eclipse.jetty.server.Connector; -import org.eclipse.jetty.server.HttpConnection; import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.component.AggregateLifeCycle; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.thread.ThreadPool; -import org.eclipse.jetty.util.thread.Timeout.Task; /* ------------------------------------------------------------------------------- */ /** @@ -91,10 +89,15 @@ public class SelectChannelConnector extends AbstractNIOConnector @Override public void accept(int acceptorID) throws IOException { - ServerSocketChannel server = _acceptChannel; - if (server!=null && server.isOpen()) + ServerSocketChannel server; + synchronized(this) { - SocketChannel channel = _acceptChannel.accept(); + server = _acceptChannel; + } + + if (server!=null && server.isOpen() && _manager.isStarted()) + { + SocketChannel channel = server.accept(); channel.configureBlocking(false); Socket socket = channel.socket(); configure(socket); @@ -118,9 +121,9 @@ public class SelectChannelConnector extends AbstractNIOConnector @Override public void customize(EndPoint endpoint, Request request) throws IOException { - SelectChannelEndPoint cep = ((SelectChannelEndPoint)endpoint); - cep.cancelIdle(); - request.setTimeStamp(cep.getSelectSet().getNow()); + AsyncEndPoint aEndp = ((AsyncEndPoint)endpoint); + aEndp.setCheckForIdle(false); + request.setTimeStamp(System.currentTimeMillis()); endpoint.setMaxIdleTime(_maxIdleTime); super.customize(endpoint, request); } @@ -129,7 +132,8 @@ public class SelectChannelConnector extends AbstractNIOConnector @Override public void persist(EndPoint endpoint) throws IOException { - ((SelectChannelEndPoint)endpoint).scheduleIdle(); + AsyncEndPoint aEndp = ((AsyncEndPoint)endpoint); + aEndp.setCheckForIdle(true); super.persist(endpoint); } @@ -138,9 +142,9 @@ public class SelectChannelConnector extends AbstractNIOConnector { return _manager; } - + /* ------------------------------------------------------------ */ - public Object getConnection() + public synchronized Object getConnection() { return _acceptChannel; } @@ -277,7 +281,9 @@ public class SelectChannelConnector extends AbstractNIOConnector /* ------------------------------------------------------------ */ protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey key) throws IOException { - return new SelectChannelEndPoint(channel,selectSet,key, SelectChannelConnector.this._maxIdleTime); + SelectChannelEndPoint endp= new SelectChannelEndPoint(channel,selectSet,key, SelectChannelConnector.this._maxIdleTime); + endp.setConnection(selectSet.getManager().newConnection(channel,endp, key.attachment())); + return endp; } /* ------------------------------------------------------------------------------- */ @@ -287,48 +293,24 @@ public class SelectChannelConnector extends AbstractNIOConnector } /* ------------------------------------------------------------------------------- */ - protected Connection newConnection(SocketChannel channel,final SelectChannelEndPoint endpoint) + protected AsyncConnection newConnection(SocketChannel channel,final AsyncEndPoint endpoint) { - return new SelectChannelHttpConnection(SelectChannelConnector.this,endpoint,getServer(),endpoint); + return new AsyncHttpConnection(SelectChannelConnector.this,endpoint,getServer()); } /* ------------------------------------------------------------ */ public void dump(Appendable out, String indent) throws IOException { - out.append(String.valueOf(this)).append("\n"); - ServerSocketChannel channel=_acceptChannel; + super.dump(out, indent); + ServerSocketChannel channel; + synchronized (this) + { + channel=_acceptChannel; + } if (channel==null) - AggregateLifeCycle.dump(out,indent,Arrays.asList(new Object[]{null,"CLOSED",_manager})); + AggregateLifeCycle.dump(out,indent,Arrays.asList(null,"CLOSED",_manager)); else - AggregateLifeCycle.dump(out,indent,Arrays.asList(new Object[]{_acceptChannel,_acceptChannel.isOpen()?"OPEN":"CLOSED",_manager})); - } - - /* ------------------------------------------------------------ */ - /* ------------------------------------------------------------ */ - /* ------------------------------------------------------------ */ - private class SelectChannelHttpConnection extends AsyncHttpConnection - { - private final SelectChannelEndPoint _endpoint; - - private SelectChannelHttpConnection(Connector connector, EndPoint endpoint, Server server, SelectChannelEndPoint endpoint2) - { - super(connector,endpoint,server); - _endpoint = endpoint2; - } - - /* ------------------------------------------------------------ */ - @Override - public void cancelTimeout(Task task) - { - _endpoint.getSelectSet().cancelTimeout(task); - } - - /* ------------------------------------------------------------ */ - @Override - public void scheduleTimeout(Task task, long timeoutMs) - { - _endpoint.getSelectSet().scheduleTimeout(task,timeoutMs); - } + AggregateLifeCycle.dump(out,indent,Arrays.asList(channel,channel.isOpen()?"OPEN":"CLOSED",_manager)); } /* ------------------------------------------------------------ */ @@ -365,7 +347,7 @@ public class SelectChannelConnector extends AbstractNIOConnector } @Override - protected Connection newConnection(SocketChannel channel,SelectChannelEndPoint endpoint) + public AsyncConnection newConnection(SocketChannel channel,AsyncEndPoint endpoint, Object attachment) { return SelectChannelConnector.this.newConnection(channel,endpoint); } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSession.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSession.java index 8712f766a59..73a4b6f81b8 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSession.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSession.java @@ -30,7 +30,7 @@ import org.eclipse.jetty.util.log.Logger; @SuppressWarnings("deprecation") public abstract class AbstractSession implements AbstractSessionManager.SessionIf { - final static Logger __log = SessionHandler.__log; + final static Logger LOG = SessionHandler.LOG; private final AbstractSessionManager _manager; private final String _clusterId; // ID unique within cluster @@ -63,7 +63,8 @@ public abstract class AbstractSession implements AbstractSessionManager.SessionI _lastAccessed=_created; _requests=1; _maxIdleMs=_manager._dftMaxIdleSecs>0?_manager._dftMaxIdleSecs*1000:-1; - __log.debug("new session & id "+_nodeId+" "+_clusterId); + if (LOG.isDebugEnabled()) + LOG.debug("new session & id "+_nodeId+" "+_clusterId); } /* ------------------------------------------------------------- */ @@ -76,7 +77,8 @@ public abstract class AbstractSession implements AbstractSessionManager.SessionI _accessed=accessed; _lastAccessed=accessed; _requests=1; - __log.debug("new session "+_nodeId+" "+_clusterId); + if (LOG.isDebugEnabled()) + LOG.debug("new session "+_nodeId+" "+_clusterId); } /* ------------------------------------------------------------- */ @@ -300,7 +302,7 @@ public abstract class AbstractSession implements AbstractSessionManager.SessionI { try { - __log.debug("invalidate ",_clusterId); + LOG.debug("invalidate {}",_clusterId); if (isValid()) clearAttributes(); } @@ -455,7 +457,10 @@ public abstract class AbstractSession implements AbstractSessionManager.SessionI /* ------------------------------------------------------------- */ protected void cookieSet() { - _cookieSet=_accessed; + synchronized (this) + { + _cookieSet=_accessed; + } } /* ------------------------------------------------------------ */ diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionIdManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionIdManager.java index d0c53610c5b..d93fc79c694 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionIdManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionIdManager.java @@ -78,7 +78,7 @@ public abstract class AbstractSessionIdManager extends AbstractLifeCycle impleme } /* ------------------------------------------------------------ */ - public void setRandom(Random random) + public synchronized void setRandom(Random random) { _random=random; _weakRandom=false; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java index 60d1f3de713..b6c1a381668 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java @@ -56,7 +56,7 @@ import org.eclipse.jetty.util.statistic.SampleStatistic; @SuppressWarnings("deprecation") public abstract class AbstractSessionManager extends AbstractLifeCycle implements SessionManager { - final static Logger __log = SessionHandler.__log; + final static Logger __log = SessionHandler.LOG; /* ------------------------------------------------------------ */ public final static int __distantFuture=60*60*24*7*52*20; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java index c015eb283f0..3b59213b516 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java @@ -4,11 +4,11 @@ // 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 +// 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. +// You may elect to redistribute this code under either of these licenses. // ======================================================================== package org.eclipse.jetty.server.session; @@ -42,12 +42,12 @@ import org.eclipse.jetty.util.log.Logger; *

    * This manager will create it's own Timer instance to scavenge threads, unless it discovers a shared Timer instance * set as the "org.eclipse.jetty.server.session.timer" attribute of the ContextHandler. - * + * */ public class HashSessionManager extends AbstractSessionManager { - final static Logger __log = SessionHandler.__log; - + final static Logger __log = SessionHandler.LOG; + protected final ConcurrentMap _sessions=new ConcurrentHashMap(); private static int __id; private Timer _timer; @@ -60,7 +60,7 @@ public class HashSessionManager extends AbstractSessionManager File _storeDir; private boolean _lazyLoad=false; private volatile boolean _sessionsLoaded=false; - + /* ------------------------------------------------------------ */ public HashSessionManager() { @@ -85,7 +85,7 @@ public class HashSessionManager extends AbstractSessionManager _timerStop=true; _timer=new Timer("HashSessionScavenger-"+__id++, true); } - + setScavengePeriod(getScavengePeriod()); if (_storeDir!=null) @@ -96,7 +96,7 @@ public class HashSessionManager extends AbstractSessionManager if (!_lazyLoad) restoreSessions(); } - + setSavePeriod(getSavePeriod()); } @@ -106,7 +106,7 @@ public class HashSessionManager extends AbstractSessionManager */ @Override public void doStop() throws Exception - { + { // stop the scavengers synchronized(this) { @@ -120,16 +120,16 @@ public class HashSessionManager extends AbstractSessionManager _timer.cancel(); _timer=null; } - - // This will callback invalidate sessions - where we decide if we will save + + // This will callback invalidate sessions - where we decide if we will save super.doStop(); - + _sessions.clear(); } /* ------------------------------------------------------------ */ - /** + /** * @return the period in seconds at which a check is made for sessions to be invalidated. */ public int getScavengePeriod() @@ -153,7 +153,7 @@ public class HashSessionManager extends AbstractSessionManager /* ------------------------------------------------------------ */ /** - * @return seconds Idle period after which a session is saved + * @return seconds Idle period after which a session is saved */ public int getIdleSavePeriod() { @@ -162,15 +162,15 @@ public class HashSessionManager extends AbstractSessionManager return _idleSavePeriodMs / 1000; } - + /* ------------------------------------------------------------ */ /** - * Configures the period in seconds after which a session is deemed idle and saved - * to save on session memory. - * - * The session is persisted, the values attribute map is cleared and the session set to idled. - * - * @param seconds Idle period after which a session is saved + * Configures the period in seconds after which a session is deemed idle and saved + * to save on session memory. + * + * The session is persisted, the values attribute map is cleared and the session set to idled. + * + * @param seconds Idle period after which a session is saved */ public void setIdleSavePeriod(int seconds) { @@ -196,7 +196,7 @@ public class HashSessionManager extends AbstractSessionManager if (period < 0) period=0; _savePeriodMs=period; - + if (_timer!=null) { synchronized (this) @@ -218,7 +218,7 @@ public class HashSessionManager extends AbstractSessionManager { __log.warn(e); } - } + } }; _timer.schedule(_saveTask,_savePeriodMs,_savePeriodMs); } @@ -234,10 +234,10 @@ public class HashSessionManager extends AbstractSessionManager { if (_savePeriodMs<=0) return 0; - + return _savePeriodMs/1000; } - + /* ------------------------------------------------------------ */ /** * @param seconds the period in seconds at which a check is made for sessions to be invalidated. @@ -267,13 +267,13 @@ public class HashSessionManager extends AbstractSessionManager public void run() { scavenge(); - } + } }; _timer.schedule(_task,_scavengePeriodMs,_scavengePeriodMs); } } } - + /* -------------------------------------------------------------- */ /** * Find sessions that have timed out and invalidate them. This runs in the @@ -284,14 +284,14 @@ public class HashSessionManager extends AbstractSessionManager //don't attempt to scavenge if we are shutting down if (isStopping() || isStopped()) return; - + Thread thread=Thread.currentThread(); ClassLoader old_loader=thread.getContextClassLoader(); try { if (_loader!=null) thread.setContextClassLoader(_loader); - + // For each session long now=System.currentTimeMillis(); for (Iterator i=_sessions.values().iterator(); i.hasNext();) @@ -305,23 +305,20 @@ public class HashSessionManager extends AbstractSessionManager } else if (_idleSavePeriodMs>0&&session.getAccessed()+_idleSavePeriodMs sessions=_sessions; if (sessions==null) return null; - + HashedSession session = sessions.get(idInCluster); if (session == null && _lazyLoad) @@ -359,7 +356,7 @@ public class HashSessionManager extends AbstractSessionManager if (_idleSavePeriodMs!=0) session.deIdle(); - + return session; } @@ -387,7 +384,7 @@ public class HashSessionManager extends AbstractSessionManager for (HashedSession session : sessions) session.invalidate(); } - + // check that no new sessions were created while we were iterating sessions=new ArrayList(_sessions.values()); } @@ -399,13 +396,13 @@ public class HashSessionManager extends AbstractSessionManager { return new HashedSession(this, request); } - + /* ------------------------------------------------------------ */ protected AbstractSession newSession(long created, long accessed, String clusterId) { return new HashedSession(this, created,accessed, clusterId); } - + /* ------------------------------------------------------------ */ @Override protected boolean removeSession(String clusterId) @@ -430,7 +427,7 @@ public class HashSessionManager extends AbstractSessionManager { _lazyLoad = lazyLoad; } - + /* ------------------------------------------------------------ */ public boolean isLazyLoad() { @@ -441,7 +438,7 @@ public class HashSessionManager extends AbstractSessionManager public void restoreSessions () throws Exception { _sessionsLoaded = true; - + if (_storeDir==null || !_storeDir.exists()) { return; @@ -468,9 +465,9 @@ public class HashSessionManager extends AbstractSessionManager File file = new File(_storeDir,idInCuster); if (file.exists()) { - FileInputStream in = new FileInputStream(file); + FileInputStream in = new FileInputStream(file); HashedSession session = restoreSession(in, null); - in.close(); + in.close(); addSession(session, false); session.didActivate(); file.delete(); @@ -483,7 +480,7 @@ public class HashSessionManager extends AbstractSessionManager } return null; } - + /* ------------------------------------------------------------ */ public void saveSessions(boolean reactivate) throws Exception { @@ -491,7 +488,7 @@ public class HashSessionManager extends AbstractSessionManager { return; } - + if (!_storeDir.canWrite()) { __log.warn ("Unable to save Sessions: Session persistence storage directory "+_storeDir.getAbsolutePath()+ " is not writeable"); @@ -506,7 +503,7 @@ public class HashSessionManager extends AbstractSessionManager public HashedSession restoreSession (InputStream is, HashedSession session) throws Exception { /* - * Take care of this class's fields first by calling + * Take care of this class's fields first by calling * defaultReadObject */ DataInputStream in = new DataInputStream(is); @@ -515,7 +512,7 @@ public class HashSessionManager extends AbstractSessionManager long created = in.readLong(); long accessed = in.readLong(); int requests = in.readInt(); - + if (session == null) session = (HashedSession)newSession(created, accessed, clusterId); session.setRequests(requests); @@ -536,7 +533,7 @@ public class HashSessionManager extends AbstractSessionManager return session; } - + /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ protected class ClassLoadingObjectInputStream extends ObjectInputStream diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashedSession.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashedSession.java index fd60ea8addc..acfc47f5f3f 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashedSession.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashedSession.java @@ -162,11 +162,8 @@ public class HashedSession extends AbstractSession // Access now to prevent race with idling period access(System.currentTimeMillis()); - if (LOG.isDebugEnabled()) - { LOG.debug("Deidling " + super.getId()); - } FileInputStream fis = null; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java index f9a01d68131..5280023e7d1 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java @@ -53,7 +53,7 @@ import org.eclipse.jetty.util.log.Logger; */ public class JDBCSessionIdManager extends AbstractSessionIdManager { - final static Logger LOG = SessionHandler.__log; + final static Logger LOG = SessionHandler.LOG; protected final HashSet _sessionIds = new HashSet(); protected Server _server; @@ -108,7 +108,7 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager throws SQLException { _dbName = dbMeta.getDatabaseProductName().toLowerCase(); - LOG.debug ("Using database "+_dbName); + LOG.debug ("Using database {}",_dbName); _isLower = dbMeta.storesLowerCaseIdentifiers(); _isUpper = dbMeta.storesUpperCaseIdentifiers(); } @@ -255,7 +255,8 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager if ((System.currentTimeMillis()%2) == 0) _scavengeIntervalMs += tenPercent; - if (LOG.isDebugEnabled()) LOG.debug("Scavenging every "+_scavengeIntervalMs+" ms"); + if (LOG.isDebugEnabled()) + LOG.debug("Scavenging every "+_scavengeIntervalMs+" ms"); if (_timer!=null && (period!=old_period || _task==null)) { synchronized (this) @@ -434,7 +435,8 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager initializeDatabase(); prepareTables(); super.doStart(); - if (LOG.isDebugEnabled()) LOG.debug("Scavenging interval = "+getScavengeInterval()+" sec"); + if (LOG.isDebugEnabled()) + LOG.debug("Scavenging interval = "+getScavengeInterval()+" sec"); _timer=new Timer("JDBCSessionScavenger", true); setScavengeInterval(getScavengeInterval()); } @@ -684,7 +686,8 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager List expiredSessionIds = new ArrayList(); try { - if (LOG.isDebugEnabled()) LOG.debug("Scavenge sweep started at "+System.currentTimeMillis()); + if (LOG.isDebugEnabled()) + LOG.debug("Scavenge sweep started at "+System.currentTimeMillis()); if (_lastScavengeTime > 0) { connection = getConnection(); @@ -693,7 +696,8 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager PreparedStatement statement = connection.prepareStatement(_selectExpiredSessions); long lowerBound = (_lastScavengeTime - _scavengeIntervalMs); long upperBound = _lastScavengeTime; - if (LOG.isDebugEnabled()) LOG.debug (" Searching for sessions expired between "+lowerBound + " and "+upperBound); + if (LOG.isDebugEnabled()) + LOG.debug (" Searching for sessions expired between "+lowerBound + " and "+upperBound); statement.setLong(1, lowerBound); statement.setLong(2, upperBound); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java index 0c15fb0df62..bcf443eb8d4 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java @@ -4,11 +4,11 @@ // 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 +// 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. +// You may elect to redistribute this code under either of these licenses. // ======================================================================== @@ -44,9 +44,9 @@ import org.eclipse.jetty.util.log.Logger; * JDBCSessionManager * * SessionManager that persists sessions to a database to enable clustering. - * + * * Session data is persisted to the JettySessions table: - * + * * rowId (unique in cluster: webapp name/path + virtualhost + sessionId) * contextPath (of the context owning the session) * sessionId (unique in a context) @@ -58,7 +58,7 @@ import org.eclipse.jetty.util.log.Logger; * lastSavedTime (last time in milliseconds session access times were saved) * expiryTime (time in milliseconds that the session is due to expire) * map (attribute map) - * + * * As an optimization, to prevent thrashing the database, we do not persist * the accessTime and lastAccessTime every time the session is accessed. Rather, * we write it out every so often. The frequency is controlled by the saveIntervalSec @@ -67,18 +67,18 @@ import org.eclipse.jetty.util.log.Logger; public class JDBCSessionManager extends AbstractSessionManager { private static final Logger LOG = Log.getLogger(JDBCSessionManager.class); - - protected String __insertSession; - protected String __deleteSession; - protected String __selectSession; - protected String __updateSession; - protected String __updateSessionNode; + + protected String __insertSession; + protected String __deleteSession; + protected String __selectSession; + protected String __updateSession; + protected String __updateSessionNode; protected String __updateSessionAccessTime; protected String __sessionTableRowId; - + private ConcurrentHashMap _sessions; protected long _saveIntervalSec = 60; //only persist changes to session access times every 60 secs - + /** * SessionData * @@ -108,7 +108,7 @@ public class JDBCSessionManager extends AbstractSessionManager _attributes = new HashMap(); _lastNode = getSessionIdManager().getWorkerName(); } - + public SessionData (String sessionId,Map attributes) { _id=sessionId; @@ -127,23 +127,23 @@ public class JDBCSessionManager extends AbstractSessionManager { return _created; } - + protected synchronized void setCreated (long ms) { _created = ms; } - + public synchronized long getAccessed () { return _accessed; } - + protected synchronized void setAccessed (long ms) { _accessed = ms; } - - + + public synchronized void setMaxIdleMs (long ms) { _maxIdleMs = ms; @@ -173,77 +173,77 @@ public class JDBCSessionManager extends AbstractSessionManager { return _cookieSet; } - + public synchronized void setRowId (String rowId) { _rowId=rowId; } - + protected synchronized String getRowId() { return _rowId; } - + protected synchronized Map getAttributeMap () { return _attributes; } - + protected synchronized void setAttributeMap (Map map) { _attributes = map; - } - + } + public synchronized void setLastNode (String node) { _lastNode=node; } - + public synchronized String getLastNode () { return _lastNode; } - + public synchronized void setCanonicalContext(String str) { _canonicalContext=str; } - + public synchronized String getCanonicalContext () { return _canonicalContext; } - + public synchronized long getLastSaved () { return _lastSaved; } - + public synchronized void setLastSaved (long time) { _lastSaved=time; } - + public synchronized void setExpiryTime (long time) { - _expiryTime=time; + _expiryTime=time; } - + public synchronized long getExpiryTime () { return _expiryTime; } - + public synchronized void setVirtualHost (String vhost) { _virtualHost=vhost; } - + public synchronized String getVirtualHost () { return _virtualHost; } - + @Override public String toString () { @@ -254,8 +254,8 @@ public class JDBCSessionManager extends AbstractSessionManager } } - - + + /** * Session * @@ -269,12 +269,12 @@ public class JDBCSessionManager extends AbstractSessionManager /** * Session from a request. - * + * * @param request */ protected Session (HttpServletRequest request) { - super(JDBCSessionManager.this,request); + super(JDBCSessionManager.this,request); _data = new SessionData(getClusterId(),_jdbcAttributes); if (_dftMaxIdleSecs>0) _data.setMaxIdleMs(_dftMaxIdleSecs*1000); @@ -297,7 +297,7 @@ public class JDBCSessionManager extends AbstractSessionManager _jdbcAttributes.putAll(_data.getAttributeMap()); _data.setAttributeMap(_jdbcAttributes); } - + @Override public void setAttribute (String name, Object value) { @@ -308,20 +308,20 @@ public class JDBCSessionManager extends AbstractSessionManager @Override public void removeAttribute (String name) { - super.removeAttribute(name); + super.removeAttribute(name); _dirty=true; } - + @Override protected void cookieSet() { _data.setCookieSet(_data.getAccessed()); } - /** + /** * Entry to session. * Called by SessionHandler on inbound request and the session already exists in this node's memory. - * + * * @see org.eclipse.jetty.server.session.AbstractSession#access(long) */ @Override @@ -339,7 +339,7 @@ public class JDBCSessionManager extends AbstractSessionManager return false; } - /** + /** * Exit from session * @see org.eclipse.jetty.server.session.AbstractSession#complete() */ @@ -350,7 +350,7 @@ public class JDBCSessionManager extends AbstractSessionManager try { if (_dirty) - { + { //The session attributes have changed, write to the db, ensuring //http passivation/activation listeners called willPassivate(); @@ -358,7 +358,7 @@ public class JDBCSessionManager extends AbstractSessionManager didActivate(); } else if ((_data._accessed - _data._lastSaved) >= (getSaveInterval() * 1000)) - { + { updateSessionAccessTime(_data); } } @@ -371,18 +371,19 @@ public class JDBCSessionManager extends AbstractSessionManager _dirty=false; } } - + @Override protected void timeout() throws IllegalStateException { - if (LOG.isDebugEnabled()) LOG.debug("Timing out session id="+getClusterId()); + if (LOG.isDebugEnabled()) + LOG.debug("Timing out session id="+getClusterId()); super.timeout(); } } - - - - + + + + /** * ClassLoadingObjectInputStream * @@ -413,46 +414,46 @@ public class JDBCSessionManager extends AbstractSessionManager } } } - - + + /** * Set the time in seconds which is the interval between * saving the session access time to the database. - * + * * This is an optimization that prevents the database from * being overloaded when a session is accessed very frequently. - * + * * On session exit, if the session attributes have NOT changed, * the time at which we last saved the accessed * time is compared to the current accessed time. If the interval * is at least saveIntervalSecs, then the access time will be * persisted to the database. - * + * * If any session attribute does change, then the attributes and * the accessed time are persisted. - * + * * @param sec */ public void setSaveInterval (long sec) { _saveIntervalSec=sec; } - + public long getSaveInterval () { return _saveIntervalSec; } - - + + /** * A method that can be implemented in subclasses to support * distributed caching of sessions. This method will be * called whenever the session is written to the database * because the session data has changed. - * + * * This could be used eg with a JMS backplane to notify nodes * that the session has changed and to delete the session from * the node's cache, and re-read it from the database. @@ -460,46 +461,46 @@ public class JDBCSessionManager extends AbstractSessionManager */ public void cacheInvalidate (Session session) { - + } - - - /** + + + /** * A session has been requested by it's id on this node. - * + * * Load the session by id AND context path from the database. * Multiple contexts may share the same session id (due to dispatching) * but they CANNOT share the same contents. - * + * * Check if last node id is my node id, if so, then the session we have * in memory cannot be stale. If another node used the session last, then * we need to refresh from the db. - * - * NOTE: this method will go to the database, so if you only want to check + * + * NOTE: this method will go to the database, so if you only want to check * for the existence of a Session in memory, use _sessions.get(id) instead. - * + * * @see org.eclipse.jetty.server.session.AbstractSessionManager#getSession(java.lang.String) */ @Override public Session getSession(String idInCluster) { Session session = (Session)_sessions.get(idInCluster); - + synchronized (this) - { + { try - { - //check if we need to reload the session - + { + //check if we need to reload the session - //as an optimization, don't reload on every access - //to reduce the load on the database. This introduces a window of + //to reduce the load on the database. This introduces a window of //possibility that the node may decide that the session is local to it, //when the session has actually been live on another node, and then //re-migrated to this node. This should be an extremely rare occurrence, - //as load-balancers are generally well-behaved and consistently send - //sessions to the same node, changing only iff that node fails. + //as load-balancers are generally well-behaved and consistently send + //sessions to the same node, changing only iff that node fails. SessionData data = null; long now = System.currentTimeMillis(); - if (LOG.isDebugEnabled()) + if (LOG.isDebugEnabled()) { if (session==null) LOG.debug("getSession("+idInCluster+"): not in session map,"+ @@ -515,9 +516,9 @@ public class JDBCSessionManager extends AbstractSessionManager " thisNode="+getSessionIdManager().getWorkerName()+ " difference="+(now - session._data._lastSaved)); } - + if (session==null || ((now - session._data._lastSaved) >= (_saveIntervalSec * 1000))) - { + { LOG.debug("getSession("+idInCluster+"): no session in session map or stale session. Reloading session data from db."); data = loadSession(idInCluster, canonicalize(_context.getContextPath()), getVirtualHost(_context)); } @@ -528,10 +529,10 @@ public class JDBCSessionManager extends AbstractSessionManager } else { - LOG.debug("getSession("+idInCluster+"): session in session map"); + LOG.debug("getSession("+idInCluster+"): session in session map"); data = session._data; } - + if (data != null) { if (!data.getLastNode().equals(getSessionIdManager().getWorkerName()) || session==null) @@ -551,7 +552,7 @@ public class JDBCSessionManager extends AbstractSessionManager } else if (LOG.isDebugEnabled()) LOG.debug("getSession("+idInCluster+"): Session has expired"); - + } else if (LOG.isDebugEnabled()) LOG.debug("getSession("+idInCluster+"): Session not stale "+session._data); @@ -563,7 +564,7 @@ public class JDBCSessionManager extends AbstractSessionManager session=null; if (LOG.isDebugEnabled()) LOG.debug("getSession("+idInCluster+"): No session in database matching id="+idInCluster); } - + return session; } catch (Exception e) @@ -573,10 +574,10 @@ public class JDBCSessionManager extends AbstractSessionManager } } } - - /** + + /** * Get the number of sessions. - * + * * @see org.eclipse.jetty.server.session.AbstractSessionManager#getSessions() */ @Override @@ -591,9 +592,9 @@ public class JDBCSessionManager extends AbstractSessionManager } - /** + /** * Start the session manager. - * + * * @see org.eclipse.jetty.server.session.AbstractSessionManager#doStart() */ @Override @@ -601,17 +602,17 @@ public class JDBCSessionManager extends AbstractSessionManager { if (_sessionIdManager==null) throw new IllegalStateException("No session id manager defined"); - + prepareTables(); - + _sessions = new ConcurrentHashMap(); super.doStart(); } - - - /** + + + /** * Stop the session manager. - * + * * @see org.eclipse.jetty.server.session.AbstractSessionManager#doStop() */ @Override @@ -619,10 +620,10 @@ public class JDBCSessionManager extends AbstractSessionManager { _sessions.clear(); _sessions = null; - + super.doStop(); - } - + } + @Override protected void invalidateSessions() { @@ -634,10 +635,10 @@ public class JDBCSessionManager extends AbstractSessionManager //any other nodes } - + /** * Invalidate a session. - * + * * @param idInCluster */ protected void invalidateSession (String idInCluster) @@ -647,24 +648,24 @@ public class JDBCSessionManager extends AbstractSessionManager { session = (Session)_sessions.get(idInCluster); } - + if (session != null) { session.invalidate(); } } - - /** + + /** * Delete an existing session, both from the in-memory map and * the database. - * + * * @see org.eclipse.jetty.server.session.AbstractSessionManager#removeSession(java.lang.String) */ @Override protected boolean removeSession(String idInCluster) { synchronized (this) - { + { Session session = (Session)_sessions.remove(idInCluster); try { @@ -680,9 +681,9 @@ public class JDBCSessionManager extends AbstractSessionManager } - /** + /** * Add a newly created session to our in-memory list for this node and persist it. - * + * * @see org.eclipse.jetty.server.session.AbstractSessionManager#addSession(org.eclipse.jetty.server.session.AbstractSession) */ @Override @@ -695,7 +696,7 @@ public class JDBCSessionManager extends AbstractSessionManager { _sessions.put(session.getClusterId(), session); } - + //TODO or delay the store until exit out of session? If we crash before we store it //then session data will be lost. try @@ -711,9 +712,9 @@ public class JDBCSessionManager extends AbstractSessionManager } - /** + /** * Make a new Session. - * + * * @see org.eclipse.jetty.server.session.AbstractSessionManager#newSession(javax.servlet.http.HttpServletRequest) */ @Override @@ -721,9 +722,9 @@ public class JDBCSessionManager extends AbstractSessionManager { return new Session(request); } - + /* ------------------------------------------------------------ */ - /** Remove session from manager + /** Remove session from manager * @param session The session to remove * @param invalidate True if {@link HttpSessionListener#sessionDestroyed(HttpSessionEvent)} and * {@link SessionIdManager#invalidateAll(String)} should be called. @@ -733,7 +734,7 @@ public class JDBCSessionManager extends AbstractSessionManager { // Remove session from context and global maps boolean removed = false; - + synchronized (this) { //take this session out of the map of sessions for this context @@ -748,10 +749,10 @@ public class JDBCSessionManager extends AbstractSessionManager { // Remove session from all context and global id maps _sessionIdManager.removeSession(session); - + if (invalidate) _sessionIdManager.invalidateAll(session.getClusterId()); - + if (invalidate && !_sessionListeners.isEmpty()) { HttpSessionEvent event=new HttpSessionEvent(session); @@ -764,16 +765,16 @@ public class JDBCSessionManager extends AbstractSessionManager } } } - - + + /** * Expire any Sessions we have in memory matching the list of * expired Session ids. - * + * * @param sessionIds */ protected void expire (List sessionIds) - { + { //don't attempt to scavenge if we are shutting down if (isStopping() || isStopped()) return; @@ -788,8 +789,9 @@ public class JDBCSessionManager extends AbstractSessionManager while (itor.hasNext()) { String sessionId = (String)itor.next(); - if (LOG.isDebugEnabled()) LOG.debug("Expiring session id "+sessionId); - + if (LOG.isDebugEnabled()) + LOG.debug("Expiring session id "+sessionId); + Session session = (Session)_sessions.get(sessionId); if (session != null) { @@ -798,28 +800,26 @@ public class JDBCSessionManager extends AbstractSessionManager } else { - if (LOG.isDebugEnabled()) LOG.debug("Unrecognized session id="+sessionId); + if (LOG.isDebugEnabled()) + LOG.debug("Unrecognized session id="+sessionId); } } } catch (Throwable t) { - if (t instanceof ThreadDeath) - throw ((ThreadDeath)t); - else - LOG.warn("Problem expiring sessions", t); + LOG.warn("Problem expiring sessions", t); } finally { thread.setContextClassLoader(old_loader); } } - - + + protected void prepareTables () { __sessionTableRowId = ((JDBCSessionIdManager)_sessionIdManager)._sessionTableRowId; - + __insertSession = "insert into "+((JDBCSessionIdManager)_sessionIdManager)._sessionTable+ " ("+__sessionTableRowId+", sessionId, contextPath, virtualHost, lastNode, accessTime, lastAccessTime, createTime, cookieTime, lastSavedTime, expiryTime, map) "+ " values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; @@ -839,7 +839,7 @@ public class JDBCSessionManager extends AbstractSessionManager __updateSessionAccessTime = "update "+((JDBCSessionIdManager)_sessionIdManager)._sessionTable+ " set lastNode = ?, accessTime = ?, lastAccessTime = ?, lastSavedTime = ?, expiryTime = ? where "+__sessionTableRowId+" = ?"; } - + /** * Load a session from the database * @param id @@ -860,7 +860,7 @@ public class JDBCSessionManager extends AbstractSessionManager Connection connection=null; PreparedStatement statement = null; try - { + { connection = getConnection(); statement = connection.prepareStatement(__selectSession); statement.setString(1, id); @@ -903,24 +903,24 @@ public class JDBCSessionManager extends AbstractSessionManager try { connection.close();} catch(Exception e) { LOG.warn(e); } } - } + } } }; - + if (_context==null) load.run(); else _context.getContextHandler().handle(load); - + if (_exception.get()!=null) throw _exception.get(); - + return _reference.get(); } - + /** * Insert a session into the database. - * + * * @param data * @throws Exception */ @@ -929,14 +929,14 @@ public class JDBCSessionManager extends AbstractSessionManager { if (data==null) return; - - //put into the database + + //put into the database Connection connection = getConnection(); PreparedStatement statement = null; try - { + { String rowId = calculateRowId(data); - + long now = System.currentTimeMillis(); connection.setAutoCommit(true); statement = connection.prepareStatement(__insertSession); @@ -951,34 +951,34 @@ public class JDBCSessionManager extends AbstractSessionManager statement.setLong(9, data.getCookieSet());//time cookie was set statement.setLong(10, now); //last saved time statement.setLong(11, data.getExpiryTime()); - + ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(data.getAttributeMap()); byte[] bytes = baos.toByteArray(); - + ByteArrayInputStream bais = new ByteArrayInputStream(bytes); statement.setBinaryStream(12, bais, bytes.length);//attribute map as blob - + statement.executeUpdate(); data.setRowId(rowId); //set it on the in-memory data as well as in db data.setLastSaved(now); - + if (LOG.isDebugEnabled()) LOG.debug("Stored session "+data); - } + } finally { if (connection!=null) connection.close(); } } - - + + /** * Update data on an existing persisted session. - * + * * @param data * @throws Exception */ @@ -987,30 +987,30 @@ public class JDBCSessionManager extends AbstractSessionManager { if (data==null) return; - + Connection connection = getConnection(); PreparedStatement statement = null; try - { + { long now = System.currentTimeMillis(); connection.setAutoCommit(true); - statement = connection.prepareStatement(__updateSession); + statement = connection.prepareStatement(__updateSession); statement.setString(1, getSessionIdManager().getWorkerName());//my node id statement.setLong(2, data.getAccessed());//accessTime statement.setLong(3, data.getLastAccessed()); //lastAccessTime statement.setLong(4, now); //last saved time statement.setLong(5, data.getExpiryTime()); - + ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(data.getAttributeMap()); byte[] bytes = baos.toByteArray(); ByteArrayInputStream bais = new ByteArrayInputStream(bytes); - - statement.setBinaryStream(6, bais, bytes.length);//attribute map as blob + + statement.setBinaryStream(6, bais, bytes.length);//attribute map as blob statement.setString(7, data.getRowId()); //rowId statement.executeUpdate(); - + data.setLastSaved(now); if (LOG.isDebugEnabled()) LOG.debug("Updated session "+data); @@ -1021,11 +1021,11 @@ public class JDBCSessionManager extends AbstractSessionManager connection.close(); } } - - + + /** * Update the node on which the session was last seen to be my node. - * + * * @param data * @throws Exception */ @@ -1036,7 +1036,7 @@ public class JDBCSessionManager extends AbstractSessionManager Connection connection = getConnection(); PreparedStatement statement = null; try - { + { connection.setAutoCommit(true); statement = connection.prepareStatement(__updateSessionNode); statement.setString(1, nodeId); @@ -1052,10 +1052,10 @@ public class JDBCSessionManager extends AbstractSessionManager connection.close(); } } - + /** * Persist the time the session was last accessed. - * + * * @param data * @throws Exception */ @@ -1065,7 +1065,7 @@ public class JDBCSessionManager extends AbstractSessionManager Connection connection = getConnection(); PreparedStatement statement = null; try - { + { long now = System.currentTimeMillis(); connection.setAutoCommit(true); statement = connection.prepareStatement(__updateSessionAccessTime); @@ -1087,14 +1087,14 @@ public class JDBCSessionManager extends AbstractSessionManager connection.close(); } } - - - - + + + + /** * Delete a session from the database. Should only be called * when the session has been invalidated. - * + * * @param data * @throws Exception */ @@ -1116,11 +1116,11 @@ public class JDBCSessionManager extends AbstractSessionManager { if (connection!=null) connection.close(); - } + } } - - - + + + /** * Get a connection from the driver. * @return @@ -1128,13 +1128,13 @@ public class JDBCSessionManager extends AbstractSessionManager */ private Connection getConnection () throws SQLException - { + { return ((JDBCSessionIdManager)getSessionIdManager()).getConnection(); } /** * Calculate a unique id for this session across the cluster. - * + * * Unique id is composed of: contextpath_virtualhost0_sessionid * @param data * @return @@ -1146,31 +1146,31 @@ public class JDBCSessionManager extends AbstractSessionManager rowId = rowId+"_"+data.getId(); return rowId; } - + /** * Get the first virtual host for the context. - * + * * Used to help identify the exact session/contextPath. - * + * * @return 0.0.0.0 if no virtual host is defined */ private String getVirtualHost (ContextHandler.Context context) { String vhost = "0.0.0.0"; - + if (context==null) return vhost; - + String [] vhosts = context.getContextHandler().getVirtualHosts(); if (vhosts==null || vhosts.length==0 || vhosts[0]==null) return vhost; - + return vhosts[0]; } - + /** * Make an acceptable file name from a context path. - * + * * @param path * @return */ @@ -1178,7 +1178,7 @@ public class JDBCSessionManager extends AbstractSessionManager { if (path==null) return ""; - + return path.replace('/', '_').replace('.','_').replace('\\','_'); } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java index 2e951710c73..a18a6e7553b 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java @@ -36,7 +36,7 @@ import org.eclipse.jetty.util.log.Logger; */ public class SessionHandler extends ScopedHandler { - final static Logger __log = Log.getLogger("org.eclipse.jetty.server.session"); + final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session"); /* -------------------------------------------------------------- */ private SessionManager _sessionManager; @@ -175,10 +175,10 @@ public class SessionHandler extends ScopedHandler } } - if(__log.isDebugEnabled()) + if(LOG.isDebugEnabled()) { - __log.debug("sessionManager="+_sessionManager); - __log.debug("session="+session); + LOG.debug("sessionManager="+_sessionManager); + LOG.debug("session="+session); } // start manual inline of nextScope(target,baseRequest,request,response); @@ -264,7 +264,8 @@ public class SessionHandler extends ScopedHandler { requested_session_id=cookies[i].getValue(); requested_session_id_from_cookie = true; - if(__log.isDebugEnabled())__log.debug("Got Session ID "+requested_session_id+" from cookie"); + if(LOG.isDebugEnabled()) + LOG.debug("Got Session ID {} from cookie",requested_session_id); session=sessionManager.getHttpSession(requested_session_id); if (session!=null && sessionManager.isValid(session)) @@ -297,8 +298,8 @@ public class SessionHandler extends ScopedHandler requested_session_id = uri.substring(s,i); requested_session_id_from_cookie = false; session=sessionManager.getHttpSession(requested_session_id); - if(__log.isDebugEnabled()) - __log.debug("Got Session ID "+requested_session_id+" from URL"); + if(LOG.isDebugEnabled()) + LOG.debug("Got Session ID {} from URL",requested_session_id); } } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslConnector.java index 1bf6b4680cf..fb04d57d61c 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslConnector.java @@ -9,8 +9,8 @@ import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.TrustManagerFactory; -import org.eclipse.jetty.http.ssl.SslContextFactory; import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.util.ssl.SslContextFactory; /* ------------------------------------------------------------ */ diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslSelectChannelConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslSelectChannelConnector.java index 50162605839..1986e7bc08d 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslSelectChannelConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslSelectChannelConnector.java @@ -14,30 +14,26 @@ package org.eclipse.jetty.server.ssl; import java.io.IOException; -import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; -import java.util.Arrays; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; -import org.eclipse.jetty.http.HttpParser; import org.eclipse.jetty.http.HttpSchemes; -import org.eclipse.jetty.http.ssl.SslContextFactory; +import org.eclipse.jetty.io.AsyncEndPoint; import org.eclipse.jetty.io.Buffers; import org.eclipse.jetty.io.Buffers.Type; import org.eclipse.jetty.io.BuffersFactory; -import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RuntimeIOException; import org.eclipse.jetty.io.bio.SocketEndPoint; -import org.eclipse.jetty.io.nio.SelectChannelEndPoint; -import org.eclipse.jetty.io.nio.SelectorManager.SelectSet; -import org.eclipse.jetty.io.nio.SslSelectChannelEndPoint; -import org.eclipse.jetty.server.HttpConnection; +import org.eclipse.jetty.io.nio.AsyncConnection; +import org.eclipse.jetty.io.nio.SslConnection; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.nio.SelectChannelConnector; +import org.eclipse.jetty.util.ssl.SslContextFactory; /* ------------------------------------------------------------ */ /** @@ -54,6 +50,7 @@ public class SslSelectChannelConnector extends SelectChannelConnector implements public SslSelectChannelConnector() { this(new SslContextFactory(SslContextFactory.DEFAULT_KEYSTORE_PATH)); + setSoLingerTime(30000); } /* ------------------------------------------------------------ */ @@ -61,6 +58,7 @@ public class SslSelectChannelConnector extends SelectChannelConnector implements { _sslContextFactory = sslContextFactory; setUseDirectBuffers(false); + setSoLingerTime(30000); } /* ------------------------------------------------------------ */ @@ -95,10 +93,10 @@ public class SslSelectChannelConnector extends SelectChannelConnector implements request.setScheme(HttpSchemes.HTTPS); super.customize(endpoint,request); - SslSelectChannelEndPoint sslHttpChannelEndpoint=(SslSelectChannelEndPoint)endpoint; - SSLEngine sslEngine=sslHttpChannelEndpoint.getSSLEngine(); + SslConnection.SslEndPoint sslEndpoint=(SslConnection.SslEndPoint)endpoint; + SSLEngine sslEngine=sslEndpoint.getSslEngine(); SSLSession sslSession=sslEngine.getSession(); - + SslCertificates.customize(sslSession,endpoint,request); } @@ -539,21 +537,31 @@ public class SslSelectChannelConnector extends SelectChannelConnector implements /* ------------------------------------------------------------------------------- */ @Override - protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey key) throws IOException + protected AsyncConnection newConnection(SocketChannel channel, AsyncEndPoint endpoint) { - SSLEngine engine = createSSLEngine(channel); - SslSelectChannelEndPoint endp = new SslSelectChannelEndPoint(_sslBuffers,channel,selectSet,key,engine, SslSelectChannelConnector.this._maxIdleTime); - endp.setAllowRenegotiate(_sslContextFactory.isAllowRenegotiate()); - return endp; + try + { + SSLEngine engine = createSSLEngine(channel); + SslConnection connection = newSslConnection(endpoint, engine); + AsyncConnection delegate = newPlainConnection(channel, connection.getSslEndPoint()); + connection.getSslEndPoint().setConnection(delegate); + connection.setAllowRenegotiate(_sslContextFactory.isAllowRenegotiate()); + return connection; + } + catch (IOException e) + { + throw new RuntimeIOException(e); + } } - /* ------------------------------------------------------------------------------- */ - @Override - protected Connection newConnection(SocketChannel channel, SelectChannelEndPoint endpoint) + protected AsyncConnection newPlainConnection(SocketChannel channel, AsyncEndPoint endPoint) { - HttpConnection connection=(HttpConnection)super.newConnection(channel,endpoint); - ((HttpParser)connection.getParser()).setForceContentBuffer(true); - return connection; + return super.newConnection(channel, endPoint); + } + + protected SslConnection newSslConnection(AsyncEndPoint endpoint, SSLEngine engine) + { + return new SslConnection(engine, endpoint); } /* ------------------------------------------------------------ */ @@ -576,7 +584,7 @@ public class SslSelectChannelConnector extends SelectChannelConnector implements { engine = _sslContextFactory.newSslEngine(); } - + engine.setUseClientMode(false); return engine; } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslSocketConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslSocketConnector.java index 85785b79f50..f18981cce7b 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslSocketConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslSocketConnector.java @@ -16,6 +16,7 @@ package org.eclipse.jetty.server.ssl; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; + import javax.net.ssl.HandshakeCompletedEvent; import javax.net.ssl.HandshakeCompletedListener; import javax.net.ssl.SSLContext; @@ -25,7 +26,6 @@ import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import org.eclipse.jetty.http.HttpSchemes; -import org.eclipse.jetty.http.ssl.SslContextFactory; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.RuntimeIOException; import org.eclipse.jetty.io.bio.SocketEndPoint; @@ -33,6 +33,7 @@ import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.bio.SocketConnector; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.ssl.SslContextFactory; /* ------------------------------------------------------------ */ /** @@ -63,6 +64,7 @@ public class SslSocketConnector extends SocketConnector implements SslConnector public SslSocketConnector() { this(new SslContextFactory(SslContextFactory.DEFAULT_KEYSTORE_PATH)); + setSoLingerTime(30000); } /* ------------------------------------------------------------ */ diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/AbstractConnectorTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/AbstractConnectorTest.java index f006313c14a..7c35f79d147 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/AbstractConnectorTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/AbstractConnectorTest.java @@ -13,6 +13,9 @@ package org.eclipse.jetty.server; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -20,6 +23,7 @@ import java.io.PrintWriter; import java.net.Socket; import java.util.concurrent.CountDownLatch; import java.util.concurrent.CyclicBarrier; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -35,9 +39,6 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - public class AbstractConnectorTest { private static final Logger LOG = Log.getLogger(AbstractConnectorTest.class); diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncUploadTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncRequestReadTest.java similarity index 65% rename from jetty-server/src/test/java/org/eclipse/jetty/server/AsyncUploadTest.java rename to jetty-server/src/test/java/org/eclipse/jetty/server/AsyncRequestReadTest.java index d970ca15bdd..e63f875a9ef 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncUploadTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncRequestReadTest.java @@ -22,6 +22,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.util.Arrays; +import java.util.concurrent.Exchanger; import java.util.concurrent.TimeUnit; import javax.servlet.ServletException; @@ -33,6 +34,7 @@ import org.eclipse.jetty.continuation.ContinuationSupport; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.server.nio.SelectChannelConnector; import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.StringUtil; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -40,17 +42,18 @@ import org.junit.Test; /** * @version $Revision: 889 $ $Date: 2009-09-14 14:52:16 +1000 (Mon, 14 Sep 2009) $ */ -public class AsyncUploadTest +public class AsyncRequestReadTest { private static Server server; private static Connector connector; - private static int total; + private final static Exchanger __total=new Exchanger(); @BeforeClass public static void startServer() throws Exception { server = new Server(); connector = new SelectChannelConnector(); + connector.setMaxIdleTime(10000); server.addConnector(connector); server.setHandler(new EmptyHandler()); server.start(); @@ -62,7 +65,7 @@ public class AsyncUploadTest server.stop(); server.join(); } - + @Test public void test() throws Exception { @@ -71,14 +74,17 @@ public class AsyncUploadTest byte[] content = new byte[16*4096]; Arrays.fill(content, (byte)120); - long start = System.nanoTime(); OutputStream out = socket.getOutputStream(); - out.write("POST / HTTP/1.1\r\n".getBytes()); - out.write("Host: localhost\r\n".getBytes()); - out.write(("Content-Length: "+content.length+"\r\n").getBytes()); - out.write("Content-Type: bytes\r\n".getBytes()); - out.write("Connection: close\r\n".getBytes()); - out.write("\r\n".getBytes()); + String header= + "POST / HTTP/1.1\r\n"+ + "Host: localhost\r\n"+ + "Content-Length: "+content.length+"\r\n"+ + "Content-Type: bytes\r\n"+ + "Connection: close\r\n"+ + "\r\n"; + byte[] h=header.getBytes(StringUtil.__ISO_8859_1); + + out.write(h); out.flush(); out.write(content,0,4*4096); @@ -91,14 +97,63 @@ public class AsyncUploadTest InputStream in = socket.getInputStream(); String response = IO.toString(in); - // System.err.println(response); assertTrue(response.indexOf("200 OK")>0); - long end = System.nanoTime(); - System.err.println("upload time: " + TimeUnit.NANOSECONDS.toMillis(end - start)); + long total=__total.exchange(0L,30,TimeUnit.SECONDS); assertEquals(content.length, total); } + + @Test + public void tests() throws Exception + { + runTest(64,4,4,20); + runTest(256,16,16,50); + runTest(256,1,128,10); + runTest(128*1024,1,64,10); + runTest(256*1024,5321,10,100); + runTest(512*1024,32*1024,10,10); + } + + + public void runTest(int contentSize, int chunkSize, int chunks, int delayMS) throws Exception + { + String tst=contentSize+","+chunkSize+","+chunks+","+delayMS; + //System.err.println(tst); + + final Socket socket = new Socket("localhost",connector.getLocalPort()); + byte[] content = new byte[contentSize]; + Arrays.fill(content, (byte)120); + + OutputStream out = socket.getOutputStream(); + out.write("POST / HTTP/1.1\r\n".getBytes()); + out.write("Host: localhost\r\n".getBytes()); + out.write(("Content-Length: "+content.length+"\r\n").getBytes()); + out.write("Content-Type: bytes\r\n".getBytes()); + out.write("Connection: close\r\n".getBytes()); + out.write("\r\n".getBytes()); + out.flush(); + + int offset=0; + for (int i=0;i0); + + long total=__total.exchange(0L,30,TimeUnit.SECONDS); + assertEquals(tst,content.length, total); + } + + private static class EmptyHandler extends AbstractHandler { public void handle(String path, final Request request, HttpServletRequest httpRequest, final HttpServletResponse httpResponse) throws IOException, ServletException @@ -112,15 +167,14 @@ public class AsyncUploadTest @Override public void run() { + long total=0; try { - Thread.sleep(100); InputStream in = request.getInputStream(); byte[] b = new byte[4*4096]; int read; while((read =in.read(b))>=0) total += read; - System.err.println("Read "+ total); } catch(Exception e) { @@ -131,6 +185,14 @@ public class AsyncUploadTest { httpResponse.setStatus(200); continuation.complete(); + try + { + __total.exchange(total); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } } } }.start(); diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/BusySelectChannelServerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/BusySelectChannelServerTest.java deleted file mode 100644 index 1f6c6852a8f..00000000000 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/BusySelectChannelServerTest.java +++ /dev/null @@ -1,155 +0,0 @@ -// ======================================================================== -// Copyright (c) 2004-2009 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 java.io.IOException; -import java.nio.channels.SelectionKey; -import java.nio.channels.SocketChannel; - -import org.eclipse.jetty.io.Buffer; -import org.eclipse.jetty.io.View; -import org.eclipse.jetty.io.nio.IndirectNIOBuffer; -import org.eclipse.jetty.io.nio.NIOBuffer; -import org.eclipse.jetty.io.nio.SelectChannelEndPoint; -import org.eclipse.jetty.io.nio.SelectorManager.SelectSet; -import org.eclipse.jetty.server.nio.SelectChannelConnector; -import org.junit.BeforeClass; - -/** - * HttpServer Tester. - */ -public class BusySelectChannelServerTest extends HttpServerTestBase -{ - @BeforeClass - public static void init() throws Exception - { - SelectChannelConnector connector=new SelectChannelConnector() - { - @Override - protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey key) throws IOException - { - return new SelectChannelEndPoint(channel,selectSet,key, _maxIdleTime) - { - int write; - int read; - - /* ------------------------------------------------------------ */ - /* (non-Javadoc) - * @see org.eclipse.io.nio.SelectChannelEndPoint#flush(org.eclipse.io.Buffer, org.eclipse.io.Buffer, org.eclipse.io.Buffer) - */ - @Override - public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException - { - int x=write++&0xff; - if (x<8) - { - clearWritable(); - return 0; - } - if (x<32) - return flush(header); - return super.flush(header,buffer,trailer); - } - - /* ------------------------------------------------------------ */ - /* (non-Javadoc) - * @see org.eclipse.io.nio.SelectChannelEndPoint#flush(org.eclipse.io.Buffer) - */ - @Override - public int flush(Buffer buffer) throws IOException - { - int x=write++&0xff; - if (x<8) - { - clearWritable(); - return 0; - } - if (x<32) - { - View v = new View(buffer); - v.setPutIndex(v.getIndex()+1); - int l=super.flush(v); - if (l>0) - buffer.skip(l); - clearWritable(); - return l; - } - return super.flush(buffer); - } - - /* ------------------------------------------------------------ */ - /* (non-Javadoc) - * @see org.eclipse.io.nio.ChannelEndPoint#fill(org.eclipse.io.Buffer) - */ - @Override - public int fill(Buffer buffer) throws IOException - { - int x=read++&0xff; - if (x<8) - return 0; - - if (x<16 && buffer.space()>=1) - { - NIOBuffer one = new IndirectNIOBuffer(1); - int l=super.fill(one); - if (l>0) - buffer.put(one.peek(0)); - return l; - } - - if (x<24 && buffer.space()>=2) - { - NIOBuffer two = new IndirectNIOBuffer(2); - int l=super.fill(two); - if (l>0) - buffer.put(two.peek(0)); - if (l>1) - buffer.put(two.peek(1)); - return l; - } - - if (x<64 && buffer.space()>=3) - { - NIOBuffer three = new IndirectNIOBuffer(3); - int l=super.fill(three); - if (l>0) - buffer.put(three.peek(0)); - if (l>1) - buffer.put(three.peek(1)); - if (l>2) - buffer.put(three.peek(2)); - return l; - } - - return super.fill(buffer); - } - }; - } - }; - connector.setAcceptors(1); - startServer(connector); - } - - @Override - public void testAvailable() throws Exception - { - } - - @Override - public void testBlockingWhileWritingResponseContent() throws Exception - { - // TODO work out why this one fails - } - - -} diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorTimeoutTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorTimeoutTest.java index 26caaa934b5..7b2e2b5650e 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorTimeoutTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorTimeoutTest.java @@ -54,8 +54,6 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture OutputStream os=client.getOutputStream(); InputStream is=client.getInputStream(); - String content="Wibble"; - byte[] contentB=content.getBytes("utf-8"); os.write(( "GET / HTTP/1.0\r\n"+ "host: "+HOST+":"+_connector.getLocalPort()+"\r\n"+ @@ -64,7 +62,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture os.flush(); long start = System.currentTimeMillis(); - String in = IO.toString(is); + IO.toString(is); Thread.sleep(300); assertEquals(-1, is.read()); @@ -97,7 +95,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture os.flush(); long start = System.currentTimeMillis(); - String in = IO.toString(is); + IO.toString(is); Thread.sleep(300); assertEquals(-1, is.read()); diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/DumpHandler.java b/jetty-server/src/test/java/org/eclipse/jetty/server/DumpHandler.java index adaf8751b28..301c352899a 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/DumpHandler.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/DumpHandler.java @@ -151,8 +151,7 @@ public class DumpHandler extends AbstractHandler String val=request.getParameter("CookieVal"); val=val.replaceAll("[ \n\r=<>]","?"); Cookie cookie= - new Cookie(cookie_name.trim(), - request.getParameter("CookieVal")); + new Cookie(cookie_name.trim(),val); if ("Clear Cookie".equals(cookie_action)) cookie.setMaxAge(0); response.addCookie(cookie); diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/EncodedHttpURITest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/EncodedHttpURITest.java index f5afdb662be..b73c3fdbc5c 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/EncodedHttpURITest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/EncodedHttpURITest.java @@ -28,7 +28,7 @@ public class EncodedHttpURITest { String url = "http://www.foo.com/ma\u00F1ana"; byte[] asISO = url.getBytes("ISO-8859-1"); - String str = new String(asISO, "ISO-8859-1"); + new String(asISO, "ISO-8859-1"); //use a non UTF-8 charset as the encoding and url-escape as per //http://www.w3.org/TR/html40/appendix/notes.html#non-ascii-chars diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java index 50e175dae19..67207366582 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java @@ -33,7 +33,6 @@ import org.eclipse.jetty.http.HttpHeaders; import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; -import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.StdErrLog; import org.junit.After; import org.junit.Before; @@ -124,7 +123,7 @@ public class HttpConnectionTest int offset=0; offset = checkContains(response,offset,"HTTP/1.1 200"); - offset = checkContains(response,offset,"/R1"); + checkContains(response,offset,"/R1"); } @Test @@ -155,7 +154,7 @@ public class HttpConnectionTest { try { - ((StdErrLog)Log.getLogger(HttpConnection.class)).setHideStacks(true); + ((StdErrLog)Log.getLogger(AbstractHttpConnection.class)).setHideStacks(true); String response; @@ -186,7 +185,7 @@ public class HttpConnectionTest } finally { - ((StdErrLog)Log.getLogger(HttpConnection.class)).setHideStacks(false); + ((StdErrLog)Log.getLogger(AbstractHttpConnection.class)).setHideStacks(false); } } @@ -336,7 +335,7 @@ public class HttpConnectionTest Logger logger=null; try { - ((StdErrLog)Log.getLogger(HttpConnection.class)).setHideStacks(true); + ((StdErrLog)Log.getLogger(AbstractHttpConnection.class)).setHideStacks(true); response=connector.getResponses(requests); offset = checkContains(response,offset,"HTTP/1.1 500"); offset = checkContains(response,offset,"Connection: close"); @@ -344,7 +343,7 @@ public class HttpConnectionTest } finally { - ((StdErrLog)Log.getLogger(HttpConnection.class)).setHideStacks(false); + ((StdErrLog)Log.getLogger(AbstractHttpConnection.class)).setHideStacks(false); } } @@ -366,7 +365,7 @@ public class HttpConnectionTest "5;\015\012"+ "12345\015\012"+ "0;\015\012\015\012"); - offset = checkContains(response,offset,"Connection: close"); + checkContains(response,offset,"Connection: close"); } catch (Exception e) { @@ -395,7 +394,7 @@ public class HttpConnectionTest "Cookie: "+cookie+"\n"+ "\015\012" ); - offset = checkContains(response, offset, "HTTP/1.1 413"); + checkContains(response, offset, "HTTP/1.1 413"); } catch(Exception e) { @@ -424,14 +423,14 @@ public class HttpConnectionTest request.append("\015\012"); response = connector.getResponses(request.toString()); - offset = checkContains(response, offset, "HTTP/1.1 413"); + checkContains(response, offset, "HTTP/1.1 413"); } @Test public void testOversizedResponse() throws Exception { - String str = "thisisastringthatshouldreachover1kbytes"; - for (int i=0;i<400;i++) + String str = "thisisastringthatshouldreachover1kbytes-"; + for (int i=0;i<500;i++) str+="xxxxxxxxxxxx"; final String longstr = str; @@ -470,7 +469,7 @@ public class HttpConnectionTest "\015\012" ); - offset = checkContains(response, offset, "HTTP/1.1 500"); + checkContains(response, offset, "HTTP/1.1 500"); } catch(Exception e) { @@ -549,7 +548,7 @@ public class HttpConnectionTest response=connector.getResponses("CONNECT www.webtide.com:8080 HTTP/1.1\n"+ "Host: myproxy:8888\015\012"+ "\015\012"); - offset = checkContains(response,offset,"HTTP/1.1 200"); + checkContains(response,offset,"HTTP/1.1 200"); } catch (Exception e) diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java index 683bf5f5126..661fcde9a65 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java @@ -57,7 +57,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture private static final String REQUEST1=REQUEST1_HEADER+REQUEST1_CONTENT.getBytes().length+"\n\n"+REQUEST1_CONTENT; /** The expected response. */ - private static final String RESPONSE1="HTTP/1.1 200 OK\n"+"Connection: close\n"+"Server: Jetty("+Server.getVersion()+")\n"+"\n"+"Hello world\n"; + private static final String RESPONSE1="HTTP/1.1 200 OK\n"+"Content-Length: 13\n"+"Server: Jetty("+Server.getVersion()+")\n"+"\n"+"Hello world\n"; // Break the request up into three pieces, splitting the header. private static final String FRAGMENT1=REQUEST1.substring(0,16); @@ -65,12 +65,12 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture private static final String FRAGMENT3=REQUEST1.substring(34); /** Second test request. */ - private static final String REQUEST2_HEADER= + protected static final String REQUEST2_HEADER= "POST / HTTP/1.0\n"+ "Host: localhost\n"+ "Content-Type: text/xml;charset=ISO-8859-1\n"+ "Content-Length: "; - private static final String REQUEST2_CONTENT= + protected static final String REQUEST2_CONTENT= "\n"+ "\n"+ @@ -80,10 +80,10 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture " \n"+ " \n"+ ""; - private static final String REQUEST2=REQUEST2_HEADER+REQUEST2_CONTENT.getBytes().length+"\n\n"+REQUEST2_CONTENT; + protected static final String REQUEST2=REQUEST2_HEADER+REQUEST2_CONTENT.getBytes().length+"\n\n"+REQUEST2_CONTENT; /** The second expected response. */ - private static final String RESPONSE2_CONTENT= + protected static final String RESPONSE2_CONTENT= "\n"+ "\n"+ @@ -93,7 +93,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture " \n"+ " \n" +"\n"; - private static final String RESPONSE2= + protected static final String RESPONSE2= "HTTP/1.1 200 OK\n"+ "Content-Type: text/xml;charset=ISO-8859-1\n"+ "Content-Length: "+RESPONSE2_CONTENT.getBytes().length+"\n"+ @@ -143,19 +143,24 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture { OutputStream os=client.getOutputStream(); - os.write(("GET /R2 HTTP/1.1\015\012"+"Host: localhost\015\012"+"Transfer-Encoding: chunked\015\012"+"Content-Type: text/plain\015\012" - +"Connection: close\015\012"+"\015\012").getBytes()); + os.write(("GET /R2 HTTP/1.1\015\012"+ + "Host: localhost\015\012"+ + "Transfer-Encoding: chunked\015\012"+ + "Content-Type: text/plain\015\012"+ + "Connection: close\015\012"+ + "\015\012").getBytes()); os.flush(); Thread.sleep(PAUSE); os.write(("5\015\012").getBytes()); os.flush(); Thread.sleep(PAUSE); - os.write(("ABCDE\015\012"+"0;\015\012\015\012").getBytes()); + os.write(("ABCDE\015\012"+ + "0;\015\012\015\012").getBytes()); os.flush(); // Read the response. String response=readResponse(client); - assertTrue(true); // nothing checked yet. + assertTrue (response.indexOf("200")>0); } finally { @@ -221,6 +226,12 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture // Check the response assertEquals("response "+i,RESPONSE2,response); } + catch(IOException e) + { + e.printStackTrace(); + _server.dumpStdErr(); + throw e; + } finally { client.close(); @@ -545,8 +556,8 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture // read and check the times are < 999ms String[] times=in.readLine().split(","); - - // Assert.assertTrue(Integer.valueOf(t).intValue()<999); + for (String t: times) + Assert.assertTrue(Integer.valueOf(t).intValue()<999); // read the EOF chunk @@ -576,7 +587,8 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture // read and check the times are < 999ms times=in.readLine().split(","); - //Assert.assertTrue(t,Integer.valueOf(t).intValue()<999); + for (String t: times) + Assert.assertTrue(t,Integer.valueOf(t).intValue()<999); // check close Assert.assertTrue(in.readLine()==null); @@ -729,6 +741,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture IO.copy(is,bout); byte[] b=bout.toByteArray(); + System.err.println("OUTPUT: "+new String(b)); int i=0; while (b[i]!='Z') i++; @@ -926,7 +939,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture Socket client=newSocket(HOST,_connector.getLocalPort()); try { - ((StdErrLog)Log.getLogger(HttpConnection.class)).setHideStacks(true); + ((StdErrLog)Log.getLogger(AbstractHttpConnection.class)).setHideStacks(true); OutputStream os=client.getOutputStream(); InputStream is=client.getInputStream(); @@ -954,7 +967,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture } finally { - ((StdErrLog)Log.getLogger(HttpConnection.class)).setHideStacks(false); + ((StdErrLog)Log.getLogger(AbstractHttpConnection.class)).setHideStacks(false); if (!client.isClosed()) client.close(); @@ -1049,7 +1062,6 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture AvailableHandler ah=new AvailableHandler(); configureServer(ah); - long start=System.currentTimeMillis(); Socket client=newSocket(HOST,_connector.getLocalPort()); try { @@ -1136,15 +1148,15 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture * @return The response string. * @throws IOException in case of I/O problems */ - private static String readResponse(Socket client) throws IOException + protected static String readResponse(Socket client) throws IOException { BufferedReader br=null; + StringBuilder sb=new StringBuilder(); try { br=new BufferedReader(new InputStreamReader(client.getInputStream())); - StringBuilder sb=new StringBuilder(); String line; while ((line=br.readLine())!=null) @@ -1155,6 +1167,11 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture return sb.toString(); } + catch(IOException e) + { + System.err.println(e+" while reading '"+sb+"'"); + throw e; + } finally { if (br!=null) diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestFixture.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestFixture.java index 268491098ef..09e1970c3e9 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestFixture.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestFixture.java @@ -26,7 +26,7 @@ import org.junit.AfterClass; public class HttpServerTestFixture { // Useful constants protected static final long PAUSE=10L; - protected static final int LOOPS=Stress.isEnabled()?250:25; + protected static final int LOOPS=Stress.isEnabled()?250:50; protected static final String HOST="localhost"; protected static Server _server; @@ -36,7 +36,7 @@ public class HttpServerTestFixture protected Socket newSocket(String host,int port) throws Exception { Socket socket = new Socket(host,port); - socket.setSoTimeout(30000); + socket.setSoTimeout(10000); socket.setTcpNoDelay(true); socket.setSoLinger(false,0); return socket; @@ -189,7 +189,7 @@ public class HttpServerTestFixture } // Create a trust manager that does not validate certificate chains - public static TrustManager[] __trustAllCerts = new TrustManager[] { + public final static TrustManager[] __trustAllCerts = new TrustManager[] { new X509TrustManager(){ public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; @@ -203,7 +203,7 @@ public class HttpServerTestFixture } }; - public static HostnameVerifier __hostnameverifier = new HostnameVerifier() + public final static HostnameVerifier __hostnameverifier = new HostnameVerifier() { public boolean verify(String hostname, SSLSession session) { diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpURITest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpURITest.java index 3ab42194a01..d75235d40e0 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpURITest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpURITest.java @@ -27,9 +27,7 @@ import junit.framework.Assert; import org.eclipse.jetty.http.HttpURI; import org.eclipse.jetty.io.ByteArrayBuffer; import org.eclipse.jetty.util.MultiMap; -import org.eclipse.jetty.util.URIUtil; import org.junit.Test; -import org.omg.Dynamic.Parameter; public class HttpURITest { diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpWriterTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpWriterTest.java index 73828e81bf1..3d21c413757 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpWriterTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpWriterTest.java @@ -52,7 +52,7 @@ public class HttpWriterTest } @Override - public long flushBuffer() throws IOException + public int flushBuffer() throws IOException { return 0; } @@ -76,7 +76,7 @@ public class HttpWriterTest }; - HttpConnection connection = new HttpConnection(null,endp,new Server(),null,generator,null) + AbstractHttpConnection connection = new AbstractHttpConnection(null,endp,new Server(),null,generator,null) { @Override public Connection handle() throws IOException @@ -169,7 +169,7 @@ public class HttpWriterTest hb.setResponse(200,"OK"); - HttpConnection connection = new HttpConnection(null,endp,new Server(),null,hb,null) + AbstractHttpConnection connection = new AbstractHttpConnection(null,endp,new Server(),null,hb,null) { @Override public Connection handle() throws IOException diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncContextTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/LocalAsyncContextTest.java similarity index 94% rename from jetty-server/src/test/java/org/eclipse/jetty/server/AsyncContextTest.java rename to jetty-server/src/test/java/org/eclipse/jetty/server/LocalAsyncContextTest.java index c0b85eb0a2c..e5b13f6dc52 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncContextTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/LocalAsyncContextTest.java @@ -31,16 +31,16 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; -public class AsyncContextTest +public class LocalAsyncContextTest { protected Server _server = new Server(); protected SuspendHandler _handler = new SuspendHandler(); - protected LocalConnector _connector; + protected Connector _connector; @Before public void init() throws Exception { - _connector = new LocalConnector(); + _connector = initConnector(); _server.setConnectors(new Connector[]{ _connector }); SessionHandler session = new SessionHandler(); @@ -49,6 +49,11 @@ public class AsyncContextTest _server.setHandler(session); _server.start(); } + + protected Connector initConnector() + { + return new LocalConnector(); + } @After public void destroy() throws Exception @@ -129,14 +134,21 @@ public class AsyncContextTest private synchronized String process(String content) throws Exception { - String request = "GET / HTTP/1.1\r\n" + "Host: localhost\r\n"; + String request = "GET / HTTP/1.1\r\n" + + "Host: localhost\r\n"+ + "Connection: close\r\n"; if (content==null) request+="\r\n"; else - request+="Content-Length: "+content.length()+"\r\n" + "\r\n" + content; + request+="Content-Length: "+content.length()+"\r\n" +"\r\n" + content; - return _connector.getResponses(request); + return getResponse(request); + } + + protected String getResponse(String request) throws Exception + { + return ((LocalConnector)_connector).getResponses(request); } private static class SuspendHandler extends HandlerWrapper diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/NetworkTrafficListenerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/NetworkTrafficListenerTest.java index 551fac58f05..56e8e66b68b 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/NetworkTrafficListenerTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/NetworkTrafficListenerTest.java @@ -13,6 +13,9 @@ package org.eclipse.jetty.server; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -21,6 +24,7 @@ import java.net.Socket; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; + import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; @@ -33,9 +37,6 @@ import org.eclipse.jetty.server.nio.NetworkTrafficSelectChannelConnector; import org.junit.After; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - public class NetworkTrafficListenerTest { private static final byte END_OF_CONTENT = '~'; @@ -94,10 +95,10 @@ public class NetworkTrafficListenerTest // Connect to the server Socket socket = new Socket("localhost", port); - assertTrue(openedLatch.await(1, TimeUnit.SECONDS)); - + assertTrue(openedLatch.await(10, TimeUnit.SECONDS)); + socket.close(); - assertTrue(closedLatch.await(1, TimeUnit.SECONDS)); + assertTrue(closedLatch.await(10, TimeUnit.SECONDS)); } @Test diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/RFC2616Test.java b/jetty-server/src/test/java/org/eclipse/jetty/server/RFC2616Test.java index 5492ac5a437..d072621a772 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/RFC2616Test.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/RFC2616Test.java @@ -29,8 +29,6 @@ import java.util.List; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.HandlerCollection; -import org.eclipse.jetty.util.log.Log; -import org.eclipse.jetty.util.log.StdErrLog; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -723,7 +721,7 @@ public class RFC2616Test "GET /R2 HTTP/1.0\n"+"Host: localhost\n"+"Connection: close\n"+"\n"+ "GET /R3 HTTP/1.0\n"+"Host: localhost\n"+"Connection: close\n"+"\n"); - + offset=checkContains(response,offset,"HTTP/1.1 200 OK\015\012","19.6.2 Keep-alive 1")+1; offset=checkContains(response,offset,"Connection: keep-alive","19.6.2 Keep-alive 1")+1; @@ -732,7 +730,6 @@ public class RFC2616Test offset=checkContains(response,offset,"/R1","19.6.2 Keep-alive 1")+1; offset=checkContains(response,offset,"HTTP/1.1 200 OK\015\012","19.6.2 Keep-alive 2")+11; - offset=checkContains(response,offset,"Connection: close","19.6.2 Keep-alive close")+1; offset=checkContains(response,offset,"/R2","19.6.2 Keep-alive close")+3; assertEquals("19.6.2 closed",-1,response.indexOf("/R3")); @@ -756,7 +753,6 @@ public class RFC2616Test offset=checkContains(response,offset,"ABCDEFGHIJ","19.6.2 Keep-alive 1")+1; offset=checkContains(response,offset,"HTTP/1.1 200 OK\015\012","19.6.2 Keep-alive 2")+11; - offset=checkContains(response,offset,"Connection: close","19.6.2 Keep-alive close")+1; offset=checkContains(response,offset,"/R2","19.6.2 Keep-alive close")+3; assertEquals("19.6.2 closed",-1,response.indexOf("/R3")); diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java index a71cc28090d..71cfdcd236f 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java @@ -492,13 +492,12 @@ public class RequestTest "\n" ); assertTrue(response.indexOf("200")>0); - assertTrue(response.indexOf("Connection: close")>0); assertTrue(response.indexOf("Hello World")>0); response=_connector.getResponses( "GET / HTTP/1.0\n"+ "Host: whatever\n"+ - "Connection: Other, keep-alive\n"+ + "Connection: Other,,keep-alive\n"+ "\n" ); assertTrue(response.indexOf("200")>0); diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java index ecc45ee835e..6f2b1172f44 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java @@ -29,6 +29,7 @@ import java.util.Map; import javax.servlet.ServletContext; import javax.servlet.ServletException; +import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSessionContext; @@ -80,7 +81,7 @@ public class ResponseTest @Test public void testContentType() throws Exception { - HttpConnection connection = new TestHttpConnection(connector,new ByteArrayEndPoint(), connector.getServer()); + AbstractHttpConnection connection = new TestHttpConnection(connector,new ByteArrayEndPoint(), connector.getServer()); Response response = connection.getResponse(); assertEquals(null,response.getContentType()); @@ -134,7 +135,7 @@ public class ResponseTest public void testLocale() throws Exception { - HttpConnection connection = new TestHttpConnection(connector,new ByteArrayEndPoint(), connector.getServer()); + AbstractHttpConnection connection = new TestHttpConnection(connector,new ByteArrayEndPoint(), connector.getServer()); Request request = connection.getRequest(); Response response = connection.getResponse(); ContextHandler context = new ContextHandler(); @@ -158,9 +159,8 @@ public class ResponseTest @Test public void testContentTypeCharacterEncoding() throws Exception { - HttpConnection connection = new TestHttpConnection(connector,new ByteArrayEndPoint(), connector.getServer()); + AbstractHttpConnection connection = new TestHttpConnection(connector,new ByteArrayEndPoint(), connector.getServer()); - Request request = connection.getRequest(); Response response = connection.getResponse(); @@ -332,7 +332,7 @@ public class ResponseTest public void testEncodeRedirect() throws Exception { - HttpConnection connection=new TestHttpConnection(connector,new ByteArrayEndPoint(), connector.getServer()); + AbstractHttpConnection connection=new TestHttpConnection(connector,new ByteArrayEndPoint(), connector.getServer()); Response response = new Response(connection); Request request = connection.getRequest(); request.setServerName("myhost"); @@ -397,7 +397,7 @@ public class ResponseTest for (int i=1;i= 0) - break; - } - - handleCount.set(0); - - // Send TCP FIN without SSL close alert - socket.close(); - - // Sleep for a while to detect eventual spin looping - TimeUnit.SECONDS.sleep(1); - - Assert.assertTrue("handle() invocations", handleCount.get()<=1); - Assert.assertTrue("endpoint not closed", endPointClosed.get()); - } - - @Test - public void testTruncationAttackBeforeReading() throws Exception - { - Socket socket = new Socket("localhost", connector.getLocalPort()); - SSLSocket sslSocket = (SSLSocket)sslContext.getSocketFactory().createSocket(socket, socket.getInetAddress().getHostName(), socket.getPort(), true); - sslSocket.setUseClientMode(true); - final CountDownLatch handshakeLatch = new CountDownLatch(1); - sslSocket.addHandshakeCompletedListener(new HandshakeCompletedListener() - { - public void handshakeCompleted(HandshakeCompletedEvent handshakeCompletedEvent) - { - handshakeLatch.countDown(); - } - }); - sslSocket.startHandshake(); - - Assert.assertTrue(handshakeLatch.await(1, TimeUnit.SECONDS)); - - String request = "" + - "GET / HTTP/1.1\r\n" + - "Host: localhost:" + connector.getLocalPort() + "\r\n" + - "\r\n"; - sslSocket.getOutputStream().write(request.getBytes("UTF-8")); - - // Do not read the response, just close the underlying socket - - handleCount.set(0); - - // Send TCP FIN without SSL close alert - socket.close(); - - // Sleep for a while to detect eventual spin looping - TimeUnit.SECONDS.sleep(1); - - Assert.assertEquals("handle() invocations", 1, handleCount.get()); - Assert.assertTrue("endpoint not closed", endPointClosed.get()); - } - - @Test - public void testTruncationAttackAfterHandshake() throws Exception - { - Socket socket = new Socket("localhost", connector.getLocalPort()); - SSLSocket sslSocket = (SSLSocket)sslContext.getSocketFactory().createSocket(socket, socket.getInetAddress().getHostName(), socket.getPort(), true); - sslSocket.setUseClientMode(true); - final CountDownLatch handshakeLatch = new CountDownLatch(1); - sslSocket.addHandshakeCompletedListener(new HandshakeCompletedListener() - { - public void handshakeCompleted(HandshakeCompletedEvent handshakeCompletedEvent) - { - handshakeLatch.countDown(); - } - }); - sslSocket.startHandshake(); - - Assert.assertTrue(handshakeLatch.await(1, TimeUnit.SECONDS)); - - handleCount.set(0); - - // Send TCP FIN without SSL close alert - socket.close(); - - // Sleep for a while to detect eventual spin looping - TimeUnit.SECONDS.sleep(1); - - Assert.assertTrue("endpoint closed", endPointClosed.get()); - Assert.assertEquals("handle() invocations", 1, handleCount.get()); - } - - - @Test - public void testTruncationAttackBeforeHandshake() throws Exception - { - Socket socket = new Socket("localhost", connector.getLocalPort()); - SSLSocket sslSocket = (SSLSocket)sslContext.getSocketFactory().createSocket(socket, socket.getInetAddress().getHostName(), socket.getPort(), true); - sslSocket.setUseClientMode(true); - - // Wait for the socket to be connected - TimeUnit.SECONDS.sleep(1); - - handleCount.set(0); - - // Send TCP FIN without SSL close alert - socket.close(); - - // Sleep for a while to detect eventual spin looping - TimeUnit.SECONDS.sleep(1); - - Assert.assertEquals("handle() invocations", 1, handleCount.get()); - Assert.assertTrue("endpoint not closed", endPointClosed.get()); - } - - private class EmptyHandler extends AbstractHandler - { - public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException - { - baseRequest.setHandled(true); - } - } -} diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslUploadTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslUploadTest.java index 7bcf8a3e79d..b06acc1ea60 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslUploadTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslUploadTest.java @@ -15,6 +15,7 @@ package org.eclipse.jetty.server.ssl; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import java.io.FileInputStream; import java.io.IOException; @@ -31,11 +32,11 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.eclipse.jetty.http.ssl.SslContextFactory; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.ssl.SslContextFactory; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -129,6 +130,7 @@ public class SslUploadTest InputStream in = socket.getInputStream(); String response = IO.toString(in); + assertTrue (response.indexOf("200")>0); // System.err.println(response); long end = System.nanoTime(); diff --git a/jetty-servlet/pom.xml b/jetty-servlet/pom.xml index 09908ee58f9..70ab5daf8a3 100644 --- a/jetty-servlet/pom.xml +++ b/jetty-servlet/pom.xml @@ -3,7 +3,7 @@ jetty-project org.eclipse.jetty - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 jetty-servlet diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java index b6ebf48708f..1f742af5e6d 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java @@ -22,6 +22,7 @@ import java.net.URL; import java.util.Enumeration; import java.util.List; import java.util.Map; + import javax.servlet.RequestDispatcher; import javax.servlet.ServletContext; import javax.servlet.ServletException; @@ -39,9 +40,9 @@ import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.ByteArrayBuffer; import org.eclipse.jetty.io.WriterOutputStream; +import org.eclipse.jetty.server.AbstractHttpConnection; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Dispatcher; -import org.eclipse.jetty.server.HttpConnection; import org.eclipse.jetty.server.HttpOutput; import org.eclipse.jetty.server.InclusiveByteRange; import org.eclipse.jetty.server.ResourceCache; @@ -280,7 +281,8 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory if (h.getServletInstance()==this) _defaultHolder=h; - if (LOG.isDebugEnabled()) LOG.debug("resource base = "+_resourceBase); + if (LOG.isDebugEnabled()) + LOG.debug("resource base = "+_resourceBase); } /** @@ -770,7 +772,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory } else { - Connector connector = HttpConnection.getCurrentConnection().getConnector(); + Connector connector = AbstractHttpConnection.getCurrentConnection().getConnector(); direct=connector instanceof NIOConnector && ((NIOConnector)connector).getUseDirectBuffers() && !(connector instanceof SslConnector); content_length=content.getContentLength(); } @@ -786,7 +788,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory // has a filter already written to the response? written = out instanceof HttpOutput ? ((HttpOutput)out).isWritten() - : HttpConnection.getCurrentConnection().getGenerator().isWritten(); + : AbstractHttpConnection.getCurrentConnection().getGenerator().isWritten(); } catch(IllegalStateException e) { @@ -809,7 +811,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory if (response instanceof Response) { writeOptionHeaders(((Response)response).getHttpFields()); - ((HttpConnection.Output)out).sendContent(content); + ((AbstractHttpConnection.Output)out).sendContent(content); } else { @@ -817,7 +819,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory if (buffer!=null) { writeHeaders(response,content,content_length); - ((HttpConnection.Output)out).sendContent(buffer); + ((AbstractHttpConnection.Output)out).sendContent(buffer); } else { diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ErrorPageErrorHandler.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ErrorPageErrorHandler.java index af9629ce146..ada696e330e 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ErrorPageErrorHandler.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ErrorPageErrorHandler.java @@ -18,14 +18,15 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; + import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.http.HttpMethods; +import org.eclipse.jetty.server.AbstractHttpConnection; import org.eclipse.jetty.server.Dispatcher; -import org.eclipse.jetty.server.HttpConnection; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ErrorHandler; @@ -63,7 +64,7 @@ public class ErrorPageErrorHandler extends ErrorHandler String method = request.getMethod(); if(!method.equals(HttpMethods.GET) && !method.equals(HttpMethods.POST) && !method.equals(HttpMethods.HEAD)) { - HttpConnection.getCurrentConnection().getRequest().setHandled(true); + AbstractHttpConnection.getCurrentConnection().getRequest().setHandled(true); return; } if (_errorPages!=null) diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterHolder.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterHolder.java index a4fd20c328e..937dba53f18 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterHolder.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterHolder.java @@ -19,12 +19,12 @@ import java.util.Collection; import java.util.EnumSet; import java.util.List; -import org.eclipse.jetty.server.DispatcherType; import javax.servlet.Filter; import javax.servlet.FilterConfig; -import org.eclipse.jetty.servlet.api.FilterRegistration; import javax.servlet.ServletException; +import org.eclipse.jetty.server.DispatcherType; +import org.eclipse.jetty.servlet.api.FilterRegistration; import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterMapping.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterMapping.java index bef03e29e15..7bd6559d6d8 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterMapping.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterMapping.java @@ -14,17 +14,14 @@ package org.eclipse.jetty.servlet; import java.io.IOException; -import java.util.Arrays; import java.util.EnumSet; -import org.eclipse.jetty.server.DispatcherType; - import org.eclipse.jetty.http.PathMap; +import org.eclipse.jetty.server.DispatcherType; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.component.AggregateLifeCycle; import org.eclipse.jetty.util.component.Dumpable; -import org.eclipse.jetty.util.log.Log; public class FilterMapping implements Dumpable diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Holder.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Holder.java index e1be344bdcf..cbbbb02ec9b 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Holder.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Holder.java @@ -21,11 +21,11 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; -import org.eclipse.jetty.servlet.api.Registration; import javax.servlet.ServletContext; import javax.servlet.UnavailableException; import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.servlet.api.Registration; import org.eclipse.jetty.util.Loader; import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.eclipse.jetty.util.component.AggregateLifeCycle; @@ -82,7 +82,8 @@ public class Holder extends AbstractLifeCycle implements Dumpable try { _class=Loader.loadClass(Holder.class, _className); - if(LOG.isDebugEnabled())LOG.debug("Holding {}",_class); + if(LOG.isDebugEnabled()) + LOG.debug("Holding {}",_class); } catch (Exception e) { diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Invoker.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Invoker.java index 9a378264186..b4c0ca6a442 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Invoker.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Invoker.java @@ -26,9 +26,9 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.server.AbstractHttpConnection; import org.eclipse.jetty.server.Dispatcher; import org.eclipse.jetty.server.Handler; -import org.eclipse.jetty.server.HttpConnection; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.HandlerWrapper; @@ -140,7 +140,8 @@ public class Invoker extends HttpServlet { // Found a named servlet (from a user's web.xml file) so // now we add a mapping for it - LOG.debug("Adding servlet mapping for named servlet:"+servlet+":"+URIUtil.addPaths(servlet_path,servlet)+"/*"); + if (LOG.isDebugEnabled()) + LOG.debug("Adding servlet mapping for named servlet:"+servlet+":"+URIUtil.addPaths(servlet_path,servlet)+"/*"); ServletMapping mapping = new ServletMapping(); mapping.setServletName(servlet); mapping.setPathSpec(URIUtil.addPaths(servlet_path,servlet)+"/*"); @@ -174,7 +175,8 @@ public class Invoker extends HttpServlet else { // Make a holder - LOG.debug("Making new servlet="+servlet+" with path="+path+"/*"); + if (LOG.isDebugEnabled()) + LOG.debug("Making new servlet="+servlet+" with path="+path+"/*"); holder=_servletHandler.addServletWithMapping(servlet, path+"/*"); if (_parameters!=null) @@ -211,7 +213,7 @@ public class Invoker extends HttpServlet } } - if (_verbose) + if (_verbose && LOG.isDebugEnabled()) LOG.debug("Dynamic load '"+servlet+"' at "+path); } } @@ -219,7 +221,7 @@ public class Invoker extends HttpServlet if (holder!=null) { - final Request baseRequest=(request instanceof Request)?((Request)request):HttpConnection.getCurrentConnection().getRequest(); + final Request baseRequest=(request instanceof Request)?((Request)request):AbstractHttpConnection.getCurrentConnection().getRequest(); holder.handle(baseRequest, new InvokedRequest(request,included,servlet,servlet_path,path_info), response); diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java index 72de75cf444..1a5e485f216 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java @@ -18,7 +18,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.EnumSet; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Queue; @@ -26,7 +25,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentMap; -import org.eclipse.jetty.server.DispatcherType; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.RequestDispatcher; @@ -46,8 +44,9 @@ import org.eclipse.jetty.io.EofException; import org.eclipse.jetty.io.RuntimeIOException; import org.eclipse.jetty.security.IdentityService; import org.eclipse.jetty.security.SecurityHandler; +import org.eclipse.jetty.server.AbstractHttpConnection; import org.eclipse.jetty.server.Dispatcher; -import org.eclipse.jetty.server.HttpConnection; +import org.eclipse.jetty.server.DispatcherType; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServletRequestHttpWrapper; @@ -390,27 +389,24 @@ public class ServletHandler extends ScopedHandler } if (LOG.isDebugEnabled()) - LOG.debug("servlet {} -> {}",baseRequest.getContextPath()+"|"+baseRequest.getServletPath()+"|"+baseRequest.getPathInfo(),servlet_holder); + LOG.debug("servlet {}|{}|{} -> {}",baseRequest.getContextPath(),baseRequest.getServletPath(),baseRequest.getPathInfo(),servlet_holder); try { // Do the filter/handling thang - if (servlet_holder!=null) - { - old_scope=baseRequest.getUserIdentityScope(); - baseRequest.setUserIdentityScope(servlet_holder); + old_scope=baseRequest.getUserIdentityScope(); + baseRequest.setUserIdentityScope(servlet_holder); - // start manual inline of nextScope(target,baseRequest,request,response); - if (never()) - nextScope(target,baseRequest,request,response); - else if (_nextScope!=null) - _nextScope.doScope(target,baseRequest,request, response); - else if (_outerScope!=null) - _outerScope.doHandle(target,baseRequest,request, response); - else - doHandle(target,baseRequest,request, response); - // end manual inline (pathentic attempt to reduce stack depth) - } + // start manual inline of nextScope(target,baseRequest,request,response); + if (never()) + nextScope(target,baseRequest,request,response); + else if (_nextScope!=null) + _nextScope.doScope(target,baseRequest,request, response); + else if (_outerScope!=null) + _outerScope.doHandle(target,baseRequest,request, response); + else + doHandle(target,baseRequest,request, response); + // end manual inline (pathentic attempt to reduce stack depth) } finally { @@ -455,13 +451,16 @@ public class ServletHandler extends ScopedHandler } } - LOG.debug("chain=",chain); + LOG.debug("chain={}",chain); try { if (servlet_holder==null) { - notFound(request, response); + if (getHandler()==null) + notFound(request, response); + else + nextHandle(target,baseRequest,request,response); } else { @@ -1231,7 +1230,8 @@ public class ServletHandler extends ScopedHandler HttpServletResponse response) throws IOException { - if(LOG.isDebugEnabled())LOG.debug("Not Found "+request.getRequestURI()); + if(LOG.isDebugEnabled()) + LOG.debug("Not Found "+request.getRequestURI()); response.sendError(HttpServletResponse.SC_NOT_FOUND); } @@ -1318,7 +1318,9 @@ public class ServletHandler extends ScopedHandler /* ------------------------------------------------------------ */ public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException - { + { + final Request baseRequest=(request instanceof Request)?((Request)request):AbstractHttpConnection.getCurrentConnection().getRequest(); + // pass to next filter if (_filterHolder!=null) { @@ -1329,7 +1331,6 @@ public class ServletHandler extends ScopedHandler filter.doFilter(request, response, _next); else { - final Request baseRequest=(request instanceof Request)?((Request)request):HttpConnection.getCurrentConnection().getRequest(); final boolean suspendable=baseRequest.isAsyncSupported(); if (suspendable) { @@ -1350,15 +1351,20 @@ public class ServletHandler extends ScopedHandler } // Call servlet + + HttpServletRequest srequest = (HttpServletRequest)request; if (_servletHolder != null) { if (LOG.isDebugEnabled()) LOG.debug("call servlet " + _servletHolder); - final Request baseRequest=(request instanceof Request)?((Request)request):HttpConnection.getCurrentConnection().getRequest(); _servletHolder.handle(baseRequest,request, response); } - else // Not found - notFound((HttpServletRequest)request, (HttpServletResponse)response); + else if (getHandler()==null) + notFound(srequest, (HttpServletResponse)response); + else + nextHandle(URIUtil.addPaths(srequest.getServletPath(),srequest.getPathInfo()), + baseRequest,srequest,(HttpServletResponse)response); + } public String toString() @@ -1392,13 +1398,15 @@ public class ServletHandler extends ScopedHandler public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { - if (LOG.isDebugEnabled()) LOG.debug("doFilter " + _filter); + if (LOG.isDebugEnabled()) + LOG.debug("doFilter " + _filter); // pass to next filter if (_filter < LazyList.size(_chain)) { FilterHolder holder= (FilterHolder)LazyList.get(_chain, _filter++); - if (LOG.isDebugEnabled()) LOG.debug("call filter " + holder); + if (LOG.isDebugEnabled()) + LOG.debug("call filter " + holder); Filter filter= holder.getFilter(); if (holder.isAsyncSupported() || !_baseRequest.isAsyncSupported()) @@ -1422,13 +1430,21 @@ public class ServletHandler extends ScopedHandler } // Call servlet + HttpServletRequest srequest = (HttpServletRequest)request; if (_servletHolder != null) { - if (LOG.isDebugEnabled()) LOG.debug("call servlet " + _servletHolder); + if (LOG.isDebugEnabled()) + LOG.debug("call servlet " + _servletHolder); _servletHolder.handle(_baseRequest,request, response); } - else // Not found - notFound((HttpServletRequest)request, (HttpServletResponse)response); + else if (getHandler()==null) + notFound(srequest, (HttpServletResponse)response); + else + { + Request baseRequest=(request instanceof Request)?((Request)request):AbstractHttpConnection.getCurrentConnection().getRequest(); + nextHandle(URIUtil.addPaths(srequest.getServletPath(),srequest.getPathInfo()), + baseRequest,srequest,(HttpServletResponse)response); + } } /* ------------------------------------------------------------ */ diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java index 1bcbe0822fd..59299c608c4 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java @@ -25,21 +25,20 @@ import java.util.Map; import java.util.Set; import java.util.Stack; - import javax.servlet.Servlet; import javax.servlet.ServletConfig; -import javax.servlet.ServletException; -import org.eclipse.jetty.servlet.api.ServletRegistration; - import javax.servlet.ServletContext; +import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.SingleThreadModel; import javax.servlet.UnavailableException; + import org.eclipse.jetty.security.IdentityService; import org.eclipse.jetty.security.RunAsToken; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.UserIdentity; +import org.eclipse.jetty.servlet.api.ServletRegistration; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/listener/ELContextCleaner.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/listener/ELContextCleaner.java index 6dd03442b81..285de0f3035 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/listener/ELContextCleaner.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/listener/ELContextCleaner.java @@ -14,8 +14,6 @@ package org.eclipse.jetty.servlet.listener; import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.Iterator; import java.util.concurrent.ConcurrentHashMap; diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DefaultServletTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DefaultServletTest.java index 4a07b031192..4a4f2fad1b5 100644 --- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DefaultServletTest.java +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DefaultServletTest.java @@ -1,8 +1,11 @@ package org.eclipse.jetty.servlet; +import static org.junit.Assert.assertTrue; + import java.io.File; import java.io.FileOutputStream; import java.io.IOException; + import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; @@ -11,6 +14,7 @@ import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import junit.framework.AssertionFailedError; + import org.eclipse.jetty.server.LocalConnector; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.toolchain.test.FS; @@ -25,8 +29,6 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import static org.junit.Assert.assertTrue; - public class DefaultServletTest { @Rule diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherTest.java index c034633d125..b17796f0d4d 100644 --- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherTest.java +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherTest.java @@ -13,14 +13,13 @@ package org.eclipse.jetty.servlet; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import java.io.IOException; -import java.io.PrintWriter; -import java.net.HttpRetryException; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; import javax.servlet.Filter; import javax.servlet.FilterChain; @@ -30,7 +29,6 @@ import javax.servlet.RequestDispatcher; import javax.servlet.Servlet; import javax.servlet.ServletContext; import javax.servlet.ServletException; -import javax.servlet.ServletOutputStream; import javax.servlet.ServletRequest; import javax.servlet.ServletRequestWrapper; import javax.servlet.ServletResponse; @@ -52,12 +50,8 @@ import org.eclipse.jetty.server.handler.ResourceHandler; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - public class DispatcherTest { private Server _server; diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/InvokerTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/InvokerTest.java index 1456e80cb53..ae9da69e67a 100644 --- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/InvokerTest.java +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/InvokerTest.java @@ -13,7 +13,10 @@ package org.eclipse.jetty.servlet; +import static org.junit.Assert.assertEquals; + import java.io.IOException; + import javax.servlet.Servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -26,8 +29,6 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; -import static org.junit.Assert.assertEquals; - /** * */ diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/StatisticsServletTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/StatisticsServletTest.java index 7154d32acaa..d775512cb7f 100644 --- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/StatisticsServletTest.java +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/StatisticsServletTest.java @@ -13,6 +13,7 @@ package org.eclipse.jetty.servlet; import junit.framework.AssertionFailedError; + import org.eclipse.jetty.server.LocalConnector; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.StatisticsHandler; diff --git a/jetty-servlets/pom.xml b/jetty-servlets/pom.xml index a1e809a71b5..9e112abff9e 100644 --- a/jetty-servlets/pom.xml +++ b/jetty-servlets/pom.xml @@ -3,7 +3,7 @@ jetty-project org.eclipse.jetty - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 jetty-servlets diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CGI.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CGI.java index 0c6fe30bbc8..8df44021880 100644 --- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CGI.java +++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CGI.java @@ -4,11 +4,11 @@ // 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 +// 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. +// You may elect to redistribute this code under either of these licenses. // ======================================================================== package org.eclipse.jetty.servlets; @@ -34,27 +34,26 @@ import org.eclipse.jetty.util.log.Logger; //----------------------------------------------------------------------------- /** * CGI Servlet. - * - * The cgi bin directory can be set with the "cgibinResourceBase" init parameter - * or it will default to the resource base of the context. - * - * The "commandPrefix" init parameter may be used to set a prefix to all - * commands passed to exec. This can be used on systems that need assistance to - * execute a particular file type. For example on windows this can be set to - * "perl" so that perl scripts are executed. - * - * The "Path" init param is passed to the exec environment as PATH. Note: Must - * be run unpacked somewhere in the filesystem. - * - * Any initParameter that starts with ENV_ is used to set an environment - * variable with the name stripped of the leading ENV_ and using the init - * parameter value. - * - * - * + *

    + * The cgi bin directory can be set with the "cgibinResourceBase" init parameter or it will default to the resource base of the context. If the + * "cgibinResourceBaseIsRelative" init parameter is set the resource base is relative to the webapp. For example "WEB-INF/cgi" would work. + *
    + * Not that this only works for extracted war files as "jar cf" will not reserve the execute permissions on the cgi files. + *

    + * The "commandPrefix" init parameter may be used to set a prefix to all commands passed to exec. This can be used on systems that need assistance to execute a + * particular file type. For example on windows this can be set to "perl" so that perl scripts are executed. + *

    + * The "Path" init param is passed to the exec environment as PATH. Note: Must be run unpacked somewhere in the filesystem. + *

    + * Any initParameter that starts with ENV_ is used to set an environment variable with the name stripped of the leading ENV_ and using the init parameter value. */ public class CGI extends HttpServlet { + /** + * + */ + private static final long serialVersionUID = -6182088932884791073L; + private static final Logger LOG = Log.getLogger(CGI.class); private boolean _ok; @@ -63,81 +62,89 @@ public class CGI extends HttpServlet private String _cmdPrefix; private EnvList _env; private boolean _ignoreExitState; + private boolean _relative; /* ------------------------------------------------------------ */ + @Override public void init() throws ServletException { - _env=new EnvList(); - _cmdPrefix=getInitParameter("commandPrefix"); + _env = new EnvList(); + _cmdPrefix = getInitParameter("commandPrefix"); + _relative = Boolean.parseBoolean(getInitParameter("cgibinResourceBaseIsRelative")); - String tmp=getInitParameter("cgibinResourceBase"); - if (tmp==null) + String tmp = getInitParameter("cgibinResourceBase"); + if (tmp == null) { - tmp=getInitParameter("resourceBase"); - if (tmp==null) - tmp=getServletContext().getRealPath("/"); + tmp = getInitParameter("resourceBase"); + if (tmp == null) + tmp = getServletContext().getRealPath("/"); + } + else if (_relative) + { + tmp = getServletContext().getRealPath(tmp); } - if (tmp==null) + if (tmp == null) { LOG.warn("CGI: no CGI bin !"); return; } - File dir=new File(tmp); + File dir = new File(tmp); if (!dir.exists()) { - LOG.warn("CGI: CGI bin does not exist - "+dir); + LOG.warn("CGI: CGI bin does not exist - " + dir); return; } if (!dir.canRead()) { - LOG.warn("CGI: CGI bin is not readable - "+dir); + LOG.warn("CGI: CGI bin is not readable - " + dir); return; } if (!dir.isDirectory()) { - LOG.warn("CGI: CGI bin is not a directory - "+dir); + LOG.warn("CGI: CGI bin is not a directory - " + dir); return; } try { - _docRoot=dir.getCanonicalFile(); + _docRoot = dir.getCanonicalFile(); } catch (IOException e) { - LOG.warn("CGI: CGI bin failed - "+dir,e); + LOG.warn("CGI: CGI bin failed - " + dir,e); return; } - _path=getInitParameter("Path"); - if (_path!=null) + _path = getInitParameter("Path"); + if (_path != null) _env.set("PATH",_path); - _ignoreExitState="true".equalsIgnoreCase(getInitParameter("ignoreExitState")); - Enumeration e=getInitParameterNames(); + _ignoreExitState = "true".equalsIgnoreCase(getInitParameter("ignoreExitState")); + Enumeration e = getInitParameterNames(); while (e.hasMoreElements()) { - String n=(String)e.nextElement(); - if (n!=null&&n.startsWith("ENV_")) + String n = (String)e.nextElement(); + if (n != null && n.startsWith("ENV_")) _env.set(n.substring(4),getInitParameter(n)); } - if(!_env.envMap.containsKey("SystemRoot")) + if (!_env.envMap.containsKey("SystemRoot")) { - String os = System.getProperty("os.name"); - if (os!=null && os.toLowerCase().indexOf("windows")!=-1) + String os = System.getProperty("os.name"); + if (os != null && os.toLowerCase().indexOf("windows") != -1) { - _env.set("SystemRoot", "C:\\WINDOWS"); + _env.set("SystemRoot","C:\\WINDOWS"); } - } - - _ok=true; + } + + _ok = true; } /* ------------------------------------------------------------ */ + @Override public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { if (!_ok) @@ -145,39 +152,38 @@ public class CGI extends HttpServlet res.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); return; } - - String pathInContext=StringUtil.nonNull(req.getServletPath())+StringUtil.nonNull(req.getPathInfo()); + String pathInContext = (_relative?"":StringUtil.nonNull(req.getServletPath())) + StringUtil.nonNull(req.getPathInfo()); if (LOG.isDebugEnabled()) { - LOG.debug("CGI: ContextPath : "+req.getContextPath()); - LOG.debug("CGI: ServletPath : "+req.getServletPath()); - LOG.debug("CGI: PathInfo : "+req.getPathInfo()); - LOG.debug("CGI: _docRoot : "+_docRoot); - LOG.debug("CGI: _path : "+_path); - LOG.debug("CGI: _ignoreExitState: "+_ignoreExitState); + LOG.debug("CGI: ContextPath : " + req.getContextPath()); + LOG.debug("CGI: ServletPath : " + req.getServletPath()); + LOG.debug("CGI: PathInfo : " + req.getPathInfo()); + LOG.debug("CGI: _docRoot : " + _docRoot); + LOG.debug("CGI: _path : " + _path); + LOG.debug("CGI: _ignoreExitState: " + _ignoreExitState); } // pathInContext may actually comprises scriptName/pathInfo...We will // walk backwards up it until we find the script - the rest must // be the pathInfo; - String both=pathInContext; - String first=both; - String last=""; + String both = pathInContext; + String first = both; + String last = ""; - File exe=new File(_docRoot,first); + File exe = new File(_docRoot,first); - while ((first.endsWith("/")||!exe.exists())&&first.length()>=0) + while ((first.endsWith("/") || !exe.exists()) && first.length() >= 0) { - int index=first.lastIndexOf('/'); + int index = first.lastIndexOf('/'); - first=first.substring(0,index); - last=both.substring(index,both.length()); - exe=new File(_docRoot,first); + first = first.substring(0,index); + last = both.substring(index,both.length()); + exe = new File(_docRoot,first); } - if (first.length()==0||!exe.exists()||exe.isDirectory()||!exe.getCanonicalPath().equals(exe.getAbsolutePath())) + if (first.length() == 0 || !exe.exists() || exe.isDirectory() || !exe.getCanonicalPath().equals(exe.getAbsolutePath())) { res.sendError(404); } @@ -185,8 +191,8 @@ public class CGI extends HttpServlet { if (LOG.isDebugEnabled()) { - LOG.debug("CGI: script is "+exe); - LOG.debug("CGI: pathInfo is "+last); + LOG.debug("CGI: script is " + exe); + LOG.debug("CGI: pathInfo is " + last); } exec(exe,last,req,res); } @@ -198,19 +204,19 @@ public class CGI extends HttpServlet */ private void exec(File command, String pathInfo, HttpServletRequest req, HttpServletResponse res) throws IOException { - String path=command.getAbsolutePath(); - File dir=command.getParentFile(); - String scriptName=req.getRequestURI().substring(0,req.getRequestURI().length()-pathInfo.length()); - String scriptPath=getServletContext().getRealPath(scriptName); - String pathTranslated=req.getPathTranslated(); + String path = command.getAbsolutePath(); + File dir = command.getParentFile(); + String scriptName = req.getRequestURI().substring(0,req.getRequestURI().length() - pathInfo.length()); + String scriptPath = getServletContext().getRealPath(scriptName); + String pathTranslated = req.getPathTranslated(); - int len=req.getContentLength(); - if (len<0) - len=0; - if ((pathTranslated==null)||(pathTranslated.length()==0)) - pathTranslated=path; + int len = req.getContentLength(); + if (len < 0) + len = 0; + if ((pathTranslated == null) || (pathTranslated.length() == 0)) + pathTranslated = path; - EnvList env=new EnvList(_env); + EnvList env = new EnvList(_env); // these ones are from "The WWW Common Gateway Interface Version 1.1" // look at : // http://Web.Golux.Com/coar/cgi/draft-coar-cgi-v11-03-clean.html#6.1.1 @@ -218,7 +224,7 @@ public class CGI extends HttpServlet env.set("CONTENT_LENGTH",Integer.toString(len)); env.set("CONTENT_TYPE",req.getContentType()); env.set("GATEWAY_INTERFACE","CGI/1.1"); - if ((pathInfo!=null)&&(pathInfo.length()>0)) + if ((pathInfo != null) && (pathInfo.length() > 0)) { env.set("PATH_INFO",pathInfo); } @@ -240,12 +246,12 @@ public class CGI extends HttpServlet env.set("SERVER_PROTOCOL",req.getProtocol()); env.set("SERVER_SOFTWARE",getServletContext().getServerInfo()); - Enumeration enm=req.getHeaderNames(); + Enumeration enm = req.getHeaderNames(); while (enm.hasMoreElements()) { - String name=(String)enm.nextElement(); - String value=req.getHeader(name); - env.set("HTTP_"+name.toUpperCase().replace('-','_'),value); + String name = (String)enm.nextElement(); + String value = req.getHeader(name); + env.set("HTTP_" + name.toUpperCase().replace('-','_'),value); } // these extra ones were from printenv on www.dev.nomura.co.uk @@ -257,28 +263,28 @@ public class CGI extends HttpServlet // are we meant to decode args here ? or does the script get them // via PATH_INFO ? if we are, they should be decoded and passed // into exec here... - String execCmd=path; - if ((execCmd.charAt(0)!='"')&&(execCmd.indexOf(" ")>=0)) - execCmd="\""+execCmd+"\""; - if (_cmdPrefix!=null) - execCmd=_cmdPrefix+" "+execCmd; + String execCmd = path; + if ((execCmd.charAt(0) != '"') && (execCmd.indexOf(" ") >= 0)) + execCmd = "\"" + execCmd + "\""; + if (_cmdPrefix != null) + execCmd = _cmdPrefix + " " + execCmd; - Process p=(dir==null)?Runtime.getRuntime().exec(execCmd,env.getEnvArray()):Runtime.getRuntime().exec(execCmd,env.getEnvArray(),dir); + Process p = (dir == null)?Runtime.getRuntime().exec(execCmd,env.getEnvArray()):Runtime.getRuntime().exec(execCmd,env.getEnvArray(),dir); // hook processes input to browser's output (async) - final InputStream inFromReq=req.getInputStream(); - final OutputStream outToCgi=p.getOutputStream(); - final int inLength=len; + final InputStream inFromReq = req.getInputStream(); + final OutputStream outToCgi = p.getOutputStream(); + final int inLength = len; IO.copyThread(p.getErrorStream(),System.err); - + new Thread(new Runnable() { public void run() { try { - if (inLength>0) + if (inLength > 0) IO.copy(inFromReq,outToCgi,inLength); outToCgi.close(); } @@ -296,28 +302,28 @@ public class CGI extends HttpServlet { // read any headers off the top of our input stream // NOTE: Multiline header items not supported! - String line=null; - InputStream inFromCgi=p.getInputStream(); + String line = null; + InputStream inFromCgi = p.getInputStream(); - //br=new BufferedReader(new InputStreamReader(inFromCgi)); - //while ((line=br.readLine())!=null) - while( (line = getTextLineFromStream( inFromCgi )).length() > 0 ) + // br=new BufferedReader(new InputStreamReader(inFromCgi)); + // while ((line=br.readLine())!=null) + while ((line = getTextLineFromStream(inFromCgi)).length() > 0) { if (!line.startsWith("HTTP")) { - int k=line.indexOf(':'); - if (k>0) + int k = line.indexOf(':'); + if (k > 0) { - String key=line.substring(0,k).trim(); - String value = line.substring(k+1).trim(); + String key = line.substring(0,k).trim(); + String value = line.substring(k + 1).trim(); if ("Location".equals(key)) { res.sendRedirect(res.encodeRedirectURL(value)); } else if ("Status".equals(key)) { - String[] token = value.split( " " ); - int status=Integer.parseInt(token[0]); + String[] token = value.split(" "); + int status = Integer.parseInt(token[0]); res.setStatus(status); } else @@ -330,15 +336,15 @@ public class CGI extends HttpServlet } // copy cgi content to response stream... os = res.getOutputStream(); - IO.copy(inFromCgi, os); + IO.copy(inFromCgi,os); p.waitFor(); if (!_ignoreExitState) { - int exitValue=p.exitValue(); - if (0!=exitValue) + int exitValue = p.exitValue(); + if (0 != exitValue) { - LOG.warn("Non-zero exit status ("+exitValue+") from CGI program: "+path); + LOG.warn("Non-zero exit status (" + exitValue + ") from CGI program: " + path); if (!res.isCommitted()) res.sendError(500,"Failed to exec CGI"); } @@ -356,13 +362,13 @@ public class CGI extends HttpServlet } finally { - if( os != null ) + if (os != null) { try { os.close(); } - catch(Exception e) + catch (Exception e) { LOG.ignore(e); } @@ -375,19 +381,24 @@ public class CGI extends HttpServlet /** * Utility method to get a line of text from the input stream. - * @param is the input stream + * + * @param is + * the input stream * @return the line of text * @throws IOException */ - private String getTextLineFromStream( InputStream is ) throws IOException { + private String getTextLineFromStream(InputStream is) throws IOException + { StringBuilder buffer = new StringBuilder(); int b; - while( (b = is.read()) != -1 && b != (int) '\n' ) { - buffer.append( (char) b ); - } - return buffer.toString().trim(); + while ((b = is.read()) != -1 && b != '\n') + { + buffer.append((char)b); + } + return buffer.toString().trim(); } + /* ------------------------------------------------------------ */ /** * private utility class that manages the Environment passed to exec. @@ -398,12 +409,12 @@ public class CGI extends HttpServlet EnvList() { - envMap=new HashMap(); + envMap = new HashMap(); } EnvList(EnvList l) { - envMap=new HashMap(l.envMap); + envMap = new HashMap(l.envMap); } /** @@ -411,7 +422,7 @@ public class CGI extends HttpServlet */ public void set(String name, String value) { - envMap.put(name,name+"="+StringUtil.nonNull(value)); + envMap.put(name,name + "=" + StringUtil.nonNull(value)); } /** Get representation suitable for passing to exec. */ @@ -420,6 +431,7 @@ public class CGI extends HttpServlet return (String[])envMap.values().toArray(new String[envMap.size()]); } + @Override public String toString() { return envMap.toString(); diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CloseableDoSFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CloseableDoSFilter.java index 0bec5df794e..9b2b659dcff 100644 --- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CloseableDoSFilter.java +++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CloseableDoSFilter.java @@ -18,7 +18,7 @@ import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.eclipse.jetty.server.HttpConnection; +import org.eclipse.jetty.server.AbstractHttpConnection; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -37,7 +37,7 @@ public class CloseableDoSFilter extends DoSFilter { try { - Request base_request=(request instanceof Request)?(Request)request:HttpConnection.getCurrentConnection().getRequest(); + Request base_request=(request instanceof Request)?(Request)request:AbstractHttpConnection.getCurrentConnection().getRequest(); base_request.getConnection().getEndPoint().close(); } catch(IOException e) diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CrossOriginFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CrossOriginFilter.java index e38c73de306..f4026369372 100644 --- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CrossOriginFilter.java +++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CrossOriginFilter.java @@ -18,6 +18,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; + import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; @@ -74,7 +75,7 @@ import org.eclipse.jetty.util.log.Logger; */ public class CrossOriginFilter implements Filter { - private static final Logger logger = Log.getLogger(CrossOriginFilter.class); + private static final Logger LOG = Log.getLogger(CrossOriginFilter.class); // Request headers private static final String ORIGIN_HEADER = "Origin"; @@ -145,7 +146,7 @@ public class CrossOriginFilter implements Filter } catch (NumberFormatException x) { - logger.info("Cross-origin filter, could not parse '{}' parameter as integer: {}", PREFLIGHT_MAX_AGE_PARAM, preflightMaxAgeConfig); + LOG.info("Cross-origin filter, could not parse '{}' parameter as integer: {}", PREFLIGHT_MAX_AGE_PARAM, preflightMaxAgeConfig); } String allowedCredentialsConfig = config.getInitParameter(ALLOW_CREDENTIALS_PARAM); @@ -153,12 +154,15 @@ public class CrossOriginFilter implements Filter allowedCredentialsConfig = "true"; allowCredentials = Boolean.parseBoolean(allowedCredentialsConfig); - logger.debug("Cross-origin filter configuration: " + - ALLOWED_ORIGINS_PARAM + " = " + allowedOriginsConfig + ", " + - ALLOWED_METHODS_PARAM + " = " + allowedMethodsConfig + ", " + - ALLOWED_HEADERS_PARAM + " = " + allowedHeadersConfig + ", " + - PREFLIGHT_MAX_AGE_PARAM + " = " + preflightMaxAgeConfig + ", " + - ALLOW_CREDENTIALS_PARAM + " = " + allowedCredentialsConfig); + if (LOG.isDebugEnabled()) + { + LOG.debug("Cross-origin filter configuration: " + + ALLOWED_ORIGINS_PARAM + " = " + allowedOriginsConfig + ", " + + ALLOWED_METHODS_PARAM + " = " + allowedMethodsConfig + ", " + + ALLOWED_HEADERS_PARAM + " = " + allowedHeadersConfig + ", " + + PREFLIGHT_MAX_AGE_PARAM + " = " + preflightMaxAgeConfig + ", " + + ALLOW_CREDENTIALS_PARAM + " = " + allowedCredentialsConfig); + } } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException @@ -176,23 +180,23 @@ public class CrossOriginFilter implements Filter { if (isSimpleRequest(request)) { - logger.debug("Cross-origin request to {} is a simple cross-origin request", request.getRequestURI()); + LOG.debug("Cross-origin request to {} is a simple cross-origin request", request.getRequestURI()); handleSimpleResponse(request, response, origin); } else if (isPreflightRequest(request)) { - logger.debug("Cross-origin request to {} is a preflight cross-origin request", request.getRequestURI()); + LOG.debug("Cross-origin request to {} is a preflight cross-origin request", request.getRequestURI()); handlePreflightResponse(request, response, origin); } else { - logger.debug("Cross-origin request to {} is a non-simple cross-origin request", request.getRequestURI()); + LOG.debug("Cross-origin request to {} is a non-simple cross-origin request", request.getRequestURI()); handleSimpleResponse(request, response, origin); } } else { - logger.debug("Cross-origin request to " + request.getRequestURI() + " with origin " + origin + " does not match allowed origins " + allowedOrigins); + LOG.debug("Cross-origin request to " + request.getRequestURI() + " with origin " + origin + " does not match allowed origins " + allowedOrigins); } } @@ -291,18 +295,18 @@ public class CrossOriginFilter implements Filter private boolean isMethodAllowed(HttpServletRequest request) { String accessControlRequestMethod = request.getHeader(ACCESS_CONTROL_REQUEST_METHOD_HEADER); - logger.debug("{} is {}", ACCESS_CONTROL_REQUEST_METHOD_HEADER, accessControlRequestMethod); + LOG.debug("{} is {}", ACCESS_CONTROL_REQUEST_METHOD_HEADER, accessControlRequestMethod); boolean result = false; if (accessControlRequestMethod != null) result = allowedMethods.contains(accessControlRequestMethod); - logger.debug("Method {} is" + (result ? "" : " not") + " among allowed methods {}", accessControlRequestMethod, allowedMethods); + LOG.debug("Method {} is" + (result ? "" : " not") + " among allowed methods {}", accessControlRequestMethod, allowedMethods); return result; } private boolean areHeadersAllowed(HttpServletRequest request) { String accessControlRequestHeaders = request.getHeader(ACCESS_CONTROL_REQUEST_HEADERS_HEADER); - logger.debug("{} is {}", ACCESS_CONTROL_REQUEST_HEADERS_HEADER, accessControlRequestHeaders); + LOG.debug("{} is {}", ACCESS_CONTROL_REQUEST_HEADERS_HEADER, accessControlRequestHeaders); boolean result = true; if (accessControlRequestHeaders != null) { @@ -325,7 +329,7 @@ public class CrossOriginFilter implements Filter } } } - logger.debug("Headers [{}] are" + (result ? "" : " not") + " among allowed headers {}", accessControlRequestHeaders, allowedHeaders); + LOG.debug("Headers [{}] are" + (result ? "" : " not") + " among allowed headers {}", accessControlRequestHeaders, allowedHeaders); return result; } diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java index 51c94694459..6c12e2e75b3 100644 --- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java +++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java @@ -14,13 +14,16 @@ package org.eclipse.jetty.servlets; import java.io.IOException; +import java.io.Serializable; import java.util.HashSet; +import java.util.Map; import java.util.Queue; import java.util.StringTokenizer; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; + import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; @@ -31,8 +34,10 @@ import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; +import javax.servlet.http.HttpSessionActivationListener; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; +import javax.servlet.http.HttpSessionEvent; import org.eclipse.jetty.continuation.Continuation; import org.eclipse.jetty.continuation.ContinuationListener; @@ -907,13 +912,14 @@ public class DoSFilter implements Filter * A RateTracker is associated with a connection, and stores request rate * data. */ - class RateTracker extends Timeout.Task implements HttpSessionBindingListener + class RateTracker extends Timeout.Task implements HttpSessionBindingListener, HttpSessionActivationListener { - protected final String _id; - protected final int _type; - protected final long[] _timestamps; - protected int _next; - + transient protected final String _id; + transient protected final int _type; + transient protected final long[] _timestamps; + transient protected int _next; + + public RateTracker(String id, int type,int maxRequestsPerSecond) { _id = id; @@ -952,25 +958,49 @@ public class DoSFilter implements Filter public void valueBound(HttpSessionBindingEvent event) - { + { + if (LOG.isDebugEnabled()) + LOG.debug("Value bound:"+_id); } public void valueUnbound(HttpSessionBindingEvent event) { - _rateTrackers.remove(_id); + //take the tracker out of the list of trackers + if (_rateTrackers != null) + _rateTrackers.remove(_id); + if (LOG.isDebugEnabled()) LOG.debug("Tracker removed: "+_id); } + public void sessionWillPassivate(HttpSessionEvent se) + { + //take the tracker of the list of trackers (if its still there) + //and ensure that we take ourselves out of the session so we are not saved + if (_rateTrackers != null) + _rateTrackers.remove(_id); + se.getSession().removeAttribute(__TRACKER); + if (LOG.isDebugEnabled()) LOG.debug("Value removed: "+_id); + } + + public void sessionDidActivate(HttpSessionEvent se) + { + LOG.warn("Unexpected session activation"); + } + + public void expired() { - long now = _trackerTimeoutQ.getNow(); - int latestIndex = _next == 0 ? 3 : (_next - 1 ) % _timestamps.length; - long last=_timestamps[latestIndex]; - boolean hasRecentRequest = last != 0 && (now-last)<1000L; + if (_rateTrackers != null && _trackerTimeoutQ != null) + { + long now = _trackerTimeoutQ.getNow(); + int latestIndex = _next == 0 ? 3 : (_next - 1 ) % _timestamps.length; + long last=_timestamps[latestIndex]; + boolean hasRecentRequest = last != 0 && (now-last)<1000L; - if (hasRecentRequest) - reschedule(); - else - _rateTrackers.remove(_id); + if (hasRecentRequest) + reschedule(); + else + _rateTrackers.remove(_id); + } } @Override @@ -978,6 +1008,8 @@ public class DoSFilter implements Filter { return "RateTracker/"+_id+"/"+_type; } + + } class FixedRateTracker extends RateTracker diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/IncludableGzipFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/IncludableGzipFilter.java index 0084ddaf861..7d9a24ac58a 100644 --- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/IncludableGzipFilter.java +++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/IncludableGzipFilter.java @@ -67,10 +67,10 @@ public class IncludableGzipFilter extends GzipFilter public IncludableResponseWrapper(HttpServletRequest request, HttpServletResponse response) { super(request,response); - - _mimeTypes = IncludableGzipFilter.this._mimeTypes; - _bufferSize = IncludableGzipFilter.this._bufferSize; - _minGzipSize = IncludableGzipFilter.this._minGzipSize; + + super.setMimeTypes(IncludableGzipFilter.this._mimeTypes); + super.setBufferSize(IncludableGzipFilter.this._bufferSize); + super.setMinGzipSize(IncludableGzipFilter.this._minGzipSize); } @Override diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/MultiPartFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/MultiPartFilter.java index 4111a1de6ff..049f37072b1 100644 --- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/MultiPartFilter.java +++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/MultiPartFilter.java @@ -63,6 +63,7 @@ import org.eclipse.jetty.util.TypeUtil; */ public class MultiPartFilter implements Filter { + public final static String CONTENT_TYPE_SUFFIX=".org.eclipse.jetty.servlet.contentType"; private final static String FILES ="org.eclipse.jetty.servlet.MultiPartFilter.files"; private File tempdir; private boolean _deleteFiles; @@ -132,8 +133,11 @@ public class MultiPartFilter implements Filter String content_disposition=null; String content_transfer_encoding=null; + outer:while(!lastPart) { + String type_content=null; + while(true) { // read a line @@ -155,7 +159,9 @@ public class MultiPartFilter implements Filter if(key.equals("content-disposition")) content_disposition=value; else if(key.equals("content-transfer-encoding")) - content_transfer_encoding=value; + content_transfer_encoding=value; + else if (key.equals("content-type")) + type_content = value; } } // Extract content-disposition @@ -207,6 +213,8 @@ public class MultiPartFilter implements Filter out = new BufferedOutputStream(out, _fileOutputBuffer); request.setAttribute(name,file); params.add(name, filename); + if (type_content != null) + params.add(name+CONTENT_TYPE_SUFFIX, type_content); if (_deleteFiles) { @@ -330,6 +338,8 @@ public class MultiPartFilter implements Filter { bytes = ((ByteArrayOutputStream)out).toByteArray(); params.add(name,bytes); + if (type_content != null) + params.add(name+CONTENT_TYPE_SUFFIX, type_content); } } diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/ProxyServlet.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/ProxyServlet.java index c506a82c0e4..2b186b49677 100644 --- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/ProxyServlet.java +++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/ProxyServlet.java @@ -27,6 +27,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.StringTokenizer; + import javax.servlet.Servlet; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; @@ -38,7 +39,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.client.HttpClient; -import org.eclipse.jetty.client.HttpConnection; import org.eclipse.jetty.client.HttpExchange; import org.eclipse.jetty.continuation.Continuation; import org.eclipse.jetty.continuation.ContinuationSupport; @@ -49,15 +49,11 @@ import org.eclipse.jetty.http.HttpURI; import org.eclipse.jetty.http.PathMap; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.EofException; -import org.eclipse.jetty.servlet.Holder; import org.eclipse.jetty.util.HostMap; import org.eclipse.jetty.util.IO; -import org.eclipse.jetty.util.Loader; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; -import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.thread.QueuedThreadPool; -import org.omg.CORBA._PolicyStub; /** * Asynchronous Proxy Servlet. diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/AbstractDoSFilterTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/AbstractDoSFilterTest.java index c2def316972..3774a28bf8f 100644 --- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/AbstractDoSFilterTest.java +++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/AbstractDoSFilterTest.java @@ -1,7 +1,11 @@ package org.eclipse.jetty.servlets; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import java.io.IOException; import java.net.Socket; + import javax.servlet.Filter; import javax.servlet.Servlet; import javax.servlet.ServletException; @@ -18,9 +22,6 @@ import org.junit.AfterClass; import org.junit.Before; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - /** * @version $Revision$ $Date$ */ diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/AsyncProxyServer.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/AsyncProxyServer.java index 10b93271858..252783d68e8 100644 --- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/AsyncProxyServer.java +++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/AsyncProxyServer.java @@ -16,7 +16,6 @@ package org.eclipse.jetty.servlets; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.nio.SelectChannelConnector; -import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.ServletHandler; import org.eclipse.jetty.servlet.ServletHolder; diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterContentLengthTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterContentLengthTest.java index 1b83855aa1a..7d1304ad110 100644 --- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterContentLengthTest.java +++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterContentLengthTest.java @@ -5,6 +5,7 @@ import java.util.List; import javax.servlet.Servlet; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.gzip.GzipResponseWrapper; import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.FilterHolder; @@ -104,7 +105,7 @@ public class GzipFilterContentLengthTest try { tester.start(); - tester.assertIsResponseNotGzipCompressed(filename,filesize); + tester.assertIsResponseNotGzipCompressed(filename,filesize,HttpStatus.OK_200); } finally { diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultTest.java index e597c139b1c..0f12e9f27a6 100644 --- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultTest.java +++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultTest.java @@ -1,5 +1,13 @@ package org.eclipse.jetty.servlets; +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.gzip.GzipResponseWrapper; import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.FilterHolder; @@ -13,6 +21,29 @@ import org.junit.Test; */ public class GzipFilterDefaultTest { + + + public static class HttpStatusServlet extends HttpServlet + { + private int _status = 204; + + public HttpStatusServlet() + { + super(); + } + + public void setStatus (int status) + { + _status = status; + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + resp.setStatus(_status); + } + + } @Rule public TestingDir testingdir = new TestingDir(); @@ -77,11 +108,33 @@ public class GzipFilterDefaultTest try { tester.start(); - tester.assertIsResponseNotGzipCompressed("file.mp3", filesize); + tester.assertIsResponseNotGzipCompressed("file.mp3", filesize, HttpStatus.OK_200); } finally { tester.stop(); } } + + @Test + public void testIsNotGzipCompressedHttpStatus() throws Exception + { + GzipTester tester = new GzipTester(testingdir); + + // Test error code 204 + FilterHolder holder = tester.setContentServlet(HttpStatusServlet.class); + holder.setInitParameter("mimeTypes","text/plain"); + + try + { + tester.start(); + tester.assertIsResponseNotGzipCompressed(null, -1, 204); + } + finally + { + tester.stop(); + } + + } + } diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/IncludableGzipFilterMinSizeTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/IncludableGzipFilterMinSizeTest.java new file mode 100644 index 00000000000..6e37eb311fb --- /dev/null +++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/IncludableGzipFilterMinSizeTest.java @@ -0,0 +1,85 @@ +// ======================================================================== +// Copyright (c) 2004-2009 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; + +import javax.servlet.Servlet; + +import org.eclipse.jetty.servlet.FilterHolder; +import org.eclipse.jetty.servlets.gzip.GzipTester; +import org.eclipse.jetty.servlets.gzip.TestMinGzipSizeServlet; +import org.eclipse.jetty.toolchain.test.TestingDir; +import org.junit.Rule; +import org.junit.Test; + +/** + * Perform specific tests on the IncludableGzipFilter's ability to manage + * minGzipSize initialization parameter. + * + * @see http://bugs.eclipse.org/366106 + */ +public class IncludableGzipFilterMinSizeTest +{ + @Rule + public TestingDir testdir = new TestingDir(); + + private Class testServlet = TestMinGzipSizeServlet.class; + + @Test + public void testUnderMinSize() throws Exception + { + GzipTester tester = new GzipTester(testdir); + // Use IncludableGzipFilter + tester.setGzipFilterClass(IncludableGzipFilter.class); + + FilterHolder holder = tester.setContentServlet(testServlet); + // A valid mime type that we will never use in this test. + // configured here to prevent mimeType==null logic + holder.setInitParameter("mimeTypes","application/soap+xml"); + holder.setInitParameter("minGzipSize", "2048"); + holder.setInitParameter("uncheckedPrintWriter","true"); + + tester.copyTestServerFile("small_script.js"); + + try { + tester.start(); + tester.assertIsResponseNotGzipFiltered("small_script.js", + "small_script.js.sha1", + "text/javascript; charset=utf-8"); + } finally { + tester.stop(); + } + } + + @Test + public void testOverMinSize() throws Exception + { + GzipTester tester = new GzipTester(testdir); + // Use IncludableGzipFilter + tester.setGzipFilterClass(IncludableGzipFilter.class); + + FilterHolder holder = tester.setContentServlet(testServlet); + holder.setInitParameter("mimeTypes","application/soap+xml,text/javascript"); + holder.setInitParameter("minGzipSize", "2048"); + holder.setInitParameter("uncheckedPrintWriter","true"); + + tester.copyTestServerFile("big_script.js"); + + try { + tester.start(); + tester.assertIsResponseGzipCompressed("big_script.js"); + } finally { + tester.stop(); + } + } +} diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/MultipartFilterTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/MultipartFilterTest.java index 2dc3048024c..0dfd6f17bf8 100644 --- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/MultipartFilterTest.java +++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/MultipartFilterTest.java @@ -15,6 +15,7 @@ package org.eclipse.jetty.servlets; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertNotNull; import java.io.File; import java.io.FileInputStream; @@ -39,6 +40,21 @@ public class MultipartFilterTest private File _dir; private ServletTester tester; + + public static class TestServlet extends DumpServlet + { + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + assertNotNull(req.getParameter("fileup")); + assertNotNull(req.getParameter("fileup"+MultiPartFilter.CONTENT_TYPE_SUFFIX)); + assertEquals(req.getParameter("fileup"+MultiPartFilter.CONTENT_TYPE_SUFFIX), "application/octet-stream"); + + super.doPost(req, resp); + } + + } @Before public void setUp() throws Exception { @@ -51,7 +67,7 @@ public class MultipartFilterTest tester=new ServletTester(); tester.setContextPath("/context"); tester.setResourceBase(_dir.getCanonicalPath()); - tester.addServlet(DumpServlet.class, "/"); + tester.addServlet(TestServlet.class, "/"); FilterHolder multipartFilter = tester.addFilter(MultiPartFilter.class,"/*",FilterMapping.DEFAULT); multipartFilter.setInitParameter("deleteFiles", "true"); tester.start(); @@ -93,6 +109,7 @@ public class MultipartFilterTest assertTrue(response.getMethod()==null); assertEquals(HttpServletResponse.SC_OK,response.getStatus()); } + @Test public void testPost() throws Exception @@ -124,6 +141,7 @@ public class MultipartFilterTest assertEquals(HttpServletResponse.SC_OK,response.getStatus()); assertTrue(response.getContent().indexOf("brown cow")>=0); } + @Test public void testEncodedPost() throws Exception diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/PutFilterTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/PutFilterTest.java index 2b158eb6e87..c92cc007f80 100644 --- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/PutFilterTest.java +++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/PutFilterTest.java @@ -13,6 +13,9 @@ package org.eclipse.jetty.servlets; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import java.io.File; import java.io.FileInputStream; import java.io.OutputStream; @@ -21,6 +24,7 @@ import java.net.URL; import java.util.Arrays; import java.util.HashSet; import java.util.Set; + import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.servlet.FilterHolder; @@ -31,9 +35,6 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - public class PutFilterTest { private File _dir; diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/QoSFilterTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/QoSFilterTest.java index f84b14367f8..85141c21852 100644 --- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/QoSFilterTest.java +++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/QoSFilterTest.java @@ -12,10 +12,14 @@ // ======================================================================== package org.eclipse.jetty.servlets; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import java.io.IOException; import java.net.URL; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; + import javax.servlet.Servlet; import javax.servlet.ServletException; import javax.servlet.ServletRequest; @@ -34,9 +38,6 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - public class QoSFilterTest { private static final Logger LOG = Log.getLogger(QoSFilterTest.class); diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java index 722e907a20e..2c72e5eb6ed 100644 --- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java +++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java @@ -32,6 +32,7 @@ import org.junit.Assert; public class GzipTester { + private Class gzipFilterClass = GzipFilter.class; private String encoding = "ISO8859_1"; private ServletTester servletTester; private TestingDir testdir; @@ -198,7 +199,7 @@ public class GzipTester * passing -1 will disable the Content-Length assertion) * @throws Exception */ - public void assertIsResponseNotGzipCompressed(String filename, int expectedFilesize) throws Exception + public void assertIsResponseNotGzipCompressed(String filename, int expectedFilesize, int status) throws Exception { System.err.printf("[GzipTester] requesting /context/%s%n",filename); HttpTester request = new HttpTester(); @@ -208,7 +209,10 @@ public class GzipTester request.setVersion("HTTP/1.0"); request.setHeader("Host","tester"); request.setHeader("Accept-Encoding","gzip"); - request.setURI("/context/" + filename); + if (filename == null) + request.setURI("/context/"); + else + request.setURI("/context/"+filename); // Issue the request ByteArrayBuffer reqsBuff = new ByteArrayBuffer(request.generate().getBytes()); @@ -218,7 +222,7 @@ public class GzipTester // Assert the response headers Assert.assertThat("Response.method",response.getMethod(),nullValue()); - Assert.assertThat("Response.status",response.getStatus(),is(HttpServletResponse.SC_OK)); + Assert.assertThat("Response.status",response.getStatus(),is(status)); if (expectedFilesize != (-1)) { Assert.assertThat("Response.header[Content-Length]",response.getHeader("Content-Length"),notNullValue()); @@ -226,29 +230,35 @@ public class GzipTester Assert.assertThat("Response.header[Content-Length]",serverLength,is(expectedFilesize)); } Assert.assertThat("Response.header[Content-Encoding]",response.getHeader("Content-Encoding"),not(containsString("gzip"))); - + // Assert that the contents are what we expect. - File serverFile = testdir.getFile(filename); - String expected = IO.readToString(serverFile); - String actual = null; - - InputStream in = null; - ByteArrayOutputStream out = null; - try + if (filename != null) { - in = new ByteArrayInputStream(response.getContentBytes()); - out = new ByteArrayOutputStream(); - IO.copy(in,out); + File serverFile = testdir.getFile(filename); + String expected = IO.readToString(serverFile); + String actual = null; - actual = out.toString(encoding); - Assert.assertEquals("Server contents",expected,actual); - } - finally - { - IO.close(out); - IO.close(in); + InputStream in = null; + ByteArrayOutputStream out = null; + try + { + in = new ByteArrayInputStream(response.getContentBytes()); + out = new ByteArrayOutputStream(); + IO.copy(in,out); + + actual = out.toString(encoding); + Assert.assertEquals("Server contents",expected,actual); + } + finally + { + IO.close(out); + IO.close(in); + } } } + + + /** * Generate string content of arbitrary length. @@ -320,6 +330,20 @@ public class GzipTester IO.close(fos); } } + + /** + * Copy a src/test/resource file into the server tree for eventual serving. + * + * @param filename + * the filename to look for in src/test/resources + */ + public void copyTestServerFile(String filename) throws IOException + { + File srcFile = MavenTestingUtils.getTestResourceFile(filename); + File testFile = testdir.getFile(filename); + + IO.copy(srcFile,testFile); + } /** * Set the servlet that provides content for the GzipFilter in being tested. @@ -335,10 +359,20 @@ public class GzipTester servletTester.setResourceBase(testdir.getDir().getCanonicalPath()); ServletHolder servletHolder = servletTester.addServlet(servletClass,"/"); servletHolder.setInitParameter("baseDir",testdir.getDir().getAbsolutePath()); - FilterHolder holder = servletTester.addFilter(GzipFilter.class,"/*",0); + FilterHolder holder = servletTester.addFilter(gzipFilterClass,"/*",0); return holder; } + public Class getGzipFilterClass() + { + return gzipFilterClass; + } + + public void setGzipFilterClass(Class gzipFilterClass) + { + this.gzipFilterClass = gzipFilterClass; + } + public void setEncoding(String encoding) { this.encoding = encoding; diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestMinGzipSizeServlet.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestMinGzipSizeServlet.java new file mode 100644 index 00000000000..3a3c9897f6c --- /dev/null +++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestMinGzipSizeServlet.java @@ -0,0 +1,52 @@ +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 minGzip configurable. + */ +@SuppressWarnings("serial") +public class TestMinGzipSizeServlet extends TestDirContentServlet +{ + private MimeTypes mimeTypes; + + @Override + public void init(ServletConfig config) throws ServletException + { + super.init(config); + mimeTypes = new MimeTypes(); + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + String fileName = request.getServletPath(); + byte[] dataBytes = loadContentFileBytes(fileName); + + response.setContentLength(dataBytes.length); + if (fileName.endsWith(".js")) + { + // intentionally long-form content type to test ";" splitting in code + response.setContentType("text/javascript; charset=utf-8"); + } + else + { + Buffer buf = mimeTypes.getMimeByExtension(fileName); + if (buf != null) + { + response.setContentType(buf.toString()); + } + } + ServletOutputStream out = response.getOutputStream(); + out.write(dataBytes); + } +} diff --git a/jetty-servlets/src/test/resources/big_script.js b/jetty-servlets/src/test/resources/big_script.js new file mode 100644 index 00000000000..938a413c3fd --- /dev/null +++ b/jetty-servlets/src/test/resources/big_script.js @@ -0,0 +1,792 @@ +//---------------------------------------------------------------------- +// +// Silly / Pointless Javascript to test GZIP compression. +// +//---------------------------------------------------------------------- + +var LOGO = { + dat: [ + 0x50, 0x89, 0x47, 0x4e, 0x0a, 0x0d, 0x0a, 0x1a, 0x00, 0x00, 0x0d, 0x00, 0x48, 0x49, 0x52, 0x44, + 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x78, 0x00, 0x06, 0x08, 0x00, 0x00, 0x2a, 0x00, 0x21, 0x96, + 0x00, 0x0f, 0x00, 0x00, 0x73, 0x04, 0x49, 0x42, 0x08, 0x54, 0x08, 0x08, 0x7c, 0x08, 0x64, 0x08, + 0x00, 0x88, 0x00, 0x00, 0x70, 0x09, 0x59, 0x48, 0x00, 0x73, 0x04, 0x00, 0x00, 0x27, 0x04, 0x00, + 0x01, 0x27, 0x4f, 0xd9, 0x80, 0x1d, 0x00, 0x00, 0x19, 0x00, 0x45, 0x74, 0x74, 0x58, 0x6f, 0x53, + 0x74, 0x66, 0x61, 0x77, 0x65, 0x72, 0x77, 0x00, 0x77, 0x77, 0x69, 0x2e, 0x6b, 0x6e, 0x63, 0x73, + 0x70, 0x61, 0x2e, 0x65, 0x72, 0x6f, 0x9b, 0x67, 0x3c, 0xee, 0x00, 0x1a, 0x20, 0x00, 0x49, 0x00, + 0x41, 0x44, 0x78, 0x54, 0xed, 0x9c, 0x79, 0x9d, 0x1c, 0xd8, 0x95, 0x55, 0x3f, 0xff, 0xfb, 0xa7, + 0xb2, 0xdd, 0x24, 0xef, 0x24, 0x81, 0x81, 0x2c, 0x20, 0x40, 0xd5, 0x91, 0x8b, 0xb0, 0x3f, 0xbb, + 0x1d, 0x04, 0x54, 0x1c, 0x46, 0x74, 0x17, 0x18, 0xd1, 0x98, 0x19, 0xd1, 0x05, 0x11, 0x37, 0xf4, + 0xe2, 0x8f, 0xcc, 0xb8, 0x28, 0x8c, 0x82, 0x02, 0x22, 0x8c, 0xe0, 0xe8, 0xe2, 0x86, 0x02, 0x88, + 0x8e, 0xa2, 0x08, 0xe8, 0x42, 0x42, 0x4b, 0x08, 0xc2, 0xc8, 0x12, 0x12, 0xf6, 0x42, 0x7d, 0x90, + 0xf7, 0x7d, 0x3e, 0xee, 0x47, 0xf3, 0x77, 0x75, 0xeb, 0x6a, 0xdf, 0x7e, 0xee, 0xea, 0xab, 0x7b, + 0xdc, 0x93, 0xf3, 0xcf, 0xd0, 0xf0, 0xa9, 0x55, 0x53, 0xae, 0x6f, 0x5d, 0x3d, 0xd5, 0x9e, 0xf7, + 0xee, 0x7b, 0x8a, 0xf9, 0xe2, 0xaa, 0x38, 0x70, 0x0e, 0x1c, 0xc3, 0x87, 0x99, 0x2e, 0x2f, 0xb4, + 0xe1, 0xc0, 0x38, 0x70, 0x8e, 0x1c, 0x11, 0x83, 0x80, 0xe7, 0x0e, 0x1d, 0xc3, 0x87, 0x48, 0xe1, + 0xe7, 0x01, 0x1d, 0x80, 0x87, 0x0e, 0xe1, 0xc3, 0x01, 0x48, 0x80, 0xe7, 0x0e, 0x1d, 0xc3, 0x87, + 0x48, 0xe1, 0xe7, 0x01, 0x1d, 0x80, 0x87, 0x0e, 0xe1, 0xc3, 0x01, 0x48, 0x80, 0xe7, 0x0e, 0x1d, + 0xc3, 0x87, 0x48, 0xe1, 0xe7, 0x01, 0x1d, 0x80, 0x87, 0x0e, 0xe1, 0xc3, 0x01, 0x48, 0x80, 0xe7, + 0x0e, 0x1d, 0xc3, 0x87, 0x48, 0xe1, 0xe7, 0x01, 0x1d, 0x80, 0x87, 0x0e, 0xe1, 0xc3, 0x01, 0x48, + 0x80, 0xe7, 0x0e, 0x1d, 0xc3, 0x87, 0x48, 0xe1, 0xe7, 0x01, 0x1d, 0x80, 0x87, 0x0e, 0xe1, 0xc3, + 0x01, 0x48, 0x80, 0xe7, 0x0e, 0x1d, 0xc3, 0x87, 0x48, 0xe1, 0x96, 0x81, 0x2f, 0xb4, 0x44, 0x40, + 0x2c, 0x3a, 0xe9, 0x98, 0xa7, 0x55, 0xe1, 0x3a, 0x38, 0x70, 0x8e, 0x1c, 0x42, 0x26, 0xf0, 0xd2, + 0x22, 0x4b, 0x16, 0x32, 0x0c, 0xb8, 0x02, 0xb8, 0xde, 0x38, 0xc9, 0x82, 0xe0, 0x4e, 0x80, 0xbf, + 0xa9, 0x4f, 0xbf, 0x6a, 0x7b, 0x05, 0x88, 0xb1, 0x6c, 0xc8, 0xc3, 0xe0, 0xfb, 0xc0, 0xd1, 0x81, + 0xcd, 0x86, 0x81, 0xe5, 0xc0, 0xe5, 0xab, 0xdf, 0x02, 0xea, 0xb6, 0xc3, 0x0e, 0x1c, 0xa3, 0x87, + 0x44, 0x6e, 0x12, 0x64, 0x29, 0x70, 0x41, 0xf0, 0x7a, 0x60, 0xaf, 0xc2, 0x01, 0x77, 0x80, 0xf3, + 0x55, 0x9b, 0x21, 0xf5, 0xf6, 0x0b, 0x81, 0xba, 0x81, 0xc7, 0x54, 0x5b, 0x77, 0xf5, 0xbf, 0x09, + 0xd9, 0xeb, 0xed, 0xb7, 0x45, 0x80, 0x0b, 0x24, 0x04, 0x2c, 0x5b, 0x66, 0xec, 0x35, 0x28, 0xf1, + 0xd7, 0xf0, 0xba, 0xaa, 0xb6, 0xd5, 0x11, 0x61, 0x23, 0x79, 0x27, 0xf0, 0x76, 0xdb, 0x5e, 0x81, + 0x27, 0x3c, 0xc3, 0xfc, 0x6c, 0x14, 0x1c, 0x3b, 0xc7, 0x0e, 0x10, 0xa0, 0x23, 0x91, 0x67, 0x81, + 0x91, 0x81, 0x9e, 0x75, 0x13, 0xaa, 0x4b, 0x38, 0x17, 0x55, 0xb2, 0x5b, 0x05, 0xd7, 0xa3, 0x9c, + 0x0b, 0xaa, 0x7e, 0x93, 0x8d, 0x31, 0xe0, 0x39, 0x48, 0x2b, 0xf9, 0xc7, 0x9c, 0x02, 0xbc, 0x0b, + 0xb6, 0xdb, 0x62, 0xd1, 0xe3, 0xa7, 0xdb, 0x66, 0x8b, 0x76, 0x03, 0xb4, 0xa5, 0x37, 0xdb, 0x64, + 0x70, 0xe1, 0x06, 0x38, 0x2d, 0xcb, 0xef, 0xd4, 0x01, 0x0c, 0x01, 0x86, 0x8b, 0xf7, 0xab, 0x48, + 0x7b, 0x25, 0x81, 0x43, 0x0d, 0x5f, 0x5e, 0xc2, 0x34, 0x84, 0x80, 0xe6, 0x50, 0x3f, 0x20, 0xfa, + 0x98, 0x19, 0xfa, 0xfd, 0xd7, 0xc4, 0x0c, 0x9c, 0x96, 0x55, 0x92, 0x3c, 0x0b, 0x43, 0x3d, 0xe5, + 0xcc, 0x33, 0x8c, 0x1a, 0x0e, 0x65, 0xab, 0x30, 0x31, 0xb4, 0x4d, 0xb9, 0xd6, 0x98, 0xb6, 0x6e, + 0xf3, 0xef, 0x9f, 0x6a, 0xba, 0xb2, 0xfc, 0xb7, 0xc7, 0xa3, 0xc8, 0x88, 0x55, 0x04, 0x62, 0xdd, + 0xa8, 0xd4, 0xe1, 0xc3, 0xd4, 0x70, 0x71, 0x40, 0xee, 0x7a, 0xd2, 0x82, 0xb0, 0xf6, 0x30, 0x8c, + 0x58, 0x6b, 0x36, 0xb2, 0x55, 0x72, 0x81, 0x4f, 0xfd, 0x4d, 0x88, 0xe5, 0x34, 0xee, 0x69, 0xbc, + 0x63, 0x38, 0xd6, 0xf6, 0x16, 0xf4, 0xd8, 0xd8, 0xb6, 0x57, 0x38, 0x77, 0xa8, 0x50, 0x78, 0x72, + 0x56, 0x2c, 0xb0, 0x1d, 0xb4, 0x88, 0xa7, 0x03, 0xb6, 0x95, 0x1e, 0xa7, 0xe5, 0x97, 0x9f, 0x9a, + 0x33, 0x0f, 0x73, 0x6a, 0xba, 0xdb, 0x9f, 0x02, 0x79, 0x3c, 0x7f, 0xb7, 0x34, 0xd7, 0x06, 0xa3, + 0x39, 0xe3, 0xbf, 0xdb, 0xdd, 0x71, 0x76, 0x94, 0x9f, 0x2e, 0x66, 0xd8, 0xe0, 0xd4, 0xaf, 0x95, + 0x70, 0xf4, 0xab, 0xeb, 0xfe, 0x7d, 0x87, 0x5d, 0xce, 0x03, 0x3b, 0x01, 0x8e, 0x1c, 0xe4, 0x66, + 0xff, 0x5a, 0xa7, 0xc6, 0x6d, 0x0e, 0x8b, 0xe3, 0xdb, 0x53, 0xfd, 0x07, 0x02, 0xe5, 0xfc, 0x70, + 0xbd, 0xc2, 0x07, 0x7e, 0x53, 0xbc, 0xab, 0x55, 0xc4, 0x39, 0xea, 0x6b, 0xa7, 0xb1, 0x89, 0xc0, + 0xf6, 0x8b, 0x1d, 0xfa, 0xa7, 0x70, 0x56, 0xaa, 0xf8, 0x74, 0xb0, 0x95, 0x82, 0x1d, 0x15, 0x3e, + 0x24, 0x2f, 0xc0, 0x0a, 0x73, 0x31, 0xfb, 0xcc, 0x65, 0xff, 0xe4, 0x4f, 0xbb, 0xc2, 0x1a, 0x96, + 0x1a, 0x37, 0xe0, 0x25, 0xcf, 0x80, 0x69, 0x1a, 0x77, 0xfe, 0xdd, 0xcf, 0x78, 0x13, 0xf2, 0x16, + 0x32, 0xc0, 0x46, 0xe3, 0x0e, 0x1d, 0x23, 0x87, 0x22, 0x21, 0x38, 0x72, 0x49, 0x70, 0x7b, 0x69, + 0x46, 0x68, 0xc4, 0xf8, 0x64, 0xe4, 0x94, 0x03, 0xb7, 0x7b, 0xb3, 0xf5, 0x27, 0xa2, 0x6f, 0xe0, + 0xb6, 0x2b, 0x45, 0x77, 0xef, 0x7b, 0xc7, 0xab, 0x83, 0xde, 0x72, 0x3b, 0xdf, 0x3c, 0xb0, 0x15, + 0x3c, 0xb7, 0x09, 0xd1, 0xd8, 0x8a, 0xc0, 0x76, 0x47, 0x01, 0x63, 0x34, 0xd6, 0x4e, 0xc1, 0xb8, + 0x16, 0x97, 0x3a, 0x44, 0x8f, 0x25, 0x37, 0x19, 0xe5, 0x1a, 0xd2, 0xac, 0xb1, 0x87, 0xc2, 0x2d, + 0x21, 0xcc, 0x6f, 0x66, 0xde, 0xfb, 0xb2, 0xbc, 0x2b, 0xb8, 0xbb, 0xf0, 0xa8, 0x97, 0x1e, 0xea, + 0x46, 0xa3, 0x0e, 0x1d, 0xa3, 0x87, 0x3e, 0x36, 0x2f, 0x8d, 0xfb, 0x1a, 0x43, 0xa1, 0x19, 0x5a, + 0x22, 0xdf, 0x4e, 0x89, 0xfd, 0x70, 0xbe, 0xfa, 0xae, 0xf0, 0xcd, 0x1b, 0xeb, 0xda, 0xef, 0x0d, + 0x66, 0xfa, 0x13, 0xa2, 0xb1, 0x14, 0x07, 0x3d, 0x74, 0x1c, 0xa7, 0xc0, 0x37, 0x9b, 0xd2, 0xff, + 0xc0, 0xfc, 0x38, 0x08, 0xcc, 0x0f, 0x6e, 0x37, 0x87, 0xd4, 0x1c, 0x88, 0x1c, 0x03, 0xda, 0x52, + 0x63, 0x3e, 0x96, 0x44, 0x7f, 0x64, 0xe4, 0xea, 0xd8, 0x2c, 0x27, 0x9b, 0x4c, 0x1f, 0x9f, 0x6e, + 0xd8, 0x6b, 0x01, 0xe4, 0x88, 0x83, 0x0d, 0x1c, 0x0e, 0x5c, 0x07, 0x9c, 0xff, 0x46, 0x0a, 0x54, + 0xc2, 0x6c, 0x32, 0x5b, 0x67, 0xf1, 0x46, 0x53, 0x64, 0x44, 0x5e, 0x08, 0xe1, 0xe2, 0x62, 0xdf, + 0xe9, 0x7e, 0x37, 0x5b, 0x08, 0xf0, 0x15, 0xf0, 0x8d, 0x55, 0x9e, 0x84, 0x8e, 0x1c, 0x22, 0x30, + 0x1e, 0x32, 0x48, 0xf8, 0xbb, 0x69, 0xe0, 0x45, 0x43, 0xaa, 0x8d, 0x93, 0xff, 0x46, 0x57, 0x77, + 0x67, 0x8e, 0x03, 0x3a, 0x8e, 0x03, 0xc0, 0x15, 0x9b, 0x7f, 0x37, 0xb2, 0x4f, 0x77, 0x79, 0x9e, + 0x08, 0xc1, 0x1a, 0xe3, 0xee, 0xe0, 0x27, 0x44, 0xd9, 0x29, 0xe5, 0xaf, 0x75, 0x4b, 0x1e, 0x50, + 0x8e, 0x09, 0x98, 0x9e, 0xc2, 0x61, 0xb3, 0x34, 0xc1, 0x23, 0xdd, 0xae, 0xda, 0xca, 0x03, 0x17, + 0x6a, 0x37, 0xaa, 0x91, 0x35, 0xee, 0x34, 0x6a, 0x30, 0x4a, 0x3c, 0xfc, 0xfc, 0xc2, 0x3f, 0xa8, + 0x7e, 0x14, 0xe7, 0x06, 0x07, 0x80, 0x88, 0x85, 0x1b, 0xfc, 0x59, 0xf0, 0x3a, 0xcc, 0x30, 0xde, + 0x88, 0x17, 0xa7, 0xc8, 0xf5, 0x55, 0x8d, 0x5b, 0xb1, 0x3e, 0xcc, 0x88, 0x8b, 0xc2, 0x8c, 0xf8, + 0xf4, 0x6a, 0xab, 0xb9, 0x7a, 0xf0, 0xf5, 0xe0, 0xf2, 0x22, 0x55, 0x5e, 0x6c, 0xdd, 0xae, 0xd1, + 0xff, 0x63, 0x9f, 0xe4, 0xb2, 0xf0, 0x01, 0x88, 0xef, 0x78, 0x56, 0xb8, 0x4f, 0x0e, 0xa0, 0x98, + 0xb5, 0xfa, 0xe8, 0xe8, 0x1b, 0xf7, 0xe6, 0x55, 0xeb, 0x7f, 0x17, 0xb6, 0xfa, 0x37, 0xb5, 0xad, + 0x69, 0xc3, 0x04, 0x2d, 0x22, 0x2d, 0x80, 0x33, 0xa5, 0x09, 0x1b, 0x6d, 0xe7, 0xe1, 0x4f, 0x15, + 0xb2, 0x05, 0x21, 0x9f, 0x47, 0x1d, 0x70, 0x14, 0x67, 0xc0, 0x30, 0x8f, 0xe7, 0xdf, 0xe7, 0x99, + 0x70, 0x1c, 0x44, 0x62, 0x38, 0xe4, 0x6a, 0xe0, 0x3a, 0xec, 0xf0, 0x5f, 0xc1, 0x3a, 0x8b, 0x37, + 0x39, 0xc8, 0xce, 0x06, 0x13, 0x7d, 0x9d, 0x76, 0x89, 0x6f, 0x80, 0xf3, 0x52, 0xdb, 0xeb, 0xb0, + 0x8f, 0xd8, 0x91, 0x10, 0xc0, 0x61, 0xfc, 0x27, 0xae, 0xfb, 0x6c, 0x39, 0x89, 0xf0, 0x00, 0x50, + 0x74, 0xcf, 0xf9, 0xe6, 0xae, 0xd3, 0xef, 0x80, 0x04, 0xdb, 0x65, 0xdc, 0xde, 0xca, 0x5d, 0x73, + 0x7e, 0x05, 0x23, 0xbb, 0x6f, 0x60, 0x70, 0x1b, 0xa2, 0x47, 0xf8, 0x93, 0x39, 0xb0, 0xb6, 0x02, + 0x7e, 0x1f, 0xa2, 0x7e, 0xe6, 0x29, 0xcb, 0x7f, 0xbb, 0xbf, 0xe0, 0x55, 0xe1, 0xb4, 0x3b, 0x66, + 0x05, 0x1e, 0x7e, 0x60, 0x01, 0xd0, 0x53, 0xaf, 0x35, 0xd5, 0x8d, 0x46, 0x18, 0x1e, 0x13, 0xfc, + 0xf7, 0xbe, 0x3d, 0xa1, 0x3e, 0x63, 0xdc, 0xfe, 0xec, 0x1b, 0xce, 0x1c, 0x81, 0xa4, 0x67, 0xcf, + 0x99, 0x71, 0x9b, 0xc5, 0xdb, 0x4a, 0xf1, 0x59, 0x3f, 0x9e, 0xf4, 0x93, 0x02, 0x15, 0x30, 0xeb, + 0x9a, 0x66, 0xe6, 0xb5, 0x00, 0x38, 0x02, 0xb8, 0x5b, 0x18, 0x38, 0xda, 0x4c, 0x7f, 0xb3, 0x0b, + 0x26, 0x86, 0x8d, 0x1d, 0x46, 0x46, 0x37, 0xbf, 0x6a, 0xa9, 0x85, 0x4f, 0x2f, 0xc3, 0xd7, 0xaf, + 0xd7, 0xde, 0xf4, 0x37, 0x2a, 0x12, 0x75, 0x5d, 0xaa, 0xab, 0x26, 0x76, 0x89, 0x3a, 0xf4, 0x8f, + 0x70, 0x1c, 0x19, 0x3a, 0x56, 0xd0, 0x82, 0x47, 0x32, 0x22, 0x38, 0x0e, 0xb4, 0xae, 0xca, 0x7d, + 0x2c, 0xf0, 0xf3, 0x86, 0x58, 0xaf, 0x99, 0xd2, 0x4f, 0x67, 0x02, 0x70, 0x8d, 0xd8, 0x07, 0x7e, + 0x79, 0x47, 0xda, 0x04, 0x81, 0x9e, 0xaf, 0xed, 0x89, 0x1d, 0xa5, 0xc9, 0x8b, 0xda, 0xf3, 0x3b, + 0xb2, 0x9c, 0xf0, 0x38, 0x1e, 0xde, 0x60, 0xd3, 0x81, 0x13, 0xf5, 0x11, 0x87, 0xf5, 0xf0, 0x77, + 0x84, 0xc9, 0x9e, 0x99, 0x79, 0x49, 0x73, 0xd3, 0x3c, 0x5d, 0xbb, 0xb2, 0xce, 0xfc, 0x17, 0x4d, + 0x71, 0x11, 0xd8, 0x35, 0x71, 0x1d, 0x8b, 0x14, 0x5c, 0x56, 0xdf, 0xe5, 0xed, 0x77, 0xc9, 0xa1, + 0xa3, 0x46, 0x7a, 0x2b, 0xdc, 0x0a, 0x2d, 0xbb, 0x59, 0x50, 0x37, 0x78, 0x9f, 0xf0, 0xec, 0x55, + 0xba, 0x7d, 0x7b, 0x1e, 0x7a, 0x6b, 0xfc, 0x0b, 0x6b, 0x6c, 0x5e, 0xc0, 0x5e, 0x17, 0x8f, 0x31, + 0xb1, 0x9a, 0x05, 0x99, 0x76, 0x5d, 0x6d, 0xc0, 0x84, 0x43, 0x67, 0xc3, 0x1d, 0x9b, 0xe6, 0x09, + 0xe1, 0xfb, 0x67, 0xe5, 0x23, 0x02, 0x55, 0xc1, 0xba, 0xaa, 0xa8, 0xde, 0x28, 0xd1, 0xe2, 0x67, + 0x48, 0x1b, 0x9d, 0x9d, 0xfc, 0xce, 0x3c, 0xef, 0x46, 0xe3, 0x73, 0xf7, 0x64, 0x44, 0xbe, 0x14, + 0x29, 0x42, 0x0c, 0xa7, 0x1a, 0xcb, 0xbe, 0x75, 0xfb, 0x10, 0x6a, 0x77, 0xb3, 0xf4, 0x70, 0x19, + 0xa6, 0xc0, 0xbd, 0x9f, 0xc2, 0x9c, 0x7b, 0x93, 0xbf, 0x03, 0xa3, 0x69, 0xbe, 0x73, 0x2a, 0x8e, + 0x0d, 0xfc, 0xb5, 0x30, 0x71, 0xb4, 0x88, 0xc6, 0xe7, 0x2c, 0x4c, 0x8c, 0x8c, 0xf6, 0x73, 0x7e, + 0x5f, 0x43, 0xf2, 0xb8, 0x77, 0xc5, 0x75, 0x55, 0x85, 0x57, 0xdf, 0xc3, 0xaf, 0x5f, 0x1b, 0xbd, + 0xf6, 0x37, 0x0b, 0x91, 0xd1, 0x3e, 0xa8, 0x77, 0xb6, 0xea, 0x27, 0x44, 0x61, 0x09, 0x01, 0xc5, + 0x47, 0x17, 0xc7, 0x82, 0xb6, 0x96, 0x9f, 0xa7, 0x25, 0x92, 0x78, 0x6b, 0xbc, 0x00, 0x49, 0x6a, + 0xbe, 0x9e, 0xc0, 0xee, 0x4a, 0xdd, 0xfc, 0x23, 0x68, 0x1c, 0xcf, 0x04, 0xf3, 0x72, 0xcd, 0xbf, + 0x59, 0xca, 0xde, 0xf8, 0x1b, 0x05, 0x25, 0x1d, 0x7f, 0x0b, 0xf2, 0xa7, 0x37, 0xb3, 0x23, 0x34, + 0x34, 0x9c, 0xec, 0xcc, 0x7a, 0x6f, 0x33, 0xb6, 0x57, 0x4f, 0x45, 0xc1, 0x1c, 0x5e, 0x23, 0x03, + 0x42, 0x22, 0x10, 0xa8, 0xb5, 0xc6, 0xe7, 0x09, 0x5f, 0x62, 0x57, 0xed, 0xda, 0xee, 0x08, 0x12, + 0xf7, 0x3f, 0x61, 0x52, 0x51, 0xe9, 0xec, 0x23, 0xcb, 0x6d, 0x77, 0x29, 0x0a, 0x6e, 0x7e, 0x8c, + 0xc0, 0x73, 0x89, 0x0d, 0x12, 0x4e, 0xad, 0x83, 0xf0, 0x11, 0xf8, 0x59, 0x82, 0x46, 0xc2, 0x36, + 0x2f, 0xcf, 0x48, 0x2d, 0xfc, 0x37, 0x24, 0x5c, 0x76, 0x10, 0xd3, 0x3f, 0xc3, 0x7e, 0x89, 0xde, + 0x6c, 0x57, 0x51, 0xdc, 0x50, 0x9d, 0xba, 0xa6, 0x8e, 0xf5, 0x17, 0x52, 0xfc, 0x0d, 0x89, 0x3b, + 0x14, 0xc8, 0x15, 0x7c, 0x5e, 0xdf, 0x3c, 0x3b, 0xc2, 0x4b, 0x8e, 0x65, 0x71, 0x89, 0xf7, 0x99, + 0x04, 0x8e, 0x03, 0xbc, 0x0a, 0x9f, 0xf1, 0xde, 0x32, 0x43, 0x38, 0x5c, 0xd9, 0x26, 0x77, 0x3b, + 0xc6, 0xcb, 0x70, 0xbe, 0xc5, 0x0e, 0x54, 0x8f, 0x63, 0x75, 0xc3, 0x85, 0x04, 0x2f, 0xa8, 0x4e, + 0xde, 0xc7, 0x37, 0xb7, 0x85, 0xf6, 0x98, 0x3b, 0x37, 0x77, 0xb7, 0x22, 0x96, 0xc6, 0x0e, 0x03, + 0x60, 0x25, 0x28, 0x59, 0x11, 0xc0, 0xff, 0x9d, 0x3a, 0xb5, 0x16, 0x02, 0x36, 0x91, 0x65, 0x7c, + 0x8f, 0x37, 0xc8, 0xee, 0xd1, 0x30, 0x16, 0x70, 0xba, 0xcc, 0x02, 0xbe, 0x82, 0x6b, 0xe5, 0x4f, + 0xb5, 0x13, 0x47, 0x94, 0x08, 0x3b, 0x44, 0x09, 0x4c, 0x2a, 0x94, 0x77, 0x07, 0xd6, 0x74, 0xeb, + 0xf7, 0x83, 0x6a, 0x77, 0xba, 0xe4, 0xab, 0x59, 0x67, 0xe1, 0x91, 0x70, 0x47, 0x17, 0x02, 0x22, + 0x27, 0x65, 0x73, 0x3f, 0x7b, 0x58, 0x84, 0xa2, 0xdd, 0xc7, 0xe5, 0x79, 0xc1, 0x3b, 0xbb, 0x32, + 0xe0, 0x05, 0xa6, 0xeb, 0x75, 0xec, 0x94, 0x16, 0x07, 0x6f, 0x29, 0xed, 0x7d, 0x70, 0x53, 0x82, + 0xc4, 0x54, 0x03, 0x96, 0x27, 0x2e, 0x88, 0x98, 0x31, 0xc0, 0xd9, 0xa7, 0x80, 0xc8, 0xb4, 0x0e, + 0x3b, 0x67, 0xe7, 0x81, 0x1b, 0x8c, 0x32, 0x0d, 0xe8, 0x0d, 0x6d, 0x28, 0x58, 0xd8, 0xeb, 0xff, + 0xc1, 0x1a, 0x14, 0xe4, 0x33, 0x93, 0x94, 0xe6, 0x67, 0xb6, 0x64, 0x74, 0x60, 0x98, 0xa3, 0xb8, + 0x6f, 0x34, 0xf1, 0x8f, 0x4c, 0xe8, 0xf4, 0xa8, 0x8d, 0xa9, 0x2f, 0x67, 0x9b, 0xf0, 0x93, 0x76, + 0xc9, 0x4e, 0x47, 0x57, 0x53, 0x93, 0x6e, 0x5c, 0xae, 0x57, 0x31, 0x07, 0x2d, 0xb5, 0xfb, 0xc3, + 0x27, 0xc7, 0xe4, 0x13, 0xee, 0xf9, 0x7e, 0xa6, 0x05, 0x76, 0x4d, 0x13, 0xa5, 0x7f, 0x2f, 0xaa, + 0xb0, 0x55, 0x36, 0x77, 0xf7, 0xbe, 0x16, 0xa1, 0x77, 0x7b, 0xea, 0x6e, 0x5b, 0x67, 0xf2, 0x70, + 0x41, 0xf7, 0x6d, 0x55, 0xf3, 0xc8, 0x2b, 0x6d, 0xba, 0x0b, 0x54, 0x3c, 0x0c, 0x97, 0xf1, 0x7c, + 0xb3, 0x7c, 0x30, 0xb5, 0x05, 0x0a, 0xd7, 0x65, 0xc0, 0x96, 0x9b, 0x0d, 0x42, 0x97, 0xc8, 0x42, + 0xbc, 0x5a, 0x59, 0x9e, 0xe0, 0x06, 0x3b, 0x0f, 0x9c, 0x73, 0xb8, 0xba, 0x6c, 0xe6, 0x41, 0xf5, + 0xb9, 0xb1, 0xb2, 0x3f, 0x7c, 0xf6, 0x86, 0xc0, 0x58, 0xbd, 0x75, 0x17, 0xe9, 0x22, 0x53, 0x48, + 0x16, 0x0e, 0xf6, 0x05, 0xad, 0xe0, 0xdb, 0xf1, 0xcb, 0x5e, 0x52, 0xdf, 0x0e, 0x44, 0x5e, 0x25, + 0x63, 0x88, 0x30, 0x06, 0xb4, 0xa4, 0xc7, 0x61, 0x45, 0x59, 0x4a, 0x3a, 0x45, 0xbb, 0x8c, 0xe4, + 0x9a, 0x06, 0x43, 0x78, 0x43, 0xe9, 0x38, 0x06, 0xf0, 0xc3, 0xb7, 0xd2, 0x82, 0x57, 0x30, 0xaa, + 0x55, 0xe0, 0x9a, 0x22, 0x4d, 0xe3, 0xd5, 0xbd, 0x3f, 0x44, 0x52, 0xb0, 0x77, 0x55, 0x48, 0x88, + 0x38, 0x06, 0x5f, 0x11, 0x79, 0x0d, 0xf4, 0x43, 0x2b, 0x00, 0x16, 0x92, 0x11, 0xea, 0xc3, 0x91, + 0x25, 0xf0, 0x18, 0xf7, 0x7c, 0xa2, 0xaa, 0x3f, 0x47, 0x5c, 0x70, 0x2b, 0x60, 0x12, 0x01, 0xfa, + 0x01, 0xee, 0xd7, 0x96, 0xf4, 0xb1, 0xb3, 0xe6, 0x8d, 0xfe, 0x0f, 0x2b, 0x4b, 0x6d, 0x3c, 0xb4, + 0xb7, 0xae, 0xdf, 0x00, 0x18, 0xda, 0x84, 0x29, 0x35, 0xf1, 0xf6, 0x53, 0x15, 0xfa, 0xdc, 0x6e, + 0x59, 0x10, 0x54, 0xea, 0x5e, 0xcd, 0x6c, 0x22, 0xe0, 0x39, 0x1f, 0xf2, 0x90, 0xab, 0x0d, 0x87, + 0xad, 0xcb, 0x45, 0x47, 0x6b, 0xbf, 0x20, 0xdb, 0xdb, 0x5e, 0x03, 0xb7, 0x07, 0x18, 0xd2, 0x5e, + 0xab, 0xc0, 0x56, 0xfd, 0xf7, 0x7f, 0x40, 0xcb, 0xc4, 0xa4, 0xb1, 0x61, 0xc4, 0xe0, 0xc0, 0x25, + 0x78, 0x19, 0xf2, 0x21, 0x36, 0xf1, 0xaa, 0x2f, 0xf4, 0x01, 0xc8, 0x8b, 0xc0, 0xab, 0xe0, 0x02, + 0xc0, 0xd7, 0x55, 0x03, 0x1f, 0x32, 0x91, 0x11, 0x00, 0xb7, 0x0c, 0xff, 0xcf, 0x9c, 0x1a, 0x20, + 0x1b, 0x27, 0xbf, 0xf3, 0x77, 0x73, 0x9e, 0x47, 0x86, 0x83, 0xb3, 0x84, 0x98, 0xe2, 0x34, 0x39, + 0x68, 0xc3, 0x13, 0xc3, 0xf3, 0xc0, 0xbf, 0xa2, 0x14, 0xe7, 0xf1, 0xe0, 0x4f, 0x3a, 0x2d, 0xdb, + 0x77, 0x22, 0xef, 0x00, 0x53, 0xc4, 0xb2, 0xf2, 0x16, 0xc1, 0x79, 0x11, 0xaa, 0x8f, 0x32, 0x3e, + 0x81, 0xd0, 0xf2, 0x22, 0xe0, 0x0e, 0xc0, 0xe3, 0xb0, 0x5b, 0x79, 0xd3, 0x16, 0xed, 0x2b, 0x91, + 0xf5, 0x54, 0xa1, 0x27, 0x98, 0xeb, 0x5c, 0x02, 0x5c, 0x09, 0xaf, 0x86, 0x91, 0xd0, 0x36, 0x61, + 0xc8, 0x89, 0x55, 0xbb, 0xd1, 0x35, 0x5e, 0xb4, 0x32, 0xb1, 0xdb, 0xdb, 0xdb, 0x4b, 0x5b, 0x63, + 0xcb, 0x84, 0x26, 0x27, 0x8d, 0x1b, 0x68, 0xfe, 0x5f, 0x4b, 0xb3, 0xb8, 0x67, 0xf7, 0x7d, 0x55, + 0x82, 0xb2, 0x13, 0xbd, 0x49, 0xf0, 0xd6, 0x0e, 0xef, 0x62, 0x5b, 0x67, 0x59, 0xfb, 0x17, 0xdb, + 0xd8, 0x18, 0x4d, 0xcc, 0xbb, 0xfa, 0x61, 0xab, 0xb5, 0xbc, 0xf6, 0x29, 0xaf, 0x10, 0x6d, 0x34, + 0x0a, 0xa7, 0x50, 0x7f, 0xfb, 0xd5, 0xb0, 0x53, 0x59, 0xfb, 0xce, 0x8a, 0x25, 0x37, 0xad, 0x3e, + 0xe5, 0xaa, 0x9b, 0xae, 0x4c, 0x88, 0xbe, 0x04, 0x4f, 0x81, 0x79, 0x8f, 0xa5, 0x3f, 0x6f, 0x15, + 0x31, 0xc4, 0xb8, 0x15, 0xe8, 0x18, 0x91, 0x13, 0x80, 0x07, 0x54, 0xeb, 0x30, 0x35, 0x20, 0xcd, + 0x33, 0x22, 0x07, 0x81, 0xdf, 0x8b, 0x14, 0x19, 0xe7, 0xa6, 0x3b, 0x4b, 0xca, 0x0b, 0x2e, 0xa2, + 0x1d, 0x7b, 0x73, 0xb1, 0x74, 0x6c, 0xa2, 0x28, 0xfa, 0x23, 0x30, 0x46, 0xaf, 0x04, 0x8a, 0x38, + 0x26, 0x4d, 0x7f, 0x00, 0x91, 0x14, 0x86, 0x0f, 0x9d, 0x1d, 0x11, 0x1f, 0x15, 0x39, 0x0f, 0xb8, + 0x55, 0xbb, 0x86, 0xd3, 0x3f, 0x00, 0x91, 0x16, 0xaa, 0xe9, 0x25, 0xfa, 0xfe, 0xdf, 0x00, 0x9f, + 0xb3, 0xaf, 0x1d, 0x78, 0x45, 0xe0, 0xfe, 0xcc, 0x78, 0xb7, 0x7e, 0x9f, 0xe0, 0x9a, 0xd7, 0x7b, + 0xbb, 0xe2, 0x1f, 0x67, 0xd4, 0x9f, 0xb0, 0xc6, 0xe0, 0xcc, 0x61, 0x6f, 0x6f, 0x01, 0x51, 0xe9, + 0xff, 0x88, 0xf2, 0xa8, 0x6b, 0x95, 0xb1, 0xea, 0x78, 0xa7, 0x37, 0x85, 0x34, 0x42, 0xf4, 0x6c, + 0x76, 0x0b, 0x6e, 0x7a, 0x01, 0x5f, 0x8a, 0xcc, 0x56, 0xfe, 0xfa, 0xc7, 0x57, 0xe8, 0x26, 0x44, + 0x95, 0xe3, 0x4c, 0x35, 0x35, 0x8b, 0x7a, 0xaa, 0xf1, 0x5f, 0x32, 0x5a, 0xf2, 0x22, 0xc0, 0x8f, + 0xec, 0x8b, 0xce, 0xff, 0x8e, 0x37, 0x3c, 0x36, 0xbc, 0x47, 0x44, 0x50, 0x26, 0xbe, 0x43, 0x22, + 0xff, 0x7d, 0x4d, 0xf6, 0x38, 0x12, 0xf1, 0xdf, 0xc2, 0x2d, 0x86, 0xb1, 0xa5, 0x2b, 0xb1, 0x3c, + 0x22, 0x27, 0x61, 0x94, 0x3b, 0x14, 0xc7, 0xb5, 0x28, 0x0f, 0x85, 0xdd, 0xe0, 0x16, 0x01, 0xd2, + 0xb9, 0x8e, 0xfb, 0x01, 0x4b, 0x25, 0x7c, 0x4b, 0xf4, 0xb8, 0x44, 0x41, 0xc2, 0x2e, 0xf3, 0xbe, + 0xd1, 0x2d, 0x27, 0x8a, 0x31, 0xf0, 0xe2, 0x28, 0x00, 0x24, 0x9e, 0x3f, 0x30, 0xb0, 0xaf, 0xcc, + 0xf5, 0xb7, 0x52, 0xf3, 0x72, 0x50, 0xa1, 0x70, 0xfe, 0xaa, 0x82, 0xa1, 0x69, 0xbd, 0x6b, 0x78, + 0x6b, 0x7f, 0x17, 0xb6, 0x39, 0x23, 0x1c, 0xf8, 0x9b, 0xf0, 0x27, 0x44, 0x00, 0x19, 0x21, 0x1b, + 0x0b, 0xe8, 0x1f, 0x4b, 0x8f, 0x86, 0xde, 0x15, 0xb9, 0x7a, 0xa9, 0xd9, 0xae, 0x9c, 0xca, 0x5d, + 0xbf, 0xef, 0xe8, 0x1b, 0xcc, 0xb5, 0x6a, 0x30, 0x9e, 0x30, 0xe1, 0xb7, 0x57, 0x9b, 0xcb, 0x7c, + 0x16, 0xe8, 0x31, 0x4e, 0x0e, 0xbc, 0x4f, 0xf3, 0x72, 0xbb, 0xb5, 0x3c, 0x10, 0xaf, 0x54, 0xc2, + 0xe9, 0x11, 0x53, 0xc0, 0xfa, 0x7f, 0xf0, 0x39, 0xb8, 0xae, 0x4c, 0xef, 0x13, 0x6e, 0x19, 0x4e, + 0xe5, 0x96, 0x21, 0xb8, 0x5a, 0x19, 0x9f, 0x6a, 0x33, 0xaf, 0x02, 0x8e, 0x72, 0xbb, 0x86, 0xca, + 0x02, 0xbe, 0xfa, 0x1b, 0x95, 0x95, 0x05, 0x3d, 0xe4, 0x76, 0x5e, 0x22, 0x15, 0xac, 0xc7, 0xaf, + 0x57, 0xfb, 0x72, 0x22, 0xb0, 0x3e, 0x2f, 0x05, 0x08, 0x3c, 0x94, 0xc0, 0x0c, 0xb6, 0x3b, 0x7f, + 0xba, 0xa1, 0x3e, 0x2f, 0xdd, 0xb2, 0x3d, 0xfc, 0xc8, 0xe8, 0xe2, 0x48, 0x88, 0x35, 0xf0, 0xf5, + 0x51, 0xc6, 0x5f, 0x66, 0x02, 0xf1, 0xce, 0xf0, 0xad, 0x31, 0x38, 0x5c, 0x71, 0xa6, 0x0c, 0xe7, + 0xcb, 0x3d, 0x7b, 0xbc, 0x2b, 0x5c, 0x3b, 0xd3, 0x76, 0xcc, 0x7e, 0x8c, 0xad, 0xb1, 0x95, 0x9f, + 0x1a, 0xfb, 0x8a, 0xf7, 0x86, 0x6d, 0x8c, 0x88, 0xe7, 0xc1, 0x8e, 0xf4, 0xcf, 0x68, 0x49, 0x70, + 0xa4, 0xc2, 0xa4, 0x9e, 0xbd, 0xc4, 0xc5, 0xdb, 0x8b, 0x72, 0xf9, 0x17, 0xfb, 0x8f, 0x21, 0xc8, + 0x8c, 0xad, 0xb5, 0x6f, 0x9f, 0x7a, 0x4e, 0x8e, 0xd5, 0xf0, 0xf0, 0xab, 0x9b, 0xf9, 0xd7, 0x0f, + 0x01, 0x22, 0x1d, 0x18, 0x56, 0xfd, 0xba, 0x9b, 0x0a, 0xb9, 0xe7, 0x5f, 0x16, 0xbb, 0x5f, 0x7b, + 0x8a, 0x8b, 0x5c, 0x2e, 0xe8, 0xdf, 0x22, 0x5c, 0x6f, 0xa3, 0x31, 0x67, 0x58, 0x04, 0x63, 0x9e, + 0xfe, 0x3c, 0x16, 0xec, 0xfa, 0xfe, 0x8e, 0xea, 0xdf, 0x2a, 0x9f, 0xa8, 0x1f, 0x67, 0x0f, 0xea, + 0xe0, 0x3b, 0x36, 0x06, 0x83, 0x0d, 0x90, 0x26, 0xfa, 0x85, 0x0f, 0xff, 0xe7, 0x6b, 0xc3, 0x5c, + 0x3d, 0xc9, 0xbc, 0xcf, 0x08, 0x10, 0x0a, 0x03, 0xcd, 0x5e, 0x2f, 0xd3, 0x72, 0x11, 0xad, 0xbe, + 0x57, 0x02, 0xda, 0x1d, 0x27, 0xce, 0xb5, 0x26, 0xb9, 0x31, 0x79, 0xad, 0x6e, 0x9d, 0xf2, 0x35, + 0x8f, 0x0a, 0xce, 0xec, 0xef, 0x71, 0x1c, 0xb6, 0x6f, 0x77, 0x64, 0x8d, 0x1e, 0x44, 0x3c, 0x0b, + 0x17, 0x8a, 0x2a, 0x2a, 0xf4, 0xdf, 0xc6, 0x0b, 0xf0, 0xb4, 0x69, 0xd5, 0x1c, 0xf6, 0x25, 0x5f, + 0x1a, 0xce, 0xb5, 0x91, 0x2a, 0x32, 0x45, 0xb5, 0xf8, 0x06, 0x83, 0x72, 0xeb, 0xef, 0xe0, 0xc2, + 0xcc, 0xb3, 0x87, 0xf5, 0x33, 0xb7, 0x70, 0xcd, 0xb2, 0x69, 0x83, 0xbb, 0x06, 0x25, 0x4d, 0xab, + 0x05, 0x1d, 0x1a, 0x6a, 0x34, 0x5c, 0xfc, 0xd6, 0x15, 0x73, 0xb7, 0x7a, 0x78, 0x33, 0xda, 0x6d, + 0x7c, 0x46, 0x4c, 0xed, 0x06, 0x47, 0x39, 0x6e, 0x08, 0x6a, 0x5f, 0xa6, 0xd0, 0xe9, 0x1a, 0x7d, + 0xdb, 0x54, 0x2c, 0x5a, 0x74, 0xc4, 0x69, 0x79, 0x45, 0xbb, 0x53, 0xe0, 0x25, 0x09, 0xff, 0x00, + 0xea, 0x1c, 0x01, 0x94, 0x81, 0x2b, 0x98, 0x5f, 0x37, 0xb2, 0x4f, 0x77, 0xc7, 0x9e, 0x24, 0x1b, + 0x58, 0x39, 0xd3, 0x0d, 0xe8, 0x21, 0x7a, 0xc0, 0x63, 0xc4, 0xb0, 0xcf, 0x80, 0x51, 0x32, 0x23, + 0xb5, 0x1f, 0xb8, 0xc0, 0x28, 0xd1, 0x05, 0xd6, 0x9e, 0x18, 0x3e, 0x08, 0x1b, 0x2c, 0xf7, 0x81, + 0xd3, 0xe2, 0x04, 0xbd, 0x6f, 0x38, 0x0b, 0x64, 0x9c, 0xcf, 0x9c, 0x38, 0x4e, 0xaf, 0xdf, 0x6b, + 0x9d, 0x6f, 0x78, 0x2f, 0x02, 0xc2, 0x1c, 0x6f, 0xc2, 0xd5, 0x47, 0xad, 0xf0, 0x75, 0xc9, 0xc2, + 0xb9, 0xc3, 0x42, 0x6c, 0x38, 0x6b, 0x78, 0x1e, 0x10, 0x18, 0xf7, 0x90, 0x90, 0x3b, 0xde, 0x9c, + 0x4c, 0x81, 0x4c, 0x58, 0x4a, 0x64, 0xc6, 0x5b, 0xf3, 0xb8, 0x59, 0x8d, 0xff, 0x3f, 0x95, 0x54, + 0xad, 0xc3, 0x8b, 0xca, 0xbc, 0xc6, 0x1b, 0xdb, 0x75, 0x54, 0x31, 0x63, 0xa3, 0xa7, 0x02, 0x3c, + 0x75, 0x3d, 0xd6, 0x58, 0xf3, 0xb8, 0xb9, 0x8d, 0xb1, 0x4f, 0x4f, 0xc9, 0x04, 0x31, 0x00, 0x24, + 0x19, 0x2f, 0x83, 0x5f, 0x60, 0x04, 0xf4, 0x74, 0x8d, 0xfb, 0x35, 0x2a, 0x3e, 0x0e, 0x6f, 0x81, + 0xb3, 0xda, 0x7b, 0x16, 0xa3, 0x31, 0x6f, 0xdf, 0x6a, 0xaa, 0xdc, 0x7f, 0xf5, 0xb1, 0xd5, 0x60, + 0xdb, 0x01, 0x40, 0x51, 0x5d, 0x4a, 0x61, 0x80, 0x3e, 0x2c, 0x86, 0x01, 0x87, 0x93, 0x17, 0x99, + 0x58, 0x60, 0x99, 0xda, 0x77, 0x67, 0x14, 0x70, 0xc1, 0x98, 0xb8, 0x4b, 0x73, 0x2a, 0xc4, 0xc5, + 0xfe, 0x36, 0xc2, 0x74, 0x66, 0x50, 0xbe, 0x9a, 0xcb, 0x18, 0xd2, 0x1c, 0x7c, 0x2a, 0xe8, 0xff, + 0xfc, 0x21, 0xd8, 0xe2, 0x55, 0xa1, 0x37, 0xeb, 0x58, 0xdb, 0xe4, 0x5e, 0x9f, 0xa8, 0xba, 0xe7, + 0xcf, 0x82, 0x13, 0x72, 0x9f, 0xae, 0x96, 0x0f, 0x1d, 0x97, 0xb6, 0x69, 0x8b, 0x4d, 0xb7, 0x79, + 0xd7, 0x4b, 0x96, 0x76, 0xe7, 0x7f, 0x86, 0xd9, 0x03, 0xaf, 0xab, 0x6a, 0x10, 0x33, 0x01, 0x27, + 0x75, 0x78, 0x11, 0x42, 0x67, 0x84, 0xf3, 0x3b, 0xb4, 0x3c, 0xd0, 0x33, 0x6f, 0xae, 0x7e, 0x06, + 0xc1, 0x50, 0xd0, 0xde, 0xed, 0x7a, 0xd7, 0x3d, 0xe0, 0x55, 0x0d, 0x77, 0x1c, 0x94, 0x86, 0xac, + 0x07, 0x35, 0x31, 0xdc, 0x38, 0x4c, 0x96, 0x7c, 0x01, 0x79, 0x95, 0x86, 0x83, 0x0b, 0x0a, 0x61, + 0x7d, 0x55, 0xa8, 0xd5, 0x28, 0xd1, 0x10, 0x81, 0x4a, 0x55, 0x0c, 0x02, 0xf1, 0x13, 0x5f, 0x85, + 0x3f, 0x5f, 0x85, 0xaa, 0x1d, 0x6f, 0x36, 0x69, 0x9f, 0xf4, 0x17, 0x36, 0x6d, 0x8d, 0xbe, 0xe1, + 0x86, 0xe3, 0x96, 0xc6, 0x9c, 0x5c, 0x26, 0xdc, 0x69, 0x1c, 0x5d, 0x5a, 0xd1, 0xc1, 0xc2, 0x10, + 0xb2, 0xcf, 0x75, 0x95, 0xd1, 0xd9, 0x54, 0x65, 0x8e, 0xb1, 0x58, 0xa5, 0xee, 0xdd, 0xa5, 0xb5, + 0xa3, 0xed, 0x32, 0x3a, 0x32, 0x4c, 0xb4, 0x5c, 0x4a, 0xa5, 0x4f, 0x89, 0xd5, 0xeb, 0x62, 0xce, + 0xcf, 0x96, 0x46, 0x0d, 0xe5, 0xe8, 0x7c, 0x72, 0xfd, 0x21, 0x49, 0x06, 0xde, 0x0e, 0x45, 0x5c, + 0xf0, 0x72, 0xf8, 0x23, 0x3d, 0xa6, 0x41, 0x9b, 0xb0, 0x72, 0xc6, 0x1a, 0xa5, 0x5a, 0xe1, 0x62, + 0xa3, 0x83, 0xdb, 0x4a, 0x9f, 0x47, 0x25, 0x96, 0xf8, 0x63, 0x59, 0x19, 0x38, 0xb5, 0x5f, 0x4f, + 0xea, 0x4f, 0xe1, 0x05, 0xa8, 0x53, 0x54, 0x42, 0x19, 0x79, 0xd1, 0xc8, 0x19, 0x1d, 0x99, 0xee, + 0xc4, 0x31, 0x16, 0xb8, 0x33, 0x6c, 0xe6, 0xf0, 0x2d, 0x51, 0x74, 0x7c, 0x74, 0x62, 0xe7, 0xae, + 0x0b, 0x4c, 0x71, 0x4b, 0x8d, 0x1c, 0x34, 0x21, 0xc6, 0xef, 0x16, 0x39, 0xcb, 0x1c, 0x41, 0x63, + 0x05, 0x9b, 0xaf, 0x2a, 0x3e, 0x61, 0x5f, 0x05, 0xbb, 0x75, 0x4b, 0x94, 0x96, 0x7c, 0x19, 0xdb, + 0x4e, 0xc1, 0xf9, 0x9d, 0x67, 0x50, 0xc7, 0x20, 0xd3, 0x00, 0x8d, 0x7b, 0x1c, 0x90, 0xb9, 0x7c, + 0xc0, 0xa7, 0xb7, 0x2f, 0x22, 0x07, 0x7b, 0xbf, 0xdb, 0x80, 0xd8, 0x2a, 0xa1, 0x6b, 0xc9, 0x4e, + 0xd5, 0xc1, 0x24, 0x06, 0xab, 0x07, 0x72, 0x61, 0x90, 0x58, 0xfc, 0xc2, 0xf3, 0x6f, 0x30, 0x09, + 0xa4, 0xa4, 0x54, 0x80, 0xc6, 0xe6, 0x3a, 0x23, 0x6b, 0x18, 0x12, 0xba, 0x13, 0x7f, 0xdd, 0x71, + 0xf6, 0xd4, 0xb6, 0x48, 0x9b, 0xa3, 0xdd, 0xff, 0xf0, 0x7f, 0x46, 0x8f, 0x67, 0x87, 0xd9, 0x85, + 0xcd, 0x43, 0xed, 0xfe, 0xe6, 0xf6, 0x25, 0x95, 0x75, 0xc1, 0x4b, 0xd9, 0x75, 0x54, 0x85, 0x77, + 0xad, 0xc3, 0xff, 0xce, 0xbc, 0x42, 0x8a, 0xb8, 0x0c, 0x88, 0xeb, 0xc1, 0x00, 0x0c, 0xdc, 0x70, + 0x8c, 0x90, 0x77, 0xf1, 0x4f, 0x7c, 0x3e, 0xf4, 0x54, 0x2d, 0xae, 0xd5, 0xbe, 0x01, 0x7e, 0xf6, + 0xd5, 0x7c, 0x6a, 0xe7, 0x1c, 0x95, 0x2d, 0x0c, 0xbb, 0xdb, 0x55, 0x5d, 0x56, 0x77, 0xfc, 0x38, + 0xf8, 0x7d, 0xb6, 0x8a, 0x62, 0xd4, 0x26, 0xef, 0x92, 0x03, 0xd5, 0x83, 0xe7, 0xb0, 0x6d, 0x80, + 0xff, 0xcc, 0xec, 0xa6, 0xc3, 0x80, 0x2a, 0x21, 0x02, 0x5b, 0xa1, 0x0c, 0xfe, 0xc2, 0x26, 0x65, + 0x0a, 0xb5, 0x1d, 0x6f, 0x70, 0x7d, 0xfe, 0x8d, 0xb4, 0xc6, 0xff, 0x08, 0xca, 0x1a, 0xb5, 0x4a, + 0x52, 0x31, 0xd4, 0x71, 0xc6, 0xcf, 0xe5, 0x3e, 0xdf, 0x15, 0x7c, 0xb3, 0xf0, 0xfa, 0x59, 0x2c, + 0x7d, 0xc3, 0x27, 0xc7, 0x46, 0xf6, 0x65, 0xd6, 0x7a, 0x0f, 0xf7, 0xfe, 0xc3, 0x6c, 0xeb, 0x9d, + 0xa2, 0x2a, 0x73, 0x1d, 0x2d, 0xf0, 0xb1, 0xb1, 0xff, 0x31, 0xf0, 0x9b, 0x35, 0x3e, 0x02, 0x44, + 0x53, 0x70, 0x72, 0x54, 0x8f, 0xb0, 0x92, 0x0a, 0x45, 0x83, 0xb2, 0xea, 0x35, 0xb7, 0x72, 0x5e, + 0x9b, 0xf0, 0x4e, 0x89, 0x10, 0x92, 0x0e, 0x93, 0xdc, 0xf8, 0x87, 0xd2, 0x0b, 0x6c, 0x3f, 0x4c, + 0x82, 0xd5, 0x52, 0x02, 0x03, 0x30, 0x0d, 0x3a, 0x86, 0x01, 0x88, 0x40, 0xa5, 0x2a, 0x92, 0x04, + 0x7f, 0x32, 0xa1, 0x33, 0x78, 0xd5, 0xd6, 0x03, 0xbc, 0x8c, 0xec, 0x6b, 0x75, 0xbe, 0x43, 0x99, + 0x18, 0x32, 0xc2, 0xd7, 0x34, 0x75, 0xb0, 0x86, 0x45, 0x48, 0xf9, 0x4a, 0xd5, 0x13, 0x4b, 0x1c, + 0xf1, 0x39, 0x4c, 0x69, 0x30, 0x8b, 0xe1, 0x33, 0x62, 0xdc, 0x62, 0x52, 0x9c, 0x0a, 0xc2, 0x44, + 0x60, 0xcf, 0x9d, 0xa9, 0xe4, 0x19, 0xb9, 0x3c, 0xba, 0x8f, 0x00, 0x25, 0xd9, 0x6f, 0x47, 0xd8, + 0xee, 0xb0, 0x3f, 0x06, 0xd5, 0x56, 0x71, 0xf5, 0x8a, 0xc7, 0x05, 0xc8, 0xec, 0xc0, 0xec, 0x7a, + 0x16, 0xdd, 0x1c, 0x95, 0x99, 0xfc, 0xae, 0xaa, 0x74, 0x4e, 0x84, 0x92, 0x79, 0x18, 0x44, 0x82, + 0x24, 0x64, 0x9a, 0xf0, 0xf6, 0xd2, 0xb3, 0xe1, 0xb4, 0xb3, 0x35, 0x0f, 0x09, 0xeb, 0xad, 0x36, + 0xb0, 0x2a, 0x73, 0x6b, 0xaf, 0xe0, 0x78, 0xf5, 0x02, 0x0a, 0xcd, 0x0c, 0x62, 0xf0, 0xb9, 0x94, + 0x42, 0x6c, 0xb5, 0xb2, 0x07, 0x6f, 0x87, 0x0a, 0x0a, 0xb4, 0x8a, 0x63, 0xb9, 0x21, 0x86, 0xd3, + 0x0f, 0x67, 0xf9, 0x8a, 0x03, 0xef, 0xf0, 0x81, 0x6f, 0xfc, 0x25, 0x93, 0x1d, 0x60, 0x1c, 0x03, + 0xda, 0x52, 0x63, 0x3e, 0xd6, 0x44, 0x29, 0x78, 0x2a, 0xac, 0x66, 0xd7, 0x9d, 0xbd, 0x41, 0xa9, + 0xb9, 0xef, 0x09, 0x2e, 0x3d, 0xc0, 0xe5, 0x79, 0x9b, 0x3b, 0x12, 0x22, 0x46, 0x80, 0x07, 0x25, + 0x8d, 0x6f, 0x0e, 0x4a, 0xb4, 0x36, 0x64, 0xec, 0xa6, 0x1c, 0x17, 0x62, 0xe2, 0x67, 0xee, 0x73, + 0xc2, 0x36, 0xcb, 0xcf, 0x5e, 0x53, 0x54, 0x7e, 0x94, 0x24, 0x95, 0x80, 0x1d, 0x5e, 0xe9, 0xde, + 0x99, 0x1d, 0xd9, 0xc4, 0xf5, 0x7f, 0x6c, 0xf0, 0x9e, 0x67, 0x6d, 0xef, 0x67, 0xea, 0x4f, 0x45, + 0x97, 0x81, 0x0a, 0x7b, 0x30, 0x28, 0x4d, 0xa5, 0xd6, 0x98, 0xe1, 0x9e, 0xa3, 0x6d, 0xf8, 0x5b, + 0x00, 0xc0, 0xa8, 0x55, 0x4d, 0x1a, 0x4d, 0x69, 0xfc, 0xe4, 0x79, 0x3d, 0x3f, 0xc6, 0xa7, 0xb9, + 0xb1, 0xea, 0x1e, 0x37, 0xc1, 0xd1, 0x86, 0xa5, 0xef, 0xaf, 0xcf, 0xcd, 0xf1, 0x77, 0xa7, 0x6c, + 0x1a, 0x9d, 0xc7, 0xd0, 0xc9, 0x0e, 0x77, 0x30, 0xd9, 0x96, 0xaf, 0xea, 0xde, 0x3f, 0xcf, 0xd2, + 0xab, 0x95, 0x25, 0x2a, 0xd6, 0xa2, 0xa7, 0x46, 0xc6, 0x2f, 0x15, 0x99, 0x04, 0x2f, 0x92, 0x6d, + 0x7a, 0xbc, 0x55, 0xa1, 0xeb, 0x1f, 0x8c, 0xab, 0x82, 0x78, 0x8f, 0xcb, 0x3b, 0xec, 0x02, 0xbd, + 0xbf, 0x30, 0xda, 0x18, 0x8f, 0x2d, 0x27, 0xc4, 0x0a, 0xb6, 0x1b, 0x47, 0x12, 0xae, 0x73, 0x16, + 0x56, 0x9f, 0xea, 0xa9, 0x4a, 0xc6, 0x37, 0xc7, 0x02, 0x42, 0xf6, 0xf0, 0xfd, 0x4d, 0x0c, 0xec, + 0xc6, 0x4e, 0x5a, 0xfe, 0x5f, 0x55, 0x60, 0xac, 0x4c, 0xef, 0x11, 0x7c, 0x5a, 0xd7, 0x07, 0x25, + 0x07, 0xb7, 0xfd, 0xed, 0x51, 0xb6, 0x83, 0x92, 0x30, 0xd5, 0x21, 0xe5, 0xaf, 0xec, 0x6d, 0xff, + 0x04, 0xb2, 0xb1, 0xac, 0x62, 0x2d, 0xcd, 0x7c, 0xde, 0xed, 0xb2, 0xbc, 0x42, 0xb8, 0xff, 0x61, + 0x59, 0xd7, 0xf7, 0x5a, 0x5d, 0xda, 0x2f, 0x80, 0xed, 0xae, 0xa6, 0xe1, 0x91, 0x8d, 0x09, 0x64, + 0xf7, 0x56, 0x8d, 0x7a, 0xdd, 0xc8, 0xfb, 0x5b, 0x7f, 0xb9, 0x8e, 0x47, 0xa7, 0x3b, 0x30, 0x77, + 0x42, 0xc2, 0x7c, 0x4c, 0x5e, 0x4f, 0xd4, 0xd9, 0x5d, 0xef, 0xbf, 0x4c, 0x7e, 0x12, 0x23, 0x91, + 0x6a, 0x9c, 0xa1, 0xb8, 0x2a, 0xef, 0x73, 0x28, 0xe7, 0x77, 0xb7, 0xe8, 0x8f, 0x14, 0x9d, 0x31, + 0x04, 0xb8, 0x97, 0xf0, 0xb9, 0xdd, 0xef, 0x01, 0x3d, 0x6b, 0x1e, 0xcc, 0x35, 0x9a, 0xac, 0xfe, + 0xe9, 0x2c, 0xb3, 0xca, 0xf8, 0x27, 0x56, 0xd0, 0xb9, 0x53, 0x74, 0x09, 0x4d, 0xae, 0x5c, 0x8b, + 0xbc, 0xc4, 0x4b, 0xdb, 0x75, 0x55, 0x51, 0x57, 0x68, 0x51, 0x69, 0x74, 0x8d, 0xa7, 0xcf, 0x24, + 0x5d, 0xc5, 0xf6, 0x79, 0xaf, 0xc6, 0xae, 0x45, 0x5d, 0x44, 0x80, 0x12, 0x0a, 0x7d, 0x27, 0x37, + 0x1c, 0x93, 0x8c, 0x0c, 0x1b, 0x7e, 0x39, 0x24, 0x4d, 0x58, 0xa1, 0xe4, 0x98, 0x61, 0x10, 0xf2, + 0xfa, 0xe7, 0x52, 0x37, 0x80, 0xa8, 0x60, 0x35, 0x7c, 0x8a, 0x49, 0xa3, 0x44, 0x23, 0x20, 0xb5, + 0x18, 0x9a, 0xe6, 0x51, 0x16, 0x75, 0xdf, 0x46, 0x57, 0x00, 0xea, 0xaf, 0x86, 0xe1, 0xa6, 0x68, + 0x84, 0x62, 0xb7, 0x7b, 0xf3, 0xf5, 0x3b, 0x3f, 0x14, 0xed, 0x5f, 0xed, 0x13, 0xd1, 0x94, 0x9f, + 0xc7, 0x16, 0x0b, 0x18, 0xa5, 0x1d, 0xf7, 0x27, 0xad, 0xe6, 0x5f, 0x39, 0x2d, 0x48, 0xd9, 0x24, + 0x9a, 0x50, 0xfe, 0x70, 0x6a, 0xd7, 0xbb, 0x75, 0x77, 0x98, 0x3e, 0x3b, 0x6c, 0xfc, 0xa3, 0xa1, + 0xe4, 0x9d, 0x35, 0x3e, 0x02, 0x42, 0xc7, 0xf0, 0xfa, 0x5b, 0x10, 0xd9, 0x1c, 0x94, 0x44, 0x7c, + 0x9f, 0x55, 0x60, 0xa8, 0x26, 0x6f, 0x64, 0x9e, 0xcd, 0x65, 0x62, 0xf6, 0x07, 0x24, 0x35, 0xe7, + 0x72, 0x52, 0x1a, 0xb0, 0x6f, 0x0d, 0x8b, 0xa5, 0xbf, 0x6b, 0x94, 0xe6, 0x27, 0xb6, 0xcf, 0x4d, + 0x72, 0x30, 0x59, 0xbc, 0xd4, 0x57, 0x4b, 0xb5, 0xbf, 0x59, 0x71, 0x2c, 0x7c, 0x8a, 0x69, 0xa3, + 0x79, 0x86, 0x32, 0x99, 0x6b, 0xa6, 0x83, 0x4f, 0x48, 0xd7, 0xbe, 0xdc, 0xd2, 0x29, 0x04, 0x73, + 0x72, 0xaf, 0x37, 0x4c, 0x09, 0x30, 0x33, 0xdf, 0x60, 0xb6, 0x9e, 0x6f, 0x8f, 0x77, 0x1f, 0x33, + 0x5e, 0x8a, 0x75, 0x1e, 0x6b, 0xc0, 0xed, 0x81, 0xcf, 0xc5, 0xf0, 0xc3, 0xf0, 0x95, 0xd3, 0x3b, + 0x47, 0x42, 0xe6, 0x29, 0xb4, 0xef, 0x6f, 0x12, 0x63, 0x3d, 0x98, 0xa3, 0x4f, 0x81, 0xc3, 0xfc, + 0xb3, 0xf4, 0x35, 0xb7, 0x2c, 0xa7, 0x0e, 0xeb, 0xe3, 0xdc, 0xc0, 0xdd, 0x75, 0x2b, 0x76, 0x9e, + 0xbe, 0x0a, 0x0d, 0x82, 0x54, 0xcd, 0xab, 0x01, 0x5b, 0x58, 0x6a, 0xd8, 0xbb, 0x75, 0x4e, 0x2a, + 0xd0, 0x2f, 0x58, 0xce, 0x8b, 0x59, 0x2c, 0x9c, 0xab, 0x01, 0xc9, 0x6e, 0x1b, 0xc1, 0x12, 0xa2, + 0xb6, 0x80, 0x07, 0x25, 0x8c, 0xad, 0xc1, 0x7e, 0x08, 0x4c, 0x54, 0xfa, 0x8d, 0x7c, 0x95, 0x81, + 0x47, 0xe5, 0xe6, 0x4f, 0x04, 0xd0, 0xf2, 0x29, 0xb0, 0x86, 0xa2, 0x1a, 0xca, 0x82, 0xd0, 0x19, + 0xda, 0xd3, 0x1c, 0x33, 0xb8, 0x61, 0x93, 0xf2, 0x1f, 0x02, 0xd9, 0x5d, 0xc8, 0x43, 0xdd, 0xd5, + 0x7c, 0x07, 0x34, 0xac, 0x54, 0x3f, 0xcb, 0x2c, 0x5e, 0x59, 0xb7, 0x43, 0x73, 0xa8, 0x23, 0xf0, + 0x7a, 0xd3, 0xde, 0x58, 0xb1, 0x1d, 0x89, 0xf3, 0x26, 0x92, 0x88, 0xb4, 0x10, 0x7c, 0x19, 0x5f, + 0x94, 0xbb, 0xe5, 0x12, 0xae, 0xde, 0xcb, 0xaa, 0x71, 0x1a, 0xe2, 0xee, 0xe0, 0x9c, 0x8a, 0x36, + 0xdc, 0xef, 0x16, 0x21, 0x04, 0xe6, 0xc9, 0xc3, 0xc0, 0x52, 0x55, 0xf7, 0x53, 0xf5, 0x9c, 0xf5, + 0x44, 0x53, 0x8f, 0x7e, 0x22, 0xa7, 0xa4, 0x06, 0x80, 0x92, 0x17, 0xb5, 0x54, 0x58, 0x50, 0xe1, + 0x4e, 0xfb, 0x10, 0x6f, 0xb0, 0x15, 0x64, 0x86, 0xce, 0xbc, 0xe5, 0x80, 0xd9, 0x6b, 0xc4, 0x0d, + 0x8d, 0xce, 0x42, 0x36, 0xf0, 0x02, 0xed, 0xbe, 0xe1, 0xfd, 0xdd, 0xce, 0x55, 0x22, 0x7d, 0xfd, + 0x7b, 0x05, 0xf0, 0x53, 0x22, 0x49, 0x48, 0x36, 0xf0, 0x72, 0x1a, 0x79, 0x39, 0x2c, 0x0d, 0x58, + 0x5e, 0x13, 0x7e, 0xc2, 0xf9, 0xf8, 0x81, 0x89, 0x68, 0x7b, 0x1a, 0xc5, 0xef, 0xa1, 0xce, 0x01, + 0xb9, 0x20, 0x67, 0x96, 0x58, 0x01, 0x7c, 0x07, 0x55, 0x5b, 0x55, 0x77, 0xa6, 0x38, 0x92, 0xfc, + 0x59, 0xb6, 0x60, 0x12, 0x5f, 0x98, 0x01, 0xc1, 0x98, 0xdb, 0x7d, 0xff, 0x6f, 0x7a, 0xc5, 0x9e, + 0xa4, 0xd1, 0x1e, 0xa2, 0x3a, 0xe0, 0x27, 0xd9, 0x18, 0x6d, 0x7f, 0xa8, 0xc1, 0x39, 0x3a, 0x52, + 0x4c, 0x72, 0x7a, 0x08, 0xef, 0xb9, 0xa8, 0x73, 0x35, 0x31, 0x7c, 0xfb, 0x30, 0x2a, 0x98, 0x3f, + 0xb2, 0xc0, 0x51, 0xa5, 0xb7, 0xce, 0xf1, 0xc8, 0x54, 0xf8, 0xec, 0x69, 0x28, 0x74, 0x53, 0x22, + 0x75, 0x09, 0xeb, 0x85, 0x24, 0x44, 0x97, 0x83, 0x09, 0xb8, 0x49, 0x78, 0x53, 0x34, 0xcc, 0xdb, + 0x22, 0x76, 0xf6, 0xd6, 0xc2, 0x15, 0xc7, 0x61, 0xaa, 0x27, 0xf7, 0x14, 0x1a, 0xb7, 0xad, 0xf6, + 0xc0, 0x20, 0xaa, 0xb0, 0x33, 0xa8, 0x64, 0x7e, 0xe7, 0x69, 0x2d, 0x6b, 0xf8, 0x64, 0xee, 0xaf, + 0xb0, 0x2d, 0x2f, 0xae, 0xba, 0xb2, 0x52, 0xa4, 0xba, 0x4f, 0xc9, 0x7e, 0x68, 0xc1, 0x49, 0xd9, + 0x92, 0xdb, 0x5f, 0x83, 0xb9, 0xb3, 0xc6, 0x7a, 0xeb, 0x44, 0x3d, 0x63, 0x6b, 0x01, 0x00, 0xf9, + 0x58, 0x09, 0x72, 0x22, 0x70, 0x15, 0x30, 0x1d, 0x4e, 0xb2, 0x97, 0x53, 0xc8, 0x89, 0x85, 0x5f, + 0xd0, 0x1b, 0x00, 0x88, 0xf0, 0xc3, 0x86, 0x2c, 0x21, 0x07, 0xe9, 0x2c, 0x24, 0x8c, 0x94, 0x90, + 0x31, 0xb1, 0x7b, 0xff, 0xf6, 0xdf, 0xf9, 0xd8, 0x4b, 0xdc, 0x7d, 0xaa, 0x18, 0xe7, 0x37, 0xd3, + 0x48, 0xa5, 0x04, 0x28, 0xc0, 0x5d, 0xf9, 0x0b, 0xb0, 0x96, 0x97, 0x3a, 0x7c, 0xf7, 0x21, 0x57, + 0xac, 0x9c, 0x98, 0x34, 0xb9, 0xfa, 0xb0, 0x49, 0x79, 0x5e, 0x86, 0xad, 0x29, 0x25, 0x31, 0x03, + 0xdf, 0x1b, 0xd9, 0xbb, 0xcb, 0x4c, 0x8a, 0x8f, 0xbd, 0x58, 0x31, 0xc7, 0x0c, 0x02, 0x0e, 0xa5, + 0xd5, 0x8e, 0x23, 0xce, 0x0e, 0x24, 0xd7, 0xd3, 0x01, 0x25, 0xf0, 0xf8, 0x1c, 0xae, 0x86, 0x4f, + 0xad, 0xb2, 0xf4, 0xf1, 0xe3, 0xcc, 0x8d, 0xec, 0xae, 0x01, 0xc7, 0xa8, 0x8c, 0x5e, 0xe0, 0xe4, + 0x0c, 0x5a, 0x0e, 0x48, 0xa3, 0x56, 0x2d, 0xa1, 0x31, 0x62, 0x56, 0x1c, 0x48, 0x7e, 0x4d, 0x46, + 0x26, 0x10, 0x65, 0x1d, 0x45, 0x38, 0x1f, 0xbe, 0x7e, 0x5e, 0x70, 0x26, 0x5f, 0x13, 0xa7, 0xf1, + 0x8b, 0xc8, 0x0c, 0xc8, 0x98, 0xbc, 0xbd, 0x7e, 0x17, 0xce, 0xad, 0x3c, 0x27, 0xd9, 0x35, 0x8a, + 0xfd, 0x47, 0x83, 0x34, 0x43, 0x00, 0x1b, 0x19, 0xbf, 0xf3, 0xed, 0xf7, 0xae, 0x48, 0x65, 0xcc, + 0x34, 0x3a, 0x62, 0x5a, 0x30, 0x45, 0xb5, 0x84, 0x55, 0x56, 0x21, 0x7b, 0xb9, 0xad, 0xc6, 0xbc, + 0x16, 0x8e, 0xb0, 0x63, 0x94, 0x5a, 0xb7, 0xbf, 0x4f, 0x00, 0x23, 0x07, 0x2f, 0x1f, 0x40, 0x37, + 0xc4, 0xf0, 0x88, 0x7a, 0xe6, 0x37, 0x89, 0xfd, 0xbb, 0xfd, 0x48, 0x88, 0x70, 0x3b, 0x69, 0x5a, + 0x46, 0xdb, 0xbb, 0x75, 0x06, 0xc7, 0xff, 0x39, 0xd2, 0x9b, 0x79, 0x3c, 0x0e, 0xa5, 0xdd, 0x64, + 0x80, 0x12, 0x25, 0x09, 0x3f, 0x07, 0x0c, 0x0e, 0xc7, 0xaf, 0x2d, 0x9e, 0xc1, 0xc9, 0x34, 0x6a, + 0xf9, 0xba, 0x89, 0x99, 0x91, 0x2f, 0xc6, 0xc2, 0x77, 0xe8, 0x92, 0xd5, 0x05, 0x01, 0x3e, 0x18, + 0x2f, 0x8b, 0xd1, 0x4c, 0x46, 0x00, 0x13, 0xe1, 0x28, 0x99, 0x34, 0xd2, 0x9f, 0xf5, 0xb7, 0x27, + 0xf1, 0x89, 0xe6, 0x91, 0x9e, 0x8a, 0x78, 0x42, 0x64, 0xde, 0xd8, 0x50, 0xc0, 0x70, 0xfb, 0x9b, + 0xad, 0x95, 0x11, 0xc1, 0x3f, 0xa7, 0x1c, 0xfc, 0x28, 0xe8, 0xad, 0x25, 0x5b, 0x13, 0xb5, 0x0b, + 0xb8, 0x48, 0xa4, 0x25, 0xec, 0xfc, 0xc9, 0x0d, 0x29, 0xc0, 0x47, 0x86, 0xcf, 0xf4, 0xe6, 0x74, + 0x55, 0x09, 0x6d, 0x3e, 0xef, 0xc4, 0xdb, 0x49, 0xe7, 0x01, 0xf9, 0x81, 0x0e, 0x15, 0x0d, 0x3d, + 0xd7, 0x28, 0x4c, 0x34, 0x39, 0x39, 0x98, 0xec, 0x0e, 0xd2, 0x02, 0x5b, 0x8f, 0x0c, 0x8e, 0xed, + 0xa7, 0xdc, 0x36, 0x48, 0x71, 0x72, 0xef, 0x7a, 0xfc, 0x9f, 0x92, 0xfb, 0x00, 0x4a, 0xea, 0x2e, + 0xf3, 0xcc, 0xe0, 0x87, 0x89, 0x2a, 0xc0, 0x2d, 0xe3, 0xf7, 0x2d, 0x8e, 0xc2, 0xd6, 0xe7, 0xfe, + 0xec, 0x7a, 0x48, 0xc5, 0x6e, 0x0e, 0xbe, 0x07, 0xe8, 0x97, 0x0d, 0x24, 0xd1, 0xa0, 0xa2, 0x9e, + 0x05, 0x19, 0x5e, 0x18, 0xfa, 0x5f, 0xd6, 0xd0, 0x7c, 0x21, 0x8f, 0xf9, 0xc9, 0xc3, 0x5c, 0x24, + 0xf8, 0x5f, 0x6f, 0xe4, 0xb8, 0xfa, 0xab, 0xe7, 0xa2, 0x81, 0x33, 0x03, 0x97, 0x80, 0xdb, 0x7c, + 0xd0, 0xcd, 0x05, 0x33, 0x84, 0xec, 0x27, 0x9f, 0x0a, 0xb5, 0x9d, 0xcf, 0x7c, 0x34, 0x03, 0xe0, + 0x1c, 0x43, 0xc1, 0x65, 0x3f, 0x01, 0x9d, 0x1a, 0x7c, 0x9f, 0xf7, 0xc6, 0xed, 0x39, 0x52, 0x8e, + 0xf0, 0x0f, 0xa3, 0x74, 0x2d, 0xce, 0x93, 0x22, 0x75, 0xf0, 0x6d, 0x06, 0xfd, 0x54, 0xec, 0x1a, + 0x31, 0xc8, 0xe5, 0x21, 0xcb, 0xbf, 0xac, 0x88, 0xcb, 0x30, 0x3a, 0xf0, 0x3d, 0xe6, 0x65, 0x79, + 0x54, 0x69, 0xa1, 0x80, 0x56, 0x52, 0xda, 0x7a, 0x76, 0xef, 0xfb, 0x27, 0xc3, 0x92, 0x53, 0xca, + 0x19, 0x29, 0x73, 0x81, 0x1a, 0x96, 0xc8, 0x51, 0x10, 0x0b, 0x10, 0x93, 0x37, 0xb5, 0xb0, 0xe6, + 0xe4, 0x80, 0xdb, 0xe0, 0x91, 0x6b, 0x8c, 0x1c, 0x7e, 0x8e, 0x51, 0x6f, 0xee, 0xd5, 0x87, 0x0a, + 0x18, 0x7f, 0x50, 0x98, 0x3d, 0x9f, 0x92, 0x7b, 0xd5, 0x83, 0xeb, 0x30, 0x6d, 0x80, 0x40, 0x64, + 0xa9, 0x57, 0x15, 0x80, 0x5a, 0x4e, 0x7e, 0x98, 0x96, 0x4a, 0x8e, 0xc3, 0xfe, 0x4d, 0x6d, 0x02, + 0x1f, 0x5b, 0x6b, 0x99, 0x2f, 0x2d, 0x8a, 0x71, 0x60, 0x13, 0xc4, 0x38, 0x00, 0x82, 0x9a, 0xc3, + 0x02, 0x0a, 0x36, 0x0c, 0xb0, 0x12, 0x67, 0x86, 0x63, 0x85, 0x34, 0x87, 0x7d, 0x67, 0x47, 0xc7, + 0x73, 0xa3, 0x8f, 0x7b, 0x3e, 0xf8, 0x6d, 0x5b, 0x37, 0x4c, 0x29, 0xf6, 0x83, 0x2b, 0xd1, 0xf3, + 0xaa, 0x4f, 0xec, 0x6a, 0xad, 0xba, 0x49, 0x1a, 0x4e, 0x61, 0xb1, 0xb0, 0x6f, 0x05, 0xe4, 0x44, + 0x7c, 0x30, 0x36, 0x0a, 0x28, 0x4d, 0x60, 0xc0, 0xba, 0xb5, 0xcc, 0x5d, 0x5d, 0xbb, 0x60, 0x12, + 0x5f, 0x00, 0x3f, 0xa7, 0xf0, 0xaf, 0x34, 0x62, 0xdf, 0xdb, 0xff, 0x34, 0xde, 0x19, 0x27, 0x11, + 0x78, 0x01, 0xc2, 0x4d, 0xe8, 0xd1, 0x9e, 0xca, 0xff, 0x02, 0x2d, 0xbd, 0xf9, 0x10, 0x0b, 0xdd, + 0x1a, 0xdc, 0xac, 0x77, 0x64, 0x88, 0xb5, 0xeb, 0xba, 0xb7, 0xc0, 0xb7, 0x2d, 0xcf, 0x0e, 0x4a, + 0xa3, 0x56, 0x43, 0xd1, 0xb2, 0x92, 0x6e, 0x03, 0x2a, 0x1f, 0x9b, 0x4c, 0x5d, 0x65, 0x61, 0x80, + 0x41, 0x17, 0x86, 0x01, 0xcb, 0xc0, 0xcc, 0x73, 0xda, 0x9c, 0x9e, 0x5e, 0x93, 0x90, 0x11, 0xcf, + 0xc3, 0x0c, 0x95, 0x34, 0x83, 0x00, 0x02, 0xad, 0xcd, 0x1c, 0x23, 0xca, 0x90, 0xd1, 0x63, 0xd9, + 0xee, 0x50, 0x05, 0x28, 0xf2, 0xe6, 0x77, 0x4c, 0x1a, 0x94, 0xac, 0xed, 0x48, 0x62, 0xf6, 0xbb, + 0x76, 0xf8, 0x85, 0xe0, 0x4f, 0x3a, 0x2c, 0x19, 0xd8, 0xf1, 0x09, 0xc4, 0x36, 0x58, 0xdb, 0xaa, + 0x30, 0x0d, 0xf1, 0x65, 0xe0, 0x43, 0xa3, 0x7e, 0x50, 0x17, 0x05, 0x99, 0x87, 0xc4, 0x03, 0x84, + 0x83, 0x92, 0xd6, 0x67, 0x01, 0x20, 0x63, 0x78, 0x8a, 0xb4, 0xf7, 0xde, 0x75, 0x54, 0x85, 0x47, + 0x2f, 0xc3, 0x37, 0xc1, 0xaa, 0xa8, 0xde, 0xc9, 0x88, 0x86, 0xe0, 0xe4, 0xaa, 0x9d, 0x35, 0xba, + 0x49, 0xd1, 0x44, 0x1a, 0x5a, 0xc3, 0x62, 0xeb, 0xb6, 0x4f, 0x63, 0x7c, 0x3e, 0x8e, 0x4b, 0x2d, + 0xb0, 0xd6, 0xd8, 0x2f, 0x3a, 0xbc, 0x80, 0x56, 0xbf, 0xc1, 0x14, 0x23, 0x4f, 0x12, 0x72, 0xfe, + 0xed, 0x77, 0xd6, 0x52, 0x8b, 0x05, 0x3c, 0x7c, 0x0a, 0x1b, 0x04, 0x55, 0x21, 0x47, 0x1c, 0x16, + 0x25, 0x60, 0x86, 0x01, 0xad, 0x09, 0x1b, 0xe6, 0x66, 0x88, 0x47, 0x66, 0x12, 0x2e, 0x5c, 0x5a, + 0xd3, 0xe8, 0x3d, 0x76, 0xf4, 0x95, 0x4a, 0x3b, 0x4e, 0x8d, 0xb2, 0xc0, 0xca, 0x2a, 0xac, 0xbe, + 0x10, 0xbb, 0x5f, 0x9e, 0xd7, 0x9f, 0x25, 0x80, 0x69, 0x1b, 0xff, 0xcf, 0xe5, 0x42, 0xde, 0xcc, + 0xf0, 0xe3, 0x56, 0x85, 0x16, 0x53, 0x40, 0x60, 0x77, 0x55, 0xc8, 0x8b, 0xc0, 0x45, 0x80, 0xe7, + 0x00, 0x0f, 0x0d, 0x63, 0x0e, 0x5f, 0xb3, 0xc0, 0x73, 0x78, 0xf7, 0xa3, 0x97, 0x87, 0xc5, 0x1f, + 0x0e, 0x49, 0x93, 0x5e, 0x02, 0x50, 0x73, 0x70, 0xf2, 0xbf, 0xcd, 0x83, 0xa0, 0x81, 0x3f, 0x50, + 0x25, 0xd5, 0xeb, 0x00, 0xb7, 0xb2, 0xa7, 0x35, 0x7f, 0x7c, 0x44, 0x73, 0xf0, 0x72, 0x44, 0x86, + 0x69, 0x27, 0x8d, 0x20, 0x16, 0xf4, 0x15, 0x4d, 0x2e, 0x7e, 0xaf, 0xb2, 0x12, 0x81, 0xe6, 0x38, + 0xe4, 0xb4, 0xb4, 0xd7, 0xa9, 0xfc, 0x5a, 0x1c, 0xe9, 0x3d, 0x7a, 0xc4, 0xf6, 0x91, 0x2c, 0x98, + 0x00, 0x49, 0x3d, 0xce, 0x47, 0xc8, 0x8f, 0xbf, 0xc9, 0xed, 0xe7, 0x87, 0x1e, 0x75, 0x7d, 0xf6, + 0xbb, 0x4e, 0x99, 0x52, 0xc0, 0xd2, 0x48, 0x25, 0xc4, 0xc5, 0x93, 0x97, 0xdb, 0x4b, 0x87, 0x33, + 0xca, 0x64, 0x50, 0x0a, 0x30, 0xa6, 0x50, 0x94, 0xfe, 0x56, 0xa4, 0xbb, 0xc0, 0x24, 0x56, 0xb0, + 0xd7, 0x55, 0x74, 0x0c, 0x90, 0x6d, 0xe7, 0x4a, 0x00, 0xa0, 0xee, 0x3c, 0x51, 0xdf, 0xf0, 0xec, + 0x5b, 0xfc, 0xbf, 0xf1, 0xb9, 0xb4, 0x98, 0x00, 0xda, 0x55, 0x45, 0xa8, 0xf0, 0x02, 0xa8, 0xd6, + 0xe0, 0xe4, 0x54, 0x4f, 0x5d, 0x75, 0xb1, 0xdc, 0xf2, 0x22, 0x80, 0xff, 0xeb, 0x13, 0xf7, 0xb1, + 0x14, 0xed, 0x07, 0x24, 0xd1, 0xab, 0xe1, 0xc8, 0x53, 0x52, 0x60, 0x25, 0xc8, 0x89, 0xe0, 0x30, + 0xd2, 0xa4, 0x61, 0xf6, 0x65, 0xc7, 0x3a, 0x19, 0x79, 0x2a, 0x35, 0x03, 0x22, 0x88, 0x65, 0x1f, + 0x92, 0x65, 0x2d, 0xb1, 0x71, 0xc2, 0x04, 0x16, 0x96, 0x18, 0x2c, 0xa4, 0xd0, 0xc0, 0xdc, 0xac, + 0x3d, 0xb7, 0x82, 0xb2, 0x3f, 0xc0, 0x1b, 0xff, 0xae, 0x5c, 0xb8, 0x63, 0xd2, 0xb3, 0xd0, 0x55, + 0xfa, 0xb0, 0xf3, 0xd1, 0xc2, 0x55, 0xb5, 0x6a, 0x5d, 0x60, 0x3d, 0x94, 0xa4, 0x26, 0x6f, 0x5e, + 0x8e, 0x01, 0xf8, 0xe1, 0xd4, 0x1a, 0x0c, 0xd7, 0x32, 0xcd, 0xf0, 0xdc, 0x70, 0x04, 0x61, 0x42, + 0xd0, 0x81, 0xaf, 0x12, 0x04, 0x54, 0x76, 0x18, 0x3a, 0x57, 0x09, 0xbe, 0x5b, 0xa8, 0xf0, 0x72, + 0xa8, 0xb6, 0x60, 0xe4, 0xc2, 0xb5, 0x01, 0x1b, 0x87, 0x91, 0x24, 0x5a, 0xbf, 0x07, 0x95, 0x1d, + 0xac, 0x1c, 0xcf, 0x66, 0x8d, 0x38, 0x16, 0xf4, 0x0a, 0xe5, 0xd9, 0x58, 0x2f, 0x16, 0x6d, 0x04, + 0x18, 0x9a, 0x06, 0x01, 0xdc, 0xff, 0x1c, 0xec, 0x23, 0x7c, 0x19, 0xfc, 0x6a, 0x73, 0x9e, 0xbb, + 0x9c, 0x01, 0x40, 0x7c, 0xc1, 0x80, 0x5d, 0x46, 0x98, 0xe1, 0x92, 0x04, 0xe7, 0x32, 0xc4, 0x1d, + 0xb8, 0x0e, 0x23, 0x4f, 0x41, 0xc5, 0x81, 0xba, 0xfb, 0x3f, 0xad, 0xb6, 0x94, 0x76, 0xdc, 0x9e, + 0x0f, 0x9b, 0x33, 0xcf, 0xff, 0xed, 0xbf, 0xf3, 0xd1, 0xc1, 0x1f, 0x59, 0x54, 0xf0, 0xa7, 0x9d, + 0x1d, 0x3c, 0x5f, 0x28, 0x33, 0x78, 0x9f, 0x85, 0x58, 0x8b, 0x6e, 0xad, 0x23, 0x17, 0x60, 0xc0, + 0x10, 0x45, 0x16, 0xa0, 0xe4, 0x44, 0x7c, 0x2c, 0x94, 0x1d, 0x24, 0x5a, 0xbf, 0x00, 0x95, 0x17, + 0x7c, 0x1c, 0x55, 0x40, 0x73, 0x63, 0x44, 0x0a, 0x74, 0xe4, 0xbc, 0xe0, 0xec, 0x7a, 0x11, 0xdd, + 0x39, 0x2f, 0x24, 0xb8, 0x49, 0xd1, 0x4c, 0x1a, 0xba, 0x43, 0x22, 0x74, 0x0a, 0x32, 0x9a, 0xaf, + 0x00, 0x0d, 0x66, 0xd3, 0x69, 0x65, 0x6a, 0x1f, 0x80, 0x41, 0xc5, 0xe1, 0xdc, 0xc8, 0x5f, 0xac, + 0xa1, 0x80, 0x90, 0xee, 0x85, 0x78, 0x1f, 0x22, 0x7c, 0x1b, 0x69, 0xdb, 0x35, 0x87, 0x8c, 0xcb, + 0x31, 0x69, 0x7c, 0xbf, 0x99, 0xa3, 0xe3, 0xf9, 0x1c, 0xce, 0x82, 0x3b, 0xd7, 0x2f, 0xa5, 0x83, + 0xfa, 0xb9, 0x31, 0x62, 0xf2, 0x96, 0x85, 0xb2, 0x04, 0x94, 0x8c, 0x18, 0xff, 0xcd, 0x2a, 0x42, + 0x0c, 0x02, 0xa9, 0x4f, 0xbd, 0x6a, 0xc0, 0x82, 0x9b, 0x4d, 0x25, 0x80, 0x87, 0x22, 0x5b, 0xe0, + 0x6b, 0x77, 0xba, 0xa3, 0x40, 0xdd, 0x03, 0x9d, 0x19, 0x11, 0xbc, 0x0d, 0xc6, 0x89, 0x35, 0xd6, + 0xa3, 0x88, 0xaf, 0x17, 0xad, 0x3d, 0xb0, 0x26, 0x42, 0x61, 0xb0, 0x72, 0x08, 0x9a, 0xdd, 0x42, + 0x83, 0x92, 0x45, 0x37, 0x3a, 0x97, 0x93, 0x55, 0xb4, 0x38, 0xa3, 0x42, 0x2a, 0x62, 0xe2, 0x67, + 0x4d, 0x1b, 0x98, 0xdb, 0x5d, 0xff, 0xd4, 0xfe, 0x02, 0x80, 0x81, 0x0c, 0xaa, 0x06, 0xa2, 0x96, + 0xab, 0x20, 0xe4, 0x96, 0xeb, 0xe9, 0x1a, 0xae, 0xb2, 0xae, 0x0c, 0xbf, 0x42, 0xa4, 0xd6, 0x02, + 0x16, 0x39, 0x4a, 0x9c, 0x73, 0x33, 0xb6, 0xcf, 0xf8, 0x48, 0x1e, 0xf9, 0xe7, 0xdf, 0xb3, 0xb4, + 0xa1, 0x85, 0x23, 0x81, 0x62, 0xe0, 0x4b, 0xf2, 0x76, 0xb9, 0xc4, 0xf2, 0x61, 0x56, 0x7d, 0xba, + 0x86, 0x01, 0x17, 0x86, 0x48, 0xe0, 0x14, 0x61, 0x07, 0xbf, 0x5c, 0x58, 0xd0, 0xe1, 0xfc, 0xb3, + 0x56, 0x1b, 0xe7, 0x46, 0x9e, 0x15, 0x11, 0x19, 0x12, 0x39, 0x57, 0xf8, 0xdd, 0xe0, 0x84, 0xf8, + 0x0c, 0x6e, 0x27, 0xd3, 0x9f, 0x22, 0xd5, 0x50, 0x71, 0x48, 0xa2, 0x8a, 0xe0, 0xe4, 0xa5, 0x85, + 0x5a, 0xed, 0x00, 0x24, 0xb2, 0x7f, 0x9f, 0xa5, 0x41, 0xf5, 0xc1, 0xc9, 0x55, 0x47, 0xf1, 0xf5, + 0x63, 0xb8, 0xc9, 0x8b, 0x17, 0x70, 0x63, 0xd5, 0xee, 0xef, 0xc9, 0x78, 0x47, 0xc1, 0x9d, 0x13, + 0x00, 0xc4, 0xf2, 0x8d, 0xf6, 0x18, 0x7f, 0xe7, 0xcc, 0xab, 0x86, 0xcd, 0xa8, 0x1b, 0x13, 0xd1, + 0x4c, 0x33, 0x96, 0x98, 0x81, 0xbc, 0x44, 0x1a, 0x0f, 0x91, 0x21, 0xab, 0x98, 0xaa, 0x92, 0x04, + 0xaf, 0x32, 0x75, 0x1b, 0x26, 0xf0, 0xf5, 0x60, 0xe0, 0x16, 0x60, 0xfe, 0xb9, 0xf8, 0x78, 0x13, + 0xb7, 0xd0, 0xb6, 0x9d, 0xc3, 0x00, 0x55, 0x46, 0x59, 0x5d, 0xf0, 0xe1, 0x38, 0x5a, 0xcb, 0x01, + 0xec, 0xa2, 0x02, 0x31, 0xf5, 0x0c, 0xff, 0xae, 0xe2, 0xcd, 0xa3, 0x09, 0x70, 0x00, 0x7b, 0x78, + 0x29, 0x86, 0x05, 0x86, 0x62, 0x18, 0x05, 0x84, 0x09, 0xaa, 0x58, 0x30, 0xff, 0x9d, 0x10, 0x2d, + 0xb7, 0x79, 0xfb, 0xd7, 0x83, 0x92, 0x07, 0x6e, 0x6c, 0xde, 0x02, 0xf4, 0xb4, 0xa2, 0xb7, 0x01, + 0xc8, 0x8b, 0x55, 0x14, 0x2e, 0xbd, 0x6f, 0xf4, 0x4b, 0x75, 0xc6, 0x00, 0x0e, 0x48, 0x1b, 0x56, + 0x5e, 0x8d, 0xaf, 0x5d, 0x6f, 0xbd, 0x88, 0xc4, 0x0c, 0x2e, 0x04, 0xfa, 0x69, 0x06, 0x53, 0xd4, + 0x50, 0x15, 0xb2, 0x40, 0x82, 0x51, 0x9a, 0xb2, 0x54, 0x73, 0x15, 0x55, 0xf2, 0x91, 0x3c, 0xd3, + 0x92, 0x66, 0xf4, 0xf0, 0x92, 0xef, 0xf8, 0x97, 0x26, 0x7c, 0x1d, 0xfa, 0x93, 0xbf, 0x08, 0x0f, + 0x9c, 0x30, 0x21, 0x62, 0xb5, 0x44, 0xab, 0xb4, 0x00, 0x8a, 0x85, 0x83, 0xfa, 0x1e, 0xbb, 0xc3, + 0x54, 0x72, 0x80, 0x18, 0xca, 0xa7, 0xae, 0xe2, 0xf1, 0x48, 0x07, 0xfa, 0xd3, 0x43, 0x56, 0x11, + 0x4a, 0x3b, 0x02, 0x31, 0x66, 0x0c, 0x7f, 0xe7, 0x4c, 0x9b, 0x7d, 0x94, 0xcc, 0x90, 0x46, 0x06, + 0x36, 0x94, 0x70, 0x9a, 0x58, 0x59, 0x1e, 0xe0, 0x5e, 0x9b, 0x15, 0xe2, 0xae, 0x23, 0x1a, 0x52, + 0xbe, 0xfd, 0xfb, 0x15, 0xd7, 0xce, 0xc5, 0xcf, 0x42, 0x78, 0x00, 0x36, 0xc8, 0x88, 0xea, 0x54, + 0x1c, 0x94, 0x7f, 0xbc, 0x2e, 0x7b, 0x44, 0x5c, 0x09, 0x64, 0xdb, 0xf0, 0x63, 0xb8, 0xcb, 0x8b, + 0x3f, 0x5b, 0x8f, 0x50, 0xdf, 0xbd, 0xc8, 0xec, 0x7c, 0xf1, 0xde, 0xd0, 0xc0, 0xf3, 0x89, 0x03, + 0x62, 0x4e, 0xba, 0x88, 0x65, 0x3d, 0xc0, 0xb1, 0x9c, 0xc1, 0xf6, 0xd2, 0xa3, 0xa4, 0x8c, 0x32, + 0x60, 0x9a, 0x18, 0x36, 0xcf, 0x9a, 0xe6, 0x45, 0x57, 0x66, 0xe6, 0x85, 0x01, 0x66, 0x51, 0x96, + 0x61, 0xec, 0xb2, 0xbf, 0xcf, 0x28, 0x97, 0x8d, 0x5d, 0xd5, 0x6c, 0x65, 0xc1, 0x23, 0x1f, 0x22, + 0x05, 0xd6, 0x2a, 0x18, 0x3f, 0x2d, 0xe0, 0x12, 0xc3, 0x78, 0xbf, 0xf3, 0x85, 0x79, 0x96, 0x8b, + 0x87, 0x75, 0x48, 0x33, 0x95, 0x9b, 0x82, 0xb2, 0x58, 0x4a, 0xc1, 0x80, 0x47, 0x46, 0x46, 0x29, + 0xc1, 0x80, 0x03, 0xa8, 0xc7, 0x3e, 0x00, 0xbe, 0xb2, 0xc3, 0x14, 0x06, 0x48, 0x2e, 0xfc, 0x3b, + 0x95, 0x0c, 0x7f, 0xe7, 0xf0, 0x3b, 0xec, 0x0a, 0x9e, 0x00, 0x97, 0x8a, 0xa2, 0xe9, 0x55, 0x2d, + 0x38, 0x3a, 0xfb, 0xe7, 0x7e, 0x77, 0x72, 0x6a, 0xc7, 0x1b, 0x4e, 0x18, 0xbc, 0x20, 0x4b, 0x6b, + 0x5f, 0xbf, 0xf1, 0x0e, 0x11, 0x68, 0xf8, 0xc9, 0x00, 0xd6, 0x90, 0x9b, 0xbc, 0x00, 0xca, 0xbe, + 0xf2, 0x7a, 0xe1, 0x4f, 0xc6, 0x8d, 0xb6, 0x6b, 0x7d, 0x77, 0x7b, 0x32, 0x69, 0x56, 0x50, 0xc4, + 0x50, 0x35, 0xc0, 0xe0, 0xe8, 0xc6, 0xf5, 0x77, 0x73, 0xc0, 0x00, 0xb3, 0xe1, 0x0f, 0x71, 0x0b, + 0x80, 0x35, 0x09, 0x7f, 0x44, 0x2d, 0xbb, 0x4f, 0x70, 0xae, 0x69, 0x99, 0x19, 0xed, 0xaa, 0xe3, + 0x31, 0x3e, 0x37, 0xcb, 0x95, 0x9a, 0xc0, 0x05, 0xbe, 0x7d, 0xb4, 0xed, 0x72, 0xd7, 0x41, 0x82, + 0xa4, 0x05, 0x19, 0x21, 0xc9, 0x38, 0x28, 0xb0, 0xd3, 0xfb, 0x01, 0x51, 0x86, 0x86, 0x7f, 0xd6, + 0xa6, 0x86, 0x60, 0x13, 0x83, 0x98, 0xd1, 0xaf, 0xb9, 0x4f, 0x10, 0x73, 0xb6, 0x28, 0x2f, 0xf2, + 0xda, 0x53, 0x6d, 0x19, 0xbd, 0x78, 0x77, 0xf7, 0x05, 0x36, 0x7f, 0x22, 0x78, 0x2d, 0x14, 0x62, + 0xc5, 0xdd, 0x5f, 0xa8, 0x12, 0x5d, 0x7f, 0x80, 0x9d, 0xd9, 0x41, 0x67, 0xd4, 0x30, 0x1a, 0xbe, + 0x59, 0xf8, 0xb1, 0xdc, 0xa4, 0xc5, 0xbf, 0xb3, 0xc7, 0xaf, 0x05, 0x5e, 0xc1, 0xc9, 0x26, 0x9f, + 0x89, 0x3a, 0x1a, 0x41, 0x6c, 0xd1, 0xc2, 0x07, 0x69, 0xcf, 0xff, 0xaf, 0xa1, 0x2d, 0x5f, 0xaa, + 0xeb, 0xc0, 0xd5, 0x3d, 0x34, 0x1b, 0x01, 0xdd, 0x08, 0xdc, 0x15, 0xdc, 0x6f, 0xda, 0x44, 0x35, + 0x95, 0x05, 0xb0, 0x13, 0x58, 0x66, 0x7e, 0xa8, 0x4d, 0x55, 0xb0, 0xfe, 0xc8, 0x49, 0xcd, 0xe3, + 0x5e, 0xfb, 0x9a, 0x1a, 0x9b, 0xd7, 0x3f, 0xe9, 0x4c, 0xe8, 0x1d, 0x0b, 0x67, 0xa5, 0x0d, 0x82, + 0x17, 0x4c, 0x35, 0x5e, 0x86, 0xa1, 0x22, 0x20, 0xf1, 0x23, 0x9c, 0x55, 0x21, 0xb3, 0xf0, 0xc0, + 0x67, 0x4c, 0xc3, 0xc3, 0xe0, 0xcf, 0x44, 0xeb, 0xcc, 0x8c, 0xb3, 0x0a, 0x77, 0x0c, 0x76, 0x22, + 0x95, 0xe7, 0xc1, 0xa5, 0xb1, 0xdf, 0x82, 0xb8, 0x00, 0xb0, 0xae, 0x58, 0x0c, 0xff, 0x02, 0xf1, + 0xe1, 0x0c, 0x2b, 0x62, 0x0f, 0xc7, 0x18, 0xcd, 0xbe, 0x77, 0xee, 0x15, 0x49, 0x53, 0xa1, 0x14, + 0xc9, 0x6e, 0x98, 0xc1, 0xef, 0xd1, 0xbe, 0x0d, 0xd7, 0x5a, 0xfe, 0x61, 0xdf, 0x11, 0xc5, 0x34, + 0x2a, 0xfe, 0x58, 0x39, 0x46, 0x8d, 0x59, 0x3c, 0x58, 0xcd, 0x0b, 0x01, 0x55, 0x00, 0x49, 0xbd, + 0xfe, 0x44, 0x37, 0x8c, 0xae, 0x12, 0x7b, 0x25, 0x03, 0x70, 0x09, 0x5e, 0xc0, 0xb6, 0x8b, 0x72, + 0x00, 0xb0, 0x8d, 0x83, 0xd5, 0x10, 0xbe, 0xda, 0x38, 0x42, 0xaf, 0x8b, 0xb1, 0xcc, 0x54, 0x16, + 0x62, 0x86, 0xff, 0x9c, 0x03, 0xbf, 0x8d, 0x9b, 0x1e, 0x1b, 0x3d, 0x1c, 0x6b, 0x78, 0xd7, 0x08, + 0xfc, 0xc7, 0x24, 0xdb, 0xc6, 0xff, 0xed, 0x11, 0xa7, 0x86, 0x14, 0x47, 0x0e, 0x5e, 0xf6, 0x96, + 0xb1, 0x79, 0x03, 0x4a, 0x0b, 0x53, 0xfd, 0x93, 0x47, 0x1b, 0x6c, 0x25, 0x6a, 0x90, 0xf4, 0x21, + 0x00, 0x7b, 0xd6, 0x96, 0xbe, 0x73, 0x3c, 0xe2, 0xd4, 0xe2, 0xf6, 0xd2, 0x23, 0xe9, 0x0d, 0xb2, + 0x4a, 0x2f, 0xe6, 0x1f, 0x3d, 0x89, 0x42, 0x79, 0x62, 0xbf, 0x02, 0xf0, 0x86, 0x0c, 0xd7, 0x3b, + 0x04, 0x95, 0x44, 0x18, 0x5c, 0xe4, 0x6d, 0x7c, 0x8d, 0xb4, 0x16, 0x55, 0xa3, 0xf3, 0xa9, 0xf7, + 0x4b, 0x54, 0x20, 0x3d, 0x28, 0x39, 0x97, 0x24, 0x5c, 0x00, 0x99, 0xdc, 0x7f, 0xe7, 0x92, 0x82, + 0x5b, 0x83, 0x3b, 0x81, 0x8e, 0xe3, 0x4e, 0x2d, 0x7c, 0x03, 0x1e, 0xb2, 0xcd, 0x7b, 0x39, 0x22, + 0x8d, 0x58, 0xfe, 0xba, 0xc5, 0xa2, 0xe3, 0x6c, 0xc3, 0xf2, 0x72, 0x3a, 0x30, 0xbc, 0xba, 0x69, + 0x06, 0xf9, 0x46, 0x21, 0xa1, 0x80, 0x83, 0x62, 0xaa, 0xa0, 0xd8, 0x8b, 0xe7, 0x27, 0x28, 0xd5, + 0xd7, 0xec, 0xae, 0x15, 0xfd, 0x92, 0x63, 0x64, 0x1c, 0x04, 0xab, 0x23, 0x1b, 0x77, 0xf1, 0x5e, + 0x34, 0x93, 0x01, 0xc9, 0x06, 0xdb, 0xcf, 0x98, 0xc4, 0x39, 0x31, 0xbf, 0x70, 0xde, 0x69, 0x47, + 0x6f, 0x75, 0xe0, 0x81, 0x0a, 0xea, 0x89, 0x26, 0x4d, 0xc8, 0x3a, 0xc0, 0xf0, 0xbc, 0x06, 0xd7, + 0xfb, 0xbc, 0xc7, 0x97, 0x7e, 0x4b, 0xfb, 0xcd, 0x8b, 0xfe, 0xee, 0xdb, 0xd5, 0x54, 0xbe, 0x5d, + 0x05, 0xf3, 0x8f, 0x7e, 0x8e, 0xe9, 0x78, 0x1f, 0xcf, 0xfa, 0xae, 0x21, 0x91, 0x15, 0x80, 0x17, + 0x78, 0x55, 0xc4, 0x21, 0x44, 0xdd, 0xbf, 0xaf, 0x6f, 0xd2, 0x11, 0xcb, 0x9a, 0x8a, 0x34, 0xa5, + 0xf0, 0x2d, 0x51, 0x42, 0x31, 0xce, 0xf2, 0x8e, 0xa4, 0x12, 0x16, 0x71, 0xb6, 0x8a, 0x49, 0x54, + 0x6b, 0x0e, 0x79, 0xa6, 0x8f, 0xf2, 0xe3, 0x00, 0x3b, 0x4b, 0xaf, 0xcf, 0x02, 0x41, 0xeb, 0xf0, + 0x75, 0xd1, 0xb7, 0xb8, 0x92, 0x86, 0xfd, 0x25, 0x08, 0x7c, 0xb3, 0xdf, 0x8b, 0x5e, 0x66, 0xbd, + 0x1c, 0x91, 0x46, 0xac, 0x5d, 0xbd, 0xe3, 0xaa, 0x15, 0xf1, 0xb7, 0x04, 0xff, 0x31, 0x65, 0xbb, + 0x81, 0x4d, 0x9b, 0x9d, 0x0d, 0x02, 0xfc, 0xc2, 0x91, 0x70, 0x0b, 0x70, 0x1d, 0xa4, 0x0a, 0xa2, + 0xba, 0x60, 0x58, 0x81, 0x5d, 0x13, 0xd1, 0xbe, 0xe7, 0x81, 0x9a, 0x4c, 0xed, 0x81, 0x72, 0x22, + 0xaa, 0xa9, 0x66, 0xc6, 0x12, 0x51, 0x58, 0x72, 0x5a, 0x13, 0xde, 0xcc, 0x18, 0xab, 0x75, 0x67, + 0xbe, 0x28, 0x7c, 0x9e, 0x88, 0x2d, 0x42, 0xc8, 0xe2, 0xe0, 0x61, 0x62, 0xe0, 0x84, 0x69, 0xef, + 0xdf, 0x35, 0x5a, 0xf9, 0xfd, 0x19, 0x17, 0x4d, 0xf2, 0x37, 0x29, 0xfc, 0xe0, 0x39, 0x17, 0x6f, + 0xab, 0xff, 0x8d, 0x8a, 0x2d, 0xa2, 0xd6, 0x55, 0x37, 0x8c, 0xa1, 0x43, 0xb9, 0x9f, 0x90, 0x8d, + 0x7c, 0x1c, 0xb7, 0xa5, 0x5e, 0x10, 0xdf, 0xab, 0x75, 0x49, 0xc1, 0xc9, 0xd4, 0xcf, 0xaf, 0x63, + 0x24, 0x99, 0xab, 0x07, 0xef, 0x51, 0x9b, 0xdb, 0xfa, 0xc2, 0xe3, 0x5f, 0xa6, 0x19, 0x21, 0x83, + 0x04, 0xed, 0x00, 0x92, 0x47, 0xa6, 0xd5, 0xc0, 0x7f, 0x9c, 0x30, 0x13, 0x78, 0x16, 0x44, 0x40, + 0xaa, 0xae, 0xef, 0xf0, 0x4b, 0x13, 0x46, 0x1f, 0x05, 0x64, 0x11, 0xd3, 0x13, 0xdb, 0x66, 0x86, + 0x30, 0x31, 0x77, 0x4d, 0xf0, 0x32, 0x88, 0xa4, 0x8d, 0x9c, 0xf7, 0xef, 0x57, 0x80, 0xc3, 0x84, + 0x27, 0x34, 0x9c, 0x1b, 0x6d, 0x9f, 0x98, 0x11, 0xf8, 0x63, 0xc9, 0xfd, 0x2c, 0x29, 0xf1, 0xa8, + 0x4e, 0x19, 0x5c, 0x41, 0x00, 0xa5, 0x17, 0xcc, 0x0c, 0x91, 0x5a, 0xbe, 0x93, 0x06, 0x84, 0xdb, + 0x0c, 0xa3, 0x35, 0x4f, 0xdc, 0xc5, 0x35, 0xa7, 0x61, 0x45, 0xf7, 0x84, 0xd3, 0xe1, 0xae, 0x6c, + 0x02, 0x55, 0x94, 0x30, 0x79, 0xc3, 0xaa, 0x87, 0xab, 0x6e, 0xf8, 0x70, 0x80, 0x7b, 0xea, 0xa3, + 0xd7, 0xb5, 0x92, 0x2c, 0xd5, 0x83, 0xac, 0x68, 0xb6, 0x03, 0xfe, 0xb2, 0x5c, 0xd7, 0xa1, 0x85, + 0x04, 0x04, 0x54, 0x42, 0x1a, 0xc7, 0x80, 0x76, 0xaa, 0x7b, 0x30, 0x08, 0xcb, 0x0c, 0x87, 0x08, + 0x2e, 0x19, 0x10, 0x60, 0x05, 0x53, 0xd9, 0xa9, 0xe0, 0xc8, 0xb4, 0x49, 0x51, 0xc5, 0xd0, 0x8e, + 0x33, 0x8c, 0xc0, 0x9c, 0x70, 0x02, 0x9b, 0x58, 0xdb, 0xf0, 0x43, 0x99, 0x75, 0x39, 0xb6, 0x78, + 0x99, 0xd1, 0xe3, 0xe9, 0x8f, 0x80, 0x92, 0x12, 0x9b, 0xa0, 0xc1, 0x60, 0xdf, 0x01, 0x64, 0x7c, + 0x1f, 0x07, 0xc4, 0x3e, 0xe3, 0xbb, 0x00, 0x58, 0x0f, 0x00, 0x49, 0x3b, 0x41, 0x44, 0xa4, 0x54, + 0x88, 0xd5, 0xd7, 0xad, 0xcd, 0x0c, 0x2a, 0x26, 0x50, 0xaa, 0x8b, 0x0b, 0xf2, 0xbb, 0x16, 0x74, + 0xcb, 0x6a, 0x37, 0xe0, 0x72, 0x2d, 0x23, 0x11, 0xb2, 0xae, 0x28, 0xa4, 0x30, 0xc0, 0x18, 0x0b, + 0xda, 0x55, 0x63, 0x69, 0xc5, 0x95, 0xce, 0xa2, 0x7d, 0xc8, 0xb5, 0x2a, 0x75, 0xad, 0x00, 0x49, + 0x89, 0x6e, 0x0e, 0x4a, 0x38, 0xe6, 0x24, 0x88, 0xab, 0x07, 0xef, 0x51, 0xb5, 0x5f, 0x80, 0xec, + 0x87, 0xdb, 0x87, 0x0a, 0xb6, 0xcf, 0xc0, 0x20, 0x1c, 0x10, 0x87, 0x01, 0x18, 0x05, 0xd3, 0x8c, + 0x02, 0x6c, 0x43, 0x0c, 0x18, 0x32, 0x3f, 0xb7, 0x45, 0xb6, 0x24, 0xb8, 0xe1, 0x61, 0x1b, 0x73, + 0xd7, 0x3c, 0x67, 0x95, 0x57, 0x69, 0x3c, 0x39, 0x5e, 0x3a, 0xc6, 0x44, 0x28, 0xc4, 0x95, 0x0e, + 0x5d, 0x33, 0x85, 0x43, 0x8d, 0xd8, 0xc6, 0xf1, 0x2d, 0x51, 0x31, 0x3c, 0x7b, 0xdb, 0xb6, 0xb5, + 0x94, 0xe6, 0xbd, 0x75, 0xd6, 0x05, 0x2a, 0xf4, 0xfa, 0x6b, 0xbc, 0x0a, 0xa7, 0xda, 0x54, 0xe4, + 0x6b, 0xc9, 0x72, 0x71, 0x8b, 0x54, 0xa3, 0x93, 0x79, 0x0a, 0x7d, 0xd5, 0x81, 0x9f, 0xc2, 0x82, + 0x77, 0x1f, 0xd8, 0xe5, 0x5f, 0xd2, 0xee, 0xbe, 0x84, 0x19, 0x10, 0x04, 0xfc, 0x6c, 0x71, 0xa6, + 0xc2, 0x2d, 0xd3, 0xed, 0x1c, 0xbd, 0x9e, 0xc6, 0x97, 0x82, 0xb8, 0x10, 0xb7, 0xae, 0x9a, 0xc0, + 0x65, 0x3e, 0x6f, 0x6d, 0xce, 0x81, 0xc6, 0xbc, 0x96, 0xfc, 0x67, 0xd2, 0xfc, 0x0d, 0x95, 0x9e, + 0x05, 0xbd, 0xfb, 0xe6, 0x74, 0x3a, 0x56, 0x36, 0xec, 0x0f, 0x69, 0xca, 0xcf, 0xcd, 0xa9, 0xef, + 0xb3, 0x86, 0x63, 0xb3, 0xed, 0xde, 0x0b, 0x66, 0x5f, 0x3f, 0xaf, 0x80, 0x7f, 0xce, 0x92, 0x4d, + 0xaf, 0x83, 0x11, 0xf6, 0x83, 0xac, 0x4f, 0xc1, 0x49, 0x2b, 0x16, 0x40, 0x8f, 0x0b, 0xcb, 0xec, + 0x6e, 0xfc, 0xe4, 0x8c, 0xcf, 0xe0, 0x94, 0xd3, 0xac, 0x1c, 0xcd, 0x46, 0x58, 0x0e, 0xa6, 0x44, + 0x47, 0x00, 0xb6, 0x94, 0x9f, 0xa7, 0x25, 0x9a, 0xb8, 0x6b, 0xd6, 0x93, 0x5b, 0xb5, 0xb7, 0x59, + 0xf0, 0x2c, 0x2c, 0x90, 0x61, 0x4a, 0xbd, 0x52, 0xea, 0x19, 0x97, 0x0a, 0x91, 0xb1, 0x75, 0x05, + 0xd4, 0xdd, 0x3d, 0x64, 0x5b, 0x50, 0x70, 0x7c, 0xb7, 0x45, 0x01, 0xdf, 0x37, 0x83, 0x1a, 0xdf, + 0xc0, 0x76, 0x81, 0x2b, 0xc1, 0x63, 0x62, 0x5b, 0x35, 0xb1, 0x89, 0xa7, 0xf2, 0x17, 0xc3, 0xd7, + 0x16, 0xf8, 0x7c, 0x61, 0x96, 0x4b, 0x86, 0x93, 0x7c, 0x0d, 0x1c, 0x6c, 0x3e, 0x87, 0x27, 0xb5, + 0x09, 0xb0, 0xf7, 0x2c, 0x88, 0xef, 0x50, 0xc9, 0x4a, 0x36, 0xc6, 0x47, 0xfa, 0xcb, 0xa6, 0x3f, + 0x70, 0xc6, 0xbf, 0xe8, 0xed, 0xae, 0x38, 0x0d, 0xa4, 0xe0, 0x4a, 0xcb, 0xe1, 0x6a, 0xa3, 0x0d, + 0x78, 0x5a, 0x93, 0x43, 0x6a, 0x96, 0x52, 0xad, 0x23, 0x80, 0x5d, 0x10, 0x5d, 0x34, 0xa5, 0x6c, + 0xb5, 0xca, 0xcb, 0x7c, 0x23, 0xbf, 0x04, 0xa9, 0xde, 0xe0, 0x72, 0xbc, 0x32, 0x5b, 0xc0, 0x09, + 0x4b, 0x46, 0x42, 0x0e, 0x94, 0x93, 0x8c, 0x9d, 0x9e, 0xa3, 0xdf, 0xa7, 0xf8, 0x7a, 0xc5, 0x79, + 0x39, 0x53, 0xe9, 0x42, 0xe9, 0x56, 0xff, 0xcf, 0x48, 0x5a, 0x8a, 0xc0, 0x31, 0x79, 0x95, 0x36, + 0xf4, 0x3e, 0x4e, 0xdb, 0x6b, 0x3f, 0x42, 0x22, 0xd3, 0x73, 0xc0, 0xdd, 0x31, 0x4b, 0xad, 0x87, + 0x6f, 0xf0, 0xdf, 0x3c, 0xff, 0xb5, 0xb0, 0xdc, 0x3d, 0xe5, 0x36, 0x05, 0x07, 0xf7, 0xf2, 0x7a, + 0x08, 0x8b, 0xc6, 0xfd, 0x89, 0x50, 0xa6, 0xbd, 0xfc, 0x27, 0x5b, 0x2c, 0x0b, 0xec, 0x25, 0x35, + 0x49, 0x83, 0x1a, 0xc0, 0x11, 0x9a, 0x34, 0x4e, 0x12, 0x3c, 0x88, 0x87, 0xf3, 0xde, 0x60, 0x1e, + 0xbe, 0x38, 0xf5, 0x8c, 0x24, 0x9a, 0x37, 0x07, 0xc7, 0xf7, 0x94, 0xa9, 0x17, 0x3d, 0xac, 0x77, + 0xbc, 0x88, 0x78, 0x16, 0x3d, 0x43, 0x9a, 0xf6, 0x72, 0x51, 0x1a, 0xb0, 0x95, 0xfb, 0xae, 0x03, + 0xc0, 0x26, 0x91, 0x60, 0x5d, 0x40, 0xd3, 0x61, 0xaa, 0x21, 0x01, 0x18, 0xd5, 0x86, 0xb5, 0x78, + 0x01, 0x4c, 0xf8, 0x4f, 0x52, 0xbd, 0xe8, 0x7d, 0x99, 0x03, 0x7d, 0x8d, 0xce, 0x1a, 0x7d, 0xce, + 0xc2, 0xb2, 0xfb, 0x02, 0xfc, 0x67, 0xbf, 0x1b, 0xaa, 0x8c, 0x35, 0x26, 0x15, 0x2d, 0x84, 0x96, + 0xc4, 0xac, 0xf6, 0x5b, 0xc0, 0x03, 0x77, 0x6f, 0xc2, 0xe4, 0xbd, 0xe1, 0x25, 0xa6, 0x84, 0xaf, + 0x20, 0xab, 0x54, 0xf1, 0x71, 0x05, 0x83, 0x91, 0x98, 0x81, 0x7b, 0x77, 0xaa, 0xbd, 0x2e, 0xae, + 0x3f, 0xd6, 0x27, 0x2e, 0xcd, 0x3d, 0x61, 0x19, 0xb1, 0x24, 0xc0, 0x2f, 0xbd, 0x2b, 0x36, 0x05, + 0xd7, 0x06, 0x3d, 0xcf, 0x27, 0x8e, 0x50, 0x82, 0x16, 0xa6, 0xc0, 0x09, 0xa3, 0x9b, 0x8f, 0x4b, + 0x4b, 0x6c, 0x56, 0x0e, 0x97, 0xb3, 0xf5, 0x3a, 0xe0, 0x38, 0xcf, 0x72, 0x93, 0x28, 0xa3, 0x85, + 0x6b, 0x5f, 0xfe, 0x61, 0x10, 0x77, 0xb0, 0x15, 0x92, 0x4c, 0x00, 0x86, 0x92, 0xc3, 0x09, 0x68, + 0xb9, 0xc8, 0xc5, 0x78, 0x01, 0xcb, 0x9e, 0x2f, 0xf4, 0xf3, 0xd8, 0x41, 0xcd, 0x88, 0xd6, 0x8b, + 0xae, 0x5b, 0x1e, 0x54, 0x7e, 0xf1, 0xec, 0xa0, 0xef, 0x75, 0xd6, 0xd9, 0xce, 0x1f, 0x6c, 0x8e, + 0xe6, 0x5a, 0x15, 0x46, 0x1e, 0x88, 0x2e, 0x2b, 0xbb, 0xb9, 0xb4, 0xbf, 0x4f, 0x63, 0xf9, 0x5e, + 0x8e, 0xcd, 0xa3, 0xfd, 0x11, 0x53, 0x2c, 0xf3, 0x54, 0x57, 0x3a, 0xf7, 0x88, 0x18, 0x57, 0x11, + 0x9d, 0x89, 0xb5, 0xff, 0xca, 0xb1, 0x66, 0x22, 0xbe, 0x24, 0x78, 0x13, 0x69, 0x47, 0x16, 0xa3, + 0xc0, 0x09, 0x6e, 0x9f, 0x0f, 0xed, 0x5f, 0x2f, 0xab, 0x9c, 0x95, 0xaa, 0x6f, 0xea, 0x00, 0xcf, + 0x55, 0xde, 0xbd, 0x8f, 0xc9, 0x18, 0xc7, 0xc1, 0x41, 0x9b, 0xb0, 0x72, 0x35, 0x1a, 0x0f, 0xb9, + 0x19, 0x11, 0x97, 0x85, 0x07, 0xa5, 0xe1, 0xc0, 0xb2, 0xb3, 0x0c, 0x74, 0x2f, 0x33, 0xb0, 0xc0, + 0xe9, 0xe2, 0x48, 0xe0, 0x42, 0xb0, 0x23, 0xe5, 0x58, 0x93, 0x60, 0x17, 0x30, 0xa8, 0x13, 0x2f, + 0x3c, 0x48, 0x18, 0x3b, 0xb1, 0x1b, 0x16, 0xc1, 0x57, 0xd6, 0xed, 0xd5, 0x75, 0x78, 0x01, 0xc1, + 0x23, 0x2f, 0x8e, 0xf3, 0x91, 0xcd, 0x72, 0x5e, 0x12, 0x53, 0x8d, 0xfa, 0xc0, 0x7d, 0xc5, 0x93, + 0x3f, 0xcf, 0xff, 0xf1, 0xf5, 0xc3, 0x23, 0xeb, 0x2e, 0x4b, 0x92, 0x9a, 0xe5, 0xb8, 0x2f, 0x6c, + 0xc3, 0x47, 0x07, 0xec, 0x83, 0x0d, 0xff, 0x9d, 0xa9, 0x4d, 0x09, 0x9d, 0x75, 0xb4, 0x24, 0x16, + 0xaf, 0x00, 0xe7, 0xc6, 0x1a, 0x8f, 0xaf, 0x64, 0x47, 0xa9, 0x50, 0xbf, 0x08, 0xfb, 0x2c, 0xf8, + 0x77, 0xff, 0x14, 0x6d, 0x58, 0xe0, 0x74, 0xb3, 0x02, 0x50, 0xa6, 0x0c, 0xaa, 0xb1, 0x0b, 0x2b, + 0x1d, 0x15, 0xa0, 0x4d, 0xfc, 0x88, 0xb7, 0x0f, 0x57, 0xf4, 0x92, 0xcc, 0x50, 0x3e, 0x35, 0x09, + 0x0a, 0x18, 0xab, 0xcc, 0xf8, 0x72, 0xfd, 0xf7, 0x9f, 0x1b, 0xdd, 0x5d, 0x2b, 0xcb, 0xcd, 0xbd, + 0xb7, 0x1d, 0x96, 0xdd, 0xb0, 0xd3, 0xdb, 0x64, 0xbe, 0x33, 0xa8, 0x92, 0x01, 0x0f, 0x2c, 0xe5, + 0x85, 0xd2, 0x79, 0x9d, 0x23, 0xfe, 0xf8, 0xda, 0x1d, 0x34, 0xd6, 0x95, 0x7f, 0x93, 0x4d, 0x7e, + 0xec, 0x4f, 0x03, 0xfe, 0xc1, 0x9d, 0xc0, 0x54, 0xe0, 0xca, 0xea, 0x95, 0x4d, 0x34, 0x82, 0x8c, + 0x94, 0xd7, 0x87, 0xd2, 0x24, 0x5a, 0x1f, 0x00, 0x9e, 0xd8, 0x85, 0xe3, 0xf3, 0xe0, 0x1c, 0xfb, + 0x9b, 0xf0, 0x63, 0xb8, 0x64, 0x45, 0x70, 0x32, 0x3d, 0x69, 0x9a, 0xf6, 0x72, 0x59, 0x1a, 0xb0, + 0x3a, 0xb5, 0xfb, 0x60, 0x38, 0x05, 0x0e, 0x52, 0x17, 0x3f, 0xfc, 0x09, 0x14, 0xee, 0x18, 0x04, + 0xe3, 0x76, 0x13, 0xad, 0x09, 0x5c, 0x2c, 0x2c, 0x5c, 0xed, 0x5d, 0xd6, 0xae, 0xe0, 0xfb, 0x2d, + 0x18, 0x47, 0x11, 0xb2, 0x15, 0x74, 0x67, 0x94, 0x05, 0x83, 0x9f, 0xe3, 0xd5, 0x53, 0x55, 0x9d, + 0x72, 0xbe, 0xf0, 0x2f, 0x69, 0x48, 0x6f, 0x63, 0xf9, 0x5e, 0xf2, 0xc0, 0xb0, 0xee, 0x6f, 0x66, + 0x51, 0x53, 0xbb, 0x4d, 0x28, 0xb6, 0x11, 0x36, 0x16, 0xc8, 0xea, 0xfd, 0x5e, 0xba, 0xda, 0x1e, + 0x76, 0x99, 0xd0, 0x65, 0x54, 0xea, 0xc0, 0x72, 0x6e, 0x77, 0xe7, 0xee, 0xdb, 0xd7, 0xfb, 0x9b, + 0x4d, 0xda, 0x37, 0x30, 0x6e, 0xf8, 0xc5, 0xef, 0x60, 0x13, 0x0e, 0x18, 0x58, 0x9c, 0x79, 0xda, + 0xd0, 0xc2, 0xa3, 0x2c, 0x66, 0xb2, 0x8b, 0xa3, 0x73, 0x3b, 0x35, 0x5a, 0xb1, 0x39, 0x09, 0x26, + 0x0d, 0xc0, 0x24, 0x89, 0xaf, 0x00, 0x53, 0xc4, 0xaa, 0x5c, 0x5e, 0xdd, 0x03, 0xb4, 0xf5, 0xfa, + 0x91, 0x66, 0xac, 0x1c, 0xad, 0x46, 0x38, 0x0e, 0x80, 0xa8, 0xba, 0x74, 0xf5, 0xf5, 0x60, 0xbf, + 0xfe, 0x7f, 0x0d, 0xb7, 0xad, 0x38, 0x3d, 0xb4, 0x23, 0xa3, 0xbc, 0x63, 0xc2, 0x08, 0xde, 0x9a, + 0x00, 0x88, 0xbf, 0x83, 0xe6, 0xec, 0xfc, 0x57, 0xf0, 0xff, 0xd5, 0xb9, 0x07, 0x3d, 0x42, 0x4d, + 0x82, 0xcb, 0xf9, 0x3d, 0xc8, 0x70, 0xea, 0xb5, 0x50, 0xb3, 0x11, 0x7c, 0x82, 0x3f, 0xfe, 0x57, + 0xf0, 0x0e, 0xf3, 0x8a, 0xbd, 0x9f, 0x93, 0xb4, 0x96, 0xe5, 0xf1, 0x97, 0x96, 0x0c, 0xd1, 0x79, + 0xdf, 0xf9, 0x6f, 0xc0, 0xd5, 0x54, 0x81, 0xbb, 0x97, 0x5f, 0xfb, 0xb6, 0x2e, 0x15, 0xb1, 0x7c, + 0x5b, 0x8b, 0x4b, 0xa3, 0x9a, 0x30, 0xf5, 0x82, 0x1a, 0x7d, 0x1c, 0x16, 0x6c, 0x08, 0xf2, 0x5c, + 0x17, 0x52, 0xbe, 0xff, 0x52, 0x3e, 0xf7, 0xbb, 0x25, 0x80, 0x5c, 0x46, 0x89, 0xe5, 0xc7, 0x62, + 0x74, 0xea, 0xdc, 0xa0, 0xda, 0xa0, 0x3f, 0x08, 0xdf, 0xcf, 0xaf, 0x9b, 0xdf, 0x78, 0x91, 0x6b, + 0x7c, 0x00, 0x77, 0x74, 0xb0, 0x3e, 0x0c, 0xd4, 0x6c, 0xaf, 0xdd, 0xed, 0xc7, 0x71, 0xa7, 0x16, + 0x3f, 0x33, 0x8f, 0x5a, 0xc7, 0xbd, 0x47, 0x76, 0x07, 0x24, 0x11, 0xd7, 0xa6, 0x9a, 0x56, 0x69, + 0x3b, 0x12, 0xb0, 0xe0, 0xd0, 0x12, 0x23, 0xc4, 0x8c, 0x32, 0xc4, 0x3a, 0x00, 0xba, 0x6e, 0xc3, + 0x89, 0x7c, 0x96, 0x48, 0x0a, 0x08, 0x24, 0x30, 0x8d, 0x2c, 0x0b, 0x56, 0xe6, 0xd5, 0x55, 0x39, + 0x5e, 0xf5, 0xed, 0x3c, 0xc0, 0x5b, 0x59, 0x0b, 0xe9, 0xbe, 0x2e, 0xb9, 0xb5, 0x7e, 0xff, 0xbd, + 0x6f, 0x80, 0x1e, 0xc4, 0xe4, 0x1b, 0xaf, 0xfc, 0x55, 0x1f, 0x19, 0x7d, 0x4c, 0x4f, 0xcc, 0xbc, + 0xee, 0xb2, 0x67, 0x02, 0xe9, 0x2f, 0x1b, 0xe4, 0xfa, 0x1b, 0xc5, 0xfc, 0x9a, 0x2e, 0x41, 0x82, + 0xc6, 0xfe, 0x01, 0x8f, 0x4b, 0x1b, 0x79, 0x1b, 0x2b, 0x85, 0xe9, 0x5f, 0x92, 0xe1, 0xba, 0x97, + 0xae, 0x9b, 0x68, 0x7c, 0xd2, 0x4c, 0x8d, 0xdc, 0xb2, 0xc0, 0x46, 0xd2, 0xe1, 0x5e, 0x6b, 0xf3, + 0x79, 0x7a, 0xf3, 0xcb, 0x3c, 0x5d, 0x23, 0xb0, 0x93, 0x47, 0x39, 0xfd, 0x4e, 0x1a, 0xe5, 0x95, + 0xc1, 0x47, 0x5f, 0xf9, 0xed, 0x0b, 0xdc, 0xcc, 0x75, 0x2a, 0x6a, 0x06, 0x00, 0x91, 0x19, 0x8c, + 0x56, 0xfd, 0x00, 0x93, 0x18, 0xfc, 0xd2, 0x30, 0xbd, 0xa2, 0xa2, 0xa6, 0xd8, 0x96, 0x78, 0xf1, + 0x8e, 0x7c, 0x46, 0xc8, 0x79, 0xf8, 0xda, 0xcb, 0x3b, 0x02, 0x6d, 0x5e, 0x01, 0x32, 0xf4, 0x86, + 0x7f, 0xe6, 0x7c, 0xfd, 0xcf, 0x08, 0x0f, 0x09, 0x6f, 0x03, 0xfa, 0x11, 0x96, 0x7b, 0x73, 0x75, + 0x90, 0xdc, 0xfe, 0x5e, 0x62, 0x61, 0x67, 0x1b, 0xc8, 0x8c, 0xb9, 0x32, 0x98, 0x4d, 0x9a, 0xd4, + 0xa0, 0x39, 0x5b, 0xd6, 0x0b, 0x56, 0x56, 0xcf, 0x55, 0x43, 0x2f, 0x6f, 0xb8, 0xf6, 0x83, 0xff, + 0xe7, 0x62, 0x7b, 0x73, 0xf9, 0x4e, 0xea, 0xec, 0x3e, 0x1e, 0xa6, 0xbf, 0xbf, 0x87, 0xdb, 0x1a, + 0xdb, 0xca, 0xb4, 0xc7, 0xa5, 0x30, 0x2b, 0xcd, 0x79, 0xef, 0x9b, 0x58, 0xdc, 0x30, 0xf8, 0x70, + 0x8e, 0x2f, 0x42, 0xde, 0x83, 0x44, 0x25, 0xf7, 0xdd, 0x55, 0x3e, 0x12, 0x55, 0x4e, 0x15, 0x5f, + 0x37, 0x91, 0xe9, 0xe3, 0x1f, 0x20, 0xda, 0x51, 0xcb, 0xff, 0xfd, 0x6d, 0x72, 0xfc, 0x3f, 0x5b, + 0x0f, 0xc7, 0x70, 0xcd, 0x84, 0xd9, 0x8e, 0x36, 0x92, 0x1d, 0x4a, 0x61, 0x70, 0x9b, 0x9b, 0x68, + 0x9b, 0x27, 0xc6, 0x99, 0x10, 0xa3, 0xce, 0x53, 0x21, 0xf4, 0x31, 0x3c, 0x87, 0x84, 0x69, 0x81, + 0x9d, 0xa5, 0xef, 0x0f, 0xf1, 0xca, 0xae, 0xf0, 0x63, 0x1c, 0x84, 0x5b, 0x8f, 0x77, 0xe5, 0x6b, + 0x11, 0xec, 0x26, 0x59, 0x9f, 0x16, 0x89, 0xe1, 0xc2, 0xad, 0x56, 0xb8, 0x2e, 0x31, 0x68, 0x5d, + 0x2a, 0x83, 0xf6, 0xef, 0xfe, 0x61, 0x76, 0x9d, 0x60, 0x2a, 0x87, 0xc5, 0x85, 0xff, 0x22, 0xe4, + 0x4b, 0x08, 0x48, 0x13, 0x8a, 0x00, 0x3b, 0x48, 0xc9, 0x21, 0x06, 0xc1, 0xbb, 0xd9, 0xd1, 0x3d, + 0x52, 0x49, 0x6a, 0x44, 0x93, 0x09, 0xc8, 0x8b, 0x8a, 0x5e, 0x7e, 0x8d, 0x0b, 0x26, 0xc3, 0x59, + 0xbb, 0xad, 0x92, 0xaa, 0x46, 0x0b, 0xbe, 0xd6, 0xaa, 0xa0, 0x36, 0x5f, 0x34, 0x6a, 0x88, 0x84, + 0x0a, 0xfc, 0xa8, 0xb8, 0xbd, 0xb4, 0xa4, 0xf4, 0xc6, 0xe1, 0x88, 0x55, 0x5d, 0x4e, 0xc9, 0xdc, + 0x7d, 0xa2, 0x71, 0x0d, 0x18, 0x1e, 0x16, 0x1d, 0x17, 0x11, 0xb3, 0x91, 0x92, 0xf0, 0xc6, 0x1b, + 0x3a, 0x54, 0x80, 0x8f, 0x11, 0x71, 0x9b, 0x02, 0x16, 0xf4, 0x8f, 0xf0, 0x37, 0xf2, 0xea, 0xa8, + 0x95, 0x61, 0x8e, 0x8f, 0x22, 0x22, 0xc0, 0xef, 0x51, 0x0b, 0x18, 0x8d, 0xd8, 0xe8, 0x0b, 0x76, + 0x3e, 0xa5, 0x28, 0xc3, 0x6f, 0xde, 0xf1, 0xf4, 0x55, 0x7d, 0x48, 0xfd, 0xe3, 0xa5, 0x64, 0x45, + 0x70, 0x3c, 0xbe, 0x0f, 0x06, 0x22, 0xc8, 0x95, 0x89, 0x0a, 0x56, 0xd6, 0x8a, 0x36, 0x42, 0xbe, + 0xe6, 0x20, 0x00, 0x5c, 0xaa, 0xc6, 0x2e, 0xea, 0x19, 0x11, 0xfc, 0x07, 0x38, 0x08, 0x30, 0x7f, + 0xb1, 0xe7, 0xfd, 0x71, 0x1c, 0x37, 0xc1, 0xd1, 0x4c, 0x3f, 0x16, 0x4c, 0x4d, 0x06, 0x69, 0xca, + 0x3b, 0x8b, 0x79, 0xfd, 0x05, 0x0c, 0x8c, 0x60, 0xee, 0xaa, 0x91, 0x16, 0xf1, 0xab, 0x8a, 0x55, + 0xf1, 0xb2, 0x85, 0xfc, 0xa7, 0xde, 0x6f, 0x32, 0xd3, 0x1f, 0xfd, 0xc2, 0x0d, 0xc7, 0x74, 0x4d, + 0xcb, 0xae, 0x77, 0x96, 0xd3, 0xf3, 0x0a, 0xa0, 0x97, 0xd1, 0xf5, 0x54, 0xb8, 0xba, 0x45, 0x63, + 0x1f, 0xe4, 0xef, 0x80, 0x63, 0xd4, 0xef, 0xef, 0x74, 0x56, 0x93, 0xf3, 0xfe, 0x60, 0x97, 0xcb, + 0xf5, 0x55, 0x89, 0x0b, 0x92, 0x4e, 0xb5, 0x22, 0xe3, 0x0e, 0xc4, 0x5e, 0xc9, 0x0b, 0xc8, 0x52, + 0xc1, 0x43, 0x0a, 0x6e, 0x6e, 0xee, 0xe9, 0xd2, 0x58, 0xe1, 0x08, 0xd5, 0x93, 0xdd, 0x96, 0x57, + 0x8b, 0x06, 0x2f, 0xb4, 0x3b, 0x0a, 0x00, 0x5f, 0x9d, 0x55, 0x14, 0x57, 0xfe, 0xee, 0xbe, 0x19, + 0x61, 0xa5, 0x63, 0x81, 0x6a, 0xf0, 0x1e, 0xf8, 0xdc, 0xa0, 0xf4, 0x98, 0xaa, 0x0b, 0x40, 0xfa, + 0xea, 0xb1, 0xb7, 0xce, 0xb7, 0x80, 0x3b, 0x56, 0x09, 0xb6, 0xa5, 0x92, 0x84, 0x0b, 0x01, 0xca, + 0x51, 0x86, 0xad, 0xd5, 0x1a, 0xc5, 0x9f, 0xba, 0x2b, 0xc5, 0x30, 0x66, 0xd2, 0xbc, 0xa5, 0xb1, + 0xcd, 0x3a, 0x73, 0x29, 0x49, 0x47, 0x58, 0xe3, 0xb7, 0x55, 0xeb, 0x15, 0x7f, 0x0d, 0xf8, 0x12, + 0x55, 0x02, 0x92, 0x3a, 0xe7, 0x60, 0xdf, 0xfa, 0x3a, 0x3a, 0x01, 0x35, 0xf0, 0x06, 0xe5, 0x85, + 0xdd, 0x21, 0xef, 0xe7, 0x84, 0xda, 0x08, 0x22, 0xa2, 0xab, 0x83, 0x92, 0x25, 0x15, 0x8b, 0x00, + 0x4f, 0x6a, 0xc9, 0x01, 0xa4, 0xc1, 0x62, 0xf6, 0x07, 0x24, 0x19, 0xbb, 0x5d, 0xe0, 0x36, 0x69, + 0xf5, 0x6a, 0xef, 0x22, 0x73, 0xc7, 0xb6, 0xc2, 0x03, 0xc9, 0x53, 0x9f, 0x4d, 0xd5, 0x1e, 0x03, + 0x00, 0xd9, 0xa4, 0x44, 0x44, 0x45, 0x15, 0x2e, 0xdb, 0x91, 0x09, 0xf0, 0xd8, 0x30, 0x0b, 0x08, + 0x29, 0x55, 0x1e, 0xd2, 0x58, 0xa1, 0xed, 0xda, 0xe0, 0x64, 0xec, 0x83, 0x2b, 0x5b, 0x30, 0x7a, + 0x90, 0xb0, 0x5f, 0x1a, 0x55, 0x38, 0xa6, 0x5d, 0x6f, 0xaa, 0x4e, 0x03, 0x6e, 0x00, 0x0b, 0xc6, + 0x35, 0x5d, 0x37, 0x23, 0xea, 0xa9, 0x81, 0xa2, 0x52, 0x0e, 0x9c, 0xd5, 0x7e, 0xaa, 0x98, 0x05, + 0x5c, 0x0c, 0xf3, 0x41, 0x07, 0x3e, 0x81, 0x6b, 0xf1, 0x7f, 0x50, 0xef, 0x9b, 0x8f, 0xc2, 0xf0, + 0x97, 0xaf, 0xbf, 0x00, 0x52, 0x02, 0x57, 0x59, 0xfc, 0x75, 0x0c, 0x90, 0x1a, 0x47, 0x17, 0x0e, + 0xf0, 0x0d, 0x3f, 0x6e, 0x97, 0x80, 0x99, 0x60, 0x37, 0x6a, 0xcc, 0xe9, 0xd9, 0x11, 0x09, 0xc4, + 0x09, 0x60, 0x00, 0x25, 0x06, 0x2f, 0xb6, 0x8e, 0xaf, 0x68, 0xa9, 0x29, 0xab, 0xa9, 0xaa, 0xa7, + 0x45, 0xcb, 0x0c, 0xe4, 0x85, 0xbc, 0x27, 0xd5, 0xa6, 0x00, 0x72, 0xa5, 0xc0, 0x72, 0xf1, 0xef, + 0x71, 0x42, 0x0c, 0x1b, 0x02, 0xdb, 0x44, 0x40, 0x0b, 0xde, 0x15, 0x7c, 0x43, 0xdf, 0xe2, 0x52, + 0x0b, 0x2c, 0x9f, 0xf5, 0x24, 0xab, 0xc4, 0x69, 0xaa, 0xa2, 0xe0, 0x39, 0xe0, 0x2e, 0x11, 0x2e, + 0x11, 0x39, 0x0f, 0x38, 0x5e, 0x6f, 0x2a, 0x70, 0xa3, 0x5e, 0x9c, 0xdc, 0xe8, 0xd5, 0x63, 0x6b, + 0x70, 0x05, 0xf0, 0x27, 0xbd, 0x70, 0x0d, 0x4b, 0xf5, 0x54, 0xe0, 0x79, 0x11, 0x4a, 0x16, 0xb9, + 0x03, 0x2f, 0x58, 0xf5, 0x39, 0xbc, 0xa9, 0xc9, 0xff, 0xc5, 0x94, 0x6a, 0xa8, 0x51, 0x05, 0x0b, + 0x82, 0xfe, 0xa0, 0xd7, 0xd5, 0xc4, 0xae, 0xb7, 0x45, 0xfc, 0x4a, 0x2f, 0x07, 0x72, 0x87, 0x70, + 0x1c, 0x88, 0x97, 0x8d, 0x59, 0x30, 0x3d, 0xfa, 0x80, 0xd3, 0xd8, 0x09, 0xaa, 0x8f, 0xbc, 0x03, + 0xfc, 0x0a, 0xaf, 0x00, 0x70, 0x16, 0xa4, 0xec, 0xaa, 0xa3, 0xe3, 0x76, 0xd2, 0x85, 0x11, 0xef, + 0x91, 0x91, 0x1c, 0xc0, 0x67, 0xbc, 0x50, 0xf8, 0x67, 0xbc, 0x32, 0x78, 0xf5, 0xde, 0xed, 0x27, + 0x67, 0x01, 0x12, 0xf0, 0x46, 0x7d, 0x49, 0xe3, 0x75, 0xfc, 0x3a, 0x54, 0xe2, 0xf0, 0xe6, 0x71, + 0xbf, 0x55, 0x14, 0xaa, 0x28, 0xe0, 0xec, 0x65, 0x5e, 0x7f, 0x16, 0x44, 0x97, 0x02, 0x92, 0x13, + 0x34, 0x9f, 0x4b, 0xc4, 0xd3, 0x78, 0xe5, 0x6b, 0x99, 0x29, 0xa3, 0xa4, 0xad, 0xd1, 0xe5, 0x39, + 0xc9, 0x07, 0x00, 0x24, 0x65, 0x03, 0x6b, 0x27, 0xb7, 0xb1, 0x48, 0xbf, 0x56, 0x0e, 0xe6, 0xa3, + 0xb9, 0x46, 0x42, 0x18, 0xe8, 0xfa, 0x07, 0x80, 0x87, 0xee, 0xc8, 0x88, 0xc0, 0x21, 0x89, 0x8f, + 0x58, 0xe9, 0x08, 0xb4, 0x6b, 0xbc, 0x79, 0xac, 0x56, 0x5f, 0xa6, 0x4b, 0x89, 0x6f, 0x88, 0xe2, + 0xc0, 0x69, 0x93, 0x51, 0x1f, 0x63, 0xda, 0xaa, 0xe7, 0x8b, 0xfe, 0xf4, 0xf6, 0x92, 0x34, 0xb5, + 0x55, 0x0a, 0x41, 0x5d, 0x8c, 0x48, 0x2d, 0x22, 0x64, 0x44, 0xf0, 0x22, 0xc0, 0x77, 0x44, 0x67, + 0xb4, 0xc4, 0x2b, 0x97, 0x85, 0xe0, 0x4b, 0xe1, 0x77, 0x0e, 0x83, 0x04, 0x74, 0xe8, 0x6d, 0x4c, + 0xf0, 0xcb, 0x84, 0xe9, 0x60, 0x4b, 0x21, 0x6a, 0x73, 0xa6, 0x49, 0x1d, 0x2c, 0x22, 0x72, 0xae, + 0xd7, 0xb8, 0xc5, 0xf8, 0x22, 0x00, 0x76, 0xf2, 0x85, 0x7c, 0x8e, 0x30, 0x92, 0x1f, 0x02, 0xe1, + 0x12, 0x83, 0xc5, 0x80, 0xc1, 0xc4, 0x0d, 0x53, 0x5b, 0xd8, 0xe8, 0x95, 0x4d, 0x24, 0x81, 0xc0, + 0x2a, 0x34, 0x24, 0x6a, 0x47, 0x7f, 0xf3, 0x8c, 0x15, 0x6d, 0xda, 0xf8, 0x1d, 0xe1, 0x6d, 0x1c, + 0x00, 0xb8, 0x4c, 0x74, 0xfa, 0xd1, 0xaa, 0x35, 0xce, 0xba, 0x51, 0xa8, 0xc3, 0x87, 0x22, 0x10, + 0x21, 0xf2, 0xbc, 0xbc, 0xa6, 0x8d, 0x6f, 0x6d, 0x74, 0x66, 0xb8, 0x64, 0xe6, 0x7f, 0xe3, 0x50, + 0xef, 0x59, 0x02, 0x31, 0xeb, 0x0c, 0x69, 0x2a, 0x5a, 0xe3, 0x73, 0xe4, 0x8d, 0xfe, 0xcd, 0xab, + 0x00, 0x4b, 0x46, 0x06, 0x0d, 0xbf, 0x57, 0xb2, 0xe2, 0x4d, 0x69, 0xb0, 0xda, 0x69, 0x22, 0x17, + 0xde, 0x65, 0xdf, 0xe4, 0x3b, 0x98, 0x18, 0x6b, 0xda, 0xd3, 0x25, 0xbd, 0x36, 0x1c, 0xa5, 0x56, + 0x8e, 0x2c, 0x60, 0x17, 0x38, 0x70, 0x3b, 0xf6, 0x64, 0x44, 0x5e, 0x34, 0xd8, 0xe3, 0x6d, 0x74, + 0xd8, 0xcd, 0xe1, 0x16, 0x31, 0xc2, 0x5c, 0x2d, 0xb6, 0x34, 0xb7, 0x95, 0x6e, 0x8e, 0x2e, 0x31, + 0x02, 0xac, 0x20, 0x9e, 0x05, 0x45, 0x86, 0x01, 0x10, 0x54, 0xe3, 0x91, 0xad, 0xf0, 0x9e, 0x3d, + 0x96, 0xda, 0x7d, 0xe1, 0x25, 0x06, 0x45, 0x00, 0x48, 0xe4, 0xcd, 0xe0, 0xec, 0x06, 0x4e, 0x2d, + 0x92, 0x74, 0xa1, 0x26, 0x5e, 0xe9, 0x26, 0x8a, 0x30, 0x21, 0x34, 0x47, 0x44, 0x67, 0xea, 0xd6, + 0x59, 0xa4, 0x39, 0xb5, 0x87, 0x22, 0x3f, 0x63, 0x52, 0xe3, 0xda, 0xbc, 0x80, 0x01, 0x45, 0x17, + 0xdd, 0x32, 0xcd, 0x89, 0x8c, 0x22, 0xc2, 0xc8, 0x16, 0xa8, 0x44, 0x61, 0x18, 0x56, 0x15, 0x99, + 0x66, 0x46, 0x64, 0x61, 0x18, 0x56, 0x22, 0xd5, 0xce, 0x8c, 0xe7, 0x0a, 0xcc, 0x8e, 0xba, 0xf2, + 0x2d, 0x91, 0xd7, 0xd6, 0x37, 0xc7, 0xbb, 0xe1, 0xc9, 0x3d, 0xf1, 0xbf, 0x93, 0x8f, 0x28, 0xdb, + 0xf4, 0x28, 0x32, 0x0e, 0xa2, 0xb3, 0x35, 0x5f, 0x02, 0x4e, 0xda, 0xb0, 0xf7, 0xe8, 0xcb, 0x50, + 0x9a, 0xf6, 0xe7, 0x1a, 0xe3, 0x80, 0x4f, 0x29, 0xb5, 0x04, 0xfd, 0x8a, 0x95, 0x8c, 0x09, 0xa4, + 0x0e, 0x58, 0x13, 0x47, 0xa8, 0x13, 0x34, 0xec, 0xf6, 0x7f, 0x2b, 0x30, 0xf2, 0xe2, 0xca, 0xcd, + 0x44, 0xff, 0x75, 0xeb, 0xfd, 0xa7, 0x8f, 0x6e, 0x6f, 0xf4, 0x6e, 0x7c, 0x0f, 0x75, 0x5b, 0x9f, + 0x97, 0x5d, 0xc6, 0x38, 0xaa, 0xe3, 0x2d, 0x5a, 0x22, 0x8f, 0xcc, 0xb0, 0x82, 0xcc, 0xa6, 0xbd, + 0x00, 0xe6, 0xcf, 0x2a, 0x50, 0xd0, 0x0e, 0xca, 0x4d, 0xb8, 0x27, 0xec, 0x86, 0x8c, 0xe4, 0xaa, + 0xc1, 0xec, 0xd9, 0x7e, 0x87, 0x4d, 0x51, 0xa3, 0x1d, 0x94, 0x88, 0xf0, 0x30, 0xac, 0x20, 0xfb, + 0xbe, 0x76, 0xf2, 0xbb, 0x1f, 0xca, 0x0e, 0x83, 0x13, 0x78, 0x6c, 0xf0, 0x97, 0x4a, 0x62, 0x53, + 0xd0, 0x21, 0xa5, 0xc8, 0x5f, 0x3b, 0xe0, 0x1a, 0x17, 0xdf, 0x9b, 0x01, 0x68, 0x1b, 0x6a, 0xcf, + 0xa6, 0x41, 0xe3, 0x46, 0x70, 0x1c, 0xfb, 0x3c, 0xb0, 0x1c, 0x3b, 0xe5, 0x7c, 0xf4, 0x21, 0x77, + 0x62, 0x2c, 0x2a, 0x5d, 0xee, 0xd2, 0xec, 0x70, 0x88, 0x57, 0x11, 0xc8, 0x42, 0xf8, 0x67, 0x8e, + 0xc8, 0x8c, 0xd4, 0x1e, 0xce, 0x0d, 0xdb, 0x03, 0xf4, 0x73, 0x43, 0x05, 0xbf, 0xad, 0xa1, 0x4c, + 0x6e, 0xa4, 0x55, 0x00, 0x0b, 0x5d, 0x07, 0xbc, 0x19, 0x6f, 0x3d, 0x4f, 0x80, 0xe4, 0x53, 0x7f, + 0xdf, 0xd5, 0xc2, 0x0f, 0xc5, 0xde, 0xfa, 0x78, 0xf5, 0xd6, 0xfb, 0xda, 0xaa, 0xba, 0xae, 0xfe, + 0xf3, 0xce, 0x8a, 0xa4, 0x41, 0x0b, 0xe3, 0xc7, 0x01, 0x1b, 0x35, 0xdb, 0x83, 0xfc, 0x91, 0x4d, + 0xf2, 0xe7, 0x76, 0x0f, 0xc0, 0xaf, 0x68, 0xe1, 0x81, 0x18, 0xb3, 0xf0, 0x02, 0x8d, 0xcd, 0x36, + 0x01, 0x4a, 0x94, 0xe2, 0x7e, 0xaa, 0xc2, 0x91, 0x44, 0xa5, 0xd5, 0x50, 0x45, 0x87, 0xf5, 0xe4, + 0x3b, 0xc0, 0xd9, 0x81, 0x1b, 0x24, 0x29, 0x98, 0xd4, 0x9e, 0x7f, 0xe7, 0xea, 0xa9, 0x83, 0xab, + 0xf7, 0xb4, 0x88, 0x98, 0x87, 0x9c, 0xf6, 0xb7, 0x35, 0xf8, 0xd8, 0x35, 0x4b, 0xfb, 0xde, 0xd1, + 0x81, 0xc6, 0x6e, 0x0e, 0x9c, 0x76, 0x8e, 0x03, 0x95, 0x27, 0x74, 0x10, 0xc2, 0x9f, 0x82, 0x8f, + 0x4d, 0x75, 0x78, 0xf3, 0x91, 0xeb, 0x8e, 0x1d, 0x91, 0xfd, 0x80, 0x80, 0x39, 0xc9, 0xb1, 0x07, + 0xbe, 0x03, 0x63, 0x65, 0x58, 0x5f, 0x74, 0x2e, 0x4d, 0x35, 0xe6, 0xb4, 0x55, 0x5c, 0x62, 0xe7, + 0x3e, 0x71, 0x55, 0x5a, 0x03, 0xe7, 0x6d, 0xf3, 0x6b, 0xd9, 0x0e, 0x56, 0x88, 0xe6, 0x35, 0x50, + 0x09, 0x52, 0xdf, 0x41, 0xad, 0xbb, 0xad, 0x9f, 0xf4, 0xc1, 0x07, 0xfa, 0x84, 0x0f, 0x9e, 0x5e, + 0xa0, 0xe3, 0x3c, 0xa5, 0x6e, 0x02, 0x6f, 0x11, 0xc1, 0x35, 0xc8, 0xc1, 0xce, 0xe2, 0x5f, 0x3c, + 0x13, 0x58, 0xac, 0xa9, 0xc5, 0xf9, 0xc3, 0xb4, 0x8e, 0xcf, 0x71, 0xf4, 0x38, 0x0e, 0xeb, 0x1e, + 0xe8, 0x21, 0x39, 0xad, 0x9a, 0xe5, 0x36, 0x68, 0x9d, 0xe0, 0xac, 0x76, 0x1c, 0x3b, 0xa5, 0x8d, + 0xfe, 0xb8, 0x84, 0xf7, 0xf6, 0xd2, 0xc3, 0x89, 0x0c, 0xb2, 0x1c, 0xcb, 0x9a, 0x40, 0x83, 0x98, + 0x97, 0xe4, 0xfa, 0xdb, 0x77, 0x39, 0x17, 0x69, 0xc1, 0x9d, 0x4d, 0xaa, 0x54, 0x7f, 0xff, 0xd5, + 0xe9, 0x4a, 0x1c, 0x92, 0x84, 0x4d, 0x41, 0x0b, 0xb3, 0xc7, 0x19, 0x6f, 0xa5, 0x92, 0xe3, 0x46, + 0x15, 0x8a, 0x6c, 0xdd, 0x66, 0x08, 0xbc, 0x68, 0xa7, 0x80, 0xea, 0xf3, 0xec, 0x70, 0x9c, 0x8f, + 0x6f, 0x89, 0x5f, 0x05, 0x42, 0xd2, 0xfb, 0xfb, 0x0b, 0x3b, 0xf3, 0x3b, 0xb2, 0xdc, 0x8f, 0xb1, + 0x6e, 0x1f, 0xe8, 0x89, 0xef, 0xc2, 0xfe, 0x02, 0x85, 0x3e, 0x72, 0x4b, 0x21, 0x34, 0x01, 0xce, + 0x63, 0xc7, 0x04, 0x6d, 0x33, 0xbc, 0x7c, 0xa7, 0x55, 0x7a, 0x0f, 0x0f, 0x45, 0x04, 0xf3, 0xa5, + 0xe5, 0xc0, 0xfb, 0x2e, 0xb1, 0xd9, 0x73, 0x1f, 0x7f, 0x96, 0x40, 0xe3, 0xc0, 0x76, 0xf6, 0xaf, + 0x2b, 0x2b, 0x0a, 0x7b, 0x7f, 0xde, 0x05, 0xbd, 0xbf, 0x7e, 0x17, 0x23, 0x25, 0x2e, 0x62, 0x5b, + 0xf0, 0x27, 0x55, 0x4e, 0xc5, 0x7d, 0x25, 0xf2, 0x9a, 0x3a, 0xe7, 0x14, 0xe3, 0x80, 0xea, 0x31, + 0x0b, 0x80, 0x8b, 0xc0, 0x05, 0x5d, 0xdd, 0x1e, 0xe3, 0x93, 0x6b, 0xcb, 0x79, 0x7b, 0x3f, 0xb5, + 0x06, 0x52, 0x2b, 0xe6, 0x3a, 0xaa, 0xf1, 0xaf, 0x1d, 0x96, 0x6b, 0x0e, 0x52, 0x04, 0xd7, 0x7e, + 0x69, 0xf7, 0x2b, 0x58, 0x69, 0xb6, 0xf5, 0x50, 0xbc, 0x7a, 0x85, 0x79, 0x42, 0x9c, 0x95, 0x5e, + 0x60, 0x5c, 0x72, 0x1b, 0xe4, 0xaa, 0xb6, 0xf1, 0xe6, 0x77, 0xec, 0xb4, 0x5f, 0x6c, 0xd1, 0xe9, + 0x28, 0xb0, 0x25, 0x40, 0xc2, 0x5e, 0xbe, 0x73, 0x06, 0xcb, 0xd2, 0x3c, 0xd0, 0x71, 0x75, 0x20, + 0xb9, 0xaa, 0x90, 0x1d, 0xc8, 0x88, 0xe0, 0x4a, 0xf0, 0x48, 0x07, 0x8a, 0x1c, 0xcc, 0x41, 0x92, + 0x27, 0xd9, 0xa9, 0xd0, 0x2f, 0x78, 0x64, 0x6d, 0x79, 0x5f, 0x06, 0xbf, 0xa1, 0xf6, 0xe3, 0xc1, + 0xf7, 0xd6, 0xb8, 0x69, 0xac, 0x1e, 0xdf, 0x9f, 0x17, 0x00, 0xd1, 0xb9, 0x63, 0xaf, 0x46, 0x7f, + 0x2e, 0x44, 0x7e, 0x01, 0xf6, 0x96, 0x34, 0x75, 0xdd, 0x01, 0xf7, 0xc0, 0x2f, 0x80, 0x15, 0x14, + 0x1c, 0xe4, 0x32, 0x8e, 0x01, 0xce, 0x20, 0xc7, 0x5f, 0x22, 0x3e, 0x02, 0xd9, 0x6f, 0x01, 0x6c, + 0x18, 0x4f, 0x5a, 0xfe, 0x6d, 0x55, 0xa1, 0xce, 0xc3, 0x82, 0x48, 0x31, 0xe4, 0x44, 0xe0, 0x28, + 0x3c, 0x19, 0xc0, 0x39, 0x91, 0x83, 0xc0, 0xe5, 0x81, 0x77, 0xa8, 0x1f, 0xf6, 0xea, 0x2f, 0xb4, + 0xd1, 0xc6, 0x38, 0x9c, 0x1c, 0x07, 0x88, 0x83, 0x07, 0x4c, 0xc6, 0x9e, 0xe3, 0x5e, 0x08, 0xb1, + 0x98, 0xf8, 0x3e, 0xaa, 0xc9, 0x6e, 0xc3, 0x9e, 0x1c, 0x61, 0x99, 0x11, 0xdc, 0x01, 0x1c, 0x07, + 0xf6, 0x97, 0x18, 0xb5, 0x13, 0xa2, 0xe9, 0x58, 0xef, 0xfb, 0xe2, 0xe5, 0x97, 0xff, 0xea, 0xa9, + 0x34, 0xca, 0xcc, 0x2f, 0x7f, 0xb1, 0x1c, 0xe0, 0x05, 0x70, 0x64, 0x44, 0xf0, 0x0c, 0xe0, 0x19, + 0xc0, 0x63, 0x2a, 0x10, 0x9c, 0x44, 0xdc, 0x6b, 0xaa, 0x57, 0xbc, 0xef, 0x78, 0x00, 0x39, 0xc4, + 0xc7, 0x5e, 0x8a, 0x81, 0x8c, 0x88, 0xde, 0x00, 0x8c, 0x06, 0xbe, 0xc3, 0xc9, 0xae, 0x51, 0x60, + 0xd3, 0x8a, 0x78, 0xc0, 0x0e, 0x25, 0xff, 0xfd, 0xdb, 0x8f, 0x09, 0xd7, 0x52, 0xac, 0x4d, 0xd5, + 0x5c, 0xa9, 0xe3, 0xad, 0xc1, 0x80, 0x60, 0x39, 0xc3, 0x87, 0x70, 0xe1, 0x52, 0x38, 0x15, 0xc0, + 0x70, 0xe2, 0x1c, 0x38, 0x87, 0x0e, 0x05, 0x23, 0x03, 0x9c, 0x38, 0x76, 0x0e, 0x1c, 0x23, 0x87, + 0x9c, 0x05, 0x76, 0x03, 0x1c, 0x38, 0x87, 0x0e, 0x05, 0x23, 0x03, 0x9c, 0x38, 0x76, 0x0e, 0x1c, + 0x23, 0x87, 0x9c, 0x05, 0x76, 0x03, 0x1c, 0x38, 0x87, 0x0e, 0x05, 0x23, 0x03, 0x9c, 0x38, 0x76, + 0x0e, 0x1c, 0x23, 0x87, 0x9c, 0x05, 0x76, 0x03, 0x1c, 0x38, 0x87, 0x0e, 0x05, 0x23, 0x03, 0x9c, + 0x38, 0x76, 0x0e, 0x1c, 0x23, 0x87, 0x9c, 0x05, 0x76, 0x03, 0x1c, 0x38, 0x87, 0x0e, 0x05, 0x23, + 0x03, 0x9c, 0x38, 0x76, 0x0e, 0x1c, 0x23, 0x87, 0x9c, 0x05, 0x76, 0x03, 0x1c, 0x38, 0x87, 0x0e, + 0x05, 0x23, 0x03, 0x9c, 0x38, 0x76, 0x0e, 0x1c, 0x23, 0x87, 0x9c, 0x05, 0x76, 0x03, 0x1c, 0x38, + 0x87, 0x0e, 0x05, 0x23, 0x0f, 0xfe, 0xf6, 0xa7, 0xbf, 0x78, 0xfb, 0x2d, 0xfb, 0xc5, 0x00, 0x00, + 0x00, 0x00, 0x45, 0x49, 0x44, 0x4e, 0x42, 0xae, 0x82, 0x60, + ], + disp: function() + { + // Do Nothing + + throw "Does Nothing!"; + } + +}; + +try +{ + LOGO.disp(); +} +catch(e) +{ + alert("Error: " + e + "\n"); +} + diff --git a/jetty-servlets/src/test/resources/big_script.js.sha1 b/jetty-servlets/src/test/resources/big_script.js.sha1 new file mode 100644 index 00000000000..5ac71c8b8b3 --- /dev/null +++ b/jetty-servlets/src/test/resources/big_script.js.sha1 @@ -0,0 +1 @@ +77634b336741a98d2ef79484281245b12e9db939 big_script.js diff --git a/jetty-servlets/src/test/resources/small_script.js b/jetty-servlets/src/test/resources/small_script.js new file mode 100644 index 00000000000..207de2be410 --- /dev/null +++ b/jetty-servlets/src/test/resources/small_script.js @@ -0,0 +1,29 @@ +//---------------------------------------------------------------------- +// +// Silly / Pointless Javascript to test GZIP compression. +// +//---------------------------------------------------------------------- + +var LOGO = { + dat: [ + 0x50, 0x89, 0x47, 0x4e, 0x0a, 0x0d, 0x0a, 0x1a, 0x00, 0x00, 0x0d, 0x00, 0x48, 0x49, 0x52, 0x44, + 0x00, 0x00, 0x45, 0x49, 0x44, 0x4e, 0x42, 0xae, 0x82, 0x60, + ], + disp: function() + { + // Do Nothing + + throw "Does Nothing!"; + } + +}; + +try +{ + LOGO.disp(); +} +catch(e) +{ + alert("Error: " + e + "\n"); +} + diff --git a/jetty-servlets/src/test/resources/small_script.js.sha1 b/jetty-servlets/src/test/resources/small_script.js.sha1 new file mode 100644 index 00000000000..ca4c476094a --- /dev/null +++ b/jetty-servlets/src/test/resources/small_script.js.sha1 @@ -0,0 +1 @@ +b8455ea37194b938cfc1773a73ecee2c994fa98e small_script.js diff --git a/jetty-start/pom.xml b/jetty-start/pom.xml index 223482fb80e..9fcfa2c2cc8 100644 --- a/jetty-start/pom.xml +++ b/jetty-start/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 jetty-start diff --git a/jetty-util/pom.xml b/jetty-util/pom.xml index ba57b47dd8d..9e5dc45445a 100644 --- a/jetty-util/pom.xml +++ b/jetty-util/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 jetty-util @@ -29,7 +29,7 @@ - + + + org.slf4j + slf4j-jdk14 + ${slf4j-version} + test + diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/DateCache.java b/jetty-util/src/main/java/org/eclipse/jetty/util/DateCache.java index 6fbe1cebe1a..98e29502940 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/DateCache.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/DateCache.java @@ -104,7 +104,7 @@ public class DateCache /** Set the timezone. * @param tz TimeZone */ - public void setTimeZone(TimeZone tz) + public synchronized void setTimeZone(TimeZone tz) { setTzFormatString(tz); if( _locale != null ) @@ -145,7 +145,7 @@ public class DateCache } /* ------------------------------------------------------------ */ - private void setTzFormatString(final TimeZone tz ) + private synchronized void setTzFormatString(final TimeZone tz ) { int zIndex = _formatString.indexOf( "ZZZ" ); if( zIndex >= 0 ) diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java index 06976d45832..91e4a03196d 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java @@ -330,7 +330,7 @@ public class StringUtil /* ------------------------------------------------------------ */ public static boolean isUTF8(String charset) { - return charset==__UTF8||__UTF8.equalsIgnoreCase(charset)||__UTF8Alt.equalsIgnoreCase(charset); + return __UTF8.equalsIgnoreCase(charset)||__UTF8Alt.equalsIgnoreCase(charset); } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/UrlEncoded.java b/jetty-util/src/main/java/org/eclipse/jetty/util/UrlEncoded.java index b040de5900b..e0ed56cbaf7 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/UrlEncoded.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/UrlEncoded.java @@ -43,7 +43,7 @@ import org.eclipse.jetty.util.log.Logger; * * @see java.net.URLEncoder */ -public class UrlEncoded extends MultiMap +public class UrlEncoded extends MultiMap implements Cloneable { private static final Logger LOG = Log.getLogger(UrlEncoded.class); diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8Appendable.java b/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8Appendable.java index 306b6bff125..8fe840c1b88 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8Appendable.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8Appendable.java @@ -134,7 +134,7 @@ public abstract class Utf8Appendable protected void appendByte(byte b) throws IOException { - if (b > 0 && isUtf8SequenceComplete()) + if (b > 0 && _state == UTF8_ACCEPT) { _appendable.append((char)(b & 0xFF)); } @@ -142,41 +142,48 @@ public abstract class Utf8Appendable { int i = b & 0xFF; int type = BYTE_TABLE[i]; - _codep = isUtf8SequenceComplete() ? (0xFF >> type) & i : (i & 0x3F) | (_codep << 6); - _state = TRANS_TABLE[_state + type]; + _codep = _state == UTF8_ACCEPT ? (0xFF >> type) & i : (i & 0x3F) | (_codep << 6); + int next = TRANS_TABLE[_state + type]; - if (isUtf8SequenceComplete()) + switch(next) { - if (_codep < Character.MIN_HIGH_SURROGATE) - { - _appendable.append((char)_codep); - } - else - { - for (char c : Character.toChars(_codep)) - _appendable.append(c); - } - } - else if (_state == UTF8_REJECT) - { - _codep=0; - _state = UTF8_ACCEPT; - _appendable.append(REPLACEMENT); - throw new NotUtf8Exception(); + case UTF8_ACCEPT: + _state=next; + if (_codep < Character.MIN_HIGH_SURROGATE) + { + _appendable.append((char)_codep); + } + else + { + for (char c : Character.toChars(_codep)) + _appendable.append(c); + } + break; + + case UTF8_REJECT: + String reason = "byte "+TypeUtil.toHexString(b)+" in state "+(_state/12); + _codep=0; + _state = UTF8_ACCEPT; + _appendable.append(REPLACEMENT); + throw new NotUtf8Exception(reason); + + default: + _state=next; + } } } - protected boolean isUtf8SequenceComplete() + public boolean isUtf8SequenceComplete() { return _state == UTF8_ACCEPT; } public static class NotUtf8Exception extends IllegalArgumentException { - public NotUtf8Exception() + public NotUtf8Exception(String reason) { - super("Not valid UTF8!"); + super("Not valid UTF8! "+reason); } } } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ajax/JSON.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ajax/JSON.java index 360a836d3d5..84824567afa 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/ajax/JSON.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ajax/JSON.java @@ -373,7 +373,7 @@ public class JSON @Deprecated public void appendJSON(final StringBuffer buffer, Convertible converter) { - appendJSON((StringBuffer)buffer,converter); + appendJSON((Appendable)buffer,converter); } /* ------------------------------------------------------------ */ diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/component/AbstractLifeCycle.java b/jetty-util/src/main/java/org/eclipse/jetty/util/component/AbstractLifeCycle.java index dab77534ae2..b0ef41ce86c 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/component/AbstractLifeCycle.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/component/AbstractLifeCycle.java @@ -188,7 +188,7 @@ public abstract class AbstractLifeCycle implements LifeCycle private void setStopped() { _state = __STOPPED; - LOG.debug(STOPPED+" {}",this); + LOG.debug("{} {}",STOPPED,this); for (Listener listener : _listeners) listener.lifeCycleStopped(this); } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/component/AggregateLifeCycle.java b/jetty-util/src/main/java/org/eclipse/jetty/util/component/AggregateLifeCycle.java index bc9205bdf8c..8e14c84b7d0 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/component/AggregateLifeCycle.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/component/AggregateLifeCycle.java @@ -6,8 +6,6 @@ import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; -import java.util.Queue; -import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CopyOnWriteArrayList; import org.eclipse.jetty.util.log.Log; @@ -144,7 +142,7 @@ public class AggregateLifeCycle extends AbstractLifeCycle implements Destroyable t=(T)o; } } - if (count>1) + if (count>1 && LOG.isDebugEnabled()) LOG.debug("getBean({}) 1 of {}",clazz.getName(),count); return t; diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/log/JavaUtilLog.java b/jetty-util/src/main/java/org/eclipse/jetty/util/log/JavaUtilLog.java index 94d8e795dc1..789bbf77511 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/log/JavaUtilLog.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/log/JavaUtilLog.java @@ -24,17 +24,13 @@ import java.util.logging.Level; *

    * *

    - * Honors the standard jetty system property "org.eclipse.jetty.util.log.DEBUG" to set logger into debug - * mode (defaults to false, set to "true" to enable) - *

    - * - *

    * You can also set the logger level using - * standard java.util.logging configuration against the name "org.eclipse.jetty.util.log". + * standard java.util.logging configuration. *

    */ public class JavaUtilLog implements Logger { + private Level configuredLevel; private java.util.logging.Logger _logger; public JavaUtilLog() @@ -45,8 +41,11 @@ public class JavaUtilLog implements Logger public JavaUtilLog(String name) { _logger = java.util.logging.Logger.getLogger(name); - if (Boolean.getBoolean("org.eclipse.jetty.util.log.DEBUG")) + if (Boolean.parseBoolean(Log.__props.getProperty("org.eclipse.jetty.util.log.DEBUG", "false"))) + { _logger.setLevel(Level.FINE); + } + configuredLevel = _logger.getLevel(); } public String getName() @@ -91,7 +90,15 @@ public class JavaUtilLog implements Logger public void setDebugEnabled(boolean enabled) { - _logger.setLevel(Level.FINE); + if (enabled) + { + configuredLevel = _logger.getLevel(); + _logger.setLevel(Level.FINE); + } + else + { + _logger.setLevel(configuredLevel); + } } public void debug(String msg, Object... args) diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/log/Log.java b/jetty-util/src/main/java/org/eclipse/jetty/util/log/Log.java index 95bafcec4d8..74f8e5fa66d 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/log/Log.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/log/Log.java @@ -13,10 +13,16 @@ package org.eclipse.jetty.util.log; +import java.io.IOException; +import java.io.InputStream; import java.lang.reflect.Method; +import java.net.URL; import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.Enumeration; +import java.util.Properties; +import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.Loader; /** @@ -40,73 +46,130 @@ public class Log public final static String EXCEPTION= "EXCEPTION "; public final static String IGNORED= "IGNORED "; + /** + * Logging Configuration Properties + */ + protected static Properties __props; + /** + * The {@link Logger} implementation class name + */ public static String __logClass; + /** + * Legacy flag indicating if {@link Log#ignore(Throwable)} methods produce any output in the {@link Logger}s + */ public static boolean __ignored; static { + /* Instantiate a default configuration properties (empty) + */ + __props = new Properties(); + AccessController.doPrivileged(new PrivilegedAction() { public Object run() { - __logClass = System.getProperty("org.eclipse.jetty.util.log.class", "org.eclipse.jetty.util.log.Slf4jLog"); - __ignored = Boolean.parseBoolean(System.getProperty("org.eclipse.jetty.util.log.IGNORED", "false")); + /* First see if the jetty-logging.properties object exists in the classpath. + * This is an optional feature used by embedded mode use, and test cases to allow for early + * configuration of the Log class in situations where access to the System.properties are + * either too late or just impossible. + */ + URL testProps = Log.class.getClassLoader().getResource("jetty-logging.properties"); + if (testProps != null) + { + InputStream in = null; + try + { + in = testProps.openStream(); + __props.load(in); + } + catch (IOException e) + { + System.err.println("Unable to load " + testProps); + e.printStackTrace(System.err); + } + finally + { + IO.close(in); + } + } + + /* Now load the System.properties as-is into the __props, these values will override + * any key conflicts in __props. + */ + @SuppressWarnings("unchecked") + Enumeration systemKeyEnum = (Enumeration)System.getProperties().propertyNames(); + while (systemKeyEnum.hasMoreElements()) + { + String key = systemKeyEnum.nextElement(); + __props.setProperty(key,System.getProperty(key)); + } + + /* Now use the configuration properties to configure the Log statics + */ + __logClass = __props.getProperty("org.eclipse.jetty.util.log.class","org.eclipse.jetty.util.log.Slf4jLog"); + __ignored = Boolean.parseBoolean(__props.getProperty("org.eclipse.jetty.util.log.IGNORED","false")); return null; } }); } - private static Logger __log; + private static Logger LOG; private static boolean __initialized; public static boolean initialized() { - if (__log != null) + if (LOG != null) + { return true; + } synchronized (Log.class) { if (__initialized) - return __log != null; + { + return LOG != null; + } __initialized = true; } try { Class log_class = Loader.loadClass(Log.class, __logClass); - if (__log == null || !__log.getClass().equals(log_class)) + if (LOG == null || !LOG.getClass().equals(log_class)) { - __log = (Logger)log_class.newInstance(); - __log.debug("Logging to {} via {}", __log, log_class.getName()); + LOG = (Logger)log_class.newInstance(); + LOG.debug("Logging to {} via {}", LOG, log_class.getName()); } } catch(Throwable e) { - if (e instanceof ThreadDeath) - throw (ThreadDeath)e; - + // Unable to load specified Logger implementation, default to standard logging. initStandardLogging(e); } - return __log != null; + return LOG != null; } private static void initStandardLogging(Throwable e) { Class log_class; if(e != null && __ignored) + { e.printStackTrace(); - if (__log == null) + } + + if (LOG == null) { log_class = StdErrLog.class; - __log = new StdErrLog(); - __log.debug("Logging to {} via {}", __log, log_class.getName()); + LOG = new StdErrLog(); + LOG.debug("Logging to {} via {}", LOG, log_class.getName()); } } public static void setLog(Logger log) { - Log.__log = log; + Log.LOG = log; } /** @@ -116,16 +179,16 @@ public class Log public static Logger getLog() { initialized(); - return __log; + return LOG; } - + /** * Get the root logger. * @return the root logger */ public static Logger getRootLogger() { initialized(); - return __log; + return LOG; } static boolean isIgnored() @@ -179,7 +242,7 @@ public class Log { if (!isDebugEnabled()) return; - __log.debug(EXCEPTION, th); + LOG.debug(EXCEPTION, th); } /** @@ -190,7 +253,7 @@ public class Log { if (!initialized()) return; - __log.debug(msg); + LOG.debug(msg); } /** @@ -201,7 +264,7 @@ public class Log { if (!initialized()) return; - __log.debug(msg, arg); + LOG.debug(msg, arg); } /** @@ -212,7 +275,7 @@ public class Log { if (!initialized()) return; - __log.debug(msg, arg0, arg1); + LOG.debug(msg, arg0, arg1); } /** @@ -228,7 +291,7 @@ public class Log { if (!initialized()) return; - __log.ignore(thrown); + LOG.ignore(thrown); } /** @@ -239,7 +302,7 @@ public class Log { if (!initialized()) return; - __log.info(msg); + LOG.info(msg); } /** @@ -250,7 +313,7 @@ public class Log { if (!initialized()) return; - __log.info(msg, arg); + LOG.info(msg, arg); } /** @@ -261,7 +324,7 @@ public class Log { if (!initialized()) return; - __log.info(msg, arg0, arg1); + LOG.info(msg, arg0, arg1); } /** @@ -272,7 +335,7 @@ public class Log { if (!initialized()) return false; - return __log.isDebugEnabled(); + return LOG.isDebugEnabled(); } /** @@ -283,7 +346,7 @@ public class Log { if (!initialized()) return; - __log.warn(msg); + LOG.warn(msg); } /** @@ -294,7 +357,7 @@ public class Log { if (!initialized()) return; - __log.warn(msg, arg); + LOG.warn(msg, arg); } /** @@ -305,7 +368,7 @@ public class Log { if (!initialized()) return; - __log.warn(msg, arg0, arg1); + LOG.warn(msg, arg0, arg1); } /** @@ -316,7 +379,7 @@ public class Log { if (!initialized()) return; - __log.warn(msg, th); + LOG.warn(msg, th); } /** @@ -327,12 +390,12 @@ public class Log { if (!initialized()) return; - __log.warn(EXCEPTION, th); + LOG.warn(EXCEPTION, th); } - + /** * Obtain a named Logger based on the fully qualified class name. - * + * * @param clazz * the class to base the Logger name off of * @return the Logger with the given name @@ -352,6 +415,6 @@ public class Log if (!initialized()) return null; - return name == null ? __log : __log.getLogger(name); + return name == null ? LOG : LOG.getLogger(name); } } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/log/Slf4jLog.java b/jetty-util/src/main/java/org/eclipse/jetty/util/log/Slf4jLog.java index bdd688f5fb8..2b8e268d1a4 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/log/Slf4jLog.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/log/Slf4jLog.java @@ -25,7 +25,6 @@ public class Slf4jLog implements Logger public Slf4jLog() throws Exception { this("org.eclipse.jetty.util.log"); - } public Slf4jLog(String name) @@ -42,6 +41,7 @@ public class Slf4jLog implements Logger } org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger( name ); + // Fix LocationAwareLogger use to indicate FQCN of this class - // https://bugs.eclipse.org/bugs/show_bug.cgi?id=276670 if (logger instanceof org.slf4j.spi.LocationAwareLogger) diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/log/StdErrLog.java b/jetty-util/src/main/java/org/eclipse/jetty/util/log/StdErrLog.java index d2457c10f25..72192728e0d 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/log/StdErrLog.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/log/StdErrLog.java @@ -37,21 +37,26 @@ import org.eclipse.jetty.util.DateCache; */ public class StdErrLog implements Logger { + private static final String EOL = System.getProperty("line.separator"); private static DateCache _dateCache; + private static Properties __props = Log.__props; - private final static boolean __source = Boolean.parseBoolean(System.getProperty("org.eclipse.jetty.util.log.SOURCE", - System.getProperty("org.eclipse.jetty.util.log.stderr.SOURCE","false"))); - private final static boolean __long = Boolean.parseBoolean(System.getProperty("org.eclipse.jetty.util.log.stderr.LONG","false")); + private final static boolean __source = Boolean.parseBoolean(Log.__props.getProperty("org.eclipse.jetty.util.log.SOURCE", + Log.__props.getProperty("org.eclipse.jetty.util.log.stderr.SOURCE","false"))); + private final static boolean __long = Boolean.parseBoolean(Log.__props.getProperty("org.eclipse.jetty.util.log.stderr.LONG","false")); + /** + * Tracking for child loggers only. + */ private final static ConcurrentMap __loggers = new ConcurrentHashMap(); static { - String deprecatedProperites[] = + String deprecatedProperties[] = { "DEBUG", "org.eclipse.jetty.util.log.DEBUG", "org.eclipse.jetty.util.log.stderr.DEBUG" }; // Toss a message to users about deprecated system properties - for (String deprecatedProp : deprecatedProperites) + for (String deprecatedProp : deprecatedProperties) { if (System.getProperty(deprecatedProp) != null) { @@ -75,6 +80,8 @@ public class StdErrLog implements Logger public static final int LEVEL_WARN = 3; private int _level = LEVEL_INFO; + // Level that this Logger was configured as (remembered in special case of .setDebugEnabled()) + private int _configuredLevel; private PrintStream _stderr = System.err; private boolean _source = __source; // Print the long form names, otherwise use abbreviated @@ -92,13 +99,20 @@ public class StdErrLog implements Logger public StdErrLog(String name) { + this(name,__props); + } + + public StdErrLog(String name, Properties props) + { + __props = props; this._name = name == null?"":name; this._abbrevname = condensePackageString(this._name); - this._level = getLoggingLevel(System.getProperties(),this._name); + this._level = getLoggingLevel(props,this._name); + this._configuredLevel = this._level; try { - _source = Boolean.parseBoolean(System.getProperty(_name + ".SOURCE",Boolean.toString(_source))); + _source = Boolean.parseBoolean(props.getProperty(_name + ".SOURCE",Boolean.toString(_source))); } catch (AccessControlException ace) { @@ -109,7 +123,7 @@ public class StdErrLog implements Logger /** * Get the Logging Level for the provided log name. Using the FQCN first, then each package segment from longest to * shortest. - * + * * @param props * the properties to check * @param name @@ -126,34 +140,29 @@ public class StdErrLog implements Logger { String levelStr = props.getProperty(nameSegment + ".LEVEL"); // System.err.printf("[StdErrLog.CONFIG] Checking for property [%s.LEVEL] = %s%n",nameSegment,levelStr); - if (levelStr == null) + int level = getLevelId(nameSegment + ".LEVEL",levelStr); + if (level != (-1)) { - // Trim and try again. - int idx = nameSegment.lastIndexOf('.'); - if (idx >= 0) - { - nameSegment = nameSegment.substring(0,idx); - } - else - { - nameSegment = null; - } + return level; + } + + // Trim and try again. + int idx = nameSegment.lastIndexOf('.'); + if (idx >= 0) + { + nameSegment = nameSegment.substring(0,idx); } else { - int level = getLevelId(levelStr); - if (level != (-1)) - { - return level; - } + nameSegment = null; } } // Default Logging Level - return getLevelId(props.getProperty("log.LEVEL", "INFO")); + return getLevelId("log.LEVEL",props.getProperty("log.LEVEL","INFO")); } - - protected static int getLevelId(String levelName) + + protected static int getLevelId(String levelSegment, String levelName) { if (levelName == null) { @@ -177,7 +186,7 @@ public class StdErrLog implements Logger return LEVEL_WARN; } - System.err.println("Unknown StdErrLog level [" + levelStr + "], expecting only [ALL, DEBUG, INFO, WARN] as values."); + System.err.println("Unknown StdErrLog level [" + levelSegment + "]=[" + levelStr + "], expecting only [ALL, DEBUG, INFO, WARN] as values."); return -1; } @@ -185,13 +194,13 @@ public class StdErrLog implements Logger * Condenses a classname by stripping down the package name to just the first character of each package name * segment.Configured *

    - * + * *

          * Examples:
          * "org.eclipse.jetty.test.FooTest"           = "oejt.FooTest"
          * "org.eclipse.jetty.server.logging.LogTest" = "orjsl.LogTest"
          * 
    - * + * * @param classname * the fully qualified class name * @return the condensed name @@ -240,7 +249,7 @@ public class StdErrLog implements Logger /* ------------------------------------------------------------ */ /** * Is the source of a log, logged - * + * * @return true if the class, method, file and line number of a log is logged. */ public boolean isSource() @@ -251,7 +260,7 @@ public class StdErrLog implements Logger /* ------------------------------------------------------------ */ /** * Set if a log source is logged. - * + * * @param source * true if the class, method, file and line number of a log is logged. */ @@ -312,22 +321,40 @@ public class StdErrLog implements Logger public boolean isDebugEnabled() { - return (_level >= LEVEL_DEBUG); + return (_level <= LEVEL_DEBUG); } /** - * @deprecated use {@link #setLevel(int)} instead. + * Legacy interface where a programmatic configuration of the logger level + * is done as a wholesale approach. */ - @Deprecated public void setDebugEnabled(boolean enabled) { if (enabled) { - _level = LEVEL_DEBUG; + synchronized (__loggers) + { + this._level = LEVEL_DEBUG; + + // Boot stomp all cached log levels to DEBUG + for(StdErrLog log: __loggers.values()) + { + log._level = LEVEL_DEBUG; + } + } } else { - _level = LEVEL_INFO; + synchronized (__loggers) + { + this._level = this._configuredLevel; + + // restore all cached log configured levels + for(StdErrLog log: __loggers.values()) + { + log._level = log._configuredLevel; + } + } } } @@ -341,7 +368,7 @@ public class StdErrLog implements Logger *

    * Available values ({@link StdErrLog#LEVEL_ALL}, {@link StdErrLog#LEVEL_DEBUG}, {@link StdErrLog#LEVEL_INFO}, * {@link StdErrLog#LEVEL_WARN}) - * + * * @param level * the level to set the logger to */ @@ -525,34 +552,71 @@ public class StdErrLog implements Logger } else { - buffer.append('\n'); + buffer.append(EOL); format(buffer,thrown.toString()); StackTraceElement[] elements = thrown.getStackTrace(); for (int i = 0; elements != null && i < elements.length; i++) { - buffer.append("\n\tat "); + buffer.append(EOL).append("\tat "); format(buffer,elements[i].toString()); } Throwable cause = thrown.getCause(); if (cause != null && cause != thrown) { - buffer.append("\nCaused by: "); + buffer.append(EOL).append("Caused by: "); format(buffer,cause); } } } + /** + * A more robust form of name blank test. Will return true for null names, and names that have only whitespace + * + * @param name + * the name to test + * @return true for null or blank name, false if any non-whitespace character is found. + */ + private static boolean isBlank(String name) + { + if (name == null) + { + return true; + } + int size = name.length(); + char c; + for (int i = 0; i < size; i++) + { + c = name.charAt(i); + if (!Character.isWhitespace(c)) + { + return false; + } + } + return true; + } + + /** + * Get a Child Logger relative to this Logger. + * + * @param name + * the child name + * @return the appropriate child logger (if name specified results in a new unique child) + */ public Logger getLogger(String name) { - String fullname = _name == null || _name.length() == 0?name:_name + "." + name; - - if ((name == null && this._name == null) || fullname.equals(_name)) + if (isBlank(name)) { return this; } - StdErrLog logger = __loggers.get(name); + String fullname = name; + if (!isBlank(_name)) + { + fullname = _name + "." + name; + } + + StdErrLog logger = __loggers.get(fullname); if (logger == null) { StdErrLog sel = new StdErrLog(fullname); @@ -560,6 +624,7 @@ public class StdErrLog implements Logger sel.setPrintLongNames(_printLongNames); // Let Level come from configured Properties instead - sel.setLevel(_level); sel.setSource(_source); + sel._stderr = this._stderr; logger = __loggers.putIfAbsent(fullname,sel); if (logger == null) { @@ -598,6 +663,11 @@ public class StdErrLog implements Logger return s.toString(); } + public static void setProperties(Properties props) + { + __props = props; + } + public void ignore(Throwable ignored) { if (_level <= LEVEL_ALL) diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/JarResource.java b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/JarResource.java index e317ec00cd2..670eda6b4ad 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/JarResource.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/JarResource.java @@ -130,7 +130,8 @@ public class JarResource extends URLResource if (!exists()) return; - if(LOG.isDebugEnabled())LOG.debug("Extract "+this+" to "+directory); + if(LOG.isDebugEnabled()) + LOG.debug("Extract "+this+" to "+directory); String urlString = this.getURL().toExternalForm().trim(); int endOfJarUrl = urlString.indexOf("!/"); @@ -143,13 +144,13 @@ public class JarResource extends URLResource String subEntryName = (endOfJarUrl+2 < urlString.length() ? urlString.substring(endOfJarUrl + 2) : null); boolean subEntryIsDir = (subEntryName != null && subEntryName.endsWith("/")?true:false); - if (LOG.isDebugEnabled()) LOG.debug("Extracting entry = "+subEntryName+" from jar "+jarFileURL); + if (LOG.isDebugEnabled()) + LOG.debug("Extracting entry = "+subEntryName+" from jar "+jarFileURL); InputStream is = jarFileURL.openConnection().getInputStream(); JarInputStream jin = new JarInputStream(is); JarEntry entry; boolean shouldExtract; - String directoryCanonicalPath = directory.getCanonicalPath()+"/"; while((entry=jin.getNextJarEntry())!=null) { String entryName = entry.getName(); @@ -194,7 +195,8 @@ public class JarResource extends URLResource if (!shouldExtract) { - if (LOG.isDebugEnabled()) LOG.debug("Skipping entry: "+entryName); + if (LOG.isDebugEnabled()) + LOG.debug("Skipping entry: "+entryName); continue; } @@ -202,7 +204,8 @@ public class JarResource extends URLResource dotCheck = URIUtil.canonicalPath(dotCheck); if (dotCheck == null) { - if (LOG.isDebugEnabled()) LOG.debug("Invalid entry: "+entryName); + if (LOG.isDebugEnabled()) + LOG.debug("Invalid entry: "+entryName); continue; } diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/security/B64Code.java b/jetty-util/src/main/java/org/eclipse/jetty/util/security/B64Code.java similarity index 96% rename from jetty-http/src/main/java/org/eclipse/jetty/http/security/B64Code.java rename to jetty-util/src/main/java/org/eclipse/jetty/util/security/B64Code.java index 99550ee2ed9..ded3a40c53c 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/security/B64Code.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/security/B64Code.java @@ -12,7 +12,7 @@ // ======================================================================== -package org.eclipse.jetty.http.security; +package org.eclipse.jetty.util.security; /* ------------------------------------------------------------ */ diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/security/Constraint.java b/jetty-util/src/main/java/org/eclipse/jetty/util/security/Constraint.java similarity index 99% rename from jetty-http/src/main/java/org/eclipse/jetty/http/security/Constraint.java rename to jetty-util/src/main/java/org/eclipse/jetty/util/security/Constraint.java index 984a8aa4719..320625ca96c 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/security/Constraint.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/security/Constraint.java @@ -11,7 +11,7 @@ // You may elect to redistribute this code under either of these licenses. // ======================================================================== -package org.eclipse.jetty.http.security; +package org.eclipse.jetty.util.security; import java.io.Serializable; import java.util.Arrays; diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/security/Credential.java b/jetty-util/src/main/java/org/eclipse/jetty/util/security/Credential.java similarity index 99% rename from jetty-http/src/main/java/org/eclipse/jetty/http/security/Credential.java rename to jetty-util/src/main/java/org/eclipse/jetty/util/security/Credential.java index 93dfa1c5f4d..c0fe0b36f7b 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/security/Credential.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/security/Credential.java @@ -11,7 +11,7 @@ // You may elect to redistribute this code under either of these licenses. // ======================================================================== -package org.eclipse.jetty.http.security; +package org.eclipse.jetty.util.security; import java.io.Serializable; import java.security.MessageDigest; diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/security/Password.java b/jetty-util/src/main/java/org/eclipse/jetty/util/security/Password.java similarity index 99% rename from jetty-http/src/main/java/org/eclipse/jetty/http/security/Password.java rename to jetty-util/src/main/java/org/eclipse/jetty/util/security/Password.java index fdaf58741ec..76d33a0de71 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/security/Password.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/security/Password.java @@ -11,7 +11,7 @@ // You may elect to redistribute this code under either of these licenses. // ======================================================================== -package org.eclipse.jetty.http.security; +package org.eclipse.jetty.util.security; import java.io.IOException; import java.util.Arrays; diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/security/UnixCrypt.java b/jetty-util/src/main/java/org/eclipse/jetty/util/security/UnixCrypt.java similarity index 99% rename from jetty-http/src/main/java/org/eclipse/jetty/http/security/UnixCrypt.java rename to jetty-util/src/main/java/org/eclipse/jetty/util/security/UnixCrypt.java index 96f472d464c..e3f98e807f6 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/security/UnixCrypt.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/security/UnixCrypt.java @@ -21,7 +21,7 @@ * by Iris Van den Broeke, Daniel Deville */ -package org.eclipse.jetty.http.security; +package org.eclipse.jetty.util.security; /* ------------------------------------------------------------ */ diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/ssl/AliasedX509ExtendedKeyManager.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/AliasedX509ExtendedKeyManager.java similarity index 99% rename from jetty-http/src/main/java/org/eclipse/jetty/http/ssl/AliasedX509ExtendedKeyManager.java rename to jetty-util/src/main/java/org/eclipse/jetty/util/ssl/AliasedX509ExtendedKeyManager.java index 5e128ddfc57..cbff56277d8 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/ssl/AliasedX509ExtendedKeyManager.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/AliasedX509ExtendedKeyManager.java @@ -14,7 +14,7 @@ //You may elect to redistribute this code under either of these licenses. //======================================================================== -package org.eclipse.jetty.http.ssl; +package org.eclipse.jetty.util.ssl; import java.net.Socket; import java.security.Principal; diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/ssl/AliasedX509KeyManager.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/AliasedX509KeyManager.java similarity index 99% rename from jetty-http/src/main/java/org/eclipse/jetty/http/ssl/AliasedX509KeyManager.java rename to jetty-util/src/main/java/org/eclipse/jetty/util/ssl/AliasedX509KeyManager.java index 955c1452de3..56308821677 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/ssl/AliasedX509KeyManager.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/AliasedX509KeyManager.java @@ -14,7 +14,7 @@ //You may elect to redistribute this code under either of these licenses. //======================================================================== -package org.eclipse.jetty.http.ssl; +package org.eclipse.jetty.util.ssl; import java.net.Socket; import java.security.Principal; diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslContextFactory.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslContextFactory.java new file mode 100644 index 00000000000..6180022cd7e --- /dev/null +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslContextFactory.java @@ -0,0 +1,1525 @@ +//======================================================================== +//Copyright (c) Webtide LLC +//------------------------------------------------------------------------ +//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.apache.org/licenses/LICENSE-2.0.txt +// +//You may elect to redistribute this code under either of these licenses. +//======================================================================== + +package org.eclipse.jetty.util.ssl; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetAddress; +import java.security.InvalidParameterException; +import java.security.KeyStore; +import java.security.SecureRandom; +import java.security.Security; +import java.security.cert.CRL; +import java.security.cert.CertStore; +import java.security.cert.Certificate; +import java.security.cert.CollectionCertStoreParameters; +import java.security.cert.PKIXBuilderParameters; +import java.security.cert.X509CertSelector; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.net.ssl.CertPathTrustManagerParameters; +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509KeyManager; +import javax.net.ssl.X509TrustManager; + +import org.eclipse.jetty.util.IO; +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.resource.Resource; +import org.eclipse.jetty.util.security.CertificateUtils; +import org.eclipse.jetty.util.security.CertificateValidator; +import org.eclipse.jetty.util.security.Password; + + +/* ------------------------------------------------------------ */ +/** + * SslContextFactory is used to configure SSL connectors + * as well as HttpClient. It holds all SSL parameters and + * creates SSL context based on these parameters to be + * used by the SSL connectors. + */ +public class SslContextFactory extends AbstractLifeCycle +{ + private static final Logger LOG = Log.getLogger(SslContextFactory.class); + + public static final String DEFAULT_KEYMANAGERFACTORY_ALGORITHM = + (Security.getProperty("ssl.KeyManagerFactory.algorithm") == null ? + "SunX509" : Security.getProperty("ssl.KeyManagerFactory.algorithm")); + public static final String DEFAULT_TRUSTMANAGERFACTORY_ALGORITHM = + (Security.getProperty("ssl.TrustManagerFactory.algorithm") == null ? + "SunX509" : Security.getProperty("ssl.TrustManagerFactory.algorithm")); + + /** Default value for the keystore location path. */ + public static final String DEFAULT_KEYSTORE_PATH = + System.getProperty("user.home") + File.separator + ".keystore"; + + /** String name of key password property. */ + public static final String KEYPASSWORD_PROPERTY = "org.eclipse.jetty.ssl.keypassword"; + + /** String name of keystore password property. */ + public static final String PASSWORD_PROPERTY = "org.eclipse.jetty.ssl.password"; + + /** Excluded protocols. */ + private final Set _excludeProtocols = new HashSet(); + // private final Set _excludeProtocols = new HashSet(Collections.singleton("SSLv2Hello")); + /** Included protocols. */ + private Set _includeProtocols = null; + + /** Excluded cipher suites. */ + private final Set _excludeCipherSuites = new HashSet(); + /** Included cipher suites. */ + private Set _includeCipherSuites = null; + + /** Keystore path. */ + private String _keyStorePath; + /** Keystore provider name */ + private String _keyStoreProvider; + /** Keystore type */ + private String _keyStoreType = "JKS"; + /** Keystore input stream */ + private InputStream _keyStoreInputStream; + + /** SSL certificate alias */ + private String _certAlias; + + /** Truststore path */ + private String _trustStorePath; + /** Truststore provider name */ + private String _trustStoreProvider; + /** Truststore type */ + private String _trustStoreType = "JKS"; + /** Truststore input stream */ + private InputStream _trustStoreInputStream; + + /** Set to true if client certificate authentication is required */ + private boolean _needClientAuth = false; + /** Set to true if client certificate authentication is desired */ + private boolean _wantClientAuth = false; + + /** Set to true if renegotiation is allowed */ + private boolean _allowRenegotiate = true; + + /** Keystore password */ + private transient Password _keyStorePassword; + /** Key manager password */ + private transient Password _keyManagerPassword; + /** Truststore password */ + private transient Password _trustStorePassword; + + /** SSL provider name */ + private String _sslProvider; + /** SSL protocol name */ + private String _sslProtocol = "TLS"; + + /** SecureRandom algorithm */ + private String _secureRandomAlgorithm; + /** KeyManager factory algorithm */ + private String _keyManagerFactoryAlgorithm = DEFAULT_KEYMANAGERFACTORY_ALGORITHM; + /** TrustManager factory algorithm */ + private String _trustManagerFactoryAlgorithm = DEFAULT_TRUSTMANAGERFACTORY_ALGORITHM; + + /** Set to true if SSL certificate validation is required */ + private boolean _validateCerts; + /** Set to true if SSL certificate of the peer validation is required */ + private boolean _validatePeerCerts; + /** Maximum certification path length (n - number of intermediate certs, -1 for unlimited) */ + private int _maxCertPathLength = -1; + /** Path to file that contains Certificate Revocation List */ + private String _crlPath; + /** Set to true to enable CRL Distribution Points (CRLDP) support */ + private boolean _enableCRLDP = false; + /** Set to true to enable On-Line Certificate Status Protocol (OCSP) support */ + private boolean _enableOCSP = false; + /** Location of OCSP Responder */ + private String _ocspResponderURL; + + /** SSL keystore */ + private KeyStore _keyStore; + /** SSL truststore */ + private KeyStore _trustStore; + /** Set to true to enable SSL Session caching */ + private boolean _sessionCachingEnabled = true; + /** SSL session cache size */ + private int _sslSessionCacheSize; + /** SSL session timeout */ + private int _sslSessionTimeout; + + /** SSL context */ + private SSLContext _context; + + private boolean _trustAll; + + /* ------------------------------------------------------------ */ + /** + * Construct an instance of SslContextFactory + * Default constructor for use in XmlConfiguration files + */ + public SslContextFactory() + { + _trustAll=true; + } + + /* ------------------------------------------------------------ */ + /** + * Construct an instance of SslContextFactory + * Default constructor for use in XmlConfiguration files + */ + public SslContextFactory(boolean trustAll) + { + _trustAll=trustAll; + } + + /* ------------------------------------------------------------ */ + /** + * Construct an instance of SslContextFactory + * @param keyStorePath default keystore location + */ + public SslContextFactory(String keyStorePath) + { + _keyStorePath = keyStorePath; + } + + /* ------------------------------------------------------------ */ + /** + * Create the SSLContext object and start the lifecycle + * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart() + */ + @Override + protected void doStart() throws Exception + { + if (_context == null) + { + if (_keyStore==null && _keyStoreInputStream == null && _keyStorePath == null && + _trustStore==null && _trustStoreInputStream == null && _trustStorePath == null ) + { + TrustManager[] trust_managers=null; + + if (_trustAll) + { + LOG.debug("No keystore or trust store configured. ACCEPTING UNTRUSTED CERTIFICATES!!!!!"); + // Create a trust manager that does not validate certificate chains + TrustManager trustAllCerts = new X509TrustManager() + { + public java.security.cert.X509Certificate[] getAcceptedIssuers() + { + return null; + } + + public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) + { + } + + public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) + { + } + }; + trust_managers = new TrustManager[] { trustAllCerts }; + } + + SecureRandom secureRandom = (_secureRandomAlgorithm == null)?null:SecureRandom.getInstance(_secureRandomAlgorithm); + _context = SSLContext.getInstance(_sslProtocol); + _context.init(null, trust_managers, secureRandom); + } + else + { + // verify that keystore and truststore + // parameters are set up correctly + checkKeyStore(); + + KeyStore keyStore = loadKeyStore(); + KeyStore trustStore = loadTrustStore(); + + Collection crls = loadCRL(_crlPath); + + if (_validateCerts && keyStore != null) + { + if (_certAlias == null) + { + List aliases = Collections.list(keyStore.aliases()); + _certAlias = aliases.size() == 1 ? aliases.get(0) : null; + } + + Certificate cert = _certAlias == null?null:keyStore.getCertificate(_certAlias); + if (cert == null) + { + throw new Exception("No certificate found in the keystore" + (_certAlias==null ? "":" for alias " + _certAlias)); + } + + CertificateValidator validator = new CertificateValidator(trustStore, crls); + validator.setMaxCertPathLength(_maxCertPathLength); + validator.setEnableCRLDP(_enableCRLDP); + validator.setEnableOCSP(_enableOCSP); + validator.setOcspResponderURL(_ocspResponderURL); + validator.validate(keyStore, cert); + } + + KeyManager[] keyManagers = getKeyManagers(keyStore); + TrustManager[] trustManagers = getTrustManagers(trustStore,crls); + + SecureRandom secureRandom = (_secureRandomAlgorithm == null)?null:SecureRandom.getInstance(_secureRandomAlgorithm); + _context = (_sslProvider == null)?SSLContext.getInstance(_sslProtocol):SSLContext.getInstance(_sslProtocol,_sslProvider); + _context.init(keyManagers,trustManagers,secureRandom); + + SSLEngine engine=newSslEngine(); + + LOG.info("Enabled Protocols {} of {}",Arrays.asList(engine.getEnabledProtocols()),Arrays.asList(engine.getSupportedProtocols())); + if (LOG.isDebugEnabled()) + LOG.debug("Enabled Ciphers {} of {}",Arrays.asList(engine.getEnabledCipherSuites()),Arrays.asList(engine.getSupportedCipherSuites())); + } + } + } + + /* ------------------------------------------------------------ */ + /** + * @return The array of protocol names to exclude from + * {@link SSLEngine#setEnabledProtocols(String[])} + */ + public String[] getExcludeProtocols() + { + return _excludeProtocols.toArray(new String[_excludeProtocols.size()]); + } + + /* ------------------------------------------------------------ */ + /** + * @param Protocols + * The array of protocol names to exclude from + * {@link SSLEngine#setEnabledProtocols(String[])} + */ + public void setExcludeProtocols(String... protocols) + { + checkNotStarted(); + + _excludeProtocols.clear(); + _excludeProtocols.addAll(Arrays.asList(protocols)); + } + + /* ------------------------------------------------------------ */ + /** + * @param protocol Protocol names to add to {@link SSLEngine#setEnabledProtocols(String[])} + */ + public void addExcludeProtocols(String... protocol) + { + checkNotStarted(); + _excludeProtocols.addAll(Arrays.asList(protocol)); + } + + /* ------------------------------------------------------------ */ + /** + * @return The array of protocol names to include in + * {@link SSLEngine#setEnabledProtocols(String[])} + */ + public String[] getIncludeProtocols() + { + return _includeProtocols.toArray(new String[_includeProtocols.size()]); + } + + /* ------------------------------------------------------------ */ + /** + * @param Protocols + * The array of protocol names to include in + * {@link SSLEngine#setEnabledProtocols(String[])} + */ + public void setIncludeProtocols(String... protocols) + { + checkNotStarted(); + + _includeProtocols = new HashSet(Arrays.asList(protocols)); + } + + /* ------------------------------------------------------------ */ + /** + * @return The array of cipher suite names to exclude from + * {@link SSLEngine#setEnabledCipherSuites(String[])} + */ + public String[] getExcludeCipherSuites() + { + return _excludeCipherSuites.toArray(new String[_excludeCipherSuites.size()]); + } + + /* ------------------------------------------------------------ */ + /** + * @param cipherSuites + * The array of cipher suite names to exclude from + * {@link SSLEngine#setEnabledCipherSuites(String[])} + */ + public void setExcludeCipherSuites(String... cipherSuites) + { + checkNotStarted(); + _excludeCipherSuites.clear(); + _excludeCipherSuites.addAll(Arrays.asList(cipherSuites)); + } + + /* ------------------------------------------------------------ */ + /** + * @param cipher Cipher names to add to {@link SSLEngine#setEnabledCipherSuites(String[])} + */ + public void addExcludeCipherSuites(String... cipher) + { + checkNotStarted(); + _excludeCipherSuites.addAll(Arrays.asList(cipher)); + } + + /* ------------------------------------------------------------ */ + /** + * @return The array of cipher suite names to include in + * {@link SSLEngine#setEnabledCipherSuites(String[])} + */ + public String[] getIncludeCipherSuites() + { + return _includeCipherSuites.toArray(new String[_includeCipherSuites.size()]); + } + + /* ------------------------------------------------------------ */ + /** + * @param cipherSuites + * The array of cipher suite names to include in + * {@link SSLEngine#setEnabledCipherSuites(String[])} + */ + public void setIncludeCipherSuites(String... cipherSuites) + { + checkNotStarted(); + + _includeCipherSuites = new HashSet(Arrays.asList(cipherSuites)); + } + + /* ------------------------------------------------------------ */ + /** + * @return The file or URL of the SSL Key store. + */ + public String getKeyStorePath() + { + return _keyStorePath; + } + + /* ------------------------------------------------------------ */ + @Deprecated + public String getKeyStore() + { + return _keyStorePath; + } + + /* ------------------------------------------------------------ */ + /** + * @param keyStorePath + * The file or URL of the SSL Key store. + */ + public void setKeyStorePath(String keyStorePath) + { + checkNotStarted(); + + _keyStorePath = keyStorePath; + } + + /* ------------------------------------------------------------ */ + /** + * @param keyStorePath + * @deprecated Use {@link #setKeyStorePath(String)} + */ + @Deprecated + public void setKeyStore(String keyStorePath) + { + checkNotStarted(); + + _keyStorePath = keyStorePath; + } + + /* ------------------------------------------------------------ */ + /** + * @return The provider of the key store + */ + public String getKeyStoreProvider() + { + return _keyStoreProvider; + } + + /* ------------------------------------------------------------ */ + /** + * @param keyStoreProvider + * The provider of the key store + */ + public void setKeyStoreProvider(String keyStoreProvider) + { + checkNotStarted(); + + _keyStoreProvider = keyStoreProvider; + } + + /* ------------------------------------------------------------ */ + /** + * @return The type of the key store (default "JKS") + */ + public String getKeyStoreType() + { + return (_keyStoreType); + } + + /* ------------------------------------------------------------ */ + /** + * @param keyStoreType + * The type of the key store (default "JKS") + */ + public void setKeyStoreType(String keyStoreType) + { + checkNotStarted(); + + _keyStoreType = keyStoreType; + } + + /* ------------------------------------------------------------ */ + /** Get the _keyStoreInputStream. + * @return the _keyStoreInputStream + * + * @deprecated + */ + @Deprecated + public InputStream getKeyStoreInputStream() + { + checkKeyStore(); + + return _keyStoreInputStream; + } + + /* ------------------------------------------------------------ */ + /** Set the keyStoreInputStream. + * @param keyStoreInputStream the InputStream to the KeyStore + * + * @deprecated Use {@link #setKeyStore(KeyStore)} + */ + @Deprecated + public void setKeyStoreInputStream(InputStream keyStoreInputStream) + { + checkNotStarted(); + + _keyStoreInputStream = keyStoreInputStream; + } + + /* ------------------------------------------------------------ */ + /** + * @return Alias of SSL certificate for the connector + */ + public String getCertAlias() + { + return _certAlias; + } + + /* ------------------------------------------------------------ */ + /** + * @param certAlias + * Alias of SSL certificate for the connector + */ + public void setCertAlias(String certAlias) + { + checkNotStarted(); + + _certAlias = certAlias; + } + + /* ------------------------------------------------------------ */ + /** + * @return The file name or URL of the trust store location + */ + public String getTrustStore() + { + return _trustStorePath; + } + + /* ------------------------------------------------------------ */ + /** + * @param trustStorePath + * The file name or URL of the trust store location + */ + public void setTrustStore(String trustStorePath) + { + checkNotStarted(); + + _trustStorePath = trustStorePath; + } + + /* ------------------------------------------------------------ */ + /** + * @return The provider of the trust store + */ + public String getTrustStoreProvider() + { + return _trustStoreProvider; + } + + /* ------------------------------------------------------------ */ + /** + * @param trustStoreProvider + * The provider of the trust store + */ + public void setTrustStoreProvider(String trustStoreProvider) + { + checkNotStarted(); + + _trustStoreProvider = trustStoreProvider; + } + + /* ------------------------------------------------------------ */ + /** + * @return The type of the trust store (default "JKS") + */ + public String getTrustStoreType() + { + return _trustStoreType; + } + + /* ------------------------------------------------------------ */ + /** + * @param trustStoreType + * The type of the trust store (default "JKS") + */ + public void setTrustStoreType(String trustStoreType) + { + checkNotStarted(); + + _trustStoreType = trustStoreType; + } + + /* ------------------------------------------------------------ */ + /** Get the _trustStoreInputStream. + * @return the _trustStoreInputStream + * + * @deprecated + */ + @Deprecated + public InputStream getTrustStoreInputStream() + { + checkKeyStore(); + + return _trustStoreInputStream; + } + + /* ------------------------------------------------------------ */ + /** Set the _trustStoreInputStream. + * @param trustStoreInputStream the InputStream to the TrustStore + * + * @deprecated + */ + @Deprecated + public void setTrustStoreInputStream(InputStream trustStoreInputStream) + { + checkNotStarted(); + + _trustStoreInputStream = trustStoreInputStream; + } + + /* ------------------------------------------------------------ */ + /** + * @return True if SSL needs client authentication. + * @see SSLEngine#getNeedClientAuth() + */ + public boolean getNeedClientAuth() + { + return _needClientAuth; + } + + /* ------------------------------------------------------------ */ + /** + * @param needClientAuth + * True if SSL needs client authentication. + * @see SSLEngine#getNeedClientAuth() + */ + public void setNeedClientAuth(boolean needClientAuth) + { + checkNotStarted(); + + _needClientAuth = needClientAuth; + } + + /* ------------------------------------------------------------ */ + /** + * @return True if SSL wants client authentication. + * @see SSLEngine#getWantClientAuth() + */ + public boolean getWantClientAuth() + { + return _wantClientAuth; + } + + /* ------------------------------------------------------------ */ + /** + * @param wantClientAuth + * True if SSL wants client authentication. + * @see SSLEngine#getWantClientAuth() + */ + public void setWantClientAuth(boolean wantClientAuth) + { + checkNotStarted(); + + _wantClientAuth = wantClientAuth; + } + + /* ------------------------------------------------------------ */ + /** + * @return true if SSL certificate has to be validated + * @deprecated + */ + @Deprecated + public boolean getValidateCerts() + { + return _validateCerts; + } + + /* ------------------------------------------------------------ */ + /** + * @return true if SSL certificate has to be validated + */ + public boolean isValidateCerts() + { + return _validateCerts; + } + + /* ------------------------------------------------------------ */ + /** + * @param validateCerts + * true if SSL certificates have to be validated + */ + public void setValidateCerts(boolean validateCerts) + { + checkNotStarted(); + + _validateCerts = validateCerts; + } + + /* ------------------------------------------------------------ */ + /** + * @return true if SSL certificates of the peer have to be validated + */ + public boolean isValidatePeerCerts() + { + return _validatePeerCerts; + } + + /* ------------------------------------------------------------ */ + /** + * @param validatePeerCerts + * true if SSL certificates of the peer have to be validated + */ + public void setValidatePeerCerts(boolean validatePeerCerts) + { + checkNotStarted(); + + _validatePeerCerts = validatePeerCerts; + } + + /* ------------------------------------------------------------ */ + /** + * @return True if SSL re-negotiation is allowed (default false) + */ + public boolean isAllowRenegotiate() + { + return _allowRenegotiate; + } + + /* ------------------------------------------------------------ */ + /** + * Set if SSL re-negotiation is allowed. CVE-2009-3555 discovered + * a vulnerability in SSL/TLS with re-negotiation. If your JVM + * does not have CVE-2009-3555 fixed, then re-negotiation should + * not be allowed. CVE-2009-3555 was fixed in Sun java 1.6 with a ban + * of renegotiates in u19 and with RFC5746 in u22. + * + * @param allowRenegotiate + * true if re-negotiation is allowed (default false) + */ + public void setAllowRenegotiate(boolean allowRenegotiate) + { + checkNotStarted(); + + _allowRenegotiate = allowRenegotiate; + } + + /* ------------------------------------------------------------ */ + /** + * @param password + * The password for the key store + */ + public void setKeyStorePassword(String password) + { + checkNotStarted(); + + _keyStorePassword = Password.getPassword(PASSWORD_PROPERTY,password,null); + } + + /* ------------------------------------------------------------ */ + /** + * @param password + * The password (if any) for the specific key within the key store + */ + public void setKeyManagerPassword(String password) + { + checkNotStarted(); + + _keyManagerPassword = Password.getPassword(KEYPASSWORD_PROPERTY,password,null); + } + + /* ------------------------------------------------------------ */ + /** + * @param password + * The password for the trust store + */ + public void setTrustStorePassword(String password) + { + checkNotStarted(); + + _trustStorePassword = Password.getPassword(PASSWORD_PROPERTY,password,null); + } + + /* ------------------------------------------------------------ */ + /** + * @return The SSL provider name, which if set is passed to + * {@link SSLContext#getInstance(String, String)} + */ + public String getProvider() + { + return _sslProvider; + } + + /* ------------------------------------------------------------ */ + /** + * @param provider + * The SSL provider name, which if set is passed to + * {@link SSLContext#getInstance(String, String)} + */ + public void setProvider(String provider) + { + checkNotStarted(); + + _sslProvider = provider; + } + + /* ------------------------------------------------------------ */ + /** + * @return The SSL protocol (default "TLS") passed to + * {@link SSLContext#getInstance(String, String)} + */ + public String getProtocol() + { + return _sslProtocol; + } + + /* ------------------------------------------------------------ */ + /** + * @param protocol + * The SSL protocol (default "TLS") passed to + * {@link SSLContext#getInstance(String, String)} + */ + public void setProtocol(String protocol) + { + checkNotStarted(); + + _sslProtocol = protocol; + } + + /* ------------------------------------------------------------ */ + /** + * @return The algorithm name, which if set is passed to + * {@link SecureRandom#getInstance(String)} to obtain the {@link SecureRandom} instance passed to + * {@link SSLContext#init(javax.net.ssl.KeyManager[], javax.net.ssl.TrustManager[], SecureRandom)} + */ + public String getSecureRandomAlgorithm() + { + return _secureRandomAlgorithm; + } + + /* ------------------------------------------------------------ */ + /** + * @param algorithm + * The algorithm name, which if set is passed to + * {@link SecureRandom#getInstance(String)} to obtain the {@link SecureRandom} instance passed to + * {@link SSLContext#init(javax.net.ssl.KeyManager[], javax.net.ssl.TrustManager[], SecureRandom)} + */ + public void setSecureRandomAlgorithm(String algorithm) + { + checkNotStarted(); + + _secureRandomAlgorithm = algorithm; + } + + /* ------------------------------------------------------------ */ + /** + * @return The algorithm name (default "SunX509") used by the {@link KeyManagerFactory} + */ + public String getSslKeyManagerFactoryAlgorithm() + { + return (_keyManagerFactoryAlgorithm); + } + + /* ------------------------------------------------------------ */ + /** + * @param algorithm + * The algorithm name (default "SunX509") used by the {@link KeyManagerFactory} + */ + public void setSslKeyManagerFactoryAlgorithm(String algorithm) + { + checkNotStarted(); + + _keyManagerFactoryAlgorithm = algorithm; + } + + /* ------------------------------------------------------------ */ + /** + * @return The algorithm name (default "SunX509") used by the {@link TrustManagerFactory} + */ + public String getTrustManagerFactoryAlgorithm() + { + return (_trustManagerFactoryAlgorithm); + } + + /* ------------------------------------------------------------ */ + /** + * @return True if all certificates should be trusted if there is no KeyStore or TrustStore + */ + public boolean isTrustAll() + { + return _trustAll; + } + + /* ------------------------------------------------------------ */ + /** + * @param trustAll True if all certificates should be trusted if there is no KeyStore or TrustStore + */ + public void setTrustAll(boolean trustAll) + { + _trustAll = trustAll; + } + + /* ------------------------------------------------------------ */ + /** + * @param algorithm + * The algorithm name (default "SunX509") used by the {@link TrustManagerFactory} + * Use the string "TrustAll" to install a trust manager that trusts all. + */ + public void setTrustManagerFactoryAlgorithm(String algorithm) + { + checkNotStarted(); + + _trustManagerFactoryAlgorithm = algorithm; + } + + /* ------------------------------------------------------------ */ + /** + * @return Path to file that contains Certificate Revocation List + */ + public String getCrlPath() + { + return _crlPath; + } + + /* ------------------------------------------------------------ */ + /** + * @param crlPath + * Path to file that contains Certificate Revocation List + */ + public void setCrlPath(String crlPath) + { + checkNotStarted(); + + _crlPath = crlPath; + } + + /* ------------------------------------------------------------ */ + /** + * @return Maximum number of intermediate certificates in + * the certification path (-1 for unlimited) + */ + public int getMaxCertPathLength() + { + return _maxCertPathLength; + } + + /* ------------------------------------------------------------ */ + /** + * @param maxCertPathLength + * maximum number of intermediate certificates in + * the certification path (-1 for unlimited) + */ + public void setMaxCertPathLength(int maxCertPathLength) + { + checkNotStarted(); + + _maxCertPathLength = maxCertPathLength; + } + + /* ------------------------------------------------------------ */ + /** + * @return The SSLContext + */ + public SSLContext getSslContext() + { + if (!isStarted()) + throw new IllegalStateException(getState()); + return _context; + } + + /* ------------------------------------------------------------ */ + /** + * @param sslContext + * Set a preconfigured SSLContext + */ + public void setSslContext(SSLContext sslContext) + { + checkNotStarted(); + + _context = sslContext; + } + + /* ------------------------------------------------------------ */ + /** + * Override this method to provide alternate way to load a keystore. + * + * @return the key store instance + * @throws Exception + */ + protected KeyStore loadKeyStore() throws Exception + { + return _keyStore != null ? _keyStore : getKeyStore(_keyStoreInputStream, + _keyStorePath, _keyStoreType, _keyStoreProvider, + _keyStorePassword==null? null: _keyStorePassword.toString()); + } + + /* ------------------------------------------------------------ */ + /** + * Override this method to provide alternate way to load a truststore. + * + * @return the key store instance + * @throws Exception + */ + protected KeyStore loadTrustStore() throws Exception + { + return _trustStore != null ? _trustStore : getKeyStore(_trustStoreInputStream, + _trustStorePath, _trustStoreType, _trustStoreProvider, + _trustStorePassword==null? null: _trustStorePassword.toString()); + } + + /* ------------------------------------------------------------ */ + /** + * Loads keystore using an input stream or a file path in the same + * order of precedence. + * + * Required for integrations to be able to override the mechanism + * used to load a keystore in order to provide their own implementation. + * + * @param storeStream keystore input stream + * @param storePath path of keystore file + * @param storeType keystore type + * @param storeProvider keystore provider + * @param storePassword keystore password + * @return created keystore + * @throws Exception + * + * @deprecated + */ + @Deprecated + protected KeyStore getKeyStore(InputStream storeStream, String storePath, String storeType, String storeProvider, String storePassword) throws Exception + { + return CertificateUtils.getKeyStore(storeStream, storePath, storeType, storeProvider, storePassword); + } + + /* ------------------------------------------------------------ */ + /** + * Loads certificate revocation list (CRL) from a file. + * + * Required for integrations to be able to override the mechanism used to + * load CRL in order to provide their own implementation. + * + * @param crlPath path of certificate revocation list file + * @return Collection of CRL's + * @throws Exception + */ + protected Collection loadCRL(String crlPath) throws Exception + { + return CertificateUtils.loadCRL(crlPath); + } + + /* ------------------------------------------------------------ */ + protected KeyManager[] getKeyManagers(KeyStore keyStore) throws Exception + { + KeyManager[] managers = null; + + if (keyStore != null) + { + KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(_keyManagerFactoryAlgorithm); + keyManagerFactory.init(keyStore,_keyManagerPassword == null?(_keyStorePassword == null?null:_keyStorePassword.toString().toCharArray()):_keyManagerPassword.toString().toCharArray()); + managers = keyManagerFactory.getKeyManagers(); + + if (_certAlias != null) + { + for (int idx = 0; idx < managers.length; idx++) + { + if (managers[idx] instanceof X509KeyManager) + { + managers[idx] = new AliasedX509ExtendedKeyManager(_certAlias,(X509KeyManager)managers[idx]); + } + } + } + } + + return managers; + } + + /* ------------------------------------------------------------ */ + protected TrustManager[] getTrustManagers(KeyStore trustStore, Collection crls) throws Exception + { + TrustManager[] managers = null; + if (trustStore != null) + { + // Revocation checking is only supported for PKIX algorithm + if (_validatePeerCerts && _trustManagerFactoryAlgorithm.equalsIgnoreCase("PKIX")) + { + PKIXBuilderParameters pbParams = new PKIXBuilderParameters(trustStore,new X509CertSelector()); + + // Set maximum certification path length + pbParams.setMaxPathLength(_maxCertPathLength); + + // Make sure revocation checking is enabled + pbParams.setRevocationEnabled(true); + + if (crls != null && !crls.isEmpty()) + { + pbParams.addCertStore(CertStore.getInstance("Collection",new CollectionCertStoreParameters(crls))); + } + + if (_enableCRLDP) + { + // Enable Certificate Revocation List Distribution Points (CRLDP) support + System.setProperty("com.sun.security.enableCRLDP","true"); + } + + if (_enableOCSP) + { + // Enable On-Line Certificate Status Protocol (OCSP) support + Security.setProperty("ocsp.enable","true"); + + if (_ocspResponderURL != null) + { + // Override location of OCSP Responder + Security.setProperty("ocsp.responderURL", _ocspResponderURL); + } + } + + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(_trustManagerFactoryAlgorithm); + trustManagerFactory.init(new CertPathTrustManagerParameters(pbParams)); + + managers = trustManagerFactory.getTrustManagers(); + } + else + { + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(_trustManagerFactoryAlgorithm); + trustManagerFactory.init(trustStore); + + managers = trustManagerFactory.getTrustManagers(); + } + } + + return managers; + } + + /* ------------------------------------------------------------ */ + /** + * Check KeyStore Configuration. Ensures that if keystore has been + * configured but there's no truststore, that keystore is + * used as truststore. + * @throws IllegalStateException if SslContextFactory configuration can't be used. + */ + public void checkKeyStore() + { + if (_context != null) + return; //nothing to check if using preconfigured context + + + if (_keyStore == null && _keyStoreInputStream == null && _keyStorePath == null) + throw new IllegalStateException("SSL doesn't have a valid keystore"); + + // if the keystore has been configured but there is no + // truststore configured, use the keystore as the truststore + if (_trustStore == null && _trustStoreInputStream == null && _trustStorePath == null) + { + _trustStore = _keyStore; + _trustStorePath = _keyStorePath; + _trustStoreInputStream = _keyStoreInputStream; + _trustStoreType = _keyStoreType; + _trustStoreProvider = _keyStoreProvider; + _trustStorePassword = _keyStorePassword; + _trustManagerFactoryAlgorithm = _keyManagerFactoryAlgorithm; + } + + // It's the same stream we cannot read it twice, so read it once in memory + if (_keyStoreInputStream != null && _keyStoreInputStream == _trustStoreInputStream) + { + try + { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + IO.copy(_keyStoreInputStream, baos); + _keyStoreInputStream.close(); + + _keyStoreInputStream = new ByteArrayInputStream(baos.toByteArray()); + _trustStoreInputStream = new ByteArrayInputStream(baos.toByteArray()); + } + catch (Exception ex) + { + throw new IllegalStateException(ex); + } + } + } + + /* ------------------------------------------------------------ */ + /** + * Select cipher suites to be used by the connector + * based on configured inclusion and exclusion lists + * as well as enabled and supported cipher suite lists. + * @param enabledCipherSuites Array of enabled cipher suites + * @param supportedCipherSuites Array of supported cipher suites + * @return Array of cipher suites to enable + */ + public String[] selectProtocols(String[] enabledProtocols, String[] supportedProtocols) + { + Set selected_protocols = new HashSet(); + + // Set the starting protocols - either from the included or enabled list + if (_includeProtocols!=null) + { + // Use only the supported included protocols + for (String protocol : supportedProtocols) + if (_includeProtocols.contains(protocol)) + selected_protocols.add(protocol); + } + else + selected_protocols.addAll(Arrays.asList(enabledProtocols)); + + + // Remove any excluded protocols + if (_excludeProtocols != null) + selected_protocols.removeAll(_excludeProtocols); + + return selected_protocols.toArray(new String[selected_protocols.size()]); + } + + /* ------------------------------------------------------------ */ + /** + * Select cipher suites to be used by the connector + * based on configured inclusion and exclusion lists + * as well as enabled and supported cipher suite lists. + * @param enabledCipherSuites Array of enabled cipher suites + * @param supportedCipherSuites Array of supported cipher suites + * @return Array of cipher suites to enable + */ + public String[] selectCipherSuites(String[] enabledCipherSuites, String[] supportedCipherSuites) + { + Set selected_ciphers = new HashSet(); + + // Set the starting ciphers - either from the included or enabled list + if (_includeCipherSuites!=null) + { + // Use only the supported included ciphers + for (String cipherSuite : supportedCipherSuites) + if (_includeCipherSuites.contains(cipherSuite)) + selected_ciphers.add(cipherSuite); + } + else + selected_ciphers.addAll(Arrays.asList(enabledCipherSuites)); + + + // Remove any excluded ciphers + if (_excludeCipherSuites != null) + selected_ciphers.removeAll(_excludeCipherSuites); + return selected_ciphers.toArray(new String[selected_ciphers.size()]); + } + + /* ------------------------------------------------------------ */ + /** + * Check if the lifecycle has been started and throw runtime exception + */ + protected void checkNotStarted() + { + if (isStarted()) + throw new IllegalStateException("Cannot modify configuration when "+getState()); + } + + /* ------------------------------------------------------------ */ + /** + * @return true if CRL Distribution Points support is enabled + */ + public boolean isEnableCRLDP() + { + return _enableCRLDP; + } + + /* ------------------------------------------------------------ */ + /** Enables CRL Distribution Points Support + * @param enableCRLDP true - turn on, false - turns off + */ + public void setEnableCRLDP(boolean enableCRLDP) + { + checkNotStarted(); + + _enableCRLDP = enableCRLDP; + } + + /* ------------------------------------------------------------ */ + /** + * @return true if On-Line Certificate Status Protocol support is enabled + */ + public boolean isEnableOCSP() + { + return _enableOCSP; + } + + /* ------------------------------------------------------------ */ + /** Enables On-Line Certificate Status Protocol support + * @param enableOCSP true - turn on, false - turn off + */ + public void setEnableOCSP(boolean enableOCSP) + { + checkNotStarted(); + + _enableOCSP = enableOCSP; + } + + /* ------------------------------------------------------------ */ + /** + * @return Location of the OCSP Responder + */ + public String getOcspResponderURL() + { + return _ocspResponderURL; + } + + /* ------------------------------------------------------------ */ + /** Set the location of the OCSP Responder. + * @param ocspResponderURL location of the OCSP Responder + */ + public void setOcspResponderURL(String ocspResponderURL) + { + checkNotStarted(); + + _ocspResponderURL = ocspResponderURL; + } + + /* ------------------------------------------------------------ */ + /** Set the key store. + * @param keyStore the key store to set + */ + public void setKeyStore(KeyStore keyStore) + { + checkNotStarted(); + + _keyStore = keyStore; + } + + /* ------------------------------------------------------------ */ + /** Set the trust store. + * @param trustStore the trust store to set + */ + public void setTrustStore(KeyStore trustStore) + { + checkNotStarted(); + + _trustStore = trustStore; + } + + /* ------------------------------------------------------------ */ + /** Set the key store resource. + * @param resource the key store resource to set + */ + public void setKeyStoreResource(Resource resource) + { + checkNotStarted(); + + try + { + _keyStoreInputStream = resource.getInputStream(); + } + catch (IOException e) + { + throw new InvalidParameterException("Unable to get resource "+ + "input stream for resource "+resource.toString()); + } + } + + /* ------------------------------------------------------------ */ + /** Set the trust store resource. + * @param resource the trust store resource to set + */ + public void setTrustStoreResource(Resource resource) + { + checkNotStarted(); + + try + { + _trustStoreInputStream = resource.getInputStream(); + } + catch (IOException e) + { + throw new InvalidParameterException("Unable to get resource "+ + "input stream for resource "+resource.toString()); + } + } + + /* ------------------------------------------------------------ */ + /** + * @return true if SSL Session caching is enabled + */ + public boolean isSessionCachingEnabled() + { + return _sessionCachingEnabled; + } + + /* ------------------------------------------------------------ */ + /** Set the flag to enable SSL Session caching. + * @param enableSessionCaching the value of the flag + */ + public void setSessionCachingEnabled(boolean enableSessionCaching) + { + _sessionCachingEnabled = enableSessionCaching; + } + + /* ------------------------------------------------------------ */ + /** Get SSL session cache size. + * @return SSL session cache size + */ + public int getSslSessionCacheSize() + { + return _sslSessionCacheSize; + } + + /* ------------------------------------------------------------ */ + /** SEt SSL session cache size. + * @param sslSessionCacheSize SSL session cache size to set + */ + public void setSslSessionCacheSize(int sslSessionCacheSize) + { + _sslSessionCacheSize = sslSessionCacheSize; + } + + /* ------------------------------------------------------------ */ + /** Get SSL session timeout. + * @return SSL session timeout + */ + public int getSslSessionTimeout() + { + return _sslSessionTimeout; + } + + /* ------------------------------------------------------------ */ + /** Set SSL session timeout. + * @param sslSessionTimeout SSL session timeout to set + */ + public void setSslSessionTimeout(int sslSessionTimeout) + { + _sslSessionTimeout = sslSessionTimeout; + } + + + /* ------------------------------------------------------------ */ + public SSLServerSocket newSslServerSocket(String host,int port,int backlog) throws IOException + { + SSLServerSocketFactory factory = _context.getServerSocketFactory(); + + SSLServerSocket socket = + (SSLServerSocket) (host==null ? + factory.createServerSocket(port,backlog): + factory.createServerSocket(port,backlog,InetAddress.getByName(host))); + + if (getWantClientAuth()) + socket.setWantClientAuth(getWantClientAuth()); + if (getNeedClientAuth()) + socket.setNeedClientAuth(getNeedClientAuth()); + + socket.setEnabledCipherSuites(selectCipherSuites( + socket.getEnabledCipherSuites(), + socket.getSupportedCipherSuites())); + socket.setEnabledProtocols(selectProtocols(socket.getEnabledProtocols(),socket.getSupportedProtocols())); + + return socket; + } + + /* ------------------------------------------------------------ */ + public SSLSocket newSslSocket() throws IOException + { + SSLSocketFactory factory = _context.getSocketFactory(); + + SSLSocket socket = (SSLSocket)factory.createSocket(); + + if (getWantClientAuth()) + socket.setWantClientAuth(getWantClientAuth()); + if (getNeedClientAuth()) + socket.setNeedClientAuth(getNeedClientAuth()); + + socket.setEnabledCipherSuites(selectCipherSuites( + socket.getEnabledCipherSuites(), + socket.getSupportedCipherSuites())); + socket.setEnabledProtocols(selectProtocols(socket.getEnabledProtocols(),socket.getSupportedProtocols())); + + return socket; + } + + /* ------------------------------------------------------------ */ + public SSLEngine newSslEngine(String host,int port) + { + SSLEngine sslEngine=isSessionCachingEnabled() + ?_context.createSSLEngine(host, port) + :_context.createSSLEngine(); + + customize(sslEngine); + return sslEngine; + } + + /* ------------------------------------------------------------ */ + public SSLEngine newSslEngine() + { + SSLEngine sslEngine=_context.createSSLEngine(); + customize(sslEngine); + return sslEngine; + } + + /* ------------------------------------------------------------ */ + public void customize(SSLEngine sslEngine) + { + if (getWantClientAuth()) + sslEngine.setWantClientAuth(getWantClientAuth()); + if (getNeedClientAuth()) + sslEngine.setNeedClientAuth(getNeedClientAuth()); + + sslEngine.setEnabledCipherSuites(selectCipherSuites( + sslEngine.getEnabledCipherSuites(), + sslEngine.getSupportedCipherSuites())); + + sslEngine.setEnabledProtocols(selectProtocols(sslEngine.getEnabledProtocols(),sslEngine.getSupportedProtocols())); + } + +} diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ExecutorThreadPool.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ExecutorThreadPool.java index cd92f7ff9d9..0934e7c24a1 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ExecutorThreadPool.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ExecutorThreadPool.java @@ -4,11 +4,11 @@ // 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 +// 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. +// You may elect to redistribute this code under either of these licenses. // ======================================================================== package org.eclipse.jetty.util.thread; @@ -28,9 +28,9 @@ import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; /* ------------------------------------------------------------ */ -/** +/** * Jetty ThreadPool using java 5 ThreadPoolExecutor - * This class wraps a {@link ExecutorService} as a {@link ThreadPool} and + * This class wraps a {@link ExecutorService} as a {@link ThreadPool} and * {@link LifeCycle} interfaces so that it may be used by the Jetty org.eclipse.jetty.server.Server */ public class ExecutorThreadPool extends AbstractLifeCycle implements ThreadPool, LifeCycle @@ -41,67 +41,83 @@ public class ExecutorThreadPool extends AbstractLifeCycle implements ThreadPool, /* ------------------------------------------------------------ */ public ExecutorThreadPool(ExecutorService executor) { - _executor=executor; + _executor = executor; } - + /* ------------------------------------------------------------ */ - /** constructor. + /** * Wraps an {@link ThreadPoolExecutor}. - * Core size is 32, max pool size is 256, pool thread timeout after 60 seconds and + * Max pool size is 256, pool thread timeout after 60 seconds and * an unbounded {@link LinkedBlockingQueue} is used for the job queue; */ public ExecutorThreadPool() { - this(new ThreadPoolExecutor(32,256,60,TimeUnit.SECONDS,new LinkedBlockingQueue())); + // Using an unbounded queue makes the maxThreads parameter useless + // Refer to ThreadPoolExecutor javadocs for details + this(new ThreadPoolExecutor(256, 256, 60, TimeUnit.SECONDS, new LinkedBlockingQueue())); } - + /* ------------------------------------------------------------ */ - /** constructor. + /** * Wraps an {@link ThreadPoolExecutor}. - * Core size is 32, max pool size is 256, pool thread timeout after 60 seconds - * @param queueSize if -1, an unbounded {@link LinkedBlockingQueue} is used, if 0 then a - * {@link SynchronousQueue} is used, other a {@link ArrayBlockingQueue} of the given size is used. + * Max pool size is 256, pool thread timeout after 60 seconds, and core pool size is 32 when queueSize >= 0. + * @param queueSize can be -1 for using an unbounded {@link LinkedBlockingQueue}, 0 for using a + * {@link SynchronousQueue}, greater than 0 for using a {@link ArrayBlockingQueue} of the given size. */ public ExecutorThreadPool(int queueSize) { - this(new ThreadPoolExecutor(32,256,60,TimeUnit.SECONDS, - queueSize<0?new LinkedBlockingQueue() - : (queueSize==0?new SynchronousQueue() - :new ArrayBlockingQueue(queueSize)))); + this(queueSize < 0 ? new ThreadPoolExecutor(256, 256, 60, TimeUnit.SECONDS, new LinkedBlockingQueue()) : + queueSize == 0 ? new ThreadPoolExecutor(32, 256, 60, TimeUnit.SECONDS, new SynchronousQueue()) : + new ThreadPoolExecutor(32, 256, 60, TimeUnit.SECONDS, new ArrayBlockingQueue(queueSize))); } /* ------------------------------------------------------------ */ - /** constructor. + /** * Wraps an {@link ThreadPoolExecutor} using * an unbounded {@link LinkedBlockingQueue} is used for the jobs queue; + * @param corePoolSize must be equal to maximumPoolSize + * @param maximumPoolSize the maximum number of threads to allow in the pool + * @param keepAliveTime the max time a thread can remain idle, in milliseconds */ public ExecutorThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime) { - this(new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,TimeUnit.MILLISECONDS,new LinkedBlockingQueue())); + this(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.MILLISECONDS); } /* ------------------------------------------------------------ */ - /** constructor. + /** * Wraps an {@link ThreadPoolExecutor} using - * an unbounded {@link LinkedBlockingQueue} is used for the jobs queue; + * an unbounded {@link LinkedBlockingQueue} is used for the jobs queue. + * @param corePoolSize must be equal to maximumPoolSize + * @param maximumPoolSize the maximum number of threads to allow in the pool + * @param keepAliveTime the max time a thread can remain idle + * @param unit the unit for the keepAliveTime */ public ExecutorThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit) { - this(new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,new LinkedBlockingQueue())); + this(corePoolSize, maximumPoolSize, keepAliveTime, unit, new LinkedBlockingQueue()); } /* ------------------------------------------------------------ */ + + /** + * Wraps an {@link ThreadPoolExecutor} + * @param corePoolSize the number of threads to keep in the pool, even if they are idle + * @param maximumPoolSize the maximum number of threads to allow in the pool + * @param keepAliveTime the max time a thread can remain idle + * @param unit the unit for the keepAliveTime + * @param workQueue the queue to use for holding tasks before they are executed + */ public ExecutorThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue) { - this(new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue)); + this(new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue)); } - /* ------------------------------------------------------------ */ public boolean dispatch(Runnable job) { try - { + { _executor.execute(job); return true; } @@ -118,7 +134,7 @@ public class ExecutorThreadPool extends AbstractLifeCycle implements ThreadPool, if (_executor instanceof ThreadPoolExecutor) { final ThreadPoolExecutor tpe = (ThreadPoolExecutor)_executor; - return tpe.getPoolSize() -tpe.getActiveCount(); + return tpe.getPoolSize() - tpe.getActiveCount(); } return -1; } @@ -139,8 +155,10 @@ public class ExecutorThreadPool extends AbstractLifeCycle implements ThreadPool, { if (_executor instanceof ThreadPoolExecutor) { - final ThreadPoolExecutor tpe = (ThreadPoolExecutor)_executor; - return tpe.getTaskCount()>=(tpe.getMaximumPoolSize()); + 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; } @@ -148,14 +166,7 @@ public class ExecutorThreadPool extends AbstractLifeCycle implements ThreadPool, /* ------------------------------------------------------------ */ public void join() throws InterruptedException { - _executor.awaitTermination(Long.MAX_VALUE,TimeUnit.MILLISECONDS); - } - - /* ------------------------------------------------------------ */ - @Override - protected void doStart() throws Exception - { - super.doStart(); + _executor.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS); } /* ------------------------------------------------------------ */ @@ -165,5 +176,4 @@ public class ExecutorThreadPool extends AbstractLifeCycle implements ThreadPool, super.doStop(); _executor.shutdownNow(); } - } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ShutdownThread.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ShutdownThread.java index 96c9c2527e8..d0bbce9fe06 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ShutdownThread.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ShutdownThread.java @@ -122,7 +122,7 @@ public class ShutdownThread extends Thread try { lifeCycle.stop(); - LOG.debug("Stopped " + lifeCycle); + LOG.debug("Stopped {}",lifeCycle); } catch (Exception ex) { diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/LazyListTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/LazyListTest.java index 5cfdf20d87d..151338e5b21 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/LazyListTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/LazyListTest.java @@ -13,7 +13,11 @@ package org.eclipse.jetty.util; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import java.net.URI; import java.util.ArrayList; diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/URLEncodedTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/URLEncodedTest.java index 30f70621404..5d4c22159a2 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/URLEncodedTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/URLEncodedTest.java @@ -19,7 +19,6 @@ import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.UnsupportedEncodingException; -import org.junit.Ignore; import org.junit.Test; diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/Utf8StringBufferTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/Utf8StringBufferTest.java index eacd85b33b9..6af4c080781 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/Utf8StringBufferTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/Utf8StringBufferTest.java @@ -13,13 +13,13 @@ package org.eclipse.jetty.util; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import java.io.UnsupportedEncodingException; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - public class Utf8StringBufferTest { @Test diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/Utf8StringBuilderTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/Utf8StringBuilderTest.java index b83aa1099db..7ff2eb9fd6c 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/Utf8StringBuilderTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/Utf8StringBuilderTest.java @@ -13,18 +13,19 @@ package org.eclipse.jetty.util; -import org.junit.Test; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import org.junit.Test; + public class Utf8StringBuilderTest { @Test public void testInvalid() throws Exception { String[] invalids = - { "c0af", "EDA080", "f08080af", "f8808080af", "e080af", "F4908080", "fbbfbfbfbf", "10FFFF" }; + { "c0af", "EDA080", "f08080af", "f8808080af", "e080af", "F4908080", "fbbfbfbfbf", "10FFFF", + "CeBaE1BdB9Cf83CeBcCeB5EdA080656469746564" }; for (String i : invalids) { diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/ajax/JSONPojoConvertorFactoryTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/ajax/JSONPojoConvertorFactoryTest.java index 8cffa5896f6..066d0af5d42 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/ajax/JSONPojoConvertorFactoryTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/ajax/JSONPojoConvertorFactoryTest.java @@ -113,7 +113,7 @@ public class JSONPojoConvertorFactoryTest Map bz = (Map)br.get("baz"); Map f = (Map)bz.get("foo"); - + assertTrue(f != null); Object[] bazs = (Object[])br.get("bazs"); assertTrue(bazs.length==2); assertEquals(((Map)bazs[0]).get("message"), "baz0"); diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/ajax/JSONTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/ajax/JSONTest.java index 4f700dd0c58..58d8257b52a 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/ajax/JSONTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/ajax/JSONTest.java @@ -203,7 +203,7 @@ public class JSONTest public void testZeroByte() { String withzero="\u0000"; - String json = JSON.toString(withzero); + JSON.toString(withzero); } /* ------------------------------------------------------------ */ diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/component/LifeCycleListenerTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/component/LifeCycleListenerTest.java index f45463b5196..7a7a915ccf6 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/component/LifeCycleListenerTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/component/LifeCycleListenerTest.java @@ -135,7 +135,7 @@ public class LifeCycleListenerTest lifecycle.stop(); assertFalse("The stopping event occurred", listener.stopping); } - private class TestLifeCycle extends AbstractLifeCycle + private static class TestLifeCycle extends AbstractLifeCycle { Exception cause; diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/log/CapturingJULHandler.java b/jetty-util/src/test/java/org/eclipse/jetty/util/log/CapturingJULHandler.java new file mode 100644 index 00000000000..1ec646e669c --- /dev/null +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/log/CapturingJULHandler.java @@ -0,0 +1,69 @@ +package org.eclipse.jetty.util.log; + +import static org.hamcrest.Matchers.containsString; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.logging.Handler; +import java.util.logging.LogRecord; + +import org.eclipse.jetty.util.IO; +import org.junit.Assert; + +public class CapturingJULHandler extends Handler +{ + private static final String LN = System.getProperty("line.separator"); + private StringBuilder output = new StringBuilder(); + + @Override + public void publish(LogRecord record) + { + StringBuilder buf = new StringBuilder(); + buf.append(record.getLevel().getName()).append("|"); + buf.append(record.getLoggerName()).append("|"); + buf.append(record.getMessage()); + + output.append(buf); + if (record.getMessage().length() > 0) + { + output.append(LN); + } + + if (record.getThrown() != null) + { + StringWriter sw = new StringWriter(128); + PrintWriter capture = new PrintWriter(sw); + record.getThrown().printStackTrace(capture); + capture.flush(); + output.append(sw.toString()); + IO.close(capture); + } + } + + public void clear() + { + output.setLength(0); + } + + @Override + public void flush() + { + /* do nothing */ + } + + @Override + public void close() throws SecurityException + { + /* do nothing */ + } + + public void dump() + { + System.out.println(output); + } + + public void assertContainsLine(String line) + { + Assert.assertThat(output.toString(),containsString(line)); + } +} diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/log/JavaUtilLogTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/log/JavaUtilLogTest.java new file mode 100644 index 00000000000..244b1e1306e --- /dev/null +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/log/JavaUtilLogTest.java @@ -0,0 +1,224 @@ +package org.eclipse.jetty.util.log; + +import static org.hamcrest.Matchers.is; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogManager; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class JavaUtilLogTest +{ + private static Handler[] originalHandlers; + private static CapturingJULHandler jul; + + @BeforeClass + public static void setJUL() + { + LogManager lmgr = LogManager.getLogManager(); + java.util.logging.Logger root = lmgr.getLogger(""); + // Remember original handlers + originalHandlers = root.getHandlers(); + // Remove original handlers + for (Handler existing : originalHandlers) + { + root.removeHandler(existing); + } + // Set test/capturing handler + jul = new CapturingJULHandler(); + root.addHandler(jul); + } + + @AfterClass + public static void restoreJUL() + { + LogManager lmgr = LogManager.getLogManager(); + java.util.logging.Logger root = lmgr.getLogger(""); + // Remove test handlers + for (Handler existing : root.getHandlers()) + { + root.removeHandler(existing); + } + // Restore original handlers + for (Handler original : originalHandlers) + { + root.addHandler(original); + } + } + + @Test + public void testNamedLogger() + { + jul.clear(); + JavaUtilLog log = new JavaUtilLog("test"); + log.info("Info test"); + + jul.assertContainsLine("INFO|test|Info test"); + + JavaUtilLog loglong = new JavaUtilLog("test.a.long.name"); + loglong.info("Long test"); + + jul.assertContainsLine("INFO|test.a.long.name|Long test"); + } + + @Test + public void testDebugOutput() + { + jul.clear(); + + // Common Throwable (for test) + Throwable th = new Throwable("Message"); + + // Capture raw string form + StringWriter tout = new StringWriter(); + th.printStackTrace(new PrintWriter(tout)); + String ths = tout.toString(); + + // Tests + JavaUtilLog log = new JavaUtilLog("test.de.bug"); + setJulLevel("test.de.bug",Level.FINE); + + log.debug("Simple debug"); + log.debug("Debug with {} parameter",1); + log.debug("Debug with {} {} parameters", 2, "spiffy"); + log.debug("Debug with throwable", th); + log.debug(th); + + // jul.dump(); + + jul.assertContainsLine("FINE|test.de.bug|Simple debug"); + jul.assertContainsLine("FINE|test.de.bug|Debug with 1 parameter"); + jul.assertContainsLine("FINE|test.de.bug|Debug with 2 spiffy parameters"); + jul.assertContainsLine("FINE|test.de.bug|Debug with throwable"); + jul.assertContainsLine(ths); + } + + @Test + public void testInfoOutput() + { + jul.clear(); + + // Common Throwable (for test) + Throwable th = new Throwable("Message"); + + // Capture raw string form + StringWriter tout = new StringWriter(); + th.printStackTrace(new PrintWriter(tout)); + String ths = tout.toString(); + + // Tests + JavaUtilLog log = new JavaUtilLog("test.in.fo"); + setJulLevel("test.in.fo",Level.INFO); + + log.info("Simple info"); + log.info("Info with {} parameter",1); + log.info("Info with {} {} parameters", 2, "spiffy"); + log.info("Info with throwable", th); + log.info(th); + + // jul.dump(); + + jul.assertContainsLine("INFO|test.in.fo|Simple info"); + jul.assertContainsLine("INFO|test.in.fo|Info with 1 parameter"); + jul.assertContainsLine("INFO|test.in.fo|Info with 2 spiffy parameters"); + jul.assertContainsLine("INFO|test.in.fo|Info with throwable"); + jul.assertContainsLine(ths); + } + + @Test + public void testWarnOutput() + { + jul.clear(); + + // Common Throwable (for test) + Throwable th = new Throwable("Message"); + + // Capture raw string form + StringWriter tout = new StringWriter(); + th.printStackTrace(new PrintWriter(tout)); + String ths = tout.toString(); + + // Tests + JavaUtilLog log = new JavaUtilLog("test.wa.rn"); + setJulLevel("test.wa.rn",Level.WARNING); + + log.warn("Simple warn"); + log.warn("Warn with {} parameter",1); + log.warn("Warn with {} {} parameters", 2, "spiffy"); + log.warn("Warn with throwable", th); + log.warn(th); + + // jul.dump(); + + jul.assertContainsLine("WARNING|test.wa.rn|Simple warn"); + jul.assertContainsLine("WARNING|test.wa.rn|Warn with 1 parameter"); + jul.assertContainsLine("WARNING|test.wa.rn|Warn with 2 spiffy parameters"); + jul.assertContainsLine("WARNING|test.wa.rn|Warn with throwable"); + jul.assertContainsLine(ths); + } + + @Test + public void testFormattingWithNulls() + { + jul.clear(); + + JavaUtilLog log = new JavaUtilLog("test.nu.ll"); + setJulLevel("test.nu.ll",Level.INFO); + + log.info("Testing info(msg,null,null) - {} {}","arg0","arg1"); + log.info("Testing info(msg,null,null) - {}/{}",null,null); + log.info("Testing info(msg,null,null) > {}",null,null); + log.info("Testing info(msg,null,null)",null,null); + log.info(null,"Testing","info(null,arg0,arg1)"); + log.info(null,null,null); + + jul.dump(); + + jul.assertContainsLine("INFO|test.nu.ll|Testing info(msg,null,null) - null/null"); + jul.assertContainsLine("INFO|test.nu.ll|Testing info(msg,null,null) > null null"); + jul.assertContainsLine("INFO|test.nu.ll|Testing info(msg,null,null) null null"); + jul.assertContainsLine("INFO|test.nu.ll|null Testing info(null,arg0,arg1)"); + jul.assertContainsLine("INFO|test.nu.ll|null null null"); + } + + @Test + public void testIsDebugEnabled() { + JavaUtilLog log = new JavaUtilLog("test.legacy"); + + setJulLevel("test.legacy",Level.ALL); + Assert.assertThat("log.level(all).isDebugEnabled", log.isDebugEnabled(), is(true)); + + setJulLevel("test.legacy",Level.FINEST); + Assert.assertThat("log.level(finest).isDebugEnabled", log.isDebugEnabled(), is(true)); + + setJulLevel("test.legacy",Level.FINER); + Assert.assertThat("log.level(finer).isDebugEnabled", log.isDebugEnabled(), is(true)); + + setJulLevel("test.legacy",Level.FINE); + Assert.assertThat("log.level(fine).isDebugEnabled", log.isDebugEnabled(), is(true)); + + setJulLevel("test.legacy",Level.INFO); + Assert.assertThat("log.level(info).isDebugEnabled", log.isDebugEnabled(), is(false)); + + setJulLevel("test.legacy",Level.WARNING); + Assert.assertThat("log.level(warn).isDebugEnabled", log.isDebugEnabled(), is(false)); + + log.setDebugEnabled(true); + Assert.assertThat("log.isDebugEnabled", log.isDebugEnabled(), is(true)); + + log.setDebugEnabled(false); + Assert.assertThat("log.isDebugEnabled", log.isDebugEnabled(), is(false)); + } + + private void setJulLevel(String name, Level lvl) + { + java.util.logging.Logger log = java.util.logging.Logger.getLogger(name); + log.setLevel(lvl); + } +} diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/log/LogTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/log/LogTest.java index d4b90db6e04..e5322ca63b9 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/log/LogTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/log/LogTest.java @@ -13,165 +13,71 @@ package org.eclipse.jetty.util.log; -import static org.junit.Assert.*; - -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; - -import junit.framework.Assert; +import static org.hamcrest.Matchers.is; import org.junit.AfterClass; +import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; - public class LogTest { - static PrintStream _orig= System.err; - static ByteArrayOutputStream _out = new ByteArrayOutputStream(); - static PrintStream _pout = new PrintStream(_out); - + private static Logger originalLogger; + @SuppressWarnings("deprecation") @BeforeClass - public static void setUp() + public static void rememberOriginalLogger() { - System.setErr(_pout); + originalLogger = Log.getLog(); } - + @AfterClass - public static void tearDown() + public static void restoreOriginalLogger() { - System.setErr(_orig); - } - - private void logNotContains(String text) - { - _pout.flush(); - String err = _out.toString(); - _out.reset(); - - if (err.indexOf(text)<0) - return; - - _orig.println("FAIL '"+text+"' in '"+err+"'"); - - assertTrue(false); - } - - private void logContains(String text) - { - _pout.flush(); - String err = _out.toString(); - _out.reset(); - - err = err.replaceAll("\r\n","\n"); - text = text.replaceAll("\r\n","\n"); - - if (err.indexOf(text)>=0) - return; - - _orig.println("FAIL '"+text+"' not in '"+err+"'"); - assertTrue(false); + Log.setLog(originalLogger); } @Test - public void testStdErrLogFormat() + public void testDefaultLogging() { - StdErrLog log = new StdErrLog(LogTest.class.getName()); + Logger log = Log.getLogger(LogTest.class); + log.info("Test default logging"); + } - log.info("testing:{},{}","test","format"); - logContains("INFO:oejul.LogTest:testing:test,format"); - - log.info("testing:{}","test","format"); - logContains("INFO:oejul.LogTest:testing:test format"); - - log.info("testing","test","format"); - logContains("INFO:oejul.LogTest:testing test format"); - - log.info("testing:{},{}","test",null); - logContains("INFO:oejul.LogTest:testing:test,null"); - - log.info("testing {} {}",null,null); - logContains("INFO:oejul.LogTest:testing null null"); - - log.info("testing:{}",null,null); - logContains("INFO:oejul.LogTest:testing:null"); - - log.info("testing",null,null); - logContains("INFO:oejul.LogTest:testing"); + // @Test + public void testNamedLogNamed_StdErrLog() + { + Log.setLog(new StdErrLog()); + + assertNamedLogging(Red.class); + assertNamedLogging(Blue.class); + assertNamedLogging(Green.class); + } + + @Test + public void testNamedLogNamed_JUL() + { + Log.setLog(new JavaUtilLog()); + + assertNamedLogging(Red.class); + assertNamedLogging(Blue.class); + assertNamedLogging(Green.class); + } + + @Test + public void testNamedLogNamed_Slf4J() throws Exception + { + Log.setLog(new Slf4jLog()); + + assertNamedLogging(Red.class); + assertNamedLogging(Blue.class); + assertNamedLogging(Green.class); } @SuppressWarnings("deprecation") - @Test - public void testStdErrLogDebug() + private void assertNamedLogging(Class clazz) { - StdErrLog log = new StdErrLog("xxx"); - - log.setLevel(StdErrLog.LEVEL_DEBUG); - log.debug("testing {} {}","test","debug"); - logContains("DBUG:xxx:testing test debug"); - - log.info("testing {} {}","test","info"); - logContains("INFO:xxx:testing test info"); - - log.warn("testing {} {}","test","warn"); - logContains("WARN:xxx:testing test warn"); - - log.setLevel(StdErrLog.LEVEL_INFO); - log.debug("YOU SHOULD NOT SEE THIS!",null,null); - logNotContains("YOU SHOULD NOT SEE THIS!"); - - // Test for backward compat with old (now deprecated) method - log.setDebugEnabled(true); - log.debug("testing {} {}","test","debug-deprecated"); - logContains("DBUG:xxx:testing test debug-deprecated"); - - log.setDebugEnabled(false); - log.debug("testing {} {}","test","debug-deprecated-false"); - logNotContains("DBUG:xxx:testing test debug-depdeprecated-false"); - } - - @Test - public void testStdErrLogName() - { - StdErrLog log = new StdErrLog("test"); - log.setPrintLongNames(true); - Assert.assertEquals("test",log.getName()); - - Logger next=log.getLogger("next"); - - Assert.assertEquals("test.next",next.getName()); - - next.info("testing {} {}","next","info"); - logContains(":test.next:testing next info"); - } - - @Test - public void testStdErrThrowable() - { - Throwable th = new Throwable("Message"); - - th.printStackTrace(); - _pout.flush(); - String ths = _out.toString(); - _out.reset(); - - - StdErrLog log = new StdErrLog("test"); - log.warn("ex",th); - - logContains(ths); - - th = new Throwable("Message with \033 escape"); - - log.warn("ex",th); - logNotContains("Message with \033 escape"); - log.info(th.toString()); - logNotContains("Message with \033 escape"); - - log.warn("ex",th); - logContains("Message with ? escape"); - log.info(th.toString()); - logContains("Message with ? escape"); + Logger lc = Log.getLogger(clazz); + Assert.assertThat("Named logging (impl=" + Log.getLog().getClass().getName() + ")",lc.getName(),is(clazz.getName())); } } diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/log/NamedLogTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/log/NamedLogTest.java index 67869ce1a24..dc1a86e84a1 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/log/NamedLogTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/log/NamedLogTest.java @@ -1,43 +1,24 @@ package org.eclipse.jetty.util.log; -import static org.hamcrest.Matchers.*; - -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; +import org.junit.AfterClass; +import org.junit.BeforeClass; import org.junit.Test; public class NamedLogTest { - private PrintStream orig; - private ByteArrayOutputStream logstream; - private PrintStream perr; - private Logger origLogger; + private static Logger originalLogger; - @Before - public void setUp() + @SuppressWarnings("deprecation") + @BeforeClass + public static void rememberOriginalLogger() { - origLogger = Log.getRootLogger(); - - orig = System.err; - logstream = new ByteArrayOutputStream(); - perr = new PrintStream(logstream); - System.setErr(perr); - - StdErrLog logger = new StdErrLog(); - Log.setLog(logger); + originalLogger = Log.getLog(); } - @After - public void tearDown() + @AfterClass + public static void restoreOriginalLogger() { - System.out.println(logstream.toString()); - System.setErr(orig); - - Log.setLog(origLogger); + Log.setLog(originalLogger); } @Test @@ -46,29 +27,32 @@ public class NamedLogTest Red red = new Red(); Green green = new Green(); Blue blue = new Blue(); - - setLoggerOptions(Red.class); - setLoggerOptions(Green.class); - setLoggerOptions(Blue.class); + + StdErrCapture output = new StdErrCapture(); + + setLoggerOptions(Red.class,output); + setLoggerOptions(Green.class,output); + setLoggerOptions(Blue.class,output); red.generateLogs(); green.generateLogs(); blue.generateLogs(); - String rawlog = logstream.toString(); - - Assert.assertThat(rawlog,containsString(Red.class.getName())); - Assert.assertThat(rawlog,containsString(Green.class.getName())); - Assert.assertThat(rawlog,containsString(Blue.class.getName())); + output.assertContains(Red.class.getName()); + output.assertContains(Green.class.getName()); + output.assertContains(Blue.class.getName()); } - private void setLoggerOptions(Class clazz) + private void setLoggerOptions(Class clazz, StdErrCapture output) { Logger logger = Log.getLogger(clazz); logger.setDebugEnabled(true); - - if(logger instanceof StdErrLog) { - ((StdErrLog)logger).setPrintLongNames(true); + + if (logger instanceof StdErrLog) + { + StdErrLog sel = (StdErrLog)logger; + sel.setPrintLongNames(true); + output.capture(sel); } } } diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/log/Slf4jHelper.java b/jetty-util/src/test/java/org/eclipse/jetty/util/log/Slf4jHelper.java new file mode 100644 index 00000000000..10b2d02c205 --- /dev/null +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/log/Slf4jHelper.java @@ -0,0 +1,42 @@ +package org.eclipse.jetty.util.log; + +import java.io.File; +import java.io.FileFilter; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; + +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.junit.Assume; + +public final class Slf4jHelper +{ + public static ClassLoader createTestClassLoader(ClassLoader parentClassLoader) throws MalformedURLException + { + File testJarDir = MavenTestingUtils.getTargetFile("test-jars"); + Assume.assumeTrue(testJarDir.exists()); // trigger @Ignore if dir not there + + File jarfiles[] = testJarDir.listFiles(new FileFilter() + { + public boolean accept(File path) + { + if (!path.isFile()) + { + return false; + } + return path.getName().endsWith(".jar"); + } + }); + + Assume.assumeTrue(jarfiles.length > 0); // trigger @Ignore if no jar files. + + URL urls[] = new URL[jarfiles.length]; + for (int i = 0; i < jarfiles.length; i++) + { + urls[i] = jarfiles[i].toURI().toURL(); + // System.out.println("Adding test-jar => " + urls[i]); + } + + return new URLClassLoader(urls,parentClassLoader); + } +} diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/log/StdErrCapture.java b/jetty-util/src/test/java/org/eclipse/jetty/util/log/StdErrCapture.java new file mode 100644 index 00000000000..b9b536db806 --- /dev/null +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/log/StdErrCapture.java @@ -0,0 +1,46 @@ +package org.eclipse.jetty.util.log; + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.not; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import org.junit.Assert; + +public class StdErrCapture +{ + private ByteArrayOutputStream test; + private PrintStream err; + + public StdErrCapture(StdErrLog log) + { + this(); + log.setStdErrStream(err); + } + + public StdErrCapture() + { + test = new ByteArrayOutputStream(); + err = new PrintStream(test); + } + + public void capture(StdErrLog log) + { + log.setStdErrStream(err); + } + + public void assertContains(String expectedString) + { + err.flush(); + String output = new String(test.toByteArray()); + Assert.assertThat(output,containsString(expectedString)); + } + + public void assertNotContains(String unexpectedString) + { + err.flush(); + String output = new String(test.toByteArray()); + Assert.assertThat(output,not(containsString(unexpectedString))); + } +} diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/log/StdErrLogTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/log/StdErrLogTest.java index 12b15ca0b1b..6a0e8e2b622 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/log/StdErrLogTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/log/StdErrLogTest.java @@ -13,10 +13,13 @@ package org.eclipse.jetty.util.log; -import static org.hamcrest.Matchers.*; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; import java.io.ByteArrayOutputStream; import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.util.Properties; @@ -28,6 +31,104 @@ import org.junit.Test; */ public class StdErrLogTest { + @Test + public void testStdErrLogFormat() throws UnsupportedEncodingException + { + StdErrLog log = new StdErrLog(LogTest.class.getName()); + StdErrCapture output = new StdErrCapture(log); + + log.info("testing:{},{}","test","format1"); + log.info("testing:{}","test","format2"); + log.info("testing","test","format3"); + log.info("testing:{},{}","test",null); + log.info("testing {} {}",null,null); + log.info("testing:{}",null,null); + log.info("testing",null,null); + + output.assertContains("INFO:oejul.LogTest:testing:test,format1"); + output.assertContains("INFO:oejul.LogTest:testing:test,format1"); + output.assertContains("INFO:oejul.LogTest:testing:test format2"); + output.assertContains("INFO:oejul.LogTest:testing test format3"); + output.assertContains("INFO:oejul.LogTest:testing:test,null"); + output.assertContains("INFO:oejul.LogTest:testing null null"); + output.assertContains("INFO:oejul.LogTest:testing:null"); + output.assertContains("INFO:oejul.LogTest:testing"); + } + + @Test + public void testStdErrLogDebug() + { + StdErrLog log = new StdErrLog("xxx"); + StdErrCapture output = new StdErrCapture(log); + + log.setLevel(StdErrLog.LEVEL_DEBUG); + log.debug("testing {} {}","test","debug"); + log.info("testing {} {}","test","info"); + log.warn("testing {} {}","test","warn"); + log.setLevel(StdErrLog.LEVEL_INFO); + log.debug("YOU SHOULD NOT SEE THIS!",null,null); + + // Test for backward compat with old (now deprecated) method + log.setDebugEnabled(true); + log.debug("testing {} {}","test","debug-deprecated"); + + log.setDebugEnabled(false); + log.debug("testing {} {}","test","debug-deprecated-false"); + + output.assertContains("DBUG:xxx:testing test debug"); + output.assertContains("INFO:xxx:testing test info"); + output.assertContains("WARN:xxx:testing test warn"); + output.assertNotContains("YOU SHOULD NOT SEE THIS!"); + output.assertContains("DBUG:xxx:testing test debug-deprecated"); + output.assertNotContains("DBUG:xxx:testing test debug-depdeprecated-false"); + } + + @Test + public void testStdErrLogName() + { + StdErrLog log = new StdErrLog("test"); + log.setPrintLongNames(true); + StdErrCapture output = new StdErrCapture(log); + + Assert.assertThat("Log.name", log.getName(), is("test")); + Logger next=log.getLogger("next"); + Assert.assertThat("Log.name(child)", next.getName(), is("test.next")); + next.info("testing {} {}","next","info"); + + output.assertContains(":test.next:testing next info"); + } + + @Test + public void testStdErrThrowable() + { + // Common Throwable (for test) + Throwable th = new Throwable("Message"); + + // Capture raw string form + StringWriter tout = new StringWriter(); + th.printStackTrace(new PrintWriter(tout)); + String ths = tout.toString(); + + // Start test + StdErrLog log = new StdErrLog("test"); + StdErrCapture output = new StdErrCapture(log); + + log.warn("ex",th); + output.assertContains(ths); + + th = new Throwable("Message with \033 escape"); + + log.warn("ex",th); + output.assertNotContains("Message with \033 escape"); + log.info(th.toString()); + output.assertNotContains("Message with \033 escape"); + + log.warn("ex",th); + output.assertContains("Message with ? escape"); + log.info(th.toString()); + output.assertContains("Message with ? escape"); + } + /** * Test to make sure that using a Null parameter on parameterized messages does not result in a NPE */ @@ -78,6 +179,30 @@ public class StdErrLogTest Assert.assertEquals("Default Logging Level",StdErrLog.LEVEL_INFO,StdErrLog.getLoggingLevel(props,StdErrLogTest.class.getName())); } + @Test + public void testGetLoggingLevel_Bad() + { + Properties props = new Properties(); + props.setProperty("log.LEVEL", "WARN"); + props.setProperty("org.eclipse.jetty.bad.LEVEL","FRUIT"); + + // Default Level (because of bad level value) + Assert.assertEquals("Bad Logging Level",StdErrLog.LEVEL_WARN,StdErrLog.getLoggingLevel(props,"org.eclipse.jetty.bad")); + } + + @Test + public void testGetLoggingLevel_Lowercase() + { + Properties props = new Properties(); + props.setProperty("log.LEVEL", "warn"); + props.setProperty("org.eclipse.jetty.util.LEVEL","info"); + + // Default Level + Assert.assertEquals("Lowercase Level",StdErrLog.LEVEL_WARN,StdErrLog.getLoggingLevel(props,"org.eclipse.jetty")); + // Specific Level + Assert.assertEquals("Lowercase Level",StdErrLog.LEVEL_INFO,StdErrLog.getLoggingLevel(props,"org.eclipse.jetty.util")); + } + @Test public void testGetLoggingLevel_Root() { @@ -157,11 +282,9 @@ public class StdErrLogTest public void testWarnFiltering() throws UnsupportedEncodingException { StdErrLog log = new StdErrLog(StdErrLogTest.class.getName()); - log.setHideStacks(true); - - ByteArrayOutputStream test = new ByteArrayOutputStream(); - PrintStream err = new PrintStream(test); - log.setStdErrStream(err); + log.setHideStacks(false); + + StdErrCapture output = new StdErrCapture(log); // Start with default level log.warn("See Me"); @@ -174,12 +297,19 @@ public class StdErrLogTest log.setLevel(StdErrLog.LEVEL_WARN); log.warn("Cheer Me"); + log.warn("", new Throwable("out of focus")); + log.warn(new Throwable("scene lost")); + // Validate Output - String output = new String(test.toByteArray(),"UTF-8"); - System.err.print(output); - Assert.assertThat(output,containsString("See Me")); - Assert.assertThat(output,containsString("Hear Me")); - Assert.assertThat(output,containsString("Cheer Me")); + // System.err.print(output); + output.assertContains("See Me"); + output.assertContains("Hear Me"); + output.assertContains("Cheer Me"); + + // Validate Stack Traces + output.assertContains(".StdErrLogTest:"); + output.assertContains("java.lang.Throwable: out of focus"); + output.assertContains("java.lang.Throwable: scene lost"); } /** @@ -191,11 +321,9 @@ public class StdErrLogTest public void testInfoFiltering() throws UnsupportedEncodingException { StdErrLog log = new StdErrLog(StdErrLogTest.class.getName()); - log.setHideStacks(true); + log.setHideStacks(false); - ByteArrayOutputStream test = new ByteArrayOutputStream(); - PrintStream err = new PrintStream(test); - log.setStdErrStream(err); + StdErrCapture output = new StdErrCapture(log); // Normal/Default behavior log.info("I will not buy"); @@ -208,17 +336,27 @@ public class StdErrLogTest log.setLevel(StdErrLog.LEVEL_ALL); log.info("it is scratched."); + log.info("", new Throwable("out of focus")); + log.info(new Throwable("scene lost")); + // Level Warn log.setLevel(StdErrLog.LEVEL_WARN); log.info("sorry?"); + log.info("", new Throwable("on editing room floor")); // Validate Output - String output = new String(test.toByteArray(),"UTF-8"); - System.err.print(output); - Assert.assertThat(output,containsString("I will not buy")); - Assert.assertThat(output,containsString("this record")); - Assert.assertThat(output,containsString("it is scratched.")); - Assert.assertThat(output,not(containsString("sorry?"))); + output.assertContains("I will not buy"); + output.assertContains("this record"); + output.assertContains("it is scratched."); + output.assertNotContains("sorry?"); + + // Validate Stack Traces + output.assertNotContains(""); + output.assertNotContains("on editing room floor"); + + output.assertContains(".StdErrLogTest:"); + output.assertContains("java.lang.Throwable: out of focus"); + output.assertContains("java.lang.Throwable: scene lost"); } /** @@ -232,16 +370,18 @@ public class StdErrLogTest StdErrLog log = new StdErrLog(StdErrLogTest.class.getName()); log.setHideStacks(true); - ByteArrayOutputStream test = new ByteArrayOutputStream(); - PrintStream err = new PrintStream(test); - log.setStdErrStream(err); + StdErrCapture output = new StdErrCapture(log); // Normal/Default behavior log.debug("Tobacconist"); + log.debug("", new Throwable("on editing room floor")); // Level Debug log.setLevel(StdErrLog.LEVEL_DEBUG); log.debug("my hovercraft is"); + + log.debug("", new Throwable("out of focus")); + log.debug(new Throwable("scene lost")); // Level All log.setLevel(StdErrLog.LEVEL_ALL); @@ -252,12 +392,19 @@ public class StdErrLogTest log.debug("what?"); // Validate Output - String output = new String(test.toByteArray(),"UTF-8"); - System.err.print(output); - Assert.assertThat(output,not(containsString("Tobacconist"))); - Assert.assertThat(output,containsString("my hovercraft is")); - Assert.assertThat(output,containsString("full of eels.")); - Assert.assertThat(output,not(containsString("what?"))); + // System.err.print(output); + output.assertNotContains("Tobacconist"); + output.assertContains("my hovercraft is"); + output.assertContains("full of eels."); + output.assertNotContains("what?"); + + // Validate Stack Traces + output.assertNotContains(""); + output.assertNotContains("on editing room floor"); + + output.assertContains(".StdErrLogTest:"); + output.assertContains("java.lang.Throwable: out of focus"); + output.assertContains("java.lang.Throwable: scene lost"); } /** @@ -271,9 +418,7 @@ public class StdErrLogTest StdErrLog log = new StdErrLog(StdErrLogTest.class.getName()); log.setHideStacks(true); - ByteArrayOutputStream test = new ByteArrayOutputStream(); - PrintStream err = new PrintStream(test); - log.setStdErrStream(err); + StdErrCapture output = new StdErrCapture(log); // Normal/Default behavior log.ignore(new Throwable("IGNORE ME")); @@ -281,16 +426,222 @@ public class StdErrLogTest // Show Ignored log.setLevel(StdErrLog.LEVEL_ALL); log.ignore(new Throwable("Don't ignore me")); - + // Set to Debug level log.setLevel(StdErrLog.LEVEL_DEBUG); log.ignore(new Throwable("Debug me")); // Validate Output + // System.err.print(output); + output.assertNotContains("IGNORE ME"); + output.assertContains("Don't ignore me"); + output.assertNotContains("Debug me"); + } + + @Test + public void testIsDebugEnabled() { + StdErrLog log = new StdErrLog(StdErrLogTest.class.getName()); + log.setHideStacks(true); + + log.setLevel(StdErrLog.LEVEL_ALL); + Assert.assertThat("log.level(all).isDebugEnabled", log.isDebugEnabled(), is(true)); + + log.setLevel(StdErrLog.LEVEL_DEBUG); + Assert.assertThat("log.level(debug).isDebugEnabled", log.isDebugEnabled(), is(true)); + + log.setLevel(StdErrLog.LEVEL_INFO); + Assert.assertThat("log.level(info).isDebugEnabled", log.isDebugEnabled(), is(false)); + + log.setLevel(StdErrLog.LEVEL_WARN); + Assert.assertThat("log.level(warn).isDebugEnabled", log.isDebugEnabled(), is(false)); + } + + @Test + public void testSetGetLevel() + { + StdErrLog log = new StdErrLog(StdErrLogTest.class.getName()); + log.setHideStacks(true); + + log.setLevel(StdErrLog.LEVEL_ALL); + Assert.assertThat("log.level(all).getLevel()", log.getLevel(), is(StdErrLog.LEVEL_ALL)); + + log.setLevel(StdErrLog.LEVEL_DEBUG); + Assert.assertThat("log.level(debug).getLevel()", log.getLevel(), is(StdErrLog.LEVEL_DEBUG)); + + log.setLevel(StdErrLog.LEVEL_INFO); + Assert.assertThat("log.level(info).getLevel()", log.getLevel(), is(StdErrLog.LEVEL_INFO)); + + log.setLevel(StdErrLog.LEVEL_WARN); + Assert.assertThat("log.level(warn).getLevel()", log.getLevel(), is(StdErrLog.LEVEL_WARN)); + } + + @Test + public void testGetChildLogger_Simple() + { + String baseName = "jetty"; + StdErrLog log = new StdErrLog(baseName); + log.setHideStacks(true); + + Assert.assertThat("Logger.name", log.getName(), is("jetty")); + + Logger log2 = log.getLogger("child"); + Assert.assertThat("Logger.child.name", log2.getName(), is("jetty.child")); + } + + @Test + public void testGetChildLogger_Deep() + { + String baseName = "jetty"; + StdErrLog log = new StdErrLog(baseName); + log.setHideStacks(true); + + Assert.assertThat("Logger.name", log.getName(), is("jetty")); + + Logger log2 = log.getLogger("child.of.the.sixties"); + Assert.assertThat("Logger.child.name", log2.getName(), is("jetty.child.of.the.sixties")); + } + + @Test + public void testGetChildLogger_Null() + { + String baseName = "jetty"; + StdErrLog log = new StdErrLog(baseName); + log.setHideStacks(true); + + Assert.assertThat("Logger.name", log.getName(), is("jetty")); + + // Pass null as child reference, should return parent logger + Logger log2 = log.getLogger(null); + Assert.assertThat("Logger.child.name", log2.getName(), is("jetty")); + Assert.assertSame("Should have returned same logger", log2, log); + } + + @Test + public void testGetChildLogger_EmptyName() + { + String baseName = "jetty"; + StdErrLog log = new StdErrLog(baseName); + log.setHideStacks(true); + + Assert.assertThat("Logger.name", log.getName(), is("jetty")); + + // Pass empty name as child reference, should return parent logger + Logger log2 = log.getLogger(""); + Assert.assertThat("Logger.child.name", log2.getName(), is("jetty")); + Assert.assertSame("Should have returned same logger", log2, log); + } + + @Test + public void testGetChildLogger_EmptyNameSpaces() + { + String baseName = "jetty"; + StdErrLog log = new StdErrLog(baseName); + log.setHideStacks(true); + + Assert.assertThat("Logger.name", log.getName(), is("jetty")); + + // Pass empty name as child reference, should return parent logger + Logger log2 = log.getLogger(" "); + Assert.assertThat("Logger.child.name", log2.getName(), is("jetty")); + Assert.assertSame("Should have returned same logger", log2, log); + } + + @Test + public void testGetChildLogger_NullParent() + { + StdErrLog log = new StdErrLog(null); + + Assert.assertThat("Logger.name", log.getName(), is("")); + + Logger log2 = log.getLogger("jetty"); + Assert.assertThat("Logger.child.name", log2.getName(), is("jetty")); + Assert.assertNotSame("Should have returned same logger", log2, log); + } + + @Test + public void testToString() + { + StdErrLog log = new StdErrLog("jetty"); + + log.setLevel(StdErrLog.LEVEL_ALL); + Assert.assertThat("Logger.toString", log.toString(), is("StdErrLog:jetty:LEVEL=ALL")); + + log.setLevel(StdErrLog.LEVEL_DEBUG); + Assert.assertThat("Logger.toString", log.toString(), is("StdErrLog:jetty:LEVEL=DEBUG")); + + log.setLevel(StdErrLog.LEVEL_INFO); + Assert.assertThat("Logger.toString", log.toString(), is("StdErrLog:jetty:LEVEL=INFO")); + + log.setLevel(StdErrLog.LEVEL_WARN); + Assert.assertThat("Logger.toString", log.toString(), is("StdErrLog:jetty:LEVEL=WARN")); + + log.setLevel(99); // intentionally bogus level + Assert.assertThat("Logger.toString", log.toString(), is("StdErrLog:jetty:LEVEL=?")); + } + + @Test + public void testPrintSource() throws UnsupportedEncodingException + { + StdErrLog log = new StdErrLog("test"); + log.setLevel(StdErrLog.LEVEL_DEBUG); + log.setSource(true); + + ByteArrayOutputStream test = new ByteArrayOutputStream(); + PrintStream err = new PrintStream(test); + log.setStdErrStream(err); + + log.debug("Show me the source!"); + String output = new String(test.toByteArray(),"UTF-8"); - System.err.print(output); - Assert.assertThat(output,not(containsString("IGNORE ME"))); - Assert.assertThat(output,containsString("Don't ignore me")); - Assert.assertThat(output,not(containsString("Debug me"))); + // System.err.print(output); + + Assert.assertThat(output, containsString(".StdErrLogTest#testPrintSource(StdErrLogTest.java:")); + } + + @Test + public void testConfiguredAndSetDebugEnabled() + { + Properties props = new Properties(); + props.setProperty("org.eclipse.jetty.util.LEVEL","WARN"); + props.setProperty("org.eclipse.jetty.io.LEVEL", "WARN"); + + StdErrLog root = new StdErrLog("", props); + assertLevel(root,StdErrLog.LEVEL_INFO); // default + + StdErrLog log = (StdErrLog)root.getLogger(StdErrLogTest.class.getName()); + Assert.assertThat("Log.isDebugEnabled()", log.isDebugEnabled(), is(false)); + assertLevel(log,StdErrLog.LEVEL_WARN); // as configured + + // Boot stomp it all to debug + root.setDebugEnabled(true); + Assert.assertThat("Log.isDebugEnabled()", log.isDebugEnabled(), is(true)); + assertLevel(log,StdErrLog.LEVEL_DEBUG); // as stomped + + // Restore configured + root.setDebugEnabled(false); + Assert.assertThat("Log.isDebugEnabled()", log.isDebugEnabled(), is(false)); + assertLevel(log,StdErrLog.LEVEL_WARN); // as configured + } + + private void assertLevel(StdErrLog log, int expectedLevel) + { + Assert.assertThat("Log[" + log.getName() + "].level",levelToString(log.getLevel()),is(levelToString(expectedLevel))); + } + + private String levelToString(int level) + { + switch (level) + { + case StdErrLog.LEVEL_ALL: + return "ALL"; + case StdErrLog.LEVEL_DEBUG: + return "DEBUG"; + case StdErrLog.LEVEL_INFO: + return "INFO"; + case StdErrLog.LEVEL_WARN: + return "WARN"; + default: + return Integer.toString(level); + } } } diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ResourceTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ResourceTest.java index c8d46eff9e8..aa5c14815a2 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ResourceTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ResourceTest.java @@ -464,9 +464,22 @@ public class ResourceTest // This test is intended to run only on Windows platform assumeTrue(OS.IS_WINDOWS); + String path = __userURL.toURI().getPath().replace('/','\\')+"ResourceTest.java"; + System.err.println(path); + + Resource resource = Resource.newResource(path, false); + System.err.println(resource); + assertTrue(resource.exists()); + + /* + String uncPath = "\\\\127.0.0.1"+__userURL.toURI().getPath().replace('/','\\').replace(':','$')+"ResourceTest.java"; + System.err.println(uncPath); Resource uncResource = Resource.newResource(uncPath, false); - assertTrue(uncResource.exists()); + System.err.println(uncResource); + assertTrue(uncResource.exists()); + + */ } } diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/ssl/SslContextFactoryTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/ssl/SslContextFactoryTest.java new file mode 100644 index 00000000000..4e2c7e1861d --- /dev/null +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/ssl/SslContextFactoryTest.java @@ -0,0 +1,183 @@ +package org.eclipse.jetty.util.ssl; + +import static junit.framework.Assert.assertTrue; + +import java.io.FileInputStream; +import java.io.IOException; +import java.security.KeyStore; + +import org.eclipse.jetty.util.component.AbstractLifeCycle; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.StdErrLog; +import org.eclipse.jetty.util.resource.Resource; +import org.junit.Assert; +import org.junit.Test; + + +public class SslContextFactoryTest +{ + @Test + public void testNoTsFileKs() throws Exception + { + String keystorePath = System.getProperty("basedir",".") + "/src/test/resources/keystore"; + SslContextFactory cf = new SslContextFactory(keystorePath); + cf.setKeyStorePassword("storepwd"); + cf.setKeyManagerPassword("keypwd"); + + cf.start(); + + assertTrue(cf.getSslContext()!=null); + } + + @Test + public void testNoTsStreamKs() throws Exception + { + String keystorePath = System.getProperty("basedir",".") + "/src/test/resources/keystore"; + + SslContextFactory cf = new SslContextFactory(); + + cf.setKeyStoreInputStream(new FileInputStream(keystorePath)); + cf.setKeyStorePassword("storepwd"); + cf.setKeyManagerPassword("keypwd"); + + cf.start(); + + assertTrue(cf.getSslContext()!=null); + } + + @Test + public void testNoTsSetKs() throws Exception + { + String keystorePath = System.getProperty("basedir",".") + "/src/test/resources/keystore"; + + KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(new FileInputStream(keystorePath),"storepwd".toCharArray()); + + SslContextFactory cf = new SslContextFactory(); + cf.setKeyStore(ks); + cf.setKeyManagerPassword("keypwd"); + + cf.start(); + + assertTrue(cf.getSslContext()!=null); + } + + @Test + public void testNoTsNoKs() throws Exception + { + SslContextFactory cf = new SslContextFactory(); + cf.start(); + assertTrue(cf.getSslContext()!=null); + } + + @Test + public void testTrustAll() throws Exception + { + SslContextFactory cf = new SslContextFactory(); + cf.start(); + assertTrue(cf.getSslContext()!=null); + } + + @Test + public void testNoTsResourceKs() throws Exception + { + Resource keystoreResource = Resource.newSystemResource("keystore"); + + SslContextFactory cf = new SslContextFactory(); + cf.setKeyStoreResource(keystoreResource); + cf.setKeyStorePassword("storepwd"); + cf.setKeyManagerPassword("keypwd"); + + cf.start(); + + assertTrue(cf.getSslContext()!=null); + + } + + @Test + public void testResourceTsResourceKs() throws Exception + { + Resource keystoreResource = Resource.newSystemResource("keystore"); + Resource truststoreResource = Resource.newSystemResource("keystore"); + + SslContextFactory cf = new SslContextFactory(); + cf.setKeyStoreResource(keystoreResource); + cf.setTrustStoreResource(truststoreResource); + cf.setKeyStorePassword("storepwd"); + cf.setKeyManagerPassword("keypwd"); + cf.setTrustStorePassword("storepwd"); + + cf.start(); + + assertTrue(cf.getSslContext()!=null); + } + + @Test + public void testResourceTsResourceKsWrongPW() throws Exception + { + Resource keystoreResource = Resource.newSystemResource("keystore"); + Resource truststoreResource = Resource.newSystemResource("keystore"); + + SslContextFactory cf = new SslContextFactory(); + cf.setKeyStoreResource(keystoreResource); + cf.setTrustStoreResource(truststoreResource); + cf.setKeyStorePassword("storepwd"); + cf.setKeyManagerPassword("wrong_keypwd"); + cf.setTrustStorePassword("storepwd"); + + try + { + ((StdErrLog)Log.getLogger(AbstractLifeCycle.class)).setHideStacks(true); + cf.start(); + Assert.fail(); + } + catch(java.security.UnrecoverableKeyException e) + { + } + } + + @Test + public void testResourceTsWrongPWResourceKs() throws Exception + { + Resource keystoreResource = Resource.newSystemResource("keystore"); + Resource truststoreResource = Resource.newSystemResource("keystore"); + + SslContextFactory cf = new SslContextFactory(); + cf.setKeyStoreResource(keystoreResource); + cf.setTrustStoreResource(truststoreResource); + cf.setKeyStorePassword("storepwd"); + cf.setKeyManagerPassword("keypwd"); + cf.setTrustStorePassword("wrong_storepwd"); + + try + { + ((StdErrLog)Log.getLogger(AbstractLifeCycle.class)).setHideStacks(true); + cf.start(); + Assert.fail(); + } + catch(IOException e) + { + } + } + + @Test + public void testNoKeyConfig() throws Exception + { + SslContextFactory cf = new SslContextFactory(); + try + { + ((StdErrLog)Log.getLogger(AbstractLifeCycle.class)).setHideStacks(true); + cf.setTrustStore("/foo"); + cf.start(); + Assert.fail(); + } + catch (IllegalStateException e) + { + + } + catch (Exception e) + { + Assert.fail("Unexpected exception"); + } + } +} diff --git a/jetty-util/src/test/resources/jetty-logging.properties b/jetty-util/src/test/resources/jetty-logging.properties new file mode 100644 index 00000000000..1fdedf40df2 --- /dev/null +++ b/jetty-util/src/test/resources/jetty-logging.properties @@ -0,0 +1,2 @@ +# Setup default logging implementation for during testing +org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog \ No newline at end of file diff --git a/jetty-util/src/test/resources/keystore b/jetty-util/src/test/resources/keystore new file mode 100644 index 00000000000..b727bd0fb77 Binary files /dev/null and b/jetty-util/src/test/resources/keystore differ diff --git a/jetty-webapp/pom.xml b/jetty-webapp/pom.xml index b40f797c81b..eb1971c5043 100644 --- a/jetty-webapp/pom.xml +++ b/jetty-webapp/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 jetty-webapp diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/JettyWebXmlConfiguration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/JettyWebXmlConfiguration.java index d6cc0be137c..57df7945011 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/JettyWebXmlConfiguration.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/JettyWebXmlConfiguration.java @@ -54,12 +54,11 @@ public class JettyWebXmlConfiguration extends AbstractConfiguration //cannot configure if the _context is already started if (context.isStarted()) { - if (LOG.isDebugEnabled()){LOG.debug("Cannot configure webapp after it is started");} + LOG.debug("Cannot configure webapp after it is started"); return; } - if(LOG.isDebugEnabled()) - LOG.debug("Configuring web-jetty.xml"); + LOG.debug("Configuring web-jetty.xml"); Resource web_inf = context.getWebInf(); // handle any WEB-INF descriptors @@ -79,9 +78,8 @@ public class JettyWebXmlConfiguration extends AbstractConfiguration try { context.setServerClasses(null); - if(LOG.isDebugEnabled()) { + if(LOG.isDebugEnabled()) LOG.debug("Configure: "+jetty); - } XmlConfiguration jetty_config = (XmlConfiguration)context.getAttribute(XML_CONFIGURATION); diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java index c4d327465a6..db5ec955afa 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java @@ -26,7 +26,6 @@ import java.util.Map; import javax.servlet.ServletException; -import org.eclipse.jetty.http.security.Constraint; import org.eclipse.jetty.security.ConstraintAware; import org.eclipse.jetty.security.ConstraintMapping; import org.eclipse.jetty.security.authentication.FormAuthenticator; @@ -42,6 +41,7 @@ import org.eclipse.jetty.util.Loader; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.security.Constraint; import org.eclipse.jetty.xml.XmlParser; /** @@ -147,7 +147,8 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor break; } } - if (LOG.isDebugEnabled()) LOG.debug("ContextParam: " + name + "=" + value); + if (LOG.isDebugEnabled()) + LOG.debug("ContextParam: " + name + "=" + value); } diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/TagLibConfiguration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/TagLibConfiguration.java index 863c85c9113..5adda2233a8 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/TagLibConfiguration.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/TagLibConfiguration.java @@ -437,7 +437,8 @@ public class TagLibConfiguration extends AbstractConfiguration public void visitListener (WebAppContext context, Descriptor descriptor, XmlParser.Node node) { String className=node.getString("listener-class",false,true); - if (LOG.isDebugEnabled()) LOG.debug("listener="+className); + if (LOG.isDebugEnabled()) + LOG.debug("listener="+className); try { diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java index 35bc7bb4601..60ff124635f 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java @@ -24,6 +24,7 @@ import java.util.EventListener; import java.util.HashMap; import java.util.List; import java.util.Map; + import javax.servlet.ServletContext; import javax.servlet.http.HttpSessionActivationListener; import javax.servlet.http.HttpSessionAttributeListener; @@ -401,11 +402,11 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL if (LOG.isDebugEnabled()) { ClassLoader loader = getClassLoader(); - LOG.debug("Thread Context class loader is: " + loader); + LOG.debug("Thread Context classloader {}",loader); loader=loader.getParent(); while(loader!=null) { - LOG.debug("Parent class loader is: " + loader); + LOG.debug("Parent class loader: {} ",loader); loader=loader.getParent(); } } diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebDescriptor.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebDescriptor.java index 7507aaceab7..ccad435a463 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebDescriptor.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebDescriptor.java @@ -198,7 +198,8 @@ public class WebDescriptor extends Descriptor _metaDataComplete = Boolean.valueOf(s).booleanValue()?MetaDataComplete.True:MetaDataComplete.False; } - LOG.debug(_xml.toString()+": Calculated metadatacomplete = " + _metaDataComplete + " with version=" + version); + if (LOG.isDebugEnabled()) + LOG.debug(_xml.toString()+": Calculated metadatacomplete = " + _metaDataComplete + " with version=" + version); } public void processOrdering () diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java index 817f77b5dfa..9495f68a923 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java @@ -125,7 +125,8 @@ public class WebInfConfiguration extends AbstractConfiguration //cannot configure if the context is already started if (context.isStarted()) { - if (LOG.isDebugEnabled()){LOG.debug("Cannot configure webapp "+context+" after it is started");} + if (LOG.isDebugEnabled()) + LOG.debug("Cannot configure webapp "+context+" after it is started"); return; } diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebXmlConfiguration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebXmlConfiguration.java index a2a362827a3..63b2ae18794 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebXmlConfiguration.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebXmlConfiguration.java @@ -79,7 +79,7 @@ public class WebXmlConfiguration extends AbstractConfiguration // cannot configure if the context is already started if (context.isStarted()) { - if (LOG.isDebugEnabled()) LOG.debug("Cannot configure webapp after it is started"); + LOG.debug("Cannot configure webapp after it is started"); return; } @@ -102,7 +102,8 @@ public class WebXmlConfiguration extends AbstractConfiguration // do web.xml file Resource web = web_inf.addPath("web.xml"); if (web.exists()) return web; - LOG.debug("No WEB-INF/web.xml in " + context.getWar() + ". Serving files and default/dynamic servlets only"); + if (LOG.isDebugEnabled()) + LOG.debug("No WEB-INF/web.xml in " + context.getWar() + ". Serving files and default/dynamic servlets only"); } return null; } diff --git a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/OrderingTest.java b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/OrderingTest.java index 996a44452ff..afe91299605 100644 --- a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/OrderingTest.java +++ b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/OrderingTest.java @@ -25,10 +25,10 @@ import java.net.URL; import java.util.ArrayList; import java.util.List; -import org.junit.Test; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.webapp.Ordering.AbsoluteOrdering; import org.eclipse.jetty.webapp.Ordering.RelativeOrdering; +import org.junit.Test; /** * OrderingTest diff --git a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppClassLoaderTest.java b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppClassLoaderTest.java index 469a28b091b..ba572a6de54 100644 --- a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppClassLoaderTest.java +++ b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppClassLoaderTest.java @@ -1,5 +1,8 @@ package org.eclipse.jetty.webapp; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; @@ -9,10 +12,6 @@ import org.eclipse.jetty.util.resource.Resource; 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.assertTrue; - public class WebAppClassLoaderTest { private WebAppContext _context; diff --git a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppContextTest.java b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppContextTest.java index d2cfc895c47..359da247524 100644 --- a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppContextTest.java +++ b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppContextTest.java @@ -12,6 +12,10 @@ // ======================================================================== package org.eclipse.jetty.webapp; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + import java.io.IOException; import java.util.Arrays; @@ -27,10 +31,6 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.HandlerList; import org.junit.Test; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - public class WebAppContextTest { @Test diff --git a/jetty-websocket/pom.xml b/jetty-websocket/pom.xml index 1011cf2ebb2..cfb4b91e581 100644 --- a/jetty-websocket/pom.xml +++ b/jetty-websocket/pom.xml @@ -3,7 +3,7 @@ jetty-project org.eclipse.jetty - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 @@ -41,10 +41,15 @@ jetty-http ${project.version} - - junit - junit + org.eclipse.jetty.toolchain + jetty-test-helper + test + + + org.eclipse.jetty + jetty-servlet + ${project.version} test diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocket.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocket.java index b5efcc9de2e..ebe48263a3e 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocket.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocket.java @@ -109,7 +109,23 @@ public interface WebSocket String getProtocol(); void sendMessage(String data) throws IOException; void sendMessage(byte[] data, int offset, int length) throws IOException; + + /** + * @deprecated Use {@link #close()} + */ void disconnect(); + + /** + * Close the connection with normal close code. + */ + void close(); + + /** Close the connection with specific closeCode and message. + * @param closeCode The close code to send, or -1 for no close code + * @param message The message to send or null for no message + */ + void close(int closeCode,String message); + boolean isOpen(); /** @@ -154,12 +170,6 @@ public interface WebSocket */ public interface FrameConnection extends Connection { - /** Close the connection with specific closeCode and message. - * @param closeCode - * @param message - */ - void close(int closeCode,String message); - /** * @return The opcode of a binary message */ diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketClient.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketClient.java index 4bde98307e9..ef9520ff325 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketClient.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketClient.java @@ -320,8 +320,6 @@ public class WebSocketClient String scheme=uri.getScheme(); if (!("ws".equalsIgnoreCase(scheme) || "wss".equalsIgnoreCase(scheme))) throw new IllegalArgumentException("Bad WebSocket scheme '"+scheme+"'"); - if ("wss".equalsIgnoreCase(scheme)) - throw new IOException("wss not supported"); SocketChannel channel = SocketChannel.open(); if (_bindAddress != null) @@ -334,7 +332,7 @@ public class WebSocketClient channel.configureBlocking(false); channel.connect(address); - _factory.getSelectorManager().register( channel, holder); + _factory.getSelectorManager().register(channel, holder); return holder; } diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketClientFactory.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketClientFactory.java index 0b64d340234..5f13fecdcff 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketClientFactory.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketClientFactory.java @@ -5,43 +5,48 @@ import java.io.IOException; import java.net.ProtocolException; import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; +import java.util.Map; import java.util.Random; +import javax.net.ssl.SSLEngine; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpParser; import org.eclipse.jetty.io.AbstractConnection; +import org.eclipse.jetty.io.AsyncEndPoint; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.Buffers; import org.eclipse.jetty.io.ByteArrayBuffer; import org.eclipse.jetty.io.ConnectedEndPoint; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.SimpleBuffers; +import org.eclipse.jetty.io.nio.AsyncConnection; import org.eclipse.jetty.io.nio.SelectChannelEndPoint; import org.eclipse.jetty.io.nio.SelectorManager; +import org.eclipse.jetty.io.nio.SslConnection; import org.eclipse.jetty.util.B64Code; import org.eclipse.jetty.util.QuotedStringTokenizer; import org.eclipse.jetty.util.component.AggregateLifeCycle; -import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.util.thread.ThreadPool; - /* ------------------------------------------------------------ */ /** *

    WebSocketClientFactory contains the common components needed by multiple {@link WebSocketClient} instances * (for example, a {@link ThreadPool}, a {@link SelectorManager NIO selector}, etc).

    *

    WebSocketClients with different configurations should share the same factory to avoid to waste resources.

    *

    If a ThreadPool or MaskGen is passed in the constructor, then it is not added with {@link AggregateLifeCycle#addBean(Object)}, - * so it's lifecycle must be controlled externally. + * so it's lifecycle must be controlled externally. + * * @see WebSocketClient */ public class WebSocketClientFactory extends AggregateLifeCycle { private final static Logger __log = org.eclipse.jetty.util.log.Log.getLogger(WebSocketClientFactory.class.getName()); - private final static Random __random = new Random(); private final static ByteArrayBuffer __ACCEPT = new ByteArrayBuffer.CaseInsensitive("Sec-WebSocket-Accept"); + private SslContextFactory _sslContextFactory = new SslContextFactory(); private final ThreadPool _threadPool; private final WebSocketClientSelector _selector; private MaskGen _maskGen; @@ -53,54 +58,67 @@ public class WebSocketClientFactory extends AggregateLifeCycle */ public WebSocketClientFactory() { - _threadPool=new QueuedThreadPool(); - addBean(_threadPool); - _buffers=new WebSocketBuffers(8*1024); - addBean(_buffers); - _maskGen=new RandomMaskGen(); - addBean(_maskGen); - _selector=new WebSocketClientSelector(); - addBean(_selector); + this(new QueuedThreadPool()); } /* ------------------------------------------------------------ */ /** *

    Creates a WebSocketClientFactory with the given ThreadPool and the default configuration.

    + * * @param threadPool the ThreadPool instance to use */ public WebSocketClientFactory(ThreadPool threadPool) { - _threadPool=threadPool; - addBean(threadPool); - _buffers=new WebSocketBuffers(8*1024); - addBean(_buffers); - _maskGen=new RandomMaskGen(); - addBean(_maskGen); - _selector=new WebSocketClientSelector(); - addBean(_selector); + this(threadPool, new RandomMaskGen()); } /* ------------------------------------------------------------ */ /** - *

    Creates a WebSocketClientFactory with the specified configuration.

    + *

    Creates a WebSocketClientFactory with the given ThreadPool and the given MaskGen.

    + * * @param threadPool the ThreadPool instance to use - * @param maskGen the mask generator to use + * @param maskGen the MaskGen instance to use + */ + public WebSocketClientFactory(ThreadPool threadPool, MaskGen maskGen) + { + this(threadPool, maskGen, 8192); + } + + /* ------------------------------------------------------------ */ + + /** + *

    Creates a WebSocketClientFactory with the specified configuration.

    + * + * @param threadPool the ThreadPool instance to use + * @param maskGen the mask generator to use * @param bufferSize the read buffer size */ - public WebSocketClientFactory(ThreadPool threadPool,MaskGen maskGen,int bufferSize) + public WebSocketClientFactory(ThreadPool threadPool, MaskGen maskGen, int bufferSize) { - _threadPool=threadPool; + _threadPool = threadPool; addBean(threadPool); - _buffers=new WebSocketBuffers(bufferSize); + _buffers = new WebSocketBuffers(bufferSize); addBean(_buffers); - _maskGen=maskGen; - _selector=new WebSocketClientSelector(); + _maskGen = maskGen; + addBean(_maskGen); + _selector = new WebSocketClientSelector(); addBean(_selector); + addBean(_sslContextFactory); + } + + /* ------------------------------------------------------------ */ + /** + * @return the SslContextFactory used to configure SSL parameters + */ + public SslContextFactory getSslContextFactory() + { + return _sslContextFactory; } /* ------------------------------------------------------------ */ /** * Get the selectorManager. Used to configure the manager. + * * @return The {@link SelectorManager} instance. */ public SelectorManager getSelectorManager() @@ -109,8 +127,10 @@ public class WebSocketClientFactory extends AggregateLifeCycle } /* ------------------------------------------------------------ */ - /** Get the ThreadPool. + /** + * Get the ThreadPool. * Used to set/query the thread pool configuration. + * * @return The {@link ThreadPool} */ public ThreadPool getThreadPool() @@ -137,9 +157,9 @@ public class WebSocketClientFactory extends AggregateLifeCycle { if (isRunning()) throw new IllegalStateException(getState()); - if (removeBean(_maskGen)) - addBean(maskGen); - _maskGen=maskGen; + removeBean(_maskGen); + _maskGen = maskGen; + addBean(maskGen); } /* ------------------------------------------------------------ */ @@ -152,7 +172,7 @@ public class WebSocketClientFactory extends AggregateLifeCycle if (isRunning()) throw new IllegalStateException(getState()); removeBean(_buffers); - _buffers=new WebSocketBuffers(bufferSize); + _buffers = new WebSocketBuffers(bufferSize); addBean(_buffers); } @@ -177,24 +197,28 @@ public class WebSocketClientFactory extends AggregateLifeCycle return new WebSocketClient(this); } - /* ------------------------------------------------------------ */ - @Override - protected void doStart() throws Exception + protected SSLEngine newSslEngine(SocketChannel channel) throws IOException { - super.doStart(); - if (getThreadPool() instanceof LifeCycle && !((LifeCycle)getThreadPool()).isStarted()) - ((LifeCycle)getThreadPool()).start(); + SSLEngine sslEngine; + if (channel != null) + { + String peerHost = channel.socket().getInetAddress().getHostAddress(); + int peerPort = channel.socket().getPort(); + sslEngine = _sslContextFactory.newSslEngine(peerHost, peerPort); + } + else + { + sslEngine = _sslContextFactory.newSslEngine(); + } + sslEngine.setUseClientMode(true); + sslEngine.beginHandshake(); + + return sslEngine; } /* ------------------------------------------------------------ */ - @Override - protected void doStop() throws Exception - { - super.doStop(); - } - - /* ------------------------------------------------------------ */ - /** WebSocket Client Selector Manager + /** + * WebSocket Client Selector Manager */ class WebSocketClientSelector extends SelectorManager { @@ -205,16 +229,35 @@ public class WebSocketClientFactory extends AggregateLifeCycle } @Override - protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, final SelectionKey sKey) throws IOException + protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, final SelectionKey key) throws IOException { - return new SelectChannelEndPoint(channel,selectSet,sKey); + WebSocketClient.WebSocketFuture holder = (WebSocketClient.WebSocketFuture)key.attachment(); + int maxIdleTime = holder.getMaxIdleTime(); + if (maxIdleTime < 0) + maxIdleTime = (int)getMaxIdleTime(); + SelectChannelEndPoint result = new SelectChannelEndPoint(channel, selectSet, key, maxIdleTime); + AsyncEndPoint endPoint = result; + + // Detect if it is SSL, and wrap the connection if so + if ("wss".equals(holder.getURI().getScheme())) + { + SSLEngine sslEngine = newSslEngine(channel); + SslConnection sslConnection = new SslConnection(sslEngine, endPoint); + endPoint.setConnection(sslConnection); + endPoint = sslConnection.getSslEndPoint(); + } + + AsyncConnection connection = selectSet.getManager().newConnection(channel, endPoint, holder); + endPoint.setConnection(connection); + + return result; } @Override - protected Connection newConnection(SocketChannel channel, SelectChannelEndPoint endpoint) + public AsyncConnection newConnection(SocketChannel channel, AsyncEndPoint endpoint, Object attachment) { - WebSocketClient.WebSocketFuture holder = (WebSocketClient.WebSocketFuture) endpoint.getSelectionKey().attachment(); - return new HandshakeConnection(endpoint,holder); + WebSocketClient.WebSocketFuture holder = (WebSocketClient.WebSocketFuture)attachment; + return new HandshakeConnection(endpoint, holder); } @Override @@ -226,20 +269,20 @@ public class WebSocketClientFactory extends AggregateLifeCycle @Override protected void endPointUpgraded(ConnectedEndPoint endpoint, Connection oldConnection) { - throw new IllegalStateException(); + LOG.debug("upgrade {} -> {}", oldConnection, endpoint.getConnection()); } @Override protected void endPointClosed(SelectChannelEndPoint endpoint) { - endpoint.getConnection().closed(); + endpoint.getConnection().onClose(); } @Override protected void connectionFailed(SocketChannel channel, Throwable ex, Object attachment) { if (!(attachment instanceof WebSocketClient.WebSocketFuture)) - super.connectionFailed(channel,ex,attachment); + super.connectionFailed(channel, ex, attachment); else { __log.debug(ex); @@ -250,42 +293,40 @@ public class WebSocketClientFactory extends AggregateLifeCycle } } - /* ------------------------------------------------------------ */ - /** Handshake Connection. + /** + * Handshake Connection. * Handles the connection until the handshake succeeds or fails. */ - class HandshakeConnection extends AbstractConnection + class HandshakeConnection extends AbstractConnection implements AsyncConnection { - private final SelectChannelEndPoint _endp; + private final AsyncEndPoint _endp; private final WebSocketClient.WebSocketFuture _future; private final String _key; private final HttpParser _parser; private String _accept; private String _error; + private boolean _handshaken; - public HandshakeConnection(SelectChannelEndPoint endpoint, WebSocketClient.WebSocketFuture future) + public HandshakeConnection(AsyncEndPoint endpoint, WebSocketClient.WebSocketFuture future) { - super(endpoint,System.currentTimeMillis()); - _endp=endpoint; - _future=future; + super(endpoint, System.currentTimeMillis()); + _endp = endpoint; + _future = future; - byte[] bytes=new byte[16]; - __random.nextBytes(bytes); - _key=new String(B64Code.encode(bytes)); + byte[] bytes = new byte[16]; + new Random().nextBytes(bytes); + _key = new String(B64Code.encode(bytes)); - - Buffers buffers = new SimpleBuffers(_buffers.getBuffer(),null); - _parser=new HttpParser(buffers,_endp, - - new HttpParser.EventHandler() + Buffers buffers = new SimpleBuffers(_buffers.getBuffer(), null); + _parser = new HttpParser(buffers, _endp, new HttpParser.EventHandler() { @Override public void startResponse(Buffer version, int status, Buffer reason) throws IOException { - if (status!=101) + if (status != 101) { - _error="Bad response status "+status+" "+reason; + _error = "Bad response status " + status + " " + reason; _endp.close(); } } @@ -294,58 +335,64 @@ public class WebSocketClientFactory extends AggregateLifeCycle public void parsedHeader(Buffer name, Buffer value) throws IOException { if (__ACCEPT.equals(name)) - _accept=value.toString(); + _accept = value.toString(); } @Override public void startRequest(Buffer method, Buffer url, Buffer version) throws IOException { - if (_error==null) - _error="Bad response: "+method+" "+url+" "+version; + if (_error == null) + _error = "Bad response: " + method + " " + url + " " + version; _endp.close(); } @Override public void content(Buffer ref) throws IOException { - if (_error==null) - _error="Bad response. "+ref.length()+"B of content?"; + if (_error == null) + _error = "Bad response. " + ref.length() + "B of content?"; _endp.close(); } }); + } - String path=_future.getURI().getPath(); - if (path==null || path.length()==0) - path="/"; + private void handshake() + { + String path = _future.getURI().getPath(); + if (path == null || path.length() == 0) + path = "/"; - String origin = future.getOrigin(); + if (_future.getURI().getRawQuery() != null) + path += "?" + _future.getURI().getRawQuery(); + + String origin = _future.getOrigin(); StringBuilder request = new StringBuilder(512); - request - .append("GET ").append(path).append(" HTTP/1.1\r\n") - .append("Host: ").append(future.getURI().getHost()).append(":").append(_future.getURI().getPort()).append("\r\n") - .append("Upgrade: websocket\r\n") - .append("Connection: Upgrade\r\n") - .append("Sec-WebSocket-Key: ") - .append(_key).append("\r\n"); - - if(origin!=null) + request.append("GET ").append(path).append(" HTTP/1.1\r\n") + .append("Host: ").append(_future.getURI().getHost()).append(":") + .append(_future.getURI().getPort()).append("\r\n") + .append("Upgrade: websocket\r\n") + .append("Connection: Upgrade\r\n") + .append("Sec-WebSocket-Key: ") + .append(_key).append("\r\n"); + + if (origin != null) request.append("Origin: ").append(origin).append("\r\n"); - + request.append("Sec-WebSocket-Version: ").append(WebSocketConnectionD13.VERSION).append("\r\n"); - if (future.getProtocol()!=null) - request.append("Sec-WebSocket-Protocol: ").append(future.getProtocol()).append("\r\n"); + if (_future.getProtocol() != null) + request.append("Sec-WebSocket-Protocol: ").append(_future.getProtocol()).append("\r\n"); - if (future.getCookies()!=null && future.getCookies().size()>0) + Map cookies = _future.getCookies(); + if (cookies != null && cookies.size() > 0) { - for (String cookie : future.getCookies().keySet()) - request - .append("Cookie: ") - .append(QuotedStringTokenizer.quoteIfNeeded(cookie,HttpFields.__COOKIE_DELIM)) - .append("=") - .append(QuotedStringTokenizer.quoteIfNeeded(future.getCookies().get(cookie),HttpFields.__COOKIE_DELIM)) - .append("\r\n"); + for (String cookie : cookies.keySet()) + request.append("Cookie: ") + .append(QuotedStringTokenizer.quoteIfNeeded(cookie, HttpFields.__COOKIE_DELIM)) + .append("=") + .append(QuotedStringTokenizer.quoteIfNeeded(cookies.get(cookie), HttpFields.__COOKIE_DELIM)) + .append("\r\n"); } request.append("\r\n"); @@ -354,44 +401,58 @@ public class WebSocketClientFactory extends AggregateLifeCycle try { - Buffer handshake = new ByteArrayBuffer(request.toString(),false); - int len=handshake.length(); - if (len!=_endp.flush(handshake)) + Buffer handshake = new ByteArrayBuffer(request.toString(), false); + int len = handshake.length(); + if (len != _endp.flush(handshake)) throw new IOException("incomplete"); } - catch(IOException e) + catch (IOException e) { - future.handshakeFailed(e); + _future.handshakeFailed(e); + } + finally + { + _handshaken = true; } - } public Connection handle() throws IOException { while (_endp.isOpen() && !_parser.isComplete()) { - switch (_parser.parseAvailable()) + if (!_handshaken) + handshake(); + + if (!_parser.parseAvailable()) { - case -1: + if (_endp.isInputShutdown()) _future.handshakeFailed(new IOException("Incomplete handshake response")); - return this; - case 0: - return this; - default: - break; + return this; } } - if (_error==null) + if (_error == null) { - if (_accept==null) - _error="No Sec-WebSocket-Accept"; + if (_accept == null) + { + _error = "No Sec-WebSocket-Accept"; + } else if (!WebSocketConnectionD13.hashKey(_key).equals(_accept)) - _error="Bad Sec-WebSocket-Accept"; + { + _error = "Bad Sec-WebSocket-Accept"; + } else { - Buffer header=_parser.getHeaderBuffer(); - MaskGen maskGen=_future.getMaskGen(); - WebSocketConnectionD13 connection = new WebSocketConnectionD13(_future.getWebSocket(),_endp,_buffers,System.currentTimeMillis(),_future.getMaxIdleTime(),_future.getProtocol(),null,10,maskGen); + Buffer header = _parser.getHeaderBuffer(); + MaskGen maskGen = _future.getMaskGen(); + WebSocketConnectionD13 connection = + new WebSocketConnectionD13(_future.getWebSocket(), + _endp, + _buffers, System.currentTimeMillis(), + _future.getMaxIdleTime(), + _future.getProtocol(), + null, + WebSocketConnectionD13.VERSION, + maskGen); if (header.hasContent()) connection.fillBuffersFrom(header); @@ -407,6 +468,11 @@ public class WebSocketClientFactory extends AggregateLifeCycle return this; } + public void onInputShutdown() throws IOException + { + _endp.close(); + } + public boolean isIdle() { return false; @@ -417,9 +483,9 @@ public class WebSocketClientFactory extends AggregateLifeCycle return false; } - public void closed() + public void onClose() { - if (_error!=null) + if (_error != null) _future.handshakeFailed(new ProtocolException(_error)); else _future.handshakeFailed(new EOFException()); diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnection.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnection.java index 8a4f7f78ae3..bb3192c41f8 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnection.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnection.java @@ -8,9 +8,9 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.io.Buffer; -import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.nio.AsyncConnection; -public interface WebSocketConnection extends Connection +public interface WebSocketConnection extends AsyncConnection { void fillBuffersFrom(Buffer buffer); diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD00.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD00.java index e19e9ef0ca1..aa37a0c65ba 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD00.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD00.java @@ -18,6 +18,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Collections; import java.util.List; + import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -29,7 +30,6 @@ import org.eclipse.jetty.io.ByteArrayBuffer; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.nio.IndirectNIOBuffer; -import org.eclipse.jetty.io.nio.SelectChannelEndPoint; import org.eclipse.jetty.util.QuotedStringTokenizer; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.log.Log; @@ -43,7 +43,6 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc public final static byte LENGTH_FRAME=(byte)0x80; public final static byte SENTINEL_FRAME=(byte)0x00; - final IdleCheck _idle; final WebSocketParser _parser; final WebSocketGenerator _generator; final WebSocket _websocket; @@ -56,8 +55,6 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc throws IOException { super(endpoint,timestamp); - if (endpoint instanceof AsyncEndPoint) - ((AsyncEndPoint)endpoint).cancelIdle(); _endp.setMaxIdleTime(maxIdleTime); @@ -66,28 +63,6 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc _generator = new WebSocketGeneratorD00(buffers, _endp); _parser = new WebSocketParserD00(buffers, endpoint, new FrameHandlerD00(_websocket)); - - if (_endp instanceof SelectChannelEndPoint) - { - final SelectChannelEndPoint scep=(SelectChannelEndPoint)_endp; - scep.cancelIdle(); - _idle=new IdleCheck() - { - public void access(EndPoint endp) - { - scep.scheduleIdle(); - } - }; - scep.scheduleIdle(); - } - else - { - _idle = new IdleCheck() - { - public void access(EndPoint endp) - {} - }; - } } /* ------------------------------------------------------------ */ @@ -189,8 +164,6 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc { if (_endp.isOpen()) { - _idle.access(_endp); - if (_endp.isInputShutdown() && _generator.isBufferEmpty()) _endp.close(); else @@ -202,6 +175,12 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc return this; } + /* ------------------------------------------------------------ */ + public void onInputShutdown() throws IOException + { + // TODO + } + /* ------------------------------------------------------------ */ private void doTheHixieHixieShake() { @@ -232,7 +211,7 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc } /* ------------------------------------------------------------ */ - public void closed() + public void onClose() { _websocket.onClose(WebSocketConnectionD06.CLOSE_NORMAL,""); } @@ -246,7 +225,6 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc _generator.addFrame((byte)0,SENTINEL_FRAME,data,0,data.length); _generator.flush(); checkWriteable(); - _idle.access(_endp); } /* ------------------------------------------------------------ */ @@ -255,7 +233,6 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc _generator.addFrame((byte)0,LENGTH_FRAME,data,offset,length); _generator.flush(); checkWriteable(); - _idle.access(_endp); } /* ------------------------------------------------------------ */ @@ -278,7 +255,6 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc _generator.addFrame((byte)0,opcode,content,offset,length); _generator.flush(); checkWriteable(); - _idle.access(_endp); } /* ------------------------------------------------------------ */ @@ -289,6 +265,12 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc /* ------------------------------------------------------------ */ public void disconnect() + { + close(); + } + + /* ------------------------------------------------------------ */ + public void close() { try { @@ -358,11 +340,6 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc } } - private interface IdleCheck - { - void access(EndPoint endp); - } - public void handshake(HttpServletRequest request, HttpServletResponse response, String subprotocol) throws IOException { String uri=request.getRequestURI(); @@ -477,10 +454,6 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc ((WebSocket.OnBinaryMessage)_websocket).onMessage(array,buffer.getIndex(),buffer.length()); } } - catch(ThreadDeath th) - { - throw th; - } catch(Throwable th) { LOG.warn(th); diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD06.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD06.java index fdd7c77de8d..b9af801eb59 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD06.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD06.java @@ -18,6 +18,7 @@ import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.util.Collections; import java.util.List; + import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -27,7 +28,6 @@ import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.ByteArrayBuffer; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.io.nio.SelectChannelEndPoint; import org.eclipse.jetty.util.B64Code; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.Utf8StringBuilder; @@ -75,7 +75,6 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc private final static byte[] MAGIC; - private final IdleCheck _idle; private final WebSocketParser _parser; private final WebSocketGenerator _generator; private final WebSocket _webSocket; @@ -115,9 +114,6 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc { super(endpoint,timestamp); - if (endpoint instanceof AsyncEndPoint) - ((AsyncEndPoint)endpoint).cancelIdle(); - _endp.setMaxIdleTime(maxIdleTime); _webSocket = websocket; @@ -129,28 +125,6 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc _parser = new WebSocketParserD06(buffers, endpoint, _frameHandler,true); _protocol=protocol; - if (_endp instanceof SelectChannelEndPoint) - { - final SelectChannelEndPoint scep=(SelectChannelEndPoint)_endp; - scep.cancelIdle(); - _idle=new IdleCheck() - { - public void access(EndPoint endp) - { - scep.scheduleIdle(); - } - }; - scep.scheduleIdle(); - } - else - { - _idle = new IdleCheck() - { - public void access(EndPoint endp) - {} - }; - } - _maxTextMessageSize=buffers.getBufferSize(); _maxBinaryMessageSize=-1; } @@ -199,7 +173,6 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc { if (_endp.isOpen()) { - _idle.access(_endp); if (_closedIn && _closedOut && _generator.isBufferEmpty()) _endp.close(); else if (_endp.isInputShutdown() && !_closedIn) @@ -212,6 +185,12 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc return this; } + /* ------------------------------------------------------------ */ + public void onInputShutdown() throws IOException + { + // TODO + } + /* ------------------------------------------------------------ */ public boolean isIdle() { @@ -220,7 +199,7 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc /* ------------------------------------------------------------ */ @Override - public void idleExpired() + public void onIdleExpired(long idleForMs) { closeOut(WebSocketConnectionD06.CLOSE_NORMAL,"Idle"); } @@ -232,7 +211,7 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc } /* ------------------------------------------------------------ */ - public void closed() + public void onClose() { _webSocket.onClose(WebSocketConnectionD06.CLOSE_NORMAL,""); } @@ -327,7 +306,6 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc _generator.addFrame((byte)0x8,WebSocketConnectionD06.OP_TEXT,data,0,data.length); _generator.flush(); checkWriteable(); - _idle.access(_endp); } /* ------------------------------------------------------------ */ @@ -338,7 +316,6 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc _generator.addFrame((byte)0x8,WebSocketConnectionD06.OP_BINARY,content,offset,length); _generator.flush(); checkWriteable(); - _idle.access(_endp); } /* ------------------------------------------------------------ */ @@ -349,7 +326,6 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc _generator.addFrame(flags,opcode,content,offset,length); _generator.flush(); checkWriteable(); - _idle.access(_endp); } /* ------------------------------------------------------------ */ @@ -360,7 +336,6 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc _generator.addFrame((byte)0x8,control,data,offset,length); _generator.flush(); checkWriteable(); - _idle.access(_endp); } /* ------------------------------------------------------------ */ @@ -501,6 +476,12 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc /* ------------------------------------------------------------ */ public void disconnect() + { + close(); + } + + /* ------------------------------------------------------------ */ + public void close() { close(CLOSE_NORMAL,null); } @@ -700,10 +681,6 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc } } } - catch(ThreadDeath th) - { - throw th; - } catch(Throwable th) { LOG.warn(th); @@ -722,12 +699,6 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc } } - /* ------------------------------------------------------------ */ - private interface IdleCheck - { - void access(EndPoint endp); - } - /* ------------------------------------------------------------ */ public void handshake(HttpServletRequest request, HttpServletResponse response, String subprotocol) throws IOException { diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD08.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD08.java index 41cb3c97c18..69e6ed3e0f2 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD08.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD08.java @@ -18,6 +18,7 @@ import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.util.Collections; import java.util.List; + import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -76,7 +77,6 @@ public class WebSocketConnectionD08 extends AbstractConnection implements WebSoc } private final static byte[] MAGIC; - private final IdleCheck _idle; private final List _extensions; private final WebSocketParserD08 _parser; private final WebSocketParser.FrameHandler _inbound; @@ -129,9 +129,6 @@ public class WebSocketConnectionD08 extends AbstractConnection implements WebSoc _context=Thread.currentThread().getContextClassLoader(); - if (endpoint instanceof AsyncEndPoint) - ((AsyncEndPoint)endpoint).cancelIdle(); - _draft=draft; _endp.setMaxIdleTime(maxIdleTime); @@ -163,27 +160,6 @@ public class WebSocketConnectionD08 extends AbstractConnection implements WebSoc _protocol=protocol; - if (_endp instanceof SelectChannelEndPoint) - { - final SelectChannelEndPoint scep=(SelectChannelEndPoint)_endp; - scep.cancelIdle(); - _idle=new IdleCheck() - { - public void access(EndPoint endp) - { - scep.scheduleIdle(); - } - }; - scep.scheduleIdle(); - } - else - { - _idle = new IdleCheck() - { - public void access(EndPoint endp) - {} - }; - } } /* ------------------------------------------------------------ */ @@ -245,7 +221,6 @@ public class WebSocketConnectionD08 extends AbstractConnection implements WebSoc _generator.returnBuffer(); if (_endp.isOpen()) { - _idle.access(_endp); if (_closedIn && _closedOut && _outbound.isBufferEmpty()) _endp.close(); else if (_endp.isInputShutdown() && !_closedIn) @@ -257,6 +232,12 @@ public class WebSocketConnectionD08 extends AbstractConnection implements WebSoc return this; } + /* ------------------------------------------------------------ */ + public void onInputShutdown() throws IOException + { + // TODO + } + /* ------------------------------------------------------------ */ public boolean isIdle() { @@ -265,10 +246,9 @@ public class WebSocketConnectionD08 extends AbstractConnection implements WebSoc /* ------------------------------------------------------------ */ @Override - public void idleExpired() + public void onIdleExpired(long idleForMs) { - long idle = System.currentTimeMillis()-((SelectChannelEndPoint)_endp).getIdleTimestamp(); - closeOut(WebSocketConnectionD08.CLOSE_NORMAL,"Idle for "+idle+"ms > "+_endp.getMaxIdleTime()+"ms"); + closeOut(WebSocketConnectionD08.CLOSE_NORMAL,"Idle for "+idleForMs+"ms > "+_endp.getMaxIdleTime()+"ms"); } /* ------------------------------------------------------------ */ @@ -278,7 +258,7 @@ public class WebSocketConnectionD08 extends AbstractConnection implements WebSoc } /* ------------------------------------------------------------ */ - public void closed() + public void onClose() { final boolean closed; synchronized (this) @@ -410,7 +390,6 @@ public class WebSocketConnectionD08 extends AbstractConnection implements WebSoc byte[] data = content.getBytes(StringUtil.__UTF8); _outbound.addFrame((byte)FLAG_FIN,WebSocketConnectionD08.OP_TEXT,data,0,data.length); checkWriteable(); - _idle.access(_endp); } /* ------------------------------------------------------------ */ @@ -420,7 +399,6 @@ public class WebSocketConnectionD08 extends AbstractConnection implements WebSoc throw new IOException("closedOut "+_closeCode+":"+_closeMessage); _outbound.addFrame((byte)FLAG_FIN,WebSocketConnectionD08.OP_BINARY,content,offset,length); checkWriteable(); - _idle.access(_endp); } /* ------------------------------------------------------------ */ @@ -430,7 +408,6 @@ public class WebSocketConnectionD08 extends AbstractConnection implements WebSoc throw new IOException("closedOut "+_closeCode+":"+_closeMessage); _outbound.addFrame(flags,opcode,content,offset,length); checkWriteable(); - _idle.access(_endp); } /* ------------------------------------------------------------ */ @@ -440,7 +417,6 @@ public class WebSocketConnectionD08 extends AbstractConnection implements WebSoc throw new IOException("closedOut "+_closeCode+":"+_closeMessage); _outbound.addFrame((byte)FLAG_FIN,ctrl,data,offset,length); checkWriteable(); - _idle.access(_endp); } /* ------------------------------------------------------------ */ @@ -581,6 +557,12 @@ public class WebSocketConnectionD08 extends AbstractConnection implements WebSoc /* ------------------------------------------------------------ */ public void disconnect() + { + close(); + } + + /* ------------------------------------------------------------ */ + public void close() { close(CLOSE_NORMAL,null); } @@ -773,10 +755,6 @@ public class WebSocketConnectionD08 extends AbstractConnection implements WebSoc } } } - catch(ThreadDeath th) - { - throw th; - } catch(Throwable th) { LOG.warn(th); @@ -821,12 +799,6 @@ public class WebSocketConnectionD08 extends AbstractConnection implements WebSoc } } - /* ------------------------------------------------------------ */ - private interface IdleCheck - { - void access(EndPoint endp); - } - /* ------------------------------------------------------------ */ public void handshake(HttpServletRequest request, HttpServletResponse response, String subprotocol) throws IOException { diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD13.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD13.java index 3ca71841a9e..8dde654f892 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD13.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD13.java @@ -18,6 +18,7 @@ import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.util.Collections; import java.util.List; + import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -30,7 +31,6 @@ import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.nio.SelectChannelEndPoint; import org.eclipse.jetty.util.B64Code; import org.eclipse.jetty.util.StringUtil; -import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.Utf8Appendable; import org.eclipse.jetty.util.Utf8StringBuilder; import org.eclipse.jetty.util.log.Log; @@ -40,12 +40,34 @@ import org.eclipse.jetty.websocket.WebSocket.OnControl; import org.eclipse.jetty.websocket.WebSocket.OnFrame; import org.eclipse.jetty.websocket.WebSocket.OnTextMessage; + +/* ------------------------------------------------------------ */ +/** + *
    + *    0                   1                   2                   3
    + *    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    + *   +-+-+-+-+-------+-+-------------+-------------------------------+
    + *   |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
    + *   |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
    + *   |N|V|V|V|       |S|             |   (if payload len==126/127)   |
    + *   | |1|2|3|       |K|             |                               |
    + *   +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
    + *   |     Extended payload length continued, if payload len == 127  |
    + *   + - - - - - - - - - - - - - - - +-------------------------------+
    + *   |                               |Masking-key, if MASK set to 1  |
    + *   +-------------------------------+-------------------------------+
    + *   | Masking-key (continued)       |          Payload Data         |
    + *   +-------------------------------- - - - - - - - - - - - - - - - +
    + *   :                     Payload Data continued ...                :
    + *   + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
    + *   |                     Payload Data continued ...                |
    + *   +---------------------------------------------------------------+
    + * 
    + */ public class WebSocketConnectionD13 extends AbstractConnection implements WebSocketConnection { private static final Logger LOG = Log.getLogger(WebSocketConnectionD13.class); - private static final boolean STRICT=Boolean.getBoolean("org.eclipse.jetty.websocket.STRICT"); - private static final boolean BRUTAL=Boolean.getBoolean("org.eclipse.jetty.websocket.BRUTAL"); - + final static byte OP_CONTINUATION = 0x00; final static byte OP_TEXT = 0x01; final static byte OP_BINARY = 0x02; @@ -84,7 +106,6 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc } private final static byte[] MAGIC; - private final IdleCheck _idle; private final List _extensions; private final WebSocketParserD13 _parser; private final WebSocketGeneratorD13 _generator; @@ -134,9 +155,6 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc _context=Thread.currentThread().getContextClassLoader(); - if (endpoint instanceof AsyncEndPoint) - ((AsyncEndPoint)endpoint).cancelIdle(); - _draft=draft; _endp.setMaxIdleTime(maxIdleTime); @@ -169,28 +187,6 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc _protocol=protocol; - // TODO should these be AsyncEndPoint checks/calls? - if (_endp instanceof SelectChannelEndPoint) - { - final SelectChannelEndPoint scep=(SelectChannelEndPoint)_endp; - scep.cancelIdle(); - _idle=new IdleCheck() - { - public void access(EndPoint endp) - { - scep.scheduleIdle(); - } - }; - scep.scheduleIdle(); - } - else - { - _idle = new IdleCheck() - { - public void access(EndPoint endp) - {} - }; - } } /* ------------------------------------------------------------ */ @@ -226,18 +222,18 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc progress = flushed>0 || filled>0; - if (filled<0 || flushed<0) - { - _endp.close(); - break; - } + _endp.flush(); + + if (_endp instanceof AsyncEndPoint && ((AsyncEndPoint)_endp).hasProgressed()) + progress=true; } } catch(IOException e) { try { - _endp.close(); + if (_endp.isOpen()) + _endp.close(); } catch(IOException e2) { @@ -252,7 +248,6 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc _generator.returnBuffer(); if (_endp.isOpen()) { - _idle.access(_endp); if (_closedIn && _closedOut && _outbound.isBufferEmpty()) _endp.close(); else if (_endp.isInputShutdown() && !_closedIn) @@ -264,6 +259,13 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc return this; } + /* ------------------------------------------------------------ */ + public void onInputShutdown() throws IOException + { + if (!_closedIn) + _endp.close(); + } + /* ------------------------------------------------------------ */ public boolean isIdle() { @@ -272,10 +274,9 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc /* ------------------------------------------------------------ */ @Override - public void idleExpired() + public void onIdleExpired(long idleForMs) { - long idle = System.currentTimeMillis()-((SelectChannelEndPoint)_endp).getIdleTimestamp(); - closeOut(WebSocketConnectionD13.CLOSE_NORMAL,"Idle for "+idle+"ms > "+_endp.getMaxIdleTime()+"ms"); + closeOut(WebSocketConnectionD13.CLOSE_NORMAL,"Idle for "+idleForMs+"ms > "+_endp.getMaxIdleTime()+"ms"); } /* ------------------------------------------------------------ */ @@ -285,7 +286,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc } /* ------------------------------------------------------------ */ - public void closed() + public void onClose() { final boolean closed; synchronized (this) @@ -301,13 +302,13 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc /* ------------------------------------------------------------ */ public void closeIn(int code,String message) { - LOG.debug("ClosedIn {} {}",this,message); + LOG.debug("ClosedIn {} {} {}",this,code,message); - final boolean close; + final boolean closed_out; final boolean tell_app; synchronized (this) { - close=_closedOut; + closed_out=_closedOut; _closedIn=true; tell_app=_closeCode==0; if (tell_app) @@ -324,32 +325,21 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc } finally { - try - { - if (close) - _endp.close(); - else - closeOut(code,message); - } - catch(IOException e) - { - LOG.ignore(e); - } + if (!closed_out) + closeOut(code,message); } } /* ------------------------------------------------------------ */ public void closeOut(int code,String message) { - LOG.debug("ClosedOut {} {}",this,message); + LOG.debug("ClosedOut {} {} {}",this,code,message); - final boolean close; + final boolean closed_out; final boolean tell_app; - final boolean send_close; synchronized (this) { - close=_closedIn; - send_close=!_closedOut; + closed_out=_closedOut; _closedOut=true; tell_app=_closeCode==0; if (tell_app) @@ -368,21 +358,22 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc { try { - if (send_close) + if (!closed_out) { - if (code<=0) + // Close code 1005/1006 are never to be sent as a status over + // a Close control frame. Code<-1 also means no node. + + if (code<0 || (code == WebSocketConnectionD13.CLOSE_NO_CODE) || code==WebSocketConnectionD13.CLOSE_NO_CLOSE) + code=-1; + else if (code==0) code=WebSocketConnectionD13.CLOSE_NORMAL; + byte[] bytes = ("xx"+(message==null?"":message)).getBytes(StringUtil.__ISO_8859_1); bytes[0]=(byte)(code/0x100); bytes[1]=(byte)(code%0x100); - _outbound.addFrame((byte)FLAG_FIN,WebSocketConnectionD13.OP_CLOSE,bytes,0,bytes.length); + _outbound.addFrame((byte)FLAG_FIN,WebSocketConnectionD13.OP_CLOSE,bytes,0,code>0?bytes.length:0); _outbound.flush(); - if (close) - _endp.shutdownOutput(); } - else if (close) - _endp.close(); - } catch(IOException e) { @@ -421,7 +412,6 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc byte[] data = content.getBytes(StringUtil.__UTF8); _outbound.addFrame((byte)FLAG_FIN,WebSocketConnectionD13.OP_TEXT,data,0,data.length); checkWriteable(); - _idle.access(_endp); } /* ------------------------------------------------------------ */ @@ -431,7 +421,6 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc throw new IOException("closedOut "+_closeCode+":"+_closeMessage); _outbound.addFrame((byte)FLAG_FIN,WebSocketConnectionD13.OP_BINARY,content,offset,length); checkWriteable(); - _idle.access(_endp); } /* ------------------------------------------------------------ */ @@ -441,17 +430,16 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc throw new IOException("closedOut "+_closeCode+":"+_closeMessage); _outbound.addFrame(flags,opcode,content,offset,length); checkWriteable(); - _idle.access(_endp); } /* ------------------------------------------------------------ */ public void sendControl(byte ctrl, byte[] data, int offset, int length) throws IOException { + // TODO: section 5.5 states that control frames MUST never be length > 125 bytes and MUST NOT be fragmented if (_closedOut) throw new IOException("closedOut "+_closeCode+":"+_closeMessage); _outbound.addFrame((byte)FLAG_FIN,ctrl,data,offset,length); checkWriteable(); - _idle.access(_endp); } /* ------------------------------------------------------------ */ @@ -596,6 +584,12 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc close(CLOSE_NORMAL,null); } + /* ------------------------------------------------------------ */ + public void close() + { + close(CLOSE_NORMAL,null); + } + /* ------------------------------------------------------------ */ public void setAllowFrameFragmentation(boolean allowFragmentation) { @@ -621,6 +615,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc /* ------------------------------------------------------------ */ private class WSFrameHandler implements WebSocketParser.FrameHandler { + private static final int MAX_CONTROL_FRAME_PAYLOAD = 125; private final Utf8StringBuilder _utf8 = new Utf8StringBuilder(512); // TODO configure initial capacity private ByteArrayBuffer _aggregate; private byte _opcode=-1; @@ -628,7 +623,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc public void onFrame(final byte flags, final byte opcode, final Buffer buffer) { boolean lastFrame = isLastFrame(flags); - + synchronized(WebSocketConnectionD13.this) { // Ignore incoming after a close @@ -639,25 +634,25 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc { byte[] array=buffer.array(); - if (STRICT) + if (isControlFrame(opcode) && buffer.length()>MAX_CONTROL_FRAME_PAYLOAD) { - if (isControlFrame(opcode) && buffer.length()>125) - { - errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"Control frame too large"); - return; - } - - if ((flags&0x7)!=0) - { - errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"RSV bits set 0x"+Integer.toHexString(flags)); - return; - } - - // Ignore all frames after error close - if (_closeCode!=0 && _closeCode!=CLOSE_NORMAL && opcode!=OP_CLOSE) - return; + errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"Control frame too large: " + buffer.length() + " > " + MAX_CONTROL_FRAME_PAYLOAD); + return; } - + + // TODO: check extensions for RSV bit(s) meanings + if ((flags&0x7)!=0) + { + errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"RSV bits set 0x"+Integer.toHexString(flags)); + return; + } + + // Ignore all frames after error close + if (_closeCode!=0 && _closeCode!=CLOSE_NORMAL && opcode!=OP_CLOSE) + { + return; + } + // Deliver frame if websocket is a FrameWebSocket if (_onFrame!=null) { @@ -670,7 +665,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc if (_onControl.onControl(opcode,array,buffer.getIndex(),buffer.length())) return; } - + switch(opcode) { case WebSocketConnectionD13.OP_CONTINUATION: @@ -680,7 +675,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"Bad Continuation"); return; } - + // If text, append to the message buffer if (_onTextMessage!=null && _opcode==WebSocketConnectionD13.OP_TEXT) { @@ -726,7 +721,9 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc { LOG.debug("PING {}",this); if (!_closedOut) + { _connection.sendControl(WebSocketConnectionD13.OP_PONG,buffer.array(),buffer.getIndex(),buffer.length()); + } break; } @@ -743,8 +740,33 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc if (buffer.length()>=2) { code=(0xff&buffer.array()[buffer.getIndex()])*0x100+(0xff&buffer.array()[buffer.getIndex()+1]); + + // Validate close status codes. + if (code < WebSocketConnectionD13.CLOSE_NORMAL || + code == WebSocketConnectionD13.CLOSE_UNDEFINED || + code == WebSocketConnectionD13.CLOSE_NO_CLOSE || + code == WebSocketConnectionD13.CLOSE_NO_CODE || + ( code > 1010 && code <= 2999 ) || + code >= 5000 ) + { + errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"Invalid close code " + code); + return; + } + if (buffer.length()>2) - message=new String(buffer.array(),buffer.getIndex()+2,buffer.length()-2,StringUtil.__UTF8); + { + if(_utf8.append(buffer.array(),buffer.getIndex()+2,buffer.length()-2,_connection.getMaxTextMessageSize())) + { + message = _utf8.toString(); + _utf8.reset(); + } + } + } + else if(buffer.length() == 1) + { + // Invalid length. use status code 1002 (Protocol error) + errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"Invalid payload length of 1"); + return; } closeIn(code,message); break; @@ -752,12 +774,12 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc case WebSocketConnectionD13.OP_TEXT: { - if (STRICT && _opcode!=-1) + if (_opcode!=-1) { errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"Expected Continuation"+Integer.toHexString(opcode)); return; } - + if(_onTextMessage!=null) { if (_connection.getMaxTextMessageSize()<=0) @@ -790,15 +812,15 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc } break; } - + case WebSocketConnectionD13.OP_BINARY: { - if (STRICT && _opcode!=-1) + if (_opcode!=-1) { errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"Expected Continuation"+Integer.toHexString(opcode)); return; } - + if (_onBinaryMessage!=null && checkBinaryMessageSize(0,buffer.length())) { if (lastFrame) @@ -823,15 +845,10 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc } default: - if (STRICT) - errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"Bad opcode 0x"+Integer.toHexString(opcode)); - return; + errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"Bad opcode 0x"+Integer.toHexString(opcode)); + break; } } - catch(ThreadDeath th) - { - throw th; - } catch(Utf8Appendable.NotUtf8Exception notUtf8) { LOG.warn("{} for {}",notUtf8,_endp); @@ -849,20 +866,19 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc private void errorClose(int code, String message) { _connection.close(code,message); - if (BRUTAL) + + // Brutally drop the connection + try { - try - { - _endp.close(); - } - catch (IOException e) - { - LOG.warn(e.toString()); - LOG.debug(e); - } + _endp.close(); + } + catch (IOException e) + { + LOG.warn(e.toString()); + LOG.debug(e); } } - + private boolean checkBinaryMessageSize(int bufferLen, int length) { int max = _connection.getMaxBinaryMessageSize(); @@ -900,13 +916,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc return WebSocketConnectionD13.this.toString()+"FH"; } } - - /* ------------------------------------------------------------ */ - private interface IdleCheck - { - void access(EndPoint endp); - } - + /* ------------------------------------------------------------ */ public void handshake(HttpServletRequest request, HttpServletResponse response, String subprotocol) throws IOException { diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketFactory.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketFactory.java index 4fc1bb53627..ed663826e3c 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketFactory.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketFactory.java @@ -20,13 +20,15 @@ import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; + import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.http.HttpException; import org.eclipse.jetty.http.HttpParser; import org.eclipse.jetty.io.ConnectedEndPoint; -import org.eclipse.jetty.server.HttpConnection; +import org.eclipse.jetty.server.AbstractHttpConnection; +import org.eclipse.jetty.server.BlockingHttpConnection; import org.eclipse.jetty.util.QuotedStringTokenizer; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -193,7 +195,9 @@ public class WebSocketFactory int draft = request.getIntHeader("Sec-WebSocket-Version"); if (draft < 0) draft = request.getIntHeader("Sec-WebSocket-Draft"); - HttpConnection http = HttpConnection.getCurrentConnection(); + AbstractHttpConnection http = AbstractHttpConnection.getCurrentConnection(); + if (http instanceof BlockingHttpConnection) + throw new IllegalStateException("Websockets not supported on blocking connectors"); ConnectedEndPoint endp = (ConnectedEndPoint)http.getEndPoint(); List extensions_requested = new ArrayList(); @@ -204,6 +208,7 @@ public class WebSocketFactory extensions_requested.add(tok.nextToken()); } + final WebSocketConnection connection; final List extensions; switch (draft) @@ -250,6 +255,7 @@ public class WebSocketFactory connection.fillBuffersFrom(((HttpParser)http.getParser()).getBodyBuffer()); // Tell jetty about the new connection + LOG.debug("Websocket upgrade {} {} {} {}",request.getRequestURI(),draft,protocol,connection); request.setAttribute("org.eclipse.jetty.io.Connection", connection); } diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD13.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD13.java index 2f923520fe7..0ed3e7c02ac 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD13.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD13.java @@ -36,6 +36,7 @@ public class WebSocketGeneratorD13 implements WebSocketGenerator private int _m; private boolean _opsent; private final MaskGen _maskGen; + private boolean _closed; public WebSocketGeneratorD13(WebSocketBuffers buffers, EndPoint endp) { @@ -60,6 +61,11 @@ public class WebSocketGeneratorD13 implements WebSocketGenerator { // System.err.printf("<< %s %s %s\n",TypeUtil.toHexString(flags),TypeUtil.toHexString(opcode),length); + if (_closed) + throw new EofException("Closed"); + if (opcode==WebSocketConnectionD13.OP_CLOSE) + _closed=true; + boolean mask=_maskGen!=null; if (_buffer==null) @@ -131,7 +137,6 @@ public class WebSocketGeneratorD13 implements WebSocketGenerator _buffer.put(_mask); } - // write payload int remaining = payload; while (remaining > 0) @@ -183,7 +188,12 @@ public class WebSocketGeneratorD13 implements WebSocketGenerator throw new EofException(); if (_buffer!=null) - return _endp.flush(_buffer); + { + int flushed=_buffer.hasContent()?_endp.flush(_buffer):0; + if (_closed&&_buffer.length()==0) + _endp.shutdownOutput(); + return flushed; + } return 0; } diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketParserD13.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketParserD13.java index 5b21494df50..f57c4976f53 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketParserD13.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketParserD13.java @@ -34,7 +34,7 @@ public class WebSocketParserD13 implements WebSocketParser public enum State { - START(0), OPCODE(1), LENGTH_7(1), LENGTH_16(2), LENGTH_63(8), MASK(4), PAYLOAD(0), DATA(0), SKIP(1); + START(0), OPCODE(1), LENGTH_7(1), LENGTH_16(2), LENGTH_63(8), MASK(4), PAYLOAD(0), DATA(0), SKIP(1), SEEK_EOF(1); int _needs; @@ -125,11 +125,12 @@ public class WebSocketParserD13 implements WebSocketParser { if (_buffer==null) _buffer=_buffers.getBuffer(); - int total_filled=0; - int events=0; + + boolean progress=false; + int filled=-1; // Loop until a datagram call back or can't fill anymore - while(true) + while(!progress && (!_endp.isInputShutdown()||_buffer.length()>0)) { int available=_buffer.length(); @@ -158,35 +159,38 @@ public class WebSocketParserD13 implements WebSocketParser } // System.err.printf("%s %s %s >>\n",TypeUtil.toHexString(_flags),TypeUtil.toHexString(_opcode),data.length()); - events++; _bytesNeeded-=data.length(); + progress=true; _handler.onFrame((byte)(_flags&(0xff^WebSocketConnectionD13.FLAG_FIN)), _opcode, data); _opcode=WebSocketConnectionD13.OP_CONTINUATION; } if (_buffer.space() == 0) - throw new IllegalStateException("FULL: "+_state+" "+_bytesNeeded+">"+_buffer.capacity()); + throw new IllegalStateException("FULL: "+_state+" "+_bytesNeeded+">"+_buffer.capacity()); } // catch IOExceptions (probably EOF) and try to parse what we have try { - int filled=_endp.isOpen()?_endp.fill(_buffer):-1; - if (filled<=0) - return (total_filled+events)>0?(total_filled+events):filled; - total_filled+=filled; + filled=_endp.isInputShutdown()?-1:_endp.fill(_buffer); available=_buffer.length(); + // System.err.printf(">> filled %d/%d%n",filled,available); + if (filled<=0) + break; } catch(IOException e) { LOG.debug(e); - return (total_filled+events)>0?(total_filled+events):-1; + filled=-1; + break; } } - + // Did we get enough? + if (available<(_state==State.SKIP?1:_bytesNeeded)) + break; + // if we are here, then we have sufficient bytes to process the current state. - // Parse the buffer byte by byte (unless it is STATE_DATA) byte b; while (_state!=State.DATA && available>=(_state==State.SKIP?1:_bytesNeeded)) @@ -195,7 +199,7 @@ public class WebSocketParserD13 implements WebSocketParser { case START: _skip=false; - _state=State.OPCODE; + _state=_opcode==WebSocketConnectionD13.OP_CLOSE?State.SEEK_EOF:State.OPCODE; _bytesNeeded=_state.getNeeds(); continue; @@ -207,9 +211,9 @@ public class WebSocketParserD13 implements WebSocketParser if (WebSocketConnectionD13.isControlFrame(_opcode)&&!WebSocketConnectionD13.isLastFrame(_flags)) { - events++; LOG.warn("Fragmented Control from "+_endp); _handler.close(WebSocketConnectionD13.CLOSE_PROTOCOL,"Fragmented control"); + progress=true; _skip=true; } @@ -249,7 +253,7 @@ public class WebSocketParserD13 implements WebSocketParser { if (_length>_buffer.capacity() && !_fragmentFrames) { - events++; + progress=true; _handler.close(WebSocketConnectionD13.CLOSE_POLICY_VIOLATION,"frame size "+_length+">"+_buffer.capacity()); _skip=true; } @@ -268,7 +272,7 @@ public class WebSocketParserD13 implements WebSocketParser _bytesNeeded=(int)_length; if (_length>=_buffer.capacity() && !_fragmentFrames) { - events++; + progress=true; _handler.close(WebSocketConnectionD13.CLOSE_POLICY_VIOLATION,"frame size "+_length+">"+_buffer.capacity()); _skip=true; } @@ -296,12 +300,19 @@ public class WebSocketParserD13 implements WebSocketParser case SKIP: int skip=Math.min(available,_bytesNeeded); + progress=true; _buffer.skip(skip); available-=skip; _bytesNeeded-=skip; if (_bytesNeeded==0) _state=State.START; - + break; + + case SEEK_EOF: + progress=true; + _buffer.skip(available); + available=0; + break; } } @@ -311,7 +322,7 @@ public class WebSocketParserD13 implements WebSocketParser { _buffer.skip(_bytesNeeded); _state=State.START; - events++; + progress=true; _handler.close(WebSocketConnectionD13.CLOSE_PROTOCOL,"Not masked"); } else @@ -328,15 +339,18 @@ public class WebSocketParserD13 implements WebSocketParser } // System.err.printf("%s %s %s >>\n",TypeUtil.toHexString(_flags),TypeUtil.toHexString(_opcode),data.length()); - events++; + + progress=true; _handler.onFrame(_flags, _opcode, data); _bytesNeeded=0; _state=State.START; } - return total_filled+events; + break; } } + + return progress?1:filled; } /* ------------------------------------------------------------ */ diff --git a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/SafariWebsocketDraft0Test.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/SafariWebsocketDraft0Test.java new file mode 100644 index 00000000000..3d7f2864143 --- /dev/null +++ b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/SafariWebsocketDraft0Test.java @@ -0,0 +1,110 @@ +package org.eclipse.jetty.websocket; + +import static org.hamcrest.Matchers.*; + +import java.net.URI; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.util.log.StdErrLog; +import org.eclipse.jetty.websocket.helper.CaptureSocket; +import org.eclipse.jetty.websocket.helper.SafariD00; +import org.eclipse.jetty.websocket.helper.WebSocketCaptureServlet; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; + +public class SafariWebsocketDraft0Test +{ + private Server server; + private WebSocketCaptureServlet servlet; + private URI serverUri; + + @BeforeClass + public static void initLogging() + { + // Configure Logging + System.setProperty("org.eclipse.jetty.util.log.class",StdErrLog.class.getName()); + System.setProperty("org.eclipse.jetty.LEVEL","DEBUG"); + } + + @Before + public void startServer() throws Exception + { + // Configure Server + server = new Server(0); + + ServletContextHandler context = new ServletContextHandler(); + context.setContextPath("/"); + server.setHandler(context); + + // Serve capture servlet + servlet = new WebSocketCaptureServlet(); + context.addServlet(new ServletHolder(servlet),"/"); + + // Start Server + server.start(); + + Connector conn = server.getConnectors()[0]; + String host = conn.getHost(); + if (host == null) + { + host = "localhost"; + } + int port = conn.getLocalPort(); + serverUri = new URI(String.format("ws://%s:%d/",host,port)); + System.out.printf("Server URI: %s%n",serverUri); + } + + @Test + @Ignore + public void testSendTextMessages() throws Exception + { + SafariD00 safari = new SafariD00(serverUri); + + try + { + safari.connect(); + safari.issueHandshake(); + + // Send 5 short messages, using technique seen in Safari. + safari.sendMessage("aa-0"); // single msg + safari.sendMessage("aa-1", "aa-2", "aa-3", "aa-4"); + + // Servlet should show only 1 connection. + Assert.assertThat("Servlet.captureSockets.size",servlet.captures.size(),is(1)); + + CaptureSocket socket = servlet.captures.get(0); + Assert.assertThat("CaptureSocket",socket,notNullValue()); + Assert.assertThat("CaptureSocket.isConnected", socket.isConnected(), is(true)); + + // Give servlet 500 millisecond to process messages + threadSleep(1,TimeUnit.SECONDS); + // Should have captured 5 messages. + Assert.assertThat("CaptureSocket.messages.size",socket.messages.size(),is(5)); + } + finally + { + System.out.println("Closing client socket"); + safari.disconnect(); + } + } + + public static void threadSleep(int dur, TimeUnit unit) throws InterruptedException + { + long ms = TimeUnit.MILLISECONDS.convert(dur,unit); + Thread.sleep(ms); + } + + @After + public void stopServer() throws Exception + { + server.stop(); + } +} diff --git a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/TestServer.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/TestServer.java index 5ffc4bb87c2..da0b41f4389 100644 --- a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/TestServer.java +++ b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/TestServer.java @@ -14,7 +14,6 @@ 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; -import org.eclipse.jetty.websocket.WebSocket.FrameConnection; public class TestServer extends Server { diff --git a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketClientTest.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketClientTest.java index 0e65c889941..80042a97631 100644 --- a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketClientTest.java +++ b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketClientTest.java @@ -1,5 +1,7 @@ package org.eclipse.jetty.websocket; +import static org.hamcrest.Matchers.greaterThan; + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -26,7 +28,6 @@ import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.junit.After; import org.junit.Assert; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; public class WebSocketClientTest @@ -419,6 +420,7 @@ public class WebSocketClientTest final AtomicInteger close = new AtomicInteger(); final CountDownLatch _latch = new CountDownLatch(1); final BlockingQueue queue = new BlockingArrayQueue(); + final StringBuilder closeMessage = new StringBuilder(); Future future=client.open(new URI("ws://127.0.0.1:"+_serverPort+"/"),new WebSocket.OnTextMessage() { public void onOpen(Connection connection) @@ -429,6 +431,7 @@ public class WebSocketClientTest public void onClose(int closeCode, String message) { close.set(closeCode); + closeMessage.append(message); _latch.countDown(); } @@ -477,8 +480,8 @@ public class WebSocketClientTest _latch.await(10,TimeUnit.SECONDS); Assert.assertTrue(System.currentTimeMillis()-start<5000); - Assert.assertEquals(1111,close.get()); - + Assert.assertEquals(1002,close.get()); + Assert.assertEquals("Invalid close code 1111", closeMessage.toString()); } @@ -517,16 +520,18 @@ public class WebSocketClientTest Assert.assertTrue(open.get()); Assert.assertEquals(0,close.get()); - final int messages=20000; + final int messages=200000; final AtomicLong totalB=new AtomicLong(); Thread consumer = new Thread() { + @Override public void run() { + // Thread.sleep is for artificially poor performance reader needed for this testcase. try { - Thread.sleep(2000); + Thread.sleep(200); byte[] recv = new byte[32*1024]; int len=0; @@ -550,26 +555,23 @@ public class WebSocketClientTest consumer.start(); // Send lots of messages client to server - long max=0; long start=System.currentTimeMillis(); String mesg="This is a test message to send"; for (int i=0;imax) - max=duration; - } + connection.sendMessage(mesg); } + // Duration for the write phase + long writeDur = (System.currentTimeMillis() - start); + // wait for consumer to complete - while (totalB.get()1000); // writing was blocked + } + + Assert.assertThat("write duration", writeDur, greaterThan(1000L)); // writing was blocked Assert.assertEquals(messages*(mesg.length()+6L),totalB.get()); consumer.interrupt(); @@ -585,6 +587,7 @@ public class WebSocketClientTest final AtomicBoolean open = new AtomicBoolean(); final AtomicInteger close = new AtomicInteger(); final CountDownLatch _latch = new CountDownLatch(1); + final StringBuilder closeMessage = new StringBuilder(); final Exchanger exchanger = new Exchanger(); Future future=client.open(new URI("ws://127.0.0.1:"+_serverPort+"/"),new WebSocket.OnTextMessage() { @@ -595,8 +598,8 @@ public class WebSocketClientTest public void onClose(int closeCode, String message) { - //System.err.println("CLOSE "+closeCode+" "+message); close.set(closeCode); + closeMessage.append(message); _latch.countDown(); } @@ -632,18 +635,28 @@ public class WebSocketClientTest // Set up a consumer of received messages that waits a while before consuming Thread consumer = new Thread() { + @Override public void run() { try { - Thread.sleep(2000); - while(m.get()max) - max=duration; - } } - - while(consumer.isAlive()) + + while(consumer.isAlive()) + { Thread.sleep(10); + } + + // Duration of the read operation. + long readDur = (System.currentTimeMillis() - start); - - Assert.assertTrue(max>1000); // writing was blocked + Assert.assertThat("read duration", readDur, greaterThan(1000L)); // reading was blocked Assert.assertEquals(m.get(),messages); // Close with code @@ -689,8 +696,8 @@ public class WebSocketClientTest _latch.await(10,TimeUnit.SECONDS); Assert.assertTrue(System.currentTimeMillis()-start<5000); - Assert.assertEquals(1111,close.get()); - + Assert.assertEquals(1002,close.get()); + Assert.assertEquals("Invalid close code 1111", closeMessage.toString()); } private void respondToClient(Socket connection, String serverResponse) throws IOException diff --git a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketCommTest.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketCommTest.java new file mode 100644 index 00000000000..c2017f7c0a9 --- /dev/null +++ b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketCommTest.java @@ -0,0 +1,112 @@ +package org.eclipse.jetty.websocket; + +import static org.hamcrest.Matchers.*; + +import java.net.URI; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.util.log.StdErrLog; +import org.eclipse.jetty.websocket.helper.CaptureSocket; +import org.eclipse.jetty.websocket.helper.MessageSender; +import org.eclipse.jetty.websocket.helper.WebSocketCaptureServlet; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * WebSocketCommTest - to test reported undelivered messages in bug JETTY-1463 + */ +public class WebSocketCommTest +{ + private Server server; + private WebSocketCaptureServlet servlet; + private URI serverUri; + + @BeforeClass + public static void initLogging() + { + // Configure Logging + System.setProperty("org.eclipse.jetty.util.log.class",StdErrLog.class.getName()); + System.setProperty("org.eclipse.jetty.LEVEL","DEBUG"); + } + + @Before + public void startServer() throws Exception + { + // Configure Server + server = new Server(0); + + ServletContextHandler context = new ServletContextHandler(); + context.setContextPath("/"); + server.setHandler(context); + + // Serve capture servlet + servlet = new WebSocketCaptureServlet(); + context.addServlet(new ServletHolder(servlet),"/"); + + // Start Server + server.start(); + + Connector conn = server.getConnectors()[0]; + String host = conn.getHost(); + if (host == null) + { + host = "localhost"; + } + int port = conn.getLocalPort(); + serverUri = new URI(String.format("ws://%s:%d/",host,port)); + System.out.printf("Server URI: %s%n",serverUri); + } + + @Test + public void testSendTextMessages() throws Exception + { + WebSocketClientFactory clientFactory = new WebSocketClientFactory(); + clientFactory.start(); + + WebSocketClient wsc = clientFactory.newWebSocketClient(); + MessageSender sender = new MessageSender(); + wsc.open(serverUri,sender); + + try + { + sender.awaitConnect(); + + // Send 5 short messages + for (int i = 0; i < 5; i++) + { + System.out.printf("Sending msg-%d%n",i); + sender.sendMessage("msg-%d",i); + } + + // Servlet should show only 1 connection. + Assert.assertThat("Servlet.captureSockets.size",servlet.captures.size(),is(1)); + + CaptureSocket socket = servlet.captures.get(0); + Assert.assertThat("CaptureSocket",socket,notNullValue()); + Assert.assertThat("CaptureSocket.isConnected", socket.isConnected(), is(true)); + + // Give servlet 500 millisecond to process messages + threadSleep(500,TimeUnit.MILLISECONDS); + // Should have captured 5 messages. + Assert.assertThat("CaptureSocket.messages.size",socket.messages.size(),is(5)); + } + finally + { + System.out.println("Closing client socket"); + sender.close(); + } + } + + public static void threadSleep(int dur, TimeUnit unit) throws InterruptedException + { + long ms = TimeUnit.MILLISECONDS.convert(dur,unit); + Thread.sleep(ms); + } +} diff --git a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketGeneratorD13Test.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketGeneratorD13Test.java index bc0585447e7..696a70abc48 100644 --- a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketGeneratorD13Test.java +++ b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketGeneratorD13Test.java @@ -1,9 +1,11 @@ package org.eclipse.jetty.websocket; import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; import org.eclipse.jetty.io.ByteArrayBuffer; import org.eclipse.jetty.io.ByteArrayEndPoint; +import org.eclipse.jetty.io.EofException; import org.eclipse.jetty.util.StringUtil; import org.junit.Before; import org.junit.Test; @@ -196,5 +198,41 @@ public class WebSocketGeneratorD13Test for (int i=0;i1000); // was blocked + // if (duration<1500) + System.err.println("max="+duration); + assertTrue(duration>1500); // was blocked } @Test diff --git a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketMessageD13Test.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketMessageD13Test.java index e48885c2f01..4fb5fe2096b 100644 --- a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketMessageD13Test.java +++ b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketMessageD13Test.java @@ -65,7 +65,7 @@ public class WebSocketMessageD13Test } }; wsHandler.getWebSocketFactory().setBufferSize(8192); - wsHandler.getWebSocketFactory().setMaxIdleTime(1000); + wsHandler.getWebSocketFactory().setMaxIdleTime(1000); wsHandler.setHandler(new DefaultHandler()); __server.setHandler(wsHandler); __server.start(); @@ -390,7 +390,7 @@ public class WebSocketMessageD13Test output.write(bytes[i]^0xff); output.flush(); // Make sure the read times out if there are problems with the implementation - socket.setSoTimeout(1000); + socket.setSoTimeout(1000); InputStream input = socket.getInputStream(); @@ -461,6 +461,7 @@ public class WebSocketMessageD13Test // unblock the latch in 4s new Thread() { + @Override public void run() { try @@ -544,6 +545,7 @@ public class WebSocketMessageD13Test final AtomicLong totalB=new AtomicLong(); new Thread() { + @Override public void run() { try @@ -908,52 +910,241 @@ public class WebSocketMessageD13Test assertEquals(WebSocketConnectionD13.CLOSE_MESSAGE_TOO_LARGE,code); lookFor("Message size > 15",input); } + + @Test - public void testCloseCode() throws Exception + public void testCloseIn() throws Exception { - Socket socket = new Socket("localhost", __connector.getLocalPort()); - OutputStream output = socket.getOutputStream(); - output.write( - ("GET /chat HTTP/1.1\r\n"+ - "Host: server.example.com\r\n"+ - "Upgrade: websocket\r\n"+ - "Connection: Upgrade\r\n"+ - "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"+ - "Sec-WebSocket-Origin: http://example.com\r\n"+ - "Sec-WebSocket-Protocol: chat\r\n" + - "Sec-WebSocket-Version: "+WebSocketConnectionD13.VERSION+"\r\n"+ - "\r\n").getBytes("ISO-8859-1")); - output.flush(); + int[][] tests = + { + {-1,0,-1}, + {-1,0,-1}, + {1000,2,1000}, + {1000,2+4,1000}, + {1005,2+23,1002}, + {1005,2+23,1002}, + {1006,2+23,1002}, + {1006,2+23,1002}, + {4000,2,4000}, + {4000,2+4,4000}, + {9000,2+23,1002}, + {9000,2+23,1002} + }; - socket.setSoTimeout(100000); - InputStream input = socket.getInputStream(); + String[] mesg = + { + "", + "", + "", + "mesg", + "", + "mesg", + "", + "mesg", + "", + "mesg", + "", + "mesg" + }; + + String[] resp = + { + "", + "", + "", + "mesg", + "Invalid close code 1005", + "Invalid close code 1005", + "Invalid close code 1006", + "Invalid close code 1006", + "", + "mesg", + "Invalid close code 9000", + "Invalid close code 9000" + }; - lookFor("HTTP/1.1 101 Switching Protocols\r\n",input); - skipTo("Sec-WebSocket-Accept: ",input); - lookFor("s3pPLMBiTxaQ9kYGzzhZRbK+xOo=",input); - skipTo("\r\n\r\n",input); + for (int t=0;t0) + { + output.write(code/0x100); + output.write(code%0x100); + output.write(m.getBytes()); + } + output.flush(); + + __serverWebSocket.awaitDisconnected(1000); + + byte[] buf = new byte[128]; + int len = input.read(buf); + + assertEquals(tst,2+tests[t][1],len); + assertEquals(tst,(byte)0x88,buf[0]); + + if (len>=4) + { + code=(0xff&buf[2])*0x100+(0xff&buf[3]); + assertEquals(tst,tests[t][2],code); + + if (len>4) + { + m = new String(buf,4,len-4,"UTF-8"); + assertEquals(tst,resp[t],m); + } + } + else + assertEquals(tst,tests[t][2],-1); + + + len = input.read(buf); + assertEquals(tst,-1,len); + } } + + + + @Test + public void testCloseOut() throws Exception + { + int[][] tests = + { + {-1,0,-1}, + {-1,0,-1}, + {0,2,1000}, + {0,2+4,1000}, + {1000,2,1000}, + {1000,2+4,1000}, + {1005,0,-1}, + {1005,0,-1}, + {1006,0,-1}, + {1006,0,-1}, + {9000,2,9000}, + {9000,2+4,9000} + }; + + String[] mesg = + { + null, + "Not Sent", + null, + "mesg", + null, + "mesg", + null, + "mesg", + null, + "mesg", + null, + "mesg" + }; + + for (int t=0;t=4) + { + int code=(0xff&buf[2])*0x100+(0xff&buf[3]); + assertEquals(tst,tests[t][2],code); + + if (len>4) + { + String m = new String(buf,4,len-4,"UTF-8"); + assertEquals(tst,mesg[t],m); + } + } + else + assertEquals(tst,tests[t][2],-1); + + try + { + output.write(0x88); + output.write(0x80); + output.write(0x00); + output.write(0x00); + output.write(0x00); + output.write(0x00); + output.flush(); + } + catch(IOException e) + { + System.err.println("socket "+socket); + throw e; + } + + len = input.read(buf); + assertEquals(tst,-1,len); + } + } + @Test public void testNotUTF8() throws Exception diff --git a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketOverSSLTest.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketOverSSLTest.java new file mode 100644 index 00000000000..fd73e6b43ff --- /dev/null +++ b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketOverSSLTest.java @@ -0,0 +1,184 @@ +package org.eclipse.jetty.websocket; + +import java.io.IOException; +import java.net.URI; +import java.util.Arrays; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import javax.servlet.http.HttpServletRequest; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ssl.SslSelectChannelConnector; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; + +public class WebSocketOverSSLTest +{ + private Server _server; + private int _port; + private WebSocket.Connection _connection; + + private void startServer(final WebSocket webSocket) throws Exception + { + _server = new Server(); + SslSelectChannelConnector connector = new SslSelectChannelConnector(); + _server.addConnector(connector); + SslContextFactory cf = connector.getSslContextFactory(); + cf.setKeyStorePath(MavenTestingUtils.getTestResourceFile("keystore").getAbsolutePath()); + cf.setKeyStorePassword("storepwd"); + cf.setKeyManagerPassword("keypwd"); + _server.setHandler(new WebSocketHandler() + { + public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) + { + return webSocket; + } + }); + _server.start(); + _port = connector.getLocalPort(); + } + + private void startClient(final WebSocket webSocket) throws Exception + { + Assert.assertTrue(_server.isStarted()); + + WebSocketClientFactory factory = new WebSocketClientFactory(new QueuedThreadPool(), new ZeroMaskGen()); + SslContextFactory cf = factory.getSslContextFactory(); + cf.setKeyStorePath(MavenTestingUtils.getTestResourceFile("keystore").getAbsolutePath()); + cf.setKeyStorePassword("storepwd"); + cf.setKeyManagerPassword("keypwd"); + factory.start(); + WebSocketClient client = new WebSocketClient(factory); + _connection = client.open(new URI("wss://localhost:" + _port), webSocket).get(5, TimeUnit.SECONDS); + } + + @After + public void destroy() throws Exception + { + if (_connection != null) + _connection.close(); + if (_server != null) + { + _server.stop(); + _server.join(); + } + } + + @Test + public void testWebSocketOverSSL() throws Exception + { + final String message = "message"; + final CountDownLatch serverLatch = new CountDownLatch(1); + startServer(new WebSocket.OnTextMessage() + { + private Connection connection; + + public void onOpen(Connection connection) + { + this.connection = connection; + } + + public void onMessage(String data) + { + try + { + Assert.assertEquals(message, data); + connection.sendMessage(data); + serverLatch.countDown(); + } + catch (IOException x) + { + x.printStackTrace(); + } + } + + public void onClose(int closeCode, String message) + { + } + }); + final CountDownLatch clientLatch = new CountDownLatch(1); + startClient(new WebSocket.OnTextMessage() + { + public void onOpen(Connection connection) + { + } + + public void onMessage(String data) + { + Assert.assertEquals(message, data); + clientLatch.countDown(); + } + + public void onClose(int closeCode, String message) + { + } + }); + _connection.sendMessage(message); + + Assert.assertTrue(serverLatch.await(5, TimeUnit.SECONDS)); + Assert.assertTrue(clientLatch.await(5, TimeUnit.SECONDS)); + } + + @Test + public void testManyMessages() throws Exception + { + startServer(new WebSocket.OnTextMessage() + { + private Connection connection; + + public void onOpen(Connection connection) + { + this.connection = connection; + } + + public void onMessage(String data) + { + try + { + connection.sendMessage(data); + } + catch (IOException x) + { + x.printStackTrace(); + } + } + + public void onClose(int closeCode, String message) + { + } + }); + int count = 1000; + final CountDownLatch clientLatch = new CountDownLatch(count); + startClient(new WebSocket.OnTextMessage() + { + public void onOpen(Connection connection) + { + } + + public void onMessage(String data) + { + clientLatch.countDown(); + } + + public void onClose(int closeCode, String message) + { + } + }); + + char[] chars = new char[256]; + Arrays.fill(chars, 'x'); + String message = new String(chars); + for (int i = 0; i < count; ++i) + _connection.sendMessage(message); + + Assert.assertTrue(clientLatch.await(20, TimeUnit.SECONDS)); + + // While messages may have all arrived, the SSL close alert + // may be in the way so give some time for it to be processed. + TimeUnit.SECONDS.sleep(1); + } +} diff --git a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketParserD13Test.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketParserD13Test.java index 4378e1ba8d6..56b573d7ac0 100644 --- a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketParserD13Test.java +++ b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketParserD13Test.java @@ -22,6 +22,7 @@ import org.junit.Test; */ public class WebSocketParserD13Test { + private ByteArrayEndPoint _endPoint; private MaskedByteArrayBuffer _in; private Handler _handler; private WebSocketParserD13 _parser; @@ -84,14 +85,14 @@ public class WebSocketParserD13Test public void setUp() throws Exception { WebSocketBuffers buffers = new WebSocketBuffers(1024); - ByteArrayEndPoint endPoint = new ByteArrayEndPoint(); - endPoint.setNonBlocking(true); + _endPoint = new ByteArrayEndPoint(); + _endPoint.setNonBlocking(true); _handler = new Handler(); - _parser=new WebSocketParserD13(buffers, endPoint,_handler,true); + _parser=new WebSocketParserD13(buffers, _endPoint,_handler,true); _parser.setFakeFragments(false); _in = new MaskedByteArrayBuffer(); - endPoint.setIn(_in); + _endPoint.setIn(_in); } @Test @@ -128,7 +129,7 @@ public class WebSocketParserD13Test int progress =_parser.parseNext(); - assertEquals(18,progress); + assertTrue(progress>0); assertEquals("Hello World",_handler._data.get(0)); assertEquals(0x8,_handler._flags); assertEquals(0x1,_handler._opcode); @@ -150,7 +151,7 @@ public class WebSocketParserD13Test int progress =_parser.parseNext(); - assertEquals(bytes.length+7,progress); + assertTrue(progress>0); assertEquals(string,_handler._data.get(0)); assertEquals(0x8,_handler._flags); assertEquals(0x1,_handler._opcode); @@ -178,7 +179,7 @@ public class WebSocketParserD13Test int progress =_parser.parseNext(); - assertEquals(bytes.length+9,progress); + assertTrue(progress>0); assertEquals(string,_handler._data.get(0)); assertEquals(0x8,_handler._flags); assertEquals(0x1,_handler._opcode); @@ -219,7 +220,7 @@ public class WebSocketParserD13Test int progress =parser.parseNext(); parser.returnBuffer(); - assertEquals(bytes.length+11,progress); + assertTrue(progress>0); assertEquals(string,_handler._data.get(0)); assertTrue(parser.isBufferEmpty()); assertTrue(parser.getBuffer()==null); @@ -239,7 +240,7 @@ public class WebSocketParserD13Test int progress =_parser.parseNext(); - assertEquals(24,progress); + assertTrue(progress>0); assertEquals(0,_handler._data.size()); assertFalse(_parser.isBufferEmpty()); assertFalse(_parser.getBuffer()==null); @@ -247,7 +248,7 @@ public class WebSocketParserD13Test progress =_parser.parseNext(); _parser.returnBuffer(); - assertEquals(1,progress); + assertTrue(progress>0); assertEquals("Hello World",_handler._data.get(0)); assertTrue(_parser.isBufferEmpty()); assertTrue(_parser.getBuffer()==null); @@ -268,18 +269,18 @@ public class WebSocketParserD13Test int progress =_parser.parseNext(); assertTrue(progress>0); - assertEquals(WebSocketConnectionD13.CLOSE_POLICY_VIOLATION,_handler._code); + + for (int i=0;i<2048;i++) _in.put((byte)'a'); progress =_parser.parseNext(); - assertEquals(2048,progress); + assertTrue(progress>0); assertEquals(0,_handler._data.size()); assertEquals(0,_handler._utf8.length()); _handler._code=0; - _handler._message=null; _in.putUnmasked((byte)0x81); _in.putUnmasked((byte)0xFE); @@ -291,8 +292,7 @@ public class WebSocketParserD13Test progress =_parser.parseNext(); assertTrue(progress>0); - assertEquals(1,_handler._data.size()); - assertEquals(1024,_handler._data.get(0).length()); + assertEquals(0,_handler._data.size()); } @Test @@ -323,6 +323,43 @@ public class WebSocketParserD13Test assertEquals(('a'+i%26),mesg.charAt(i)); } + @Test + public void testClose() throws Exception + { + String string = "Game Over"; + byte[] bytes = string.getBytes("UTF-8"); + + _in.putUnmasked((byte)(0x80|0x08)); + _in.putUnmasked((byte)(0x80|(2+bytes.length))); + _in.sendMask(); + _in.put((byte)(1000/0x100)); + _in.put((byte)(1000%0x100)); + _in.put(bytes); + + int progress =_parser.parseNext(); + + assertTrue(progress>0); + assertEquals(string,_handler._data.get(0).substring(2)); + assertEquals(0x8,_handler._flags); + assertEquals(0x8,_handler._opcode); + _parser.returnBuffer(); + assertTrue(_parser.isBufferEmpty()); + assertTrue(_parser.getBuffer()==null); + + _in.clear(); + _in.put(bytes); + _endPoint.setIn(_in); + progress =_parser.parseNext(); + assertTrue(progress>0); + + _endPoint.shutdownInput(); + + progress =_parser.parseNext(); + assertEquals(-1,progress); + + } + + private class Handler implements WebSocketParser.FrameHandler { Utf8StringBuilder _utf8 = new Utf8StringBuilder(); @@ -330,7 +367,6 @@ public class WebSocketParserD13Test private byte _flags; private byte _opcode; int _code; - String _message; int _frames; public void onFrame(byte flags, byte opcode, Buffer buffer) @@ -353,7 +389,6 @@ public class WebSocketParserD13Test public void close(int code,String message) { _code=code; - _message=message; } } } diff --git a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/helper/CaptureSocket.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/helper/CaptureSocket.java new file mode 100644 index 00000000000..45842596af3 --- /dev/null +++ b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/helper/CaptureSocket.java @@ -0,0 +1,42 @@ +package org.eclipse.jetty.websocket.helper; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jetty.websocket.WebSocket; + +public class CaptureSocket implements WebSocket, WebSocket.OnTextMessage +{ + private Connection conn; + public List messages; + + public CaptureSocket() + { + messages = new ArrayList(); + } + + public boolean isConnected() + { + if (conn == null) + { + return false; + } + return conn.isOpen(); + } + + public void onMessage(String data) + { + System.out.printf("Received Message \"%s\" [size %d]%n", data, data.length()); + messages.add(data); + } + + public void onOpen(Connection connection) + { + this.conn = connection; + } + + public void onClose(int closeCode, String message) + { + this.conn = null; + } +} \ No newline at end of file diff --git a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/helper/MessageSender.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/helper/MessageSender.java new file mode 100644 index 00000000000..1a039ceb192 --- /dev/null +++ b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/helper/MessageSender.java @@ -0,0 +1,52 @@ +package org.eclipse.jetty.websocket.helper; + +import java.io.IOException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.websocket.WebSocket; + +public class MessageSender implements WebSocket +{ + private Connection conn; + private CountDownLatch connectLatch = new CountDownLatch(1); + + public void onOpen(Connection connection) + { + this.conn = connection; + connectLatch.countDown(); + } + + public void onClose(int closeCode, String message) + { + this.conn = null; + } + + public boolean isConnected() + { + if (this.conn == null) + { + return false; + } + return this.conn.isOpen(); + } + + public void sendMessage(String format, Object... args) throws IOException + { + this.conn.sendMessage(String.format(format,args)); + } + + public void awaitConnect() throws InterruptedException + { + connectLatch.await(1,TimeUnit.SECONDS); + } + + public void close() + { + if (this.conn == null) + { + return; + } + this.conn.close(); + } +} \ No newline at end of file diff --git a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/helper/SafariD00.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/helper/SafariD00.java new file mode 100644 index 00000000000..a1370f092a0 --- /dev/null +++ b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/helper/SafariD00.java @@ -0,0 +1,129 @@ +package org.eclipse.jetty.websocket.helper; + +import static org.hamcrest.Matchers.*; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.URI; + +import org.eclipse.jetty.io.ByteArrayBuffer; +import org.eclipse.jetty.util.TypeUtil; +import org.junit.Assert; + +public class SafariD00 +{ + private URI uri; + private SocketAddress endpoint; + private Socket socket; + private OutputStream out; + private InputStream in; + + public SafariD00(URI uri) + { + this.uri = uri; + this.endpoint = new InetSocketAddress(uri.getHost(),uri.getPort()); + } + + /** + * Open the Socket to the destination endpoint and + * + * @return the open java Socket. + * @throws IOException + */ + public Socket connect() throws IOException + { + socket = new Socket(); + socket.connect(endpoint,1000); + + out = socket.getOutputStream(); + in = socket.getInputStream(); + + return socket; + } + + /** + * Issue an Http websocket (Draft-0) upgrade request using the Safari particulars. + * + * @throws UnsupportedEncodingException + */ + public void issueHandshake() throws IOException + { + StringBuilder req = new StringBuilder(); + req.append("GET ").append(uri.getPath()).append(" HTTP/1.1\r\n"); + req.append("Upgrade: WebSocket\r\n"); + req.append("Connection: Upgrade\r\n"); + req.append("Host: ").append(uri.getHost()).append(":").append(uri.getPort()).append("\r\n"); + req.append("Origin: http://www.google.com/\r\n"); + req.append("Sec-WebSocket-Key1: 15{ft :6@87 0 M 5 c901\r\n"); + req.append("Sec-WebSocket-Key2: 3? C;7~0 8 \" 3 2105 6 `_ {\r\n"); + req.append("\r\n"); + + System.out.printf("--- Request ---%n%s",req); + + byte reqBytes[] = req.toString().getBytes("UTF-8"); + byte hixieBytes[] = TypeUtil.fromHexString("e739617916c9daf3"); + byte buf[] = new byte[reqBytes.length + hixieBytes.length]; + System.arraycopy(reqBytes,0,buf,0,reqBytes.length); + System.arraycopy(hixieBytes,0,buf,reqBytes.length,hixieBytes.length); + + // Send HTTP GET Request (with hixie bytes) + out.write(buf,0,buf.length); + out.flush(); + + // Read HTTP 101 Upgrade / Handshake Response + InputStreamReader reader = new InputStreamReader(in); + BufferedReader br = new BufferedReader(reader); + + boolean foundEnd = false; + String line; + while (!foundEnd) + { + line = br.readLine(); + System.out.printf("RESP: %s%n",line); + if (line.length() == 0) + { + foundEnd = true; + } + } + + // Read expected handshake hixie bytes + byte hixieHandshakeExpected[] = TypeUtil.fromHexString("c7438d956cf611a6af70603e6fa54809"); + byte hixieHandshake[] = new byte[hixieHandshakeExpected.length]; + + int readLen = in.read(hixieHandshake,0,hixieHandshake.length); + Assert.assertThat("Read hixie handshake bytes",readLen,is(hixieHandshake.length)); + } + + public void sendMessage(String... msgs) throws IOException + { + int len = 0; + for (String msg : msgs) + { + len += (msg.length() + 2); + } + + ByteArrayBuffer buf = new ByteArrayBuffer(len); + + for (String msg : msgs) + { + buf.put((byte)0x00); + buf.put(msg.getBytes("UTF-8")); + buf.put((byte)0xFF); + } + + out.write(buf.array()); + out.flush(); + } + + public void disconnect() throws IOException + { + socket.close(); + } +} diff --git a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/helper/WebSocketCaptureServlet.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/helper/WebSocketCaptureServlet.java new file mode 100644 index 00000000000..706c4bd7eb4 --- /dev/null +++ b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/helper/WebSocketCaptureServlet.java @@ -0,0 +1,31 @@ +package org.eclipse.jetty.websocket.helper; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.websocket.WebSocket; +import org.eclipse.jetty.websocket.WebSocketServlet; + +@SuppressWarnings("serial") +public class WebSocketCaptureServlet extends WebSocketServlet +{ + public List captures = new ArrayList();; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + resp.sendError(404); + } + + public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) + { + CaptureSocket capture = new CaptureSocket(); + captures.add(capture); + return capture; + } +} \ No newline at end of file diff --git a/jetty-websocket/src/test/resources/keystore b/jetty-websocket/src/test/resources/keystore new file mode 100644 index 00000000000..b727bd0fb77 Binary files /dev/null and b/jetty-websocket/src/test/resources/keystore differ diff --git a/jetty-xml/pom.xml b/jetty-xml/pom.xml index a8579dbd7a9..b2a39954309 100644 --- a/jetty-xml/pom.xml +++ b/jetty-xml/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 jetty-xml diff --git a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java index c14e8046964..6da0355acf8 100644 --- a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java +++ b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java @@ -4,11 +4,11 @@ // 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 +// 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. +// You may elect to redistribute this code under either of these licenses. // ======================================================================== package org.eclipse.jetty.xml; @@ -53,11 +53,11 @@ import org.xml.sax.SAXException; /** * Configure Objects from XML. This class reads an XML file conforming to the configure.dtd DTD and uses it to configure and object by calling set, put or other * methods on the object. - * + * *

    * The actual XML file format may be changed (eg to spring XML) by implementing the {@link ConfigurationProcessorFactory} interfaces to be found by the * ServiceLoader by using the DTD and first tag element in the file. Note that DTD will be null if validation is off. - * + * */ public class XmlConfiguration { @@ -69,7 +69,7 @@ public class XmlConfiguration private static final Class[] __primitiveHolders = { Boolean.class, Character.class, Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class, Void.class }; private static final Integer ZERO = new Integer(0); - + private static final Iterable __factoryLoader; static { @@ -77,7 +77,7 @@ public class XmlConfiguration try { // Use reflection to look up 1.6 service loader - // loader=ServiceLoader.load(ConfigurationProcessorFactory.class); + // loader=ServiceLoader.load(ConfigurationProcessorFactory.class); Class slc = ClassLoader.getSystemClassLoader().loadClass("java.util.ServiceLoader"); Method load = slc.getMethod("load",Class.class); loader=(Iterable)load.invoke(null,ConfigurationProcessorFactory.class); @@ -90,7 +90,7 @@ public class XmlConfiguration { __factoryLoader=loader; } - } + } /* ------------------------------------------------------------ */ private static XmlParser __parser; @@ -135,7 +135,7 @@ public class XmlConfiguration /* ------------------------------------------------------------ */ /** * Constructor. Reads the XML configuration file. - * + * * @param configuration */ public XmlConfiguration(URL configuration) throws SAXException, IOException @@ -152,7 +152,7 @@ public class XmlConfiguration /* ------------------------------------------------------------ */ /** * Constructor. - * + * * @param configuration * String of XML configuration commands excluding the normal XML preamble. The String should start with a " getIdMap() @@ -263,13 +263,13 @@ public class XmlConfiguration /* ------------------------------------------------------------ */ /** - * Configure an object. + * Configure an object. * *

    Apply the XML configuration script to the passed object.

    - * + * * @param obj * The object to be configured, which must be of a type or super type of the class attribute of the Configure element. - * @exception Exception + * @exception Exception */ public Object configure(Object obj) throws Exception { @@ -279,7 +279,7 @@ public class XmlConfiguration /* ------------------------------------------------------------ */ /** * Configure an object. If the configuration has an ID, an object is looked up by ID and it's type check. Otherwise a new object is created. - * + * * @return The newly created configured object. * @exception Exception */ @@ -335,7 +335,7 @@ public class XmlConfiguration configure(obj,_config,0); return obj; } - + /* ------------------------------------------------------------ */ private Class nodeClass(XmlParser.Node node) throws ClassNotFoundException { @@ -345,11 +345,11 @@ public class XmlConfiguration return Loader.loadClass(XmlConfiguration.class,className,true); } - + /* ------------------------------------------------------------ */ /** * Recursive configuration step. This method applies the remaining Set, Put and Call elements to the current object. - * + * * @param obj * @param cfg * @param i @@ -403,7 +403,7 @@ public class XmlConfiguration * Call a set method. This method makes a best effort to find a matching set method. The type of the value is used to find a suitable set method by 1. * Trying for a trivial type match. 2. Looking for a native type match. 3. Trying all correctly named methods for an auto conversion. 4. Attempting to * construct a suitable value from original value. @param obj - * + * * @param node */ private void set(Object obj, XmlParser.Node node) throws Exception @@ -583,7 +583,7 @@ public class XmlConfiguration /* ------------------------------------------------------------ */ /* * Call a put method. - * + * * @param obj @param node */ private void put(Object obj, XmlParser.Node node) throws Exception @@ -602,7 +602,7 @@ public class XmlConfiguration /* ------------------------------------------------------------ */ /* * Call a get method. Any object returned from the call is passed to the configure method to consume the remaining elements. @param obj @param node - * + * * @return @exception Exception */ private Object get(Object obj, XmlParser.Node node) throws Exception @@ -648,7 +648,7 @@ public class XmlConfiguration * Call a method. A method is selected by trying all methods with matching names and number of arguments. Any object returned from the call is passed to * the configure method to consume the remaining elements. Note that if this is a static call we consider only methods declared directly in the given * class. i.e. we ignore any static methods in superclasses. @param obj - * + * * @param node @return @exception Exception */ private Object call(Object obj, XmlParser.Node node) throws Exception @@ -710,7 +710,7 @@ public class XmlConfiguration /* ------------------------------------------------------------ */ /* * Create a new value object. - * + * * @param obj @param node @return @exception Exception */ private Object newObj(Object obj, XmlParser.Node node) throws Exception @@ -785,7 +785,7 @@ public class XmlConfiguration /* ------------------------------------------------------------ */ /* * Reference an id value object. - * + * * @param obj @param node @return @exception NoSuchMethodException @exception ClassNotFoundException @exception InvocationTargetException */ private Object refObj(Object obj, XmlParser.Node node) throws Exception @@ -903,7 +903,7 @@ public class XmlConfiguration /* ------------------------------------------------------------ */ /* * Create a new value object. - * + * * @param obj @param node @return @exception Exception */ private Object propertyObj(Object obj, XmlParser.Node node) throws Exception @@ -1113,7 +1113,7 @@ public class XmlConfiguration *

    * Any IDs created in a configuration are passed to the next configuration file on the command line using {@link #getIdMap()} and {@link #setIdMap(Map)} . * This allows objects with IDs created in one config file to be referenced in subsequent config files on the command line. - * + * * @param args * array of property and xml configuration filenames or {@link Resource}s. */ @@ -1221,16 +1221,13 @@ public class XmlConfiguration Throwable th = exception.get(); if (th != null) { - if (th instanceof Exception) + if (th instanceof RuntimeException) + throw (RuntimeException)th; + else if (th instanceof Exception) throw (Exception)th; else if (th instanceof Error) throw (Error)th; - else if (th instanceof RuntimeException) - throw (RuntimeException)th; - else if (th instanceof ThreadDeath) - throw (ThreadDeath)th; throw new Error(th); } } - } diff --git a/pom.xml b/pom.xml index 12d10ef83a3..dcc7fb7a289 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ 19 jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT Jetty :: Project ${jetty.url} pom @@ -185,7 +185,7 @@ org.eclipse.jetty.toolchain jetty-version-maven-plugin - 1.0.6 + 1.0.7 org.apache.maven.plugins @@ -336,7 +336,6 @@ jetty-servlet jetty-webapp jetty-servlets - jetty-jsp-2.1 jetty-deploy jetty-ajp jetty-jndi diff --git a/test-continuation-jetty6/pom.xml b/test-continuation-jetty6/pom.xml index 16f73ba368a..02fa6e29176 100644 --- a/test-continuation-jetty6/pom.xml +++ b/test-continuation-jetty6/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 test-continuation-jetty6 diff --git a/test-continuation/pom.xml b/test-continuation/pom.xml index 622977bcf77..498bf751457 100644 --- a/test-continuation/pom.xml +++ b/test-continuation/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 test-continuation diff --git a/test-jetty-nested/pom.xml b/test-jetty-nested/pom.xml index 6263fe10db2..2f5b334f991 100644 --- a/test-jetty-nested/pom.xml +++ b/test-jetty-nested/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT test-jetty-nested Jetty :: Nested Test diff --git a/test-jetty-servlet/pom.xml b/test-jetty-servlet/pom.xml index 913686c973a..65f98aa0f62 100644 --- a/test-jetty-servlet/pom.xml +++ b/test-jetty-servlet/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 test-jetty-servlet diff --git a/test-jetty-servlet/src/main/java/Jetty400Repro.java b/test-jetty-servlet/src/main/java/Jetty400Repro.java index c47077857f0..1bdd320c6da 100644 --- a/test-jetty-servlet/src/main/java/Jetty400Repro.java +++ b/test-jetty-servlet/src/main/java/Jetty400Repro.java @@ -12,7 +12,6 @@ import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.DefaultHandler; import org.eclipse.jetty.server.handler.HandlerList; -import org.eclipse.jetty.util.resource.FileResource; import org.eclipse.jetty.webapp.WebAppContext; import org.xml.sax.SAXException; diff --git a/test-jetty-servlet/src/main/java/org/eclipse/jetty/testing/HttpTester.java b/test-jetty-servlet/src/main/java/org/eclipse/jetty/testing/HttpTester.java index 1dfe2b320d6..46e31be676b 100644 --- a/test-jetty-servlet/src/main/java/org/eclipse/jetty/testing/HttpTester.java +++ b/test-jetty-servlet/src/main/java/org/eclipse/jetty/testing/HttpTester.java @@ -26,6 +26,7 @@ import org.eclipse.jetty.http.HttpVersions; import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.ByteArrayBuffer; +import org.eclipse.jetty.io.EofException; import org.eclipse.jetty.io.SimpleBuffers; import org.eclipse.jetty.io.View; import org.eclipse.jetty.io.bio.StringEndPoint; @@ -130,15 +131,32 @@ public class HttpTester * @return Any unparsed data in the rawHTTP (eg pipelined requests) * @throws IOException */ - public String parse(String rawHTTP) throws IOException + public String parse(String rawHTTP, boolean isHeadResponse) throws IOException { _charset = _defaultCharset; ByteArrayBuffer buf = new ByteArrayBuffer(getByteArray(rawHTTP)); View view = new View(buf); - HttpParser parser = new HttpParser(view,new PH()); + PH ph = new PH(); + HttpParser parser = new HttpParser(view,ph); + parser.setHeadResponse(isHeadResponse); parser.parse(); + if (ph.isEarlyEOF()) + throw new EofException(); return getString(view.asArray()); } + + + /* ------------------------------------------------------------ */ + /** + * Parse one HTTP request or response + * @param rawHTTP Raw HTTP to parse + * @return Any unparsed data in the rawHTTP (eg pipelined requests) + * @throws IOException + */ + public String parse(String rawHTTP) throws IOException + { + return parse(rawHTTP, false); + } /* ------------------------------------------------------------ */ /** @@ -147,16 +165,32 @@ public class HttpTester * @return Any unparsed data in the rawHTTP (eg pipelined requests) * @throws IOException */ - public byte[] parse(byte[] rawHTTP) throws IOException + public byte[] parse(byte[] rawHTTP, boolean isHeadResponse) throws IOException { _charset = _defaultCharset; ByteArrayBuffer buf = new ByteArrayBuffer(rawHTTP); View view = new View(buf); - HttpParser parser = new HttpParser(view,new PH()); + PH ph = new PH(); + HttpParser parser = new HttpParser(view,ph); + parser.setHeadResponse(isHeadResponse); parser.parse(); + if (ph.isEarlyEOF()) + throw new EofException(); return view.asArray(); } + /* ------------------------------------------------------------ */ + /** + * Parse one HTTP request or response + * @param rawHTTP Raw HTTP to parse + * @return Any unparsed data in the rawHTTP (eg pipelined requests) + * @throws IOException + */ + public byte[] parse(byte[] rawHTTP) throws IOException + { + return parse(rawHTTP, false); + } + /* ------------------------------------------------------------ */ public String generate() throws IOException { @@ -484,6 +518,8 @@ public class HttpTester /* ------------------------------------------------------------ */ private class PH extends HttpParser.EventHandler { + private volatile boolean _earlyEOF; + @Override public void startRequest(Buffer method, Buffer url, Buffer version) throws IOException { @@ -532,6 +568,18 @@ public class HttpTester _parsedContent=new ByteArrayOutputStream2(); _parsedContent.write(ref.asArray()); } + + @Override + public void earlyEOF() + { + _earlyEOF = true; + } + + public boolean isEarlyEOF() + { + return _earlyEOF; + } + } } diff --git a/test-jetty-servlet/src/test/java/org/eclipse/jetty/testing/HttpTesterTest.java b/test-jetty-servlet/src/test/java/org/eclipse/jetty/testing/HttpTesterTest.java index b019d34da78..b12ea713cce 100644 --- a/test-jetty-servlet/src/test/java/org/eclipse/jetty/testing/HttpTesterTest.java +++ b/test-jetty-servlet/src/test/java/org/eclipse/jetty/testing/HttpTesterTest.java @@ -36,5 +36,21 @@ public class HttpTesterTest extends TestCase assertEquals("utf-8",tester.getCharacterEncoding()); assertEquals("123456789\uA74A",tester.getContent()); } + + + public void testHead() throws Exception + { + String headResponse = "HTTP/1.1 200 OK\r\n"+ + "Content-Type: text/html\r\n"+ + "Content-Length: 22\r\n"+ + "\r\n"; + + HttpTester tester = new HttpTester(); + tester.parse(headResponse, true); + assertEquals(200, tester.getStatus()); + assertEquals("22", tester.getHeader("Content-Length")); + assertEquals("text/html",tester.getContentType()); + System.err.println(tester.getContent()); + } } diff --git a/test-jetty-webapp/pom.xml b/test-jetty-webapp/pom.xml index 28f154ef2bc..5c9fbf6c19b 100644 --- a/test-jetty-webapp/pom.xml +++ b/test-jetty-webapp/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 org.eclipse.jetty @@ -209,5 +209,11 @@ 2.1 provided + + javax.servlet + jstl + 1.2 + provided + diff --git a/test-jetty-webapp/src/main/java/com/acme/Dump.java b/test-jetty-webapp/src/main/java/com/acme/Dump.java index 0da3a69dbfc..063e38b3b13 100644 --- a/test-jetty-webapp/src/main/java/com/acme/Dump.java +++ b/test-jetty-webapp/src/main/java/com/acme/Dump.java @@ -28,6 +28,8 @@ import java.util.Collection; import java.util.Date; import java.util.Enumeration; import java.util.Locale; +import java.util.Timer; +import java.util.TimerTask; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; @@ -64,6 +66,7 @@ public class Dump extends HttpServlet private static final Logger LOG = Log.getLogger(Dump.class); boolean fixed; + Timer _timer; /* ------------------------------------------------------------ */ @Override @@ -77,6 +80,8 @@ public class Dump extends HttpServlet fixed=true; throw new UnavailableException("Unavailable test",Integer.parseInt(config.getInitParameter("unavailable"))); } + + _timer=new Timer(true); } /* ------------------------------------------------------------ */ @@ -168,40 +173,26 @@ public class Dump extends HttpServlet request.setAttribute("RESUME",Boolean.TRUE); final long resume=Long.parseLong(request.getParameter("resume")); - new Thread(new Runnable() + final Continuation continuation = ContinuationSupport.getContinuation(request); + _timer.schedule(new TimerTask() { + @Override public void run() { - try - { - Thread.sleep(resume); - } - catch (InterruptedException e) - { - e.printStackTrace(); - } - Continuation continuation = ContinuationSupport.getContinuation(request); continuation.resume(); } - - }).start(); + },resume); + } if (request.getParameter("complete")!=null) { final long complete=Long.parseLong(request.getParameter("complete")); - new Thread(new Runnable() + _timer.schedule(new TimerTask() { + @Override public void run() { - try - { - Thread.sleep(complete); - } - catch (InterruptedException e) - { - e.printStackTrace(); - } try { response.setContentType("text/html"); @@ -209,13 +200,12 @@ public class Dump extends HttpServlet Continuation continuation = ContinuationSupport.getContinuation(request); continuation.complete(); } - catch (IOException e) + catch(Exception e) { e.printStackTrace(); } } - - }).start(); + },complete); } if (request.getParameter("suspend")!=null && request.getAttribute("SUSPEND")!=Boolean.TRUE) @@ -796,7 +786,7 @@ public class Dump extends HttpServlet } catch (Exception e) { - getServletContext().log("dump", e); + getServletContext().log("dump "+e); } String lines= request.getParameter("lines"); @@ -839,6 +829,7 @@ public class Dump extends HttpServlet @Override public synchronized void destroy() { + _timer.cancel(); } /* ------------------------------------------------------------ */ diff --git a/test-jetty-webapp/src/main/webapp/WEB-INF/web.xml b/test-jetty-webapp/src/main/webapp/WEB-INF/web.xml index 50f475ec36b..c08ca7fdbd9 100644 --- a/test-jetty-webapp/src/main/webapp/WEB-INF/web.xml +++ b/test-jetty-webapp/src/main/webapp/WEB-INF/web.xml @@ -36,7 +36,7 @@ org.eclipse.jetty.servlets.QoSFilter maxRequests - 20 + 10000 managedAttr diff --git a/test-jetty-webapp/src/test/java/org/eclipse/jetty/TestServer.java b/test-jetty-webapp/src/test/java/org/eclipse/jetty/TestServer.java index c5fb5d9d0ac..8e87ee83df1 100644 --- a/test-jetty-webapp/src/test/java/org/eclipse/jetty/TestServer.java +++ b/test-jetty-webapp/src/test/java/org/eclipse/jetty/TestServer.java @@ -21,7 +21,7 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.eclipse.jetty.http.ssl.SslContextFactory; +import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.jmx.MBeanContainer; import org.eclipse.jetty.security.HashLoginService; import org.eclipse.jetty.server.Handler; diff --git a/tests/pom.xml b/tests/pom.xml index 71be5773d95..28cee13d78b 100644 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -21,7 +21,7 @@ org.eclipse.jetty jetty-project - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT org.eclipse.jetty.tests tests-parent @@ -37,14 +37,6 @@ true - - org.apache.maven.plugins - maven-deploy-plugin - - - true - - diff --git a/tests/test-integration/pom.xml b/tests/test-integration/pom.xml index 07c82cafc06..bf1daf62b96 100644 --- a/tests/test-integration/pom.xml +++ b/tests/test-integration/pom.xml @@ -20,7 +20,7 @@ org.eclipse.jetty.tests tests-parent - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT 4.0.0 test-integration diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/DigestPostTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/DigestPostTest.java index 60a15693333..bb491353fc1 100644 --- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/DigestPostTest.java +++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/DigestPostTest.java @@ -16,8 +16,6 @@ import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.security.Realm; import org.eclipse.jetty.client.security.SimpleRealmResolver; import org.eclipse.jetty.http.HttpMethods; -import org.eclipse.jetty.http.security.Constraint; -import org.eclipse.jetty.http.security.Password; import org.eclipse.jetty.io.ByteArrayBuffer; import org.eclipse.jetty.security.ConstraintMapping; import org.eclipse.jetty.security.ConstraintSecurityHandler; @@ -33,6 +31,8 @@ import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.TypeUtil; +import org.eclipse.jetty.util.security.Constraint; +import org.eclipse.jetty.util.security.Password; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/rfcs/RFC2616BaseTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/rfcs/RFC2616BaseTest.java index ae4ed46a43e..60cc571af76 100644 --- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/rfcs/RFC2616BaseTest.java +++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/rfcs/RFC2616BaseTest.java @@ -1075,8 +1075,6 @@ public abstract class RFC2616BaseTest specId = "10.3 Redirection HTTP/1.0 - basic"; response.assertStatus(specId,HttpStatus.FOUND_302); response.assertHeader(specId,"Location",serverURI + "/tests/"); - response.assertHeader(specId,"Connection","close"); - } /** @@ -1136,9 +1134,6 @@ public abstract class RFC2616BaseTest String specId = "10.3 Redirection HTTP/1.0 w/content"; response.assertStatus(specId,HttpStatus.FOUND_302); response.assertHeader(specId,"Location",server.getScheme() + "://localhost/tests/R1.txt"); - response.assertHeader(specId,"Connection","close"); - response.assertHeaderNotPresent(specId,"Content-Length"); - } /** @@ -1803,7 +1798,6 @@ public abstract class RFC2616BaseTest response = responses.get(1); // response 2 response.assertStatusOK(specId); - response.assertHeader(specId,"Connection","close"); response.assertBodyContains(specId,"Resource=R2"); /* Compatibility with HTTP/1.0 */ @@ -1849,7 +1843,6 @@ public abstract class RFC2616BaseTest response = responses.get(2); response.assertStatusOK(specId); - response.assertHeader(specId,"Connection","close"); response.assertBody(specId,"Host=Default\nResource=R2\n"); } diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/rfcs/RFC2616NIOHttpsTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/rfcs/RFC2616NIOHttpsTest.java index ebdc675d71f..beaf97cd2de 100644 --- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/rfcs/RFC2616NIOHttpsTest.java +++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/rfcs/RFC2616NIOHttpsTest.java @@ -44,4 +44,9 @@ public class RFC2616NIOHttpsTest extends RFC2616BaseTest { return new HttpsSocketImpl(); } + + public void test8_2_ExpectInvalid() throws Exception + { + super.test8_2_ExpectInvalid(); + } } diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/EchoHandler.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/EchoHandler.java index a1ea78406a6..2ed885db932 100644 --- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/EchoHandler.java +++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/EchoHandler.java @@ -24,7 +24,6 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.eclipse.jetty.server.HttpConnection; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.AbstractHandler; @@ -32,8 +31,7 @@ public class EchoHandler extends AbstractHandler { public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { - Request base_request = (request instanceof Request)?(Request)request:HttpConnection.getCurrentConnection().getRequest(); - base_request.setHandled(true); + baseRequest.setHandled(true); if (request.getContentType() != null) response.setContentType(request.getContentType()); diff --git a/tests/test-loginservice/pom.xml b/tests/test-loginservice/pom.xml index 981d3f6adc5..a5e08c91017 100644 --- a/tests/test-loginservice/pom.xml +++ b/tests/test-loginservice/pom.xml @@ -21,7 +21,7 @@ org.eclipse.jetty.tests tests-parent - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT test-loginservice Jetty Tests :: Login Service diff --git a/tests/test-loginservice/src/test/java/org/eclipse/jetty/JdbcLoginServiceTest.java b/tests/test-loginservice/src/test/java/org/eclipse/jetty/JdbcLoginServiceTest.java index 6eb658dd57e..122a8f2fe76 100644 --- a/tests/test-loginservice/src/test/java/org/eclipse/jetty/JdbcLoginServiceTest.java +++ b/tests/test-loginservice/src/test/java/org/eclipse/jetty/JdbcLoginServiceTest.java @@ -44,7 +44,7 @@ import org.eclipse.jetty.client.security.Realm; import org.eclipse.jetty.client.security.SimpleRealmResolver; import org.eclipse.jetty.http.HttpMethods; import org.eclipse.jetty.http.HttpStatus; -import org.eclipse.jetty.http.security.Constraint; +import org.eclipse.jetty.util.security.Constraint; import org.eclipse.jetty.io.ByteArrayBuffer; import org.eclipse.jetty.io.EofException; import org.eclipse.jetty.security.ConstraintMapping; diff --git a/tests/test-sessions/pom.xml b/tests/test-sessions/pom.xml index 8422080a703..8c829fa6935 100644 --- a/tests/test-sessions/pom.xml +++ b/tests/test-sessions/pom.xml @@ -21,7 +21,7 @@ org.eclipse.jetty.tests tests-parent - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT test-sessions-parent Jetty Tests :: Sessions :: Parent diff --git a/tests/test-sessions/test-hash-sessions/pom.xml b/tests/test-sessions/test-hash-sessions/pom.xml index bfc169deaf7..1152f8a40e2 100644 --- a/tests/test-sessions/test-hash-sessions/pom.xml +++ b/tests/test-sessions/test-hash-sessions/pom.xml @@ -21,20 +21,28 @@ org.eclipse.jetty.tests test-sessions-parent - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT test-hash-sessions Jetty Tests :: Sessions :: Hash - - - maven-compiler-plugin - - 1.5 - 1.5 - - - + + + maven-compiler-plugin + + 1.5 + 1.5 + + + + org.apache.maven.plugins + maven-deploy-plugin + + + true + + + diff --git a/tests/test-sessions/test-jdbc-sessions/pom.xml b/tests/test-sessions/test-jdbc-sessions/pom.xml index a8977083e65..567cfad6daa 100644 --- a/tests/test-sessions/test-jdbc-sessions/pom.xml +++ b/tests/test-sessions/test-jdbc-sessions/pom.xml @@ -21,20 +21,28 @@ org.eclipse.jetty.tests test-sessions-parent - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT test-jdbc-sessions Jetty Tests :: Sessions :: JDBC - - - maven-compiler-plugin - - 1.5 - 1.5 - - - + + + maven-compiler-plugin + + 1.5 + 1.5 + + + + org.apache.maven.plugins + maven-deploy-plugin + + + true + + + diff --git a/tests/test-sessions/test-sessions-common/pom.xml b/tests/test-sessions/test-sessions-common/pom.xml index 7508516f24c..2979f679288 100644 --- a/tests/test-sessions/test-sessions-common/pom.xml +++ b/tests/test-sessions/test-sessions-common/pom.xml @@ -21,7 +21,7 @@ org.eclipse.jetty.tests test-sessions-parent - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT test-sessions-common Jetty Tests :: Sessions :: Common diff --git a/tests/test-webapps/pom.xml b/tests/test-webapps/pom.xml index bdd30b579dd..c34dda4cdc9 100644 --- a/tests/test-webapps/pom.xml +++ b/tests/test-webapps/pom.xml @@ -21,12 +21,22 @@ org.eclipse.jetty.tests tests-parent - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT test-webapps-parent Jetty Tests :: WebApps :: Parent pom + + + org.apache.maven.plugins + maven-deploy-plugin + + + true + + + test-webapp-rfc2616 diff --git a/tests/test-webapps/test-webapp-rfc2616/pom.xml b/tests/test-webapps/test-webapp-rfc2616/pom.xml index 875eb80e439..fcc140014f9 100644 --- a/tests/test-webapps/test-webapp-rfc2616/pom.xml +++ b/tests/test-webapps/test-webapp-rfc2616/pom.xml @@ -21,12 +21,22 @@ org.eclipse.jetty.tests test-webapps-parent - 7.5.4-SNAPSHOT + 7.6.0-SNAPSHOT test-webapp-rfc2616 Jetty Tests :: WebApp :: RFC2616 war + + + org.apache.maven.plugins + maven-deploy-plugin + + + true + + +