Merge branch 'master' into javawebsocket-jsr
This commit is contained in:
commit
bcff9de1e7
|
@ -5,6 +5,6 @@ org.eclipse.jetty.SOURCE=false
|
|||
#org.eclipse.jetty.STACKS=false
|
||||
#org.eclipse.jetty.spdy.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.server.LEVEL=DEBUG
|
||||
org.eclipse.jetty.io.LEVEL=DEBUG
|
||||
org.eclipse.jetty.io.ssl.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.io.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.io.ssl.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.spdy.server.LEVEL=DEBUG
|
||||
|
|
|
@ -118,9 +118,9 @@ started()
|
|||
for T in 1 2 3 4 5 6 7 9 10 11 12 13 14 15
|
||||
do
|
||||
sleep 4
|
||||
[ -z "$(grep STARTED $1)" ] || return 0
|
||||
[ -z "$(grep STOPPED $1)" ] || return 1
|
||||
[ -z "$(grep FAILED $1)" ] || return 1
|
||||
[ -z "$(grep STARTED $1 2>/dev/null)" ] || return 0
|
||||
[ -z "$(grep STOPPED $1 2>/dev/null)" ] || return 1
|
||||
[ -z "$(grep FAILED $1 2>/dev/null)" ] || return 1
|
||||
local PID=$(cat "$2" 2>/dev/null) || return 1
|
||||
kill -0 "$PID" 2>/dev/null || return 1
|
||||
echo -n ". "
|
||||
|
@ -338,13 +338,17 @@ then
|
|||
fi
|
||||
|
||||
#####################################################
|
||||
# Find a PID for the pid file
|
||||
# Find a pid and state file
|
||||
#####################################################
|
||||
if [ -z "$JETTY_PID" ]
|
||||
then
|
||||
JETTY_PID="$JETTY_RUN/jetty.pid"
|
||||
fi
|
||||
JETTY_STATE=$(dirname $JETTY_PID)/jetty.state
|
||||
|
||||
if [ -z "$JETTY_STATE" ]
|
||||
then
|
||||
JETTY_STATE=$JETTY_HOME/jetty.state
|
||||
fi
|
||||
JAVA_OPTIONS+=("-Djetty.state=$JETTY_STATE")
|
||||
rm -f $JETTY_STATE
|
||||
|
||||
|
@ -415,8 +419,8 @@ if (( DEBUG ))
|
|||
then
|
||||
echo "JETTY_HOME = $JETTY_HOME"
|
||||
echo "JETTY_CONF = $JETTY_CONF"
|
||||
echo "JETTY_RUN = $JETTY_RUN"
|
||||
echo "JETTY_PID = $JETTY_PID"
|
||||
echo "JETTY_START = $JETTY_START"
|
||||
echo "JETTY_ARGS = $JETTY_ARGS"
|
||||
echo "CONFIGS = ${CONFIGS[*]}"
|
||||
echo "JAVA_OPTIONS = ${JAVA_OPTIONS[*]}"
|
||||
|
@ -566,22 +570,21 @@ case "$ACTION" in
|
|||
fi
|
||||
|
||||
exec "${RUN_CMD[@]}"
|
||||
|
||||
;;
|
||||
|
||||
check|status)
|
||||
echo "Checking arguments to Jetty: "
|
||||
echo "START_INI = $START_INI"
|
||||
echo "JETTY_HOME = $JETTY_HOME"
|
||||
echo "JETTY_CONF = $JETTY_CONF"
|
||||
echo "JETTY_RUN = $JETTY_RUN"
|
||||
echo "JETTY_PID = $JETTY_PID"
|
||||
echo "JETTY_PORT = $JETTY_PORT"
|
||||
echo "JETTY_START = $JETTY_START"
|
||||
echo "JETTY_LOGS = $JETTY_LOGS"
|
||||
echo "START_INI = $START_INI"
|
||||
echo "CONFIGS = ${CONFIGS[*]}"
|
||||
echo "JAVA_OPTIONS = ${JAVA_OPTIONS[*]}"
|
||||
echo "JAVA = $JAVA"
|
||||
echo "CLASSPATH = $CLASSPATH"
|
||||
echo "JAVA = $JAVA"
|
||||
echo "JAVA_OPTIONS = ${JAVA_OPTIONS[*]}"
|
||||
echo "JETTY_ARGS = $JETTY_ARGS"
|
||||
echo "RUN_CMD = ${RUN_CMD[*]}"
|
||||
echo
|
||||
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.server;
|
||||
|
||||
|
||||
import javax.servlet.AsyncContext;
|
||||
import javax.servlet.AsyncEvent;
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
|
||||
import org.eclipse.jetty.server.handler.ContextHandler.Context;
|
||||
import org.eclipse.jetty.util.thread.Scheduler;
|
||||
|
||||
public class AsyncContextEvent extends AsyncEvent
|
||||
{
|
||||
final private Context _context;
|
||||
final private AsyncContextState _asyncContext;
|
||||
volatile HttpChannelState _state;
|
||||
private ServletContext _dispatchContext;
|
||||
private String _pathInContext;
|
||||
private Scheduler.Task _timeoutTask;
|
||||
private Throwable _throwable;
|
||||
|
||||
public AsyncContextEvent(Context context,AsyncContextState asyncContext, HttpChannelState state, Request baseRequest, ServletRequest request, ServletResponse response)
|
||||
{
|
||||
super(null,request,response,null);
|
||||
_context=context;
|
||||
_asyncContext=asyncContext;
|
||||
_state=state;
|
||||
|
||||
// If we haven't been async dispatched before
|
||||
if (baseRequest.getAttribute(AsyncContext.ASYNC_REQUEST_URI)==null)
|
||||
{
|
||||
// We are setting these attributes during startAsync, when the spec implies that
|
||||
// they are only available after a call to AsyncContext.dispatch(...);
|
||||
|
||||
// have we been forwarded before?
|
||||
String uri=(String)baseRequest.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
|
||||
if (uri!=null)
|
||||
{
|
||||
baseRequest.setAttribute(AsyncContext.ASYNC_REQUEST_URI,uri);
|
||||
baseRequest.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH,baseRequest.getAttribute(RequestDispatcher.FORWARD_CONTEXT_PATH));
|
||||
baseRequest.setAttribute(AsyncContext.ASYNC_SERVLET_PATH,baseRequest.getAttribute(RequestDispatcher.FORWARD_SERVLET_PATH));
|
||||
baseRequest.setAttribute(AsyncContext.ASYNC_PATH_INFO,baseRequest.getAttribute(RequestDispatcher.FORWARD_PATH_INFO));
|
||||
baseRequest.setAttribute(AsyncContext.ASYNC_QUERY_STRING,baseRequest.getAttribute(RequestDispatcher.FORWARD_QUERY_STRING));
|
||||
}
|
||||
else
|
||||
{
|
||||
baseRequest.setAttribute(AsyncContext.ASYNC_REQUEST_URI,baseRequest.getRequestURI());
|
||||
baseRequest.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH,baseRequest.getContextPath());
|
||||
baseRequest.setAttribute(AsyncContext.ASYNC_SERVLET_PATH,baseRequest.getServletPath());
|
||||
baseRequest.setAttribute(AsyncContext.ASYNC_PATH_INFO,baseRequest.getPathInfo());
|
||||
baseRequest.setAttribute(AsyncContext.ASYNC_QUERY_STRING,baseRequest.getQueryString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ServletContext getSuspendedContext()
|
||||
{
|
||||
return _context;
|
||||
}
|
||||
|
||||
public Context getContext()
|
||||
{
|
||||
return _context;
|
||||
}
|
||||
|
||||
public ServletContext getDispatchContext()
|
||||
{
|
||||
return _dispatchContext;
|
||||
}
|
||||
|
||||
public ServletContext getServletContext()
|
||||
{
|
||||
return _dispatchContext==null?_context:_dispatchContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The path in the context
|
||||
*/
|
||||
public String getPath()
|
||||
{
|
||||
return _pathInContext;
|
||||
}
|
||||
|
||||
public void setTimeoutTask(Scheduler.Task task)
|
||||
{
|
||||
_timeoutTask = task;
|
||||
}
|
||||
|
||||
public void cancelTimeoutTask()
|
||||
{
|
||||
Scheduler.Task task=_timeoutTask;
|
||||
_timeoutTask=null;
|
||||
if (task!=null)
|
||||
task.cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncContext getAsyncContext()
|
||||
{
|
||||
return _asyncContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Throwable getThrowable()
|
||||
{
|
||||
return _throwable;
|
||||
}
|
||||
|
||||
public void setThrowable(Throwable throwable)
|
||||
{
|
||||
_throwable=throwable;
|
||||
}
|
||||
|
||||
public void setDispatchTarget(ServletContext context, String path)
|
||||
{
|
||||
if (context!=null)
|
||||
_dispatchContext=context;
|
||||
if (path!=null)
|
||||
_pathInContext=path;
|
||||
}
|
||||
|
||||
|
||||
public void completed()
|
||||
{
|
||||
_asyncContext.reset();
|
||||
}
|
||||
|
||||
public HttpChannelState getHttpChannelState()
|
||||
{
|
||||
return _state;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.server;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.AsyncContext;
|
||||
import javax.servlet.AsyncEvent;
|
||||
import javax.servlet.AsyncListener;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
|
||||
|
||||
public class AsyncContextState implements AsyncContext
|
||||
{
|
||||
volatile HttpChannelState _state;
|
||||
|
||||
public AsyncContextState(HttpChannelState state)
|
||||
{
|
||||
_state=state;
|
||||
}
|
||||
|
||||
private HttpChannelState state()
|
||||
{
|
||||
HttpChannelState state=_state;
|
||||
if (state==null)
|
||||
throw new IllegalStateException("AsyncContext completed");
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(final AsyncListener listener, final ServletRequest request, final ServletResponse response)
|
||||
{
|
||||
AsyncListener wrap = new AsyncListener()
|
||||
{
|
||||
@Override
|
||||
public void onTimeout(AsyncEvent event) throws IOException
|
||||
{
|
||||
listener.onTimeout(new AsyncEvent(event.getAsyncContext(),request,response,event.getThrowable()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartAsync(AsyncEvent event) throws IOException
|
||||
{
|
||||
listener.onStartAsync(new AsyncEvent(event.getAsyncContext(),request,response,event.getThrowable()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(AsyncEvent event) throws IOException
|
||||
{
|
||||
listener.onComplete(new AsyncEvent(event.getAsyncContext(),request,response,event.getThrowable()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete(AsyncEvent event) throws IOException
|
||||
{
|
||||
listener.onComplete(new AsyncEvent(event.getAsyncContext(),request,response,event.getThrowable()));
|
||||
}
|
||||
};
|
||||
state().addListener(wrap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(AsyncListener listener)
|
||||
{
|
||||
state().addListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void complete()
|
||||
{
|
||||
state().complete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends AsyncListener> T createListener(Class<T> clazz) throws ServletException
|
||||
{
|
||||
try
|
||||
{
|
||||
return clazz.newInstance();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw new ServletException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatch()
|
||||
{
|
||||
state().dispatch(null,null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatch(String path)
|
||||
{
|
||||
state().dispatch(null,path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatch(ServletContext context, String path)
|
||||
{
|
||||
state().dispatch(context,path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletRequest getRequest()
|
||||
{
|
||||
return state().getAsyncContextEvent().getSuppliedRequest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletResponse getResponse()
|
||||
{
|
||||
return state().getAsyncContextEvent().getSuppliedResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimeout()
|
||||
{
|
||||
return state().getTimeout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasOriginalRequestAndResponse()
|
||||
{
|
||||
HttpChannel<?> channel=state().getHttpChannel();
|
||||
return channel.getRequest()==getRequest() && channel.getResponse()==getResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTimeout(long arg0)
|
||||
{
|
||||
state().setTimeout(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(final Runnable task)
|
||||
{
|
||||
state().getHttpChannel().execute(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
state().getAsyncContextEvent().getContext().getContextHandler().handle(task);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
_state=null;
|
||||
}
|
||||
|
||||
public HttpChannelState getHttpChannelState()
|
||||
{
|
||||
return state();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -21,19 +21,14 @@ package org.eclipse.jetty.server;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.servlet.AsyncContext;
|
||||
import javax.servlet.AsyncEvent;
|
||||
|
||||
import javax.servlet.AsyncListener;
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
import org.eclipse.jetty.server.handler.ContextHandler.Context;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.thread.Scheduler;
|
||||
|
@ -56,7 +51,7 @@ import org.eclipse.jetty.util.thread.Scheduler;
|
|||
* <tr><th align=right>COMPLETED:</th> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td></tr>
|
||||
* </table>
|
||||
*/
|
||||
public class HttpChannelState implements AsyncContext
|
||||
public class HttpChannelState
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(HttpChannelState.class);
|
||||
|
||||
|
@ -86,7 +81,7 @@ public class HttpChannelState implements AsyncContext
|
|||
private boolean _expired;
|
||||
private volatile boolean _responseWrapped;
|
||||
private long _timeoutMs=DEFAULT_TIMEOUT;
|
||||
private AsyncEventState _event;
|
||||
private AsyncContextEvent _event;
|
||||
|
||||
protected HttpChannelState(HttpChannel<?> channel)
|
||||
{
|
||||
|
@ -103,7 +98,6 @@ public class HttpChannelState implements AsyncContext
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(AsyncListener listener)
|
||||
{
|
||||
synchronized(this)
|
||||
|
@ -114,19 +108,6 @@ public class HttpChannelState implements AsyncContext
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(AsyncListener listener,ServletRequest request, ServletResponse response)
|
||||
{
|
||||
synchronized(this)
|
||||
{
|
||||
if (_asyncListeners==null)
|
||||
_asyncListeners=new ArrayList<>();
|
||||
_asyncListeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setTimeout(long ms)
|
||||
{
|
||||
synchronized(this)
|
||||
|
@ -135,7 +116,6 @@ public class HttpChannelState implements AsyncContext
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimeout()
|
||||
{
|
||||
synchronized(this)
|
||||
|
@ -144,7 +124,7 @@ public class HttpChannelState implements AsyncContext
|
|||
}
|
||||
}
|
||||
|
||||
public AsyncEventState getAsyncEventState()
|
||||
public AsyncContextEvent getAsyncContextEvent()
|
||||
{
|
||||
synchronized(this)
|
||||
{
|
||||
|
@ -218,7 +198,8 @@ public class HttpChannelState implements AsyncContext
|
|||
}
|
||||
}
|
||||
|
||||
public void startAsync()
|
||||
|
||||
public void startAsync(AsyncContextEvent event)
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
|
@ -228,56 +209,15 @@ public class HttpChannelState implements AsyncContext
|
|||
case REDISPATCHED:
|
||||
_dispatched=false;
|
||||
_expired=false;
|
||||
_responseWrapped=event.getSuppliedResponse()!=_channel.getResponse();
|
||||
_responseWrapped=false;
|
||||
_event=new AsyncEventState(_channel.getRequest().getServletContext(),_channel.getRequest(),_channel.getResponse());
|
||||
_event=event;
|
||||
_state=State.ASYNCSTARTED;
|
||||
List<AsyncListener> listeners=_lastAsyncListeners;
|
||||
_lastAsyncListeners=_asyncListeners;
|
||||
if (listeners!=null)
|
||||
listeners.clear();
|
||||
_asyncListeners=listeners;
|
||||
if (_asyncListeners!=null)
|
||||
_asyncListeners.clear();
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IllegalStateException(this.getStatusString());
|
||||
}
|
||||
}
|
||||
|
||||
if (_lastAsyncListeners!=null)
|
||||
{
|
||||
for (AsyncListener listener : _lastAsyncListeners)
|
||||
{
|
||||
try
|
||||
{
|
||||
listener.onStartAsync(_event);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.warn(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void startAsync(final ServletContext context,final ServletRequest request,final ServletResponse response)
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
switch(_state)
|
||||
{
|
||||
case DISPATCHED:
|
||||
case REDISPATCHED:
|
||||
_dispatched=false;
|
||||
_expired=false;
|
||||
_responseWrapped=response!=_channel.getResponse();
|
||||
_event=new AsyncEventState(context,request,response);
|
||||
_event._pathInContext = (request instanceof HttpServletRequest)?URIUtil.addPaths(((HttpServletRequest)request).getServletPath(),((HttpServletRequest)request).getPathInfo()):null;
|
||||
_state=State.ASYNCSTARTED;
|
||||
List<AsyncListener> listeners=_lastAsyncListeners;
|
||||
_lastAsyncListeners=_asyncListeners;
|
||||
_asyncListeners=listeners;
|
||||
if (_asyncListeners!=null)
|
||||
_asyncListeners.clear();
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -306,7 +246,7 @@ public class HttpChannelState implements AsyncContext
|
|||
synchronized (this)
|
||||
{
|
||||
if (_event!=null)
|
||||
_event._cause=th;
|
||||
_event.setThrowable(th);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -362,26 +302,29 @@ public class HttpChannelState implements AsyncContext
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatch()
|
||||
public void dispatch(ServletContext context, String path)
|
||||
{
|
||||
boolean dispatch;
|
||||
synchronized (this)
|
||||
{
|
||||
|
||||
switch(_state)
|
||||
{
|
||||
case ASYNCSTARTED:
|
||||
_state=State.REDISPATCHING;
|
||||
_event.setDispatchTarget(context,path);
|
||||
_dispatched=true;
|
||||
return;
|
||||
|
||||
case ASYNCWAIT:
|
||||
dispatch=!_expired;
|
||||
_state=State.REDISPATCH;
|
||||
_event.setDispatchTarget(context,path);
|
||||
_dispatched=true;
|
||||
break;
|
||||
|
||||
case REDISPATCH:
|
||||
_event.setDispatchTarget(context,path);
|
||||
return;
|
||||
|
||||
default:
|
||||
|
@ -453,7 +396,6 @@ public class HttpChannelState implements AsyncContext
|
|||
scheduleDispatch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void complete()
|
||||
{
|
||||
// just like resume, except don't set _dispatched=true;
|
||||
|
@ -488,19 +430,6 @@ public class HttpChannelState implements AsyncContext
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends AsyncListener> T createListener(Class<T> clazz) throws ServletException
|
||||
{
|
||||
try
|
||||
{
|
||||
return clazz.newInstance();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw new ServletException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void completed()
|
||||
{
|
||||
final List<AsyncListener> aListeners;
|
||||
|
@ -524,10 +453,10 @@ public class HttpChannelState implements AsyncContext
|
|||
{
|
||||
try
|
||||
{
|
||||
if (_event!=null && _event._cause!=null)
|
||||
if (_event!=null && _event.getThrowable()!=null)
|
||||
{
|
||||
_event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,_event._cause);
|
||||
_event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_MESSAGE,_event._cause.getMessage());
|
||||
_event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,_event.getThrowable());
|
||||
_event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_MESSAGE,_event.getThrowable().getMessage());
|
||||
listener.onError(_event);
|
||||
}
|
||||
else
|
||||
|
@ -539,6 +468,7 @@ public class HttpChannelState implements AsyncContext
|
|||
}
|
||||
}
|
||||
}
|
||||
_event.completed();
|
||||
}
|
||||
|
||||
protected void recycle()
|
||||
|
@ -563,14 +493,6 @@ public class HttpChannelState implements AsyncContext
|
|||
}
|
||||
}
|
||||
|
||||
public void cancel()
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
cancelTimeout();
|
||||
}
|
||||
}
|
||||
|
||||
protected void scheduleDispatch()
|
||||
{
|
||||
_channel.execute(_channel);
|
||||
|
@ -580,18 +502,14 @@ public class HttpChannelState implements AsyncContext
|
|||
{
|
||||
Scheduler scheduler = _channel.getScheduler();
|
||||
if (scheduler!=null && _timeoutMs>0)
|
||||
_event._timeout=scheduler.schedule(new AsyncTimeout(),_timeoutMs,TimeUnit.MILLISECONDS);
|
||||
_event.setTimeoutTask(scheduler.schedule(new AsyncTimeout(),_timeoutMs,TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
protected void cancelTimeout()
|
||||
{
|
||||
AsyncEventState event=_event;
|
||||
AsyncContextEvent event=_event;
|
||||
if (event!=null)
|
||||
{
|
||||
Scheduler.Task task=event._timeout;
|
||||
if (task!=null)
|
||||
task.cancel();
|
||||
}
|
||||
event.cancelTimeoutTask();
|
||||
}
|
||||
|
||||
public boolean isExpired()
|
||||
|
@ -656,71 +574,19 @@ public class HttpChannelState implements AsyncContext
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatch(ServletContext context, String path)
|
||||
{
|
||||
_event._dispatchContext=context;
|
||||
_event._pathInContext=path;
|
||||
dispatch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatch(String path)
|
||||
{
|
||||
_event._pathInContext=path;
|
||||
dispatch();
|
||||
}
|
||||
|
||||
public Request getBaseRequest()
|
||||
{
|
||||
return _channel.getRequest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletRequest getRequest()
|
||||
public HttpChannel<?> getHttpChannel()
|
||||
{
|
||||
if (_event!=null)
|
||||
return _event.getSuppliedRequest();
|
||||
return _channel.getRequest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletResponse getResponse()
|
||||
{
|
||||
if (_responseWrapped && _event!=null && _event.getSuppliedResponse()!=null)
|
||||
return _event.getSuppliedResponse();
|
||||
return _channel.getResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(final Runnable run)
|
||||
{
|
||||
final AsyncEventState event=_event;
|
||||
if (event!=null)
|
||||
{
|
||||
_channel.execute(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
((Context)event.getServletContext()).getContextHandler().handle(run);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasOriginalRequestAndResponse()
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
return (_event!=null && _event.getSuppliedRequest()==_channel.getRequest() && _event.getSuppliedResponse()==_channel.getResponse());
|
||||
}
|
||||
return _channel;
|
||||
}
|
||||
|
||||
public ContextHandler getContextHandler()
|
||||
{
|
||||
final AsyncEventState event=_event;
|
||||
final AsyncContextEvent event=_event;
|
||||
if (event!=null)
|
||||
return ((Context)event.getServletContext()).getContextHandler();
|
||||
return null;
|
||||
|
@ -757,70 +623,4 @@ public class HttpChannelState implements AsyncContext
|
|||
}
|
||||
}
|
||||
|
||||
public class AsyncEventState extends AsyncEvent
|
||||
{
|
||||
final private ServletContext _suspendedContext;
|
||||
private String _pathInContext;
|
||||
private Scheduler.Task _timeout;
|
||||
private ServletContext _dispatchContext;
|
||||
private Throwable _cause;
|
||||
|
||||
public AsyncEventState(ServletContext context, ServletRequest request, ServletResponse response)
|
||||
{
|
||||
super(HttpChannelState.this, request,response);
|
||||
_suspendedContext=context;
|
||||
|
||||
// Get the base request So we can remember the initial paths
|
||||
Request r=_channel.getRequest();
|
||||
|
||||
// If we haven't been async dispatched before
|
||||
if (r.getAttribute(AsyncContext.ASYNC_REQUEST_URI)==null)
|
||||
{
|
||||
// We are setting these attributes during startAsync, when the spec implies that
|
||||
// they are only available after a call to AsyncContext.dispatch(...);
|
||||
|
||||
// have we been forwarded before?
|
||||
String uri=(String)r.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
|
||||
if (uri!=null)
|
||||
{
|
||||
r.setAttribute(AsyncContext.ASYNC_REQUEST_URI,uri);
|
||||
r.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH,r.getAttribute(RequestDispatcher.FORWARD_CONTEXT_PATH));
|
||||
r.setAttribute(AsyncContext.ASYNC_SERVLET_PATH,r.getAttribute(RequestDispatcher.FORWARD_SERVLET_PATH));
|
||||
r.setAttribute(AsyncContext.ASYNC_PATH_INFO,r.getAttribute(RequestDispatcher.FORWARD_PATH_INFO));
|
||||
r.setAttribute(AsyncContext.ASYNC_QUERY_STRING,r.getAttribute(RequestDispatcher.FORWARD_QUERY_STRING));
|
||||
}
|
||||
else
|
||||
{
|
||||
r.setAttribute(AsyncContext.ASYNC_REQUEST_URI,r.getRequestURI());
|
||||
r.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH,r.getContextPath());
|
||||
r.setAttribute(AsyncContext.ASYNC_SERVLET_PATH,r.getServletPath());
|
||||
r.setAttribute(AsyncContext.ASYNC_PATH_INFO,r.getPathInfo());
|
||||
r.setAttribute(AsyncContext.ASYNC_QUERY_STRING,r.getQueryString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ServletContext getSuspendedContext()
|
||||
{
|
||||
return _suspendedContext;
|
||||
}
|
||||
|
||||
public ServletContext getDispatchContext()
|
||||
{
|
||||
return _dispatchContext;
|
||||
}
|
||||
|
||||
public ServletContext getServletContext()
|
||||
{
|
||||
return _dispatchContext==null?_suspendedContext:_dispatchContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The path in the context
|
||||
*/
|
||||
public String getPath()
|
||||
{
|
||||
return _pathInContext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -203,7 +203,8 @@ public class Request implements HttpServletRequest
|
|||
private long _dispatchTime;
|
||||
private HttpURI _uri;
|
||||
private MultiPartInputStreamParser _multiPartInputStream; //if the request is a multi-part mime
|
||||
|
||||
private AsyncContextState _async;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public Request(HttpChannel<?> channel, HttpInput<?> input)
|
||||
{
|
||||
|
@ -369,10 +370,11 @@ public class Request implements HttpServletRequest
|
|||
@Override
|
||||
public AsyncContext getAsyncContext()
|
||||
{
|
||||
HttpChannelState continuation = getHttpChannelState();
|
||||
if (continuation.isInitial() && !continuation.isAsync())
|
||||
throw new IllegalStateException(continuation.getStatusString());
|
||||
return continuation;
|
||||
HttpChannelState state = getHttpChannelState();
|
||||
if (_async==null || state.isInitial() && !state.isAsync())
|
||||
throw new IllegalStateException(state.getStatusString());
|
||||
|
||||
return _async;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -1506,6 +1508,9 @@ public class Request implements HttpServletRequest
|
|||
|
||||
setAuthentication(Authentication.NOT_CHECKED);
|
||||
getHttpChannelState().recycle();
|
||||
if (_async!=null)
|
||||
_async.reset();
|
||||
_async=null;
|
||||
_asyncSupported = true;
|
||||
_handled = false;
|
||||
if (_context != null)
|
||||
|
@ -1989,8 +1994,11 @@ public class Request implements HttpServletRequest
|
|||
if (!_asyncSupported)
|
||||
throw new IllegalStateException("!asyncSupported");
|
||||
HttpChannelState state = getHttpChannelState();
|
||||
state.startAsync();
|
||||
return state;
|
||||
if (_async==null)
|
||||
_async=new AsyncContextState(state);
|
||||
AsyncContextEvent event = new AsyncContextEvent(_context,_async,state,this,this,getResponse());
|
||||
state.startAsync(event);
|
||||
return _async;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -2000,8 +2008,12 @@ public class Request implements HttpServletRequest
|
|||
if (!_asyncSupported)
|
||||
throw new IllegalStateException("!asyncSupported");
|
||||
HttpChannelState state = getHttpChannelState();
|
||||
state.startAsync(_context, servletRequest, servletResponse);
|
||||
return state;
|
||||
if (_async==null)
|
||||
_async=new AsyncContextState(state);
|
||||
AsyncContextEvent event = new AsyncContextEvent(_context,_async,state,this,servletRequest,servletResponse);
|
||||
event.setDispatchTarget(getServletContext(),URIUtil.addPaths(getServletPath(),getPathInfo()));
|
||||
state.startAsync(event);
|
||||
return _async;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
|
@ -57,8 +57,8 @@ public class ResourceCache
|
|||
private final ResourceCache _parent;
|
||||
private final MimeTypes _mimeTypes;
|
||||
private final boolean _etagSupported;
|
||||
|
||||
private boolean _useFileMappedBuffer=true;
|
||||
private final boolean _useFileMappedBuffer;
|
||||
|
||||
private int _maxCachedFileSize =4*1024*1024;
|
||||
private int _maxCachedFiles=2048;
|
||||
private int _maxCacheSize =32*1024*1024;
|
||||
|
@ -142,12 +142,6 @@ public class ResourceCache
|
|||
return _useFileMappedBuffer;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void setUseFileMappedBuffer(boolean useFileMappedBuffer)
|
||||
{
|
||||
_useFileMappedBuffer = useFileMappedBuffer;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void flushCache()
|
||||
{
|
||||
|
|
|
@ -476,16 +476,16 @@ public class Server extends HandlerWrapper implements Attributes
|
|||
*/
|
||||
public void handleAsync(HttpChannel<?> connection) throws IOException, ServletException
|
||||
{
|
||||
final HttpChannelState async = connection.getRequest().getHttpChannelState();
|
||||
final HttpChannelState.AsyncEventState state = async.getAsyncEventState();
|
||||
final HttpChannelState state = connection.getRequest().getHttpChannelState();
|
||||
final AsyncContextEvent event = state.getAsyncContextEvent();
|
||||
|
||||
final Request baseRequest=connection.getRequest();
|
||||
final String path=state.getPath();
|
||||
final String path=event.getPath();
|
||||
|
||||
if (path!=null)
|
||||
{
|
||||
// this is a dispatch with a path
|
||||
ServletContext context=state.getServletContext();
|
||||
ServletContext context=event.getServletContext();
|
||||
HttpURI uri = new HttpURI(context==null?path:URIUtil.addPaths(context.getContextPath(),path));
|
||||
baseRequest.setUri(uri);
|
||||
baseRequest.setRequestURI(null);
|
||||
|
@ -495,8 +495,8 @@ public class Server extends HandlerWrapper implements Attributes
|
|||
}
|
||||
|
||||
final String target=baseRequest.getPathInfo();
|
||||
final HttpServletRequest request=(HttpServletRequest)async.getRequest();
|
||||
final HttpServletResponse response=(HttpServletResponse)async.getResponse();
|
||||
final HttpServletRequest request=(HttpServletRequest)event.getSuppliedRequest();
|
||||
final HttpServletResponse response=(HttpServletResponse)event.getSuppliedResponse();
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
|
@ -509,7 +509,6 @@ public class Server extends HandlerWrapper implements Attributes
|
|||
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void join() throws InterruptedException
|
||||
{
|
||||
|
|
|
@ -28,6 +28,7 @@ import javax.servlet.ServletException;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.server.AsyncContextEvent;
|
||||
import org.eclipse.jetty.server.HttpChannelState;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
|
@ -76,12 +77,13 @@ public class StatisticsHandler extends HandlerWrapper
|
|||
public void onError(AsyncEvent event) throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onComplete(AsyncEvent event) throws IOException
|
||||
{
|
||||
HttpChannelState state = (HttpChannelState)event.getAsyncContext();
|
||||
|
||||
HttpChannelState state = ((AsyncContextEvent)event).getHttpChannelState();
|
||||
|
||||
Request request = state.getBaseRequest();
|
||||
final long elapsed = System.currentTimeMillis()-request.getTimeStamp();
|
||||
|
||||
|
@ -93,7 +95,7 @@ public class StatisticsHandler extends HandlerWrapper
|
|||
if (!state.isDispatched())
|
||||
_asyncWaitStats.decrement();
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -160,7 +160,6 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
private ServletHandler _servletHandler;
|
||||
private ServletHolder _defaultHolder;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public void init()
|
||||
|
|
|
@ -103,6 +103,7 @@ public class AsyncContextTest
|
|||
Assert.assertEquals("servlet gets right path", "doGet:getServletPath:/servletPath", br.readLine());
|
||||
Assert.assertEquals("async context gets right path in get","doGet:async:getServletPath:/servletPath",br.readLine());
|
||||
Assert.assertEquals("async context gets right path in async","async:run:attr:servletPath:/servletPath",br.readLine());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -121,6 +122,16 @@ public class AsyncContextTest
|
|||
Assert.assertEquals("query string attr is correct","async:run:attr:queryString:dispatch=true",br.readLine());
|
||||
Assert.assertEquals("context path attr is correct","async:run:attr:contextPath:",br.readLine());
|
||||
Assert.assertEquals("request uri attr is correct","async:run:attr:requestURI:/servletPath",br.readLine());
|
||||
|
||||
try
|
||||
{
|
||||
__asyncContext.getRequest();
|
||||
Assert.fail();
|
||||
}
|
||||
catch (IllegalStateException e)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -193,8 +204,11 @@ public class AsyncContextTest
|
|||
@Test
|
||||
public void testDispatchRequestResponse() throws Exception
|
||||
{
|
||||
String request = "GET /forward?dispatchRequestResponse=true HTTP/1.1\r\n" + "Host: localhost\r\n"
|
||||
+ "Content-Type: application/x-www-form-urlencoded\r\n" + "Connection: close\r\n" + "\r\n";
|
||||
String request = "GET /forward?dispatchRequestResponse=true HTTP/1.1\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"Content-Type: application/x-www-form-urlencoded\r\n" +
|
||||
"Connection: close\r\n" +
|
||||
"\r\n";
|
||||
|
||||
String responseString = _connector.getResponses(request);
|
||||
|
||||
|
@ -233,10 +247,12 @@ public class AsyncContextTest
|
|||
}
|
||||
}
|
||||
|
||||
public static volatile AsyncContext __asyncContext;
|
||||
|
||||
private class AsyncDispatchingServlet extends HttpServlet
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, final HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
|
@ -253,13 +269,14 @@ public class AsyncContextTest
|
|||
{
|
||||
wrapped = true;
|
||||
asyncContext = request.startAsync(request, new Wrapper(response));
|
||||
__asyncContext=asyncContext;
|
||||
}
|
||||
else
|
||||
{
|
||||
asyncContext = request.startAsync();
|
||||
__asyncContext=asyncContext;
|
||||
}
|
||||
|
||||
|
||||
new Thread(new DispatchingRunnable(asyncContext, wrapped)).start();
|
||||
}
|
||||
}
|
||||
|
@ -301,12 +318,14 @@ public class AsyncContextTest
|
|||
if (request.getParameter("dispatch") != null)
|
||||
{
|
||||
AsyncContext asyncContext = request.startAsync(request,response);
|
||||
__asyncContext=asyncContext;
|
||||
asyncContext.dispatch("/servletPath2");
|
||||
}
|
||||
else
|
||||
{
|
||||
response.getOutputStream().print("doGet:getServletPath:" + request.getServletPath() + "\n");
|
||||
AsyncContext asyncContext = request.startAsync(request,response);
|
||||
__asyncContext=asyncContext;
|
||||
response.getOutputStream().print("doGet:async:getServletPath:" + ((HttpServletRequest)asyncContext.getRequest()).getServletPath() + "\n");
|
||||
asyncContext.start(new AsyncRunnable(asyncContext));
|
||||
|
||||
|
@ -323,6 +342,7 @@ public class AsyncContextTest
|
|||
{
|
||||
response.getOutputStream().print("doGet:getServletPath:" + request.getServletPath() + "\n");
|
||||
AsyncContext asyncContext = request.startAsync(request, response);
|
||||
__asyncContext=asyncContext;
|
||||
response.getOutputStream().print("doGet:async:getServletPath:" + ((HttpServletRequest)asyncContext.getRequest()).getServletPath() + "\n");
|
||||
asyncContext.start(new AsyncRunnable(asyncContext));
|
||||
}
|
||||
|
|
|
@ -59,7 +59,9 @@ public interface IStream extends Stream, Callback
|
|||
*/
|
||||
public void setStreamFrameListener(StreamFrameListener listener);
|
||||
|
||||
//TODO: javadoc thomas
|
||||
/**
|
||||
* @return the stream frame listener associated to this stream
|
||||
*/
|
||||
public StreamFrameListener getStreamFrameListener();
|
||||
|
||||
/**
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.eclipse.jetty.spdy.server.http;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
@ -46,6 +47,7 @@ import org.eclipse.jetty.spdy.api.StreamStatus;
|
|||
import org.eclipse.jetty.util.BlockingCallback;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.ConcurrentArrayQueue;
|
||||
import org.eclipse.jetty.util.Fields;
|
||||
import org.eclipse.jetty.util.Promise;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
|
@ -206,28 +208,100 @@ public class HttpTransportOverSPDY implements HttpTransport
|
|||
short version = stream.getSession().getVersion();
|
||||
if (responseHeaders.get(HTTPSPDYHeader.STATUS.name(version)).value().startsWith("200") && !stream.isClosed())
|
||||
{
|
||||
// We have a 200 OK with some content to send, check the push strategy
|
||||
Set<String> pushResources = pushStrategy.apply(stream, requestHeaders, responseHeaders);
|
||||
if (pushResources.size() > 0)
|
||||
{
|
||||
PushResourceCoordinator pushResourceCoordinator = new PushResourceCoordinator(pushResources);
|
||||
pushResourceCoordinator.coordinate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class PushHttpTransportOverSPDY extends HttpTransportOverSPDY
|
||||
{
|
||||
private final PushResourceCoordinator pushResourceCoordinator;
|
||||
|
||||
private PushHttpTransportOverSPDY(Connector connector, HttpConfiguration configuration, EndPoint endPoint,
|
||||
PushStrategy pushStrategy, Stream stream, Fields requestHeaders,
|
||||
PushResourceCoordinator pushResourceCoordinator)
|
||||
{
|
||||
super(connector, configuration, endPoint, pushStrategy, stream, requestHeaders);
|
||||
this.pushResourceCoordinator = pushResourceCoordinator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void completed()
|
||||
{
|
||||
pushResourceCoordinator.complete();
|
||||
}
|
||||
}
|
||||
|
||||
private class PushResourceCoordinator
|
||||
{
|
||||
private final Queue<PushResource> queue = new ConcurrentArrayQueue<>();
|
||||
private final AtomicBoolean channelActive = new AtomicBoolean(false);
|
||||
private final Set<String> pushResources;
|
||||
|
||||
private PushResourceCoordinator(Set<String> pushResources)
|
||||
{
|
||||
this.pushResources = pushResources;
|
||||
}
|
||||
|
||||
private void coordinate()
|
||||
{
|
||||
for (String pushResource : pushResources)
|
||||
pushResource(pushResource);
|
||||
}
|
||||
|
||||
private void sendNextResourceData()
|
||||
{
|
||||
if (channelActive.compareAndSet(false, true))
|
||||
{
|
||||
PushResource pushResource = queue.poll();
|
||||
if (pushResource != null)
|
||||
{
|
||||
HttpChannelOverSPDY pushChannel = newHttpChannelOverSPDY(pushResource.getPushStream(),
|
||||
pushResource.getPushRequestHeaders());
|
||||
pushChannel.requestStart(pushResource.getPushRequestHeaders(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private HttpChannelOverSPDY newHttpChannelOverSPDY(Stream pushStream, Fields pushRequestHeaders)
|
||||
{
|
||||
HttpTransport transport = new PushHttpTransportOverSPDY(connector, configuration, endPoint, pushStrategy,
|
||||
pushStream, pushRequestHeaders, this);
|
||||
HttpInputOverSPDY input = new HttpInputOverSPDY();
|
||||
return new HttpChannelOverSPDY(connector, configuration, endPoint, transport, input, pushStream);
|
||||
}
|
||||
|
||||
private void pushResource(String pushResource)
|
||||
{
|
||||
short version = stream.getSession().getVersion();
|
||||
Fields.Field scheme = requestHeaders.get(HTTPSPDYHeader.SCHEME.name(version));
|
||||
Fields.Field host = requestHeaders.get(HTTPSPDYHeader.HOST.name(version));
|
||||
Fields.Field uri = requestHeaders.get(HTTPSPDYHeader.URI.name(version));
|
||||
Set<String> pushResources = pushStrategy.apply(stream, requestHeaders, responseHeaders);
|
||||
Fields pushHeaders = createPushHeaders(scheme, host, pushResource);
|
||||
final Fields pushRequestHeaders = createRequestHeaders(scheme, host, uri, pushResource);
|
||||
|
||||
for (String pushResource : pushResources)
|
||||
// TODO: handle the timeout better
|
||||
stream.push(new PushInfo(0, TimeUnit.MILLISECONDS, pushHeaders, false), new Promise.Adapter<Stream>()
|
||||
{
|
||||
Fields pushHeaders = createPushHeaders(scheme, host, pushResource);
|
||||
final Fields pushRequestHeaders = createRequestHeaders(scheme, host, uri, pushResource);
|
||||
|
||||
// TODO: handle the timeout better
|
||||
stream.push(new PushInfo(0, TimeUnit.MILLISECONDS, pushHeaders, false), new Promise.Adapter<Stream>()
|
||||
@Override
|
||||
public void succeeded(Stream pushStream)
|
||||
{
|
||||
@Override
|
||||
public void succeeded(Stream pushStream)
|
||||
{
|
||||
HttpChannelOverSPDY pushChannel = newHttpChannelOverSPDY(pushStream, pushRequestHeaders);
|
||||
pushChannel.requestStart(pushRequestHeaders, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
queue.offer(new PushResource(pushStream, pushRequestHeaders));
|
||||
sendNextResourceData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void complete()
|
||||
{
|
||||
if (channelActive.compareAndSet(true, false))
|
||||
sendNextResourceData();
|
||||
else
|
||||
throw new IllegalStateException("No channel was active when complete has been called.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -261,10 +335,25 @@ public class HttpTransportOverSPDY implements HttpTransport
|
|||
return pushHeaders;
|
||||
}
|
||||
|
||||
private HttpChannelOverSPDY newHttpChannelOverSPDY(Stream pushStream, Fields pushRequestHeaders)
|
||||
private class PushResource
|
||||
{
|
||||
HttpTransport transport = new HttpTransportOverSPDY(connector, configuration, endPoint, pushStrategy, pushStream, pushRequestHeaders);
|
||||
HttpInputOverSPDY input = new HttpInputOverSPDY();
|
||||
return new HttpChannelOverSPDY(connector, configuration, endPoint, transport, input, pushStream);
|
||||
private final Stream pushStream;
|
||||
private final Fields pushRequestHeaders;
|
||||
|
||||
public PushResource(Stream pushStream, Fields pushRequestHeaders)
|
||||
{
|
||||
this.pushStream = pushStream;
|
||||
this.pushRequestHeaders = pushRequestHeaders;
|
||||
}
|
||||
|
||||
public Stream getPushStream()
|
||||
{
|
||||
return pushStream;
|
||||
}
|
||||
|
||||
public Fields getPushRequestHeaders()
|
||||
{
|
||||
return pushRequestHeaders;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,8 @@ public interface PushStrategy
|
|||
/**
|
||||
* <p>Applies the SPDY push logic for the primary resource.</p>
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param stream the primary resource stream
|
||||
* @param requestHeaders the primary resource request headers
|
||||
* @param responseHeaders the primary resource response headers
|
||||
|
|
|
@ -27,6 +27,7 @@ import java.util.Locale;
|
|||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.regex.Pattern;
|
||||
|
@ -262,7 +263,7 @@ public class ReferrerPushStrategy implements PushStrategy
|
|||
private class MainResource
|
||||
{
|
||||
private final String name;
|
||||
private final Set<String> resources = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
|
||||
private final CopyOnWriteArraySet<String> resources = new CopyOnWriteArraySet<>();
|
||||
private final AtomicLong firstResourceAdded = new AtomicLong(-1);
|
||||
|
||||
private MainResource(String name)
|
||||
|
|
|
@ -22,9 +22,13 @@ import java.io.IOException;
|
|||
import java.io.PrintWriter;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
@ -41,6 +45,8 @@ import org.eclipse.jetty.spdy.api.RstInfo;
|
|||
import org.eclipse.jetty.spdy.api.SPDY;
|
||||
import org.eclipse.jetty.spdy.api.Session;
|
||||
import org.eclipse.jetty.spdy.api.SessionFrameListener;
|
||||
import org.eclipse.jetty.spdy.api.Settings;
|
||||
import org.eclipse.jetty.spdy.api.SettingsInfo;
|
||||
import org.eclipse.jetty.spdy.api.Stream;
|
||||
import org.eclipse.jetty.spdy.api.StreamFrameListener;
|
||||
import org.eclipse.jetty.spdy.api.StreamStatus;
|
||||
|
@ -56,6 +62,7 @@ import org.junit.Before;
|
|||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.core.IsEqual.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
|
||||
|
@ -159,25 +166,187 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
|
|||
public void testMaxConcurrentStreamsToDisablePush() throws Exception
|
||||
{
|
||||
final CountDownLatch pushReceivedLatch = new CountDownLatch(1);
|
||||
Session session = sendMainRequestAndCSSRequest(new SessionFrameListener.Adapter()
|
||||
|
||||
Session pushCacheBuildSession = startClient(version, serverAddress, null);
|
||||
|
||||
pushCacheBuildSession.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter());
|
||||
pushCacheBuildSession.syn(new SynInfo(associatedCSSRequestHeaders, true), new StreamFrameListener.Adapter());
|
||||
|
||||
Session session = startClient(version, serverAddress, null);
|
||||
|
||||
Settings settings = new Settings();
|
||||
settings.put(new Settings.Setting(Settings.ID.MAX_CONCURRENT_STREAMS, 0));
|
||||
SettingsInfo settingsInfo = new SettingsInfo(settings);
|
||||
session.settings(settingsInfo);
|
||||
|
||||
session.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
|
||||
public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
|
||||
{
|
||||
pushReceivedLatch.countDown();
|
||||
return super.onPush(stream, pushInfo);
|
||||
}
|
||||
});
|
||||
|
||||
assertThat("No push stream is received", pushReceivedLatch.await(1, TimeUnit.SECONDS), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPushResourceOrder() throws Exception
|
||||
{
|
||||
final CountDownLatch allExpectedPushesReceivedLatch = new CountDownLatch(4);
|
||||
|
||||
Session pushCacheBuildSession = startClient(version, serverAddress, null);
|
||||
|
||||
sendRequest(pushCacheBuildSession, mainRequestHeaders, null, null);
|
||||
sendRequest(pushCacheBuildSession, associatedCSSRequestHeaders, null, null);
|
||||
sendRequest(pushCacheBuildSession, associatedJSRequestHeaders, null, null);
|
||||
sendRequest(pushCacheBuildSession, createHeaders("/image1.jpg", mainResource), null, null);
|
||||
sendRequest(pushCacheBuildSession, createHeaders("/image2.jpg", mainResource), null, null);
|
||||
|
||||
Session session = startClient(version, serverAddress, null);
|
||||
|
||||
session.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
|
||||
{
|
||||
LOG.info("onPush: stream: {}, pushInfo: {}", stream, pushInfo);
|
||||
String uriHeader = pushInfo.getHeaders().get(HTTPSPDYHeader.URI.name(version)).value();
|
||||
switch ((int)allExpectedPushesReceivedLatch.getCount())
|
||||
{
|
||||
case 4:
|
||||
assertThat("1st pushed resource is the css", uriHeader.endsWith("css"), is(true));
|
||||
break;
|
||||
case 3:
|
||||
assertThat("2nd pushed resource is the js", uriHeader.endsWith("js"), is(true));
|
||||
break;
|
||||
case 2:
|
||||
assertThat("3rd pushed resource is image1", uriHeader.endsWith("image1.jpg"),
|
||||
is(true));
|
||||
break;
|
||||
case 1:
|
||||
assertThat("4th pushed resource is image2", uriHeader.endsWith("image2.jpg"),
|
||||
is(true));
|
||||
break;
|
||||
}
|
||||
allExpectedPushesReceivedLatch.countDown();
|
||||
return super.onPush(stream, pushInfo);
|
||||
}
|
||||
});
|
||||
|
||||
assertThat("All expected push resources have been received", allExpectedPushesReceivedLatch.await(5,
|
||||
TimeUnit.SECONDS), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatPushResourcesAreUnique() throws Exception
|
||||
{
|
||||
final CountDownLatch pushReceivedLatch = new CountDownLatch(2);
|
||||
sendMainRequestAndCSSRequest(null);
|
||||
sendMainRequestAndCSSRequest(null);
|
||||
|
||||
Session session = startClient(version, serverAddress, null);
|
||||
|
||||
session.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
|
||||
{
|
||||
pushReceivedLatch.countDown();
|
||||
LOG.info("Push received: {}", pushInfo);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Settings settings = new Settings();
|
||||
// settings.put(new Settings.Setting(Settings.ID.MAX_CONCURRENT_STREAMS, 0));
|
||||
// SettingsInfo settingsInfo = new SettingsInfo(settings);
|
||||
//
|
||||
// session.settings(settingsInfo);
|
||||
assertThat("style.css has been pushed only once", pushReceivedLatch.await(1, TimeUnit.SECONDS), is(false));
|
||||
}
|
||||
|
||||
sendRequest(session, mainRequestHeaders, null, null);
|
||||
@Test
|
||||
public void testPushResourceAreSentNonInterleaved() throws Exception
|
||||
{
|
||||
final CountDownLatch allExpectedPushesReceivedLatch = new CountDownLatch(4);
|
||||
final CountDownLatch allPushDataReceivedLatch = new CountDownLatch(4);
|
||||
final CopyOnWriteArrayList<Integer> dataReceivedOrder = new CopyOnWriteArrayList<>();
|
||||
|
||||
assertThat(pushReceivedLatch.await(1, TimeUnit.SECONDS), is(false));
|
||||
InetSocketAddress bigResponseServerAddress = startHTTPServer(version, new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
byte[] bytes = new byte[32768];
|
||||
new Random().nextBytes(bytes);
|
||||
ServletOutputStream outputStream = response.getOutputStream();
|
||||
outputStream.write(bytes);
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
});
|
||||
Session pushCacheBuildSession = startClient(version, bigResponseServerAddress, null);
|
||||
|
||||
Fields mainResourceHeaders = createHeadersWithoutReferrer(mainResource);
|
||||
sendRequest(pushCacheBuildSession, mainResourceHeaders, null, null);
|
||||
sendRequest(pushCacheBuildSession, createHeaders("/style.css", mainResource), null, null);
|
||||
sendRequest(pushCacheBuildSession, createHeaders("/javascript.js", mainResource), null, null);
|
||||
sendRequest(pushCacheBuildSession, createHeaders("/image1.jpg", mainResource), null, null);
|
||||
sendRequest(pushCacheBuildSession, createHeaders("/image2.jpg", mainResource), null, null);
|
||||
|
||||
Session session = startClient(version, bigResponseServerAddress, null);
|
||||
|
||||
session.syn(new SynInfo(mainResourceHeaders, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
AtomicInteger currentStreamId = new AtomicInteger(2);
|
||||
|
||||
@Override
|
||||
public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
|
||||
{
|
||||
LOG.info("Received push for stream: {} {}", stream.getId(), pushInfo);
|
||||
String uriHeader = pushInfo.getHeaders().get(HTTPSPDYHeader.URI.name(version)).value();
|
||||
switch ((int)allExpectedPushesReceivedLatch.getCount())
|
||||
{
|
||||
case 4:
|
||||
assertThat("1st pushed resource is the css", uriHeader.endsWith("css"), is(true));
|
||||
break;
|
||||
case 3:
|
||||
assertThat("2nd pushed resource is the js", uriHeader.endsWith("js"), is(true));
|
||||
break;
|
||||
case 2:
|
||||
assertThat("3rd pushed resource is image1", uriHeader.endsWith("image1.jpg"),
|
||||
is(true));
|
||||
break;
|
||||
case 1:
|
||||
assertThat("4th pushed resource is image2", uriHeader.endsWith("image2.jpg"),
|
||||
is(true));
|
||||
break;
|
||||
}
|
||||
allExpectedPushesReceivedLatch.countDown();
|
||||
return new Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
if (stream.getId() != currentStreamId.get())
|
||||
throw new IllegalStateException("Streams interleaved. Expected StreamId: " +
|
||||
currentStreamId + " but was: " + stream.getId());
|
||||
dataInfo.consume(dataInfo.available());
|
||||
if (dataInfo.isClose())
|
||||
{
|
||||
currentStreamId.compareAndSet(currentStreamId.get(), currentStreamId.get() + 2);
|
||||
allPushDataReceivedLatch.countDown();
|
||||
dataReceivedOrder.add(stream.getId());
|
||||
}
|
||||
|
||||
LOG.info(stream.getId() + ":" + dataInfo);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
assertThat("All push resources received", allExpectedPushesReceivedLatch.await(5, TimeUnit.SECONDS), is(true));
|
||||
assertThat("All pushData received", allPushDataReceivedLatch.await(5, TimeUnit.SECONDS), is(true));
|
||||
assertThat("The data for different push streams has not been interleaved",
|
||||
dataReceivedOrder.toString(), equalTo("[2, 4, 6, 8]"));
|
||||
LOG.info(dataReceivedOrder.toString());
|
||||
}
|
||||
|
||||
private InetSocketAddress createServer() throws Exception
|
||||
|
|
|
@ -18,10 +18,6 @@
|
|||
|
||||
package org.eclipse.jetty.spdy.server.http;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -35,6 +31,10 @@ import org.junit.runner.RunWith;
|
|||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ReferrerPushStrategyUnitTest
|
||||
{
|
||||
|
|
|
@ -4,4 +4,5 @@ org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
|||
#org.eclipse.jetty.io.ssl.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.spdy.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.client.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.spdy.server.http.ReferrerPushStrategy.LEVEL=DEBUG
|
||||
#org.mortbay.LEVEL=DEBUG
|
||||
|
|
|
@ -497,6 +497,10 @@ public class Dump extends HttpServlet
|
|||
pout.write("<th align=\"right\">isSecure(): </th>");
|
||||
pout.write("<td>"+request.isSecure()+"</td>");
|
||||
|
||||
pout.write("</tr><tr>\n");
|
||||
pout.write("<th align=\"right\">encodeRedirectURL(/foo?bar): </th>");
|
||||
pout.write("<td>"+response.encodeRedirectURL("/foo?bar")+"</td>");
|
||||
|
||||
pout.write("</tr><tr>\n");
|
||||
pout.write("<th align=\"right\">isUserInRole(admin): </th>");
|
||||
pout.write("<td>"+request.isUserInRole("admin")+"</td>");
|
||||
|
|
Loading…
Reference in New Issue