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 037a6e3fb81..9cd0753096b 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.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
@@ -41,13 +40,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.RuntimeIOException;
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;
@@ -68,7 +66,7 @@ import org.eclipse.jetty.util.thread.Timeout;
* with the connection via the parser and/or generator.
*
*
- * 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.
*
@@ -76,12 +74,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.
*
*
@@ -378,7 +376,7 @@ 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;
@@ -396,9 +394,9 @@ public class HttpConnection implements Connection
{
if (_request._async.isAsync())
{
- // TODO - handle the case of input being read for a
+ // TODO - handle the case of input being read for a
// suspended request.
-
+
Log.debug("async request",_request);
if (!_request._async.isComplete())
handleRequest();
@@ -470,7 +468,7 @@ public class HttpConnection implements Connection
// 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)
{
@@ -490,7 +488,7 @@ public class HttpConnection implements Connection
if (more_in_buffer)
{
reset(false);
- more_in_buffer = _parser.isMoreInBuffer() || _endp.isBufferingInput();
+ more_in_buffer = _parser.isMoreInBuffer() || _endp.isBufferingInput();
}
else
reset(true);
@@ -503,7 +501,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);
}
}
@@ -628,7 +626,7 @@ public class HttpConnection implements Connection
Log.debug(e);
_request.setHandled(true);
_generator.sendError(info==null?400:500, null, null, true);
-
+
}
finally
{
@@ -839,18 +837,17 @@ public class HttpConnection implements Connection
switch (HttpMethods.CACHE.getOrdinal(method))
{
case HttpMethods.CONNECT_ORDINAL:
- // _uri.parseConnect(uri.array(), uri.getIndex(), uri.length());
- _uri.parse("http://"+uri+"/");
+ _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)
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 93bd8b3bc1d..29b685e2369 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;
@@ -28,7 +28,6 @@ import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
-
import javax.servlet.RequestDispatcher;
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
@@ -44,6 +43,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpException;
+import org.eclipse.jetty.http.HttpMethods;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.io.Buffer;
import org.eclipse.jetty.server.Dispatcher;
@@ -64,31 +64,31 @@ 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.
- *
+ *
*
* 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"
*
- *
+ *
*
*/
public class ContextHandler extends ScopedHandler implements Attributes, Server.Graceful
{
private static final ThreadLocal __context=new ThreadLocal();
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()
@@ -97,14 +97,14 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
}
protected Context _scontext;
-
+
private AttributesMap _attributes;
private AttributesMap _contextAttributes;
private ClassLoader _classLoader;
private String _contextPath="/";
private Map _initParams;
private String _displayName;
- private Resource _baseResource;
+ private Resource _baseResource;
private MimeTypes _mimeTypes;
private Map _localeEncodingMap;
private String[] _welcomeFiles;
@@ -125,15 +125,15 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
private Map _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()
{
@@ -142,10 +142,10 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
_attributes=new AttributesMap();
_initParams=new HashMap();
}
-
+
/* ------------------------------------------------------------ */
/**
- *
+ *
*/
protected ContextHandler(Context context)
{
@@ -154,20 +154,20 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
_attributes=new AttributesMap();
_initParams=new HashMap();
}
-
+
/* ------------------------------------------------------------ */
/**
- *
+ *
*/
public ContextHandler(String contextPath)
{
this();
setContextPath(contextPath);
}
-
+
/* ------------------------------------------------------------ */
/**
- *
+ *
*/
public ContextHandler(HandlerContainer parent, String contextPath)
{
@@ -184,7 +184,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
{
return _scontext;
}
-
+
/* ------------------------------------------------------------ */
/**
* @return the allowNullPathInfo true if /context is not redirected to /context/
@@ -212,13 +212,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);
}
/* ------------------------------------------------------------ */
@@ -238,8 +238,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++ )
@@ -265,8 +265,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)
@@ -293,15 +293,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.
*/
@@ -312,9 +312,9 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
else
_connectors= new HashSet(Arrays.asList(connectors));
}
-
+
/* ------------------------------------------------------------ */
- /*
+ /*
* @see javax.servlet.ServletContext#getAttribute(java.lang.String)
*/
public Object getAttribute(String name)
@@ -323,7 +323,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
}
/* ------------------------------------------------------------ */
- /*
+ /*
* @see javax.servlet.ServletContext#getAttributeNames()
*/
@SuppressWarnings("unchecked")
@@ -331,7 +331,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
{
return AttributesMap.getAttributeNamesCopy(_attributes);
}
-
+
/* ------------------------------------------------------------ */
/**
* @return Returns the attributes.
@@ -340,7 +340,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
{
return _attributes;
}
-
+
/* ------------------------------------------------------------ */
/**
* @return Returns the classLoader.
@@ -393,9 +393,9 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
{
return _contextPath;
}
-
+
/* ------------------------------------------------------------ */
- /*
+ /*
* @see javax.servlet.ServletContext#getInitParameter(java.lang.String)
*/
public String getInitParameter(String name)
@@ -404,7 +404,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
}
/* ------------------------------------------------------------ */
- /*
+ /*
* @see javax.servlet.ServletContext#getInitParameterNames()
*/
@SuppressWarnings("unchecked")
@@ -412,7 +412,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
{
return Collections.enumeration(_initParams.keySet());
}
-
+
/* ------------------------------------------------------------ */
/**
* @return Returns the initParams.
@@ -423,7 +423,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
}
/* ------------------------------------------------------------ */
- /*
+ /*
* @see javax.servlet.ServletContext#getServletContextName()
*/
public String getDisplayName()
@@ -436,7 +436,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
{
return _eventListeners;
}
-
+
/* ------------------------------------------------------------ */
/**
* Set the context event listeners.
@@ -451,26 +451,26 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
_contextAttributeListeners=null;
_requestListeners=null;
_requestAttributeListeners=null;
-
+
_eventListeners=eventListeners;
-
+
for (int i=0; eventListeners!=null && i0)
{
String vhost = normalizeHostname( baseRequest.getServerName());
boolean match=false;
-
+
// TODO non-linear lookup
for (int i=0;!match && i<_vhosts.length;i++)
{
@@ -750,7 +750,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
if (!match)
return false;
}
-
+
// Check the connector
if (_connectors!=null && _connectors.size()>0)
{
@@ -758,8 +758,8 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
if (connector==null || !_connectors.contains(connector))
return false;
}
-
-
+
+
if (target.startsWith(_contextPath))
{
if (_contextPath.length()==target.length() && _contextPath.length()>1 &&!_allowNullPathInfo)
@@ -768,20 +768,23 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
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
{
+ if (HttpMethods.CONNECT.equalsIgnoreCase(baseRequest.getMethod()) && "/".equals(_contextPath))
+ return true;
+
// Not for this context!
return false;
}
-
+
return true;
- }
-
+ }
+
/* ------------------------------------------------------------ */
@@ -790,7 +793,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;
@@ -800,9 +803,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)
{
@@ -813,7 +816,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)
@@ -840,13 +843,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("/"))
@@ -858,7 +861,7 @@ 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)
@@ -867,7 +870,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
_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)
}
@@ -880,16 +883,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)
@@ -919,10 +922,10 @@ 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)
@@ -950,7 +953,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
for(int i=0;i0;)
@@ -959,12 +962,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
@@ -976,7 +979,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
old_classloader=current_thread.getContextClassLoader();
current_thread.setContextClassLoader(_classLoader);
}
-
+
runnable.run();
}
finally
@@ -997,12 +1000,12 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
*/
/* ------------------------------------------------------------ */
protected boolean isProtectedTarget(String target)
- {
+ {
return false;
}
/* ------------------------------------------------------------ */
- /*
+ /*
* @see javax.servlet.ServletContext#removeAttribute(java.lang.String)
*/
public void removeAttribute(String name)
@@ -1014,7 +1017,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
/* ------------------------------------------------------------ */
/* 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)
*/
@@ -1023,7 +1026,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
setManagedAttribute(name,value);
_attributes.setAttribute(name,value);
}
-
+
/* ------------------------------------------------------------ */
/**
* @param attributes The attributes to set.
@@ -1068,7 +1071,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
/* ------------------------------------------------------------ */
private void setManagedAttribute(String name, Object value)
- {
+ {
if (_managedAttributes!=null && _managedAttributes.containsKey(name))
{
Object old =_managedAttributes.put(name,value);
@@ -1081,7 +1084,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
}
}
}
-
+
/* ------------------------------------------------------------ */
/**
* @param classLoader The classLoader to set.
@@ -1090,7 +1093,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
{
_classLoader = classLoader;
}
-
+
/* ------------------------------------------------------------ */
/**
* @param contextPath The _contextPath to set.
@@ -1100,7 +1103,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);
@@ -1108,7 +1111,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
((ContextHandlerCollection)contextCollections[h]).mapContexts();
}
}
-
+
/* ------------------------------------------------------------ */
/**
* @param initParams The initParams to set.
@@ -1119,7 +1122,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
return;
_initParams = new HashMap(initParams);
}
-
+
/* ------------------------------------------------------------ */
/**
* @param servletContextName The servletContextName to set.
@@ -1128,7 +1131,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
{
_displayName = servletContextName;
}
-
+
/* ------------------------------------------------------------ */
/**
* @return Returns the resourceBase.
@@ -1150,12 +1153,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;
}
@@ -1164,7 +1167,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
{
@@ -1203,7 +1206,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
{
return _mimeTypes;
}
-
+
/* ------------------------------------------------------------ */
/**
* @param mimeTypes The mimeTypes to set.
@@ -1216,7 +1219,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
/* ------------------------------------------------------------ */
/**
*/
- public void setWelcomeFiles(String[] files)
+ public void setWelcomeFiles(String[] files)
{
_welcomeFiles=files;
}
@@ -1227,7 +1230,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
* @see The Servlet Specification
* @see #setWelcomeFiles
*/
- public String[] getWelcomeFiles()
+ public String[] getWelcomeFiles()
{
return _welcomeFiles;
}
@@ -1253,13 +1256,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)
{
@@ -1289,7 +1292,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
@Override
public String toString()
{
-
+
return super.toString()+"@"+Integer.toHexString(hashCode())+getContextPath()+","+getBaseResource();
}
@@ -1299,13 +1302,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)
@@ -1314,12 +1317,12 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
_localeEncodingMap=new HashMap();
_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 Locale
value
* @return a String
representing the character encoding for
@@ -1334,15 +1337,15 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
encoding = _localeEncodingMap.get(locale.getLanguage());
return encoding;
}
-
+
/* ------------------------------------------------------------ */
- /*
+ /*
*/
public Resource getResource(String path) throws MalformedURLException
{
if (path==null || !path.startsWith(URIUtil.SLASH))
throw new MalformedURLException(path);
-
+
if (_baseResource==null)
return null;
@@ -1350,7 +1353,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())
@@ -1359,20 +1362,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
@@ -1382,7 +1385,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
@@ -1391,20 +1394,20 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
}
/* ------------------------------------------------------------ */
- /*
+ /*
*/
public Set 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)
{
@@ -1412,7 +1415,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
for(int i=0;i
* 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 org.eclipse.jetty.servlet.ServletContextHandler.Context}.
*
- *
+ *
*
*/
public class Context implements ServletContext
@@ -1460,7 +1463,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
}
/* ------------------------------------------------------------ */
- /*
+ /*
* @see javax.servlet.ServletContext#getContext(java.lang.String)
*/
public ServletContext getContext(String uripath)
@@ -1481,14 +1484,14 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
context=ch;
}
}
-
+
if (context!=null)
return context._scontext;
return null;
}
/* ------------------------------------------------------------ */
- /*
+ /*
* @see javax.servlet.ServletContext#getMajorVersion()
*/
public int getMajorVersion()
@@ -1497,7 +1500,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
}
/* ------------------------------------------------------------ */
- /*
+ /*
* @see javax.servlet.ServletContext#getMimeType(java.lang.String)
*/
public String getMimeType(String file)
@@ -1511,7 +1514,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
}
/* ------------------------------------------------------------ */
- /*
+ /*
* @see javax.servlet.ServletContext#getMinorVersion()
*/
public int getMinorVersion()
@@ -1520,7 +1523,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
}
/* ------------------------------------------------------------ */
- /*
+ /*
* @see javax.servlet.ServletContext#getNamedDispatcher(java.lang.String)
*/
public RequestDispatcher getNamedDispatcher(String name)
@@ -1528,7 +1531,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
return null;
}
/* ------------------------------------------------------------ */
- /*
+ /*
* @see javax.servlet.ServletContext#getRequestDispatcher(java.lang.String)
*/
public RequestDispatcher getRequestDispatcher(String uriInContext)
@@ -1538,7 +1541,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
if (!uriInContext.startsWith("/"))
return null;
-
+
try
{
String query=null;
@@ -1563,7 +1566,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
return null;
}
/* ------------------------------------------------------------ */
- /*
+ /*
* @see javax.servlet.ServletContext#getRealPath(java.lang.String)
*/
public String getRealPath(String path)
@@ -1574,7 +1577,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);
@@ -1589,7 +1592,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
{
Log.ignore(e);
}
-
+
return null;
}
@@ -1601,9 +1604,9 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
return resource.getURL();
return null;
}
-
+
/* ------------------------------------------------------------ */
- /*
+ /*
* @see javax.servlet.ServletContext#getResourceAsStream(java.lang.String)
*/
public InputStream getResourceAsStream(String path)
@@ -1623,16 +1626,16 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
}
/* ------------------------------------------------------------ */
- /*
+ /*
* @see javax.servlet.ServletContext#getResourcePaths(java.lang.String)
*/
public Set getResourcePaths(String path)
- {
+ {
return ContextHandler.this.getResourcePaths(path);
}
/* ------------------------------------------------------------ */
- /*
+ /*
* @see javax.servlet.ServletContext#getServerInfo()
*/
public String getServerInfo()
@@ -1641,7 +1644,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
}
/* ------------------------------------------------------------ */
- /*
+ /*
* @see javax.servlet.ServletContext#getServlet(java.lang.String)
*/
public Servlet getServlet(String name) throws ServletException
@@ -1650,7 +1653,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
}
/* ------------------------------------------------------------ */
- /*
+ /*
* @see javax.servlet.ServletContext#getServletNames()
*/
@SuppressWarnings("unchecked")
@@ -1660,7 +1663,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
}
/* ------------------------------------------------------------ */
- /*
+ /*
* @see javax.servlet.ServletContext#getServlets()
*/
@SuppressWarnings("unchecked")
@@ -1670,7 +1673,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
}
/* ------------------------------------------------------------ */
- /*
+ /*
* @see javax.servlet.ServletContext#log(java.lang.Exception, java.lang.String)
*/
public void log(Exception exception, String msg)
@@ -1679,7 +1682,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
}
/* ------------------------------------------------------------ */
- /*
+ /*
* @see javax.servlet.ServletContext#log(java.lang.String)
*/
public void log(String msg)
@@ -1688,7 +1691,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
}
/* ------------------------------------------------------------ */
- /*
+ /*
* @see javax.servlet.ServletContext#log(java.lang.String, java.lang.Throwable)
*/
public void log(String message, Throwable throwable)
@@ -1697,7 +1700,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
}
/* ------------------------------------------------------------ */
- /*
+ /*
* @see javax.servlet.ServletContext#getInitParameter(java.lang.String)
*/
public String getInitParameter(String name)
@@ -1706,7 +1709,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
}
/* ------------------------------------------------------------ */
- /*
+ /*
* @see javax.servlet.ServletContext#getInitParameterNames()
*/
@SuppressWarnings("unchecked")
@@ -1716,7 +1719,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
}
/* ------------------------------------------------------------ */
- /*
+ /*
* @see javax.servlet.ServletContext#getAttribute(java.lang.String)
*/
public synchronized Object getAttribute(String name)
@@ -1728,7 +1731,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
}
/* ------------------------------------------------------------ */
- /*
+ /*
* @see javax.servlet.ServletContext#getAttributeNames()
*/
@SuppressWarnings("unchecked")
@@ -1744,17 +1747,17 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
Enumeration 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)
*/
public synchronized void setAttribute(String name, Object value)
{
-
+
if (_contextAttributes==null)
{
// Set it on the handler
@@ -1764,12 +1767,12 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
setManagedAttribute(name,value);
Object old_value=_contextAttributes.getAttribute(name);
-
+
if (value==null)
_contextAttributes.removeAttribute(name);
else
_contextAttributes.setAttribute(name,value);
-
+
if (_contextAttributeListeners!=null)
{
ServletContextAttributeEvent event =
@@ -1778,7 +1781,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
for(int i=0;iHandles a CONNECT request.
+ * CONNECT requests may have authentication headers such as Proxy-Authorization
+ * that authenticate the client with the proxy.
+ * @param request the http request
+ * @param response the http response
+ * @param connectURI the CONNECT URI
+ * @throws ServletException if an application error occurs
+ * @throws IOException if an I/O error occurs
+ */
+ protected void handle(HttpServletRequest request, HttpServletResponse response, String connectURI) throws ServletException, IOException
+ {
+ boolean proceed = handleAuthentication(request, response, connectURI);
+ if (!proceed)
+ return;
+
+ String host = connectURI;
+ int port = 80;
+ boolean secure = false;
+ int colon = connectURI.indexOf(':');
+ if (colon > 0)
+ {
+ host = connectURI.substring(0, colon);
+ port = Integer.parseInt(connectURI.substring(colon + 1));
+ secure = isTunnelSecure(host, port);
+ }
+
+ setupTunnel(request, response, host, port, secure);
+ }
+
+ protected boolean isTunnelSecure(String host, int port)
+ {
+ return port == 443;
+ }
+
+ protected boolean handleAuthentication(HttpServletRequest request, HttpServletResponse response, String connectURI) throws ServletException, IOException
+ {
+ return true;
+ }
+
+ protected void setupTunnel(HttpServletRequest request, HttpServletResponse response, String host, int port, boolean secure) throws IOException
+ {
+ SocketChannel channel = connect(request, host, port);
+ channel.configureBlocking(false);
+
+ // 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();
+ }
+ }
+
+ // Setup connections, before registering the channel to avoid races
+ // where the server sends data before the connections are set up
+ ProxyToServerConnection proxyToServer = new ProxyToServerConnection(secure, buffer);
+ ClientToProxyConnection clientToProxy = new ClientToProxyConnection(channel, httpConnection.getEndPoint(), httpConnection.getTimeStamp());
+ clientToProxy.setConnection(proxyToServer);
+ proxyToServer.setConnection(clientToProxy);
+
+ upgradeConnection(request, response, clientToProxy);
+ }
+
+ protected SocketChannel connect(HttpServletRequest request, String host, int port) throws IOException
+ {
+ logger.info("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.info("Established connection to {}:{}", host, port);
+ return channel;
+ }
+
+ private void upgradeConnection(HttpServletRequest request, HttpServletResponse response, Connection connection) throws IOException
+ {
+ // CONNECT expects a 200 response
+ response.setStatus(HttpServletResponse.SC_OK);
+ // Flush it so that the client receives it
+ response.flushBuffer();
+ // 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.info("Upgraded connection to {}", connection, null);
+ }
+
+ private void register(SocketChannel channel, ProxyToServerConnection proxyToServer) throws IOException
+ {
+ selectorManager.register(channel, proxyToServer);
+ proxyToServer.waitReady(connectTimeout);
+ }
+
+ /**
+ * Writes (with blocking semantic) the given buffer of data onto the given endPoint
+ * @param endPoint the endPoint to write to
+ * @param buffer the buffer to write
+ * @throws IOException if the buffer cannot be written
+ */
+ private void write(EndPoint endPoint, Buffer buffer) throws IOException
+ {
+ if (buffer == null)
+ return;
+
+ 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.info("Written {}/{} bytes " + endPoint, builder, 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
+ {
+ ProxyToServerConnection proxyToServer = (ProxyToServerConnection)selectionKey.attachment();
+ if (proxyToServer.secure)
+ {
+ throw new UnsupportedOperationException();
+// return new SslSelectChannelEndPoint(???, channel, selectSet, selectionKey, sslContext.createSSLEngine(address.host, address.port));
+ }
+ else
+ {
+ 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)
+ {
+ }
+ }
+
+ private class ProxyToServerConnection implements Connection
+ {
+ private final CountDownLatch ready = new CountDownLatch(1);
+ private final Buffer buffer = new IndirectNIOBuffer(1024);
+ private final boolean secure;
+ private volatile Buffer data;
+ private volatile ClientToProxyConnection connection;
+ private volatile long timestamp;
+ private volatile SelectChannelEndPoint endPoint;
+
+ public ProxyToServerConnection(boolean secure, Buffer data)
+ {
+ this.secure = secure;
+ this.data = data;
+ }
+
+ public Connection handle() throws IOException
+ {
+ logger.info("ProxyToServer: handle entered");
+ if (data != null)
+ {
+ write(endPoint, data);
+ data = null;
+ }
+
+ while (true)
+ {
+ int read = endPoint.fill(buffer);
+
+ if (read == -1)
+ {
+ logger.info("ProxyToServer: closed connection {}", endPoint, null);
+ connection.close();
+ break;
+ }
+
+ if (read == 0)
+ break;
+
+ logger.info("ProxyToServer: read {} bytes {}", read, endPoint);
+ write(connection.endPoint, buffer);
+ }
+ logger.info("ProxyToServer: handle exited");
+ return this;
+ }
+
+ public void setConnection(ClientToProxyConnection connection)
+ {
+ this.connection = connection;
+ }
+
+ public long getTimeStamp()
+ {
+ return timestamp;
+ }
+
+ public void setTimeStamp(long timestamp)
+ {
+ this.timestamp = timestamp;
+ }
+
+ public void setEndPoint(SelectChannelEndPoint endpoint)
+ {
+ this.endPoint = 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 (InterruptedException x)
+ {
+ throw new IOException(x);
+ }
+ }
+
+ public void close() throws IOException
+ {
+ endPoint.close();
+ }
+ }
+
+ private class ClientToProxyConnection implements Connection
+ {
+ private final Buffer buffer = new IndirectNIOBuffer(1024);
+ private final SocketChannel channel;
+ private final EndPoint endPoint;
+ private final long timestamp;
+ private volatile ProxyToServerConnection connection;
+ private boolean firstTime = true;
+
+ public ClientToProxyConnection(SocketChannel channel, EndPoint endPoint, long timestamp)
+ {
+ this.channel = channel;
+ this.endPoint = endPoint;
+ this.timestamp = timestamp;
+ }
+
+ public Connection handle() throws IOException
+ {
+ logger.info("ClientToProxy: handle entered");
+
+ if (firstTime)
+ {
+ firstTime = false;
+ register(channel, connection);
+ }
+
+ while (true)
+ {
+ int read = endPoint.fill(buffer);
+
+ if (read == -1)
+ {
+ logger.info("ClientToProxy: closed connection {}", endPoint, null);
+ connection.close();
+ break;
+ }
+
+ if (read == 0)
+ break;
+
+ logger.info("ClientToProxy: read {} bytes {}", read, endPoint);
+ write(connection.endPoint, buffer);
+ }
+ logger.info("ClientToProxy: handle exited");
+ return this;
+ }
+
+ public long getTimeStamp()
+ {
+ return timestamp;
+ }
+
+ public boolean isIdle()
+ {
+ return false;
+ }
+
+ public boolean isSuspended()
+ {
+ return false;
+ }
+
+ public void setConnection(ProxyToServerConnection connection)
+ {
+ this.connection = connection;
+ }
+
+ public void close() throws IOException
+ {
+ endPoint.close();
+ }
+ }
+}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ProxyHandlerConnectTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ProxyHandlerConnectTest.java
new file mode 100644
index 00000000000..3f40fc03016
--- /dev/null
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ProxyHandlerConnectTest.java
@@ -0,0 +1,328 @@
+package org.eclipse.jetty.server.handler;
+
+import java.io.BufferedReader;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import junit.framework.TestCase;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.nio.SelectChannelConnector;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class ProxyHandlerConnectTest extends TestCase
+{
+ private Server server;
+ private Connector serverConnector;
+ private Server proxy;
+ private Connector proxyConnector;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ server = new Server();
+ serverConnector = new SelectChannelConnector();
+ server.addConnector(serverConnector);
+ server.setHandler(new ServerHandler());
+ server.start();
+
+ proxy = new Server();
+ proxyConnector = new SelectChannelConnector();
+ proxy.addConnector(proxyConnector);
+ proxy.setHandler(new ProxyHandler());
+ proxy.start();
+ }
+
+ @Override
+ protected void tearDown() throws Exception
+ {
+ proxy.stop();
+ proxy.join();
+
+ server.stop();
+ server.join();
+ }
+
+ public void testHttpConnect() throws Exception
+ {
+ String request = "" +
+ "CONNECT localhost:" + serverConnector.getLocalPort() + " HTTP/1.1\r\n" +
+ "Host: localhost\r\n" +
+ "\r\n";
+ Socket socket = new Socket("localhost", proxyConnector.getLocalPort());
+ try
+ {
+ OutputStream output = socket.getOutputStream();
+ output.write(request.getBytes("UTF-8"));
+ output.flush();
+
+ // Expect 200 OK from the CONNECT request
+ InputStream input = socket.getInputStream();
+ Response response = readResponse(input);
+ System.err.println(response);
+ assertEquals("200", response.code);
+ }
+ finally
+ {
+ socket.close();
+ }
+ }
+
+ public void testHttpConnectWithNormalRequest() throws Exception
+ {
+ String request = "" +
+ "CONNECT localhost:" + serverConnector.getLocalPort() + " HTTP/1.1\r\n" +
+ "Host: localhost\r\n" +
+ "\r\n";
+ Socket socket = new Socket("localhost", proxyConnector.getLocalPort());
+ try
+ {
+ OutputStream output = socket.getOutputStream();
+ output.write(request.getBytes("UTF-8"));
+ output.flush();
+
+ // Expect 200 OK from the CONNECT request
+ InputStream input = socket.getInputStream();
+ Response response = readResponse(input);
+ System.err.println(response);
+ assertEquals("200", response.code);
+
+ String echoURI = "GET /echo";
+ request = "" +
+ echoURI + " HTTP/1.1\r\n" +
+ "Host: localhost\r\n" +
+ "\r\n";
+ output.write(request.getBytes("UTF-8"));
+ output.flush();
+
+ response = readResponse(input);
+ System.err.println(response);
+ assertEquals("200", response.code);
+ assertEquals(echoURI, response.body);
+ }
+ finally
+ {
+ socket.close();
+ }
+ }
+
+ public void testHttpConnectWithPipelinedRequest() throws Exception
+ {
+ String pipelinedMethodURI = "GET /echo";
+ String request = "" +
+ "CONNECT localhost:" + serverConnector.getLocalPort() + " HTTP/1.1\r\n" +
+ "Host: localhost\r\n" +
+ "\r\n" +
+ pipelinedMethodURI + " HTTP/1.1\r\n" +
+ "Host: localhost\r\n" +
+ "\r\n";
+ Socket socket = new Socket("localhost", proxyConnector.getLocalPort());
+ try
+ {
+ OutputStream output = socket.getOutputStream();
+ output.write(request.getBytes("UTF-8"));
+ output.flush();
+
+ // Expect 200 OK from the CONNECT request
+ InputStream input = socket.getInputStream();
+ Response response = readResponse(input);
+ System.err.println(response);
+ assertEquals("200", response.code);
+
+ // The pipelined request must have gone up to the server as is
+ response = readResponse(input);
+ System.err.println(response);
+ assertEquals("200", response.code);
+ assertEquals(pipelinedMethodURI, response.body);
+ }
+ finally
+ {
+ socket.close();
+ }
+ }
+
+ public void testHttpConnectWithNoRequestServerClose() throws Exception
+ {
+ String request = "" +
+ "CONNECT localhost:" + serverConnector.getLocalPort() + " HTTP/1.1\r\n" +
+ "Host: localhost\r\n" +
+ "\r\n";
+ Socket socket = new Socket("localhost", proxyConnector.getLocalPort());
+ try
+ {
+ OutputStream output = socket.getOutputStream();
+ output.write(request.getBytes("UTF-8"));
+ output.flush();
+
+ // Expect 200 OK from the CONNECT request
+ InputStream input = socket.getInputStream();
+ Response response = readResponse(input);
+ System.err.println(response);
+ assertEquals("200", response.code);
+
+ // Idle server is shut down
+ server.stop();
+ server.join();
+
+ int read = input.read();
+ assertEquals(-1, read);
+ }
+ finally
+ {
+ socket.close();
+ }
+ }
+
+ public void testHttpConnectWithRequestServerClose() throws Exception
+ {
+ String request = "" +
+ "CONNECT localhost:" + serverConnector.getLocalPort() + " HTTP/1.1\r\n" +
+ "Host: localhost\r\n" +
+ "\r\n";
+ Socket socket = new Socket("localhost", proxyConnector.getLocalPort());
+ try
+ {
+ OutputStream output = socket.getOutputStream();
+ output.write(request.getBytes("UTF-8"));
+ output.flush();
+
+ // Expect 200 OK from the CONNECT request
+ InputStream input = socket.getInputStream();
+ Response response = readResponse(input);
+ System.err.println(response);
+ assertEquals("200", response.code);
+
+ request = "" +
+ "GET /close HTTP/1.1\r\n" +
+ "Host: localhost\r\n" +
+ "\r\n";
+ output.write(request.getBytes("UTF-8"));
+ output.flush();
+
+ int read = input.read();
+ assertEquals(-1, read);
+ }
+ finally
+ {
+ socket.close();
+ }
+ }
+
+ private Response readResponse(InputStream input) throws IOException
+ {
+ // Simplified parser for HTTP responses
+ BufferedReader reader = new BufferedReader(new InputStreamReader(input));
+ String line = reader.readLine();
+ if (line == null)
+ throw new EOFException();
+ Matcher responseLine = Pattern.compile("HTTP/1\\.1\\s+(\\d+)").matcher(line);
+ assertTrue(responseLine.lookingAt());
+ String code = responseLine.group(1);
+
+ Map headers = new LinkedHashMap();
+ while ((line = reader.readLine()) != null)
+ {
+ if (line.trim().length() == 0)
+ break;
+
+ Matcher header = Pattern.compile("([^:]+):\\s*(.*)").matcher(line);
+ assertTrue(header.lookingAt());
+ String headerName = header.group(1);
+ String headerValue = header.group(2);
+ headers.put(headerName.toLowerCase(), headerValue.toLowerCase());
+ }
+
+ StringBuilder body = new StringBuilder();
+ if (headers.containsKey("content-length"))
+ {
+ int length = Integer.parseInt(headers.get("content-length"));
+ for (int i = 0; i < length; ++i)
+ body.append((char)reader.read());
+ }
+ else if ("chunked".equals(headers.get("transfer-encoding")))
+ {
+ while ((line = reader.readLine()) != null)
+ {
+ if ("0".equals(line))
+ {
+ reader.readLine();
+ break;
+ }
+
+ body.append(reader.readLine());
+ reader.readLine();
+ }
+ }
+
+ return new Response(code, headers, body.toString().trim());
+ }
+
+ public class TestServlet extends HttpServlet
+ {
+ }
+
+ private class ServerHandler extends AbstractHandler
+ {
+ public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
+ {
+ request.setHandled(true);
+
+ String uri = httpRequest.getRequestURI();
+ if ("/echo".equals(uri))
+ {
+ StringBuilder builder = new StringBuilder();
+ builder.append(httpRequest.getMethod()).append(" ").append(uri);
+ System.err.println("server echoing:\r\n" + builder);
+ ServletOutputStream output = httpResponse.getOutputStream();
+ output.println(builder.toString());
+ }
+ else if ("/close".equals(uri))
+ {
+ request.getConnection().getEndPoint().close();
+ System.err.println("server closed");
+ }
+ }
+ }
+
+ private class Response
+ {
+ private final String code;
+ private final Map headers;
+ private final String body;
+
+ private Response(String code, Map headers, String body)
+ {
+ this.code = code;
+ this.headers = headers;
+ this.body = body;
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder builder = new StringBuilder();
+ builder.append(code).append("\r\n");
+ for (Map.Entry entry : headers.entrySet())
+ builder.append(entry.getKey()).append(": ").append(entry.getValue()).append("\r\n");
+ builder.append("\r\n");
+ builder.append(body);
+ return builder.toString();
+ }
+ }
+}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ProxyHandlerSSLTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ProxyHandlerSSLTest.java
new file mode 100644
index 00000000000..377de6619e1
--- /dev/null
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ProxyHandlerSSLTest.java
@@ -0,0 +1,82 @@
+package org.eclipse.jetty.server.handler;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+import junit.framework.TestCase;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ssl.SslSelectChannelConnector;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class ProxyHandlerSSLTest extends TestCase
+{
+ private Server server;
+ private Connector serverConnector;
+ private Server proxy;
+ private Connector proxyConnector;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ server = new Server();
+ serverConnector = new SslSelectChannelConnector();
+ server.addConnector(serverConnector);
+// server.setHandler(new EchoHandler());
+ server.start();
+
+ proxy = new Server();
+ proxyConnector = new SslSelectChannelConnector();
+ proxy.addConnector(proxyConnector);
+ proxy.setHandler(new ProxyHandler()
+ {
+ @Override
+ protected boolean isTunnelSecure(String host, int port)
+ {
+ return true;
+ }
+ });
+ proxy.start();
+ }
+
+ @Override
+ protected void tearDown() throws Exception
+ {
+ proxy.stop();
+ proxy.join();
+
+ server.stop();
+ server.join();
+ }
+
+ public void testHttpConnectWithNormalRequest() throws Exception
+ {
+ String request = "" +
+ "CONNECT localhost:" + serverConnector.getLocalPort() + " HTTP/1.1\r\n" +
+ "Host: localhost\r\n" +
+ "\r\n";
+ Socket socket = new Socket("localhost", proxyConnector.getLocalPort());
+ try
+ {
+ OutputStream output = socket.getOutputStream();
+ output.write(request.getBytes("UTF-8"));
+ output.flush();
+
+ // Expect 200 OK from the CONNECT request
+ InputStream input = socket.getInputStream();
+// Response response = readResponse(input);
+// System.err.println(response);
+// assertEquals("200", response.code);
+
+ // Now what ? Upgrade the socket to SSL ?
+
+ }
+ finally
+ {
+ socket.close();
+ }
+ }
+}