mirror of
https://github.com/jetty/jetty.project.git
synced 2025-03-04 12:59:30 +00:00
Jetty9 - Removed old code not used anymore, greatly simplifying the implementation.
This commit is contained in:
parent
71e7f519c7
commit
fa06ae9762
@ -22,7 +22,6 @@ import java.io.IOException;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
||||||
import javax.servlet.DispatcherType;
|
import javax.servlet.DispatcherType;
|
||||||
import javax.servlet.RequestDispatcher;
|
import javax.servlet.RequestDispatcher;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
@ -38,8 +37,8 @@ import org.eclipse.jetty.util.UrlEncoded;
|
|||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/** Servlet RequestDispatcher.
|
/** Servlet RequestDispatcher.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class Dispatcher implements RequestDispatcher
|
public class Dispatcher implements RequestDispatcher
|
||||||
{
|
{
|
||||||
@ -58,7 +57,7 @@ public class Dispatcher implements RequestDispatcher
|
|||||||
private final String _path;
|
private final String _path;
|
||||||
private final String _dQuery;
|
private final String _dQuery;
|
||||||
private final String _named;
|
private final String _named;
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* @param contextHandler
|
* @param contextHandler
|
||||||
@ -77,7 +76,7 @@ public class Dispatcher implements RequestDispatcher
|
|||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/** Constructor.
|
/** Constructor.
|
||||||
* @param contextHandler
|
* @param contextHandler
|
||||||
* @param name
|
* @param name
|
||||||
*/
|
*/
|
||||||
@ -90,9 +89,9 @@ public class Dispatcher implements RequestDispatcher
|
|||||||
_path=null;
|
_path=null;
|
||||||
_dQuery=null;
|
_dQuery=null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/*
|
/*
|
||||||
* @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
|
* @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@ -100,34 +99,34 @@ public class Dispatcher implements RequestDispatcher
|
|||||||
{
|
{
|
||||||
forward(request, response, DispatcherType.FORWARD);
|
forward(request, response, DispatcherType.FORWARD);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/*
|
/*
|
||||||
* @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
|
* @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
|
||||||
*/
|
*/
|
||||||
public void error(ServletRequest request, ServletResponse response) throws ServletException, IOException
|
public void error(ServletRequest request, ServletResponse response) throws ServletException, IOException
|
||||||
{
|
{
|
||||||
forward(request, response, DispatcherType.ERROR);
|
forward(request, response, DispatcherType.ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/*
|
/*
|
||||||
* @see javax.servlet.RequestDispatcher#include(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
|
* @see javax.servlet.RequestDispatcher#include(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException
|
public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException
|
||||||
{
|
{
|
||||||
Request baseRequest=(request instanceof Request)?((Request)request):HttpChannel.getCurrentHttpChannel().getRequest();
|
Request baseRequest=(request instanceof Request)?((Request)request):HttpChannel.getCurrentHttpChannel().getRequest();
|
||||||
|
|
||||||
|
|
||||||
if (!(request instanceof HttpServletRequest))
|
if (!(request instanceof HttpServletRequest))
|
||||||
request = new ServletRequestHttpWrapper(request);
|
request = new ServletRequestHttpWrapper(request);
|
||||||
if (!(response instanceof HttpServletResponse))
|
if (!(response instanceof HttpServletResponse))
|
||||||
response = new ServletResponseHttpWrapper(response);
|
response = new ServletResponseHttpWrapper(response);
|
||||||
|
|
||||||
|
|
||||||
// TODO - allow stream or writer????
|
// TODO - allow stream or writer????
|
||||||
|
|
||||||
final DispatcherType old_type = baseRequest.getDispatcherType();
|
final DispatcherType old_type = baseRequest.getDispatcherType();
|
||||||
final Attributes old_attr=baseRequest.getAttributes();
|
final Attributes old_attr=baseRequest.getAttributes();
|
||||||
MultiMap<String> old_params=baseRequest.getParameters();
|
MultiMap<String> old_params=baseRequest.getParameters();
|
||||||
@ -137,10 +136,10 @@ public class Dispatcher implements RequestDispatcher
|
|||||||
baseRequest.getResponse().include();
|
baseRequest.getResponse().include();
|
||||||
if (_named!=null)
|
if (_named!=null)
|
||||||
_contextHandler.handle(_named,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
|
_contextHandler.handle(_named,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
String query=_dQuery;
|
String query=_dQuery;
|
||||||
|
|
||||||
if (query!=null)
|
if (query!=null)
|
||||||
{
|
{
|
||||||
// force parameter extraction
|
// force parameter extraction
|
||||||
@ -149,27 +148,27 @@ public class Dispatcher implements RequestDispatcher
|
|||||||
baseRequest.extractParameters();
|
baseRequest.extractParameters();
|
||||||
old_params=baseRequest.getParameters();
|
old_params=baseRequest.getParameters();
|
||||||
}
|
}
|
||||||
|
|
||||||
MultiMap<String> parameters=new MultiMap<>();
|
MultiMap<String> parameters=new MultiMap<>();
|
||||||
UrlEncoded.decodeTo(query,parameters,baseRequest.getCharacterEncoding());
|
UrlEncoded.decodeTo(query,parameters,baseRequest.getCharacterEncoding());
|
||||||
|
|
||||||
if(old_params != null) {
|
if(old_params != null) {
|
||||||
// Merge parameters.
|
// Merge parameters.
|
||||||
parameters.addAllValues(old_params);
|
parameters.addAllValues(old_params);
|
||||||
}
|
}
|
||||||
baseRequest.setParameters(parameters);
|
baseRequest.setParameters(parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
IncludeAttributes attr = new IncludeAttributes(old_attr);
|
IncludeAttributes attr = new IncludeAttributes(old_attr);
|
||||||
|
|
||||||
attr._requestURI=_uri;
|
attr._requestURI=_uri;
|
||||||
attr._contextPath=_contextHandler.getContextPath();
|
attr._contextPath=_contextHandler.getContextPath();
|
||||||
attr._servletPath=null; // set by ServletHandler
|
attr._servletPath=null; // set by ServletHandler
|
||||||
attr._pathInfo=_path;
|
attr._pathInfo=_path;
|
||||||
attr._query=query;
|
attr._query=query;
|
||||||
|
|
||||||
baseRequest.setAttributes(attr);
|
baseRequest.setAttributes(attr);
|
||||||
|
|
||||||
_contextHandler.handle(_path,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
|
_contextHandler.handle(_path,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -182,24 +181,23 @@ public class Dispatcher implements RequestDispatcher
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/*
|
/*
|
||||||
* @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
|
* @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
|
||||||
*/
|
*/
|
||||||
protected void forward(ServletRequest request, ServletResponse response, DispatcherType dispatch) throws ServletException, IOException
|
protected void forward(ServletRequest request, ServletResponse response, DispatcherType dispatch) throws ServletException, IOException
|
||||||
{
|
{
|
||||||
Request baseRequest=(request instanceof Request)?((Request)request):HttpChannel.getCurrentHttpChannel().getRequest();
|
Request baseRequest=(request instanceof Request)?((Request)request):HttpChannel.getCurrentHttpChannel().getRequest();
|
||||||
Response base_response=baseRequest.getResponse();
|
Response base_response=baseRequest.getResponse();
|
||||||
response.resetBuffer();
|
base_response.resetForForward();
|
||||||
base_response.fwdReset();
|
|
||||||
|
|
||||||
|
|
||||||
if (!(request instanceof HttpServletRequest))
|
if (!(request instanceof HttpServletRequest))
|
||||||
request = new ServletRequestHttpWrapper(request);
|
request = new ServletRequestHttpWrapper(request);
|
||||||
if (!(response instanceof HttpServletResponse))
|
if (!(response instanceof HttpServletResponse))
|
||||||
response = new ServletResponseHttpWrapper(response);
|
response = new ServletResponseHttpWrapper(response);
|
||||||
|
|
||||||
final boolean old_handled=baseRequest.isHandled();
|
final boolean old_handled=baseRequest.isHandled();
|
||||||
final String old_uri=baseRequest.getRequestURI();
|
final String old_uri=baseRequest.getRequestURI();
|
||||||
final String old_context_path=baseRequest.getContextPath();
|
final String old_context_path=baseRequest.getContextPath();
|
||||||
@ -209,17 +207,17 @@ public class Dispatcher implements RequestDispatcher
|
|||||||
final Attributes old_attr=baseRequest.getAttributes();
|
final Attributes old_attr=baseRequest.getAttributes();
|
||||||
final DispatcherType old_type=baseRequest.getDispatcherType();
|
final DispatcherType old_type=baseRequest.getDispatcherType();
|
||||||
MultiMap<String> old_params=baseRequest.getParameters();
|
MultiMap<String> old_params=baseRequest.getParameters();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
baseRequest.setHandled(false);
|
baseRequest.setHandled(false);
|
||||||
baseRequest.setDispatcherType(dispatch);
|
baseRequest.setDispatcherType(dispatch);
|
||||||
|
|
||||||
if (_named!=null)
|
if (_named!=null)
|
||||||
_contextHandler.handle(_named,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
|
_contextHandler.handle(_named,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
// process any query string from the dispatch URL
|
// process any query string from the dispatch URL
|
||||||
String query=_dQuery;
|
String query=_dQuery;
|
||||||
if (query!=null)
|
if (query!=null)
|
||||||
@ -230,13 +228,13 @@ public class Dispatcher implements RequestDispatcher
|
|||||||
baseRequest.extractParameters();
|
baseRequest.extractParameters();
|
||||||
old_params=baseRequest.getParameters();
|
old_params=baseRequest.getParameters();
|
||||||
}
|
}
|
||||||
|
|
||||||
baseRequest.mergeQueryString(query);
|
baseRequest.mergeQueryString(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
ForwardAttributes attr = new ForwardAttributes(old_attr);
|
ForwardAttributes attr = new ForwardAttributes(old_attr);
|
||||||
|
|
||||||
//If we have already been forwarded previously, then keep using the established
|
//If we have already been forwarded previously, then keep using the established
|
||||||
//original value. Otherwise, this is the first forward and we need to establish the values.
|
//original value. Otherwise, this is the first forward and we need to establish the values.
|
||||||
//Note: the established value on the original request for pathInfo and
|
//Note: the established value on the original request for pathInfo and
|
||||||
//for queryString is allowed to be null, but cannot be null for the other values.
|
//for queryString is allowed to be null, but cannot be null for the other values.
|
||||||
@ -255,16 +253,16 @@ public class Dispatcher implements RequestDispatcher
|
|||||||
attr._requestURI=old_uri;
|
attr._requestURI=old_uri;
|
||||||
attr._contextPath=old_context_path;
|
attr._contextPath=old_context_path;
|
||||||
attr._servletPath=old_servlet_path;
|
attr._servletPath=old_servlet_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
baseRequest.setRequestURI(_uri);
|
baseRequest.setRequestURI(_uri);
|
||||||
baseRequest.setContextPath(_contextHandler.getContextPath());
|
baseRequest.setContextPath(_contextHandler.getContextPath());
|
||||||
baseRequest.setServletPath(null);
|
baseRequest.setServletPath(null);
|
||||||
baseRequest.setPathInfo(_uri);
|
baseRequest.setPathInfo(_uri);
|
||||||
baseRequest.setAttributes(attr);
|
baseRequest.setAttributes(attr);
|
||||||
|
|
||||||
_contextHandler.handle(_path,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
|
_contextHandler.handle(_path,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
|
||||||
|
|
||||||
if (!baseRequest.getAsyncContinuation().isAsyncStarted())
|
if (!baseRequest.getAsyncContinuation().isAsyncStarted())
|
||||||
commitResponse(response,baseRequest);
|
commitResponse(response,baseRequest);
|
||||||
}
|
}
|
||||||
@ -318,41 +316,41 @@ public class Dispatcher implements RequestDispatcher
|
|||||||
private class ForwardAttributes implements Attributes
|
private class ForwardAttributes implements Attributes
|
||||||
{
|
{
|
||||||
final Attributes _attr;
|
final Attributes _attr;
|
||||||
|
|
||||||
String _requestURI;
|
String _requestURI;
|
||||||
String _contextPath;
|
String _contextPath;
|
||||||
String _servletPath;
|
String _servletPath;
|
||||||
String _pathInfo;
|
String _pathInfo;
|
||||||
String _query;
|
String _query;
|
||||||
|
|
||||||
ForwardAttributes(Attributes attributes)
|
ForwardAttributes(Attributes attributes)
|
||||||
{
|
{
|
||||||
_attr=attributes;
|
_attr=attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
public Object getAttribute(String key)
|
public Object getAttribute(String key)
|
||||||
{
|
{
|
||||||
if (Dispatcher.this._named==null)
|
if (Dispatcher.this._named==null)
|
||||||
{
|
{
|
||||||
if (key.equals(FORWARD_PATH_INFO))
|
if (key.equals(FORWARD_PATH_INFO))
|
||||||
return _pathInfo;
|
return _pathInfo;
|
||||||
if (key.equals(FORWARD_REQUEST_URI))
|
if (key.equals(FORWARD_REQUEST_URI))
|
||||||
return _requestURI;
|
return _requestURI;
|
||||||
if (key.equals(FORWARD_SERVLET_PATH))
|
if (key.equals(FORWARD_SERVLET_PATH))
|
||||||
return _servletPath;
|
return _servletPath;
|
||||||
if (key.equals(FORWARD_CONTEXT_PATH))
|
if (key.equals(FORWARD_CONTEXT_PATH))
|
||||||
return _contextPath;
|
return _contextPath;
|
||||||
if (key.equals(FORWARD_QUERY_STRING))
|
if (key.equals(FORWARD_QUERY_STRING))
|
||||||
return _query;
|
return _query;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key.startsWith(__INCLUDE_PREFIX))
|
if (key.startsWith(__INCLUDE_PREFIX))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return _attr.getAttribute(key);
|
return _attr.getAttribute(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
public Enumeration getAttributeNames()
|
public Enumeration getAttributeNames()
|
||||||
{
|
{
|
||||||
@ -365,7 +363,7 @@ public class Dispatcher implements RequestDispatcher
|
|||||||
!name.startsWith(__FORWARD_PREFIX))
|
!name.startsWith(__FORWARD_PREFIX))
|
||||||
set.add(name);
|
set.add(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_named==null)
|
if (_named==null)
|
||||||
{
|
{
|
||||||
if (_pathInfo!=null)
|
if (_pathInfo!=null)
|
||||||
@ -383,37 +381,37 @@ public class Dispatcher implements RequestDispatcher
|
|||||||
|
|
||||||
return Collections.enumeration(set);
|
return Collections.enumeration(set);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
public void setAttribute(String key, Object value)
|
public void setAttribute(String key, Object value)
|
||||||
{
|
{
|
||||||
if (_named==null && key.startsWith("javax.servlet."))
|
if (_named==null && key.startsWith("javax.servlet."))
|
||||||
{
|
{
|
||||||
if (key.equals(FORWARD_PATH_INFO))
|
if (key.equals(FORWARD_PATH_INFO))
|
||||||
_pathInfo=(String)value;
|
_pathInfo=(String)value;
|
||||||
else if (key.equals(FORWARD_REQUEST_URI))
|
else if (key.equals(FORWARD_REQUEST_URI))
|
||||||
_requestURI=(String)value;
|
_requestURI=(String)value;
|
||||||
else if (key.equals(FORWARD_SERVLET_PATH))
|
else if (key.equals(FORWARD_SERVLET_PATH))
|
||||||
_servletPath=(String)value;
|
_servletPath=(String)value;
|
||||||
else if (key.equals(FORWARD_CONTEXT_PATH))
|
else if (key.equals(FORWARD_CONTEXT_PATH))
|
||||||
_contextPath=(String)value;
|
_contextPath=(String)value;
|
||||||
else if (key.equals(FORWARD_QUERY_STRING))
|
else if (key.equals(FORWARD_QUERY_STRING))
|
||||||
_query=(String)value;
|
_query=(String)value;
|
||||||
|
|
||||||
else if (value==null)
|
else if (value==null)
|
||||||
_attr.removeAttribute(key);
|
_attr.removeAttribute(key);
|
||||||
else
|
else
|
||||||
_attr.setAttribute(key,value);
|
_attr.setAttribute(key,value);
|
||||||
}
|
}
|
||||||
else if (value==null)
|
else if (value==null)
|
||||||
_attr.removeAttribute(key);
|
_attr.removeAttribute(key);
|
||||||
else
|
else
|
||||||
_attr.setAttribute(key,value);
|
_attr.setAttribute(key,value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
return "FORWARD+"+_attr.toString();
|
return "FORWARD+"+_attr.toString();
|
||||||
}
|
}
|
||||||
@ -435,18 +433,18 @@ public class Dispatcher implements RequestDispatcher
|
|||||||
private class IncludeAttributes implements Attributes
|
private class IncludeAttributes implements Attributes
|
||||||
{
|
{
|
||||||
final Attributes _attr;
|
final Attributes _attr;
|
||||||
|
|
||||||
String _requestURI;
|
String _requestURI;
|
||||||
String _contextPath;
|
String _contextPath;
|
||||||
String _servletPath;
|
String _servletPath;
|
||||||
String _pathInfo;
|
String _pathInfo;
|
||||||
String _query;
|
String _query;
|
||||||
|
|
||||||
IncludeAttributes(Attributes attributes)
|
IncludeAttributes(Attributes attributes)
|
||||||
{
|
{
|
||||||
_attr=attributes;
|
_attr=attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
@ -460,13 +458,13 @@ public class Dispatcher implements RequestDispatcher
|
|||||||
if (key.equals(INCLUDE_QUERY_STRING)) return _query;
|
if (key.equals(INCLUDE_QUERY_STRING)) return _query;
|
||||||
if (key.equals(INCLUDE_REQUEST_URI)) return _requestURI;
|
if (key.equals(INCLUDE_REQUEST_URI)) return _requestURI;
|
||||||
}
|
}
|
||||||
else if (key.startsWith(__INCLUDE_PREFIX))
|
else if (key.startsWith(__INCLUDE_PREFIX))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
|
||||||
return _attr.getAttribute(key);
|
return _attr.getAttribute(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
public Enumeration getAttributeNames()
|
public Enumeration getAttributeNames()
|
||||||
{
|
{
|
||||||
@ -478,7 +476,7 @@ public class Dispatcher implements RequestDispatcher
|
|||||||
if (!name.startsWith(__INCLUDE_PREFIX))
|
if (!name.startsWith(__INCLUDE_PREFIX))
|
||||||
set.add(name);
|
set.add(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_named==null)
|
if (_named==null)
|
||||||
{
|
{
|
||||||
if (_pathInfo!=null)
|
if (_pathInfo!=null)
|
||||||
@ -493,10 +491,10 @@ public class Dispatcher implements RequestDispatcher
|
|||||||
else
|
else
|
||||||
set.remove(INCLUDE_QUERY_STRING);
|
set.remove(INCLUDE_QUERY_STRING);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Collections.enumeration(set);
|
return Collections.enumeration(set);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
public void setAttribute(String key, Object value)
|
public void setAttribute(String key, Object value)
|
||||||
{
|
{
|
||||||
@ -510,17 +508,17 @@ public class Dispatcher implements RequestDispatcher
|
|||||||
else if (value==null)
|
else if (value==null)
|
||||||
_attr.removeAttribute(key);
|
_attr.removeAttribute(key);
|
||||||
else
|
else
|
||||||
_attr.setAttribute(key,value);
|
_attr.setAttribute(key,value);
|
||||||
}
|
}
|
||||||
else if (value==null)
|
else if (value==null)
|
||||||
_attr.removeAttribute(key);
|
_attr.removeAttribute(key);
|
||||||
else
|
else
|
||||||
_attr.setAttribute(key,value);
|
_attr.setAttribute(key,value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
return "INCLUDE+"+_attr.toString();
|
return "INCLUDE+"+_attr.toString();
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ import java.net.InetSocketAddress;
|
|||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import javax.servlet.DispatcherType;
|
import javax.servlet.DispatcherType;
|
||||||
import javax.servlet.RequestDispatcher;
|
import javax.servlet.RequestDispatcher;
|
||||||
|
|
||||||
@ -48,9 +49,9 @@ import org.eclipse.jetty.util.URIUtil;
|
|||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
public class HttpChannel
|
public class HttpChannel implements HttpParser.RequestHandler
|
||||||
{
|
{
|
||||||
protected static final Logger LOG = Log.getLogger(HttpChannel.class);
|
private static final Logger LOG = Log.getLogger(HttpChannel.class);
|
||||||
private static final ThreadLocal<HttpChannel> __currentChannel = new ThreadLocal<>();
|
private static final ThreadLocal<HttpChannel> __currentChannel = new ThreadLocal<>();
|
||||||
|
|
||||||
public static HttpChannel getCurrentHttpChannel()
|
public static HttpChannel getCurrentHttpChannel()
|
||||||
@ -64,7 +65,7 @@ public class HttpChannel
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final AtomicBoolean _committed = new AtomicBoolean();
|
private final AtomicBoolean _committed = new AtomicBoolean();
|
||||||
private final ChannelEventHandler _handler = new ChannelEventHandler();
|
private final AtomicInteger _requests = new AtomicInteger();
|
||||||
private final Connector _connector;
|
private final Connector _connector;
|
||||||
private final HttpConfiguration _configuration;
|
private final HttpConfiguration _configuration;
|
||||||
private final EndPoint _endPoint;
|
private final EndPoint _endPoint;
|
||||||
@ -73,7 +74,6 @@ public class HttpChannel
|
|||||||
private final HttpChannelState _state;
|
private final HttpChannelState _state;
|
||||||
private final Request _request;
|
private final Request _request;
|
||||||
private final Response _response;
|
private final Response _response;
|
||||||
private int _requests;
|
|
||||||
private HttpVersion _version = HttpVersion.HTTP_1_1;
|
private HttpVersion _version = HttpVersion.HTTP_1_1;
|
||||||
private boolean _expect = false;
|
private boolean _expect = false;
|
||||||
private boolean _expect100Continue = false;
|
private boolean _expect100Continue = false;
|
||||||
@ -98,22 +98,22 @@ public class HttpChannel
|
|||||||
return _state;
|
return _state;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EventHandler getEventHandler()
|
|
||||||
{
|
|
||||||
return _handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isIdle()
|
|
||||||
{
|
|
||||||
return _state.isIdle();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the number of requests handled by this connection
|
* @return the number of requests handled by this connection
|
||||||
*/
|
*/
|
||||||
public int getRequests()
|
public int getRequests()
|
||||||
{
|
{
|
||||||
return _requests;
|
return _requests.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Connector getConnector()
|
||||||
|
{
|
||||||
|
return _connector;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpConfiguration getHttpConfiguration()
|
||||||
|
{
|
||||||
|
return _configuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Server getServer()
|
public Server getServer()
|
||||||
@ -121,17 +121,11 @@ public class HttpChannel
|
|||||||
return _connector.getServer();
|
return _connector.getServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Returns the request.
|
|
||||||
*/
|
|
||||||
public Request getRequest()
|
public Request getRequest()
|
||||||
{
|
{
|
||||||
return _request;
|
return _request;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Returns the response.
|
|
||||||
*/
|
|
||||||
public Response getResponse()
|
public Response getResponse()
|
||||||
{
|
{
|
||||||
return _response;
|
return _response;
|
||||||
@ -285,7 +279,7 @@ public class HttpChannel
|
|||||||
// do anything special here other than make the connection not persistent
|
// do anything special here other than make the connection not persistent
|
||||||
_expect100Continue = false;
|
_expect100Continue = false;
|
||||||
if (!isCommitted())
|
if (!isCommitted())
|
||||||
_response.addHeader(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.toString());
|
_response.addHeader(HttpHeader.CONNECTION.toString(), HttpHeaderValue.CLOSE.toString());
|
||||||
else
|
else
|
||||||
LOG.warn("Cannot send 'Connection: close' for 100-Continue, response is already committed");
|
LOG.warn("Cannot send 'Connection: close' for 100-Continue, response is already committed");
|
||||||
}
|
}
|
||||||
@ -310,78 +304,78 @@ public class HttpChannel
|
|||||||
// TODO: remove this method
|
// TODO: remove this method
|
||||||
protected void completed()
|
protected void completed()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
// This method is called by handle() when it knows that its handling of the request/response cycle
|
// This method is called by handle() when it knows that its handling of the request/response cycle
|
||||||
// is complete.
|
// is complete.
|
||||||
// This may happen in the original thread dispatched to the connection that has called handle(),
|
// This may happen in the original thread dispatched to the connection that has called handle(),
|
||||||
// or it may be from a thread dispatched to call handle() as the result of a resumed suspended request.
|
// or it may be from a thread dispatched to call handle() as the result of a resumed suspended request.
|
||||||
|
|
||||||
LOG.debug("{} complete", this);
|
LOG.debug("{} complete", this);
|
||||||
|
|
||||||
|
|
||||||
// Handle connection upgrades
|
// Handle connection upgrades
|
||||||
if (_response.getStatus() == HttpStatus.SWITCHING_PROTOCOLS_101)
|
if (_response.getStatus() == HttpStatus.SWITCHING_PROTOCOLS_101)
|
||||||
{
|
|
||||||
Connection connection = (Connection)getRequest().getAttribute(HttpConnection.UPGRADE_CONNECTION_ATTRIBUTE);
|
|
||||||
if (connection != null)
|
|
||||||
{
|
|
||||||
LOG.debug("Upgrade from {} to {}", this, connection);
|
|
||||||
getEndPoint().setConnection(connection);
|
|
||||||
// HttpConnection.this.reset(); // TODO: this should be done by the connection privately when handle returns
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Reset everything for the next cycle.
|
|
||||||
// HttpConnection.this.reset(); // TODO: this should be done by the connection privately when handle returns
|
|
||||||
|
|
||||||
// are called from non connection thread (ie dispatched from a resume)
|
|
||||||
if (getCurrentConnection()!=HttpConnection.this)
|
|
||||||
{
|
|
||||||
if (_parser.isStart())
|
|
||||||
{
|
|
||||||
// it wants to eat more
|
|
||||||
if (_requestBuffer==null)
|
|
||||||
fillInterested();
|
|
||||||
else if (getConnector().isStarted())
|
|
||||||
{
|
{
|
||||||
LOG.debug("{} pipelined",this);
|
Connection connection = (Connection)getRequest().getAttribute(HttpConnection.UPGRADE_CONNECTION_ATTRIBUTE);
|
||||||
|
if (connection != null)
|
||||||
try
|
|
||||||
{
|
{
|
||||||
execute(this);
|
LOG.debug("Upgrade from {} to {}", this, connection);
|
||||||
}
|
getEndPoint().setConnection(connection);
|
||||||
catch(RejectedExecutionException e)
|
// HttpConnection.this.reset(); // TODO: this should be done by the connection privately when handle returns
|
||||||
{
|
return;
|
||||||
if (getConnector().isStarted())
|
|
||||||
LOG.warn(e);
|
|
||||||
else
|
|
||||||
LOG.ignore(e);
|
|
||||||
getEndPoint().close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
getEndPoint().close();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_parser.isClosed()&&!getEndPoint().isOutputShutdown())
|
|
||||||
{
|
|
||||||
// TODO This is a catch all indicating some protocol handling failure
|
|
||||||
// Currently needed for requests saying they are HTTP/2.0.
|
|
||||||
// This should be removed once better error handling is in place
|
|
||||||
LOG.warn("Endpoint output not shutdown when seeking EOF");
|
|
||||||
getEndPoint().shutdownOutput();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure that an oshut connection is driven towards close
|
// Reset everything for the next cycle.
|
||||||
// TODO this is a little ugly
|
// HttpConnection.this.reset(); // TODO: this should be done by the connection privately when handle returns
|
||||||
if (getEndPoint().isOpen() && getEndPoint().isOutputShutdown())
|
|
||||||
{
|
// are called from non connection thread (ie dispatched from a resume)
|
||||||
fillInterested();
|
if (getCurrentConnection()!=HttpConnection.this)
|
||||||
}
|
{
|
||||||
*/
|
if (_parser.isStart())
|
||||||
|
{
|
||||||
|
// it wants to eat more
|
||||||
|
if (_requestBuffer==null)
|
||||||
|
fillInterested();
|
||||||
|
else if (getConnector().isStarted())
|
||||||
|
{
|
||||||
|
LOG.debug("{} pipelined",this);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
execute(this);
|
||||||
|
}
|
||||||
|
catch(RejectedExecutionException e)
|
||||||
|
{
|
||||||
|
if (getConnector().isStarted())
|
||||||
|
LOG.warn(e);
|
||||||
|
else
|
||||||
|
LOG.ignore(e);
|
||||||
|
getEndPoint().close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
getEndPoint().close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_parser.isClosed()&&!getEndPoint().isOutputShutdown())
|
||||||
|
{
|
||||||
|
// TODO This is a catch all indicating some protocol handling failure
|
||||||
|
// Currently needed for requests saying they are HTTP/2.0.
|
||||||
|
// This should be removed once better error handling is in place
|
||||||
|
LOG.warn("Endpoint output not shutdown when seeking EOF");
|
||||||
|
getEndPoint().shutdownOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure that an oshut connection is driven towards close
|
||||||
|
// TODO this is a little ugly
|
||||||
|
if (getEndPoint().isOpen() && getEndPoint().isOutputShutdown())
|
||||||
|
{
|
||||||
|
fillInterested();
|
||||||
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -390,6 +384,7 @@ public class HttpChannel
|
|||||||
* <p>It may happen that the application suspends, and then throws an exception, while an application
|
* <p>It may happen that the application suspends, and then throws an exception, while an application
|
||||||
* spawned thread writes the response content; in such case, we attempt to commit the error directly
|
* spawned thread writes the response content; in such case, we attempt to commit the error directly
|
||||||
* bypassing the {@link ErrorHandler} mechanisms and the response OutputStream.</p>
|
* bypassing the {@link ErrorHandler} mechanisms and the response OutputStream.</p>
|
||||||
|
*
|
||||||
* @param x the Throwable that caused the problem
|
* @param x the Throwable that caused the problem
|
||||||
*/
|
*/
|
||||||
private void handleError(Throwable x)
|
private void handleError(Throwable x)
|
||||||
@ -479,10 +474,10 @@ public class HttpChannel
|
|||||||
{
|
{
|
||||||
// TODO: not sure why we need to modify the request when writing an error ?
|
// TODO: not sure why we need to modify the request when writing an error ?
|
||||||
// TODO: or modify the response if the error code cannot have a body ?
|
// TODO: or modify the response if the error code cannot have a body ?
|
||||||
// _channel.getRequest().getHttpFields().remove(HttpHeader.CONTENT_TYPE);
|
// _channel.getRequest().getHttpFields().remove(HttpHeader.CONTENT_TYPE);
|
||||||
// _channel.getRequest().getHttpFields().remove(HttpHeader.CONTENT_LENGTH);
|
// _channel.getRequest().getHttpFields().remove(HttpHeader.CONTENT_LENGTH);
|
||||||
// _characterEncoding = null;
|
// _characterEncoding = null;
|
||||||
// _mimeType = null;
|
// _mimeType = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
complete();
|
complete();
|
||||||
@ -510,16 +505,6 @@ public class HttpChannel
|
|||||||
return reason;
|
return reason;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSuspended()
|
|
||||||
{
|
|
||||||
return _request.getAsyncContinuation().isSuspended();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onClose()
|
|
||||||
{
|
|
||||||
LOG.debug("closed {}", this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isExpecting100Continue()
|
public boolean isExpecting100Continue()
|
||||||
{
|
{
|
||||||
return _expect100Continue;
|
return _expect100Continue;
|
||||||
@ -533,206 +518,203 @@ public class HttpChannel
|
|||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
return String.format("%s@%x{r=%d,a=%s}",
|
return String.format("%s@%x{r=%s,a=%s}",
|
||||||
getClass().getSimpleName(),
|
getClass().getSimpleName(),
|
||||||
hashCode(),
|
hashCode(),
|
||||||
_requests,
|
_requests,
|
||||||
_state.getState());
|
_state.getState());
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ChannelEventHandler implements EventHandler
|
@Override
|
||||||
|
public boolean startRequest(HttpMethod httpMethod, String method, String uri, HttpVersion version)
|
||||||
{
|
{
|
||||||
@Override
|
_host = false;
|
||||||
public boolean startRequest(HttpMethod httpMethod, String method, String uri, HttpVersion version)
|
_expect = false;
|
||||||
|
_expect100Continue = false;
|
||||||
|
_expect102Processing = false;
|
||||||
|
|
||||||
|
if (_request.getTimeStamp() == 0)
|
||||||
|
_request.setTimeStamp(System.currentTimeMillis());
|
||||||
|
_request.setMethod(httpMethod, method);
|
||||||
|
|
||||||
|
if (httpMethod == HttpMethod.CONNECT)
|
||||||
|
_uri.parseConnect(uri);
|
||||||
|
else
|
||||||
|
_uri.parse(uri);
|
||||||
|
_request.setUri(_uri);
|
||||||
|
|
||||||
|
String path;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
_host = false;
|
path = _uri.getDecodedPath();
|
||||||
_expect = false;
|
|
||||||
_expect100Continue = false;
|
|
||||||
_expect102Processing = false;
|
|
||||||
|
|
||||||
if (_request.getTimeStamp() == 0)
|
|
||||||
_request.setTimeStamp(System.currentTimeMillis());
|
|
||||||
_request.setMethod(httpMethod, method);
|
|
||||||
|
|
||||||
if (httpMethod == HttpMethod.CONNECT)
|
|
||||||
_uri.parseConnect(uri);
|
|
||||||
else
|
|
||||||
_uri.parse(uri);
|
|
||||||
_request.setUri(_uri);
|
|
||||||
|
|
||||||
String path;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
path = _uri.getDecodedPath();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
LOG.warn("Failed UTF-8 decode for request path, trying ISO-8859-1");
|
|
||||||
LOG.ignore(e);
|
|
||||||
path = _uri.getDecodedPath(StringUtil.__ISO_8859_1);
|
|
||||||
}
|
|
||||||
String info = URIUtil.canonicalPath(path);
|
|
||||||
|
|
||||||
if (info == null)
|
|
||||||
info = "/";
|
|
||||||
_request.setPathInfo(info);
|
|
||||||
_version = version == null ? HttpVersion.HTTP_0_9 : version;
|
|
||||||
_request.setHttpVersion(_version);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
catch (Exception e)
|
||||||
@Override
|
|
||||||
public boolean parsedHeader(HttpHeader header, String name, String value)
|
|
||||||
{
|
{
|
||||||
if (value == null)
|
LOG.warn("Failed UTF-8 decode for request path, trying ISO-8859-1");
|
||||||
value = "";
|
LOG.ignore(e);
|
||||||
if (header != null)
|
path = _uri.getDecodedPath(StringUtil.__ISO_8859_1);
|
||||||
|
}
|
||||||
|
String info = URIUtil.canonicalPath(path);
|
||||||
|
|
||||||
|
if (info == null)
|
||||||
|
info = "/";
|
||||||
|
_request.setPathInfo(info);
|
||||||
|
_version = version == null ? HttpVersion.HTTP_0_9 : version;
|
||||||
|
_request.setHttpVersion(_version);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean parsedHeader(HttpHeader header, String name, String value)
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
value = "";
|
||||||
|
if (header != null)
|
||||||
|
{
|
||||||
|
switch (header)
|
||||||
{
|
{
|
||||||
switch (header)
|
case HOST:
|
||||||
{
|
// TODO check if host matched a host in the URI.
|
||||||
case HOST:
|
_host = true;
|
||||||
// TODO check if host matched a host in the URI.
|
break;
|
||||||
_host = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EXPECT:
|
case EXPECT:
|
||||||
HttpHeaderValue expect = HttpHeaderValue.CACHE.get(value);
|
HttpHeaderValue expect = HttpHeaderValue.CACHE.get(value);
|
||||||
switch (expect == null ? HttpHeaderValue.UNKNOWN : expect)
|
switch (expect == null ? HttpHeaderValue.UNKNOWN : expect)
|
||||||
{
|
{
|
||||||
case CONTINUE:
|
case CONTINUE:
|
||||||
_expect100Continue = true;
|
_expect100Continue = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PROCESSING:
|
case PROCESSING:
|
||||||
_expect102Processing = true;
|
_expect102Processing = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
String[] values = value.split(",");
|
String[] values = value.split(",");
|
||||||
for (int i = 0; values != null && i < values.length; i++)
|
for (int i = 0; values != null && i < values.length; i++)
|
||||||
|
{
|
||||||
|
expect = HttpHeaderValue.CACHE.get(values[i].trim());
|
||||||
|
if (expect == null)
|
||||||
|
_expect = true;
|
||||||
|
else
|
||||||
{
|
{
|
||||||
expect = HttpHeaderValue.CACHE.get(values[i].trim());
|
switch (expect)
|
||||||
if (expect == null)
|
|
||||||
_expect = true;
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
switch (expect)
|
case CONTINUE:
|
||||||
{
|
_expect100Continue = true;
|
||||||
case CONTINUE:
|
break;
|
||||||
_expect100Continue = true;
|
case PROCESSING:
|
||||||
break;
|
_expect102Processing = true;
|
||||||
case PROCESSING:
|
break;
|
||||||
_expect102Processing = true;
|
default:
|
||||||
break;
|
_expect = true;
|
||||||
default:
|
|
||||||
_expect = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case CONTENT_TYPE:
|
case CONTENT_TYPE:
|
||||||
MimeTypes.Type mime = MimeTypes.CACHE.get(value);
|
MimeTypes.Type mime = MimeTypes.CACHE.get(value);
|
||||||
String charset = (mime == null || mime.getCharset() == null) ? MimeTypes.getCharsetFromContentType(value) : mime.getCharset().toString();
|
String charset = (mime == null || mime.getCharset() == null) ? MimeTypes.getCharsetFromContentType(value) : mime.getCharset().toString();
|
||||||
if (charset != null)
|
if (charset != null)
|
||||||
_request.setCharacterEncodingUnchecked(charset);
|
_request.setCharacterEncodingUnchecked(charset);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (name != null)
|
||||||
|
_request.getHttpFields().add(name, value);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean headerComplete()
|
||||||
|
{
|
||||||
|
_requests.incrementAndGet();
|
||||||
|
boolean persistent;
|
||||||
|
switch (_version)
|
||||||
|
{
|
||||||
|
case HTTP_0_9:
|
||||||
|
persistent = false;
|
||||||
|
break;
|
||||||
|
case HTTP_1_0:
|
||||||
|
persistent = _request.getHttpFields().contains(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString());
|
||||||
|
if (persistent)
|
||||||
|
_response.getHttpFields().add(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE);
|
||||||
|
|
||||||
|
if (getServer().getSendDateHeader())
|
||||||
|
_response.getHttpFields().putDateField(HttpHeader.DATE.toString(), _request.getTimeStamp());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HTTP_1_1:
|
||||||
|
persistent = !_request.getHttpFields().contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString());
|
||||||
|
|
||||||
|
if (!persistent)
|
||||||
|
_response.getHttpFields().add(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE);
|
||||||
|
|
||||||
|
if (getServer().getSendDateHeader())
|
||||||
|
_response.getHttpFields().putDateField(HttpHeader.DATE.toString(), _request.getTimeStamp());
|
||||||
|
|
||||||
|
if (!_host)
|
||||||
|
{
|
||||||
|
_response.sendError(Response.SC_BAD_REQUEST, "No Host Header", null);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (name != null)
|
if (_expect)
|
||||||
_request.getHttpFields().add(name, value);
|
{
|
||||||
return false;
|
_response.sendError(Response.SC_EXPECTATION_FAILED, null, null);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
_request.setPersistent(persistent);
|
||||||
public boolean headerComplete()
|
|
||||||
|
// Either handle now or wait for first content/message complete
|
||||||
|
return _expect100Continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean content(ByteBuffer ref)
|
||||||
|
{
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
{
|
{
|
||||||
_requests++;
|
LOG.debug("{} content {}", this, BufferUtil.toDetailString(ref));
|
||||||
boolean persistent;
|
|
||||||
switch (_version)
|
|
||||||
{
|
|
||||||
case HTTP_0_9:
|
|
||||||
persistent = false;
|
|
||||||
break;
|
|
||||||
case HTTP_1_0:
|
|
||||||
persistent = _request.getHttpFields().contains(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString());
|
|
||||||
if (persistent)
|
|
||||||
_response.getHttpFields().add(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE);
|
|
||||||
|
|
||||||
if (getServer().getSendDateHeader())
|
|
||||||
_response.getHttpFields().putDateField(HttpHeader.DATE.toString(), _request.getTimeStamp());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case HTTP_1_1:
|
|
||||||
persistent = !_request.getHttpFields().contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString());
|
|
||||||
|
|
||||||
if (!persistent)
|
|
||||||
_response.getHttpFields().add(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE);
|
|
||||||
|
|
||||||
if (getServer().getSendDateHeader())
|
|
||||||
_response.getHttpFields().putDateField(HttpHeader.DATE.toString(), _request.getTimeStamp());
|
|
||||||
|
|
||||||
if (!_host)
|
|
||||||
{
|
|
||||||
_response.sendError(Response.SC_BAD_REQUEST, "No Host Header", null);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_expect)
|
|
||||||
{
|
|
||||||
_response.sendError(Response.SC_EXPECTATION_FAILED, null, null);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException();
|
|
||||||
}
|
|
||||||
|
|
||||||
_request.setPersistent(persistent);
|
|
||||||
|
|
||||||
// Either handle now or wait for first content/message complete
|
|
||||||
if (_expect100Continue)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
_request.getHttpInput().content(ref);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean content(ByteBuffer ref)
|
public boolean messageComplete(long contentLength)
|
||||||
{
|
{
|
||||||
if (LOG.isDebugEnabled())
|
_request.getHttpInput().shutdownInput();
|
||||||
{
|
return true;
|
||||||
LOG.debug("{} content {}", this, BufferUtil.toDetailString(ref));
|
}
|
||||||
}
|
|
||||||
_request.getHttpInput().content(ref);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean messageComplete(long contentLength)
|
public boolean earlyEOF()
|
||||||
{
|
{
|
||||||
_request.getHttpInput().shutdownInput();
|
_request.getHttpInput().shutdownInput();
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean earlyEOF()
|
public void badMessage(int status, String reason)
|
||||||
{
|
{
|
||||||
_request.getHttpInput().shutdownInput();
|
if (status < 400 || status > 599)
|
||||||
return false;
|
status = HttpStatus.BAD_REQUEST_400;
|
||||||
}
|
_response.sendError(status, null, null);
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public void badMessage(int status, String reason)
|
|
||||||
{
|
|
||||||
if (status < 400 || status > 599)
|
|
||||||
status = HttpStatus.BAD_REQUEST_400;
|
|
||||||
_response.sendError(status, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// TODO: port the logic present in this method
|
||||||
|
/*
|
||||||
@Override
|
@Override
|
||||||
public ResponseInfo commit()
|
public ResponseInfo commit()
|
||||||
{
|
{
|
||||||
@ -744,13 +726,7 @@ public class HttpChannel
|
|||||||
}
|
}
|
||||||
return _response.commit();
|
return _response.commit();
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
@Override
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
return "CEH:" + HttpChannel.this.getEndPoint().toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean commitResponse(ResponseInfo info, ByteBuffer content, boolean complete) throws IOException
|
protected boolean commitResponse(ResponseInfo info, ByteBuffer content, boolean complete) throws IOException
|
||||||
{
|
{
|
||||||
@ -788,23 +764,6 @@ public class HttpChannel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Connector getConnector()
|
|
||||||
{
|
|
||||||
return _connector;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpConfiguration getHttpConfiguration()
|
|
||||||
{
|
|
||||||
return _configuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
// protected abstract void flush(ByteBuffer content, boolean last) throws IOException;
|
|
||||||
|
|
||||||
// protected abstract FutureCallback<Void> write(ResponseInfo info, ByteBuffer content) throws IOException;
|
|
||||||
|
|
||||||
// protected abstract void completed();
|
|
||||||
|
|
||||||
// TODO: remove
|
|
||||||
protected void execute(Runnable task)
|
protected void execute(Runnable task)
|
||||||
{
|
{
|
||||||
_connector.getExecutor().execute(task);
|
_connector.getExecutor().execute(task);
|
||||||
@ -815,9 +774,4 @@ public class HttpChannel
|
|||||||
{
|
{
|
||||||
return _connector.getScheduler();
|
return _connector.getScheduler();
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface EventHandler extends HttpParser.RequestHandler
|
|
||||||
{
|
|
||||||
ResponseInfo commit();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -20,119 +20,81 @@ package org.eclipse.jetty.server;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.RejectedExecutionException;
|
import java.util.concurrent.RejectedExecutionException;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.http.HttpGenerator;
|
import org.eclipse.jetty.http.HttpGenerator;
|
||||||
import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
|
|
||||||
import org.eclipse.jetty.http.HttpParser;
|
import org.eclipse.jetty.http.HttpParser;
|
||||||
import org.eclipse.jetty.http.HttpStatus;
|
import org.eclipse.jetty.http.HttpStatus;
|
||||||
import org.eclipse.jetty.io.AbstractConnection;
|
import org.eclipse.jetty.io.AbstractConnection;
|
||||||
import org.eclipse.jetty.io.ByteBufferPool;
|
import org.eclipse.jetty.io.ByteBufferPool;
|
||||||
import org.eclipse.jetty.io.Connection;
|
import org.eclipse.jetty.io.Connection;
|
||||||
import org.eclipse.jetty.io.EndPoint;
|
import org.eclipse.jetty.io.EndPoint;
|
||||||
import org.eclipse.jetty.io.EofException;
|
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
import org.eclipse.jetty.util.FutureCallback;
|
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Connection that handles the HTTP protocol
|
* <p>A {@link Connection} that handles the HTTP protocol.</p>
|
||||||
*/
|
*/
|
||||||
public class HttpConnection extends AbstractConnection
|
public class HttpConnection extends AbstractConnection
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(HttpConnection.class);
|
|
||||||
|
|
||||||
private static final ThreadLocal<HttpConnection> __currentConnection = new ThreadLocal<>();
|
|
||||||
private static final FutureCallback<Void> __completed = new FutureCallback<>();
|
|
||||||
static
|
|
||||||
{
|
|
||||||
__completed.completed(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final String UPGRADE_CONNECTION_ATTRIBUTE = "org.eclispe.jetty.server.HttpConnection.UPGRADE";
|
public static final String UPGRADE_CONNECTION_ATTRIBUTE = "org.eclispe.jetty.server.HttpConnection.UPGRADE";
|
||||||
|
private static final Logger LOG = Log.getLogger(HttpConnection.class);
|
||||||
|
private static final ThreadLocal<HttpConnection> __currentConnection = new ThreadLocal<>();
|
||||||
|
|
||||||
private final Server _server;
|
private final Server _server;
|
||||||
private final HttpConfiguration _httpConfig;
|
private final HttpConfiguration _configuration;
|
||||||
private final Connector _connector;
|
private final Connector _connector;
|
||||||
private final HttpParser _parser;
|
private final HttpParser _parser;
|
||||||
private final HttpGenerator _generator;
|
private final HttpGenerator _generator;
|
||||||
private final HttpChannel _channel;
|
private final HttpChannel _channel;
|
||||||
private final ByteBufferPool _bufferPool;
|
private final ByteBufferPool _bufferPool;
|
||||||
|
|
||||||
private ResponseInfo _info;
|
private ByteBuffer _requestBuffer = null;
|
||||||
private ByteBuffer _requestBuffer=null;
|
|
||||||
private ByteBuffer _chunk=null;
|
|
||||||
private int _headerBytes;
|
private int _headerBytes;
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
public static HttpConnection getCurrentConnection()
|
public static HttpConnection getCurrentConnection()
|
||||||
{
|
{
|
||||||
return __currentConnection.get();
|
return __currentConnection.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
protected static void setCurrentConnection(HttpConnection connection)
|
protected static void setCurrentConnection(HttpConnection connection)
|
||||||
{
|
{
|
||||||
__currentConnection.set(connection);
|
__currentConnection.set(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
public HttpConnection(HttpConfiguration config, Connector connector, EndPoint endPoint)
|
public HttpConnection(HttpConfiguration config, Connector connector, EndPoint endPoint)
|
||||||
{
|
{
|
||||||
super(endPoint,connector.getExecutor());
|
super(endPoint, connector.getExecutor());
|
||||||
|
|
||||||
_httpConfig=config;
|
_configuration = config;
|
||||||
_connector = connector;
|
_connector = connector;
|
||||||
_bufferPool=_connector.getByteBufferPool();
|
_bufferPool = _connector.getByteBufferPool();
|
||||||
_server = connector.getServer();
|
_server = connector.getServer();
|
||||||
_channel = new HttpChannel(connector, config, endPoint, new HttpTransportOverHttp(_bufferPool, _httpConfig, endPoint));
|
_channel = new HttpChannel(connector, config, endPoint, new HttpTransportOverHttp(_bufferPool, _configuration, endPoint));
|
||||||
_parser = new HttpParser(_channel.getEventHandler());
|
_parser = new HttpParser(_channel);
|
||||||
_generator = new HttpGenerator();
|
_generator = new HttpGenerator();
|
||||||
_generator.setSendServerVersion(_server.getSendServerVersion());
|
_generator.setSendServerVersion(_server.getSendServerVersion());
|
||||||
|
|
||||||
LOG.debug("New HTTP Connection {}",this);
|
LOG.debug("New HTTP Connection {}", this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @return the parser used by this connection
|
|
||||||
*/
|
|
||||||
public HttpParser getParser()
|
|
||||||
{
|
|
||||||
return _parser;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
public Server getServer()
|
public Server getServer()
|
||||||
{
|
{
|
||||||
return _server;
|
return _server;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @return Returns the connector.
|
|
||||||
*/
|
|
||||||
public Connector getConnector()
|
public Connector getConnector()
|
||||||
{
|
{
|
||||||
return _connector;
|
return _connector;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @return Returns the HttpChannel.
|
|
||||||
*/
|
|
||||||
public HttpChannel getHttpChannel()
|
public HttpChannel getHttpChannel()
|
||||||
{
|
{
|
||||||
return _channel;
|
return _channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
public void reset()
|
public void reset()
|
||||||
{
|
{
|
||||||
if (_generator.isPersistent())
|
if (_generator.isPersistent())
|
||||||
@ -143,21 +105,8 @@ public class HttpConnection extends AbstractConnection
|
|||||||
_generator.reset();
|
_generator.reset();
|
||||||
_channel.reset();
|
_channel.reset();
|
||||||
releaseRequestBuffer();
|
releaseRequestBuffer();
|
||||||
if (_chunk!=null)
|
|
||||||
_bufferPool.release(_chunk);
|
|
||||||
_chunk=null;
|
|
||||||
_info=null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
public HttpGenerator getGenerator()
|
|
||||||
{
|
|
||||||
return _generator;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
@ -167,60 +116,52 @@ public class HttpConnection extends AbstractConnection
|
|||||||
_parser);
|
_parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
private void releaseRequestBuffer()
|
private void releaseRequestBuffer()
|
||||||
{
|
{
|
||||||
if (_requestBuffer!=null && !_requestBuffer.hasRemaining())
|
if (_requestBuffer != null && !_requestBuffer.hasRemaining())
|
||||||
{
|
{
|
||||||
_bufferPool.release(_requestBuffer);
|
_bufferPool.release(_requestBuffer);
|
||||||
_requestBuffer=null;
|
_requestBuffer = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/**
|
||||||
/** Parse and handle HTTP messages.
|
* <p>Parses and handles HTTP messages.</p>
|
||||||
* <p>
|
* <p>This method is called when this {@link Connection} is ready to read bytes from the {@link EndPoint}.
|
||||||
* This method is normally called as the {@link AbstractConnection} onReadable callback.
|
* However, it can also be called if there is unconsumed data in the _requestBuffer, as a result of
|
||||||
* However, it can also be called {@link HttpChannelOverHttp#completed()} if there is unconsumed
|
* resuming a suspended request when there is a pipelined request already read into the buffer.</p>
|
||||||
* data in the _requestBuffer, as a result of resuming a suspended request when there is a pipelined
|
* <p>This method fills bytes and parses them until either: EOF is filled; 0 bytes are filled;
|
||||||
* request already read into the buffer.
|
* the HttpChannel finishes handling; or the connection has changed.</p>
|
||||||
* <p>
|
|
||||||
* This method will fill data and parse it until either: EOF is filled; 0 bytes are filled;
|
|
||||||
* the HttpChannel becomes !idle; or the connection has been changed
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onFillable()
|
public void onFillable()
|
||||||
{
|
{
|
||||||
LOG.debug("{} onReadable {}",this,_channel.isIdle());
|
LOG.debug("{} onReadable {}", this, _channel.getState());
|
||||||
|
|
||||||
int filled=-2;
|
|
||||||
|
|
||||||
|
setCurrentConnection(this);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
setCurrentConnection(this);
|
|
||||||
|
|
||||||
// TODO try to generalize this loop into AbstractConnection
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
// Fill the request buffer with data only if it is totally empty.
|
// Fill the request buffer with data only if it is totally empty.
|
||||||
if (BufferUtil.isEmpty(_requestBuffer))
|
if (BufferUtil.isEmpty(_requestBuffer))
|
||||||
{
|
{
|
||||||
if (_requestBuffer==null)
|
if (_requestBuffer == null)
|
||||||
_requestBuffer=_bufferPool.acquire(_httpConfig.getRequestHeaderSize(),false); // TODO may acquire on speculative read. probably released to early
|
_requestBuffer = _bufferPool.acquire(_configuration.getRequestHeaderSize(), false);
|
||||||
|
|
||||||
filled=getEndPoint().fill(_requestBuffer);
|
int filled = getEndPoint().fill(_requestBuffer);
|
||||||
|
|
||||||
LOG.debug("{} filled {}",this,filled);
|
LOG.debug("{} filled {}", this, filled);
|
||||||
|
|
||||||
// If we failed to fill
|
// If we failed to fill
|
||||||
if (filled==0)
|
if (filled == 0)
|
||||||
{
|
{
|
||||||
// Somebody wanted to read, we didn't so schedule another attempt
|
// Somebody wanted to read, we didn't so schedule another attempt
|
||||||
releaseRequestBuffer();
|
releaseRequestBuffer();
|
||||||
fillInterested();
|
fillInterested();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (filled<0)
|
else if (filled < 0)
|
||||||
{
|
{
|
||||||
_parser.inputShutdown();
|
_parser.inputShutdown();
|
||||||
// We were only filling if fully consumed, so if we have
|
// We were only filling if fully consumed, so if we have
|
||||||
@ -237,7 +178,7 @@ public class HttpConnection extends AbstractConnection
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_headerBytes+=filled;
|
_headerBytes += filled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,7 +186,7 @@ public class HttpConnection extends AbstractConnection
|
|||||||
if (_parser.parseNext(_requestBuffer))
|
if (_parser.parseNext(_requestBuffer))
|
||||||
{
|
{
|
||||||
// reset header count
|
// reset header count
|
||||||
_headerBytes=0;
|
_headerBytes = 0;
|
||||||
|
|
||||||
// For most requests, there will not be a body, so we can try to recycle the buffer now
|
// For most requests, there will not be a body, so we can try to recycle the buffer now
|
||||||
releaseRequestBuffer();
|
releaseRequestBuffer();
|
||||||
@ -256,7 +197,7 @@ public class HttpConnection extends AbstractConnection
|
|||||||
// The parser returned true, which indicates the channel is ready to handle a request.
|
// The parser returned true, which indicates the channel is ready to handle a request.
|
||||||
// Call the channel and this will either handle the request/response to completion OR,
|
// Call the channel and this will either handle the request/response to completion OR,
|
||||||
// if the request suspends, the request/response will be incomplete so the outer loop will exit.
|
// if the request suspends, the request/response will be incomplete so the outer loop will exit.
|
||||||
boolean complete = _channel.handle();
|
boolean complete = _channel.handle(); // TODO: should we perform special processing if we are complete ?
|
||||||
|
|
||||||
// Handle connection upgrades
|
// Handle connection upgrades
|
||||||
if (_channel.getResponse().getStatus() == HttpStatus.SWITCHING_PROTOCOLS_101)
|
if (_channel.getResponse().getStatus() == HttpStatus.SWITCHING_PROTOCOLS_101)
|
||||||
@ -330,10 +271,10 @@ public class HttpConnection extends AbstractConnection
|
|||||||
}
|
}
|
||||||
|
|
||||||
// return if the connection has been changed
|
// return if the connection has been changed
|
||||||
if (getEndPoint().getConnection()!=this)
|
if (getEndPoint().getConnection() != this)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (_headerBytes>= _httpConfig.getRequestHeaderSize())
|
else if (_headerBytes >= _configuration.getRequestHeaderSize())
|
||||||
{
|
{
|
||||||
_parser.reset();
|
_parser.reset();
|
||||||
_parser.close();
|
_parser.close();
|
||||||
@ -342,17 +283,17 @@ public class HttpConnection extends AbstractConnection
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(IOException e)
|
catch (IOException e)
|
||||||
{
|
{
|
||||||
if (_parser.isIdle())
|
if (_parser.isIdle())
|
||||||
LOG.debug(e);
|
LOG.debug(e);
|
||||||
else
|
else
|
||||||
LOG.warn(this.toString(),e);
|
LOG.warn(this.toString(), e);
|
||||||
getEndPoint().close();
|
getEndPoint().close();
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
LOG.warn(this.toString(),e);
|
LOG.warn(this.toString(), e);
|
||||||
getEndPoint().close();
|
getEndPoint().close();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@ -361,471 +302,10 @@ public class HttpConnection extends AbstractConnection
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
@Override
|
@Override
|
||||||
public void onOpen()
|
public void onOpen()
|
||||||
{
|
{
|
||||||
super.onOpen();
|
super.onOpen();
|
||||||
fillInterested();
|
fillInterested();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
@Override
|
|
||||||
public void onClose()
|
|
||||||
{
|
|
||||||
super.onClose();
|
|
||||||
_channel.onClose();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
private class HttpChannelOverHttp extends HttpChannel implements Runnable
|
|
||||||
{
|
|
||||||
private HttpChannelOverHttp(Server server)
|
|
||||||
{
|
|
||||||
super(_connector, _httpConfig, HttpConnection.this.getEndPoint(), new HttpTransportOverHttp(_bufferPool, _httpConfig, HttpConnection.this.getEndPoint()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Connector getConnector()
|
|
||||||
{
|
|
||||||
return _connector;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpConfiguration getHttpConfiguration()
|
|
||||||
{
|
|
||||||
return _httpConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Override
|
|
||||||
protected boolean commitError(int status, String reason, String content)
|
|
||||||
{
|
|
||||||
// if (!super.commitError(status,reason,content))
|
|
||||||
{
|
|
||||||
// TODO - should this just be a close and we don't worry about a RST overtaking a flushed response?
|
|
||||||
|
|
||||||
// We could not send the error, so a shutdown of the connection will at least tell
|
|
||||||
// the client something is wrong
|
|
||||||
getEndPoint().shutdownOutput();
|
|
||||||
_generator.abort();
|
|
||||||
// return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void completed()
|
|
||||||
{
|
|
||||||
// This is called by HttpChannel#handle when it knows that it's handling of the request/response cycle
|
|
||||||
// is complete. This may be in the original thread dispatched to the connection that has called process from
|
|
||||||
// the connection#onFillable method, or it may be from a thread dispatched to call process as the result
|
|
||||||
// of a resumed suspended request.
|
|
||||||
// At this point the HttpChannel will have completed the generation of any response (although it might remain to
|
|
||||||
// be asynchronously flushed TBD), but it may not have consumed the entire
|
|
||||||
|
|
||||||
LOG.debug("{} completed");
|
|
||||||
|
|
||||||
// Handle connection upgrades
|
|
||||||
if (getResponse().getStatus()==HttpStatus.SWITCHING_PROTOCOLS_101)
|
|
||||||
{
|
|
||||||
Connection connection=(Connection)getRequest().getAttribute(UPGRADE_CONNECTION_ATTRIBUTE);
|
|
||||||
if (connection!=null)
|
|
||||||
{
|
|
||||||
LOG.debug("Upgrade from {} to {}",this,connection);
|
|
||||||
getEndPoint().setConnection(connection);
|
|
||||||
HttpConnection.this.reset();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Reset everything for the next cycle.
|
|
||||||
HttpConnection.this.reset();
|
|
||||||
|
|
||||||
// are called from non connection thread (ie dispatched from a resume)
|
|
||||||
if (getCurrentConnection()!=HttpConnection.this)
|
|
||||||
{
|
|
||||||
if (_parser.isStart())
|
|
||||||
{
|
|
||||||
// it wants to eat more
|
|
||||||
if (_requestBuffer==null)
|
|
||||||
fillInterested();
|
|
||||||
else if (getConnector().isStarted())
|
|
||||||
{
|
|
||||||
LOG.debug("{} pipelined",this);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
execute(this);
|
|
||||||
}
|
|
||||||
catch(RejectedExecutionException e)
|
|
||||||
{
|
|
||||||
if (getConnector().isStarted())
|
|
||||||
LOG.warn(e);
|
|
||||||
else
|
|
||||||
LOG.ignore(e);
|
|
||||||
getEndPoint().close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
getEndPoint().close();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_parser.isClosed()&&!getEndPoint().isOutputShutdown())
|
|
||||||
{
|
|
||||||
// TODO This is a catch all indicating some protocol handling failure
|
|
||||||
// Currently needed for requests saying they are HTTP/2.0.
|
|
||||||
// This should be removed once better error handling is in place
|
|
||||||
LOG.warn("Endpoint output not shutdown when seeking EOF");
|
|
||||||
getEndPoint().shutdownOutput();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure that an oshut connection is driven towards close
|
|
||||||
// TODO this is a little ugly
|
|
||||||
if (getEndPoint().isOpen() && getEndPoint().isOutputShutdown())
|
|
||||||
{
|
|
||||||
fillInterested();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
@Override
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
onFillable();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
// @Override
|
|
||||||
public void flush(ByteBuffer content, boolean last) throws IOException
|
|
||||||
{
|
|
||||||
// Only one response writer at a time.
|
|
||||||
synchronized(this)
|
|
||||||
{
|
|
||||||
ByteBuffer header=null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_generator.isEnd())
|
|
||||||
{
|
|
||||||
// TODO do we need this escape?
|
|
||||||
if (last && BufferUtil.isEmpty(content))
|
|
||||||
return;
|
|
||||||
throw new EofException();
|
|
||||||
}
|
|
||||||
|
|
||||||
loop: while (true)
|
|
||||||
{
|
|
||||||
HttpGenerator.Result result=_generator.generateResponse(_info,header,content,last);
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
LOG.debug("{} generate: {} ({},{},{})@{}",
|
|
||||||
this,
|
|
||||||
result,
|
|
||||||
BufferUtil.toSummaryString(header),
|
|
||||||
BufferUtil.toSummaryString(content),
|
|
||||||
last,
|
|
||||||
_generator.getState());
|
|
||||||
|
|
||||||
switch(result)
|
|
||||||
{
|
|
||||||
case NEED_INFO:
|
|
||||||
if (_info==null)
|
|
||||||
_info=_channel.getEventHandler().commit();
|
|
||||||
continue;
|
|
||||||
|
|
||||||
case NEED_HEADER:
|
|
||||||
if (header!=null)
|
|
||||||
_bufferPool.release(header);
|
|
||||||
header=_bufferPool.acquire(_httpConfig.getResponseHeaderSize(),false);
|
|
||||||
continue;
|
|
||||||
|
|
||||||
case NEED_CHUNK:
|
|
||||||
if (header!=null)
|
|
||||||
_bufferPool.release(header);
|
|
||||||
header=_bufferPool.acquire(HttpGenerator.CHUNK_SIZE,false);
|
|
||||||
continue;
|
|
||||||
|
|
||||||
case FLUSH:
|
|
||||||
if (_info.isHead())
|
|
||||||
{
|
|
||||||
write(header,null).get();
|
|
||||||
BufferUtil.clear(content);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
write(header,content).get();
|
|
||||||
|
|
||||||
continue;
|
|
||||||
|
|
||||||
case SHUTDOWN_OUT:
|
|
||||||
getEndPoint().shutdownOutput();
|
|
||||||
continue;
|
|
||||||
|
|
||||||
case DONE:
|
|
||||||
break loop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(InterruptedException e)
|
|
||||||
{
|
|
||||||
LOG.debug(e);
|
|
||||||
}
|
|
||||||
catch(ExecutionException e)
|
|
||||||
{
|
|
||||||
LOG.debug(e);
|
|
||||||
if (e.getCause() instanceof IOException)
|
|
||||||
throw (IOException)e.getCause();
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (header!=null)
|
|
||||||
_bufferPool.release(header);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Override
|
|
||||||
protected FutureCallback<Void> write(ResponseInfo info, ByteBuffer content) throws IOException
|
|
||||||
{
|
|
||||||
// Only one response writer at a time.
|
|
||||||
synchronized(this)
|
|
||||||
{
|
|
||||||
ByteBuffer header=null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_generator.isEnd())
|
|
||||||
throw new EofException();
|
|
||||||
|
|
||||||
FutureCallback<Void> fcb=null;
|
|
||||||
|
|
||||||
loop: while (true)
|
|
||||||
{
|
|
||||||
HttpGenerator.Result result=_generator.generateResponse(info,header,content,true);
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
LOG.debug("{} send: {} ({},{})@{}",
|
|
||||||
this,
|
|
||||||
result,
|
|
||||||
BufferUtil.toSummaryString(header),
|
|
||||||
BufferUtil.toSummaryString(content),
|
|
||||||
_generator.getState());
|
|
||||||
|
|
||||||
switch(result)
|
|
||||||
{
|
|
||||||
case NEED_INFO:
|
|
||||||
throw new IllegalStateException();
|
|
||||||
|
|
||||||
case NEED_HEADER:
|
|
||||||
if (header!=null)
|
|
||||||
_bufferPool.release(header);
|
|
||||||
header=_bufferPool.acquire(_httpConfig.getResponseHeaderSize(),false);
|
|
||||||
continue;
|
|
||||||
|
|
||||||
case NEED_CHUNK:
|
|
||||||
if (header!=null)
|
|
||||||
_bufferPool.release(header);
|
|
||||||
header=_bufferPool.acquire(HttpGenerator.CHUNK_SIZE,false);
|
|
||||||
continue;
|
|
||||||
|
|
||||||
case FLUSH:
|
|
||||||
if(info.isHead())
|
|
||||||
{
|
|
||||||
BufferUtil.clear(content);
|
|
||||||
fcb=write(header,null);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
fcb=write(header,content);
|
|
||||||
continue;
|
|
||||||
|
|
||||||
case SHUTDOWN_OUT:
|
|
||||||
getEndPoint().shutdownOutput();
|
|
||||||
continue;
|
|
||||||
|
|
||||||
case DONE:
|
|
||||||
if (fcb==null)
|
|
||||||
fcb=__completed;
|
|
||||||
break loop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fcb;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (header!=null)
|
|
||||||
_bufferPool.release(header);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ScheduledExecutorService getScheduler()
|
|
||||||
{
|
|
||||||
return _connector.getScheduler();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void execute(Runnable task)
|
|
||||||
{
|
|
||||||
_connector.getExecutor().execute(task);
|
|
||||||
}
|
|
||||||
|
|
||||||
private FutureCallback<Void> write(ByteBuffer b0,ByteBuffer b1)
|
|
||||||
{
|
|
||||||
FutureCallback<Void> fcb=new FutureCallback<>();
|
|
||||||
if (BufferUtil.hasContent(b0))
|
|
||||||
{
|
|
||||||
if (BufferUtil.hasContent(b1))
|
|
||||||
{
|
|
||||||
getEndPoint().write(null,fcb,b0,b1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
getEndPoint().write(null,fcb,b0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (BufferUtil.hasContent(b1))
|
|
||||||
{
|
|
||||||
getEndPoint().write(null,fcb,b1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fcb.completed(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fcb;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private class HttpHttpInput extends HttpInput
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
protected void blockForContent() throws IOException
|
|
||||||
{
|
|
||||||
/* We extend the blockForContent method to replace the
|
|
||||||
default implementation of a blocking queue with an implementation
|
|
||||||
that uses the calling thread to block on a readable callback and
|
|
||||||
then to do the parsing before before attempting the read.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// While progress and the connection has not changed
|
|
||||||
boolean parsed_event=_parser.parseNext(_requestBuffer==null?BufferUtil.EMPTY_BUFFER:_requestBuffer);
|
|
||||||
while (!parsed_event && !getEndPoint().isInputShutdown())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Do we have content ready to parse?
|
|
||||||
if (BufferUtil.isEmpty(_requestBuffer))
|
|
||||||
{
|
|
||||||
// Wait until we can read
|
|
||||||
FutureCallback<Void> block=new FutureCallback<>();
|
|
||||||
getEndPoint().fillInterested(null,block);
|
|
||||||
LOG.debug("{} block readable on {}",this,block);
|
|
||||||
block.get();
|
|
||||||
|
|
||||||
// We will need a buffer to read into
|
|
||||||
if (_requestBuffer==null)
|
|
||||||
_requestBuffer=_bufferPool.acquire(_httpConfig.getRequestBufferSize(),false);
|
|
||||||
|
|
||||||
int filled=getEndPoint().fill(_requestBuffer);
|
|
||||||
LOG.debug("{} block filled {}",this,filled);
|
|
||||||
if (filled<0)
|
|
||||||
_parser.inputShutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we parse to an event, return
|
|
||||||
while (BufferUtil.hasContent(_requestBuffer) && _parser.inContentState())
|
|
||||||
parsed_event|=_parser.parseNext(_requestBuffer);
|
|
||||||
if (parsed_event)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
catch (InterruptedException e)
|
|
||||||
{
|
|
||||||
LOG.debug(e);
|
|
||||||
}
|
|
||||||
catch (ExecutionException e)
|
|
||||||
{
|
|
||||||
LOG.debug(e);
|
|
||||||
FutureCallback.rethrow(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int available()
|
|
||||||
{
|
|
||||||
int available=super.available();
|
|
||||||
if (available==0 && _parser.isInContent() && BufferUtil.hasContent(_requestBuffer))
|
|
||||||
return 1;
|
|
||||||
return available;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void consumeAll()
|
|
||||||
{
|
|
||||||
// Consume content only if the connection is persistent
|
|
||||||
if (!_generator.isPersistent())
|
|
||||||
{
|
|
||||||
_parser.setState(HttpParser.State.CLOSED);
|
|
||||||
synchronized (lock())
|
|
||||||
{
|
|
||||||
_inputQ.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
synchronized (lock())
|
|
||||||
{
|
|
||||||
_inputQ.clear();
|
|
||||||
}
|
|
||||||
if (_parser.isComplete() || _parser.isClosed())
|
|
||||||
return;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
blockForContent();
|
|
||||||
}
|
|
||||||
catch(IOException e)
|
|
||||||
{
|
|
||||||
LOG.warn(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onContentQueued(ByteBuffer ref)
|
|
||||||
{
|
|
||||||
/* This callback could be used to tell the connection
|
|
||||||
* that the request did contain content and thus the request
|
|
||||||
* buffer needs to be held until a call to #onAllContentConsumed
|
|
||||||
*
|
|
||||||
* However it turns out that nothing is needed here because either a
|
|
||||||
* request will have content, in which case the request buffer will be
|
|
||||||
* released by a call to onAllContentConsumed; or it will not have content.
|
|
||||||
* If it does not have content, either it will complete quickly and the
|
|
||||||
* buffers will be released in completed() or it will be suspended and
|
|
||||||
* onReadable() contains explicit handling to release if it is suspended.
|
|
||||||
*
|
|
||||||
* We extend this method anyway, to turn off the notify done by the
|
|
||||||
* default implementation as this is not needed by our implementation
|
|
||||||
* of blockForContent
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onAllContentConsumed()
|
|
||||||
{
|
|
||||||
/* This callback tells the connection that all content that has
|
|
||||||
* been parsed has been consumed. Thus the request buffer may be
|
|
||||||
* released if it is empty.
|
|
||||||
*/
|
|
||||||
releaseRequestBuffer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -24,16 +24,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.jetty.server;
|
package org.eclipse.jetty.server;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.greaterThan;
|
|
||||||
import static org.hamcrest.Matchers.startsWith;
|
|
||||||
import static org.junit.Assert.assertThat;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
@ -50,6 +43,12 @@ import org.junit.Assert;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.greaterThan;
|
||||||
|
import static org.hamcrest.Matchers.startsWith;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@ -126,7 +125,7 @@ public class HttpConnectionTest
|
|||||||
System.err.println(response);
|
System.err.println(response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNoPath() throws Exception
|
public void testNoPath() throws Exception
|
||||||
{
|
{
|
||||||
@ -139,7 +138,7 @@ public class HttpConnectionTest
|
|||||||
offset = checkContains(response,offset,"HTTP/1.1 200");
|
offset = checkContains(response,offset,"HTTP/1.1 200");
|
||||||
checkContains(response,offset,"pathInfo=/");
|
checkContains(response,offset,"pathInfo=/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEmpty() throws Exception
|
public void testEmpty() throws Exception
|
||||||
@ -199,15 +198,15 @@ public class HttpConnectionTest
|
|||||||
"Connection: close\n"+
|
"Connection: close\n"+
|
||||||
"\015\012");
|
"\015\012");
|
||||||
checkContains(response,0,"HTTP/1.1 400");
|
checkContains(response,0,"HTTP/1.1 400");
|
||||||
|
|
||||||
response=connector.getResponses("GET /foo/bar%c0%00 HTTP/1.1\n"+
|
response=connector.getResponses("GET /foo/bar%c0%00 HTTP/1.1\n"+
|
||||||
"Host: localhost\n"+
|
"Host: localhost\n"+
|
||||||
"Connection: close\n"+
|
"Connection: close\n"+
|
||||||
"\015\012");
|
"\015\012");
|
||||||
checkContains(response,0,"HTTP/1.1 200"); //now fallback to iso-8859-1
|
checkContains(response,0,"HTTP/1.1 200"); //now fallback to iso-8859-1
|
||||||
|
|
||||||
response=connector.getResponses("GET /bad/utf8%c1 HTTP/1.1\n"+
|
response=connector.getResponses("GET /bad/utf8%c1 HTTP/1.1\n"+
|
||||||
"Host: localhost\n"+
|
"Host: localhost\n"+
|
||||||
"Connection: close\n"+
|
"Connection: close\n"+
|
||||||
"\015\012");
|
"\015\012");
|
||||||
checkContains(response,0,"HTTP/1.1 200"); //now fallback to iso-8859-1
|
checkContains(response,0,"HTTP/1.1 200"); //now fallback to iso-8859-1
|
||||||
@ -360,10 +359,11 @@ public class HttpConnectionTest
|
|||||||
"\n"+
|
"\n"+
|
||||||
"abcdefghij\n";
|
"abcdefghij\n";
|
||||||
|
|
||||||
|
Logger logger = Log.getLogger(HttpChannel.class);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
HttpChannel.LOG.info("EXPECTING: java.lang.IllegalStateException...");
|
logger.info("EXPECTING: java.lang.IllegalStateException...");
|
||||||
((StdErrLog)Log.getLogger(HttpChannel.class)).setHideStacks(true);
|
((StdErrLog)logger).setHideStacks(true);
|
||||||
response=connector.getResponses(requests);
|
response=connector.getResponses(requests);
|
||||||
offset = checkContains(response,offset,"HTTP/1.1 500");
|
offset = checkContains(response,offset,"HTTP/1.1 500");
|
||||||
offset = checkContains(response,offset,"Connection: close");
|
offset = checkContains(response,offset,"Connection: close");
|
||||||
@ -371,7 +371,7 @@ public class HttpConnectionTest
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
((StdErrLog)Log.getLogger(HttpChannel.class)).setHideStacks(false);
|
((StdErrLog)logger).setHideStacks(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user