diff options
Diffstat (limited to 'jetty-server/src/main/java/org')
38 files changed, 2249 insertions, 914 deletions
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 76064c4656..7fed43a9a1 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 @@ -68,15 +68,10 @@ public abstract class AbstractConnector extends HttpBuffers implements Connector private boolean _useDNS; private boolean _forwarded; private String _hostHeader; - private String _forwardedHostHeader = "X-Forwarded-Host"; // default to - // mod_proxy_http - // header - private String _forwardedServerHeader = "X-Forwarded-Server"; // default to - // mod_proxy_http - // header - private String _forwardedForHeader = "X-Forwarded-For"; // default to - // mod_proxy_http - // header + + private String _forwardedHostHeader = "X-Forwarded-Host"; + private String _forwardedServerHeader = "X-Forwarded-Server"; + private String _forwardedForHeader = "X-Forwarded-For"; private boolean _reuseAddress = true; protected int _maxIdleTime = 200000; @@ -87,101 +82,70 @@ public abstract class AbstractConnector extends HttpBuffers implements Connector private final AtomicLong _statsStartedAt = new AtomicLong(-1L); - private final CounterStatistic _connectionStats = new CounterStatistic(); // connections - // to - // server - private final SampleStatistic _requestStats = new SampleStatistic(); // requests - // per - // connection - private final SampleStatistic _connectionDurationStats = new SampleStatistic(); // duration - // of - // a - // connection + /** connections to server */ + private final CounterStatistic _connectionStats = new CounterStatistic(); + /** requests per connection */ + private final SampleStatistic _requestStats = new SampleStatistic(); + /** duration of a connection */ + private final SampleStatistic _connectionDurationStats = new SampleStatistic(); - /* - * -------------------------------------------------------------------------- - * ----- - */ + /* ------------------------------------------------------------ */ /** */ public AbstractConnector() { } - /* - * -------------------------------------------------------------------------- - * ----- - */ + /* ------------------------------------------------------------ */ public final Buffer newBuffer(int size) { // TODO remove once no overrides established return null; } - /* - * -------------------------------------------------------------------------- - * ----- - */ + /* ------------------------------------------------------------ */ @Override public Buffer newRequestBuffer(int size) { return new ByteArrayBuffer(size); } - /* - * -------------------------------------------------------------------------- - * ----- - */ + /* ------------------------------------------------------------ */ @Override public Buffer newRequestHeader(int size) { return new ByteArrayBuffer(size); } - /* - * -------------------------------------------------------------------------- - * ----- - */ + /* ------------------------------------------------------------ */ @Override public Buffer newResponseBuffer(int size) { return new ByteArrayBuffer(size); } - /* - * -------------------------------------------------------------------------- - * ----- - */ + /* ------------------------------------------------------------ */ @Override public Buffer newResponseHeader(int size) { return new ByteArrayBuffer(size); } - /* - * -------------------------------------------------------------------------- - * ----- - */ + /* ------------------------------------------------------------ */ @Override protected boolean isRequestHeader(Buffer buffer) { return true; } - /* - * -------------------------------------------------------------------------- - * ----- - */ + /* ------------------------------------------------------------ */ @Override protected boolean isResponseHeader(Buffer buffer) { return true; } - /* - * -------------------------------------------------------------------------- - * ----- - */ + /* ------------------------------------------------------------ */ /* */ public Server getServer() @@ -189,19 +153,13 @@ public abstract class AbstractConnector extends HttpBuffers implements Connector return _server; } - /* - * -------------------------------------------------------------------------- - * ----- - */ + /* ------------------------------------------------------------ */ public void setServer(Server server) { _server = server; } - /* - * -------------------------------------------------------------------------- - * ----- - */ + /* ------------------------------------------------------------ */ /* * @see org.eclipse.jetty.http.HttpListener#getHttpServer() */ @@ -210,19 +168,13 @@ public abstract class AbstractConnector extends HttpBuffers implements Connector return _threadPool; } - /* - * -------------------------------------------------------------------------- - * ----- - */ + /* ------------------------------------------------------------ */ public void setThreadPool(ThreadPool pool) { _threadPool = pool; } - /* - * -------------------------------------------------------------------------- - * ----- - */ + /* ------------------------------------------------------------ */ /** */ public void setHost(String host) @@ -230,10 +182,7 @@ public abstract class AbstractConnector extends HttpBuffers implements Connector _host = host; } - /* - * -------------------------------------------------------------------------- - * ----- - */ + /* ------------------------------------------------------------ */ /* */ public String getHost() @@ -241,10 +190,7 @@ public abstract class AbstractConnector extends HttpBuffers implements Connector return _host; } - /* - * -------------------------------------------------------------------------- - * ----- - */ + /* ------------------------------------------------------------ */ /* * @see org.eclipse.jetty.server.server.HttpListener#setPort(int) */ @@ -253,10 +199,7 @@ public abstract class AbstractConnector extends HttpBuffers implements Connector _port = port; } - /* - * -------------------------------------------------------------------------- - * ----- - */ + /* ------------------------------------------------------------ */ /* * @see org.eclipse.jetty.server.server.HttpListener#getPort() */ @@ -490,8 +433,6 @@ public abstract class AbstractConnector extends HttpBuffers implements Connector try { socket.setTcpNoDelay(true); - if (_maxIdleTime >= 0) - socket.setSoTimeout(_maxIdleTime); if (_soLingerTime >= 0) socket.setSoLinger(true,_soLingerTime / 1000); else @@ -777,7 +718,7 @@ public abstract class AbstractConnector extends HttpBuffers implements Connector /* ------------------------------------------------------------ */ /** - * @param forwardedHostHeader + * @param forwardedServerHeader * The header name for forwarded server (default * x-forwarded-server) */ @@ -794,12 +735,12 @@ public abstract class AbstractConnector extends HttpBuffers implements Connector /* ------------------------------------------------------------ */ /** - * @param forwardedHostHeader + * @param forwardedRemoteAddressHeader * The header name for forwarded for (default x-forwarded-for) */ - public void setForwardedForHeader(String forwardedRemoteAddressHeade) + public void setForwardedForHeader(String forwardedRemoteAddressHeader) { - _forwardedForHeader = forwardedRemoteAddressHeade; + _forwardedForHeader = forwardedRemoteAddressHeader; } /* ------------------------------------------------------------ */ diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContinuation.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContinuation.java index b4ead4ad3b..5aad13129a 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContinuation.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContinuation.java @@ -672,7 +672,7 @@ public class AsyncContinuation implements AsyncContext, Continuation { _expireAt = System.currentTimeMillis()+_timeoutMs; long wait=_timeoutMs; - while (_expireAt>0 && wait>0) + while (_expireAt>0 && wait>0 && _connection.getServer().isRunning()) { try { @@ -685,7 +685,7 @@ public class AsyncContinuation implements AsyncContext, Continuation wait=_expireAt-System.currentTimeMillis(); } - if (_expireAt>0 && wait<=0) + if (_expireAt>0 && wait<=0 && _connection.getServer().isRunning()) { expired(); } @@ -823,11 +823,19 @@ public class AsyncContinuation implements AsyncContext, Continuation } /* ------------------------------------------------------------ */ - public void start(Runnable run) + public void start(final Runnable run) { final AsyncEventState event=_event; if (event!=null) - ((Context)event.getServletContext()).getContextHandler().handle(run); + { + _connection.getServer().getThreadPool().dispatch(new Runnable() + { + public void run() + { + ((Context)event.getServletContext()).getContextHandler().handle(run); + } + }); + } } /* ------------------------------------------------------------ */ diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Connector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Connector.java index 5328a1a143..5a77634e0a 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Connector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Connector.java @@ -131,14 +131,14 @@ public interface Connector extends LifeCycle /* ------------------------------------------------------------ */ /** * @return The port to use when redirecting a request if a data constraint of integral is - * required. See {@link org.eclipse.jetty.server.server.security.Constraint#getDataConstraint()} + * required. See {@link org.eclipse.jetty.http.security.Constraint#getDataConstraint()} */ int getIntegralPort(); /* ------------------------------------------------------------ */ /** * @return The schema to use when redirecting a request if a data constraint of integral is - * required. See {@link org.eclipse.jetty.server.server.security.Constraint#getDataConstraint()} + * required. See {@link org.eclipse.jetty.http.security.Constraint#getDataConstraint()} */ String getIntegralScheme(); @@ -152,7 +152,7 @@ public interface Connector extends LifeCycle /* ------------------------------------------------------------ */ /** * @return The port to use when redirecting a request if a data constraint of confidential is - * required. See {@link org.eclipse.jetty.server.server.security.Constraint#getDataConstraint()} + * required. See {@link org.eclipse.jetty.http.security.Constraint#getDataConstraint()} */ int getConfidentialPort(); @@ -160,7 +160,7 @@ public interface Connector extends LifeCycle /* ------------------------------------------------------------ */ /** * @return The schema to use when redirecting a request if a data constraint of confidential is - * required. See {@link org.eclipse.jetty.server.server.security.Constraint#getDataConstraint()} + * required. See {@link org.eclipse.jetty.http.security.Constraint#getDataConstraint()} */ String getConfidentialScheme(); @@ -185,15 +185,23 @@ public interface Connector extends LifeCycle /** Persist an endpoint. * Called after every request if the connection is to remain open. * @param endpoint - * @param request * @throws IOException */ void persist(EndPoint endpoint) throws IOException; /* ------------------------------------------------------------ */ + /** + * @return The hostname representing the interface to which + * this connector will bind, or null for all interfaces. + */ String getHost(); /* ------------------------------------------------------------ */ + /** + * Set the hostname of the interface to bind to. + * @param hostname The hostname representing the interface to which + * this connector will bind, or null for all interfaces. + */ void setHost(String hostname); /* ------------------------------------------------------------ */ @@ -212,8 +220,8 @@ public interface Connector extends LifeCycle /* ------------------------------------------------------------ */ /** - * @return The actual port the connector is listening on or -1 if there - * is no port or the connector is not open. + * @return The actual port the connector is listening on or + * -1 if it has not been opened, or -2 if it has been closed. */ int getLocalPort(); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HandlerContainer.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HandlerContainer.java index a64c0f8d0b..32406ce3f4 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HandlerContainer.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HandlerContainer.java @@ -18,8 +18,8 @@ import org.eclipse.jetty.util.component.LifeCycle; /** * A Handler that contains other Handlers. * <p> - * The contained handlers may be one (see @{link {@link org.eclipse.jetty.server.server.handler.HandlerWrapper}) - * or many (see {@link org.eclipse.jetty.server.server.handler.HandlerList} or {@link org.eclipse.jetty.server.server.handler.HandlerCollection}. + * The contained handlers may be one (see @{link {@link org.eclipse.jetty.server.handler.HandlerWrapper}) + * or many (see {@link org.eclipse.jetty.server.handler.HandlerList} or {@link org.eclipse.jetty.server.handler.HandlerCollection}. * */ public interface HandlerContainer extends LifeCycle diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java index c85638b9cc..8c71df016c 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java @@ -16,7 +16,6 @@ package org.eclipse.jetty.server; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; - import javax.servlet.DispatcherType; import javax.servlet.ServletInputStream; import javax.servlet.ServletOutputStream; @@ -42,13 +41,12 @@ import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.http.Parser; import org.eclipse.jetty.io.AsyncEndPoint; import org.eclipse.jetty.io.Buffer; +import org.eclipse.jetty.io.BufferCache.CachedBuffer; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EofException; import org.eclipse.jetty.io.UncheckedIOException; import org.eclipse.jetty.io.UncheckedPrintWriter; -import org.eclipse.jetty.io.BufferCache.CachedBuffer; -import org.eclipse.jetty.io.nio.SelectChannelEndPoint; import org.eclipse.jetty.util.QuotedStringTokenizer; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.URIUtil; @@ -69,7 +67,7 @@ import org.eclipse.jetty.util.thread.Timeout; * with the connection via the parser and/or generator. * </p> * <p> - * The connection state is held by 3 separate state machines: The request state, the + * The connection state is held by 3 separate state machines: The request state, the * response state and the continuation state. All three state machines must be driven * to completion for every request, and all three can complete in any order. * </p> @@ -77,12 +75,12 @@ import org.eclipse.jetty.util.thread.Timeout; * The HttpConnection support protocol upgrade. If on completion of a request, the * response code is 101 (switch protocols), then the org.eclipse.jetty.io.Connection * request attribute is checked to see if there is a new Connection instance. If so, - * the new connection is returned from {@link #handle()} and is used for future + * the new connection is returned from {@link #handle()} and is used for future * handling of the underlying connection. Note that for switching protocols that * don't use 101 responses (eg CONNECT), the response should be sent and then the - * status code changed to 101 before returning from the handler. Implementors + * status code changed to 101 before returning from the handler. Implementors * of new Connection types should be careful to extract any buffered data from - * (HttpParser)http.getParser()).getHeaderBuffer() and + * (HttpParser)http.getParser()).getHeaderBuffer() and * (HttpParser)http.getParser()).getBodyBuffer() to initialise their new connection. * </p> * @@ -150,7 +148,7 @@ public class HttpConnection implements Connection HttpBuffers ab = (HttpBuffers)_connector; _parser = new HttpParser(ab.getRequestBuffers(), endpoint, new RequestHandler()); _requestFields = new HttpFields(); - _responseFields = new HttpFields(); + _responseFields = new HttpFields(server.getMaxCookieVersion()); _request = new Request(this); _response = new Response(this); _generator = new HttpGenerator(ab.getResponseBuffers(), _endp); @@ -167,7 +165,7 @@ public class HttpConnection implements Connection _endp = endpoint; _parser = parser; _requestFields = new HttpFields(); - _responseFields = new HttpFields(); + _responseFields = new HttpFields(server.getMaxCookieVersion()); _request = request; _response = new Response(this); _generator = generator; @@ -192,7 +190,13 @@ public class HttpConnection implements Connection { return _requests; } - + + /* ------------------------------------------------------------ */ + public Server getServer() + { + return _server; + } + /* ------------------------------------------------------------ */ /** * @return The time this connection was established. @@ -378,6 +382,8 @@ public class HttpConnection implements Connection /* ------------------------------------------------------------ */ public Connection handle() throws IOException { + Connection connection = this; + // Loop while more in buffer boolean more_in_buffer =true; // assume true until proven otherwise boolean progress=true; @@ -389,12 +395,15 @@ public class HttpConnection implements Connection _handling=true; setCurrentConnection(this); - while (more_in_buffer) + while (more_in_buffer && _endp.isOpen()) { try { if (_request._async.isAsync()) { + // TODO - handle the case of input being read for a + // suspended request. + Log.debug("async request",_request); if (!_request._async.isComplete()) handleRequest(); @@ -454,38 +463,44 @@ public class HttpConnection implements Connection _parser.reset(true); _endp.close(); - throw e; } finally { more_in_buffer = _parser.isMoreInBuffer() || _endp.isBufferingInput(); + // Is this request/response round complete? if (_parser.isComplete() && _generator.isComplete() && !_endp.isBufferingOutput()) { - if (_response.getStatus()==HttpStatus.SWITCHING_PROTOCOLS_101) + // look for a switched connection instance? + Connection switched=(_response.getStatus()==HttpStatus.SWITCHING_PROTOCOLS_101) + ?(Connection)_request.getAttribute("org.eclipse.jetty.io.Connection"):null; + + // have we switched? + if (switched!=null) { - Connection connection = (Connection)_request.getAttribute("org.eclipse.jetty.io.Connection"); - if (connection!=null) + _parser.reset(true); + _generator.reset(true); + connection=switched; + } + else + { + // No switch, so cleanup and reset + if (!_generator.isPersistent()) { _parser.reset(true); - return connection; + more_in_buffer=false; + _endp.close(); } - } - - if (!_generator.isPersistent()) - { - _parser.reset(true); - more_in_buffer=false; - } - if (more_in_buffer) - { - reset(false); - more_in_buffer = _parser.isMoreInBuffer() || _endp.isBufferingInput(); + if (more_in_buffer) + { + reset(false); + more_in_buffer = _parser.isMoreInBuffer() || _endp.isBufferingInput(); + } + else + reset(true); + progress=true; } - else - reset(true); - progress=true; } if (_request.isAsyncStarted()) @@ -493,7 +508,7 @@ public class HttpConnection implements Connection Log.debug("return with suspended request"); more_in_buffer=false; } - else if (_generator.isCommitted() && !_generator.isComplete() && _endp instanceof AsyncEndPoint) + else if (_generator.isCommitted() && !_generator.isComplete() && _endp instanceof AsyncEndPoint) ((AsyncEndPoint)_endp).setWritable(false); } } @@ -503,7 +518,7 @@ public class HttpConnection implements Connection setCurrentConnection(null); _handling=false; } - return this; + return connection; } /* ------------------------------------------------------------ */ @@ -567,7 +582,7 @@ public class HttpConnection implements Connection { _uri.getPort(); info=URIUtil.canonicalPath(_uri.getDecodedPath()); - if (info==null) + if (info==null && !_request.getMethod().equals(HttpMethods.CONNECT)) throw new HttpException(400); _request.setPathInfo(info); @@ -623,7 +638,6 @@ public class HttpConnection implements Connection Log.debug(e); _request.setHandled(true); _generator.sendError(info==null?400:500, null, null, true); - } finally { @@ -683,6 +697,10 @@ public class HttpConnection implements Connection _generator.setResponse(_response.getStatus(), _response.getReason()); try { + // If the client was expecting 100 continues, but we sent something + // else, then we need to close the connection + if (_expect100Continue && _response.getStatus()!=100) + _generator.setPersistent(false); _generator.completeHeader(_responseFields, last); } catch(IOException io) @@ -832,7 +850,20 @@ public class HttpConnection implements Connection try { - _uri.parse(uri.array(), uri.getIndex(), uri.length()); + switch (HttpMethods.CACHE.getOrdinal(method)) + { + case HttpMethods.CONNECT_ORDINAL: + _uri.parseConnect(uri.array(), uri.getIndex(), uri.length()); + break; + + case HttpMethods.HEAD_ORDINAL: + _head=true; + // fall through + + default: + _uri.parse(uri.array(), uri.getIndex(), uri.length()); + } + _request.setUri(_uri); if (version==null) @@ -847,8 +878,6 @@ public class HttpConnection implements Connection if (_version <= 0) _version = HttpVersions.HTTP_1_0_ORDINAL; _request.setProtocol(version.toString()); } - - _head = method == HttpMethods.HEAD_BUFFER; // depends on method being decached. } catch (Exception e) { @@ -968,6 +997,8 @@ public class HttpConnection implements Connection @Override public void headerComplete() throws IOException { + if (_endp instanceof AsyncEndPoint) + ((AsyncEndPoint)_endp).scheduleIdle(); _requests++; _generator.setVersion(_version); switch (_version) @@ -976,6 +1007,10 @@ public class HttpConnection implements Connection break; case HttpVersions.HTTP_1_0_ORDINAL: _generator.setHead(_head); + + if (_server.getSendDateHeader()) + _generator.setDate(_request.getTimeStampBuffer()); + break; case HttpVersions.HTTP_1_1_ORDINAL: _generator.setHead(_head); @@ -994,7 +1029,10 @@ public class HttpConnection implements Connection if (_expect) { - _generator.sendError(HttpStatus.EXPECTATION_FAILED_417, null, null, true); + _generator.setResponse(HttpStatus.EXPECTATION_FAILED_417, null); + _responseFields.put(HttpHeaders.CONNECTION_BUFFER, HttpHeaderValues.CLOSE_BUFFER); + _generator.completeHeader(_responseFields, true); + _generator.complete(); return; } @@ -1019,6 +1057,8 @@ public class HttpConnection implements Connection @Override public void content(Buffer ref) throws IOException { + if (_endp instanceof AsyncEndPoint) + ((AsyncEndPoint)_endp).scheduleIdle(); if (_delayedHandling) { _delayedHandling=false; @@ -1148,13 +1188,13 @@ public class HttpConnection implements Connection else { _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER, - contentType+";charset="+QuotedStringTokenizer.quote(enc,";= ")); + contentType+";charset="+QuotedStringTokenizer.quoteIfNeeded(enc,";= ")); } } else { _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER, - contentType+";charset="+QuotedStringTokenizer.quote(enc,";= ")); + contentType+";charset="+QuotedStringTokenizer.quoteIfNeeded(enc,";= ")); } } } 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 403c29d803..e7190a7b19 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 @@ -28,7 +28,7 @@ import org.eclipse.jetty.util.ByteArrayOutputStream2; /** Output. * * <p> - * Implements {@link javax.servlet.ServletOutputStream} from the {@link javax.servlet} package. + * Implements {@link javax.servlet.ServletOutputStream} from the <code>javax.servlet</code> package. * </p> * A {@link ServletOutputStream} implementation that writes content * to a {@link AbstractGenerator}. The class is designed to be reused 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 6385d2785a..30267c2a46 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 @@ -33,14 +33,6 @@ public class LocalConnector extends AbstractConnector return this; } - /** - * @deprecated Not needed anymore, as there is no need to reopen the connector to reset its state - */ - @Deprecated - public void reopen() - { - } - public String getResponses(String requests) throws Exception { return getResponses(requests, false); @@ -121,7 +113,7 @@ public class LocalConnector extends AbstractConnector boolean leaveOpen = keepOpen; try { - while (endPoint.getIn().length() > 0) + while (endPoint.getIn().length() > 0 && endPoint.isOpen()) { while (true) { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/NCSARequestLog.java b/jetty-server/src/main/java/org/eclipse/jetty/server/NCSARequestLog.java index 5a19545b10..f90e7818fc 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/NCSARequestLog.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/NCSARequestLog.java @@ -4,11 +4,11 @@ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. -// The Eclipse Public License is available at +// The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php -// You may elect to redistribute this code under either of these licenses. +// You may elect to redistribute this code under either of these licenses. // ======================================================================== package org.eclipse.jetty.server; @@ -20,7 +20,6 @@ import java.io.Writer; import java.util.ArrayList; import java.util.Locale; import java.util.TimeZone; - import javax.servlet.http.Cookie; import org.eclipse.jetty.http.HttpHeaders; @@ -28,7 +27,6 @@ import org.eclipse.jetty.http.PathMap; import org.eclipse.jetty.util.DateCache; import org.eclipse.jetty.util.RolloverFileOutputStream; import org.eclipse.jetty.util.StringUtil; -import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.Utf8StringBuilder; import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.eclipse.jetty.util.log.Log; @@ -40,12 +38,13 @@ import org.eclipse.jetty.util.log.Log; * Format (single log format). This log format can be output by most web * servers, and almost all web log analysis software can understand these * formats. - * - * - * - * + * * @org.apache.xbean.XBean element="ncsaLog" */ + +/* ------------------------------------------------------------ */ +/** + */ public class NCSARequestLog extends AbstractLifeCycle implements RequestLog { private String _filename; @@ -62,6 +61,7 @@ public class NCSARequestLog extends AbstractLifeCycle implements RequestLog private boolean _logLatency = false; private boolean _logCookies = false; private boolean _logServer = false; + private boolean _logDispatch = false; private transient OutputStream _out; private transient OutputStream _fileOut; @@ -71,6 +71,10 @@ public class NCSARequestLog extends AbstractLifeCycle implements RequestLog private transient ArrayList _buffers; private transient char[] _copy; + /* ------------------------------------------------------------ */ + /** + * Create request log object with default settings. + */ public NCSARequestLog() { _extended = true; @@ -80,9 +84,11 @@ public class NCSARequestLog extends AbstractLifeCycle implements RequestLog /* ------------------------------------------------------------ */ /** - * @param filename - * The filename for the request log. This may be in the - * format expected by {@link RolloverFileOutputStream} + * Create request log object with specified output file name. + * + * @param filename the file name for the request log. + * This may be in the format expected + * by {@link RolloverFileOutputStream} */ public NCSARequestLog(String filename) { @@ -94,9 +100,12 @@ public class NCSARequestLog extends AbstractLifeCycle implements RequestLog /* ------------------------------------------------------------ */ /** - * @param filename - * The filename for the request log. This may be in the - * format expected by {@link RolloverFileOutputStream} + * Set the output file name of the request log. + * The file name may be in the format expected by + * {@link RolloverFileOutputStream}. + * + * @param filename file name of the request log + * */ public void setFilename(String filename) { @@ -109,11 +118,25 @@ public class NCSARequestLog extends AbstractLifeCycle implements RequestLog _filename = filename; } + /* ------------------------------------------------------------ */ + /** + * Retrieve the output file name of the request log. + * + * @return file name of the request log + */ public String getFilename() { return _filename; } + /* ------------------------------------------------------------ */ + /** + * Retrieve the file name of the request log with the expanded + * date wildcard if the output is written to the disk using + * {@link RolloverFileOutputStream}. + * + * @return file name of the request log, or null if not applicable + */ public String getDatedFilename() { if (_fileOut instanceof RolloverFileOutputStream) @@ -123,116 +146,306 @@ public class NCSARequestLog extends AbstractLifeCycle implements RequestLog /* ------------------------------------------------------------ */ /** - * @param format - * Format for the timestamps in the log file. If not set, the - * pre-formated request timestamp is used. + * Set the timestamp format for request log entries in the file. + * If this is not set, the pre-formated request timestamp is used. + * + * @param format timestamp format string */ public void setLogDateFormat(String format) { _logDateFormat = format; } + /* ------------------------------------------------------------ */ + /** + * Retrieve the timestamp format string for request log entries. + * + * @return timestamp format string. + */ public String getLogDateFormat() { return _logDateFormat; } - + + /* ------------------------------------------------------------ */ + /** + * Set the locale of the request log. + * + * @param logLocale locale object + */ public void setLogLocale(Locale logLocale) { _logLocale = logLocale; } - + + /* ------------------------------------------------------------ */ + /** + * Retrieve the locale of the request log. + * + * @return locale object + */ public Locale getLogLocale() { return _logLocale; } + /* ------------------------------------------------------------ */ + /** + * Set the timezone of the request log. + * + * @param tz timezone string + */ public void setLogTimeZone(String tz) { _logTimeZone = tz; } + /* ------------------------------------------------------------ */ + /** + * Retrieve the timezone of the request log. + * + * @return timezone string + */ public String getLogTimeZone() { return _logTimeZone; } + /* ------------------------------------------------------------ */ + /** + * Set the number of days before rotated log files are deleted. + * + * @param retainDays number of days to keep a log file + */ public void setRetainDays(int retainDays) { _retainDays = retainDays; } + /* ------------------------------------------------------------ */ + /** + * Retrieve the number of days before rotated log files are deleted. + * + * @return number of days to keep a log file + */ public int getRetainDays() { return _retainDays; } + /* ------------------------------------------------------------ */ + /** + * Set the extended request log format flag. + * + * @param extended true - log the extended request information, + * false - do not log the extended request information + */ public void setExtended(boolean extended) { _extended = extended; } + /* ------------------------------------------------------------ */ + /** + * Retrieve the extended request log format flag. + * + * @return value of the flag + */ public boolean isExtended() { return _extended; } + /* ------------------------------------------------------------ */ + /** + * Set append to log flag. + * + * @param append true - request log file will be appended after restart, + * false - request log file will be overwritten after restart + */ public void setAppend(boolean append) { _append = append; } + /* ------------------------------------------------------------ */ + /** + * Retrieve append to log flag. + * + * @return value of the flag + */ public boolean isAppend() { return _append; } + /* ------------------------------------------------------------ */ + /** + * Set request paths that will not be logged. + * + * @param ignorePaths array of request paths + */ public void setIgnorePaths(String[] ignorePaths) { _ignorePaths = ignorePaths; } + /* ------------------------------------------------------------ */ + /** + * Retrieve the request paths that will not be logged. + * + * @return array of request paths + */ public String[] getIgnorePaths() { return _ignorePaths; } + /* ------------------------------------------------------------ */ + /** + * Controls logging of the request cookies. + * + * @param logCookies true - values of request cookies will be logged, + * false - values of request cookies will not be logged + */ public void setLogCookies(boolean logCookies) { _logCookies = logCookies; } + /* ------------------------------------------------------------ */ + /** + * Retrieve log cookies flag + * + * @return value of the flag + */ public boolean getLogCookies() { return _logCookies; } - public boolean getLogServer() + /* ------------------------------------------------------------ */ + /** + * Controls logging of the request hostname. + * + * @param logServer true - request hostname will be logged, + * false - request hostname will not be logged + */ + public void setLogServer(boolean logServer) { - return _logServer; + _logServer = logServer; } - public void setLogServer(boolean logServer) + /* ------------------------------------------------------------ */ + /** + * Retrieve log hostname flag. + * + * @return value of the flag + */ + public boolean getLogServer() { - _logServer = logServer; + return _logServer; } + /* ------------------------------------------------------------ */ + /** + * Controls logging of request processing time. + * + * @param logLatency true - request processing time will be logged + * false - request processing time will not be logged + */ public void setLogLatency(boolean logLatency) { _logLatency = logLatency; } + /* ------------------------------------------------------------ */ + /** + * Retrieve log request processing time flag. + * + * @return value of the flag + */ public boolean getLogLatency() { return _logLatency; } + /* ------------------------------------------------------------ */ + /** + * Controls whether the actual IP address of the connection or + * the IP address from the X-Forwarded-For header will be logged. + * + * @param preferProxiedForAddress true - IP address from header will be logged, + * false - IP address from the connection will be logged + */ public void setPreferProxiedForAddress(boolean preferProxiedForAddress) { _preferProxiedForAddress = preferProxiedForAddress; } + + /* ------------------------------------------------------------ */ + /** + * Retrieved log X-Forwarded-For IP address flag. + * + * @return value of the flag + */ + public boolean getPreferProxiedForAddress() + { + return _preferProxiedForAddress; + } + + /* ------------------------------------------------------------ */ + /** + * Set the log file name date format. + * @see RolloverFileOutputStream#RolloverFileOutputStream(String, boolean, int, TimeZone, String, String) + * + * @param logFileDateFormat format string that is passed to {@link RolloverFileOutputStream} + */ + public void setFilenameDateFormat(String logFileDateFormat) + { + _filenameDateFormat = logFileDateFormat; + } + + /* ------------------------------------------------------------ */ + /** + * Retrieve the file name date format string. + * + * @return the log File Date Format + */ + public String getFilenameDateFormat() + { + return _filenameDateFormat; + } + + /* ------------------------------------------------------------ */ + /** + * Controls logging of the request dispatch time + * + * @param value true - request dispatch time will be logged + * false - request dispatch time will not be logged + */ + public void setLogDispatch(boolean value) + { + _logDispatch = value; + } + + /* ------------------------------------------------------------ */ + /** + * Retrieve request dispatch time logging flag + * + * @return value of the flag + */ + public boolean isLogDispatch() + { + return _logDispatch; + } /* ------------------------------------------------------------ */ + /** + * Writes the request and response information to the output stream. + * + * @see org.eclipse.jetty.server.RequestLog#log(org.eclipse.jetty.server.Request, org.eclipse.jetty.server.Response) + */ public void log(Request request, Response response) { if (!isStarted()) @@ -254,7 +467,7 @@ public class NCSARequestLog extends AbstractLifeCycle implements RequestLog u8buf = size==0?new Utf8StringBuilder(160):(Utf8StringBuilder)_buffers.remove(size-1); buf = u8buf.getStringBuilder(); } - + if (_logServer) { buf.append(request.getServerName()); @@ -277,7 +490,7 @@ public class NCSARequestLog extends AbstractLifeCycle implements RequestLog buf.append(((Authentication.User)authentication).getUserIdentity().getUserPrincipal().getName()); else buf.append(" - "); - + buf.append(" ["); if (_logDateCache != null) buf.append(_logDateCache.format(request.getTimeStamp())); @@ -335,12 +548,12 @@ public class NCSARequestLog extends AbstractLifeCycle implements RequestLog buf.append(StringUtil.__LINE_SEPARATOR); int l=buf.length(); if (l>_copy.length) - l=_copy.length; - buf.getChars(0,l,_copy,0); + l=_copy.length; + buf.getChars(0,l,_copy,0); _writer.write(_copy,0,l); _writer.flush(); u8buf.reset(); - _buffers.add(u8buf); + _buffers.add(u8buf); } } else @@ -349,11 +562,11 @@ public class NCSARequestLog extends AbstractLifeCycle implements RequestLog { int l=buf.length(); if (l>_copy.length) - l=_copy.length; - buf.getChars(0,l,_copy,0); + l=_copy.length; + buf.getChars(0,l,_copy,0); _writer.write(_copy,0,l); u8buf.reset(); - _buffers.add(u8buf); + _buffers.add(u8buf); // TODO do outside synchronized scope if (_extended) @@ -362,13 +575,13 @@ public class NCSARequestLog extends AbstractLifeCycle implements RequestLog // TODO do outside synchronized scope if (_logCookies) { - Cookie[] cookies = request.getCookies(); + Cookie[] cookies = request.getCookies(); if (cookies == null || cookies.length == 0) _writer.write(" -"); else { _writer.write(" \""); - for (int i = 0; i < cookies.length; i++) + for (int i = 0; i < cookies.length; i++) { if (i != 0) _writer.write(';'); @@ -379,11 +592,20 @@ public class NCSARequestLog extends AbstractLifeCycle implements RequestLog _writer.write('\"'); } } + + final long now = System.currentTimeMillis(); + final long start = request.getTimeStamp(); + final long dispatch = request.getDispatchTime(); + if (_logDispatch) + { + _writer.write(' '); + _writer.write(Long.toString(now - (dispatch==0 ? start:dispatch))); + } if (_logLatency) { _writer.write(' '); - _writer.write(TypeUtil.toString(System.currentTimeMillis() - request.getTimeStamp())); + _writer.write(Long.toString(now - start)); } _writer.write(StringUtil.__LINE_SEPARATOR); @@ -399,9 +621,17 @@ public class NCSARequestLog extends AbstractLifeCycle implements RequestLog } /* ------------------------------------------------------------ */ - protected void logExtended(Request request, - Response response, - Writer writer) throws IOException + /** + * Writes extended request and response information to the output stream. + * + * @param request request object + * @param response response object + * @param writer log file writer + * @throws IOException + */ + protected void logExtended(Request request, + Response response, + Writer writer) throws IOException { String referer = request.getHeader(HttpHeaders.REFERER); if (referer == null) @@ -425,6 +655,11 @@ public class NCSARequestLog extends AbstractLifeCycle implements RequestLog } /* ------------------------------------------------------------ */ + /** + * Set up request logging and open log file. + * + * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart() + */ @Override protected void doStart() throws Exception { @@ -461,6 +696,11 @@ public class NCSARequestLog extends AbstractLifeCycle implements RequestLog } /* ------------------------------------------------------------ */ + /** + * Close the log file and perform cleanup. + * + * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop() + */ @Override protected void doStop() throws Exception { @@ -492,28 +732,4 @@ public class NCSARequestLog extends AbstractLifeCycle implements RequestLog _buffers = null; _copy = null; } - - /* ------------------------------------------------------------ */ - /** - * @return the log File Date Format - */ - public String getFilenameDateFormat() - { - return _filenameDateFormat; - } - - /* ------------------------------------------------------------ */ - /** - * Set the log file date format. - * - * @see {@link RolloverFileOutputStream#RolloverFileOutputStream(String, boolean, int, TimeZone, String, String)} - * @param logFileDateFormat - * the logFileDateFormat to pass to - * {@link RolloverFileOutputStream} - */ - public void setFilenameDateFormat(String logFileDateFormat) - { - _filenameDateFormat = logFileDateFormat; - } - } 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 6a06cc8ffa..a1c5599fc6 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 @@ -82,26 +82,26 @@ import org.eclipse.jetty.util.log.Log; /* ------------------------------------------------------------ */ /** Jetty Request. * <p> - * Implements {@link javax.servlet.http.HttpServletRequest} from the {@link javax.servlet.http} package. + * Implements {@link javax.servlet.http.HttpServletRequest} from the <code>javax.servlet.http</code> package. * </p> * <p> * The standard interface of mostly getters, * is extended with setters so that the request is mutable by the handlers that it is * passed to. This allows the request object to be as lightweight as possible and not - * actually implement any significant behaviour. For example<ul> + * actually implement any significant behavior. For example<ul> * - * <li>The {@link Request#getContextPath} method will return null, until the requeset has been - * passed to a {@link ContextHandler} which matches the {@link Request#getPathInfo} with a context - * path and calls {@link Request#setContextPath} as a result.</li> + * <li>The {@link Request#getContextPath()} method will return null, until the request has been + * passed to a {@link ContextHandler} which matches the {@link Request#getPathInfo()} with a context + * path and calls {@link Request#setContextPath(String)} as a result.</li> * * <li>the HTTP session methods * will all return null sessions until such time as a request has been passed to - * a {@link org.eclipse.jetty.servlet.SessionHandler} which checks for session cookies + * a {@link org.eclipse.jetty.server.session.SessionHandler} which checks for session cookies * and enables the ability to create new sessions.</li> * - * <li>The {@link Request#getServletPath} method will return null until the request has been - * passed to a {@link org.eclipse.jetty.servlet.ServletHandler} and the pathInfo matched - * against the servlet URL patterns and {@link Request#setServletPath} called as a result.</li> + * <li>The {@link Request#getServletPath()} method will return null until the request has been + * passed to a <code>org.eclipse.jetty.servlet.ServletHandler</code> and the pathInfo matched + * against the servlet URL patterns and {@link Request#setServletPath(String)} called as a result.</li> * </ul> * * A request instance is created for each {@link HttpConnection} accepted by the server @@ -166,7 +166,8 @@ public class Request implements HttpServletRequest private HttpSession _session; private SessionManager _sessionManager; private long _timeStamp; - + private long _dispatchTime; + private Buffer _timeStampBuffer; private HttpURI _uri; @@ -1225,6 +1226,16 @@ public class Request implements HttpServletRequest } /* ------------------------------------------------------------ */ + /** Get timestamp of the request dispatch + * + * @return timestamp + */ + public long getDispatchTime() + { + return _dispatchTime; + } + + /* ------------------------------------------------------------ */ public boolean isAsyncStarted() { return _async.isAsyncStarted(); @@ -1565,7 +1576,9 @@ public class Request implements HttpServletRequest /* ------------------------------------------------------------ */ /** - * @param context + * Set request context + * + * @param context context object */ public void setContext(Context context) { @@ -1575,7 +1588,8 @@ public class Request implements HttpServletRequest /* ------------------------------------------------------------ */ /** - * @return True if this is the first call of takeNewContext() since the last {@link #setContext(Context)} call. + * @return True if this is the first call of {@link #takeNewContext()} + * since the last {@link #setContext(org.eclipse.jetty.server.handler.ContextHandler.Context)} call. */ public boolean takeNewContext() { @@ -1587,7 +1601,7 @@ public class Request implements HttpServletRequest /* ------------------------------------------------------------ */ /** * Sets the "context path" for this request - * @see HttpServletRequest#getContextPath + * @see HttpServletRequest#getContextPath() */ public void setContextPath(String contextPath) { @@ -1801,6 +1815,16 @@ public class Request implements HttpServletRequest } /* ------------------------------------------------------------ */ + /** Set timetstamp of request dispatch + * + * @param value timestamp + */ + public void setDispatchTime(long value) + { + _dispatchTime = value; + } + + /* ------------------------------------------------------------ */ public AsyncContext startAsync() throws IllegalStateException { if (!_asyncSupported) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/RequestLog.java b/jetty-server/src/main/java/org/eclipse/jetty/server/RequestLog.java index 2e95bbc0e0..a2b7a9b36c 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/RequestLog.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/RequestLog.java @@ -16,9 +16,8 @@ package org.eclipse.jetty.server; import org.eclipse.jetty.util.component.LifeCycle; /** - * A <code>RequestLog</code> can be attached to a {@link org.eclipse.jetty.server.server.handler.RequestLogHandler} to enable logging of requests/responses. - * - * @see Server#setRequestLog + * A <code>RequestLog</code> can be attached to a {@link org.eclipse.jetty.server.handler.RequestLogHandler} to enable + * logging of requests/responses. */ public interface RequestLog extends LifeCycle { 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 0230f90665..37ed17efca 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 @@ -17,6 +17,8 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.Collection; import java.util.Collections; +import java.util.List; +import java.util.List; import java.util.Locale; import javax.servlet.ServletOutputStream; @@ -29,6 +31,7 @@ import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpGenerator; import org.eclipse.jetty.http.HttpHeaderValues; import org.eclipse.jetty.http.HttpHeaders; +import org.eclipse.jetty.http.HttpSchemes; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpURI; import org.eclipse.jetty.http.HttpVersions; @@ -43,14 +46,10 @@ import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.log.Log; -/* ------------------------------------------------------------ */ /** Response. * <p> - * Implements {@link javax.servlet.HttpServletResponse} from the {@link javax.servlet} package. + * Implements {@link javax.servlet.http.HttpServletResponse} from the <code>javax.servlet.http</code> package. * </p> - * - * - * */ public class Response implements HttpServletResponse { @@ -156,16 +155,30 @@ public class Response implements HttpServletResponse */ public String encodeURL(String url) { - Request request=_connection.getRequest(); + final Request request=_connection.getRequest(); SessionManager sessionManager = request.getSessionManager(); if (sessionManager==null) return url; + + if (sessionManager.isCheckingRemoteSessionIdEncoding() && URIUtil.hasScheme(url)) + { + HttpURI uri = new HttpURI(url); + int port=uri.getPort(); + if (port<0) + port = HttpSchemes.HTTPS.equalsIgnoreCase(uri.getScheme())?443:80; + if (!request.getServerName().equalsIgnoreCase(uri.getHost()) || + request.getServerPort()!=port || + !uri.getPath().startsWith(request.getContextPath())) + return url; + } + String sessionURLPrefix = sessionManager.getSessionIdPathParameterNamePrefix(); if (sessionURLPrefix==null) return url; if (url==null) return null; + // should not encode if cookies in evidence if (request.isRequestedSessionIdFromCookie()) { @@ -190,15 +203,12 @@ public class Response implements HttpServletResponse if (session == null) return url; - // invalid session if (!sessionManager.isValid(session)) return url; String id=sessionManager.getNodeId(session); - - // TODO Check host and port are for this server // Already encoded int prefix=url.indexOf(sessionURLPrefix); if (prefix!=-1) @@ -224,7 +234,7 @@ public class Response implements HttpServletResponse } /* ------------------------------------------------------------ */ - /* + /** * @see javax.servlet.http.HttpServletResponse#encodeRedirectURL(java.lang.String) */ public String encodeRedirectURL(String url) @@ -233,21 +243,17 @@ public class Response implements HttpServletResponse } /* ------------------------------------------------------------ */ - /* - * @see javax.servlet.http.HttpServletResponse#encodeUrl(java.lang.String) - */ + @Deprecated public String encodeUrl(String url) { return encodeURL(url); } /* ------------------------------------------------------------ */ - /* - * @see javax.servlet.http.HttpServletResponse#encodeRedirectUrl(java.lang.String) - */ + @Deprecated public String encodeRedirectUrl(String url) { - return encodeURL(url); + return encodeRedirectURL(url); } /* ------------------------------------------------------------ */ @@ -288,14 +294,14 @@ public class Response implements HttpServletResponse ContextHandler.Context context = request.getContext(); if (context!=null) error_handler=context.getContextHandler().getErrorHandler(); + if (error_handler==null) + error_handler = _connection.getConnector().getServer().getBean(ErrorHandler.class); if (error_handler!=null) { - // TODO - probably should reset these after the request? request.setAttribute(Dispatcher.ERROR_STATUS_CODE,new Integer(code)); request.setAttribute(Dispatcher.ERROR_MESSAGE, message); request.setAttribute(Dispatcher.ERROR_REQUEST_URI, request.getRequestURI()); request.setAttribute(Dispatcher.ERROR_SERVLET_NAME,request.getServletName()); - error_handler.handle(null,_connection.getRequest(),_connection.getRequest(),this ); } else @@ -715,7 +721,7 @@ public class Response implements HttpServletResponse if (_contentType==null) { - _contentType = _mimeType+";charset="+QuotedStringTokenizer.quote(_characterEncoding,";= "); + _contentType = _mimeType+";charset="+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= "); _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); } } @@ -724,16 +730,16 @@ public class Response implements HttpServletResponse int i1=_contentType.indexOf("charset=",i0); if (i1<0) { - _contentType = _contentType+";charset="+QuotedStringTokenizer.quote(_characterEncoding,";= "); + _contentType = _contentType+";charset="+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= "); } else { int i8=i1+8; int i2=_contentType.indexOf(" ",i8); if (i2<0) - _contentType=_contentType.substring(0,i8)+QuotedStringTokenizer.quote(_characterEncoding,";= "); + _contentType=_contentType.substring(0,i8)+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= "); else - _contentType=_contentType.substring(0,i8)+QuotedStringTokenizer.quote(_characterEncoding,";= ")+_contentType.substring(i2); + _contentType=_contentType.substring(0,i8)+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= ")+_contentType.substring(i2); } _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); } @@ -861,12 +867,12 @@ public class Response implements HttpServletResponse } else if (i2<0) { - _contentType=contentType.substring(0,i1)+";charset="+QuotedStringTokenizer.quote(_characterEncoding,";= "); + _contentType=contentType.substring(0,i1)+";charset="+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= "); _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); } else { - _contentType=contentType.substring(0,i1)+contentType.substring(i2)+";charset="+QuotedStringTokenizer.quote(_characterEncoding,";= "); + _contentType=contentType.substring(0,i1)+contentType.substring(i2)+";charset="+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= "); _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); } } @@ -912,7 +918,7 @@ public class Response implements HttpServletResponse else // No encoding in the params. { _cachedMimeType=null; - _contentType=_characterEncoding==null?contentType:contentType+";charset="+QuotedStringTokenizer.quote(_characterEncoding,";= "); + _contentType=_characterEncoding==null?contentType:contentType+";charset="+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= "); _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); } } @@ -933,13 +939,13 @@ public class Response implements HttpServletResponse } else { - _contentType=_mimeType+";charset="+QuotedStringTokenizer.quote(_characterEncoding,";= "); + _contentType=_mimeType+";charset="+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= "); _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); } } else { - _contentType=contentType+";charset="+QuotedStringTokenizer.quote(_characterEncoding,";= "); + _contentType=contentType+";charset="+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= "); _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java index 297e81f538..9ab0a6151d 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java @@ -14,15 +14,12 @@ package org.eclipse.jetty.server; import java.io.IOException; -import java.lang.reflect.Method; +import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import java.util.ListIterator; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArraySet; - import javax.servlet.AsyncContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -30,7 +27,6 @@ import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.http.HttpGenerator; import org.eclipse.jetty.http.HttpURI; -import org.eclipse.jetty.server.bio.SocketConnector; import org.eclipse.jetty.server.handler.HandlerWrapper; import org.eclipse.jetty.server.nio.SelectChannelConnector; import org.eclipse.jetty.util.Attributes; @@ -74,7 +70,9 @@ public class Server extends HandlerWrapper implements Attributes private boolean _sendDateHeader = false; //send Date: header private int _graceful=0; private boolean _stopAtShutdown; + private int _maxCookieVersion=1; + /* ------------------------------------------------------------ */ public Server() { @@ -83,7 +81,7 @@ public class Server extends HandlerWrapper implements Attributes /* ------------------------------------------------------------ */ /** Convenience constructor - * Creates server and a {@link SocketConnector} at the passed port. + * Creates server and a {@link SelectChannelConnector} at the passed port. */ public Server(int port) { @@ -93,6 +91,20 @@ public class Server extends HandlerWrapper implements Attributes connector.setPort(port); setConnectors(new Connector[]{connector}); } + + /* ------------------------------------------------------------ */ + /** Convenience constructor + * Creates server and a {@link SelectChannelConnector} at the passed address. + */ + public Server(InetSocketAddress addr) + { + setServer(this); + + Connector connector=new SelectChannelConnector(); + connector.setHost(addr.getHostName()); + connector.setPort(addr.getPort()); + setConnectors(new Connector[]{connector}); + } /* ------------------------------------------------------------ */ @@ -198,7 +210,13 @@ public class Server extends HandlerWrapper implements Attributes HttpGenerator.setServerVersion(_version); MultiException mex=new MultiException(); - Iterator itor = _dependentBeans.iterator(); + if (_threadPool==null) + { + QueuedThreadPool tp=new QueuedThreadPool(); + setThreadPool(tp); + } + + Iterator<Object> itor = _dependentBeans.iterator(); while (itor.hasNext()) { try @@ -210,12 +228,6 @@ public class Server extends HandlerWrapper implements Attributes catch (Throwable e) {mex.add(e);} } - if (_threadPool==null) - { - QueuedThreadPool tp=new QueuedThreadPool(); - setThreadPool(tp); - } - if (_sessionIdManager!=null) _sessionIdManager.start(); @@ -299,7 +311,7 @@ public class Server extends HandlerWrapper implements Attributes if (!_dependentBeans.isEmpty()) { - ListIterator itor = _dependentBeans.listIterator(_dependentBeans.size()); + ListIterator<Object> itor = _dependentBeans.listIterator(_dependentBeans.size()); while (itor.hasPrevious()) { try @@ -313,7 +325,9 @@ public class Server extends HandlerWrapper implements Attributes } mex.ifExceptionThrow(); - ShutdownThread.deregister(this); + + if (getStopAtShutdown()) + ShutdownThread.deregister(this); } /* ------------------------------------------------------------ */ @@ -436,13 +450,30 @@ public class Server extends HandlerWrapper implements Attributes { return _sendDateHeader; } - + + /* ------------------------------------------------------------ */ + /** Get the maximum cookie version. + * @return the maximum set-cookie version sent by this server + */ + public int getMaxCookieVersion() + { + return _maxCookieVersion; + } + + /* ------------------------------------------------------------ */ + /** Set the maximum cookie version. + * @param maxCookieVersion the maximum set-cookie version sent by this server + */ + public void setMaxCookieVersion(int maxCookieVersion) + { + _maxCookieVersion = maxCookieVersion; + } /* ------------------------------------------------------------ */ /** * Add a LifeCycle object to be started/stopped * along with the Server. - * @deprecated Use {@link #addBean(LifeCycle)} + * @deprecated Use {@link #addBean(Object)} * @param c */ @Deprecated @@ -457,7 +488,7 @@ public class Server extends HandlerWrapper implements Attributes * The bean will be added to the servers {@link Container} * and if it is a {@link LifeCycle} instance, it will be * started/stopped along with the Server. - * @param c + * @param o the bean object to add */ public void addBean(Object o) { @@ -500,6 +531,34 @@ public class Server extends HandlerWrapper implements Attributes return beans; } + /* ------------------------------------------------------------ */ + /** Get dependent bean of a specific class. + * If more than one bean of the type exist, the first is returned. + * @see #addBean(Object) + * @param clazz + * @return bean or null + */ + public <T> T getBean(Class<T> clazz) + { + Iterator<?> iter = _dependentBeans.iterator(); + T t=null; + int count=0; + while (iter.hasNext()) + { + Object o = iter.next(); + if (clazz.isInstance(o)) + { + count++; + if (t==null) + t=(T)o; + } + } + if (count>1) + Log.debug("getBean({}) 1 of {}",clazz.getName(),count); + + return t; + } + /** * Remove a LifeCycle object to be started/stopped * along with the Server @@ -578,7 +637,7 @@ public class Server extends HandlerWrapper implements Attributes /* ------------------------------------------------------------ */ /** - * Set graceful shutdown timeout. If set, the {@link #doStop()} method will not immediately stop the + * Set graceful shutdown timeout. If set, the internal <code>doStop()</code> method will not immediately stop the * server. Instead, all {@link Connector}s will be closed so that new connections will not be accepted * and all handlers that implement {@link Graceful} will be put into the shutdown mode so that no new requests * will be accepted, but existing requests can complete. The server will then wait the configured timeout diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/SessionIdManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/SessionIdManager.java index 1d76581822..cca2783673 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/SessionIdManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/SessionIdManager.java @@ -20,13 +20,6 @@ import org.eclipse.jetty.util.component.LifeCycle; /** Session ID Manager. * Manages session IDs across multiple contexts. - * - * - */ -/* ------------------------------------------------------------ */ -/** - * - * */ public interface SessionIdManager extends LifeCycle { @@ -57,7 +50,7 @@ public interface SessionIdManager extends LifeCycle /** * @param request * @param created - * @return + * @return the new session id */ public String newSessionId(HttpServletRequest request,long created); @@ -68,7 +61,7 @@ public interface SessionIdManager extends LifeCycle /** Get a cluster ID from a node ID. * Strip node identifier from a located session ID. * @param nodeId - * @return + * @return the cluster id */ public String getClusterId(String nodeId); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/SessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/SessionManager.java index 3d11dd554a..7d8ea58284 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/SessionManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/SessionManager.java @@ -30,14 +30,15 @@ import org.eclipse.jetty.util.component.LifeCycle; /** * Session Manager. * The API required to manage sessions for a servlet context. + * */ public interface SessionManager extends LifeCycle { /* ------------------------------------------------------------ */ /** * Session cookie name. - * Defaults to JSESSIONID, but can be set with the - * org.eclipse.jetty.servlet.SessionCookie context init parameter. + * Defaults to <code>JSESSIONID</code>, but can be set with the + * <code>org.eclipse.jetty.servlet.SessionCookie</code> context init parameter. */ public final static String __SessionCookieProperty = "org.eclipse.jetty.servlet.SessionCookie"; public final static String __DefaultSessionCookie = "JSESSIONID"; @@ -46,12 +47,13 @@ public interface SessionManager extends LifeCycle /* ------------------------------------------------------------ */ /** * Session id path parameter name. - * Defaults to jsessionid, but can be set with the - * org.eclipse.jetty.servlet.SessionIdPathParameterName context init parameter. + * Defaults to <code>jsessionid</code>, but can be set with the + * <code>org.eclipse.jetty.servlet.SessionIdPathParameterName</code> context init parameter. * If set to null or "none" no URL rewriting will be done. */ public final static String __SessionIdPathParameterNameProperty = "org.eclipse.jetty.servlet.SessionIdPathParameterName"; public final static String __DefaultSessionIdPathParameterName = "jsessionid"; + public final static String __CheckRemoteSessionEncoding = "org.eclipse.jetty.servlet.CheckingRemoteSessionIdEncoding"; /* ------------------------------------------------------------ */ @@ -101,6 +103,14 @@ public interface SessionManager extends LifeCycle */ public HttpSession newHttpSession(HttpServletRequest request); + + /* ------------------------------------------------------------ */ + /** + * @return true if session cookies should be HTTP-only (Microsoft extension) + * @see org.eclipse.jetty.http.HttpCookie#isHttpOnly() + */ + public boolean getHttpOnly(); + /* ------------------------------------------------------------ */ /** * @return the max period of inactivity, after which the session is invalidated, in seconds. @@ -267,4 +277,15 @@ public interface SessionManager extends LifeCycle public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes); public SessionCookieConfig getSessionCookieConfig(); + + /** + * @return True if absolute URLs are check for remoteness before being session encoded. + */ + public boolean isCheckingRemoteSessionIdEncoding(); + + /** + * @param remote True if absolute URLs are check for remoteness before being session encoded. + */ + public void setCheckingRemoteSessionIdEncoding(boolean remote); + } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/UserIdentity.java b/jetty-server/src/main/java/org/eclipse/jetty/server/UserIdentity.java index 0c849bd593..8422e85dc3 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/UserIdentity.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/UserIdentity.java @@ -51,14 +51,11 @@ public interface UserIdentity /* ------------------------------------------------------------ */ - /* ------------------------------------------------------------ */ - /* ------------------------------------------------------------ */ /** * A UserIdentity Scope. * A scope is the environment in which a User Identity is to * be interpreted. Typically it is set by the target servlet of * a request. - * @see org.eclipse.jetty.servlet.ServletHolder */ interface Scope { @@ -83,15 +80,11 @@ public interface UserIdentity } /* ------------------------------------------------------------ */ - /* ------------------------------------------------------------ */ - /* ------------------------------------------------------------ */ public interface UnauthenticatedUserIdentity extends UserIdentity { } /* ------------------------------------------------------------ */ - /* ------------------------------------------------------------ */ - /* ------------------------------------------------------------ */ public static final UserIdentity UNAUTHENTICATED_IDENTITY = new UnauthenticatedUserIdentity() { public Subject getSubject() diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/bio/SocketConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/bio/SocketConnector.java index 5a39e71f38..e3104dbb8d 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/bio/SocketConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/bio/SocketConnector.java @@ -4,13 +4,13 @@ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. -// The Eclipse Public License is available at +// The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php -// You may elect to redistribute this code under either of these licenses. +// You may elect to redistribute this code under either of these licenses. // ======================================================================== - + package org.eclipse.jetty.server.bio; import java.io.IOException; @@ -39,21 +39,22 @@ import org.eclipse.jetty.util.log.Log; * This connector implements a traditional blocking IO and threading model. * Normal JRE sockets are used and a thread is allocated per connection. * Buffers are managed so that large buffers are only allocated to active connections. - * + * * This Connector should only be used if NIO is not available. - * + * * @org.apache.xbean.XBean element="bioConnector" description="Creates a BIO based socket connector" - * - * + * + * */ public class SocketConnector extends AbstractConnector { protected ServerSocket _serverSocket; protected final Set<EndPoint> _connections; - + protected volatile int _localPort=-1; + /* ------------------------------------------------------------ */ /** Constructor. - * + * */ public SocketConnector() { @@ -65,7 +66,7 @@ public class SocketConnector extends AbstractConnector { return _serverSocket; } - + /* ------------------------------------------------------------ */ public void open() throws IOException { @@ -73,6 +74,10 @@ public class SocketConnector extends AbstractConnector if (_serverSocket==null || _serverSocket.isClosed()) _serverSocket= newServerSocket(getHost(),getPort(),getAcceptQueueSize()); _serverSocket.setReuseAddress(getReuseAddress()); + _localPort=_serverSocket.getLocalPort(); + if (_localPort<=0) + throw new IllegalStateException("port not allocated for "+this); + } /* ------------------------------------------------------------ */ @@ -81,26 +86,27 @@ public class SocketConnector extends AbstractConnector ServerSocket ss= host==null? new ServerSocket(port,backlog): new ServerSocket(port,backlog,InetAddress.getByName(host)); - + return ss; } - + /* ------------------------------------------------------------ */ public void close() throws IOException { if (_serverSocket!=null) _serverSocket.close(); _serverSocket=null; + _localPort=-2; } /* ------------------------------------------------------------ */ @Override public void accept(int acceptorID) throws IOException, InterruptedException - { + { Socket socket = _serverSocket.accept(); configure(socket); - + ConnectorEndPoint connection=new ConnectorEndPoint(socket); connection.dispatch(); } @@ -109,7 +115,7 @@ public class SocketConnector extends AbstractConnector /** * Allows subclass to override Conection if required. */ - protected HttpConnection newHttpConnection(EndPoint endpoint) + protected Connection newConnection(EndPoint endpoint) { return new HttpConnection(this, endpoint, getServer()); } @@ -121,21 +127,15 @@ public class SocketConnector extends AbstractConnector { ConnectorEndPoint connection = (ConnectorEndPoint)endpoint; int lrmit = isLowResources()?_lowResourceMaxIdleTime:_maxIdleTime; - if (connection._sotimeout!=lrmit) - { - connection._sotimeout=lrmit; - ((Socket)endpoint.getTransport()).setSoTimeout(lrmit); - } - + connection.setMaxIdleTime(lrmit); + super.customize(endpoint, request); } /* ------------------------------------------------------------------------------- */ public int getLocalPort() { - if (_serverSocket==null || _serverSocket.isClosed()) - return -1; - return _serverSocket.getLocalPort(); + return _localPort; } /* ------------------------------------------------------------------------------- */ @@ -157,7 +157,7 @@ public class SocketConnector extends AbstractConnector { set= new HashSet(_connections); } - + Iterator iter=set.iterator(); while(iter.hasNext()) { @@ -173,14 +173,12 @@ public class SocketConnector extends AbstractConnector { boolean _dispatched=false; volatile Connection _connection; - int _sotimeout; protected final Socket _socket; - + public ConnectorEndPoint(Socket socket) throws IOException { - super(socket); - _connection = newHttpConnection(this); - _sotimeout=socket.getSoTimeout(); + super(socket,_maxIdleTime); + _connection = newConnection(this); _socket=socket; } @@ -195,7 +193,7 @@ public class SocketConnector extends AbstractConnector connectionUpgraded(_connection,connection); _connection=connection; } - + public void dispatch() throws IOException { if (getThreadPool()==null || !getThreadPool().dispatch(this)) @@ -204,7 +202,7 @@ public class SocketConnector extends AbstractConnector close(); } } - + @Override public int fill(Buffer buffer) throws IOException { @@ -212,8 +210,8 @@ public class SocketConnector extends AbstractConnector if (l<0) close(); return l; - } - + } + @Override public void close() throws IOException { @@ -231,21 +229,14 @@ public class SocketConnector extends AbstractConnector { _connections.add(this); } - + while (isStarted() && !isClosed()) { if (_connection.isIdle()) { if (isLowResources()) - { - int lrmit = getLowResourcesMaxIdleTime(); - if (lrmit>=0 && _sotimeout!= lrmit) - { - _sotimeout=lrmit; - _socket.setSoTimeout(_sotimeout); - } - } - } + setMaxIdleTime(getLowResourcesMaxIdleTime()); + } _connection=_connection.handle(); } @@ -269,7 +260,7 @@ public class SocketConnector extends AbstractConnector catch(IOException e2){Log.ignore(e2);} } finally - { + { connectionClosed(_connection); synchronized(_connections) { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AbstractHandlerContainer.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AbstractHandlerContainer.java index fbaa3513d2..91a65a19b1 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AbstractHandlerContainer.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AbstractHandlerContainer.java @@ -94,6 +94,8 @@ public abstract class AbstractHandlerContainer extends AbstractHandler implement int last=handlers.length-1; for (int h=0;h<=last;h++) { + if (handlers[h]==null) + continue; b.append(indent); b.append(" +-"); if (handlers[h] instanceof AbstractHandler) 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 4938021beb..114a5076b3 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 @@ -4,11 +4,11 @@ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. -// The Eclipse Public License is available at +// The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php -// You may elect to redistribute this code under either of these licenses. +// You may elect to redistribute this code under either of these licenses. // ======================================================================== package org.eclipse.jetty.server.handler; @@ -73,31 +73,35 @@ import org.eclipse.jetty.util.resource.Resource; /* ------------------------------------------------------------ */ /** ContextHandler. - * + * * This handler wraps a call to handle by setting the context and * servlet path, plus setting the context classloader. - * + * * <p> * If the context init parameter "org.eclipse.jetty.server.context.ManagedAttributes" * is set to a comma separated list of names, then they are treated as context * attribute names, which if set as attributes are passed to the servers Container * so that they may be managed with JMX. - * - * @org.apache.xbean.XBean description="Creates a basic HTTP context" - * - * * + * @org.apache.xbean.XBean description="Creates a basic HTTP context" */ public class ContextHandler extends ScopedHandler implements Attributes, Server.Graceful { private static final ThreadLocal<Context> __context=new ThreadLocal<Context>(); - public static final String MANAGED_ATTRIBUTES = "org.eclipse.jetty.server.context.ManagedAttributes"; + /** + * If a context attribute with this name is set, it is interpreted as a + * comma separated list of attribute name. Any other context attributes that + * are set with a name from this list will result in a call to {@link #setManagedAttribute(String, Object)}, + * which typically initiates the creation of a JMX MBean for the attribute value. + */ + public static final String MANAGED_ATTRIBUTES = "org.eclipse.jetty.server.context.ManagedAttributes"; + /* ------------------------------------------------------------ */ /** Get the current ServletContext implementation. * This call is only valid during a call to doStart and is available to * nested handlers to access the context. - * + * * @return ServletContext implementation */ public static Context getCurrentContext() @@ -106,14 +110,14 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } protected Context _scontext; - - private AttributesMap _attributes; - private AttributesMap _contextAttributes; + + private final AttributesMap _attributes; + private final AttributesMap _contextAttributes; + private final Map<String,String> _initParams; private ClassLoader _classLoader; private String _contextPath="/"; - private Map<String,String> _initParams; private String _displayName; - private Resource _baseResource; + private Resource _baseResource; private MimeTypes _mimeTypes; private Map<String,String> _localeEncodingMap; private String[] _welcomeFiles; @@ -134,49 +138,51 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. private Map<String,Object> _managedAttributes; private boolean _shutdown=false; - private boolean _available=true; + private boolean _available=true; private volatile int _availability; // 0=STOPPED, 1=AVAILABLE, 2=SHUTDOWN, 3=UNAVAILABLE - + private final static int __STOPPED=0,__AVAILABLE=1,__SHUTDOWN=2,__UNAVAILABLE=3; - - + + /* ------------------------------------------------------------ */ /** - * + * */ public ContextHandler() { super(); _scontext=new Context(); _attributes=new AttributesMap(); + _contextAttributes=new AttributesMap(); _initParams=new HashMap<String,String>(); } - + /* ------------------------------------------------------------ */ /** - * + * */ protected ContextHandler(Context context) { super(); _scontext=context; _attributes=new AttributesMap(); + _contextAttributes=new AttributesMap(); _initParams=new HashMap<String,String>(); } - + /* ------------------------------------------------------------ */ /** - * + * */ public ContextHandler(String contextPath) { this(); setContextPath(contextPath); } - + /* ------------------------------------------------------------ */ /** - * + * */ public ContextHandler(HandlerContainer parent, String contextPath) { @@ -193,7 +199,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. { return _scontext; } - + /* ------------------------------------------------------------ */ /** * @return the allowNullPathInfo true if /context is not redirected to /context/ @@ -221,13 +227,13 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. Server old_server=getServer(); if (old_server!=null && old_server!=server) old_server.getContainer().update(this, _errorHandler, null, "error",true); - super.setServer(server); + super.setServer(server); if (server!=null && server!=old_server) server.getContainer().update(this, null, _errorHandler, "error",true); - _errorHandler.setServer(server); + _errorHandler.setServer(server); } else - super.setServer(server); + super.setServer(server); } /* ------------------------------------------------------------ */ @@ -247,8 +253,8 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. if ( vhosts == null ) { _vhosts = vhosts; - } - else + } + else { _vhosts = new String[vhosts.length]; for ( int i = 0; i < vhosts.length; i++ ) @@ -274,8 +280,8 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } /* ------------------------------------------------------------ */ - /** - * @deprecated use {@link #setConnectorNames(String[])} + /** + * @deprecated use {@link #setConnectorNames(String[])} */ @Deprecated public void setHosts(String[] hosts) @@ -302,15 +308,15 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. { if (_connectors==null || _connectors.size()==0) return null; - + return _connectors.toArray(new String[_connectors.size()]); } /* ------------------------------------------------------------ */ /** Set the names of accepted connectors. - * + * * Names are either "host:port" or a specific configured name for a connector. - * + * * @param connectors If non null, an array of connector names that this context * will accept a request from. */ @@ -321,9 +327,9 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. else _connectors= new HashSet<String>(Arrays.asList(connectors)); } - + /* ------------------------------------------------------------ */ - /* + /* * @see javax.servlet.ServletContext#getAttribute(java.lang.String) */ public Object getAttribute(String name) @@ -332,7 +338,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } /* ------------------------------------------------------------ */ - /* + /* * @see javax.servlet.ServletContext#getAttributeNames() */ @SuppressWarnings("unchecked") @@ -340,7 +346,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. { return AttributesMap.getAttributeNamesCopy(_attributes); } - + /* ------------------------------------------------------------ */ /** * @return Returns the attributes. @@ -349,7 +355,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. { return _attributes; } - + /* ------------------------------------------------------------ */ /** * @return Returns the classLoader. @@ -402,9 +408,9 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. { return _contextPath; } - + /* ------------------------------------------------------------ */ - /* + /* * @see javax.servlet.ServletContext#getInitParameter(java.lang.String) */ public String getInitParameter(String name) @@ -413,7 +419,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } /* ------------------------------------------------------------ */ - /* + /* * @see javax.servlet.ServletContext#getInitParameterNames() */ @SuppressWarnings("unchecked") @@ -421,7 +427,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. { return Collections.enumeration(_initParams.keySet()); } - + /* ------------------------------------------------------------ */ /** * @return Returns the initParams. @@ -432,7 +438,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } /* ------------------------------------------------------------ */ - /* + /* * @see javax.servlet.ServletContext#getServletContextName() */ public String getDisplayName() @@ -445,7 +451,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. { return _eventListeners; } - + /* ------------------------------------------------------------ */ /** * Set the context event listeners. @@ -460,26 +466,26 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. _contextAttributeListeners=null; _requestListeners=null; _requestAttributeListeners=null; - + _eventListeners=eventListeners; - + for (int i=0; eventListeners!=null && i<eventListeners.length;i ++) { EventListener listener = _eventListeners[i]; - + if (listener instanceof ServletContextListener) _contextListeners= LazyList.add(_contextListeners, listener); - + if (listener instanceof ServletContextAttributeListener) _contextAttributeListeners= LazyList.add(_contextAttributeListeners, listener); - + if (listener instanceof ServletRequestListener) _requestListeners= LazyList.add(_requestListeners, listener); - + if (listener instanceof ServletRequestAttributeListener) _requestAttributeListeners= LazyList.add(_requestAttributeListeners, listener); } - } + } /* ------------------------------------------------------------ */ /** @@ -489,7 +495,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. * @see ServletRequestListener * @see ServletRequestAttributeListener */ - public void addEventListener(EventListener listener) + public void addEventListener(EventListener listener) { setEventListeners((EventListener[])LazyList.addToArray(getEventListeners(), listener, EventListener.class)); } @@ -557,34 +563,34 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. { return _logger; } - + /* ------------------------------------------------------------ */ public void setLogger(Logger logger) { _logger=logger; } - + /* ------------------------------------------------------------ */ - /* + /* * @see org.eclipse.thread.AbstractLifeCycle#doStart() */ @Override protected void doStart() throws Exception { _availability=__STOPPED; - + if (_contextPath==null) throw new IllegalStateException("Null contextPath"); - + _logger=Log.getLogger(getDisplayName()==null?getContextPath():getDisplayName()); ClassLoader old_classloader=null; Thread current_thread=null; Context old_context=null; - _contextAttributes=new AttributesMap(); + _contextAttributes.clearAttributes(); try { - + // Set the classloader if (_classLoader!=null) { @@ -592,46 +598,40 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. old_classloader=current_thread.getContextClassLoader(); current_thread.setContextClassLoader(_classLoader); } - + if (_mimeTypes==null) _mimeTypes=new MimeTypes(); - + old_context=__context.get(); __context.set(_scontext); - - if (_errorHandler==null) - setErrorHandler(new ErrorHandler()); - - + // defers the calling of super.doStart() startContext(); - _availability=_shutdown?__SHUTDOWN:_available?__AVAILABLE:__UNAVAILABLE; } finally { __context.set(old_context); - + // reset the classloader if (_classLoader!=null) { current_thread.setContextClassLoader(old_classloader); } - + } } /* ------------------------------------------------------------ */ /** * Extensible startContext. - * this method is called from {@link ContextHandler#doStart()} instead of a + * this method is called from {@link ContextHandler#doStart()} instead of a * call to super.doStart(). This allows derived classes to insert additional * handling (Eg configuration) before the call to super.doStart by this method * will start contained handlers. - * @see org.eclipse.jetty.Scope.Context - * @see org.eclipse.jetty.webapp.WebAppContext + * @see org.eclipse.jetty.server.handler.ContextHandler.Context */ protected void startContext() throws Exception @@ -643,21 +643,21 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. String[] attributes = managedAttributes.split(","); for (String attribute : attributes) _managedAttributes.put(attribute,null); - + Enumeration e = _scontext.getAttributeNames(); while(e.hasMoreElements()) { String name = (String)e.nextElement(); Object value = _scontext.getAttribute(name); - setManagedAttribute(name,value); + checkManagedAttribute(name,value); } - } - + } + super.doStart(); if (_errorHandler!=null) _errorHandler.start(); - + // Context listeners if (_contextListeners != null ) { @@ -668,14 +668,14 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } } } - + public void callContextInitialized (ServletContextListener l, ServletContextEvent e) { l.contextInitialized(e); } - + public void callContextDestroyed (ServletContextListener l, ServletContextEvent e) { l.contextDestroyed(e); @@ -684,14 +684,14 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. /* ------------------------------------------------------------ */ - /* + /* * @see org.eclipse.thread.AbstractLifeCycle#doStop() */ @Override protected void doStop() throws Exception { _availability=__STOPPED; - + ClassLoader old_classloader=null; Thread current_thread=null; @@ -706,9 +706,9 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. old_classloader=current_thread.getContextClassLoader(); current_thread.setContextClassLoader(_classLoader); } - + super.doStop(); - + // Context listeners if (_contextListeners != null ) { @@ -721,12 +721,12 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. if (_errorHandler!=null) _errorHandler.stop(); - + Enumeration e = _scontext.getAttributeNames(); while(e.hasMoreElements()) { String name = (String)e.nextElement(); - setManagedAttribute(name,null); + checkManagedAttribute(name,null); } } finally @@ -737,20 +737,18 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. current_thread.setContextClassLoader(old_classloader); } - if (_contextAttributes!=null) - _contextAttributes.clearAttributes(); - _contextAttributes=null; + _contextAttributes.clearAttributes(); } /* ------------------------------------------------------------ */ - /* + /* * @see org.eclipse.jetty.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ public boolean checkContext(final String target, final Request baseRequest, final HttpServletResponse response) throws IOException, ServletException - { + { DispatcherType dispatch=baseRequest.getDispatcherType(); - + switch(_availability) { case __STOPPED: @@ -763,14 +761,14 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. if((DispatcherType.REQUEST.equals(dispatch) && baseRequest.isHandled())) return false; } - + // Check the vhosts if (_vhosts!=null && _vhosts.length>0) { String vhost = normalizeHostname( baseRequest.getServerName()); boolean match=false; - + // TODO non-linear lookup for (int i=0;!match && i<_vhosts.length;i++) { @@ -785,7 +783,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. if (!match) return false; } - + // Check the connector if (_connectors!=null && _connectors.size()>0) { @@ -794,29 +792,31 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. return false; } - - if (target.startsWith(_contextPath)) + // Are we not the root context? + if (_contextPath.length()>1) { - if (_contextPath.length()==target.length() && _contextPath.length()>1 &&!_allowNullPathInfo) + // reject requests that are not for us + if (!target.startsWith(_contextPath)) + return false; + if (target.length()>_contextPath.length() && target.charAt(_contextPath.length())!='/') + return false; + + // redirect null path infos + if (!_allowNullPathInfo && _contextPath.length()==target.length()) { // context request must end with / baseRequest.setHandled(true); if (baseRequest.getQueryString()!=null) response.sendRedirect(URIUtil.addPaths(baseRequest.getRequestURI(),URIUtil.SLASH)+"?"+baseRequest.getQueryString()); - else + else response.sendRedirect(URIUtil.addPaths(baseRequest.getRequestURI(),URIUtil.SLASH)); return false; } } - else - { - // Not for this context! - return false; - } - + return true; - } - + } + /* ------------------------------------------------------------ */ @@ -825,7 +825,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. */ @Override public void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException - { + { Context old_context=null; String old_context_path=null; String old_servlet_path=null; @@ -835,9 +835,9 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. String pathInfo=null; DispatcherType dispatch=baseRequest.getDispatcherType(); - + old_context=baseRequest.getContext(); - + // Are we already in this context? if (old_context!=_scontext) { @@ -848,7 +848,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. target=URIUtil.compactPath(target); if (!checkContext(target,baseRequest,response)) return; - + if (target.length()>_contextPath.length()) { if (_contextPath.length()>1) @@ -875,13 +875,13 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. current_thread.setContextClassLoader(_classLoader); } } - + try { old_context_path=baseRequest.getContextPath(); old_servlet_path=baseRequest.getServletPath(); old_path_info=baseRequest.getPathInfo(); - + // Update the paths baseRequest.setContext(_scontext); if (!DispatcherType.INCLUDE.equals(dispatch) && target.startsWith("/")) @@ -893,16 +893,15 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. baseRequest.setServletPath(null); baseRequest.setPathInfo(pathInfo); } - + // start manual inline of nextScope(target,baseRequest,request,response); - //noinspection ConstantIfStatement - if (false) + if (never()) nextScope(target,baseRequest,request,response); else if (_nextScope!=null) _nextScope.doScope(target,baseRequest,request, response); else if (_outerScope!=null) _outerScope.doHandle(target,baseRequest,request, response); - else + else doHandle(target,baseRequest,request, response); // end manual inline (pathentic attempt to reduce stack depth) } @@ -915,16 +914,16 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. { current_thread.setContextClassLoader(old_classloader); } - + // reset the context and servlet path. baseRequest.setContext(old_context); baseRequest.setContextPath(old_context_path); baseRequest.setServletPath(old_servlet_path); - baseRequest.setPathInfo(old_path_info); + baseRequest.setPathInfo(old_path_info); } } } - + /* ------------------------------------------------------------ */ /** * @see org.eclipse.jetty.server.handler.ScopedHandler#doHandle(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) @@ -954,13 +953,13 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. ((ServletRequestListener)LazyList.get(_requestListeners,i)).requestInitialized(sre); } } - + if (DispatcherType.REQUEST.equals(dispatch) && isProtectedTarget(target)) throw new HttpException(HttpServletResponse.SC_NOT_FOUND); - + // start manual inline of nextHandle(target,baseRequest,request,response); //noinspection ConstantIfStatement - if (false) + if (never()) nextHandle(target,baseRequest,request,response); else if (_nextScope!=null && _nextScope==_handler) _nextScope.doHandle(target,baseRequest,request, response); @@ -985,7 +984,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. for(int i=0;i<s;i++) ((ServletRequestListener)LazyList.get(_requestListeners,i)).requestDestroyed(sre); } - + if (_requestAttributeListeners!=null) { for(int i=LazyList.size(_requestAttributeListeners);i-->0;) @@ -994,12 +993,12 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } } } - + /* ------------------------------------------------------------ */ /* Handle a runnable in this context */ public void handle(Runnable runnable) - { + { ClassLoader old_classloader=null; Thread current_thread=null; try @@ -1011,7 +1010,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. old_classloader=current_thread.getContextClassLoader(); current_thread.setContextClassLoader(_classLoader); } - + runnable.run(); } finally @@ -1028,64 +1027,49 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. * Called by {@link #handle(String, Request, HttpServletRequest, HttpServletResponse)} when a * target within a context is determined. If the target is protected, 404 is returned. * The default implementation always returns false. - * @see org.eclipse.jetty.webapp.WebAppContext#isProtectedTarget(String) */ /* ------------------------------------------------------------ */ protected boolean isProtectedTarget(String target) - { + { return false; } /* ------------------------------------------------------------ */ - /* + /* * @see javax.servlet.ServletContext#removeAttribute(java.lang.String) */ public void removeAttribute(String name) { - setManagedAttribute(name,null); + checkManagedAttribute(name,null); _attributes.removeAttribute(name); } /* ------------------------------------------------------------ */ /* Set a context attribute. * Attributes set via this API cannot be overriden by the ServletContext.setAttribute API. - * Their lifecycle spans the stop/start of a context. No attribute listener events are + * Their lifecycle spans the stop/start of a context. No attribute listener events are * triggered by this API. * @see javax.servlet.ServletContext#setAttribute(java.lang.String, java.lang.Object) */ public void setAttribute(String name, Object value) { - setManagedAttribute(name,value); + checkManagedAttribute(name,value); _attributes.setAttribute(name,value); } - + /* ------------------------------------------------------------ */ /** * @param attributes The attributes to set. */ public void setAttributes(Attributes attributes) { - if (attributes instanceof AttributesMap) - { - _attributes = (AttributesMap)attributes; - Enumeration e = _attributes.getAttributeNames(); - while (e.hasMoreElements()) - { - String name = (String)e.nextElement(); - setManagedAttribute(name,attributes.getAttribute(name)); - } - } - else + _attributes.clearAttributes(); + _attributes.addAll(attributes); + Enumeration e = _attributes.getAttributeNames(); + while (e.hasMoreElements()) { - _attributes=new AttributesMap(); - Enumeration e = attributes.getAttributeNames(); - while (e.hasMoreElements()) - { - String name = (String)e.nextElement(); - Object value=attributes.getAttribute(name); - setManagedAttribute(name,value); - _attributes.setAttribute(name,value); - } + String name = (String)e.nextElement(); + checkManagedAttribute(name,attributes.getAttribute(name)); } } @@ -1096,28 +1080,28 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. while (e.hasMoreElements()) { String name = (String)e.nextElement(); - setManagedAttribute(name,null); + checkManagedAttribute(name,null); } _attributes.clearAttributes(); } /* ------------------------------------------------------------ */ - private void setManagedAttribute(String name, Object value) - { + public void checkManagedAttribute(String name, Object value) + { if (_managedAttributes!=null && _managedAttributes.containsKey(name)) { - Object old =_managedAttributes.put(name,value); - if (old!=null) - getServer().getContainer().removeBean(old); - if (value!=null) - { - if (_logger.isDebugEnabled()) _logger.debug("Managing "+name); - getServer().getContainer().addBean(value); - } + setManagedAttribute(name,value); } } /* ------------------------------------------------------------ */ + public void setManagedAttribute(String name, Object value) + { + Object old =_managedAttributes.put(name,value); + getServer().getContainer().update(this,old,value,name); + } + + /* ------------------------------------------------------------ */ /** * @param classLoader The classLoader to set. */ @@ -1125,7 +1109,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. { _classLoader = classLoader; } - + /* ------------------------------------------------------------ */ /** * @param contextPath The _contextPath to set. @@ -1135,7 +1119,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. if (contextPath!=null && contextPath.length()>1 && contextPath.endsWith("/")) throw new IllegalArgumentException("ends with /"); _contextPath = contextPath; - + if (getServer()!=null && (getServer().isStarting() || getServer().isStarted())) { Handler[] contextCollections = getServer().getChildHandlersByClass(ContextHandlerCollection.class); @@ -1143,18 +1127,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. ((ContextHandlerCollection)contextCollections[h]).mapContexts(); } } - - /* ------------------------------------------------------------ */ - /** - * @param initParams The initParams to set. - */ - public void setInitParams(Map<String,String> initParams) - { - if (initParams == null) - return; - _initParams = new HashMap<String,String>(initParams); - } - + /* ------------------------------------------------------------ */ /** * @param servletContextName The servletContextName to set. @@ -1163,7 +1136,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. { _displayName = servletContextName; } - + /* ------------------------------------------------------------ */ /** * @return Returns the resourceBase. @@ -1185,12 +1158,12 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. return null; return _baseResource.toString(); } - + /* ------------------------------------------------------------ */ /** * @param base The resourceBase to set. */ - public void setBaseResource(Resource base) + public void setBaseResource(Resource base) { _baseResource=base; } @@ -1199,7 +1172,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. /** * @param resourceBase The base resource as a string. */ - public void setResourceBase(String resourceBase) + public void setResourceBase(String resourceBase) { try { @@ -1238,7 +1211,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. { return _mimeTypes; } - + /* ------------------------------------------------------------ */ /** * @param mimeTypes The mimeTypes to set. @@ -1251,7 +1224,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. /* ------------------------------------------------------------ */ /** */ - public void setWelcomeFiles(String[] files) + public void setWelcomeFiles(String[] files) { _welcomeFiles=files; } @@ -1262,7 +1235,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. * @see <a href="http://jcp.org/aboutJava/communityprocess/final/jsr154/index.html">The Servlet Specification</a> * @see #setWelcomeFiles */ - public String[] getWelcomeFiles() + public String[] getWelcomeFiles() { return _welcomeFiles; } @@ -1288,13 +1261,13 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. getServer().getContainer().update(this, _errorHandler, errorHandler, "errorHandler",true); _errorHandler = errorHandler; } - + /* ------------------------------------------------------------ */ public int getMaxFormContentSize() { return _maxFormContentSize; } - + /* ------------------------------------------------------------ */ public void setMaxFormContentSize(int maxSize) { @@ -1324,7 +1297,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. @Override public String toString() { - + return super.toString()+"@"+Integer.toHexString(hashCode())+getContextPath()+","+getBaseResource(); } @@ -1334,13 +1307,13 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. { if (className==null) return null; - + if (_classLoader==null) return Loader.loadClass(this.getClass(), className); return _classLoader.loadClass(className); } - + /* ------------------------------------------------------------ */ public void addLocaleEncoding(String locale,String encoding) @@ -1349,12 +1322,12 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. _localeEncodingMap=new HashMap<String,String>(); _localeEncodingMap.put(locale, encoding); } - + /* ------------------------------------------------------------ */ /** * Get the character encoding for a locale. The full locale name is first * looked up in the map of encodings. If no encoding is found, then the - * locale language is looked up. + * locale language is looked up. * * @param locale a <code>Locale</code> value * @return a <code>String</code> representing the character encoding for @@ -1369,7 +1342,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. encoding = _localeEncodingMap.get(locale.getLanguage()); return encoding; } - + public String getLocaleEncoding (String locale) { if (_localeEncodingMap==null) @@ -1379,13 +1352,13 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } /* ------------------------------------------------------------ */ - /* + /* */ public Resource getResource(String path) throws MalformedURLException { if (path==null || !path.startsWith(URIUtil.SLASH)) throw new MalformedURLException(path); - + if (_baseResource==null) return null; @@ -1393,7 +1366,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. { path=URIUtil.canonicalPath(path); Resource resource=_baseResource.addPath(path); - + if (!_aliases && resource.getAlias()!=null) { if (resource.exists()) @@ -1402,20 +1375,20 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. Log.debug("Aliased resource: "+resource+"~="+resource.getAlias()); return null; } - + return resource; } catch(Exception e) { Log.ignore(e); } - + return null; } /* ------------------------------------------------------------ */ /** Convert URL to Resource - * wrapper for {@link Resource#newResource(URL)} enables extensions to + * wrapper for {@link Resource#newResource(URL)} enables extensions to * provide alternate resource implementations. */ public Resource newResource(URL url) throws IOException @@ -1425,7 +1398,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. /* ------------------------------------------------------------ */ /** Convert URL to Resource - * wrapper for {@link Resource#newResource(String)} enables extensions to + * wrapper for {@link Resource#newResource(String)} enables extensions to * provide alternate resource implementations. */ public Resource newResource(String url) throws IOException @@ -1434,20 +1407,20 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } /* ------------------------------------------------------------ */ - /* + /* */ public Set<String> getResourcePaths(String path) - { + { try { path=URIUtil.canonicalPath(path); Resource resource=getResource(path); - + if (resource!=null && resource.exists()) { if (!path.endsWith(URIUtil.SLASH)) path=path+URIUtil.SLASH; - + String[] l=resource.list(); if (l!=null) { @@ -1455,7 +1428,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. for(int i=0;i<l.length;i++) set.add(path+l[i]); return set; - } + } } } catch(Exception e) @@ -1472,20 +1445,20 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. { if ( host == null ) return null; - + if ( host.endsWith( "." ) ) return host.substring( 0, host.length() -1); - + return host; } - + /* ------------------------------------------------------------ */ /** Context. * <p> * A partial implementation of {@link javax.servlet.ServletContext}. - * A complete implementation is provided by the derived {@link org.eclipse.jetty.servlet.ServletContextHandler.Context}. + * A complete implementation is provided by the derived {@link ContextHandler}. * </p> - * + * * */ public class Context implements ServletContext @@ -1507,14 +1480,12 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } /* ------------------------------------------------------------ */ - /* + /* * @see javax.servlet.ServletContext#getContext(java.lang.String) */ @Override public ServletContext getContext(String uripath) { - // TODO this is a very poor implementation! - // TODO move this to Server ContextHandler context=null; Handler[] handlers = getServer().getChildHandlersByClass(ContextHandler.class); for (int i=0;i<handlers.length;i++) @@ -1523,20 +1494,21 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. continue; ContextHandler ch = (ContextHandler)handlers[i]; String context_path=ch.getContextPath(); - if (uripath.equals(context_path) || (uripath.startsWith(context_path)&&uripath.charAt(context_path.length())=='/')) + + if (uripath.equals(context_path) || (uripath.startsWith(context_path)&&uripath.charAt(context_path.length())=='/') || "/".equals(context_path)) { if (context==null || context_path.length()>context.getContextPath().length()) context=ch; } } - + if (context!=null) return context._scontext; return null; } /* ------------------------------------------------------------ */ - /* + /* * @see javax.servlet.ServletContext#getMajorVersion() */ @Override @@ -1547,7 +1519,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. /* ------------------------------------------------------------ */ - /* + /* * @see javax.servlet.ServletContext#getMimeType(java.lang.String) */ @Override @@ -1562,7 +1534,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } /* ------------------------------------------------------------ */ - /* + /* * @see javax.servlet.ServletContext#getMinorVersion() */ @Override @@ -1572,7 +1544,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } /* ------------------------------------------------------------ */ - /* + /* * @see javax.servlet.ServletContext#getNamedDispatcher(java.lang.String) */ @Override @@ -1582,7 +1554,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } /* ------------------------------------------------------------ */ - /* + /* * @see javax.servlet.ServletContext#getRequestDispatcher(java.lang.String) */ @Override @@ -1593,7 +1565,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. if (!uriInContext.startsWith("/")) return null; - + try { String query=null; @@ -1619,7 +1591,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } /* ------------------------------------------------------------ */ - /* + /* * @see javax.servlet.ServletContext#getRealPath(java.lang.String) */ @Override @@ -1631,7 +1603,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. path = URIUtil.SLASH; else if(path.charAt(0)!='/') path = URIUtil.SLASH + path; - + try { Resource resource=ContextHandler.this.getResource(path); @@ -1646,7 +1618,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. { Log.ignore(e); } - + return null; } @@ -1659,9 +1631,9 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. return resource.getURL(); return null; } - + /* ------------------------------------------------------------ */ - /* + /* * @see javax.servlet.ServletContext#getResourceAsStream(java.lang.String) */ @Override @@ -1682,17 +1654,17 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } /* ------------------------------------------------------------ */ - /* + /* * @see javax.servlet.ServletContext#getResourcePaths(java.lang.String) */ @Override public Set getResourcePaths(String path) - { + { return ContextHandler.this.getResourcePaths(path); } /* ------------------------------------------------------------ */ - /* + /* * @see javax.servlet.ServletContext#getServerInfo() */ @Override @@ -1702,7 +1674,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } /* ------------------------------------------------------------ */ - /* + /* * @see javax.servlet.ServletContext#getServlet(java.lang.String) */ @Override @@ -1713,7 +1685,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } /* ------------------------------------------------------------ */ - /* + /* * @see javax.servlet.ServletContext#getServletNames() */ @SuppressWarnings("unchecked") @@ -1725,7 +1697,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } /* ------------------------------------------------------------ */ - /* + /* * @see javax.servlet.ServletContext#getServlets() */ @SuppressWarnings("unchecked") @@ -1737,7 +1709,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } /* ------------------------------------------------------------ */ - /* + /* * @see javax.servlet.ServletContext#log(java.lang.Exception, java.lang.String) */ @Override @@ -1747,7 +1719,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } /* ------------------------------------------------------------ */ - /* + /* * @see javax.servlet.ServletContext#log(java.lang.String) */ @Override @@ -1757,7 +1729,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } /* ------------------------------------------------------------ */ - /* + /* * @see javax.servlet.ServletContext#log(java.lang.String, java.lang.Throwable) */ @Override @@ -1767,7 +1739,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } /* ------------------------------------------------------------ */ - /* + /* * @see javax.servlet.ServletContext#getInitParameter(java.lang.String) */ @Override @@ -1777,7 +1749,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } /* ------------------------------------------------------------ */ - /* + /* * @see javax.servlet.ServletContext#getInitParameterNames() */ @SuppressWarnings("unchecked") @@ -1788,7 +1760,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } /* ------------------------------------------------------------ */ - /* + /* * @see javax.servlet.ServletContext#getAttribute(java.lang.String) */ @Override @@ -1801,7 +1773,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } /* ------------------------------------------------------------ */ - /* + /* * @see javax.servlet.ServletContext#getAttributeNames() */ @SuppressWarnings("unchecked") @@ -1818,33 +1790,25 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. Enumeration<String> e = _attributes.getAttributeNames(); while(e.hasMoreElements()) set.add(e.nextElement()); - + return Collections.enumeration(set); } /* ------------------------------------------------------------ */ - /* + /* * @see javax.servlet.ServletContext#setAttribute(java.lang.String, java.lang.Object) */ @Override public synchronized void setAttribute(String name, Object value) { - - if (_contextAttributes==null) - { - // Set it on the handler - ContextHandler.this.setAttribute(name, value); - return; - } - - setManagedAttribute(name,value); + checkManagedAttribute(name,value); Object old_value=_contextAttributes.getAttribute(name); - + if (value==null) _contextAttributes.removeAttribute(name); else _contextAttributes.setAttribute(name,value); - + if (_contextAttributeListeners!=null) { ServletContextAttributeEvent event = @@ -1853,7 +1817,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. for(int i=0;i<LazyList.size(_contextAttributeListeners);i++) { ServletContextAttributeListener l = (ServletContextAttributeListener)LazyList.get(_contextAttributeListeners,i); - + if (old_value==null) l.attributeAdded(event); else if (value==null) @@ -1865,21 +1829,21 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } /* ------------------------------------------------------------ */ - /* + /* * @see javax.servlet.ServletContext#removeAttribute(java.lang.String) */ @Override public synchronized void removeAttribute(String name) { - setManagedAttribute(name,null); - + checkManagedAttribute(name,null); + if (_contextAttributes==null) { // Set it on the handler _attributes.removeAttribute(name); return; } - + Object old_value=_contextAttributes.getAttribute(name); _contextAttributes.removeAttribute(name); if (old_value!=null) @@ -1896,7 +1860,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } /* ------------------------------------------------------------ */ - /* + /* * @see javax.servlet.ServletContext#getServletContextName() */ @Override @@ -1914,7 +1878,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. { if ((_contextPath != null) && _contextPath.equals(URIUtil.SLASH)) return ""; - + return _contextPath; } @@ -2128,7 +2092,6 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. return _minorVersion; } - public void setEffectiveMajorVersion (int v) { _majorVersion = v; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandlerCollection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandlerCollection.java index c91e067ed3..4972bcf586 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandlerCollection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandlerCollection.java @@ -33,7 +33,7 @@ import org.eclipse.jetty.util.log.Log; /** ContextHandlerCollection. * * This {@link org.eclipse.jetty.server.handler.HandlerCollection} is creates a - * {@link org.eclipse.jetty.http.servlet.PathMap} to it's contained handlers based + * {@link org.eclipse.jetty.http.PathMap} to it's contained handlers based * on the context path and virtual hosts of any contained {@link org.eclipse.jetty.server.handler.ContextHandler}s. * The contexts do not need to be directly contained, only children of the contained handlers. * Multiple contexts may have the same context path and they are called in order until one @@ -265,9 +265,7 @@ public class ContextHandlerCollection extends HandlerCollection /* ------------------------------------------------------------ */ /** Add a context handler. * @param contextPath The context path to add - * @return - * @throws IllegalAccessException - * @throws InstantiationException + * @return the ContextHandler just added */ public ContextHandler addContext(String contextPath,String resourceBase) { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DefaultHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DefaultHandler.java index 232aba637c..073c0dfde9 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DefaultHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DefaultHandler.java @@ -50,6 +50,7 @@ public class DefaultHandler extends AbstractHandler final long _faviconModified=(System.currentTimeMillis()/1000)*1000; byte[] _favicon; boolean _serveIcon=true; + boolean _showContexts=true; public DefaultHandler() { @@ -114,44 +115,47 @@ public class DefaultHandler extends AbstractHandler writer.write("<HTML>\n<HEAD>\n<TITLE>Error 404 - Not Found"); writer.write("</TITLE>\n<BODY>\n<H2>Error 404 - Not Found.</H2>\n"); writer.write("No context on this server matched or handled this request.<BR>"); - writer.write("Contexts known to this server are: <ul>"); - - - Server server = getServer(); - Handler[] handlers = server==null?null:server.getChildHandlersByClass(ContextHandler.class); - - for (int i=0;handlers!=null && i<handlers.length;i++) + + if (_showContexts) { - ContextHandler context = (ContextHandler)handlers[i]; - if (context.isRunning()) - { - writer.write("<li><a href=\""); - if (context.getVirtualHosts()!=null && context.getVirtualHosts().length>0) - writer.write("http://"+context.getVirtualHosts()[0]+":"+request.getLocalPort()); - writer.write(context.getContextPath()); - if (context.getContextPath().length()>1 && context.getContextPath().endsWith("/")) - writer.write("/"); - writer.write("\">"); - writer.write(context.getContextPath()); - if (context.getVirtualHosts()!=null && context.getVirtualHosts().length>0) - writer.write(" @ "+context.getVirtualHosts()[0]+":"+request.getLocalPort()); - writer.write(" ---> "); - writer.write(context.toString()); - writer.write("</a></li>\n"); - } - else + writer.write("Contexts known to this server are: <ul>"); + + Server server = getServer(); + Handler[] handlers = server==null?null:server.getChildHandlersByClass(ContextHandler.class); + + for (int i=0;handlers!=null && i<handlers.length;i++) { - writer.write("<li>"); - writer.write(context.getContextPath()); - if (context.getVirtualHosts()!=null && context.getVirtualHosts().length>0) - writer.write(" @ "+context.getVirtualHosts()[0]+":"+request.getLocalPort()); - writer.write(" ---> "); - writer.write(context.toString()); - if (context.isFailed()) - writer.write(" [failed]"); - if (context.isStopped()) - writer.write(" [stopped]"); - writer.write("</li>\n"); + ContextHandler context = (ContextHandler)handlers[i]; + if (context.isRunning()) + { + writer.write("<li><a href=\""); + if (context.getVirtualHosts()!=null && context.getVirtualHosts().length>0) + writer.write("http://"+context.getVirtualHosts()[0]+":"+request.getLocalPort()); + writer.write(context.getContextPath()); + if (context.getContextPath().length()>1 && context.getContextPath().endsWith("/")) + writer.write("/"); + writer.write("\">"); + writer.write(context.getContextPath()); + if (context.getVirtualHosts()!=null && context.getVirtualHosts().length>0) + writer.write(" @ "+context.getVirtualHosts()[0]+":"+request.getLocalPort()); + writer.write(" ---> "); + writer.write(context.toString()); + writer.write("</a></li>\n"); + } + else + { + writer.write("<li>"); + writer.write(context.getContextPath()); + if (context.getVirtualHosts()!=null && context.getVirtualHosts().length>0) + writer.write(" @ "+context.getVirtualHosts()[0]+":"+request.getLocalPort()); + writer.write(" ---> "); + writer.write(context.toString()); + if (context.isFailed()) + writer.write(" [failed]"); + if (context.isStopped()) + writer.write(" [stopped]"); + writer.write("</li>\n"); + } } } @@ -183,6 +187,15 @@ public class DefaultHandler extends AbstractHandler { _serveIcon = serveIcon; } + + public boolean getShowContexts() + { + return _showContexts; + } + public void setShowContexts(boolean show) + { + _showContexts = show; + } } 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 a8dabdaf56..6a422e9be8 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 @@ -27,13 +27,11 @@ import org.eclipse.jetty.server.HttpConnection; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.util.ByteArrayISO8859Writer; - /* ------------------------------------------------------------ */ /** Handler for Error pages - * A handler that is registered at the org.eclipse.http.ErrorHandler - * context attributed and called by the HttpResponse.sendError method to write a - * error page. - * + * 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. * */ public class ErrorHandler extends AbstractHandler 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 7746bf5d75..fdb840030e 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 @@ -109,8 +109,8 @@ public class HandlerCollection extends AbstractHandlerContainer } /* ------------------------------------------------------------ */ - /* - * @see org.eclipse.jetty.server.server.EventHandler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) + /** + * @see Handler#handle(String, Request, HttpServletRequest, HttpServletResponse) */ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerList.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerList.java index c25ab0e18e..35b067f508 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerList.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerList.java @@ -24,15 +24,15 @@ import org.eclipse.jetty.server.Request; /* ------------------------------------------------------------ */ /** HandlerList. - * This extension of {@link org.eclipse.jetty.server.server.handler.HandlerCollection} will call + * This extension of {@link HandlerCollection} will call * each contained handler in turn until either an exception is thrown, the response * is committed or a positive response status is set. */ public class HandlerList extends HandlerCollection { /* ------------------------------------------------------------ */ - /* - * @see org.eclipse.jetty.server.server.EventHandler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) + /** + * @see Handler#handle(String, Request, HttpServletRequest, HttpServletResponse) */ @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IPAccessHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IPAccessHandler.java index 6ed3d15fdd..ebcaadf9d0 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IPAccessHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IPAccessHandler.java @@ -14,10 +14,9 @@ package org.eclipse.jetty.server.handler; import java.io.IOException; -import java.util.HashMap; +import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.concurrent.CopyOnWriteArrayList; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -28,78 +27,150 @@ import org.eclipse.jetty.http.PathMap; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.server.HttpConnection; import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.util.IPAddressMap; +import org.eclipse.jetty.util.log.Log; /** * IP Access Handler * <p> - * Control access to the wrapped handler by the real remote IP. - * The real IP of the connection is used (not the IP reported in the forwarded for headers), - * as this cannot be as easily forged. + * Controls access to the wrapped handler by the real remote IP. Control is provided + * by white/black lists that include both internet addresses and URIs. This handler + * uses the real internet address of the connection, not one reported in the forwarded + * for headers, as this cannot be as easily forged. * <p> - * Control is provided by white/black lists of both internet addresses and URIs. - * Internet addresses may be absolute (eg 10.1.2.3) or a prefix pattern (eg 10.1.3. ). - * URI patterns follow the servlet specification for simple prefix and suffix wild cards. + * Typically, the black/white lists will be used in one of three modes: + * <ul> + * <li>Blocking a few specific IPs/URLs by specifying several black list entries. + * <li>Allowing only some specific IPs/URLs by specifying several white lists entries. + * <li>Allowing a general range of IPs/URLs by specifying several general white list + * entries, that are then further refined by several specific black list exceptions + * </ul> * <p> - * An empty white list is treated as match all. If there is at least one entry in the - * white list, then a request must match a white list entry. Black list entries are always - * appied, so that even if an entry matches the white list, a black list entry will override. + * An empty white list is treated as match all. If there is at least one entry in + * the white list, then a request must match a white list entry. Black list entries + * are always applied, so that even if an entry matches the white list, a black list + * entry will override it. + * <p> + * Internet addresses may be specified as absolute address or as a combination of + * four octet wildcard specifications (a.b.c.d) that are defined as follows. * </p> + * <pre> + * nnn - an absolute value (0-255) + * mmm-nnn - an inclusive range of absolute values, + * with following shorthand notations: + * nnn- => nnn-255 + * -nnn => 0-nnn + * - => 0-255 + * a,b,... - a list of wildcard specifications + * </pre> + * <p> + * Internet address specification is separated from the URI pattern using the "|" (pipe) + * character. URI patterns follow the servlet specification for simple * prefix and + * suffix wild cards (e.g. /, /foo, /foo/bar, /foo/bar/*, *.baz). + * <p> + * Earlier versions of the handler used internet address prefix wildcard specification + * to define a range of the internet addresses (e.g. 127., 10.10., 172.16.1.). + * They also used the first "/" character of the URI pattern to separate it from the + * internet address. Both of these features have been deprecated in the current version. * <p> - * Examples of match specifications are: + * Examples of the entry specifications are: * <ul> - * <li>10.1.2.3 - all requests from IP 10.1.2.3 - * <li>10.1.2.3/foo/bar - all requests from IP 10.1.2.3 to URI /foo/bar - * <li>10.1.2.3/foo/* - all requests from IP 10.1.2.3 to URIs starting with /foo/ - * <li>10.1.2.3/*.html - all requests from IP 10.1.2.3 to URIs ending with .html - * <li>10.1. - all requests from IPs starting with 10.1. - * <li>10.1./foo/bar - all requests from IPs starting with 10.1. to URI /foo/bar - * <li>10.1./foo/* - all requests from IPs starting with 10.1. to URIs starting with /foo/ + * <li>10.10.1.2 - all requests from IP 10.10.1.2 + * <li>10.10.1.2|/foo/bar - all requests from IP 10.10.1.2 to URI /foo/bar + * <li>10.10.1.2|/foo/* - all requests from IP 10.10.1.2 to URIs starting with /foo/ + * <li>10.10.1.2|*.html - all requests from IP 10.10.1.2 to URIs ending with .html + * <li>10.10.0-255.0-255 - all requests from IPs within 10.10.0.0/16 subnet + * <li>10.10.0-.-255|/foo/bar - all requests from IPs within 10.10.0.0/16 subnet to URI /foo/bar + * <li>10.10.0-3,1,3,7,15|/foo/* - all requests from IPs addresses with last octet equal + * to 1,3,7,15 in subnet 10.10.0.0/22 to URIs starting with /foo/ * </ul> * <p> - * Typically, the black/white lists will be used in one of three modes: - * <nl> - * <li>Blocking a few specific IPs/URLs by specifying several black list entries. - * <li>Allowing only some specific IPs/URLs by specifying several white lists entries. - * <li>Allowing a general range of IPs/URLs by specifying serveral general white list - * entries, that are then further refined by several specific black list exceptions - * </ul> - * + * Earlier versions of the handler used internet address prefix wildcard specification + * to define a range of the internet addresses (e.g. 127., 10.10., 172.16.1.). + * They also used the first "/" character of the URI pattern to separate it from the + * internet address. Both of these features have been deprecated in the current version. */ public class IPAccessHandler extends HandlerWrapper { - Map<String,PathMap> _whiteAddr = new HashMap<String, PathMap>(); - List<String> _whitePattern = new CopyOnWriteArrayList<String>(); - Map<String,PathMap> _blackAddr = new HashMap<String, PathMap>(); - List<String> _blackPattern = new CopyOnWriteArrayList<String>(); - + IPAddressMap<PathMap> _white = new IPAddressMap<PathMap>(); + IPAddressMap<PathMap> _black = new IPAddressMap<PathMap>(); + + /* ------------------------------------------------------------ */ /** + * Creates new handler object */ public IPAccessHandler() { + super(); } - public void addBlack(String addrPath) + /* ------------------------------------------------------------ */ + /** + * Creates new handler object and initializes white- and black-list + * + * @param white array of whitelist entries + * @param black array of blacklist entries + */ + public IPAccessHandler(String[] white, String []black) { - add(addrPath, _blackAddr, _blackPattern); + super(); + + if (white != null && white.length > 0) + setWhite(white); + if (black != null && black.length > 0) + setBlack(black); } - public void addWhite(String addrPath) + /* ------------------------------------------------------------ */ + /** + * Add a whitelist entry to an existing handler configuration + * + * @param entry new whitelist entry + */ + public void addWhite(String entry) { - add(addrPath, _whiteAddr, _whitePattern); + add(entry, _white); } - public void setBlack(String[] addrPaths) + /* ------------------------------------------------------------ */ + /** + * Add a blacklist entry to an existing handler configuration + * + * @param entry new blacklist entry + */ + public void addBlack(String entry) + { + add(entry, _black); + } + + /* ------------------------------------------------------------ */ + /** + * Re-initialize the whitelist of existing handler object + * + * @param entries array of whitelist entries + */ + public void setWhite(String[] entries) { - set(addrPaths, _blackAddr, _blackPattern); + set(entries, _white); } - public void setWhite(String[] addrPaths) + /* ------------------------------------------------------------ */ + /** + * Re-initialize the blacklist of existing handler object + * + * @param entries array of blacklist entries + */ + public void setBlack(String[] entries) { - set(addrPaths, _whiteAddr, _whitePattern); + set(entries, _black); } + /* ------------------------------------------------------------ */ /** + * Checks the incoming request against the whitelist and blacklist + * + * @see org.eclipse.jetty.server.handler.HandlerWrapper#handle(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException @@ -123,78 +194,181 @@ public class IPAccessHandler extends HandlerWrapper getHandler().handle(target,baseRequest, request, response); } + - protected void add(String addrPath, Map<String,PathMap> addrMap, List<String> patternList) + /* ------------------------------------------------------------ */ + /** + * Helper method to parse the new entry and add it to + * the specified address pattern map. + * + * @param entry new entry + * @param patternMap target address pattern map + */ + protected void add(String entry, IPAddressMap<PathMap> patternMap) { - int idx = addrPath.indexOf('/'); - String addr = idx > 0 ? addrPath.substring(0,idx) : addrPath; - String path = idx > 0 ? addrPath.substring(idx) : null; - if (path!=null && path.length()>1 && path.charAt(1)=='*') - path=path.substring(1); - - PathMap map = addrMap.get(addr); - if (map==null) + if (entry != null && entry.length() > 0) { - map = new PathMap(true); - addrMap.put(addr,map); + boolean deprecated = false; + int idx; + if (entry.indexOf('|') > 0 ) + { + idx = entry.indexOf('|'); + } + else + { + idx = entry.indexOf('/'); + deprecated = (idx >= 0); + } + + String addr = idx > 0 ? entry.substring(0,idx) : entry; + String path = idx > 0 ? entry.substring(idx) : "/*"; + if (addr.endsWith(".")) - patternList.add(addr); + deprecated = true; + if (path!=null && (path.startsWith("|") || path.startsWith("/*."))) + path=path.substring(1); + + PathMap pathMap = patternMap.get(addr); + if (pathMap == null) + { + pathMap = new PathMap(true); + patternMap.put(addr,pathMap); + } + if (path != null) + pathMap.put(path,path); + + if (deprecated) + Log.debug(toString() +" - deprecated specification syntax: "+entry); } - - if (path != null) - map.put(path,path); } - protected void set(String[] addrPaths, Map<String,PathMap> addrMap, List<String> patternList) + /* ------------------------------------------------------------ */ + /** + * Helper method to process a list of new entries and replace + * the content of the specified address pattern map + * + * @param entries new entries + * @param patternMap target address pattern map + */ + protected void set(String[] entries, IPAddressMap<PathMap> patternMap) { - addrMap.clear(); - patternList.clear(); - for (String addrPath:addrPaths) - add(addrPath, addrMap, patternList); + patternMap.clear(); + + if (entries != null && entries.length > 0) + { + for (String addrPath:entries) + { + add(addrPath, patternMap); + } + } } + /* ------------------------------------------------------------ */ + /** + * Check if specified request is allowed by current IPAccess rules. + * + * @param addr internet address + * @param path context path + * @return true if request is allowed + * + */ protected boolean isAddrUriAllowed(String addr, String path) { - if (_whiteAddr.size()>0) + if (_white.size()>0) { - PathMap white=_whiteAddr.get(addr); + boolean match = false; - if (white==null || (white.size()>0 && white.match(path)==null)) + Object whiteObj = _white.getLazyMatches(addr); + if (whiteObj != null) { - boolean match=false; - for (String pattern:_whitePattern) + List whiteList = (whiteObj instanceof List) ? (List)whiteObj : Collections.singletonList(whiteObj); + + for (Object entry: whiteList) { - if (addr.startsWith(pattern)) - { - white=_whiteAddr.get(pattern); - if (white!=null && white.size()>0 && white.match(path)!=null) - { - match=true; - break; - } - } + PathMap pathMap = ((Map.Entry<String,PathMap>)entry).getValue(); + if (match = (pathMap!=null && (pathMap.size()==0 || pathMap.match(path)!=null))) + break; } - if (!match) - return false; } + + if (!match) + return false; } - PathMap black=_blackAddr.get(addr); - if (black!=null && (black.size()==0 || black.match(path)!=null)) - return false; - - for (String pattern:_blackPattern) + if (_black.size() > 0) { - if (addr.startsWith(pattern)) + Object blackObj = _black.getLazyMatches(addr); + if (blackObj != null) { - black=_blackAddr.get(pattern); - if (black!=null && black.match(path)!=null) - return false; - break; + List blackList = (blackObj instanceof List) ? (List)blackObj : Collections.singletonList(blackObj); + + for (Object entry: blackList) + { + PathMap pathMap = ((Map.Entry<String,PathMap>)entry).getValue(); + if (pathMap!=null && (pathMap.size()==0 || pathMap.match(path)!=null)) + return false; + } } } return true; } -} + /* ------------------------------------------------------------ */ + /** + * Dump the white- and black-list configurations when started + * + * @see org.eclipse.jetty.server.handler.HandlerWrapper#doStart() + */ + @Override + protected void doStart() + throws Exception + { + super.doStart(); + + if (Log.isDebugEnabled()) + { + System.err.println(dump()); + } + } + + /* ------------------------------------------------------------ */ + /** + * Dump the handler configuration + */ + public String dump() + { + StringBuilder buf = new StringBuilder(); + + buf.append(toString()); + buf.append(" WHITELIST:\n"); + dump(buf, _white); + buf.append(toString()); + buf.append(" BLACKLIST:\n"); + dump(buf, _black); + + return buf.toString(); + } + + /* ------------------------------------------------------------ */ + /** + * Dump a pattern map into a StringBuilder buffer + * + * @param buf buffer + * @param patternMap pattern map to dump + */ + protected void dump(StringBuilder buf, IPAddressMap<PathMap> patternMap) + { + for (String addr: patternMap.keySet()) + { + for (Object path: ((PathMap)patternMap.get(addr)).values()) + { + buf.append("# "); + buf.append(addr); + buf.append("|"); + buf.append(path); + buf.append("\n"); + } + } + } + } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ProxyHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ProxyHandler.java new file mode 100644 index 0000000000..60ad6d10de --- /dev/null +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ProxyHandler.java @@ -0,0 +1,847 @@ +package org.eclipse.jetty.server.handler; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.SelectionKey; +import java.nio.channels.SocketChannel; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.http.HttpMethods; +import org.eclipse.jetty.http.HttpParser; +import org.eclipse.jetty.io.Buffer; +import org.eclipse.jetty.io.ConnectedEndPoint; +import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.nio.IndirectNIOBuffer; +import org.eclipse.jetty.io.nio.SelectChannelEndPoint; +import org.eclipse.jetty.io.nio.SelectorManager; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.HttpConnection; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.HostMap; +import org.eclipse.jetty.util.component.LifeCycle; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.thread.ThreadPool; + +/** + * <p>Implementation of a tunneling proxy that supports HTTP CONNECT and transparent proxy.</p> + * <p>To work as CONNECT proxy, objects of this class must be instantiated using the no-arguments + * constructor, since the remote server information will be present in the CONNECT URI.</p> + * <p>To work as transparent proxy, objects of this class must be instantiated using the string + * argument constructor, passing the remote host address and port in the form {@code host:port}.</p> + * + * @version $Revision$ $Date$ + */ +public class ProxyHandler extends HandlerWrapper +{ + private final Logger _logger = Log.getLogger(getClass().getName()); + private final SelectorManager _selectorManager = new Manager(); + private volatile int _connectTimeout = 5000; + private volatile int _writeTimeout = 30000; + private volatile ThreadPool _threadPool; + private volatile boolean _privateThreadPool; + private HostMap<String> _white = new HostMap<String>(); + private HostMap<String> _black = new HostMap<String>(); + + public ProxyHandler() + { + this(null); + } + + public ProxyHandler(String[] white, String[] black) + { + this(null, white, black); + } + + public ProxyHandler(Handler handler) + { + setHandler(handler); + } + + public ProxyHandler(Handler handler, String[] white, String[] black) + { + setHandler(handler); + set(white, _white); + set(black, _black); + } + + /** + * @return the timeout, in milliseconds, to connect to the remote server + */ + public int getConnectTimeout() + { + return _connectTimeout; + } + + /** + * @param connectTimeout the timeout, in milliseconds, to connect to the remote server + */ + public void setConnectTimeout(int connectTimeout) + { + _connectTimeout = connectTimeout; + } + + /** + * @return the timeout, in milliseconds, to write data to a peer + */ + public int getWriteTimeout() + { + return _writeTimeout; + } + + /** + * @param writeTimeout the timeout, in milliseconds, to write data to a peer + */ + public void setWriteTimeout(int writeTimeout) + { + _writeTimeout = writeTimeout; + } + + @Override + public void setServer(Server server) + { + super.setServer(server); + + server.getContainer().update(this,null,_selectorManager,"selectManager"); + + if (_privateThreadPool) + server.getContainer().update(this,null,_privateThreadPool,"threadpool",true); + else + _threadPool=server.getThreadPool(); + } + + /** + * @return the thread pool + */ + public ThreadPool getThreadPool() + { + return _threadPool; + } + + /** + * @param threadPool the thread pool + */ + public void setThreadPool(ThreadPool threadPool) + { + if (getServer()!=null) + getServer().getContainer().update(this,_privateThreadPool?_threadPool:null,threadPool,"threadpool",true); + _privateThreadPool=threadPool!=null; + _threadPool=threadPool; + } + + @Override + protected void doStart() throws Exception + { + super.doStart(); + + if (_threadPool==null) + { + _threadPool=getServer().getThreadPool(); + _privateThreadPool=false; + } + if (_threadPool instanceof LifeCycle && !((LifeCycle)_threadPool).isRunning()) + ((LifeCycle)_threadPool).start(); + + _selectorManager.start(); + _threadPool.dispatch(new Runnable() + { + public void run() + { + while (isRunning()) + { + try + { + _selectorManager.doSelect(0); + } + catch (IOException x) + { + _logger.warn("Unexpected exception", x); + } + } + } + }); + } + + @Override + protected void doStop() throws Exception + { + _selectorManager.stop(); + + ThreadPool threadPool = _threadPool; + if (_privateThreadPool && _threadPool != null && threadPool instanceof LifeCycle) + ((LifeCycle)threadPool).stop(); + + super.doStop(); + } + + @Override + public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + if (HttpMethods.CONNECT.equalsIgnoreCase(request.getMethod())) + { + _logger.debug("CONNECT request for {}", request.getRequestURI()); + handleConnect(baseRequest, request, response, request.getRequestURI()); + } + else + { + super.handle(target, baseRequest, request, response); + } + } + + /** + * <p>Handles a CONNECT request.</p> + * <p>CONNECT requests may have authentication headers such as <code>Proxy-Authorization</code> + * that authenticate the client with the proxy.</p> + * + * @param baseRequest Jetty-specific http request + * @param request the http request + * @param response the http response + * @param serverAddress the remote server address in the form {@code host:port} + * @throws ServletException if an application error occurs + * @throws IOException if an I/O error occurs + */ + protected void handleConnect(Request baseRequest, HttpServletRequest request, HttpServletResponse response, String serverAddress) throws ServletException, IOException + { + boolean proceed = handleAuthentication(request, response, serverAddress); + if (!proceed) + return; + + String host = serverAddress; + int port = 80; + int colon = serverAddress.indexOf(':'); + if (colon > 0) + { + host = serverAddress.substring(0, colon); + port = Integer.parseInt(serverAddress.substring(colon + 1)); + } + + if (!validateDestination(host)) + { + Log.info("ProxyHandler: Forbidden destination "+host); + response.setStatus(HttpServletResponse.SC_FORBIDDEN); + baseRequest.setHandled(true); + return; + } + + SocketChannel channel = connectToServer(request, host, port); + + // Transfer unread data from old connection to new connection + // We need to copy the data to avoid races: + // 1. when this unread data is written and the server replies before the clientToProxy + // connection is installed (it is only installed after returning from this method) + // 2. when the client sends data before this unread data has been written. + HttpConnection httpConnection = HttpConnection.getCurrentConnection(); + Buffer headerBuffer = ((HttpParser)httpConnection.getParser()).getHeaderBuffer(); + Buffer bodyBuffer = ((HttpParser)httpConnection.getParser()).getBodyBuffer(); + int length = headerBuffer == null ? 0 : headerBuffer.length(); + length += bodyBuffer == null ? 0 : bodyBuffer.length(); + IndirectNIOBuffer buffer = null; + if (length > 0) + { + buffer = new IndirectNIOBuffer(length); + if (headerBuffer != null) + { + buffer.put(headerBuffer); + headerBuffer.clear(); + } + if (bodyBuffer != null) + { + buffer.put(bodyBuffer); + bodyBuffer.clear(); + } + } + + ConcurrentMap<String, Object> context = new ConcurrentHashMap<String, Object>(); + prepareContext(request, context); + + ClientToProxyConnection clientToProxy = prepareConnections(context, channel, buffer); + + // CONNECT expects a 200 response + response.setStatus(HttpServletResponse.SC_OK); + + // Prevent close + baseRequest.getConnection().getGenerator().setPersistent(true); + + // Close to force last flush it so that the client receives it + response.getOutputStream().close(); + + upgradeConnection(request, response, clientToProxy); + } + + private ClientToProxyConnection prepareConnections(ConcurrentMap<String, Object> context, SocketChannel channel, Buffer buffer) + { + HttpConnection httpConnection = HttpConnection.getCurrentConnection(); + ProxyToServerConnection proxyToServer = newProxyToServerConnection(context, buffer); + ClientToProxyConnection clientToProxy = newClientToProxyConnection(context, channel, httpConnection.getEndPoint(), httpConnection.getTimeStamp()); + clientToProxy.setConnection(proxyToServer); + proxyToServer.setConnection(clientToProxy); + return clientToProxy; + } + + /** + * <p>Handles the authentication before setting up the tunnel to the remote server.</p> + * <p>The default implementation returns true.</p> + * + * @param request the HTTP request + * @param response the HTTP response + * @param address the address of the remote server in the form {@code host:port}. + * @return true to allow to connect to the remote host, false otherwise + * @throws ServletException to report a server error to the caller + * @throws IOException to report a server error to the caller + */ + protected boolean handleAuthentication(HttpServletRequest request, HttpServletResponse response, String address) throws ServletException, IOException + { + return true; + } + + protected ClientToProxyConnection newClientToProxyConnection(ConcurrentMap<String, Object> context, SocketChannel channel, EndPoint endPoint, long timeStamp) + { + return new ClientToProxyConnection(context, channel, endPoint, timeStamp); + } + + protected ProxyToServerConnection newProxyToServerConnection(ConcurrentMap<String, Object> context, Buffer buffer) + { + return new ProxyToServerConnection(context, buffer); + } + + private SocketChannel connectToServer(HttpServletRequest request, String host, int port) throws IOException + { + SocketChannel channel = connect(request, host, port); + channel.configureBlocking(false); + return channel; + } + + /** + * <p>Establishes a connection to the remote server.</p> + * @param request the HTTP request that initiated the tunnel + * @param host the host to connect to + * @param port the port to connect to + * @return a {@link SocketChannel} connected to the remote server + * @throws IOException if the connection cannot be established + */ + protected SocketChannel connect(HttpServletRequest request, String host, int port) throws IOException + { + _logger.debug("Establishing connection to {}:{}", host, port); + // Connect to remote server + SocketChannel channel = SocketChannel.open(); + channel.socket().setTcpNoDelay(true); + channel.socket().connect(new InetSocketAddress(host, port), getConnectTimeout()); + _logger.debug("Established connection to {}:{}", host, port); + return channel; + } + + protected void prepareContext(HttpServletRequest request, ConcurrentMap<String, Object> context) + { + } + + private void upgradeConnection(HttpServletRequest request, HttpServletResponse response, Connection connection) throws IOException + { + // Set the new connection as request attribute and change the status to 101 + // so that Jetty understands that it has to upgrade the connection + request.setAttribute("org.eclipse.jetty.io.Connection", connection); + response.setStatus(HttpServletResponse.SC_SWITCHING_PROTOCOLS); + _logger.debug("Upgraded connection to {}", connection); + } + + private void register(SocketChannel channel, ProxyToServerConnection proxyToServer) throws IOException + { + _selectorManager.register(channel, proxyToServer); + proxyToServer.waitReady(_connectTimeout); + } + + /** + * <p>Reads (with non-blocking semantic) into the given {@code buffer} from the given {@code endPoint}.</p> + * @param endPoint the endPoint to read from + * @param buffer the buffer to read data into + * @param context the context information related to the connection + * @return the number of bytes read (possibly 0 since the read is non-blocking) + * or -1 if the channel has been closed remotely + * @throws IOException if the endPoint cannot be read + */ + protected int read(EndPoint endPoint, Buffer buffer, ConcurrentMap<String, Object> context) throws IOException + { + return endPoint.fill(buffer); + } + + /** + * <p>Writes (with blocking semantic) the given buffer of data onto the given endPoint.</p> + * + * @param endPoint the endPoint to write to + * @param buffer the buffer to write + * @param context the context information related to the connection + * @throws IOException if the buffer cannot be written + * @return the number of bytes written + */ + protected int write(EndPoint endPoint, Buffer buffer, ConcurrentMap<String, Object> context) throws IOException + { + if (buffer == null) + return 0; + + int length = buffer.length(); + StringBuilder builder = new StringBuilder(); + int written = endPoint.flush(buffer); + builder.append(written); + buffer.compact(); + if (!endPoint.isBlocking()) + { + while (buffer.space() == 0) + { + boolean ready = endPoint.blockWritable(getWriteTimeout()); + if (!ready) + throw new IOException("Write timeout"); + + written = endPoint.flush(buffer); + builder.append("+").append(written); + buffer.compact(); + } + } + _logger.debug("Written {}/{} bytes {}", builder, length, endPoint); + return length; + } + + private class Manager extends SelectorManager + { + @Override + protected SocketChannel acceptChannel(SelectionKey key) throws IOException + { + // This is a client-side selector manager + throw new IllegalStateException(); + } + + @Override + protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey selectionKey) throws IOException + { + return new SelectChannelEndPoint(channel, selectSet, selectionKey); + } + + @Override + protected Connection newConnection(SocketChannel channel, SelectChannelEndPoint endpoint) + { + ProxyToServerConnection proxyToServer = (ProxyToServerConnection)endpoint.getSelectionKey().attachment(); + proxyToServer.setTimeStamp(System.currentTimeMillis()); + proxyToServer.setEndPoint(endpoint); + return proxyToServer; + } + + @Override + protected void endPointOpened(SelectChannelEndPoint endpoint) + { + ProxyToServerConnection proxyToServer = (ProxyToServerConnection)endpoint.getSelectionKey().attachment(); + proxyToServer.ready(); + } + + @Override + public boolean dispatch(Runnable task) + { + return _threadPool.dispatch(task); + } + + @Override + protected void endPointClosed(SelectChannelEndPoint endpoint) + { + } + + @Override + protected void endPointUpgraded(ConnectedEndPoint endpoint, Connection oldConnection) + { + } + } + + public class ProxyToServerConnection implements Connection + { + private final CountDownLatch _ready = new CountDownLatch(1); + private final Buffer _buffer = new IndirectNIOBuffer(1024); + private final ConcurrentMap<String, Object> _context; + private volatile Buffer _data; + private volatile ClientToProxyConnection _toClient; + private volatile long _timestamp; + private volatile SelectChannelEndPoint _endPoint; + + public ProxyToServerConnection(ConcurrentMap<String, Object> context, Buffer data) + { + _context = context; + _data = data; + } + + public Connection handle() throws IOException + { + _logger.debug("ProxyToServer: begin reading from server"); + try + { + if (_data != null) + { + int written = write(_endPoint, _data, _context); + _logger.debug("ProxyToServer: written to server {} bytes", written); + _data = null; + } + + while (true) + { + int read = read(_endPoint, _buffer, _context); + + if (read == -1) + { + _logger.debug("ProxyToServer: server closed connection {}", _endPoint); + close(); + break; + } + + if (read == 0) + break; + + _logger.debug("ProxyToServer: read from server {} bytes {}", read, _endPoint); + int written = write(_toClient._endPoint, _buffer, _context); + _logger.debug("ProxyToServer: written to client {} bytes", written); + } + return this; + } + catch (IOException x) + { + _logger.warn("ProxyToServer: Unexpected exception", x); + close(); + throw x; + } + catch (RuntimeException x) + { + _logger.warn("ProxyToServer: Unexpected exception", x); + close(); + throw x; + } + finally + { + _logger.debug("ProxyToServer: end reading from server"); + } + } + + public void setConnection(ClientToProxyConnection connection) + { + _toClient = connection; + } + + public long getTimeStamp() + { + return _timestamp; + } + + public void setTimeStamp(long timestamp) + { + _timestamp = timestamp; + } + + public void setEndPoint(SelectChannelEndPoint endpoint) + { + _endPoint = endpoint; + _logger.debug("ProxyToServer: {}", _endPoint); + } + + public boolean isIdle() + { + return false; + } + + public boolean isSuspended() + { + return false; + } + + public void ready() + { + _ready.countDown(); + } + + public void waitReady(long timeout) throws IOException + { + try + { + _ready.await(timeout, TimeUnit.MILLISECONDS); + } + catch (final InterruptedException x) + { + throw new IOException(){{initCause(x);}}; + } + } + + public void closeClient() throws IOException + { + _toClient.closeClient(); + } + + public void closeServer() throws IOException + { + _endPoint.close(); + } + + public void close() + { + try + { + closeClient(); + } + catch (IOException x) + { + _logger.debug("ProxyToServer: Unexpected exception closing the client", x); + } + + try + { + closeServer(); + } + catch (IOException x) + { + _logger.debug("ProxyToServer: Unexpected exception closing the server", x); + } + } + } + + public class ClientToProxyConnection implements Connection + { + private final Buffer _buffer = new IndirectNIOBuffer(1024); + private final ConcurrentMap<String, Object> _context; + private final SocketChannel _channel; + private final EndPoint _endPoint; + private final long _timestamp; + private volatile ProxyToServerConnection _toServer; + private boolean _firstTime = true; + + public ClientToProxyConnection(ConcurrentMap<String, Object> context, SocketChannel channel, EndPoint endPoint, long timestamp) + { + _context = context; + _channel = channel; + _endPoint = endPoint; + _timestamp = timestamp; + _logger.debug("ClientToProxy: {}", _endPoint); + } + + public Connection handle() throws IOException + { + _logger.debug("ClientToProxy: begin reading from client"); + try + { + if (_firstTime) + { + _firstTime = false; + register(_channel, _toServer); + _logger.debug("ClientToProxy: registered channel {} with connection {}", _channel, _toServer); + } + + while (true) + { + int read = read(_endPoint, _buffer, _context); + + if (read == -1) + { + _logger.debug("ClientToProxy: client closed connection {}", _endPoint); + close(); + break; + } + + if (read == 0) + break; + + _logger.debug("ClientToProxy: read from client {} bytes {}", read, _endPoint); + int written = write(_toServer._endPoint, _buffer, _context); + _logger.debug("ClientToProxy: written to server {} bytes", written); + } + return this; + } + catch (ClosedChannelException x) + { + _logger.debug("ClientToProxy",x); + closeServer(); + throw x; + } + catch (IOException x) + { + _logger.warn("ClientToProxy", x); + close(); + throw x; + } + catch (RuntimeException x) + { + _logger.warn("ClientToProxy", x); + close(); + throw x; + } + finally + { + _logger.debug("ClientToProxy: end reading from client"); + } + } + + public long getTimeStamp() + { + return _timestamp; + } + + public boolean isIdle() + { + return false; + } + + public boolean isSuspended() + { + return false; + } + + public void setConnection(ProxyToServerConnection connection) + { + _toServer = connection; + } + + public void closeClient() throws IOException + { + _endPoint.close(); + } + + public void closeServer() throws IOException + { + _toServer.closeServer(); + } + + public void close() + { + try + { + closeClient(); + } + catch (IOException x) + { + _logger.debug("ClientToProxy: Unexpected exception closing the client", x); + } + + try + { + closeServer(); + } + catch (IOException x) + { + _logger.debug("ClientToProxy: Unexpected exception closing the server", x); + } + } + } + + /* ------------------------------------------------------------ */ + /** + * Add a whitelist entry to an existing handler configuration + * + * @param entry new whitelist entry + */ + public void addWhite(String entry) + { + add(entry, _white); + } + + /* ------------------------------------------------------------ */ + /** + * Add a blacklist entry to an existing handler configuration + * + * @param entry new blacklist entry + */ + public void addBlack(String entry) + { + add(entry, _black); + } + + /* ------------------------------------------------------------ */ + /** + * Re-initialize the whitelist of existing handler object + * + * @param entries array of whitelist entries + */ + public void setWhite(String[] entries) + { + set(entries, _white); + } + + /* ------------------------------------------------------------ */ + /** + * Re-initialize the blacklist of existing handler object + * + * @param entries array of blacklist entries + */ + public void setBlack(String[] entries) + { + set(entries, _black); + } + + /* ------------------------------------------------------------ */ + /** + * Helper method to process a list of new entries and replace + * the content of the specified host map + * + * @param entries new entries + * @param patternMap target host map + */ + protected void set(String[] entries, HostMap<String> hostMap) + { + hostMap.clear(); + + if (entries != null && entries.length > 0) + { + for (String addrPath:entries) + { + add(addrPath, hostMap); + } + } + } + + /* ------------------------------------------------------------ */ + /** + * Helper method to process the new entry and add it to + * the specified host map. + * + * @param entry new entry + * @param patternMap target host map + */ + private void add(String entry, HostMap<String> hostMap) + { + if (entry != null && entry.length() > 0) + { + entry = entry.trim(); + if (hostMap.get(entry) == null) + { + hostMap.put(entry,entry); + } + } + } + + /* ------------------------------------------------------------ */ + /** + * Check the request hostname against white- and blacklist. + * + * @param host hostname to check + * @return true if hostname is allowed to be proxied + */ + public boolean validateDestination(String host) + { + if (_white.size()>0) + { + Object whiteObj = _white.getLazyMatches(host); + if (whiteObj == null) + { + return false; + } + } + + if (_black.size() > 0) + { + Object blackObj = _black.getLazyMatches(host); + if (blackObj != null) + { + return false; + } + } + + return true; + } +} diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/RequestLogHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/RequestLogHandler.java index 61d949da32..76ee4391a0 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/RequestLogHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/RequestLogHandler.java @@ -20,6 +20,7 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.server.AsyncContinuation; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.RequestLog; import org.eclipse.jetty.server.Response; @@ -47,9 +48,24 @@ public class RequestLogHandler extends HandlerWrapper public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { - super.handle(target, baseRequest, request, response); - if (DispatcherType.REQUEST.equals(baseRequest.getDispatcherType()) && _requestLog!=null) - _requestLog.log((Request)request, (Response)response); + AsyncContinuation continuation = baseRequest.getAsyncContinuation(); + if (!continuation.isInitial()) + { + baseRequest.setDispatchTime(System.currentTimeMillis()); + } + + try + { + super.handle(target, baseRequest, request, response); + } + finally + { + if (_requestLog != null && DispatcherType.REQUEST.equals(baseRequest.getDispatcherType())) + { + _requestLog.log(baseRequest, (Response)response); + } + + } } /* ------------------------------------------------------------ */ @@ -131,6 +147,5 @@ public class RequestLogHandler extends HandlerWrapper if (_requestLog!=null) _requestLog.stop(); } - } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java index 2bbc84f93d..51ba08e04a 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java @@ -4,11 +4,11 @@ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. -// The Eclipse Public License is available at +// The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php -// You may elect to redistribute this code under either of these licenses. +// You may elect to redistribute this code under either of these licenses. // ======================================================================== package org.eclipse.jetty.server.handler; @@ -16,7 +16,6 @@ package org.eclipse.jetty.server.handler; import java.io.IOException; import java.io.OutputStream; import java.net.MalformedURLException; - import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -33,7 +32,6 @@ import org.eclipse.jetty.server.HttpConnection; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Response; import org.eclipse.jetty.server.handler.ContextHandler.Context; -import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.resource.FileResource; @@ -42,12 +40,12 @@ import org.eclipse.jetty.util.resource.Resource; /* ------------------------------------------------------------ */ /** Resource Handler. - * + * * This handle will serve static content and handle If-Modified-Since headers. * No caching is done. * Requests that cannot be handled are let pass (Eg no 404's) - * - * + * + * * @org.apache.xbean.XBean */ public class ResourceHandler extends AbstractHandler @@ -90,7 +88,7 @@ public class ResourceHandler extends AbstractHandler /** * Set if resource aliases (eg symlink, 8.3 names, case insensitivity) are allowed. * Allowing aliases can significantly increase security vulnerabilities. - * If this handler is deployed inside a ContextHandler, then the + * If this handler is deployed inside a ContextHandler, then the * {@link ContextHandler#isAliases()} takes precedent. * @param aliases True if aliases are supported. */ @@ -124,13 +122,13 @@ public class ResourceHandler extends AbstractHandler { Context scontext = ContextHandler.getCurrentContext(); _context = (scontext==null?null:scontext.getContextHandler()); - + if (_context!=null) _aliases=_context.isAliases(); - + if (!_aliases && !FileResource.getCheckAliases()) throw new IllegalStateException("Alias checking disabled"); - + super.doStart(); } @@ -156,12 +154,12 @@ public class ResourceHandler extends AbstractHandler return _baseResource.toString(); } - + /* ------------------------------------------------------------ */ /** * @param base The resourceBase to set. */ - public void setBaseResource(Resource base) + public void setBaseResource(Resource base) { _baseResource=base; } @@ -170,7 +168,7 @@ public class ResourceHandler extends AbstractHandler /** * @param resourceBase The base resource as a string. */ - public void setResourceBase(String resourceBase) + public void setResourceBase(String resourceBase) { try { @@ -203,18 +201,18 @@ public class ResourceHandler extends AbstractHandler } /* ------------------------------------------------------------ */ - /* + /* */ public Resource getResource(String path) throws MalformedURLException { if (path==null || !path.startsWith("/")) throw new MalformedURLException(path); - + Resource base = _baseResource; if (base==null) { if (_context==null) - return null; + return null; base=_context.getBaseResource(); if (base==null) return null; @@ -229,7 +227,7 @@ public class ResourceHandler extends AbstractHandler { Log.ignore(e); } - + return null; } @@ -254,7 +252,7 @@ public class ResourceHandler extends AbstractHandler { _welcomeFiles=welcomeFiles; } - + /* ------------------------------------------------------------ */ protected Resource getWelcome(Resource directory) throws MalformedURLException, IOException { @@ -269,14 +267,14 @@ public class ResourceHandler extends AbstractHandler } /* ------------------------------------------------------------ */ - /* + /* * @see org.eclipse.jetty.server.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int) */ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { if (baseRequest.isHandled()) return; - + boolean skipContentBody = false; if(!HttpMethods.GET.equals(request.getMethod())) { @@ -284,9 +282,9 @@ public class ResourceHandler extends AbstractHandler return; skipContentBody = true; } - + Resource resource=getResource(request); - + if (resource==null || !resource.exists()) return; if (!_aliases && resource.getAlias()!=null) @@ -297,7 +295,7 @@ public class ResourceHandler extends AbstractHandler // We are going to server something baseRequest.setHandled(true); - + if (resource.isDirectory()) { if (!request.getPathInfo().endsWith(URIUtil.SLASH)) @@ -305,7 +303,7 @@ public class ResourceHandler extends AbstractHandler response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getRequestURI(),URIUtil.SLASH))); return; } - + Resource welcome=getWelcome(resource); if (welcome!=null && welcome.exists()) resource=welcome; @@ -316,7 +314,7 @@ public class ResourceHandler extends AbstractHandler return; } } - + // set some headers long last_modified=resource.lastModified(); if (last_modified>0) @@ -328,11 +326,11 @@ public class ResourceHandler extends AbstractHandler return; } } - + Buffer mime=_mimeTypes.getMimeByExtension(resource.toString()); if (mime==null) mime=_mimeTypes.getMimeByExtension(request.getPathInfo()); - + // set the headers doResponseHeaders(response,resource,mime!=null?mime.toString():null); response.setDateHeader(HttpHeaders.LAST_MODIFIED,last_modified); @@ -342,7 +340,7 @@ public class ResourceHandler extends AbstractHandler OutputStream out =null; try {out = response.getOutputStream();} catch(IllegalStateException e) {out = new WriterOutputStream(response.getWriter());} - + // See if a short direct method can be used? if (out instanceof HttpConnection.Output) { @@ -369,7 +367,7 @@ public class ResourceHandler extends AbstractHandler else response.sendError(HttpStatus.FORBIDDEN_403); } - + /* ------------------------------------------------------------ */ /** Set the response headers. * This method is called to set the response headers such as content type and content length. @@ -384,25 +382,25 @@ public class ResourceHandler extends AbstractHandler response.setContentType(mimeType); long length=resource.length(); - + if (response instanceof Response) { HttpFields fields = ((Response)response).getHttpFields(); if (length>0) fields.putLongField(HttpHeaders.CONTENT_LENGTH_BUFFER,length); - + if (_cacheControl!=null) fields.put(HttpHeaders.CACHE_CONTROL_BUFFER,_cacheControl); } else { if (length>0) - response.setHeader(HttpHeaders.CONTENT_LENGTH,TypeUtil.toString(length)); - + response.setHeader(HttpHeaders.CONTENT_LENGTH,Long.toString(length)); + if (_cacheControl!=null) response.setHeader(HttpHeaders.CACHE_CONTROL,_cacheControl.toString()); } - + } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ScopedHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ScopedHandler.java index ae3154c3b7..d4ff318c7a 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ScopedHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ScopedHandler.java @@ -134,7 +134,7 @@ public abstract class ScopedHandler extends HandlerWrapper throws IOException, ServletException { // this method has been manually inlined in several locations, but - // is called protected by an in(false), so your IDE can find those + // is called protected by an if(never()), so your IDE can find those // locations if this code is changed. if (_nextScope!=null) _nextScope.doScope(target,baseRequest,request, response); @@ -158,7 +158,7 @@ public abstract class ScopedHandler extends HandlerWrapper public final void nextHandle(String target, final Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // this method has been manually inlined in several locations, but - // is called protected by an in(false), so your IDE can find those + // is called protected by an if(never()), so your IDE can find those // locations if this code is changed. if (_nextScope!=null && _nextScope==_handler) _nextScope.doHandle(target,baseRequest,request, response); @@ -166,4 +166,10 @@ public abstract class ScopedHandler extends HandlerWrapper _handler.handle(target,baseRequest, request, response); } + /* ------------------------------------------------------------ */ + protected boolean never() + { + return false; + } + } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/nio/BlockingChannelConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/nio/BlockingChannelConnector.java index 1973e0a4d0..3927cb1caf 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/nio/BlockingChannelConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/nio/BlockingChannelConnector.java @@ -100,14 +100,8 @@ public class BlockingChannelConnector extends AbstractNIOConnector public void customize(EndPoint endpoint, Request request) throws IOException { - ConnectorEndPoint connection = (ConnectorEndPoint)endpoint; - if (connection._sotimeout!=_maxIdleTime) - { - connection._sotimeout=_maxIdleTime; - ((SocketChannel)endpoint.getTransport()).socket().setSoTimeout(_maxIdleTime); - } - super.customize(endpoint, request); + endpoint.setMaxIdleTime(_maxIdleTime); configure(((SocketChannel)endpoint.getTransport()).socket()); } @@ -130,12 +124,12 @@ public class BlockingChannelConnector extends AbstractNIOConnector int _sotimeout; ConnectorEndPoint(ByteChannel channel) + throws IOException { - super(channel); + super(channel,BlockingChannelConnector.this._maxIdleTime); _connection = new HttpConnection(BlockingChannelConnector.this,this,getServer()); } - /* ------------------------------------------------------------ */ /** Get the connection. * @return the connection diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/nio/SelectChannelConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/nio/SelectChannelConnector.java index 175bf6e176..40cb4f7fa0 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/nio/SelectChannelConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/nio/SelectChannelConnector.java @@ -4,11 +4,11 @@ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. -// The Eclipse Public License is available at +// The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php -// You may elect to redistribute this code under either of these licenses. +// You may elect to redistribute this code under either of these licenses. // ======================================================================== package org.eclipse.jetty.server.nio; @@ -20,6 +20,7 @@ import java.nio.channels.SelectionKey; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; +import org.eclipse.jetty.continuation.Continuation; import org.eclipse.jetty.io.ConnectedEndPoint; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; @@ -44,27 +45,25 @@ import org.eclipse.jetty.util.thread.Timeout.Task; * This connector is best used when there are a many connections that have idle periods. * </p> * <p> - * When used with {@link org.eclipse.jetty.util.ajax.Continuation}, threadless waits are supported. When - * a filter or servlet calls getEvent on a Continuation, a {@link org.eclipse.jetty.server.RetryRequest} - * runtime exception is thrown to allow the thread to exit the current request handling. Jetty will - * catch this exception and will not send a response to the client. Instead the thread is released - * and the Continuation is placed on the timer queue. If the Continuation timeout expires, or it's + * When used with {@link org.eclipse.jetty.continuation.Continuation}, threadless waits are supported. + * If a filter or servlet returns after calling {@link Continuation#suspend()} or when a + * runtime exception is thrown from a call to {@link Continuation#undispatch()}, Jetty will + * will not send a response to the client. Instead the thread is released and the Continuation is + * placed on the timer queue. If the Continuation timeout expires, or it's * resume method is called, then the request is again allocated a thread and the request is retried. * The limitation of this approach is that request content is not available on the retried request, * thus if possible it should be read after the continuation or saved as a request attribute or as the * associated object of the Continuation instance. * </p> - * - * @org.apache.xbean.XBean element="nioConnector" description="Creates an NIO based socket connector" - * - * * + * @org.apache.xbean.XBean element="nioConnector" description="Creates an NIO based socket connector" */ -public class SelectChannelConnector extends AbstractNIOConnector +public class SelectChannelConnector extends AbstractNIOConnector { protected ServerSocketChannel _acceptChannel; private int _lowResourcesConnections; private int _lowResourcesMaxIdleTime; + private int _localPort=-1; private final SelectorManager _manager = new SelectorManager() { @@ -99,7 +98,7 @@ public class SelectChannelConnector extends AbstractNIOConnector // TODO handle max connections and low resources connectionOpened(endpoint.getConnection()); } - + @Override protected void endPointUpgraded(ConnectedEndPoint endpoint, Connection oldConnection) { @@ -118,23 +117,23 @@ public class SelectChannelConnector extends AbstractNIOConnector return SelectChannelConnector.this.newEndPoint(channel,selectSet,sKey); } }; - + /* ------------------------------------------------------------------------------- */ /** * Constructor. - * + * */ public SelectChannelConnector() { } - + /* ------------------------------------------------------------ */ @Override public void accept(int acceptorID) throws IOException { _manager.doSelect(acceptorID); } - + /* ------------------------------------------------------------ */ public void close() throws IOException { @@ -154,9 +153,10 @@ public class SelectChannelConnector extends AbstractNIOConnector if (_acceptChannel != null) _acceptChannel.close(); _acceptChannel = null; + _localPort=-2; } } - + /* ------------------------------------------------------------------------------- */ @Override public void customize(EndPoint endpoint, Request request) throws IOException @@ -166,7 +166,7 @@ public class SelectChannelConnector extends AbstractNIOConnector request.setTimeStamp(cep.getSelectSet().getNow()); super.customize(endpoint, request); } - + /* ------------------------------------------------------------------------------- */ @Override public void persist(EndPoint endpoint) throws IOException @@ -186,9 +186,7 @@ public class SelectChannelConnector extends AbstractNIOConnector { synchronized(this) { - if (_acceptChannel==null || !_acceptChannel.isOpen()) - return -1; - return _acceptChannel.socket().getLocalPort(); + return _localPort; } } @@ -209,12 +207,13 @@ public class SelectChannelConnector extends AbstractNIOConnector InetSocketAddress addr = getHost()==null?new InetSocketAddress(getPort()):new InetSocketAddress(getHost(),getPort()); _acceptChannel.socket().bind(addr,getAcceptQueueSize()); - if (_acceptChannel.socket().getLocalPort()==-1) + _localPort=_acceptChannel.socket().getLocalPort(); + if (_localPort<=0) throw new IOException("Server channel not bound"); - + // Set to non blocking mode _acceptChannel.configureBlocking(false); - + } } } @@ -241,7 +240,7 @@ public class SelectChannelConnector extends AbstractNIOConnector * Set the number of connections, which if exceeded places this manager in low resources state. * This is not an exact measure as the connection count is averaged over the select sets. * @param lowResourcesConnections the number of connections - * @see {@link #setLowResourcesMaxIdleTime(int)} + * @see #setLowResourcesMaxIdleTime(int) */ public void setLowResourcesConnections(int lowResourcesConnections) { @@ -264,16 +263,16 @@ public class SelectChannelConnector extends AbstractNIOConnector * than {@link #getLowResourcesConnections()} connections. This allows the server to rapidly close idle connections * in order to gracefully handle high load situations. * @param lowResourcesMaxIdleTime the period in ms that a connection is allowed to be idle when resources are low. - * @see {@link #setMaxIdleTime(long)} + * @see #setMaxIdleTime(int) */ @Override public void setLowResourcesMaxIdleTime(int lowResourcesMaxIdleTime) { _lowResourcesMaxIdleTime=lowResourcesMaxIdleTime; - super.setLowResourcesMaxIdleTime(lowResourcesMaxIdleTime); + super.setLowResourcesMaxIdleTime(lowResourcesMaxIdleTime); } - + /* ------------------------------------------------------------ */ /* * @see org.eclipse.jetty.server.server.AbstractConnector#doStart() @@ -297,7 +296,7 @@ public class SelectChannelConnector extends AbstractNIOConnector */ @Override protected void doStop() throws Exception - { + { super.doStop(); } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java index f4a2ff6fff..0f7a305a0e 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java @@ -56,12 +56,9 @@ import org.eclipse.jetty.util.statistic.SampleStatistic; * SessionManager interface provides the majority of the handling required to * implement a SessionManager. Concrete implementations of SessionManager based * on AbstractSessionManager need only implement the newSession method to return - * a specialized version of the Session inner class that provides an attribute + * a specialised version of the Session inner class that provides an attribute * Map. * <p> - * If the property - * org.eclipse.jetty.servlet.AbstractSessionManager.23Notifications is set to - * true, the 2.3 servlet spec notification style will be used. * */ public abstract class AbstractSessionManager extends AbstractLifeCycle implements SessionManager @@ -97,7 +94,8 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement protected int _maxCookieAge=-1; protected int _refreshCookieAge; protected boolean _nodeIdInSessionId; - + protected boolean _checkingRemoteSessionIdEncoding; + protected String _sessionComment; public Set<SessionTrackingMode> _sessionTrackingModes; @@ -128,7 +126,7 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement ) ) { - HttpCookie cookie=getSessionCookie(session,_context.getContextPath(),secure); + HttpCookie cookie=getSessionCookie(session,_context==null?"/":(_context.getContextPath()),secure); s.cookieSet(); s.setIdChanged(false); return cookie; @@ -209,6 +207,10 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement // set up the sessionPath if it isn't already if (_sessionPath==null) _sessionPath=_context.getInitParameter(SessionManager.__SessionPathProperty); + + tmp=_context.getInitParameter(SessionManager.__CheckRemoteSessionEncoding); + if (tmp!=null) + _checkingRemoteSessionIdEncoding=Boolean.parseBoolean(tmp); } super.doStart(); @@ -226,6 +228,15 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement } /* ------------------------------------------------------------ */ + /** + * @return Returns the httpOnly. + */ + public boolean getHttpOnly() + { + return _httpOnly; + } + + /* ------------------------------------------------------------ */ public HttpSession getHttpSession(String nodeId) { String cluster_id = getIdManager().getClusterId(nodeId); @@ -261,7 +272,7 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement /* ------------------------------------------------------------ */ /** - * @see getSessionsMax() + * @see #getSessionsMax() */ @Deprecated public int getMaxSessions() @@ -299,7 +310,7 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement /* ------------------------------------------------------------ */ /** - * @see getSessionsMin() + * @deprecated always returns 0. no replacement available. */ @Deprecated public int getMinSessions() @@ -318,16 +329,35 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement { if (isUsingCookies()) { + String sessionPath = (_sessionPath==null) ? contextPath : _sessionPath; + sessionPath = (sessionPath==null||sessionPath.length()==0) ? "/" : sessionPath; String id = getNodeId(session); - HttpCookie cookie=new HttpCookie( - _sessionCookie, - id, - _sessionDomain, - (contextPath==null||contextPath.length()==0)?"/":contextPath, - _cookieConfig.getMaxAge(), - _cookieConfig.isHttpOnly(), - requestIsSecure&&_cookieConfig.isSecure()); - + HttpCookie cookie = null; + if (_sessionComment == null) + { + cookie = new HttpCookie( + _sessionCookie, + id, + _sessionDomain, + sessionPath, + _cookieConfig.getMaxAge(), + _cookieConfig.isHttpOnly(), + _cookieConfig.isSecure()); + } + else + { + cookie = new HttpCookie( + _sessionCookie, + id, + _sessionDomain, + sessionPath, + _cookieConfig.getMaxAge(), + _cookieConfig.isHttpOnly(), + _cookieConfig.isSecure(), + _sessionComment, + 1); + } + return cookie; } return null; @@ -344,7 +374,7 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement /* ------------------------------------------------------------ */ /** - * @deprecated. Need to review if it is needed. + * @deprecated Need to review if it is needed. */ public abstract Map getSessionMap(); @@ -410,7 +440,7 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement /* ------------------------------------------------------------ */ /** - * @see statsReset() + * @see #statsReset() */ @Deprecated public void resetStats() @@ -530,7 +560,7 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement /** * Create a new session instance * @param request - * @return + * @return the new session */ protected abstract Session newSession(HttpServletRequest request); @@ -671,7 +701,7 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement @Override public String getComment() { - return null; + return _sessionComment; } @Override @@ -713,7 +743,7 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement @Override public void setComment(String comment) { - // TODO + _sessionComment = comment; } @Override @@ -784,6 +814,24 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement /* ------------------------------------------------------------ */ /** + * @see org.eclipse.jetty.server.SessionManager#isCheckingRemoteSessionIdEncoding() + */ + public boolean isCheckingRemoteSessionIdEncoding() + { + return _checkingRemoteSessionIdEncoding; + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.server.SessionManager#setCheckingRemoteSessionIdEncoding(boolean) + */ + public void setCheckingRemoteSessionIdEncoding(boolean remote) + { + _checkingRemoteSessionIdEncoding=remote; + } + + /* ------------------------------------------------------------ */ + /** * Null returning implementation of HttpSessionContext * * @@ -830,12 +878,10 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement } /* ------------------------------------------------------------ */ - /* ------------------------------------------------------------ */ - /* ------------------------------------------------------------ */ /** * * <p> - * Implements {@link javax.servlet.HttpSession} from the {@link javax.servlet} package. + * Implements {@link javax.servlet.http.HttpSession} from the <code>javax.servlet</code> package. * </p> * * @@ -1047,10 +1093,13 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement // Notify listeners and unbind values synchronized (this) { - if (_requests<=0) - doInvalidate(); - else - _doInvalidate=true; + if (!_invalid) + { + if (_requests<=0) + doInvalidate(); + else + _doInvalidate=true; + } } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionIdManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionIdManager.java index bf227dd885..830cbfc7c5 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionIdManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionIdManager.java @@ -48,6 +48,7 @@ public class HashSessionIdManager extends AbstractSessionIdManager /* ------------------------------------------------------------ */ /** Get the session ID with any worker ID. * + * @param clusterId * @param request * @return sessionId plus any worker ID. */ @@ -66,7 +67,7 @@ public class HashSessionIdManager extends AbstractSessionIdManager /* ------------------------------------------------------------ */ /** Get the session ID without any worker ID. * - * @param request + * @param nodeId the node id * @return sessionId without any worker ID. */ public String getClusterId(String nodeId) @@ -94,8 +95,8 @@ public class HashSessionIdManager extends AbstractSessionIdManager } /* ------------------------------------------------------------ */ - /* - * @see org.eclipse.jetty.server.server.SessionManager.MetaManager#idInUse(java.lang.String) + /** + * @see SessionIdManager#idInUse(String) */ public boolean idInUse(String id) { @@ -103,8 +104,8 @@ public class HashSessionIdManager extends AbstractSessionIdManager } /* ------------------------------------------------------------ */ - /* - * @see org.eclipse.jetty.server.server.SessionManager.MetaManager#addSession(javax.servlet.http.HttpSession) + /** + * @see SessionIdManager#addSession(HttpSession) */ public void addSession(HttpSession session) { @@ -112,8 +113,8 @@ public class HashSessionIdManager extends AbstractSessionIdManager } /* ------------------------------------------------------------ */ - /* - * @see org.eclipse.jetty.server.server.SessionManager.MetaManager#addSession(javax.servlet.http.HttpSession) + /** + * @see SessionIdManager#removeSession(HttpSession) */ public void removeSession(HttpSession session) { @@ -121,8 +122,8 @@ public class HashSessionIdManager extends AbstractSessionIdManager } /* ------------------------------------------------------------ */ - /* - * @see org.eclipse.jetty.server.server.SessionManager.MetaManager#invalidateAll(java.lang.String) + /** + * @see SessionIdManager#invalidateAll(String) */ public void invalidateAll(String id) { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java index 50633acbc4..5452362a4d 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java @@ -112,7 +112,7 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager * depending on the way the db stores identifiers. * * @param identifier - * @return + * @return the converted identifier */ public String convertIdentifier (String identifier) { @@ -387,8 +387,6 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager * * Makes necessary database tables and starts a Session * scavenger thread. - * - * @see org.eclipse.jetty.server.session.AbstractSessionIdManager#doStart() */ @Override public void doStart() @@ -410,8 +408,6 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager /** * Stop the scavenger. - * - * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop() */ @Override public void doStop () @@ -431,7 +427,7 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager /** * Get a connection from the driver or datasource. * - * @return + * @return the connection for the datasource * @throws SQLException */ protected Connection getConnection () 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 0ddfd131d8..ecb226fe9b 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 @@ -52,15 +52,15 @@ import org.eclipse.jetty.util.log.Log; * contextPath (of the context owning the session) * sessionId (unique in a context) * lastNode (name of node last handled session) - * accessTime (time in ms session was accessed) - * lastAccessTime (previous time in ms session was accessed) - * createTime (time in ms session created) - * cookieTime (time in ms session cookie created) - * lastSavedTime (last time in ms session access times were saved) - * expiryTime (time in ms that the session is due to expire) + * accessTime (time in milliseconds session was accessed) + * lastAccessTime (previous time in milliseconds session was accessed) + * createTime (time in milliseconds session created) + * cookieTime (time in milliseconds session cookie created) + * lastSavedTime (last time in milliseconds session access times were saved) + * expiryTime (time in milliseconds that the session is due to expire) * map (attribute map) * - * As an optimisation, to prevent thrashing the database, we do not persist + * As an optimization, to prevent thrashing the database, we do not persist * the accessTime and lastAccessTime every time the session is accessed. Rather, * we write it out every so often. The frequency is controlled by the saveIntervalSec * field. @@ -274,7 +274,7 @@ public class JDBCSessionManager extends AbstractSessionManager /** * Session restored in database. - * @param row + * @param data */ protected Session (SessionData data) { @@ -442,7 +442,7 @@ public class JDBCSessionManager extends AbstractSessionManager * This could be used eg with a JMS backplane to notify nodes * that the session has changed and to delete the session from * the node's cache, and re-read it from the database. - * @param idInCluster + * @param session */ public void cacheInvalidate (Session session) { @@ -540,8 +540,6 @@ public class JDBCSessionManager extends AbstractSessionManager /** * Get all the sessions as a map of id to Session. - * - * @see org.eclipse.jetty.server.session.AbstractSessionManager#getSessionMap() */ @Override public Map getSessionMap() @@ -817,7 +815,7 @@ public class JDBCSessionManager extends AbstractSessionManager /** * Load a session from the database * @param id - * @return + * @return the session data that was loaded * @throws Exception */ protected SessionData loadSession (String id, String canonicalContextPath, String vhost) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java index 746a2999d8..0272e8c11f 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java @@ -35,9 +35,6 @@ import org.eclipse.jetty.util.log.Log; /* ------------------------------------------------------------ */ /** SessionHandler. - * - * - * */ public class SessionHandler extends ScopedHandler { @@ -224,8 +221,7 @@ public class SessionHandler extends ScopedHandler throws IOException, ServletException { // start manual inline of nextHandle(target,baseRequest,request,response); - //noinspection ConstantIfStatement - if (false) + if (never()) nextHandle(target,baseRequest,request,response); else if (_nextScope!=null && _nextScope==_handler) _nextScope.doHandle(target,baseRequest,request, response); @@ -236,8 +232,8 @@ public class SessionHandler extends ScopedHandler /* ------------------------------------------------------------ */ /** Look for a requested session ID in cookies and URI parameters + * @param baseRequest * @param request - * @param dispatch */ protected void setRequestedId(Request baseRequest, HttpServletRequest request) { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslCertificates.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslCertificates.java index 94803ab856..25b1ac7dda 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslCertificates.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslCertificates.java @@ -10,9 +10,9 @@ import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import org.eclipse.jetty.http.HttpSchemes; -import org.eclipse.jetty.http.ssl.SslSelectChannelEndPoint; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.bio.SocketEndPoint; +import org.eclipse.jetty.io.nio.SslSelectChannelEndPoint; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.log.Log; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslSelectChannelConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslSelectChannelConnector.java index f40159f08a..71bb96de93 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslSelectChannelConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslSelectChannelConnector.java @@ -13,14 +13,12 @@ package org.eclipse.jetty.server.ssl; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; import java.security.KeyStore; import java.security.SecureRandom; -import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -29,7 +27,6 @@ import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import javax.net.ssl.TrustManager; @@ -38,7 +35,6 @@ import javax.net.ssl.TrustManagerFactory; import org.eclipse.jetty.http.HttpParser; import org.eclipse.jetty.http.HttpSchemes; import org.eclipse.jetty.http.security.Password; -import org.eclipse.jetty.http.ssl.SslSelectChannelEndPoint; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.Buffers; import org.eclipse.jetty.io.Connection; @@ -47,11 +43,11 @@ import org.eclipse.jetty.io.ThreadLocalBuffers; import org.eclipse.jetty.io.bio.SocketEndPoint; import org.eclipse.jetty.io.nio.DirectNIOBuffer; import org.eclipse.jetty.io.nio.SelectChannelEndPoint; +import org.eclipse.jetty.io.nio.SslSelectChannelEndPoint; import org.eclipse.jetty.io.nio.SelectorManager.SelectSet; import org.eclipse.jetty.server.HttpConnection; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.nio.SelectChannelConnector; -import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.resource.Resource; @@ -471,7 +467,6 @@ public class SslSelectChannelConnector extends SelectChannelConnector implements /* ------------------------------------------------------------ */ /** - * @throws Exception * @see org.eclipse.jetty.server.ssl.SslConnector#setSslContext(javax.net.ssl.SSLContext) */ public SSLContext getSslContext() diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslSocketConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslSocketConnector.java index 0d7fb53bff..ca99861cd9 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslSocketConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslSocketConnector.java @@ -137,7 +137,7 @@ public class SslSocketConnector extends SocketConnector implements SslConnector Socket socket = _serverSocket.accept(); configure(socket); - ConnectorEndPoint connection=new SslConnection(socket); + ConnectorEndPoint connection=new SslConnectorEndPoint(socket); connection.dispatch(); } @@ -205,14 +205,10 @@ public class SslSocketConnector extends SocketConnector implements SslConnector try { if (keystorePath!=null) - { keystoreInputStream = Resource.newResource(keystorePath).getInputStream(); - keystore=KeyStore.getInstance(keystoreType); - keystore.load(keystoreInputStream,keystorePassword==null?null:keystorePassword.toString().toCharArray()); - return keystore; - } - - return null; + keystore=KeyStore.getInstance(keystoreType); + keystore.load(keystoreInputStream,keystorePassword==null?null:keystorePassword.toString().toCharArray()); + return keystore; } finally { @@ -363,13 +359,13 @@ public class SslSocketConnector extends SocketConnector implements SslConnector /* ------------------------------------------------------------ */ /** - * @param addr The {@link SocketAddress address} that this server should listen on + * @param host The host name that this server should listen on + * @param port the port that this server should listen on * @param backlog See {@link ServerSocket#bind(java.net.SocketAddress, int)} * @return A new {@link ServerSocket socket object} bound to the supplied address with all other * settings as per the current configuration of this connector. - * @see #setWantClientAuth - * @see #setNeedClientAuth - * @see #setCipherSuites + * @see #setWantClientAuth(boolean) + * @see #setNeedClientAuth(boolean) * @exception IOException */ @@ -411,10 +407,10 @@ public class SslSocketConnector extends SocketConnector implements SslConnector excludedCSList = new ArrayList<String>(); } String[] enabledCipherSuites = socket.getEnabledCipherSuites(); - List<String> enabledCSList=Arrays.asList(enabledCipherSuites); + List<String> enabledCSList = new ArrayList<String>(Arrays.asList(enabledCipherSuites)); String[] supportedCipherSuites = socket.getSupportedCipherSuites(); - List<String> supportedCSList=Arrays.asList(supportedCipherSuites); + List<String> supportedCSList = Arrays.asList(supportedCipherSuites); for (String cipherName : includedCSList) { @@ -432,7 +428,7 @@ public class SslSocketConnector extends SocketConnector implements SslConnector enabledCSList.remove(cipherName); } } - enabledCipherSuites=enabledCSList.toArray(new String[0]); + enabledCipherSuites = enabledCSList.toArray(new String[enabledCSList.size()]); socket.setEnabledCipherSuites(enabledCipherSuites); } @@ -557,7 +553,6 @@ public class SslSocketConnector extends SocketConnector implements SslConnector /* ------------------------------------------------------------ */ /** - * @throws Exception * @see org.eclipse.jetty.server.ssl.SslConnector#setSslContext(javax.net.ssl.SSLContext) */ public SSLContext getSslContext() @@ -577,10 +572,10 @@ public class SslSocketConnector extends SocketConnector implements SslConnector /* ------------------------------------------------------------ */ /** - * Set the value of the _wantClientAuth property. This property is used when - * {@link #newServerSocket(SocketAddress, int) opening server sockets}. + * Set the value of the _wantClientAuth property. This property is used + * internally when opening server sockets. * - * @param wantClientAuth true iff we want client certificate authentication. + * @param wantClientAuth true if we want client certificate authentication. * @see SSLServerSocket#setWantClientAuth */ public void setWantClientAuth(boolean wantClientAuth) @@ -607,14 +602,19 @@ public class SslSocketConnector extends SocketConnector implements SslConnector } /* ------------------------------------------------------------ */ - public class SslConnection extends ConnectorEndPoint + public class SslConnectorEndPoint extends ConnectorEndPoint { - public SslConnection(Socket socket) throws IOException + public SslConnectorEndPoint(Socket socket) throws IOException { super(socket); } @Override + public void shutdownOutput() throws IOException + { + } + + @Override public void run() { try @@ -667,7 +667,8 @@ public class SslSocketConnector extends SocketConnector implements SslConnector /* ------------------------------------------------------------ */ /** * Unsupported. - * @see org.eclipse.jetty.server.ssl.SslConnector#getAlgorithm() + * + * TODO: we should remove this as it is no longer an overridden method from SslConnector (like it was in the past) */ public String getAlgorithm() { @@ -677,7 +678,8 @@ public class SslSocketConnector extends SocketConnector implements SslConnector /* ------------------------------------------------------------ */ /** * Unsupported. - * @see org.eclipse.jetty.server.ssl.SslConnector#setAlgorithm(java.lang.String) + * + * TODO: we should remove this as it is no longer an overridden method from SslConnector (like it was in the past) */ public void setAlgorithm(String algorithm) { |