Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'jetty-server/src/main')
-rw-r--r--jetty-server/src/main/config/etc/jetty-http-forwarded.xml17
-rw-r--r--jetty-server/src/main/config/etc/jetty.xml5
-rw-r--r--jetty-server/src/main/config/modules/continuation.mod7
-rw-r--r--jetty-server/src/main/config/modules/debug.mod7
-rw-r--r--jetty-server/src/main/config/modules/debuglog.mod6
-rw-r--r--jetty-server/src/main/config/modules/ext.mod6
-rw-r--r--jetty-server/src/main/config/modules/gzip.mod7
-rw-r--r--jetty-server/src/main/config/modules/home-base-warning.mod6
-rw-r--r--jetty-server/src/main/config/modules/http-forwarded.mod20
-rw-r--r--jetty-server/src/main/config/modules/http.mod7
-rw-r--r--jetty-server/src/main/config/modules/https.mod6
-rw-r--r--jetty-server/src/main/config/modules/ipaccess.mod6
-rw-r--r--jetty-server/src/main/config/modules/jdbc-sessions.mod6
-rw-r--r--jetty-server/src/main/config/modules/jvm.mod3
-rw-r--r--jetty-server/src/main/config/modules/lowresources.mod7
-rw-r--r--jetty-server/src/main/config/modules/proxy-protocol-ssl.mod9
-rw-r--r--jetty-server/src/main/config/modules/proxy-protocol.mod10
-rw-r--r--jetty-server/src/main/config/modules/requestlog.mod5
-rw-r--r--jetty-server/src/main/config/modules/resources.mod7
-rw-r--r--jetty-server/src/main/config/modules/server.mod5
-rw-r--r--jetty-server/src/main/config/modules/ssl.mod7
-rw-r--r--jetty-server/src/main/config/modules/stats.mod6
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java6
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/AbstractNCSARequestLog.java2
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/DebugListener.java1
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java63
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/ForwardedRequestCustomizer.java1
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java225
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java317
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java31
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/LocalConnector.java15
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/NetworkTrafficServerConnector.java4
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java437
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilder.java198
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilderImpl.java122
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/Request.java47
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/RequestLogCollection.java4
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/Response.java200
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/SecureRequestCustomizer.java24
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java15
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/SocketCustomizationListener.java8
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/handler/AbstractHandler.java32
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java35
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/handler/DebugHandler.java1
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorHandler.java217
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerCollection.java1
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerWrapper.java2
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java45
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java2
49 files changed, 1403 insertions, 817 deletions
diff --git a/jetty-server/src/main/config/etc/jetty-http-forwarded.xml b/jetty-server/src/main/config/etc/jetty-http-forwarded.xml
new file mode 100644
index 0000000000..0aacbb2468
--- /dev/null
+++ b/jetty-server/src/main/config/etc/jetty-http-forwarded.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
+<Configure id="httpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
+ <Call name="addCustomizer">
+ <Arg>
+ <New class="org.eclipse.jetty.server.ForwardedRequestCustomizer">
+ <Set name="forwardedHostHeader"><Property name="jetty.httpConfig.forwardedHostHeader" default="X-Forwarded-Host"/></Set>
+ <Set name="forwardedServerHeader"><Property name="jetty.httpConfig.forwardedServerHeader" default="X-Forwarded-Server"/></Set>
+ <Set name="forwardedProtoHeader"><Property name="jetty.httpConfig.forwardedProtoHeader" default="X-Forwarded-Proto"/></Set>
+ <Set name="forwardedForHeader"><Property name="jetty.httpConfig.forwardedForHeader" default="X-Forwarded-For"/></Set>
+ <Set name="forwardedSslSessionIdHeader"><Property name="jetty.httpConfig.forwardedSslSessionIdHeader" /></Set>
+ <Set name="forwardedCipherSuiteHeader"><Property name="jetty.httpConfig.forwardedCipherSuiteHeader" /></Set>
+ </New>
+ </Arg>
+ </Call>
+</Configure>
+
diff --git a/jetty-server/src/main/config/etc/jetty.xml b/jetty-server/src/main/config/etc/jetty.xml
index 5412979cac..8e6d1a4ae5 100644
--- a/jetty-server/src/main/config/etc/jetty.xml
+++ b/jetty-server/src/main/config/etc/jetty.xml
@@ -89,11 +89,6 @@
<Set name="delayDispatchUntilContent"><Property name="jetty.httpConfig.delayDispatchUntilContent" deprecated="jetty.delayDispatchUntilContent" default="true"/></Set>
<Set name="maxErrorDispatches"><Property name="jetty.httpConfig.maxErrorDispatches" default="10"/></Set>
<Set name="blockingTimeout"><Property name="jetty.httpConfig.blockingTimeout" default="-1"/></Set>
- <!-- Uncomment to enable handling of X-Forwarded- style headers
- <Call name="addCustomizer">
- <Arg><New class="org.eclipse.jetty.server.ForwardedRequestCustomizer"/></Arg>
- </Call>
- -->
</New>
<!-- =========================================================== -->
diff --git a/jetty-server/src/main/config/modules/continuation.mod b/jetty-server/src/main/config/modules/continuation.mod
index 231c09d0f3..af03ae41ce 100644
--- a/jetty-server/src/main/config/modules/continuation.mod
+++ b/jetty-server/src/main/config/modules/continuation.mod
@@ -1,6 +1,7 @@
-#
-# Classic Jetty Continuation Support Module
-#
+[description]
+Enables support for Continuation style asynchronous
+Servlets. Now deprecated in favour of Servlet 3.1
+API
[lib]
lib/jetty-continuation-${jetty.version}.jar
diff --git a/jetty-server/src/main/config/modules/debug.mod b/jetty-server/src/main/config/modules/debug.mod
index 0141699461..7b75ecc0e7 100644
--- a/jetty-server/src/main/config/modules/debug.mod
+++ b/jetty-server/src/main/config/modules/debug.mod
@@ -1,6 +1,7 @@
-#
-# Debug module
-#
+[description]
+Enables the DebugListener to generate additional
+logging regarding detailed request handling events.
+Renames threads to include request URI.
[depend]
deploy
diff --git a/jetty-server/src/main/config/modules/debuglog.mod b/jetty-server/src/main/config/modules/debuglog.mod
index ba8b60a727..a76f728a5b 100644
--- a/jetty-server/src/main/config/modules/debuglog.mod
+++ b/jetty-server/src/main/config/modules/debuglog.mod
@@ -1,6 +1,6 @@
-#
-# Debug module
-#
+[description]
+Deprecated Debug Log using the DebugHandle.
+Replaced with the debug module.
[depend]
server
diff --git a/jetty-server/src/main/config/modules/ext.mod b/jetty-server/src/main/config/modules/ext.mod
index 56b10f7ea4..4171f8dfc2 100644
--- a/jetty-server/src/main/config/modules/ext.mod
+++ b/jetty-server/src/main/config/modules/ext.mod
@@ -1,6 +1,6 @@
-#
-# Module to add all lib/ext/**.jar files to classpath
-#
+[description]
+Adds all jar files discovered in $JETTY_HOME/lib/ext
+and $JETTY_BASE/lib/ext to the servers classpath.
[lib]
lib/ext/**.jar
diff --git a/jetty-server/src/main/config/modules/gzip.mod b/jetty-server/src/main/config/modules/gzip.mod
index 1efc834648..65663a1606 100644
--- a/jetty-server/src/main/config/modules/gzip.mod
+++ b/jetty-server/src/main/config/modules/gzip.mod
@@ -1,7 +1,6 @@
-#
-# GZIP module
-# Applies GzipHandler to entire server
-#
+[description]
+Enable GzipHandler for dynamic gzip compression
+for the entire server.
[depend]
server
diff --git a/jetty-server/src/main/config/modules/home-base-warning.mod b/jetty-server/src/main/config/modules/home-base-warning.mod
index 28e5757e81..3e599f0788 100644
--- a/jetty-server/src/main/config/modules/home-base-warning.mod
+++ b/jetty-server/src/main/config/modules/home-base-warning.mod
@@ -1,6 +1,6 @@
-#
-# Home and Base Warning
-#
+[description]
+Generates a warning that server has been run from $JETTY_HOME
+rather than from a $JETTY_BASE.
[xml]
etc/home-base-warning.xml
diff --git a/jetty-server/src/main/config/modules/http-forwarded.mod b/jetty-server/src/main/config/modules/http-forwarded.mod
new file mode 100644
index 0000000000..60f10da736
--- /dev/null
+++ b/jetty-server/src/main/config/modules/http-forwarded.mod
@@ -0,0 +1,20 @@
+[description]
+Adds a forwarded request customizer to the HTTP Connector
+to process forwarded-for style headers from a proxy.
+
+[depend]
+http
+
+[xml]
+etc/jetty-http-forwarded.xml
+
+[ini-template]
+### ForwardedRequestCustomizer Configuration
+
+# jetty.httpConfig.forwardedHostHeader=X-Forwarded-Host
+# jetty.httpConfig.forwardedServerHeader=X-Forwarded-Server
+# jetty.httpConfig.forwardedProtoHeader=X-Forwarded-Proto
+# jetty.httpConfig.forwardedForHeader=X-Forwarded-For
+# jetty.httpConfig.forwardedSslSessionIdHeader=
+# jetty.httpConfig.forwardedCipherSuiteHeader=
+
diff --git a/jetty-server/src/main/config/modules/http.mod b/jetty-server/src/main/config/modules/http.mod
index 01e986243e..c59ee4b4d9 100644
--- a/jetty-server/src/main/config/modules/http.mod
+++ b/jetty-server/src/main/config/modules/http.mod
@@ -1,6 +1,7 @@
-#
-# Jetty HTTP Connector
-#
+[description]
+Enables a HTTP connector on the server.
+By default HTTP/1 is support, but HTTP2C can
+be added to the connector with the http2c module.
[depend]
server
diff --git a/jetty-server/src/main/config/modules/https.mod b/jetty-server/src/main/config/modules/https.mod
index 092e0d70c7..6ffbd69d0c 100644
--- a/jetty-server/src/main/config/modules/https.mod
+++ b/jetty-server/src/main/config/modules/https.mod
@@ -1,12 +1,12 @@
-#
-# Jetty HTTPS Connector
-#
+[description]
+Adds HTTPS protocol support to the TLS(SSL) Connector
[depend]
ssl
[optional]
http2
+http-forwarded
[xml]
etc/jetty-https.xml
diff --git a/jetty-server/src/main/config/modules/ipaccess.mod b/jetty-server/src/main/config/modules/ipaccess.mod
index 956ea0f2e3..68f04dfc57 100644
--- a/jetty-server/src/main/config/modules/ipaccess.mod
+++ b/jetty-server/src/main/config/modules/ipaccess.mod
@@ -1,6 +1,6 @@
-#
-# IPAccess module
-#
+[description]
+Enable the ipaccess handler to apply a white/black list
+control of the remote IP of requests.
[depend]
server
diff --git a/jetty-server/src/main/config/modules/jdbc-sessions.mod b/jetty-server/src/main/config/modules/jdbc-sessions.mod
index d77ff043e2..9fe2beba15 100644
--- a/jetty-server/src/main/config/modules/jdbc-sessions.mod
+++ b/jetty-server/src/main/config/modules/jdbc-sessions.mod
@@ -1,6 +1,5 @@
-#
-# Jetty JDBC Session module
-#
+[description]
+Enables JDBC Session management.
[depend]
annotations
@@ -9,7 +8,6 @@ webapp
[xml]
etc/jetty-jdbc-sessions.xml
-
[ini-template]
## JDBC Session config
diff --git a/jetty-server/src/main/config/modules/jvm.mod b/jetty-server/src/main/config/modules/jvm.mod
index 195521c57f..296c1b6a2b 100644
--- a/jetty-server/src/main/config/modules/jvm.mod
+++ b/jetty-server/src/main/config/modules/jvm.mod
@@ -1,3 +1,6 @@
+[description]
+A noop module that creates an ini template useful for
+setting JVM arguments (eg -Xmx )
[ini-template]
## JVM Configuration
## If JVM args are include in an ini file then --exec is needed
diff --git a/jetty-server/src/main/config/modules/lowresources.mod b/jetty-server/src/main/config/modules/lowresources.mod
index 2f765d9af2..257829afd8 100644
--- a/jetty-server/src/main/config/modules/lowresources.mod
+++ b/jetty-server/src/main/config/modules/lowresources.mod
@@ -1,6 +1,7 @@
-#
-# Low Resources module
-#
+[description]
+Enables a low resource monitor on the server
+that can take actions if threads and/or connections
+cross configured threshholds.
[depend]
server
diff --git a/jetty-server/src/main/config/modules/proxy-protocol-ssl.mod b/jetty-server/src/main/config/modules/proxy-protocol-ssl.mod
index 764d24b847..374763d0b5 100644
--- a/jetty-server/src/main/config/modules/proxy-protocol-ssl.mod
+++ b/jetty-server/src/main/config/modules/proxy-protocol-ssl.mod
@@ -1,6 +1,9 @@
-#
-# PROXY Protocol Module - SSL
-#
+[description]
+Enables the Proxy Protocol on the TLS(SSL) Connector.
+http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt
+This allows a Proxy operating in TCP mode to transport
+details of the proxied connection to the server.
+Both V1 and V2 versions of the protocol are supported.
[depend]
ssl
diff --git a/jetty-server/src/main/config/modules/proxy-protocol.mod b/jetty-server/src/main/config/modules/proxy-protocol.mod
index 9df2700f4e..48820e5c14 100644
--- a/jetty-server/src/main/config/modules/proxy-protocol.mod
+++ b/jetty-server/src/main/config/modules/proxy-protocol.mod
@@ -1,6 +1,10 @@
-#
-# PROXY Protocol Module - HTTP
-#
+[description]
+Enables the Proxy Protocol on the HTTP Connector.
+http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt
+This allows a proxy operating in TCP mode to
+transport details of the proxied connection to
+the server.
+Both V1 and V2 versions of the protocol are supported.
[depend]
http
diff --git a/jetty-server/src/main/config/modules/requestlog.mod b/jetty-server/src/main/config/modules/requestlog.mod
index e27b246ea2..c849f65f31 100644
--- a/jetty-server/src/main/config/modules/requestlog.mod
+++ b/jetty-server/src/main/config/modules/requestlog.mod
@@ -1,6 +1,5 @@
-#
-# Request Log module
-#
+[description]
+Enables a NCSA style request log.
[depend]
server
diff --git a/jetty-server/src/main/config/modules/resources.mod b/jetty-server/src/main/config/modules/resources.mod
index 8647d81325..5648948640 100644
--- a/jetty-server/src/main/config/modules/resources.mod
+++ b/jetty-server/src/main/config/modules/resources.mod
@@ -1,6 +1,7 @@
-#
-# Module to add resources directory to classpath
-#
+[description]
+Adds the $JETTY_HOME/resources and/or $JETTY_BASE/resources
+directory to the server classpath. Useful for configuration
+property files (eg jetty-logging.properties)
[lib]
resources/
diff --git a/jetty-server/src/main/config/modules/server.mod b/jetty-server/src/main/config/modules/server.mod
index 14d6b58e88..19e21c56fe 100644
--- a/jetty-server/src/main/config/modules/server.mod
+++ b/jetty-server/src/main/config/modules/server.mod
@@ -1,6 +1,5 @@
-#
-# Base Server Module
-#
+[description]
+Enables the core Jetty server on the classpath.
[optional]
jvm
diff --git a/jetty-server/src/main/config/modules/ssl.mod b/jetty-server/src/main/config/modules/ssl.mod
index 292780a1cb..acc8d380c9 100644
--- a/jetty-server/src/main/config/modules/ssl.mod
+++ b/jetty-server/src/main/config/modules/ssl.mod
@@ -1,6 +1,7 @@
-#
-# SSL Keystore module
-#
+[description]
+Enables a TLS(SSL) Connector on the server.
+This may be used for HTTPS and/or HTTP2 by enabling
+the associated support modules.
[name]
ssl
diff --git a/jetty-server/src/main/config/modules/stats.mod b/jetty-server/src/main/config/modules/stats.mod
index 0922469cdf..838d54a904 100644
--- a/jetty-server/src/main/config/modules/stats.mod
+++ b/jetty-server/src/main/config/modules/stats.mod
@@ -1,6 +1,6 @@
-#
-# Stats module
-#
+[description]
+Enable detailed statistics collection for the server,
+available via JMX.
[depend]
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 bfd8206df9..c46ed08be5 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
@@ -253,9 +253,11 @@ public abstract class AbstractConnector extends ContainerLifeCycle implements Co
@Override
protected void doStart() throws Exception
{
+ if(_defaultProtocol==null)
+ throw new IllegalStateException("No default protocol for "+this);
_defaultConnectionFactory = getConnectionFactory(_defaultProtocol);
if(_defaultConnectionFactory==null)
- throw new IllegalStateException("No protocol factory for default protocol: "+_defaultProtocol);
+ throw new IllegalStateException("No protocol factory for default protocol '"+_defaultProtocol+"' in "+this);
super.doStart();
@@ -298,7 +300,7 @@ public abstract class AbstractConnector extends ContainerLifeCycle implements Co
// If we have a stop timeout
long stopTimeout = getStopTimeout();
CountDownLatch stopping=_stopping;
- if (stopTimeout > 0 && stopping!=null)
+ if (stopTimeout > 0 && stopping!=null && getAcceptors()>0)
stopping.await(stopTimeout,TimeUnit.MILLISECONDS);
_stopping=null;
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractNCSARequestLog.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractNCSARequestLog.java
index ab46bd5217..036b5142ad 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractNCSARequestLog.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractNCSARequestLog.java
@@ -142,7 +142,7 @@ public abstract class AbstractNCSARequestLog extends AbstractLifeCycle implement
buf.append("] \"");
append(buf,request.getMethod());
buf.append(' ');
- append(buf,request.getHttpURI().toString());
+ append(buf,request.getOriginalURI());
buf.append(' ');
append(buf,request.getProtocol());
buf.append("\" ");
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/DebugListener.java b/jetty-server/src/main/java/org/eclipse/jetty/server/DebugListener.java
index e1f6a3e36b..955a655775 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/DebugListener.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/DebugListener.java
@@ -33,7 +33,6 @@ import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandler.Context;
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 68798b87a7..1a04bd749a 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
@@ -31,16 +31,15 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.eclipse.jetty.http.HttpFields;
-import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpURI;
-import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.MultiMap;
public class Dispatcher implements RequestDispatcher
{
+ public final static String __ERROR_DISPATCH="org.eclipse.jetty.server.Dispatcher.ERROR";
+
/** Dispatch include attribute names */
public final static String __INCLUDE_PREFIX="javax.servlet.include.";
@@ -76,7 +75,15 @@ public class Dispatcher implements RequestDispatcher
public void error(ServletRequest request, ServletResponse response) throws ServletException, IOException
{
- forward(request, response, DispatcherType.ERROR);
+ try
+ {
+ request.setAttribute(__ERROR_DISPATCH,Boolean.TRUE);
+ forward(request, response, DispatcherType.ERROR);
+ }
+ finally
+ {
+ request.setAttribute(__ERROR_DISPATCH,null);
+ }
}
@Override
@@ -129,7 +136,7 @@ public class Dispatcher implements RequestDispatcher
protected void forward(ServletRequest request, ServletResponse response, DispatcherType dispatch) throws ServletException, IOException
{
- Request baseRequest=Request.getBaseRequest(request);
+ Request baseRequest=Request.getBaseRequest(request);
Response base_response=baseRequest.getResponse();
base_response.resetForForward();
@@ -137,21 +144,18 @@ public class Dispatcher implements RequestDispatcher
request = new ServletRequestHttpWrapper(request);
if (!(response instanceof HttpServletResponse))
response = new ServletResponseHttpWrapper(response);
-
- final boolean old_handled=baseRequest.isHandled();
-
+
final HttpURI old_uri=baseRequest.getHttpURI();
final String old_context_path=baseRequest.getContextPath();
final String old_servlet_path=baseRequest.getServletPath();
final String old_path_info=baseRequest.getPathInfo();
-
+
final MultiMap<String> old_query_params=baseRequest.getQueryParameters();
final Attributes old_attr=baseRequest.getAttributes();
final DispatcherType old_type=baseRequest.getDispatcherType();
try
{
- baseRequest.setHandled(false);
baseRequest.setDispatcherType(dispatch);
if (_named!=null)
@@ -182,18 +186,18 @@ public class Dispatcher implements RequestDispatcher
attr._contextPath=old_context_path;
attr._servletPath=old_servlet_path;
}
-
+
HttpURI uri = new HttpURI(old_uri.getScheme(),old_uri.getHost(),old_uri.getPort(),
_uri.getPath(),_uri.getParam(),_uri.getQuery(),_uri.getFragment());
-
+
baseRequest.setHttpURI(uri);
-
+
baseRequest.setContextPath(_contextHandler.getContextPath());
baseRequest.setServletPath(null);
baseRequest.setPathInfo(_pathInContext);
if (_uri.getQuery()!=null || old_uri.getQuery()!=null)
baseRequest.mergeQueryParameters(old_uri.getQuery(),_uri.getQuery(), true);
-
+
baseRequest.setAttributes(attr);
_contextHandler.handle(_pathInContext, baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
@@ -204,7 +208,6 @@ public class Dispatcher implements RequestDispatcher
}
finally
{
- baseRequest.setHandled(old_handled);
baseRequest.setHttpURI(old_uri);
baseRequest.setContextPath(old_context_path);
baseRequest.setServletPath(old_servlet_path);
@@ -215,35 +218,7 @@ public class Dispatcher implements RequestDispatcher
baseRequest.setDispatcherType(old_type);
}
}
-
- /**
- * <p>Pushes a secondary resource identified by this dispatcher.</p>
- *
- * @param request the primary request
- * @deprecated Use {@link Request#getPushBuilder()} instead
- */
- @Deprecated
- public void push(ServletRequest request)
- {
- Request baseRequest = Request.getBaseRequest(request);
- HttpFields fields = new HttpFields(baseRequest.getHttpFields());
-
- String query=baseRequest.getQueryString();
- if (_uri.hasQuery())
- {
- if (query==null)
- query=_uri.getQuery();
- else
- query=query+"&"+_uri.getQuery(); // TODO is this correct semantic?
- }
-
- HttpURI uri = HttpURI.createHttpURI(request.getScheme(),request.getServerName(),request.getServerPort(),_uri.getPath(),baseRequest.getHttpURI().getParam(),query,null);
-
- MetaData.Request push = new MetaData.Request(HttpMethod.GET.asString(),uri,baseRequest.getHttpVersion(),fields);
-
- baseRequest.getHttpChannel().getHttpTransport().push(push);
- }
-
+
@Override
public String toString()
{
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ForwardedRequestCustomizer.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ForwardedRequestCustomizer.java
index 813ffd06d6..16aba09eec 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/ForwardedRequestCustomizer.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ForwardedRequestCustomizer.java
@@ -215,6 +215,7 @@ public class ForwardedRequestCustomizer implements Customizer
{
request.setAttribute("javax.servlet.request.ssl_session_id", ssl_session_id);
request.setScheme(HttpScheme.HTTPS.asString());
+ request.setSecure(true);
}
}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java
index 874ae5bb5a..942f12bde6 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java
@@ -18,25 +18,24 @@
package org.eclipse.jetty.server;
+import static javax.servlet.RequestDispatcher.ERROR_EXCEPTION;
+import static javax.servlet.RequestDispatcher.ERROR_STATUS_CODE;
+
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.util.List;
-import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
import javax.servlet.DispatcherType;
-import javax.servlet.RequestDispatcher;
-import javax.servlet.UnavailableException;
-import javax.servlet.http.HttpServletRequest;
import org.eclipse.jetty.http.BadMessageException;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpGenerator;
import org.eclipse.jetty.http.HttpHeader;
-import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
@@ -44,6 +43,7 @@ import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.ChannelEndPoint;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.EofException;
+import org.eclipse.jetty.io.RuntimeIOException;
import org.eclipse.jetty.server.HttpChannelState.Action;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ErrorHandler;
@@ -262,6 +262,8 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
handle();
}
+ AtomicReference<Action> caller = new AtomicReference<>();
+
/**
* @return True if the channel is ready to continue handling (ie it is not suspended)
*/
@@ -333,68 +335,32 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
case ERROR_DISPATCH:
{
- Throwable ex = _state.getAsyncContextEvent().getThrowable();
-
- // Check for error dispatch loops
- Integer loop_detect = (Integer)_request.getAttribute("org.eclipse.jetty.server.ERROR_DISPATCH");
- if (loop_detect==null)
- loop_detect=1;
+ if (_response.isCommitted())
+ {
+ LOG.warn("Error Dispatch already committed");
+ _transport.abort((Throwable)_request.getAttribute(ERROR_EXCEPTION));
+ }
else
- loop_detect=loop_detect+1;
- _request.setAttribute("org.eclipse.jetty.server.ERROR_DISPATCH",loop_detect);
- if (loop_detect > getHttpConfiguration().getMaxErrorDispatches())
{
- LOG.warn("ERROR_DISPATCH loop detected on {} {}",_request,ex);
+ _response.reset();
+ Integer icode = (Integer)_request.getAttribute(ERROR_STATUS_CODE);
+ int code = icode!=null?icode.intValue():HttpStatus.INTERNAL_SERVER_ERROR_500;
+ _response.setStatus(code);
+ _request.setAttribute(ERROR_STATUS_CODE,code);
+ if (icode==null)
+ _request.setAttribute(ERROR_STATUS_CODE,code);
+ _request.setHandled(false);
+ _response.getHttpOutput().reopen();
+
try
{
- _response.sendError(HttpStatus.INTERNAL_SERVER_ERROR_500);
+ _request.setDispatcherType(DispatcherType.ERROR);
+ getServer().handle(this);
}
finally
{
- _state.errorComplete();
+ _request.setDispatcherType(null);
}
- break loop;
- }
-
- _request.setHandled(false);
- _response.resetBuffer();
- _response.getHttpOutput().reopen();
-
-
- String reason;
- if (ex == null || ex instanceof TimeoutException)
- {
- reason = "Async Timeout";
- }
- else
- {
- reason = HttpStatus.Code.INTERNAL_SERVER_ERROR.getMessage();
- _request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, ex);
- }
-
- _request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE, 500);
- _request.setAttribute(RequestDispatcher.ERROR_MESSAGE, reason);
- _request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI, _request.getRequestURI());
-
- _response.setStatusWithReason(HttpStatus.INTERNAL_SERVER_ERROR_500, reason);
-
- ErrorHandler eh = ErrorHandler.getErrorHandler(getServer(), _state.getContextHandler());
- if (eh instanceof ErrorHandler.ErrorPageMapper)
- {
- String error_page = ((ErrorHandler.ErrorPageMapper)eh).getErrorPage((HttpServletRequest)_state.getAsyncContextEvent().getSuppliedRequest());
- if (error_page != null)
- _state.getAsyncContextEvent().setDispatchPath(error_page);
- }
-
-
- try
- {
- _request.setDispatcherType(DispatcherType.ERROR);
- getServer().handleAsync(this);
- }
- finally
- {
- _request.setDispatcherType(null);
}
break;
}
@@ -419,24 +385,14 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
break;
}
- case ASYNC_ERROR:
- {
- _state.onError();
- break;
- }
-
case COMPLETE:
{
- // TODO do onComplete here for continuations to work
-// _state.onComplete();
-
if (!_response.isCommitted() && !_request.isHandled())
- _response.sendError(404);
+ _response.sendError(HttpStatus.NOT_FOUND_404);
else
_response.closeOutput();
_request.setHandled(true);
- // TODO do onComplete here to detect errors in final flush
_state.onComplete();
onCompleted();
@@ -450,26 +406,12 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
}
}
}
- catch (EofException|QuietServletException|BadMessageException e)
- {
- if (LOG.isDebugEnabled())
- LOG.debug(e);
- handleException(e);
- }
- catch (Throwable e)
- {
- if ("ContinuationThrowable".equals(e.getClass().getSimpleName()))
- {
- LOG.ignore(e);
- }
+ catch (Throwable failure)
+ {
+ if ("org.eclipse.jetty.continuation.ContinuationThrowable".equals(failure.getClass().getName()))
+ LOG.ignore(failure);
else
- {
- if (_connector.isStarted())
- LOG.warn(String.valueOf(_request.getHttpURI()), e);
- else
- LOG.debug(String.valueOf(_request.getHttpURI()), e);
- handleException(e);
- }
+ handleException(failure);
}
action = _state.unhandle();
@@ -482,6 +424,23 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
return !suspended;
}
+ protected void sendError(int code, String reason)
+ {
+ try
+ {
+ _response.sendError(code, reason);
+ }
+ catch (Throwable x)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Could not send error " + code + " " + reason, x);
+ }
+ finally
+ {
+ _state.errorComplete();
+ }
+ }
+
/**
* <p>Sends an error 500, performing a special logic to detect whether the request is suspended,
* to avoid concurrent writes from the application.</p>
@@ -489,69 +448,61 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
* spawned thread writes the response content; in such case, we attempt to commit the error directly
* bypassing the {@link ErrorHandler} mechanisms and the response OutputStream.</p>
*
- * @param x the Throwable that caused the problem
+ * @param failure the Throwable that caused the problem
*/
- protected void handleException(Throwable x)
+ protected void handleException(Throwable failure)
{
- if (_state.isAsyncStarted())
+ // Unwrap wrapping Jetty exceptions.
+ if (failure instanceof RuntimeIOException)
+ failure = failure.getCause();
+
+ if (failure instanceof QuietServletException || !getServer().isRunning())
{
- // Handle exception via AsyncListener onError
- Throwable root = _state.getAsyncContextEvent().getThrowable();
- if (root==null)
- {
- _state.error(x);
- }
+ if (LOG.isDebugEnabled())
+ LOG.debug(_request.getRequestURI(), failure);
+ }
+ else if (failure instanceof BadMessageException)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.warn(_request.getRequestURI(), failure);
else
- {
- // TODO Can this happen? Should this just be ISE???
- // We've already processed an error before!
- root.addSuppressed(x);
- LOG.warn("Error while handling async error: ", root);
- abort(x);
- _state.errorComplete();
- }
+ LOG.warn("{} {}",_request.getRequestURI(), failure.getMessage());
}
else
{
+ LOG.info(_request.getRequestURI(), failure);
+ }
+
+ try
+ {
try
{
- // Handle error normally
- _request.setHandled(true);
- _request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, x);
- _request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE, x.getClass());
-
- if (isCommitted())
+ _state.onError(failure);
+ }
+ catch (Exception e)
+ {
+ LOG.warn(e);
+ // Error could not be handled, probably due to error thrown from error dispatch
+ if (_response.isCommitted())
{
- abort(x);
- if (LOG.isDebugEnabled())
- LOG.debug("Could not send response error 500, already committed", x);
+ LOG.warn("ERROR Dispatch failed: ",failure);
+ _transport.abort(failure);
}
else
{
- _response.setHeader(HttpHeader.CONNECTION.asString(), HttpHeaderValue.CLOSE.asString());
-
- if (x instanceof BadMessageException)
- {
- BadMessageException bme = (BadMessageException)x;
- _response.sendError(bme.getCode(), bme.getReason());
- }
- else if (x instanceof UnavailableException)
- {
- if (((UnavailableException)x).isPermanent())
- _response.sendError(HttpStatus.NOT_FOUND_404);
- else
- _response.sendError(HttpStatus.SERVICE_UNAVAILABLE_503);
- }
- else
- _response.sendError(HttpStatus.INTERNAL_SERVER_ERROR_500);
+ // Minimal response
+ Integer code=(Integer)_request.getAttribute(ERROR_STATUS_CODE);
+ _response.reset();
+ _response.setStatus(code==null?500:code.intValue());
+ _response.flushBuffer();
}
}
- catch (Throwable e)
- {
- abort(e);
- if (LOG.isDebugEnabled())
- LOG.debug("Could not commit response error 500", e);
- }
+ }
+ catch(Exception e)
+ {
+ failure.addSuppressed(e);
+ LOG.warn("ERROR Dispatch failed: ",failure);
+ _transport.abort(failure);
}
}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java
index 8a2b1cb761..2c16602ec4 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java
@@ -18,16 +18,23 @@
package org.eclipse.jetty.server;
+import static javax.servlet.RequestDispatcher.ERROR_EXCEPTION;
+import static javax.servlet.RequestDispatcher.ERROR_MESSAGE;
+import static javax.servlet.RequestDispatcher.ERROR_STATUS_CODE;
+
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
import javax.servlet.AsyncListener;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletResponse;
+import javax.servlet.UnavailableException;
+import org.eclipse.jetty.http.BadMessageException;
+import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandler.Context;
import org.eclipse.jetty.util.log.Log;
@@ -45,12 +52,13 @@ public class HttpChannelState
private final static long DEFAULT_TIMEOUT=Long.getLong("org.eclipse.jetty.server.HttpChannelState.DEFAULT_TIMEOUT",30000L);
/**
- * The dispatched state of the HttpChannel, used to control the overall lifecycle
+ * The state of the HttpChannel,used to control the overall lifecycle.
*/
public enum State
{
IDLE, // Idle request
DISPATCHED, // Request dispatched to filter/servlet
+ THROWN, // Exception thrown while DISPATCHED
ASYNC_WAIT, // Suspended and waiting
ASYNC_WOKEN, // Dispatch to handle from ASYNC_WAIT
ASYNC_IO, // Dispatched for async IO
@@ -67,7 +75,6 @@ public class HttpChannelState
DISPATCH, // handle a normal request dispatch
ASYNC_DISPATCH, // handle an async request dispatch
ERROR_DISPATCH, // handle a normal error
- ASYNC_ERROR, // handle an async error
WRITE_CALLBACK, // handle an IO write callback
READ_CALLBACK, // handle an IO read callback
COMPLETE, // Complete the response
@@ -76,14 +83,12 @@ public class HttpChannelState
}
/**
- * The state of the servlet async API. This can lead or follow the
- * channel dispatch state and also includes reasons such as expired,
- * dispatched or completed.
+ * The state of the servlet async API.
*/
public enum Async
{
STARTED, // AsyncContext.startAsync() has been called
- DISPATCH, //
+ DISPATCH, // AsyncContext.dispatch() has been called
COMPLETE, // AsyncContext.complete() has been called
EXPIRING, // AsyncContext timeout just happened
EXPIRED, // AsyncContext timeout has been processed
@@ -160,12 +165,18 @@ public class HttpChannelState
{
try(Locker.Lock lock= _locker.lock())
{
- return String.format("%s@%x{s=%s a=%s i=%b r=%s w=%b}",getClass().getSimpleName(),hashCode(),_state,_async,_initial,
- _asyncReadPossible?(_asyncReadUnready?"PU":"P!U"):(_asyncReadUnready?"!PU":"!P!U"),
- _asyncWrite);
+ return toStringLocked();
}
}
+ public String toStringLocked()
+ {
+ return String.format("%s@%x{s=%s a=%s i=%b r=%s w=%b}",getClass().getSimpleName(),hashCode(),_state,_async,_initial,
+ _asyncReadPossible?(_asyncReadUnready?"PU":"P!U"):(_asyncReadUnready?"!PU":"!P!U"),
+ _asyncWrite);
+ }
+
+
private String getStatusStringLocked()
{
return String.format("s=%s i=%b a=%s",_state,_initial,_async);
@@ -184,10 +195,11 @@ public class HttpChannelState
*/
protected Action handling()
{
- if(DEBUG)
- LOG.debug("{} handling {}",this,_state);
try(Locker.Lock lock= _locker.lock())
{
+ if(DEBUG)
+ LOG.debug("handling {}",toStringLocked());
+
switch(_state)
{
case IDLE:
@@ -228,17 +240,15 @@ public class HttpChannelState
_state=State.DISPATCHED;
_async=null;
return Action.ASYNC_DISPATCH;
- case EXPIRING:
- break;
case EXPIRED:
+ case ERRORED:
_state=State.DISPATCHED;
_async=null;
return Action.ERROR_DISPATCH;
case STARTED:
- return Action.WAIT;
+ case EXPIRING:
case ERRORING:
- _state=State.DISPATCHED;
- return Action.ASYNC_ERROR;
+ return Action.WAIT;
default:
throw new IllegalStateException(getStatusStringLocked());
@@ -264,6 +274,9 @@ public class HttpChannelState
try(Locker.Lock lock= _locker.lock())
{
+ if(DEBUG)
+ LOG.debug("startAsync {}",toStringLocked());
+
if (_state!=State.DISPATCHED || _async!=null)
throw new IllegalStateException(this.getStatusStringLocked());
@@ -304,19 +317,10 @@ public class HttpChannelState
}
}
- protected void error(Throwable th)
- {
- try(Locker.Lock lock= _locker.lock())
- {
- if (_event!=null)
- _event.addThrowable(th);
- _async=Async.ERRORING;
- }
- }
/**
* Signal that the HttpConnection has finished handling the request.
- * For blocking connectors, this call may block if the request has
+ * For blocking connectors,this call may block if the request has
* been suspended (startAsync called).
* @return next actions
* be handled again (eg because of a resume that happened before unhandle was called)
@@ -327,17 +331,21 @@ public class HttpChannelState
AsyncContextEvent schedule_event=null;
boolean read_interested=false;
- if(DEBUG)
- LOG.debug("{} unhandle {}",this,_state);
-
try(Locker.Lock lock= _locker.lock())
{
+ if(DEBUG)
+ LOG.debug("unhandle {}",toStringLocked());
+
switch(_state)
{
case COMPLETING:
case COMPLETED:
return Action.TERMINATED;
+ case THROWN:
+ _state=State.DISPATCHED;
+ return Action.ERROR_DISPATCH;
+
case DISPATCHED:
case ASYNC_IO:
break;
@@ -363,12 +371,6 @@ public class HttpChannelState
action=Action.ASYNC_DISPATCH;
break;
- case EXPIRED:
- _state=State.DISPATCHED;
- _async=null;
- action = Action.ERROR_DISPATCH;
- break;
-
case STARTED:
if (_asyncReadUnready && _asyncReadPossible)
{
@@ -392,26 +394,27 @@ public class HttpChannelState
break;
case EXPIRING:
- schedule_event=_event;
+ // onTimeout callbacks still being called, so just WAIT
_state=State.ASYNC_WAIT;
action=Action.WAIT;
break;
- case ERRORING:
+ case EXPIRED:
+ // onTimeout handling is complete, but did not dispatch as
+ // we were handling. So do the error dispatch here
_state=State.DISPATCHED;
- action=Action.ASYNC_ERROR;
+ _async=null;
+ action=Action.ERROR_DISPATCH;
break;
-
+
case ERRORED:
_state=State.DISPATCHED;
- action=Action.ERROR_DISPATCH;
_async=null;
+ action=Action.ERROR_DISPATCH;
break;
default:
- _state=State.COMPLETING;
- action=Action.COMPLETE;
- break;
+ throw new IllegalStateException(this.getStatusStringLocked());
}
}
else
@@ -431,9 +434,12 @@ public class HttpChannelState
public void dispatch(ServletContext context, String path)
{
boolean dispatch=false;
- AsyncContextEvent event=null;
+ AsyncContextEvent event;
try(Locker.Lock lock= _locker.lock())
{
+ if(DEBUG)
+ LOG.debug("dispatch {} -> {}",toStringLocked(),path);
+
boolean started=false;
event=_event;
switch(_async)
@@ -442,6 +448,7 @@ public class HttpChannelState
started=true;
break;
case EXPIRING:
+ case ERRORING:
case ERRORED:
break;
default:
@@ -484,6 +491,9 @@ public class HttpChannelState
AsyncContextEvent event;
try(Locker.Lock lock= _locker.lock())
{
+ if(DEBUG)
+ LOG.debug("onTimeout {}",toStringLocked());
+
if (_async!=Async.STARTED)
return;
_async=Async.EXPIRING;
@@ -492,12 +502,10 @@ public class HttpChannelState
}
- if (LOG.isDebugEnabled())
- LOG.debug("Async timeout {}",this);
-
+ final AtomicReference<Throwable> error=new AtomicReference<Throwable>();
if (listeners!=null)
{
- Runnable callback=new Runnable()
+ Runnable task=new Runnable()
{
@Override
public void run()
@@ -508,12 +516,13 @@ public class HttpChannelState
{
listener.onTimeout(event);
}
- catch(Exception e)
+ catch(Throwable x)
{
- LOG.debug(e);
- event.addThrowable(e);
- _channel.getRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,event.getThrowable());
- break;
+ LOG.debug("Exception while invoking listener " + listener,x);
+ if (error.get()==null)
+ error.set(x);
+ else
+ error.get().addSuppressed(x);
}
}
}
@@ -524,30 +533,28 @@ public class HttpChannelState
}
};
- runInContext(event,callback);
+ runInContext(event,task);
}
+ Throwable th=error.get();
boolean dispatch=false;
try(Locker.Lock lock= _locker.lock())
{
switch(_async)
{
case EXPIRING:
- if (event.getThrowable()==null)
- {
- _async=Async.EXPIRED;
- _event.addThrowable(new TimeoutException("Async API violation"));
- }
- else
- {
- _async=Async.ERRORING;
- }
+ _async=th==null ? Async.EXPIRED : Async.ERRORING;
break;
-
+
case COMPLETE:
case DISPATCH:
+ if (th!=null)
+ {
+ LOG.ignore(th);
+ th=null;
+ }
break;
-
+
default:
throw new IllegalStateException();
}
@@ -559,6 +566,13 @@ public class HttpChannelState
}
}
+ if (th!=null)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Error after async timeout {}",this,th);
+ onError(th);
+ }
+
if (dispatch)
{
if (LOG.isDebugEnabled())
@@ -569,11 +583,15 @@ public class HttpChannelState
public void complete()
{
+
// just like resume, except don't set _dispatched=true;
boolean handle=false;
- AsyncContextEvent event=null;
+ AsyncContextEvent event;
try(Locker.Lock lock= _locker.lock())
{
+ if(DEBUG)
+ LOG.debug("complete {}",toStringLocked());
+
boolean started=false;
event=_event;
@@ -583,8 +601,11 @@ public class HttpChannelState
started=true;
break;
case EXPIRING:
+ case ERRORING:
case ERRORED:
break;
+ case COMPLETE:
+ return;
default:
throw new IllegalStateException(this.getStatusStringLocked());
}
@@ -606,6 +627,9 @@ public class HttpChannelState
{
try(Locker.Lock lock= _locker.lock())
{
+ if(DEBUG)
+ LOG.debug("error complete {}",toStringLocked());
+
_async=Async.COMPLETE;
_event.setDispatchContext(null);
_event.setDispatchPath(null);
@@ -613,40 +637,142 @@ public class HttpChannelState
cancelTimeout();
}
-
- protected void onError()
+
+ protected void onError(Throwable failure)
{
- final List<AsyncListener> aListeners;
+ final List<AsyncListener> listeners;
final AsyncContextEvent event;
-
+ final Request baseRequest = _channel.getRequest();
+
+ int code=HttpStatus.INTERNAL_SERVER_ERROR_500;
+ String reason=null;
+ if (failure instanceof BadMessageException)
+ {
+ BadMessageException bme = (BadMessageException)failure;
+ code = bme.getCode();
+ reason = bme.getReason();
+ }
+ else if (failure instanceof UnavailableException)
+ {
+ if (((UnavailableException)failure).isPermanent())
+ code = HttpStatus.NOT_FOUND_404;
+ else
+ code = HttpStatus.SERVICE_UNAVAILABLE_503;
+ }
+
try(Locker.Lock lock= _locker.lock())
{
- if (_state!=State.DISPATCHED/* || _async!=Async.ERRORING*/)
+ if(DEBUG)
+ LOG.debug("onError {} {}",toStringLocked(),failure);
+
+ // Set error on request.
+ if(_event!=null)
+ {
+ if (_event.getThrowable()!=null)
+ throw new IllegalStateException("Error already set",_event.getThrowable());
+ _event.addThrowable(failure);
+ _event.getSuppliedRequest().setAttribute(ERROR_STATUS_CODE,code);
+ _event.getSuppliedRequest().setAttribute(ERROR_EXCEPTION,failure);
+ _event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,failure==null?null:failure.getClass());
+
+ _event.getSuppliedRequest().setAttribute(ERROR_MESSAGE,reason!=null?reason:null);
+ }
+ else
+ {
+ Throwable error = (Throwable)baseRequest.getAttribute(ERROR_EXCEPTION);
+ if (error!=null)
+ throw new IllegalStateException("Error already set",error);
+ baseRequest.setAttribute(ERROR_STATUS_CODE,code);
+ baseRequest.setAttribute(ERROR_EXCEPTION,failure);
+ baseRequest.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,failure==null?null:failure.getClass());
+ baseRequest.setAttribute(ERROR_MESSAGE,reason!=null?reason:null);
+ }
+
+ // Are we blocking?
+ if (_async==null)
+ {
+ // Only called from within HttpChannel Handling, so much be dispatched, let's stay dispatched!
+ if (_state==State.DISPATCHED)
+ {
+ _state=State.THROWN;
+ return;
+ }
throw new IllegalStateException(this.getStatusStringLocked());
-
- aListeners=_asyncListeners;
+ }
+
+ // We are Async
+ _async=Async.ERRORING;
+ listeners=_asyncListeners;
event=_event;
- _async=Async.ERRORED;
}
- if (event!=null && aListeners!=null)
+ if(listeners!=null)
{
- event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,event.getThrowable());
- event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_MESSAGE,event.getThrowable().getMessage());
- for (AsyncListener listener : aListeners)
+ Runnable task=new Runnable()
{
- try
+ @Override
+ public void run()
{
- listener.onError(event);
+ for (AsyncListener listener : listeners)
+ {
+ try
+ {
+ listener.onError(event);
+ }
+ catch (Throwable x)
+ {
+ LOG.info("Exception while invoking listener " + listener,x);
+ }
+ }
}
- catch(Exception x)
+
+ @Override
+ public String toString()
{
- LOG.info("Exception while invoking listener " + listener, x);
+ return "onError";
+ }
+ };
+ runInContext(event,task);
+ }
+
+ boolean dispatch=false;
+ try(Locker.Lock lock= _locker.lock())
+ {
+ switch(_async)
+ {
+ case ERRORING:
+ {
+ // Still in this state ? The listeners did not invoke API methods
+ // and the container must provide a default error dispatch.
+ _async=Async.ERRORED;
+ break;
+ }
+ case DISPATCH:
+ case COMPLETE:
+ {
+ // The listeners called dispatch() or complete().
+ break;
+ }
+ default:
+ {
+ throw new IllegalStateException(toString());
}
}
+
+ if(_state==State.ASYNC_WAIT)
+ {
+ _state=State.ASYNC_WOKEN;
+ dispatch=true;
+ }
}
- }
+ if(dispatch)
+ {
+ if(LOG.isDebugEnabled())
+ LOG.debug("Dispatch after error {}",this);
+ scheduleDispatch();
+ }
+ }
protected void onComplete()
{
@@ -655,6 +781,9 @@ public class HttpChannelState
try(Locker.Lock lock= _locker.lock())
{
+ if(DEBUG)
+ LOG.debug("onComplete {}",toStringLocked());
+
switch(_state)
{
case COMPLETING:
@@ -686,7 +815,7 @@ public class HttpChannelState
}
catch(Exception e)
{
- LOG.warn(e);
+ LOG.warn("Exception while invoking listener " + listener,e);
}
}
}
@@ -708,6 +837,9 @@ public class HttpChannelState
cancelTimeout();
try(Locker.Lock lock= _locker.lock())
{
+ if(DEBUG)
+ LOG.debug("recycle {}",toStringLocked());
+
switch(_state)
{
case DISPATCHED:
@@ -734,6 +866,9 @@ public class HttpChannelState
cancelTimeout();
try(Locker.Lock lock= _locker.lock())
{
+ if(DEBUG)
+ LOG.debug("upgrade {}",toStringLocked());
+
switch(_state)
{
case IDLE:
@@ -932,6 +1067,9 @@ public class HttpChannelState
boolean interested=false;
try(Locker.Lock lock= _locker.lock())
{
+ if(DEBUG)
+ LOG.debug("onReadUnready {}",toStringLocked());
+
// We were already unready, this is not a state change, so do nothing
if (!_asyncReadUnready)
{
@@ -958,6 +1096,9 @@ public class HttpChannelState
boolean woken=false;
try(Locker.Lock lock= _locker.lock())
{
+ if(DEBUG)
+ LOG.debug("onReadPossible {}",toStringLocked());
+
_asyncReadPossible=true;
if (_state==State.ASYNC_WAIT && _asyncReadUnready)
{
@@ -980,6 +1121,9 @@ public class HttpChannelState
boolean woken=false;
try(Locker.Lock lock= _locker.lock())
{
+ if(DEBUG)
+ LOG.debug("onReadReady {}",toStringLocked());
+
_asyncReadUnready=true;
_asyncReadPossible=true;
if (_state==State.ASYNC_WAIT)
@@ -1005,6 +1149,9 @@ public class HttpChannelState
try(Locker.Lock lock= _locker.lock())
{
+ if(DEBUG)
+ LOG.debug("onWritePossible {}",toStringLocked());
+
_asyncWrite=true;
if (_state==State.ASYNC_WAIT)
{
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 ea69d416f3..926a7a6b83 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
@@ -82,7 +82,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
setWriteListener() READY->owp ise ise ise ise ise
write() OPEN ise PENDING wpe wpe eof
flush() OPEN ise PENDING wpe wpe eof
- close() CLOSED CLOSED CLOSED CLOSED wpe CLOSED
+ close() CLOSED CLOSED CLOSED CLOSED CLOSED CLOSED
isReady() OPEN:true READY:true READY:true UNREADY:false UNREADY:false CLOSED:true
write completed - - - ASYNC READY->owp -
*/
@@ -195,11 +195,17 @@ public class HttpOutput extends ServletOutputStream implements Runnable
{
return;
}
+
+ case ASYNC:
case UNREADY:
+ case PENDING:
{
- if (_state.compareAndSet(state,OutputState.ERROR))
- _writeListener.onError(_onError==null?new EofException("Async close"):_onError);
- break;
+ if (!_state.compareAndSet(state,OutputState.CLOSED))
+ break;
+ IOException ex = new IOException("Closed while Pending/Unready");
+ LOG.warn(ex.toString());
+ LOG.debug(ex);
+ _channel.abort(ex);
}
default:
{
@@ -286,6 +292,20 @@ public class HttpOutput extends ServletOutputStream implements Runnable
return _state.get()==OutputState.CLOSED;
}
+ public boolean isAsync()
+ {
+ switch(_state.get())
+ {
+ case ASYNC:
+ case READY:
+ case PENDING:
+ case UNREADY:
+ return true;
+ default:
+ return false;
+ }
+ }
+
@Override
public void flush() throws IOException
{
@@ -307,6 +327,8 @@ public class HttpOutput extends ServletOutputStream implements Runnable
return;
case PENDING:
+ return;
+
case UNREADY:
throw new WritePendingException();
@@ -1255,4 +1277,5 @@ public class HttpOutput extends ServletOutputStream implements Runnable
super.onCompleteFailure(x);
}
}
+
}
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 7865c6b7f2..b1c1618e63 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
@@ -197,27 +197,16 @@ public class LocalConnector extends AbstractConnector
}
@Override
- public void close()
- {
- boolean wasOpen=isOpen();
- super.close();
- if (wasOpen)
- {
- getConnection().onClose();
- onClose();
- }
- }
-
- @Override
public void onClose()
{
+ getConnection().onClose();
LocalConnector.this.onEndPointClosed(this);
super.onClose();
_closed.countDown();
}
@Override
- public void shutdownOutput()
+ public void doShutdownOutput()
{
super.shutdownOutput();
close();
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/NetworkTrafficServerConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/NetworkTrafficServerConnector.java
index cfd96c1808..e4ed0a2c59 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/NetworkTrafficServerConnector.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/NetworkTrafficServerConnector.java
@@ -26,10 +26,10 @@ import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.ChannelEndPoint;
import org.eclipse.jetty.io.ManagedSelector;
import org.eclipse.jetty.io.NetworkTrafficListener;
import org.eclipse.jetty.io.NetworkTrafficSelectChannelEndPoint;
-import org.eclipse.jetty.io.SelectChannelEndPoint;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.Scheduler;
@@ -84,7 +84,7 @@ public class NetworkTrafficServerConnector extends ServerConnector
}
@Override
- protected SelectChannelEndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey key) throws IOException
+ protected ChannelEndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey key) throws IOException
{
NetworkTrafficSelectChannelEndPoint endPoint = new NetworkTrafficSelectChannelEndPoint(channel, selectSet, key, getScheduler(), getIdleTimeout(), listeners);
return endPoint;
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java
index b72cf5718d..7a5e51cca1 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java
@@ -19,17 +19,23 @@
package org.eclipse.jetty.server;
import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ReadPendingException;
import java.nio.channels.WritePendingException;
+import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.util.AttributesMap;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@@ -38,14 +44,17 @@ import org.eclipse.jetty.util.log.Logger;
/**
* ConnectionFactory for the PROXY Protocol.
* <p>This factory can be placed in front of any other connection factory
- * to process the proxy line before the normal protocol handling</p>
+ * to process the proxy v1 or v2 line before the normal protocol handling</p>
*
* @see <a href="http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt">http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt</a>
*/
public class ProxyConnectionFactory extends AbstractConnectionFactory
{
+ public static final String TLS_VERSION = "TLS_VERSION";
+
private static final Logger LOG = Log.getLogger(ProxyConnectionFactory.class);
private final String _next;
+ private int _maxProxyHeader=1024;
/* ------------------------------------------------------------ */
/** Proxy Connection Factory that uses the next ConnectionFactory
@@ -63,6 +72,16 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory
_next=nextProtocol;
}
+ public int getMaxProxyHeader()
+ {
+ return _maxProxyHeader;
+ }
+
+ public void setMaxProxyHeader(int maxProxyHeader)
+ {
+ _maxProxyHeader = maxProxyHeader;
+ }
+
@Override
public Connection newConnection(Connector connector, EndPoint endp)
{
@@ -80,10 +99,79 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory
}
}
- return new ProxyConnection(endp,connector,next);
+ return new ProxyProtocolV1orV2Connection(endp,connector,next);
+ }
+
+ public class ProxyProtocolV1orV2Connection extends AbstractConnection
+ {
+ private final Connector _connector;
+ private final String _next;
+ private ByteBuffer _buffer = BufferUtil.allocate(16);
+
+ protected ProxyProtocolV1orV2Connection(EndPoint endp, Connector connector, String next)
+ {
+ super(endp,connector.getExecutor());
+ _connector=connector;
+ _next=next;
+ }
+
+ @Override
+ public void onOpen()
+ {
+ super.onOpen();
+ fillInterested();
+ }
+
+ @Override
+ public void onFillable()
+ {
+ try
+ {
+ while(BufferUtil.space(_buffer)>0)
+ {
+ // Read data
+ int fill=getEndPoint().fill(_buffer);
+ if (fill<0)
+ {
+ getEndPoint().shutdownOutput();
+ return;
+ }
+ if (fill==0)
+ {
+ fillInterested();
+ return;
+ }
+ }
+
+ // Is it a V1?
+ switch(_buffer.get(0))
+ {
+ case 'P':
+ {
+ ProxyProtocolV1Connection v1 = new ProxyProtocolV1Connection(getEndPoint(),_connector,_next,_buffer);
+ getEndPoint().upgrade(v1);
+ return;
+ }
+ case 0x0D:
+ {
+ ProxyProtocolV2Connection v2 = new ProxyProtocolV2Connection(getEndPoint(),_connector,_next,_buffer);
+ getEndPoint().upgrade(v2);
+ return;
+ }
+ default:
+ LOG.warn("Not PROXY protocol for {}",getEndPoint());
+ close();
+ }
+ }
+ catch (Throwable x)
+ {
+ LOG.warn("PROXY error for "+getEndPoint(),x);
+ close();
+ }
+ }
}
- public static class ProxyConnection extends AbstractConnection
+ public static class ProxyProtocolV1Connection extends AbstractConnection
{
// 0 1 2 3 4 5 6
// 98765432109876543210987654321
@@ -97,11 +185,13 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory
private int _fields;
private int _length;
- protected ProxyConnection(EndPoint endp, Connector connector, String next)
+ protected ProxyProtocolV1Connection(EndPoint endp, Connector connector, String next,ByteBuffer buffer)
{
super(endp,connector.getExecutor());
_connector=connector;
_next=next;
+ _length=buffer.remaining();
+ parse(buffer);
}
@Override
@@ -110,16 +200,60 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory
super.onOpen();
fillInterested();
}
+
+
+ private boolean parse(ByteBuffer buffer)
+ {
+ // parse fields
+ while (buffer.hasRemaining())
+ {
+ byte b = buffer.get();
+ if (_fields<6)
+ {
+ if (b==' ' || b=='\r' && _fields==5)
+ {
+ _field[_fields++]=_builder.toString();
+ _builder.setLength(0);
+ }
+ else if (b<' ')
+ {
+ LOG.warn("Bad character {} for {}",b&0xFF,getEndPoint());
+ close();
+ return false;
+ }
+ else
+ {
+ _builder.append((char)b);
+ }
+ }
+ else
+ {
+ if (b=='\n')
+ {
+ _fields=7;
+ return true;
+ }
+ LOG.warn("Bad CRLF for {}",getEndPoint());
+ close();
+ return false;
+ }
+ }
+
+ return true;
+ }
+
@Override
public void onFillable()
{
try
{
ByteBuffer buffer=null;
- loop: while(true)
+ while(_fields<7)
{
// Create a buffer that will not read too much data
+ // since once read it is impossible to push back for the
+ // real connection to read it.
int size=Math.max(1,__size[_fields]-_builder.length());
if (buffer==null || buffer.capacity()!=size)
buffer=BufferUtil.allocate(size);
@@ -147,38 +281,8 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory
return;
}
- // parse fields
- while (buffer.hasRemaining())
- {
- byte b = buffer.get();
- if (_fields<6)
- {
- if (b==' ' || b=='\r' && _fields==5)
- {
- _field[_fields++]=_builder.toString();
- _builder.setLength(0);
- }
- else if (b<' ')
- {
- LOG.warn("Bad character {} for {}",b&0xFF,getEndPoint());
- close();
- return;
- }
- else
- {
- _builder.append((char)b);
- }
- }
- else
- {
- if (b=='\n')
- break loop;
-
- LOG.warn("Bad CRLF for {}",getEndPoint());
- close();
- return;
- }
- }
+ if (!parse(buffer))
+ return;
}
// Check proxy
@@ -197,10 +301,13 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory
ConnectionFactory connectionFactory = _connector.getConnectionFactory(_next);
if (connectionFactory == null)
{
- LOG.info("Next protocol '{}' for {}",_next,getEndPoint());
+ LOG.warn("No Next protocol '{}' for {}",_next,getEndPoint());
close();
return;
}
+
+ if (LOG.isDebugEnabled())
+ LOG.warn("Next protocol '{}' for {} r={} l={}",_next,getEndPoint(),remote,local);
EndPoint endPoint = new ProxyEndPoint(getEndPoint(),remote,local);
Connection newConnection = connectionFactory.newConnection(_connector, endPoint);
@@ -213,8 +320,260 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory
}
}
}
+
+
+ enum Family { UNSPEC, INET, INET6, UNIX };
+ enum Transport { UNSPEC, STREAM, DGRAM };
+ private static final byte[] MAGIC = new byte[]{0x0D,0x0A,0x0D,0x0A,0x00,0x0D,0x0A,0x51,0x55,0x49,0x54,0x0A};
+
+ public class ProxyProtocolV2Connection extends AbstractConnection
+ {
+ private final Connector _connector;
+ private final String _next;
+ private final boolean _local;
+ private final Family _family;
+ private final Transport _transport;
+ private final int _length;
+ private final ByteBuffer _buffer;
+
+ protected ProxyProtocolV2Connection(EndPoint endp, Connector connector, String next,ByteBuffer buffer)
+ throws IOException
+ {
+ super(endp,connector.getExecutor());
+ _connector=connector;
+ _next=next;
+
+ if (buffer.remaining()!=16)
+ throw new IllegalStateException();
+
+ if (LOG.isDebugEnabled())
+ LOG.debug("PROXYv2 header {} for {}",BufferUtil.toHexSummary(buffer),this);
+
+ // struct proxy_hdr_v2 {
+ // uint8_t sig[12]; /* hex 0D 0A 0D 0A 00 0D 0A 51 55 49 54 0A */
+ // uint8_t ver_cmd; /* protocol version and command */
+ // uint8_t fam; /* protocol family and address */
+ // uint16_t len; /* number of following bytes part of the header */
+ // };
+ for (int i=0;i<MAGIC.length;i++)
+ if (buffer.get()!=MAGIC[i])
+ throw new IOException("Bad PROXY protocol v2 signature");
+
+ int versionAndCommand = 0xff & buffer.get();
+ if ((versionAndCommand&0xf0) != 0x20)
+ throw new IOException("Bad PROXY protocol v2 version");
+ _local=(versionAndCommand&0xf)==0x00;
+
+ int transportAndFamily = 0xff & buffer.get();
+ switch(transportAndFamily>>4)
+ {
+ case 0: _family=Family.UNSPEC; break;
+ case 1: _family=Family.INET; break;
+ case 2: _family=Family.INET6; break;
+ case 3: _family=Family.UNIX; break;
+ default:
+ throw new IOException("Bad PROXY protocol v2 family");
+ }
+
+ switch(0xf&transportAndFamily)
+ {
+ case 0: _transport=Transport.UNSPEC; break;
+ case 1: _transport=Transport.STREAM; break;
+ case 2: _transport=Transport.DGRAM; break;
+ default:
+ throw new IOException("Bad PROXY protocol v2 family");
+ }
+
+ _length = buffer.getChar();
+
+ if (!_local && (_family==Family.UNSPEC || _family==Family.UNIX || _transport!=Transport.STREAM))
+ throw new IOException(String.format("Unsupported PROXY protocol v2 mode 0x%x,0x%x",versionAndCommand,transportAndFamily));
+
+ if (_length>_maxProxyHeader)
+ throw new IOException(String.format("Unsupported PROXY protocol v2 mode 0x%x,0x%x,0x%x",versionAndCommand,transportAndFamily,_length));
+
+ _buffer = _length>0?BufferUtil.allocate(_length):BufferUtil.EMPTY_BUFFER;
+ }
+
+ @Override
+ public void onOpen()
+ {
+ super.onOpen();
+ if (_buffer.remaining()==_length)
+ next();
+ else
+ fillInterested();
+ }
+
+ @Override
+ public void onFillable()
+ {
+ try
+ {
+ while(_buffer.remaining()<_length)
+ {
+ // Read data
+ int fill=getEndPoint().fill(_buffer);
+ if (fill<0)
+ {
+ getEndPoint().shutdownOutput();
+ return;
+ }
+ if (fill==0)
+ {
+ fillInterested();
+ return;
+ }
+ }
+ }
+ catch (Throwable x)
+ {
+ LOG.warn("PROXY error for "+getEndPoint(),x);
+ close();
+ return;
+ }
+
+ next();
+ }
+
+ private void next()
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("PROXYv2 next {} from {} for {}",_next,BufferUtil.toHexSummary(_buffer),this);
+
+ // Create the next protocol
+ ConnectionFactory connectionFactory = _connector.getConnectionFactory(_next);
+ if (connectionFactory == null)
+ {
+ LOG.info("Next protocol '{}' for {}",_next,getEndPoint());
+ close();
+ return;
+ }
+
+ // Do we need to wrap the endpoint?
+ EndPoint endPoint=getEndPoint();
+ if (!_local)
+ {
+ try
+ {
+ InetAddress src;
+ InetAddress dst;
+ int sp;
+ int dp;
+
+ switch(_family)
+ {
+ case INET:
+ {
+ byte[] addr=new byte[4];
+ _buffer.get(addr);
+ src = Inet4Address.getByAddress(addr);
+ _buffer.get(addr);
+ dst = Inet4Address.getByAddress(addr);
+ sp = _buffer.getChar();
+ dp = _buffer.getChar();
+
+ break;
+ }
+
+ case INET6:
+ {
+ byte[] addr=new byte[16];
+ _buffer.get(addr);
+ src = Inet6Address.getByAddress(addr);
+ _buffer.get(addr);
+ dst = Inet6Address.getByAddress(addr);
+ sp = _buffer.getChar();
+ dp = _buffer.getChar();
+ break;
+ }
+
+ default:
+ throw new IllegalStateException();
+ }
+
+
+ // Extract Addresses
+ InetSocketAddress remote=new InetSocketAddress(src,sp);
+ InetSocketAddress local =new InetSocketAddress(dst,dp);
+ ProxyEndPoint proxyEndPoint = new ProxyEndPoint(endPoint,remote,local);
+ endPoint = proxyEndPoint;
+
+
+ // Any additional info?
+ while(_buffer.hasRemaining())
+ {
+ int type = 0xff & _buffer.get();
+ int length = _buffer.getShort();
+ byte[] value = new byte[length];
+ _buffer.get(value);
+
+ if (LOG.isDebugEnabled())
+ LOG.debug(String.format("T=%x L=%d V=%s for %s",type,length,TypeUtil.toHexString(value),this));
+
+ // TODO interpret these values
+ switch(type)
+ {
+ case 0x01: // PP2_TYPE_ALPN
+ break;
+ case 0x02: // PP2_TYPE_AUTHORITY
+ break;
+ case 0x20: // PP2_TYPE_SSL
+ {
+ int i=0;
+ int client = 0xff & value[i++];
+ int verify = (0xff & value[i++])<<24 + (0xff & value[i++])<<16 + (0xff & value[i++])<<8 + (0xff&value[i++]);
+ while(i<value.length)
+ {
+ int ssl_type = 0xff & value[i++];
+ int ssl_length = (0xff & value[i++])*0x100 + (0xff&value[i++]);
+ byte[] ssl_val = new byte[ssl_length];
+ System.arraycopy(value,i,ssl_val,0,ssl_length);
+ i+=ssl_length;
+
+ switch(ssl_type)
+ {
+ case 0x21: // PP2_TYPE_SSL_VERSION
+ String version=new String(ssl_val,0,ssl_length,StandardCharsets.ISO_8859_1);
+ if (client==1)
+ proxyEndPoint.setAttribute(TLS_VERSION,version);
+ break;
+
+ default:
+ break;
+ }
+ }
+ break;
+ }
+ case 0x21: // PP2_TYPE_SSL_VERSION
+ break;
+ case 0x22: // PP2_TYPE_SSL_CN
+ break;
+ case 0x30: // PP2_TYPE_NETNS
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (LOG.isDebugEnabled())
+ LOG.debug("{} {}",getEndPoint(),proxyEndPoint.toString());
+
+
+ }
+ catch(Exception e)
+ {
+ LOG.warn(e);
+ }
+ }
+
+ Connection newConnection = connectionFactory.newConnection(_connector, endPoint);
+ endPoint.upgrade(newConnection);
+ }
+ }
+
- public static class ProxyEndPoint implements EndPoint
+ public static class ProxyEndPoint extends AttributesMap implements EndPoint
{
private final EndPoint _endp;
private final InetSocketAddress _remote;
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilder.java b/jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilder.java
index 5e807b45dc..a16c9960e6 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilder.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilder.java
@@ -25,63 +25,99 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
+
/** Build a request to be pushed.
- * <p>
- * A PushBuilder is obtained by calling {@link Request#getPushBuilder()}
- * which creates an initializes the builder as follows:
+ *
+ * <p>A PushBuilder is obtained by calling {@link
+ * Request#getPushBuilder()} (<code>Eventually HttpServletRequest.getPushBuilder()</code>).
+ * Each call to this method will
+ * return a new instance of a PushBuilder based off the current {@code
+ * HttpServletRequest}. Any mutations to the returned PushBuilder are
+ * not reflected on future returns.</p>
+ *
+ * <p>The instance is initialized as follows:</p>
+ *
* <ul>
- * <li> Each call to getPushBuilder() will return a new instance of a
- * PushBuilder based off the Request. Any mutations to the
- * returned PushBuilder are not reflected on future returns.</li>
+ *
* <li>The method is initialized to "GET"</li>
- * <li>The requests headers are added to the Builder, except for:<ul>
+ *
+ * <li>The existing headers of the current {@link HttpServletRequest}
+ * are added to the builder, except for:
+ *
+ * <ul>
* <li>Conditional headers (eg. If-Modified-Since)
* <li>Range headers
* <li>Expect headers
* <li>Authorization headers
* <li>Referrer headers
- * </ul></li>
- * <li>If the request was Authenticated, an Authorization header will
+ * </ul>
+ *
+ * </li>
+ *
+ * <li>If the request was authenticated, an Authorization header will
* be set with a container generated token that will result in equivalent
- * Authorization for the pushed request</li>
- * <li>The query string from {@link HttpServletRequest#getQueryString()}
- * <li>The {@link HttpServletRequest#getRequestedSessionId()} value, unless at the time
- * of the call {@link HttpServletRequest#getSession(boolean)}
- * has previously been called to create a new {@link HttpSession}, in
- * which case the new session ID will be used as the PushBuilders
- * requested session ID. The source of the requested session id will be the
- * same as for the request</li>
- * <li>The Referer header will be set to {@link HttpServletRequest#getRequestURL()}
- * plus any {@link HttpServletRequest#getQueryString()} </li>
+ * Authorization for the pushed request.</li>
+ *
+ * <li>The {@link HttpServletRequest#getRequestedSessionId()} value,
+ * unless at the time of the call {@link
+ * HttpServletRequest#getSession(boolean)} has previously been called to
+ * create a new {@link HttpSession}, in which case the new session ID
+ * will be used as the PushBuilder's requested session ID. The source of
+ * the requested session id will be the same as for the request</li>
+ *
+ * <li>The Referer(sic) header will be set to {@link
+ * HttpServletRequest#getRequestURL()} plus any {@link
+ * HttpServletRequest#getQueryString()} </li>
+ *
* <li>If {@link HttpServletResponse#addCookie(Cookie)} has been called
* on the associated response, then a corresponding Cookie header will be added
* to the PushBuilder, unless the {@link Cookie#getMaxAge()} is &lt;=0, in which
* case the Cookie will be removed from the builder.</li>
- * <li>If this request has has the conditional headers If-Modified-Since or
- * If-None-Match then the {@link #isConditional()} header is set to true.</li>
- * </ul>
- * <p>A PushBuilder can be customized by chained calls to mutator methods before the
- * {@link #push()} method is called to initiate a push request with the current state
- * of the builder. After the call to {@link #push()}, the builder may be reused for
- * another push, however the {@link #path(String)}, {@link #etag(String)} and
- * {@link #lastModified(String)} values will have been nulled. All other
- * values are retained over calls to {@link #push()}.
+ *
+ * <li>If this request has has the conditional headers If-Modified-Since
+ * or If-None-Match, then the {@link #isConditional()} header is set to
+ * true.</li>
+ *
+ * </ul>
+ *
+ * <p>The {@link #path} method must be called on the {@code PushBuilder}
+ * instance before the call to {@link #push}. Failure to do so must
+ * cause an exception to be thrown from {@link
+ * #push}, as specified in that method.</p>
+ *
+ * <p>A PushBuilder can be customized by chained calls to mutator
+ * methods before the {@link #push()} method is called to initiate an
+ * asynchronous push request with the current state of the builder.
+ * After the call to {@link #push()}, the builder may be reused for
+ * another push, however the implementation must make it so the {@link
+ * #path(String)}, {@link #etag(String)} and {@link
+ * #lastModified(String)} values are cleared before returning from
+ * {@link #push}. All other values are retained over calls to {@link
+ * #push()}.
+ *
+ * @since 4.0
*/
public interface PushBuilder
{
- /** Set the method to be used for the push.
- * Defaults to GET.
+ /**
+ * <p>Set the method to be used for the push.</p>
+ *
+ * <p>Any non-empty String may be used for the method.</p>
+ *
* @param method the method to be used for the push.
* @return this builder.
+ * @throws NullPointerException if the argument is {@code null}
+ * @throws IllegalArgumentException if the argument is the empty String
*/
public abstract PushBuilder method(String method);
/** Set the query string to be used for the push.
- * Defaults to the requests query string.
- * Will be appended to any query String included in a call to {@link #path(String)}. This
- * method should be used instead of a query in {@link #path(String)} when multiple
- * {@link #push()} calls are to be made with the same query string, or to remove a
- * query string obtained from the associated request.
+ *
+ * Will be appended to any query String included in a call to {@link
+ * #path(String)}. Any duplicate parameters must be preserved. This
+ * method should be used instead of a query in {@link #path(String)}
+ * when multiple {@link #push()} calls are to be made with the same
+ * query string.
* @param queryString the query string to be used for the push.
* @return this builder.
*/
@@ -108,33 +144,55 @@ public interface PushBuilder
*/
public abstract PushBuilder conditional(boolean conditional);
- /** Set a header to be used for the push.
+ /**
+ * <p>Set a header to be used for the push. If the builder has an
+ * existing header with the same name, its value is overwritten.</p>
+ *
* @param name The header name to set
* @param value The header value to set
* @return this builder.
*/
public abstract PushBuilder setHeader(String name, String value);
+
- /** Add a header to be used for the push.
+ /**
+ * <p>Add a header to be used for the push.</p>
* @param name The header name to add
* @param value The header value to add
* @return this builder.
*/
public abstract PushBuilder addHeader(String name, String value);
+
+
+ /**
+ * <p>Remove the named header. If the header does not exist, take
+ * no action.</p>
+ *
+ * @param name The name of the header to remove
+ * @return this builder.
+ */
+ public abstract PushBuilder removeHeader(String name);
+
- /** Set the URI path to be used for the push.
- * The path may start with "/" in which case it is treated as an
- * absolute path, otherwise it is relative to the context path of
- * the associated request.
- * There is no path default and {@link #path(String)} must be called
- * before every call to {@link #push()}
+
+ /**
+ * Set the URI path to be used for the push. The path may start
+ * with "/" in which case it is treated as an absolute path,
+ * otherwise it is relative to the context path of the associated
+ * request. There is no path default and {@link #path(String)} must
+ * be called before every call to {@link #push()}. If a query
+ * string is present in the argument {@code path}, its contents must
+ * be merged with the contents previously passed to {@link
+ * #queryString}, preserving duplicates.
+ *
* @param path the URI path to be used for the push, which may include a
* query string.
* @return this builder.
*/
public abstract PushBuilder path(String path);
- /** Set the etag to be used for conditional pushes.
+ /**
+ * Set the etag to be used for conditional pushes.
* The etag will be used only if {@link #isConditional()} is true.
* Defaults to no etag. The value is nulled after every call to
* {@link #push()}
@@ -143,33 +201,44 @@ public interface PushBuilder
*/
public abstract PushBuilder etag(String etag);
- /** Set the last modified date to be used for conditional pushes.
- * The last modified date will be used only if {@link #isConditional()} is true.
- * Defaults to no date. The value is nulled after every call to
- * {@link #push()}
+ /**
+ * Set the last modified date to be used for conditional pushes.
+ * The last modified date will be used only if {@link
+ * #isConditional()} is true. Defaults to no date. The value is
+ * nulled after every call to {@link #push()}
* @param lastModified the last modified date to be used for the push.
* @return this builder.
- * */
+ */
public abstract PushBuilder lastModified(String lastModified);
- /** Push a resource.
- * Push a resource based on the current state of the PushBuilder. If {@link #isConditional()}
- * is true and an etag or lastModified value is provided, then an appropriate conditional header
- * will be generated. If both an etag and lastModified value are provided only an If-None-Match header
- * will be generated. If the builder has a session ID, then the pushed request
- * will include the session ID either as a Cookie or as a URI parameter as appropriate. The builders
- * query string is merged with any passed query string.
- * After initiating the push, the builder has its path, etag and lastModified fields nulled. All
- * other fields are left as is for possible reuse in another push.
- * @throws IllegalArgumentException if the method set expects a request body (eg POST)
+ /** Push a resource given the current state of the builder,
+ * returning immediately without blocking.
+ *
+ * <p>Push a resource based on the current state of the PushBuilder.
+ * If {@link #isConditional()} is true and an etag or lastModified
+ * value is provided, then an appropriate conditional header will be
+ * generated. If both an etag and lastModified value are provided
+ * only an If-None-Match header will be generated. If the builder
+ * has a session ID, then the pushed request will include the
+ * session ID either as a Cookie or as a URI parameter as
+ * appropriate. The builders query string is merged with any passed
+ * query string.</p>
+ *
+ * <p>Before returning from this method, the builder has its path,
+ * etag and lastModified fields nulled. All other fields are left as
+ * is for possible reuse in another push.</p>
+ *
+ * @throws IllegalArgumentException if the method set expects a
+ * request body (eg POST)
+ *
+ * @throws IllegalStateException if there was no call to {@link
+ * #path} on this instance either between its instantiation or the
+ * last call to {@code push()} that did not throw an
+ * IllegalStateException.
*/
public abstract void push();
-
-
-
-
public abstract String getMethod();
public abstract String getQueryString();
public abstract String getSessionId();
@@ -179,7 +248,4 @@ public interface PushBuilder
public abstract String getPath();
public abstract String getEtag();
public abstract String getLastModified();
-
-
-
} \ No newline at end of file
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilderImpl.java b/jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilderImpl.java
index f39cffa878..def6bed888 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilderImpl.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilderImpl.java
@@ -32,14 +32,14 @@ import org.eclipse.jetty.util.log.Logger;
/* ------------------------------------------------------------ */
-/**
+/**
*/
public class PushBuilderImpl implements PushBuilder
-{
+{
private static final Logger LOG = Log.getLogger(PushBuilderImpl.class);
private final static HttpField JettyPush = new HttpField("x-http2-push","PushBuilder");
-
+
private final Request _request;
private final HttpFields _fields;
private String _method;
@@ -49,7 +49,7 @@ public class PushBuilderImpl implements PushBuilder
private String _path;
private String _etag;
private String _lastModified;
-
+
public PushBuilderImpl(Request request, HttpFields fields, String method, String queryString, String sessionId, boolean conditional)
{
super();
@@ -65,124 +65,88 @@ public class PushBuilderImpl implements PushBuilder
}
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#getMethod()
- */
@Override
public String getMethod()
{
return _method;
}
-
+
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#method(java.lang.String)
- */
@Override
public PushBuilder method(String method)
{
_method = method;
return this;
}
-
+
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#getQueryString()
- */
@Override
public String getQueryString()
{
return _queryString;
}
-
+
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#queryString(java.lang.String)
- */
@Override
public PushBuilder queryString(String queryString)
{
_queryString = queryString;
return this;
}
-
+
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#getSessionId()
- */
@Override
public String getSessionId()
{
return _sessionId;
}
-
+
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#sessionId(java.lang.String)
- */
@Override
public PushBuilder sessionId(String sessionId)
{
_sessionId = sessionId;
return this;
}
-
+
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#isConditional()
- */
@Override
public boolean isConditional()
{
return _conditional;
}
-
+
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#conditional(boolean)
- */
@Override
public PushBuilder conditional(boolean conditional)
{
_conditional = conditional;
return this;
}
-
+
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#getHeaderNames()
- */
@Override
public Set<String> getHeaderNames()
{
return _fields.getFieldNamesCollection();
}
-
+
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#getHeader(java.lang.String)
- */
@Override
public String getHeader(String name)
{
return _fields.get(name);
}
-
+
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#setHeader(java.lang.String, java.lang.String)
- */
@Override
public PushBuilder setHeader(String name,String value)
{
_fields.put(name,value);
return this;
}
-
+
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#addHeader(java.lang.String, java.lang.String)
- */
@Override
public PushBuilder addHeader(String name,String value)
{
@@ -190,11 +154,15 @@ public class PushBuilderImpl implements PushBuilder
return this;
}
-
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#getPath()
- */
+ @Override
+ public PushBuilder removeHeader(String name)
+ {
+ _fields.remove(name);
+ return this;
+ }
+
+ /* ------------------------------------------------------------ */
@Override
public String getPath()
{
@@ -202,9 +170,6 @@ public class PushBuilderImpl implements PushBuilder
}
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#path(java.lang.String)
- */
@Override
public PushBuilder path(String path)
{
@@ -213,9 +178,6 @@ public class PushBuilderImpl implements PushBuilder
}
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#getEtag()
- */
@Override
public String getEtag()
{
@@ -223,9 +185,6 @@ public class PushBuilderImpl implements PushBuilder
}
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#etag(java.lang.String)
- */
@Override
public PushBuilder etag(String etag)
{
@@ -234,9 +193,6 @@ public class PushBuilderImpl implements PushBuilder
}
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#getLastModified()
- */
@Override
public String getLastModified()
{
@@ -244,9 +200,6 @@ public class PushBuilderImpl implements PushBuilder
}
/* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#lastModified(java.lang.String)
- */
@Override
public PushBuilder lastModified(String lastModified)
{
@@ -255,40 +208,36 @@ public class PushBuilderImpl implements PushBuilder
}
/* ------------------------------------------------------------ */
- /* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.PushBuilder#push()
- */
@Override
public void push()
{
if (HttpMethod.POST.is(_method) || HttpMethod.PUT.is(_method))
throw new IllegalStateException("Bad Method "+_method);
-
+
if (_path==null || _path.length()==0)
throw new IllegalStateException("Bad Path "+_path);
-
+
String path=_path;
String query=_queryString;
int q=path.indexOf('?');
if (q>=0)
{
- query=(query!=null && query.length()>0)?(_path.substring(q+1)+'&'+query):_path.substring(q+1);
- path=_path.substring(0,q);
+ query=(query!=null && query.length()>0)?(path.substring(q+1)+'&'+query):path.substring(q+1);
+ path=path.substring(0,q);
}
-
+
if (!path.startsWith("/"))
path=URIUtil.addPaths(_request.getContextPath(),path);
-
+
String param=null;
if (_sessionId!=null)
{
if (_request.isRequestedSessionIdFromURL())
param="jsessionid="+_sessionId;
- // TODO else
+ // TODO else
// _fields.add("Cookie","JSESSIONID="+_sessionId);
}
-
+
if (_conditional)
{
if (_etag!=null)
@@ -296,16 +245,17 @@ public class PushBuilderImpl implements PushBuilder
else if (_lastModified!=null)
_fields.add(HttpHeader.IF_MODIFIED_SINCE,_lastModified);
}
-
- HttpURI uri = HttpURI.createHttpURI(_request.getScheme(),_request.getServerName(),_request.getServerPort(),_path,param,query,null);
+
+ HttpURI uri = HttpURI.createHttpURI(_request.getScheme(),_request.getServerName(),_request.getServerPort(),path,param,query,null);
MetaData.Request push = new MetaData.Request(_method,uri,_request.getHttpVersion(),_fields);
-
+
if (LOG.isDebugEnabled())
LOG.debug("Push {} {} inm={} ims={}",_method,uri,_fields.get(HttpHeader.IF_NONE_MATCH),_fields.get(HttpHeader.IF_MODIFIED_SINCE));
-
+
_request.getHttpChannel().getHttpTransport().push(push);
_path=null;
_etag=null;
_lastModified=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 fbc42e0708..e358cd64a6 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
@@ -161,6 +161,7 @@ public class Request implements HttpServletRequest
private final HttpInput _input;
private MetaData.Request _metadata;
+ private String _originalURI;
private String _contextPath;
private String _servletPath;
@@ -937,22 +938,25 @@ public class Request implements HttpServletRequest
@Override
public String getLocalName()
{
- if (_channel==null)
+ if (_channel!=null)
{
- try
- {
- String name =InetAddress.getLocalHost().getHostName();
- if (StringUtil.ALL_INTERFACES.equals(name))
- return null;
- return name;
- }
- catch (java.net.UnknownHostException e)
- {
- LOG.ignore(e);
- }
+ InetSocketAddress local=_channel.getLocalAddress();
+ if (local!=null)
+ return local.getHostString();
}
- InetSocketAddress local=_channel.getLocalAddress();
- return local.getHostString();
+
+ try
+ {
+ String name =InetAddress.getLocalHost().getHostName();
+ if (StringUtil.ALL_INTERFACES.equals(name))
+ return null;
+ return name;
+ }
+ catch (java.net.UnknownHostException e)
+ {
+ LOG.ignore(e);
+ }
+ return null;
}
/* ------------------------------------------------------------ */
@@ -965,7 +969,7 @@ public class Request implements HttpServletRequest
if (_channel==null)
return 0;
InetSocketAddress local=_channel.getLocalAddress();
- return local.getPort();
+ return local==null?0:local.getPort();
}
/* ------------------------------------------------------------ */
@@ -1270,6 +1274,8 @@ public class Request implements HttpServletRequest
@Override
public RequestDispatcher getRequestDispatcher(String path)
{
+ path = URIUtil.compactPath(path);
+
if (path == null || _context == null)
return null;
@@ -1580,6 +1586,14 @@ public class Request implements HttpServletRequest
/* ------------------------------------------------------------ */
/**
+ * @return Returns the original uri passed in metadata before customization/rewrite
+ */
+ public String getOriginalURI()
+ {
+ return _originalURI;
+ }
+ /* ------------------------------------------------------------ */
+ /**
* @param uri the URI to set
*/
public void setHttpURI(HttpURI uri)
@@ -1739,7 +1753,6 @@ public class Request implements HttpServletRequest
return _savedNewSessions.get(key);
}
-
/* ------------------------------------------------------------ */
/**
* @param request the Request metadata
@@ -1747,6 +1760,7 @@ public class Request implements HttpServletRequest
public void setMetaData(org.eclipse.jetty.http.MetaData.Request request)
{
_metadata=request;
+ _originalURI=_metadata.getURIString();
setMethod(request.getMethod());
HttpURI uri = request.getURI();
@@ -1803,6 +1817,7 @@ public class Request implements HttpServletRequest
protected void recycle()
{
_metadata=null;
+ _originalURI=null;
if (_context != null)
throw new IllegalStateException("Request in context!");
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/RequestLogCollection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/RequestLogCollection.java
index 2359c32616..b6cfa1cea3 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/RequestLogCollection.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/RequestLogCollection.java
@@ -18,10 +18,10 @@
package org.eclipse.jetty.server;
-import java.util.ArrayList;
-
import static java.util.Arrays.asList;
+import java.util.ArrayList;
+
class RequestLogCollection
implements RequestLog
{
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 d7c2425963..9a5b170ddb 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
@@ -51,9 +51,8 @@ import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.http.PreEncodedHttpField;
import org.eclipse.jetty.io.RuntimeIOException;
+import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ErrorHandler;
-import org.eclipse.jetty.util.ByteArrayISO8859Writer;
-import org.eclipse.jetty.util.Jetty;
import org.eclipse.jetty.util.QuotedStringTokenizer;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.URIUtil;
@@ -65,12 +64,12 @@ import org.eclipse.jetty.util.log.Logger;
*/
public class Response implements HttpServletResponse
{
- private static final Logger LOG = Log.getLogger(Response.class);
+ private static final Logger LOG = Log.getLogger(Response.class);
private static final String __COOKIE_DELIM="\",;\\ \t";
private final static String __01Jan1970_COOKIE = DateGenerator.formatCookieDate(0).trim();
private final static int __MIN_BUFFER_SIZE = 1;
private final static HttpField __EXPIRES_01JAN1970 = new PreEncodedHttpField(HttpHeader.EXPIRES,DateGenerator.__01Jan1970);
-
+
// Cookie building buffer. Reduce garbage for cookie using applications
private static final ThreadLocal<StringBuilder> __cookieBuilder = new ThreadLocal<StringBuilder>()
@@ -81,7 +80,7 @@ public class Response implements HttpServletResponse
return new StringBuilder(128);
}
};
-
+
public enum OutputType
{
NONE, STREAM, WRITER
@@ -114,7 +113,7 @@ public class Response implements HttpServletResponse
private OutputType _outputType = OutputType.NONE;
private ResponseWriter _writer;
private long _contentLength = -1;
-
+
public Response(HttpChannel channel, HttpOutput out)
{
@@ -141,7 +140,7 @@ public class Response implements HttpServletResponse
_fields.clear();
_explicitEncoding=false;
}
-
+
public HttpOutput getHttpOutput()
{
return _out;
@@ -178,7 +177,7 @@ public class Response implements HttpServletResponse
cookie.getComment(),
cookie.isSecure(),
cookie.isHttpOnly(),
- cookie.getVersion());;
+ cookie.getVersion());
}
@Override
@@ -241,13 +240,13 @@ public class Response implements HttpServletResponse
// Format value and params
StringBuilder buf = __cookieBuilder.get();
buf.setLength(0);
-
+
// Name is checked for legality by servlet spec, but can also be passed directly so check again for quoting
boolean quote_name=isQuoteNeededForCookie(name);
quoteOnlyOrAppend(buf,name,quote_name);
-
+
buf.append('=');
-
+
// Remember name= part to look for other matching set-cookie
String name_equals=buf.toString();
@@ -260,7 +259,7 @@ public class Response implements HttpServletResponse
boolean quote_domain = has_domain && isQuoteNeededForCookie(domain);
boolean has_path = path!=null && path.length()>0;
boolean quote_path = has_path && isQuoteNeededForCookie(path);
-
+
// Upgrade the version if we have a comment or we need to quote value/path/domain or if they were already quoted
if (version==0 && ( comment!=null || quote_name || quote_value || quote_domain || quote_path ||
QuotedStringTokenizer.isQuoted(name) || QuotedStringTokenizer.isQuoted(value) ||
@@ -272,14 +271,14 @@ public class Response implements HttpServletResponse
buf.append (";Version=1");
else if (version>1)
buf.append (";Version=").append(version);
-
+
// Append path
if (has_path)
{
buf.append(";Path=");
quoteOnlyOrAppend(buf,path,quote_path);
}
-
+
// Append domain
if (has_domain)
{
@@ -297,7 +296,7 @@ public class Response implements HttpServletResponse
buf.append(__01Jan1970_COOKIE);
else
DateGenerator.formatCookieDate(buf, System.currentTimeMillis() + 1000L * maxAge);
-
+
// for v1 cookies, also send max-age
if (version>=1)
{
@@ -336,7 +335,7 @@ public class Response implements HttpServletResponse
}
}
}
-
+
// add the set cookie
_fields.add(HttpHeader.SET_COOKIE.toString(), buf.toString());
@@ -355,7 +354,7 @@ public class Response implements HttpServletResponse
{
if (s==null || s.length()==0)
return true;
-
+
if (QuotedStringTokenizer.isQuoted(s))
return false;
@@ -364,15 +363,15 @@ public class Response implements HttpServletResponse
char c = s.charAt(i);
if (__COOKIE_DELIM.indexOf(c)>=0)
return true;
-
+
if (c<0x20 || c>=0x7f)
throw new IllegalArgumentException("Illegal character in cookie value");
}
return false;
}
-
-
+
+
private static void quoteOnlyOrAppend(StringBuilder buf, String s, boolean quote)
{
if (quote)
@@ -380,7 +379,7 @@ public class Response implements HttpServletResponse
else
buf.append(s);
}
-
+
@Override
public boolean containsHeader(String name)
{
@@ -404,7 +403,7 @@ public class Response implements HttpServletResponse
int port = uri.getPort();
if (port < 0)
port = HttpScheme.HTTPS.asString().equalsIgnoreCase(uri.getScheme()) ? 443 : 80;
-
+
// Is it the same server?
if (!request.getServerName().equalsIgnoreCase(uri.getHost()))
return url;
@@ -422,7 +421,7 @@ public class Response implements HttpServletResponse
return null;
// should not encode if cookies in evidence
- if ((sessionManager.isUsingCookies() && request.isRequestedSessionIdFromCookie()) || !sessionManager.isUsingURLs())
+ if ((sessionManager.isUsingCookies() && request.isRequestedSessionIdFromCookie()) || !sessionManager.isUsingURLs())
{
int prefix = url.indexOf(sessionURLPrefix);
if (prefix != -1)
@@ -524,7 +523,7 @@ public class Response implements HttpServletResponse
LOG.debug("Aborting on sendError on committed response {} {}",code,message);
code=-1;
}
-
+
switch(code)
{
case -1:
@@ -534,91 +533,44 @@ public class Response implements HttpServletResponse
sendProcessing();
return;
default:
+ break;
}
- if (isCommitted())
- LOG.warn("Committed before "+code+" "+message);
-
resetBuffer();
+ _mimeType=null;
_characterEncoding=null;
+ _outputType = OutputType.NONE;
setHeader(HttpHeader.EXPIRES,null);
setHeader(HttpHeader.LAST_MODIFIED,null);
setHeader(HttpHeader.CACHE_CONTROL,null);
setHeader(HttpHeader.CONTENT_TYPE,null);
- setHeader(HttpHeader.CONTENT_LENGTH,null);
+ setHeader(HttpHeader.CONTENT_LENGTH, null);
- _outputType = OutputType.NONE;
setStatus(code);
- _reason=message;
Request request = _channel.getRequest();
Throwable cause = (Throwable)request.getAttribute(Dispatcher.ERROR_EXCEPTION);
if (message==null)
- message=cause==null?HttpStatus.getMessage(code):cause.toString();
+ {
+ _reason=HttpStatus.getMessage(code);
+ message=cause==null?_reason:cause.toString();
+ }
+ else
+ _reason=message;
- // If we are allowed to have a body
- if (code!=SC_NO_CONTENT &&
- code!=SC_NOT_MODIFIED &&
- code!=SC_PARTIAL_CONTENT &&
- code>=SC_OK)
- {
- ErrorHandler error_handler = ErrorHandler.getErrorHandler(_channel.getServer(),request.getContext()==null?null:request.getContext().getContextHandler());
- if (error_handler!=null)
- {
- request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,new Integer(code));
- request.setAttribute(RequestDispatcher.ERROR_MESSAGE, message);
- request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI, request.getRequestURI());
- request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME,request.getServletName());
- error_handler.handle(null,_channel.getRequest(),_channel.getRequest(),this );
- }
- else
- {
- setHeader(HttpHeader.CACHE_CONTROL, "must-revalidate,no-cache,no-store");
- setContentType(MimeTypes.Type.TEXT_HTML_8859_1.toString());
- try (ByteArrayISO8859Writer writer= new ByteArrayISO8859Writer(2048);)
- {
- message=StringUtil.sanitizeXmlString(message);
- String uri= request.getRequestURI();
- uri=StringUtil.sanitizeXmlString(uri);
-
- writer.write("<html>\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html;charset=ISO-8859-1\"/>\n");
- writer.write("<title>Error ");
- writer.write(Integer.toString(code));
- writer.write(' ');
- if (message==null)
- writer.write(message);
- writer.write("</title>\n</head>\n<body>\n<h2>HTTP ERROR: ");
- writer.write(Integer.toString(code));
- writer.write("</h2>\n<p>Problem accessing ");
- writer.write(uri);
- writer.write(". Reason:\n<pre> ");
- writer.write(message);
- writer.write("</pre>");
- writer.write("</p>\n<hr />");
-
- getHttpChannel().getHttpConfiguration().writePoweredBy(writer,null,"<hr/>");
- writer.write("\n</body>\n</html>\n");
-
- writer.flush();
- setContentLength(writer.size());
- try (ServletOutputStream outputStream = getOutputStream())
- {
- writer.writeTo(outputStream);
- writer.destroy();
- }
- }
- }
- }
- else if (code!=SC_PARTIAL_CONTENT)
+ // If we are allowed to have a body, then produce the error page.
+ if (code != SC_NO_CONTENT && code != SC_NOT_MODIFIED &&
+ code != SC_PARTIAL_CONTENT && code >= SC_OK)
{
- // TODO work out why this is required?
- _channel.getRequest().getHttpFields().remove(HttpHeader.CONTENT_TYPE);
- _channel.getRequest().getHttpFields().remove(HttpHeader.CONTENT_LENGTH);
- _characterEncoding=null;
- _mimeType=null;
+ ContextHandler.Context context = request.getContext();
+ ContextHandler contextHandler = context == null ? _channel.getState().getContextHandler() : context.getContextHandler();
+ ErrorHandler error_handler = ErrorHandler.getErrorHandler(_channel.getServer(), contextHandler);
+ request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE, code);
+ request.setAttribute(RequestDispatcher.ERROR_MESSAGE, message);
+ request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI, request.getRequestURI());
+ request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME, request.getServletName());
+ error_handler.handle(null, request, request, this);
}
-
- closeOutput();
}
/**
@@ -637,7 +589,7 @@ public class Response implements HttpServletResponse
_channel.sendResponse(HttpGenerator.PROGRESS_102_INFO, null, true);
}
}
-
+
/**
* Sends a response with one of the 300 series redirection codes.
* @param code the redirect status code
@@ -648,7 +600,7 @@ public class Response implements HttpServletResponse
{
if ((code < HttpServletResponse.SC_MULTIPLE_CHOICES) || (code >= HttpServletResponse.SC_BAD_REQUEST))
throw new IllegalArgumentException("Not a 3xx redirect code");
-
+
if (isIncluding())
return;
@@ -672,11 +624,11 @@ public class Response implements HttpServletResponse
if (!location.startsWith("/"))
buf.append('/');
}
-
+
if(location==null)
throw new IllegalStateException("path cannot be above root");
buf.append(location);
-
+
location=buf.toString();
}
@@ -791,13 +743,13 @@ public class Response implements HttpServletResponse
setContentType(value);
return;
}
-
+
if (HttpHeader.CONTENT_LENGTH.is(name))
{
setHeader(name,value);
return;
}
-
+
_fields.add(name, value);
}
@@ -822,7 +774,7 @@ public class Response implements HttpServletResponse
_contentLength = value;
}
}
-
+
@Override
public void setStatus(int sc)
{
@@ -841,7 +793,7 @@ public class Response implements HttpServletResponse
{
setStatusWithReason(sc,sm);
}
-
+
public void setStatusWithReason(int sc, String sm)
{
if (sc <= 0)
@@ -903,9 +855,9 @@ public class Response implements HttpServletResponse
setCharacterEncoding(encoding,false);
}
}
-
+
Locale locale = getLocale();
-
+
if (_writer != null && _writer.isFor(locale,encoding))
_writer.reopen();
else
@@ -917,7 +869,7 @@ public class Response implements HttpServletResponse
else
_writer = new ResponseWriter(new EncodingHttpWriter(_out, encoding),locale,encoding);
}
-
+
// Set the output type at the end, because setCharacterEncoding() checks for it
_outputType = OutputType.WRITER;
}
@@ -939,7 +891,7 @@ public class Response implements HttpServletResponse
long written = _out.getWritten();
if (written > len)
throw new IllegalArgumentException("setContentLength(" + len + ") when already written " + written);
-
+
_fields.putLongField(HttpHeader.CONTENT_LENGTH, len);
if (isAllContentWritten(written))
{
@@ -963,7 +915,7 @@ public class Response implements HttpServletResponse
else
_fields.remove(HttpHeader.CONTENT_LENGTH);
}
-
+
public long getContentLength()
{
return _contentLength;
@@ -1006,7 +958,7 @@ public class Response implements HttpServletResponse
_contentLength = len;
_fields.putLongField(HttpHeader.CONTENT_LENGTH.toString(), len);
}
-
+
@Override
public void setContentLengthLong(long length)
{
@@ -1018,7 +970,7 @@ public class Response implements HttpServletResponse
{
setCharacterEncoding(encoding,true);
}
-
+
private void setCharacterEncoding(String encoding, boolean explicit)
{
if (isIncluding() || isWriting())
@@ -1029,12 +981,12 @@ public class Response implements HttpServletResponse
if (encoding == null)
{
_explicitEncoding=false;
-
+
// Clear any encoding.
if (_characterEncoding != null)
{
_characterEncoding = null;
-
+
if (_mimeType!=null)
{
_mimeType=_mimeType.getBaseType();
@@ -1070,7 +1022,7 @@ public class Response implements HttpServletResponse
}
}
}
-
+
@Override
public void setContentType(String contentType)
{
@@ -1092,7 +1044,7 @@ public class Response implements HttpServletResponse
{
_contentType = contentType;
_mimeType = MimeTypes.CACHE.get(contentType);
-
+
String charset;
if (_mimeType!=null && _mimeType.getCharset()!=null && !_mimeType.isCharsetAssumed())
charset=_mimeType.getCharsetString();
@@ -1129,7 +1081,7 @@ public class Response implements HttpServletResponse
_fields.put(_mimeType.getContentTypeField());
}
}
-
+
}
@Override
@@ -1165,7 +1117,7 @@ public class Response implements HttpServletResponse
_fields.clear();
String connection = _channel.getRequest().getHeader(HttpHeader.CONNECTION.asString());
-
+
if (connection != null)
{
for (String value: StringUtil.csvSplit(null,connection,0,connection.length()))
@@ -1195,12 +1147,12 @@ public class Response implements HttpServletResponse
}
public void reset(boolean preserveCookies)
- {
+ {
if (!preserveCookies)
reset();
else
{
- ArrayList<String> cookieValues = new ArrayList<String>(5);
+ ArrayList<String> cookieValues = new ArrayList<>(5);
Enumeration<String> vals = _fields.getValues(HttpHeader.SET_COOKIE.asString());
while (vals.hasMoreElements())
cookieValues.add(vals.nextElement());
@@ -1229,11 +1181,11 @@ public class Response implements HttpServletResponse
{
return new MetaData.Response(_channel.getRequest().getHttpVersion(), getStatus(), getReason(), _fields, getLongContentLength());
}
-
+
/** Get the MetaData.Response committed for this response.
- * This may differ from the meta data in this response for
+ * This may differ from the meta data in this response for
* exceptional responses (eg 4xx and 5xx responses generated
- * by the container) and the committedMetaData should be used
+ * by the container) and the committedMetaData should be used
* for logging purposes.
* @return The committed MetaData or a {@link #newResponseMetaData()}
* if not yet committed.
@@ -1307,7 +1259,7 @@ public class Response implements HttpServletResponse
{
return String.format("%s %d %s%n%s", _channel.getRequest().getHttpVersion(), _status, _reason == null ? "" : _reason, _fields);
}
-
+
public void putHeaders(HttpContent content,long contentLength, boolean etag)
{
@@ -1334,11 +1286,11 @@ public class Response implements HttpServletResponse
_characterEncoding=content.getCharacterEncoding();
_mimeType=content.getMimeType();
}
-
+
HttpField ce=content.getContentEncoding();
if (ce!=null)
_fields.put(ce);
-
+
if (etag)
{
HttpField et = content.getETag();
@@ -1346,9 +1298,9 @@ public class Response implements HttpServletResponse
_fields.put(et);
}
}
-
+
public static void putHeaders(HttpServletResponse response, HttpContent content, long contentLength, boolean etag)
- {
+ {
long lml=content.getResource().lastModified();
if (lml>=0)
response.setDateHeader(HttpHeader.LAST_MODIFIED.asString(),lml);
@@ -1370,7 +1322,7 @@ public class Response implements HttpServletResponse
String ce=content.getContentEncodingValue();
if (ce!=null)
response.setHeader(HttpHeader.CONTENT_ENCODING.asString(),ce);
-
+
if (etag)
{
String et=content.getETagValue();
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/SecureRequestCustomizer.java b/jetty-server/src/main/java/org/eclipse/jetty/server/SecureRequestCustomizer.java
index 7dc74588d8..aefb547f2a 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/SecureRequestCustomizer.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/SecureRequestCustomizer.java
@@ -27,6 +27,7 @@ import javax.servlet.ServletRequest;
import org.eclipse.jetty.http.BadMessageException;
import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.ssl.SslConnection;
import org.eclipse.jetty.io.ssl.SslConnection.DecryptedEndPoint;
import org.eclipse.jetty.util.TypeUtil;
@@ -66,18 +67,26 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer
@Override
public void customize(Connector connector, HttpConfiguration channelConfig, Request request)
{
- if (request.getHttpChannel().getEndPoint() instanceof DecryptedEndPoint)
+ EndPoint endp = request.getHttpChannel().getEndPoint();
+ if (endp instanceof DecryptedEndPoint)
{
- request.setSecure(true);
-
- if (request.getHttpURI().getScheme()==null)
- request.setScheme(HttpScheme.HTTPS.asString());
-
- SslConnection.DecryptedEndPoint ssl_endp = (DecryptedEndPoint)request.getHttpChannel().getEndPoint();
+ SslConnection.DecryptedEndPoint ssl_endp = (DecryptedEndPoint)endp;
SslConnection sslConnection = ssl_endp.getSslConnection();
SSLEngine sslEngine=sslConnection.getSSLEngine();
customize(sslEngine,request);
+
+ if (request.getHttpURI().getScheme()==null)
+ request.setScheme(HttpScheme.HTTPS.asString());
+ }
+ else if (endp instanceof ProxyConnectionFactory.ProxyEndPoint)
+ {
+ ProxyConnectionFactory.ProxyEndPoint proxy = (ProxyConnectionFactory.ProxyEndPoint)endp;
+ if (request.getHttpURI().getScheme()==null && proxy.getAttribute(ProxyConnectionFactory.TLS_VERSION)!=null)
+ request.setScheme(HttpScheme.HTTPS.asString());
}
+
+ if (HttpScheme.HTTPS.is(request.getScheme()))
+ request.setSecure(true);
}
/**
@@ -104,7 +113,6 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer
*/
public void customize(SSLEngine sslEngine, Request request)
{
- request.setScheme(HttpScheme.HTTPS.asString());
SSLSession sslSession = sslEngine.getSession();
if (_sniHostCheck)
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java
index 1665a19b16..4cd668dd95 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java
@@ -24,6 +24,7 @@ import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.nio.channels.Channel;
+import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
@@ -32,6 +33,7 @@ import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.ChannelEndPoint;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.ManagedSelector;
@@ -229,7 +231,6 @@ public class ServerConnector extends AbstractNetworkConnector
_manager = newSelectorManager(getExecutor(), getScheduler(),
selectors>0?selectors:Math.max(1,Math.min(4,Runtime.getRuntime().availableProcessors()/2)));
addBean(_manager, true);
- setSelectorPriorityDelta(-1);
setAcceptorPriorityDelta(-2);
}
@@ -426,7 +427,7 @@ public class ServerConnector extends AbstractNetworkConnector
return _localPort;
}
- protected SelectChannelEndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey key) throws IOException
+ protected ChannelEndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey key) throws IOException
{
return new SelectChannelEndPoint(channel, selectSet, key, getScheduler(), getIdleTimeout());
}
@@ -493,19 +494,19 @@ public class ServerConnector extends AbstractNetworkConnector
}
@Override
- protected void accepted(SocketChannel channel) throws IOException
+ protected void accepted(SelectableChannel channel) throws IOException
{
- ServerConnector.this.accepted(channel);
+ ServerConnector.this.accepted((SocketChannel)channel);
}
@Override
- protected SelectChannelEndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey selectionKey) throws IOException
+ protected ChannelEndPoint newEndPoint(SelectableChannel channel, ManagedSelector selectSet, SelectionKey selectionKey) throws IOException
{
- return ServerConnector.this.newEndPoint(channel, selectSet, selectionKey);
+ return ServerConnector.this.newEndPoint((SocketChannel)channel, selectSet, selectionKey);
}
@Override
- public Connection newConnection(SocketChannel channel, EndPoint endpoint, Object attachment) throws IOException
+ public Connection newConnection(SelectableChannel channel, EndPoint endpoint, Object attachment) throws IOException
{
return getDefaultConnectionFactory().newConnection(ServerConnector.this, endpoint);
}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/SocketCustomizationListener.java b/jetty-server/src/main/java/org/eclipse/jetty/server/SocketCustomizationListener.java
index cdc402120c..9ac93c7a52 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/SocketCustomizationListener.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/SocketCustomizationListener.java
@@ -20,12 +20,12 @@ package org.eclipse.jetty.server;
import java.net.Socket;
-import org.eclipse.jetty.io.ChannelEndPoint;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.Connection.Listener;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.SocketChannelEndPoint;
import org.eclipse.jetty.io.ssl.SslConnection;
import org.eclipse.jetty.io.ssl.SslConnection.DecryptedEndPoint;
-import org.eclipse.jetty.io.EndPoint;
/* ------------------------------------------------------------ */
@@ -70,9 +70,9 @@ public class SocketCustomizationListener implements Listener
ssl=true;
}
- if (endp instanceof ChannelEndPoint)
+ if (endp instanceof SocketChannelEndPoint)
{
- Socket socket = ((ChannelEndPoint)endp).getSocket();
+ Socket socket = ((SocketChannelEndPoint)endp).getSocket();
customize(socket,connection.getClass(),ssl);
}
}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AbstractHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AbstractHandler.java
index d5fca99b09..7501d84def 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AbstractHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AbstractHandler.java
@@ -21,7 +21,14 @@ package org.eclipse.jetty.server.handler;
import java.io.IOException;
+import javax.servlet.DispatcherType;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
@@ -47,6 +54,31 @@ public abstract class AbstractHandler extends ContainerLifeCycle implements Hand
{
}
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ if (baseRequest.getDispatcherType()==DispatcherType.ERROR)
+ doError(target,baseRequest,request,response);
+ else
+ doHandle(target,baseRequest,request,response);
+ }
+
+ /* ------------------------------------------------------------ */
+ protected void doHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ }
+
+ /* ------------------------------------------------------------ */
+ protected void doError(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ Object o = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
+ int code = (o instanceof Integer)?((Integer)o).intValue():(o!=null?Integer.valueOf(o.toString()):500);
+ o = request.getAttribute(RequestDispatcher.ERROR_MESSAGE);
+ String reason = o!=null?o.toString():null;
+
+ response.sendError(code,reason);
+ }
+
/* ------------------------------------------------------------ */
/*
* @see org.eclipse.thread.LifeCycle#start()
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 94f931cb7f..668df07f96 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
@@ -1143,11 +1143,30 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
}
}
- if (DispatcherType.REQUEST.equals(dispatch) && isProtectedTarget(target))
+ switch(dispatch)
{
- response.sendError(HttpServletResponse.SC_NOT_FOUND);
- baseRequest.setHandled(true);
- return;
+ case REQUEST:
+ if (isProtectedTarget(target))
+ {
+ response.sendError(HttpServletResponse.SC_NOT_FOUND);
+ baseRequest.setHandled(true);
+ return;
+ }
+ break;
+
+ case ERROR:
+ // If this is already a dispatch to an error page, proceed normally
+ if (Boolean.TRUE.equals(baseRequest.getAttribute(Dispatcher.__ERROR_DISPATCH)))
+ break;
+
+ Object error = request.getAttribute(Dispatcher.ERROR_STATUS_CODE);
+ // We can just call sendError here. If there is no error page, then one will
+ // be generated. If there is an error page, then a RequestDispatcher will be
+ // used to route the request through appropriate filters etc.
+ response.sendError((error instanceof Integer)?((Integer)error).intValue():500);
+ return;
+ default:
+ break;
}
// start manual inline of nextHandle(target,baseRequest,request,response);
@@ -1654,7 +1673,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
return null;
if (_classLoader == null)
- return Loader.loadClass(this.getClass(),className);
+ return Loader.loadClass(className);
return _classLoader.loadClass(className);
}
@@ -2298,7 +2317,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
try
{
@SuppressWarnings({ "unchecked", "rawtypes" })
- Class<? extends EventListener> clazz = _classLoader==null?Loader.loadClass(ContextHandler.class,className):(Class)_classLoader.loadClass(className);
+ Class<? extends EventListener> clazz = _classLoader==null?Loader.loadClass(className):(Class)_classLoader.loadClass(className);
addListener(clazz);
}
catch (ClassNotFoundException e)
@@ -2391,7 +2410,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
//classloader, or a parent of it
try
{
- Class<?> reflect = Loader.loadClass(getClass(), "sun.reflect.Reflection");
+ Class<?> reflect = Loader.loadClass("sun.reflect.Reflection");
Method getCallerClass = reflect.getMethod("getCallerClass", Integer.TYPE);
Class<?> caller = (Class<?>)getCallerClass.invoke(null, 2);
@@ -2869,7 +2888,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
/**
* @param context The context being entered
* @param request A request that is applicable to the scope, or null
- * @param reason An object that indicates the reason the scope is being entered
+ * @param reason An object that indicates the reason the scope is being entered.
*/
void enterScope(Context context, Request request, Object reason);
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DebugHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DebugHandler.java
index 97a9b388ca..f8fd5a33e2 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DebugHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DebugHandler.java
@@ -27,7 +27,6 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.server.AbstractConnector;
import org.eclipse.jetty.server.Connector;
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 a30a1e3149..97c29dac20 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
@@ -24,6 +24,7 @@ import java.io.StringWriter;
import java.io.Writer;
import java.nio.ByteBuffer;
+import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -33,38 +34,35 @@ import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.server.AsyncContextEvent;
import org.eclipse.jetty.server.Dispatcher;
+import org.eclipse.jetty.server.HttpOutput;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.ByteArrayISO8859Writer;
-import org.eclipse.jetty.util.Jetty;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
-/* ------------------------------------------------------------ */
-/** Handler for Error pages
- * An ErrorHandler is registered with {@link ContextHandler#setErrorHandler(ErrorHandler)} or
- * {@link org.eclipse.jetty.server.Server#addBean(Object)}.
- * It is called by the HttpResponse.sendError method to write a error page via {@link #handle(String, Request, HttpServletRequest, HttpServletResponse)}
- * or via {@link #badMessageError(int, String, HttpFields)} for bad requests for which a dispatch cannot be done.
- *
+/**
+ * <p>Component that handles Error Pages.</p>
+ * <p>An ErrorHandler is registered with {@link ContextHandler#setErrorHandler(ErrorHandler)} or
+ * {@link org.eclipse.jetty.server.Server#addBean(Object)}.</p>
+ * <p>It is called by {@link HttpServletResponse#sendError(int)} to write an error page via
+ * {@link #handle(String, Request, HttpServletRequest, HttpServletResponse)}
+ * or via {@link #badMessageError(int, String, HttpFields)} for bad requests for which a
+ * dispatch cannot be done.</p>
*/
public class ErrorHandler extends AbstractHandler
-{
+{
private static final Logger LOG = Log.getLogger(ErrorHandler.class);
- public final static String ERROR_PAGE="org.eclipse.jetty.server.error_page";
-
- boolean _showStacks=true;
- boolean _showMessageInTitle=true;
- String _cacheControl="must-revalidate,no-cache,no-store";
- /* ------------------------------------------------------------ */
- /*
- * @see org.eclipse.jetty.server.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
- */
+ private boolean _showStacks = true;
+ private boolean _showMessageInTitle = true;
+ private String _cacheControl = "must-revalidate,no-cache,no-store";
+
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{
@@ -74,139 +72,151 @@ public class ErrorHandler extends AbstractHandler
baseRequest.setHandled(true);
return;
}
-
+
if (this instanceof ErrorPageMapper)
{
- String error_page=((ErrorPageMapper)this).getErrorPage(request);
- if (error_page!=null && request.getServletContext()!=null)
+ String error_page = ((ErrorPageMapper)this).getErrorPage(request);
+
+ ServletContext context = request.getServletContext();
+ if (context == null)
{
- String old_error_page=(String)request.getAttribute(ERROR_PAGE);
- if (old_error_page==null || !old_error_page.equals(error_page))
- {
- request.setAttribute(ERROR_PAGE, error_page);
+ AsyncContextEvent event = baseRequest.getHttpChannelState().getAsyncContextEvent();
+ context = event == null ? null : event.getServletContext();
+ }
- Dispatcher dispatcher = (Dispatcher) request.getServletContext().getRequestDispatcher(error_page);
+ if (error_page != null && context != null)
+ {
+ Dispatcher dispatcher = (Dispatcher)context.getRequestDispatcher(error_page);
+ if (dispatcher != null)
+ {
try
{
- if(dispatcher!=null)
- {
- dispatcher.error(request, response);
- return;
- }
- LOG.warn("No error page "+error_page);
+ dispatcher.error(request, response);
+ return;
}
- catch (ServletException e)
+ catch (ServletException x)
{
- LOG.warn(Log.EXCEPTION, e);
- return;
+ throw new IOException(x);
}
}
+ else
+ {
+ LOG.warn("Could not dispatch to error page: {}", error_page);
+ // Fall through to provide the default error page.
+ }
}
}
-
+
baseRequest.setHandled(true);
- response.setContentType(MimeTypes.Type.TEXT_HTML_8859_1.asString());
- if (_cacheControl!=null)
- response.setHeader(HttpHeader.CACHE_CONTROL.asString(), _cacheControl);
- ByteArrayISO8859Writer writer= new ByteArrayISO8859Writer(4096);
- String reason=(response instanceof Response)?((Response)response).getReason():null;
- handleErrorPage(request, writer, response.getStatus(), reason);
- writer.flush();
- response.setContentLength(writer.size());
- writer.writeTo(response.getOutputStream());
- writer.destroy();
+
+ HttpOutput out = baseRequest.getResponse().getHttpOutput();
+ if (!out.isAsync())
+ {
+ response.setContentType(MimeTypes.Type.TEXT_HTML_8859_1.asString());
+ String cacheHeader = getCacheControl();
+ if (cacheHeader != null)
+ response.setHeader(HttpHeader.CACHE_CONTROL.asString(), cacheHeader);
+ ByteArrayISO8859Writer writer = new ByteArrayISO8859Writer(4096);
+ String reason = (response instanceof Response) ? ((Response)response).getReason() : null;
+ handleErrorPage(request, writer, response.getStatus(), reason);
+ writer.flush();
+ response.setContentLength(writer.size());
+ writer.writeTo(response.getOutputStream());
+ writer.destroy();
+ }
}
/* ------------------------------------------------------------ */
protected void handleErrorPage(HttpServletRequest request, Writer writer, int code, String message)
- throws IOException
+ throws IOException
{
- writeErrorPage(request, writer, code, message, _showStacks);
+ writeErrorPage(request, writer, code, message, isShowStacks());
}
/* ------------------------------------------------------------ */
protected void writeErrorPage(HttpServletRequest request, Writer writer, int code, String message, boolean showStacks)
- throws IOException
+ throws IOException
{
if (message == null)
- message=HttpStatus.getMessage(code);
+ message = HttpStatus.getMessage(code);
writer.write("<html>\n<head>\n");
- writeErrorPageHead(request,writer,code,message);
+ writeErrorPageHead(request, writer, code, message);
writer.write("</head>\n<body>");
- writeErrorPageBody(request,writer,code,message,showStacks);
+ writeErrorPageBody(request, writer, code, message, showStacks);
writer.write("\n</body>\n</html>\n");
}
/* ------------------------------------------------------------ */
protected void writeErrorPageHead(HttpServletRequest request, Writer writer, int code, String message)
- throws IOException
- {
+ throws IOException
+ {
writer.write("<meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\"/>\n");
writer.write("<title>Error ");
writer.write(Integer.toString(code));
- if (_showMessageInTitle)
+ if (getShowMessageInTitle())
{
writer.write(' ');
- write(writer,message);
+ write(writer, message);
}
writer.write("</title>\n");
}
/* ------------------------------------------------------------ */
protected void writeErrorPageBody(HttpServletRequest request, Writer writer, int code, String message, boolean showStacks)
- throws IOException
+ throws IOException
{
- String uri= request.getRequestURI();
+ String uri = request.getRequestURI();
- writeErrorPageMessage(request,writer,code,message,uri);
+ writeErrorPageMessage(request, writer, code, message, uri);
if (showStacks)
- writeErrorPageStacks(request,writer);
+ writeErrorPageStacks(request, writer);
Request.getBaseRequest(request).getHttpChannel().getHttpConfiguration()
- .writePoweredBy(writer,"<hr>","<hr/>\n");
+ .writePoweredBy(writer, "<hr>", "<hr/>\n");
}
/* ------------------------------------------------------------ */
- protected void writeErrorPageMessage(HttpServletRequest request, Writer writer, int code, String message,String uri)
- throws IOException
+ protected void writeErrorPageMessage(HttpServletRequest request, Writer writer, int code, String message, String uri)
+ throws IOException
{
writer.write("<h2>HTTP ERROR ");
writer.write(Integer.toString(code));
writer.write("</h2>\n<p>Problem accessing ");
- write(writer,uri);
+ write(writer, uri);
writer.write(". Reason:\n<pre> ");
- write(writer,message);
+ write(writer, message);
writer.write("</pre></p>");
}
/* ------------------------------------------------------------ */
protected void writeErrorPageStacks(HttpServletRequest request, Writer writer)
- throws IOException
+ throws IOException
{
Throwable th = (Throwable)request.getAttribute("javax.servlet.error.exception");
- while(th!=null)
+ while (th != null)
{
writer.write("<h3>Caused by:</h3><pre>");
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
th.printStackTrace(pw);
pw.flush();
- write(writer,sw.getBuffer().toString());
+ write(writer, sw.getBuffer().toString());
writer.write("</pre>\n");
- th =th.getCause();
+ th = th.getCause();
}
}
/* ------------------------------------------------------------ */
- /** Bad Message Error body
- * <p>Generate a error response body to be sent for a bad message.
- * In this case there is something wrong with the request, so either
+ /**
+ * <p>Generate a error response body to be sent for a bad message.</p>
+ * <p>In this case there is something wrong with the request, so either
* a request cannot be built, or it is not safe to build a request.
- * This method allows for a simple error page body to be returned
- * and some response headers to be set.
+ * This method allows for a simple error page body to be returned
+ * and some response headers to be set.</p>
+ *
* @param status The error code that will be sent
* @param reason The reason for the error code (may be null)
* @param fields The header fields that will be sent with the response.
@@ -214,14 +224,14 @@ public class ErrorHandler extends AbstractHandler
*/
public ByteBuffer badMessageError(int status, String reason, HttpFields fields)
{
- if (reason==null)
- reason=HttpStatus.getMessage(status);
- fields.put(HttpHeader.CONTENT_TYPE,MimeTypes.Type.TEXT_HTML_8859_1.asString());
+ if (reason == null)
+ reason = HttpStatus.getMessage(status);
+ fields.put(HttpHeader.CONTENT_TYPE, MimeTypes.Type.TEXT_HTML_8859_1.asString());
return BufferUtil.toBuffer("<h1>Bad Message " + status + "</h1><pre>reason: " + reason + "</pre>");
- }
-
+ }
+
/* ------------------------------------------------------------ */
- /** Get the cacheControl.
+ /**
* @return the cacheControl header to set on error responses.
*/
public String getCacheControl()
@@ -230,7 +240,7 @@ public class ErrorHandler extends AbstractHandler
}
/* ------------------------------------------------------------ */
- /** Set the cacheControl.
+ /**
* @param cacheControl the cacheControl header to set on error responses.
*/
public void setCacheControl(String cacheControl)
@@ -240,7 +250,7 @@ public class ErrorHandler extends AbstractHandler
/* ------------------------------------------------------------ */
/**
- * @return True if stack traces are shown in the error pages
+ * @return whether stack traces are shown in the error pages
*/
public boolean isShowStacks()
{
@@ -249,7 +259,7 @@ public class ErrorHandler extends AbstractHandler
/* ------------------------------------------------------------ */
/**
- * @param showStacks True if stack traces are shown in the error pages
+ * @param showStacks whether stack traces are shown in the error pages
*/
public void setShowStacks(boolean showStacks)
{
@@ -258,25 +268,27 @@ public class ErrorHandler extends AbstractHandler
/* ------------------------------------------------------------ */
/**
- * @param showMessageInTitle if true, the error message appears in page title
+ * @return whether the error message appears in page title
*/
- public void setShowMessageInTitle(boolean showMessageInTitle)
+ public boolean getShowMessageInTitle()
{
- _showMessageInTitle = showMessageInTitle;
+ return _showMessageInTitle;
}
-
/* ------------------------------------------------------------ */
- public boolean getShowMessageInTitle()
+ /**
+ * @param showMessageInTitle whether the error message appears in page title
+ */
+ public void setShowMessageInTitle(boolean showMessageInTitle)
{
- return _showMessageInTitle;
+ _showMessageInTitle = showMessageInTitle;
}
/* ------------------------------------------------------------ */
- protected void write(Writer writer,String string)
- throws IOException
+ protected void write(Writer writer, String string)
+ throws IOException
{
- if (string==null)
+ if (string == null)
return;
writer.write(StringUtil.sanitizeXmlString(string));
@@ -291,11 +303,22 @@ public class ErrorHandler extends AbstractHandler
/* ------------------------------------------------------------ */
public static ErrorHandler getErrorHandler(Server server, ContextHandler context)
{
- ErrorHandler error_handler=null;
- if (context!=null)
- error_handler=context.getErrorHandler();
- if (error_handler==null && server!=null)
- error_handler = server.getBean(ErrorHandler.class);
+ ErrorHandler error_handler = null;
+ if (context != null)
+ error_handler = context.getErrorHandler();
+ if (error_handler == null)
+ {
+ synchronized (ErrorHandler.class)
+ {
+ error_handler = server.getBean(ErrorHandler.class);
+ if (error_handler == null)
+ {
+ error_handler = new ErrorHandler();
+ error_handler.setServer(server);
+ server.addManaged(error_handler);
+ }
+ }
+ }
return error_handler;
}
}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerCollection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerCollection.java
index f799b3a4f8..2a1b771d6c 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerCollection.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerCollection.java
@@ -28,7 +28,6 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.ArrayUtil;
import org.eclipse.jetty.util.MultiException;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
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 c8ef17b61c..bc60dc7c05 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
@@ -25,10 +25,8 @@ 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.Handler;
import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.component.LifeCycle;
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java
index 535020f77b..9ebec26543 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java
@@ -144,9 +144,27 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
/* ------------------------------------------------------------ */
/**
+ * Add path to excluded paths list.
+ * <p>
+ * There are 2 syntaxes supported, Servlet <code>url-pattern</code> based, and
+ * Regex based. This means that the initial characters on the path spec
+ * line are very strict, and determine the behavior of the path matching.
+ * <ul>
+ * <li>If the spec starts with <code>'^'</code> the spec is assumed to be
+ * a regex based path spec and will match with normal Java regex rules.</li>
+ * <li>If the spec starts with <code>'/'</code> then spec is assumed to be
+ * a Servlet url-pattern rules path spec for either an exact match
+ * or prefix based match.</li>
+ * <li>If the spec starts with <code>'*.'</code> then spec is assumed to be
+ * a Servlet url-pattern rules path spec for a suffix based match.</li>
+ * <li>All other syntaxes are unsupported</li>
+ * </ul>
+ * <p>
+ * Note: inclusion takes precedence over exclude.
+ *
* @param pathspecs Path specs (as per servlet spec) to exclude. If a
* ServletContext is available, the paths are relative to the context path,
- * otherwise they are absolute.
+ * otherwise they are absolute.<br>
* For backward compatibility the pathspecs may be comma separated strings, but this
* will not be supported in future versions.
*/
@@ -191,12 +209,27 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
/* ------------------------------------------------------------ */
/**
- * Add path specs to include. Inclusion takes precedence over exclusion.
+ * Add path specs to include.
+ * <p>
+ * There are 2 syntaxes supported, Servlet <code>url-pattern</code> based, and
+ * Regex based. This means that the initial characters on the path spec
+ * line are very strict, and determine the behavior of the path matching.
+ * <ul>
+ * <li>If the spec starts with <code>'^'</code> the spec is assumed to be
+ * a regex based path spec and will match with normal Java regex rules.</li>
+ * <li>If the spec starts with <code>'/'</code> then spec is assumed to be
+ * a Servlet url-pattern rules path spec for either an exact match
+ * or prefix based match.</li>
+ * <li>If the spec starts with <code>'*.'</code> then spec is assumed to be
+ * a Servlet url-pattern rules path spec for a suffix based match.</li>
+ * <li>All other syntaxes are unsupported</li>
+ * </ul>
+ * <p>
+ * Note: inclusion takes precedence over exclude.
+ *
* @param pathspecs Path specs (as per servlet spec) to include. If a
* ServletContext is available, the paths are relative to the context path,
* otherwise they are absolute
- * For backward compatibility the pathspecs may be comma separated strings, but this
- * will not be supported in future versions.
*/
public void addIncludedPaths(String... pathspecs)
{
@@ -334,9 +367,9 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
/* ------------------------------------------------------------ */
/**
- * Get the minimum reponse size.
+ * Get the minimum response size.
*
- * @return minimum reponse size
+ * @return minimum response size
*/
public int getMinGzipSize()
{
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 7b86fbb95d..6962694507 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
@@ -535,6 +535,7 @@ public class JDBCSessionManager extends AbstractSessionManager
session.setLastNode(getSessionIdManager().getWorkerName());
_sessions.put(idInCluster, session);
+ _sessionsStats.increment();
//update in db
try
@@ -843,6 +844,7 @@ public class JDBCSessionManager extends AbstractSessionManager
//loaded an expired session last managed on this node for this context, add it to the list so we can
//treat it like a normal expired session
_sessions.put(session.getClusterId(), session);
+ _sessionsStats.increment();
}
else
{

Back to the top