Merge remote-tracking branch 'origin/master' into jetty-8
This commit is contained in:
commit
9149a69446
|
@ -235,6 +235,7 @@ public abstract class AbstractCompressedStream extends ServletOutputStream
|
|||
throw new IllegalStateException();
|
||||
|
||||
setHeader("Content-Encoding", _encoding);
|
||||
setHeader("Vary","Accept-Encoding");
|
||||
|
||||
if (_response.containsHeader("Content-Encoding"))
|
||||
{
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.eclipse.jetty.npn</groupId>
|
||||
<artifactId>npn-api</artifactId>
|
||||
<version>1.0.1-SNAPSHOT</version>
|
||||
<version>1.1.1-SNAPSHOT</version>
|
||||
<name>Jetty :: Next Protocol Negotiation :: API</name>
|
||||
|
||||
<scm>
|
||||
|
|
|
@ -87,7 +87,8 @@ import javax.net.ssl.SSLSocket;
|
|||
* </pre>
|
||||
* <p>There is no need to unregister {@link SSLSocket} or {@link SSLEngine} instances, as they
|
||||
* are kept in a {@link WeakHashMap} and will be garbage collected when the application does not
|
||||
* hard reference them anymore.</p>
|
||||
* hard reference them anymore. However, methods to explicitly unregister {@link SSLSocket} or
|
||||
* {@link SSLEngine} instances are provided.</p>
|
||||
* <p>In order to help application development, you can set the {@link NextProtoNego#debug} field
|
||||
* to {@code true} to have debug code printed to {@link System#err}.</p>
|
||||
*/
|
||||
|
@ -109,6 +110,7 @@ public class NextProtoNego
|
|||
*
|
||||
* @param socket the socket to register with the provider
|
||||
* @param provider the provider to register with the socket
|
||||
* @see #remove(SSLSocket)
|
||||
*/
|
||||
public static void put(SSLSocket socket, Provider provider)
|
||||
{
|
||||
|
@ -124,11 +126,24 @@ public class NextProtoNego
|
|||
return objects.get(socket);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Unregisters the given SSLSocket.</p>
|
||||
*
|
||||
* @param socket the socket to unregister
|
||||
* @return the provider registered with the socket
|
||||
* @see #put(SSLSocket, Provider)
|
||||
*/
|
||||
public static Provider remove(SSLSocket socket)
|
||||
{
|
||||
return objects.remove(socket);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Registers a SSLEngine with a provider.</p>
|
||||
*
|
||||
* @param engine the engine to register with the provider
|
||||
* @param provider the provider to register with the engine
|
||||
* @see #remove(SSLEngine)
|
||||
*/
|
||||
public static void put(SSLEngine engine, Provider provider)
|
||||
{
|
||||
|
@ -145,6 +160,18 @@ public class NextProtoNego
|
|||
return objects.get(engine);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Unregisters the given SSLEngine.</p>
|
||||
*
|
||||
* @param engine the engine to unregister
|
||||
* @return the provider registered with the engine
|
||||
* @see #put(SSLEngine, Provider)
|
||||
*/
|
||||
public static Provider remove(SSLEngine engine)
|
||||
{
|
||||
return objects.remove(engine);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Base, empty, interface for providers.</p>
|
||||
*/
|
||||
|
|
|
@ -312,7 +312,6 @@ public class CookieCutter
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn(e.toString());
|
||||
LOG.debug(e);
|
||||
}
|
||||
|
||||
|
|
|
@ -137,11 +137,21 @@ public class Server extends HandlerWrapper implements Attributes
|
|||
/* ------------------------------------------------------------ */
|
||||
public void setStopAtShutdown(boolean stop)
|
||||
{
|
||||
_stopAtShutdown=stop;
|
||||
//if we now want to stop
|
||||
if (stop)
|
||||
ShutdownThread.register(this);
|
||||
{
|
||||
//and we weren't stopping before
|
||||
if (!_stopAtShutdown)
|
||||
{
|
||||
//only register to stop if we're already started (otherwise we'll do it in doStart())
|
||||
if (isStarted())
|
||||
ShutdownThread.register(this);
|
||||
}
|
||||
}
|
||||
else
|
||||
ShutdownThread.deregister(this);
|
||||
|
||||
_stopAtShutdown=stop;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
|
@ -68,6 +68,7 @@ import org.eclipse.jetty.util.Attributes;
|
|||
import org.eclipse.jetty.util.AttributesMap;
|
||||
import org.eclipse.jetty.util.LazyList;
|
||||
import org.eclipse.jetty.util.Loader;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.TypeUtil;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
import org.eclipse.jetty.util.component.AggregateLifeCycle;
|
||||
|
@ -143,6 +144,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
|
|||
private Object _requestListeners;
|
||||
private Object _requestAttributeListeners;
|
||||
private Map<String, Object> _managedAttributes;
|
||||
private String[] _protectedTargets;
|
||||
|
||||
private boolean _shutdown = false;
|
||||
private boolean _available = true;
|
||||
|
@ -1131,13 +1133,46 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
|
|||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Check the target. Called by {@link #handle(String, Request, HttpServletRequest, HttpServletResponse)} when a target within a context is determined. If
|
||||
* the target is protected, 404 is returned. The default implementation always returns false.
|
||||
* the target is protected, 404 is returned.
|
||||
*/
|
||||
/* ------------------------------------------------------------ */
|
||||
protected boolean isProtectedTarget(String target)
|
||||
public boolean isProtectedTarget(String target)
|
||||
{
|
||||
return false;
|
||||
if (target == null || _protectedTargets == null)
|
||||
return false;
|
||||
|
||||
while (target.startsWith("//"))
|
||||
target=URIUtil.compactPath(target);
|
||||
|
||||
boolean isProtected = false;
|
||||
int i=0;
|
||||
while (!isProtected && i<_protectedTargets.length)
|
||||
{
|
||||
isProtected = StringUtil.startsWithIgnoreCase(target, _protectedTargets[i++]);
|
||||
}
|
||||
return isProtected;
|
||||
}
|
||||
|
||||
|
||||
public void setProtectedTargets (String[] targets)
|
||||
{
|
||||
if (targets == null)
|
||||
{
|
||||
_protectedTargets = null;
|
||||
return;
|
||||
}
|
||||
|
||||
_protectedTargets = Arrays.copyOf(targets, targets.length);
|
||||
}
|
||||
|
||||
public String[] getProtectedTargets ()
|
||||
{
|
||||
if (_protectedTargets == null)
|
||||
return null;
|
||||
|
||||
return Arrays.copyOf(_protectedTargets, _protectedTargets.length);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/*
|
||||
|
|
|
@ -77,6 +77,7 @@ public abstract class AbstractSession implements AbstractSessionManager.SessionI
|
|||
_accessed=accessed;
|
||||
_lastAccessed=accessed;
|
||||
_requests=1;
|
||||
_maxIdleMs=_manager._dftMaxIdleSecs>0?_manager._dftMaxIdleSecs*1000L:-1;
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("new session "+_nodeId+" "+_clusterId);
|
||||
}
|
||||
|
|
|
@ -21,9 +21,11 @@ import java.io.InputStreamReader;
|
|||
import java.io.LineNumberReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.BrokenBarrierException;
|
||||
import java.util.concurrent.Exchanger;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
|
@ -131,6 +133,11 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
|
||||
Assert.assertThat(response, Matchers.containsString("HTTP/1.1 413 "));
|
||||
}
|
||||
catch(SocketException e)
|
||||
{
|
||||
// TODO looks like a close is overtaking the 413 in SSL
|
||||
System.err.println("Investigate this "+e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.close();
|
||||
|
|
|
@ -292,6 +292,34 @@ public class ContextHandlerTest
|
|||
assertEquals("333",handler.getServletContext().getAttribute("ccc"));
|
||||
assertEquals(null,handler.getServletContext().getAttribute("ddd"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProtected() throws Exception
|
||||
{
|
||||
ContextHandler handler = new ContextHandler();
|
||||
String[] protectedTargets = {"/foo-inf", "/bar-inf"};
|
||||
handler.setProtectedTargets(protectedTargets);
|
||||
|
||||
assertTrue(handler.isProtectedTarget("/foo-inf/x/y/z"));
|
||||
assertFalse(handler.isProtectedTarget("/foo/x/y/z"));
|
||||
assertTrue(handler.isProtectedTarget("/foo-inf?x=y&z=1"));
|
||||
|
||||
protectedTargets = new String[4];
|
||||
System.arraycopy(handler.getProtectedTargets(), 0, protectedTargets, 0, 2);
|
||||
protectedTargets[2] = "/abc";
|
||||
protectedTargets[3] = "/def";
|
||||
handler.setProtectedTargets(protectedTargets);
|
||||
|
||||
assertTrue(handler.isProtectedTarget("/foo-inf/x/y/z"));
|
||||
assertFalse(handler.isProtectedTarget("/foo/x/y/z"));
|
||||
assertTrue(handler.isProtectedTarget("/foo-inf?x=y&z=1"));
|
||||
assertTrue(handler.isProtectedTarget("/abc/124"));
|
||||
assertTrue(handler.isProtectedTarget("//def"));
|
||||
|
||||
assertTrue(handler.isProtectedTarget("/ABC/7777"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void checkResourcePathsForExampleWebApp(String root) throws IOException
|
||||
{
|
||||
|
|
|
@ -497,6 +497,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
if (gzip)
|
||||
{
|
||||
response.setHeader(HttpHeaders.CONTENT_ENCODING,"gzip");
|
||||
response.setHeader(HttpHeaders.VARY,HttpHeaders.ACCEPT_ENCODING);
|
||||
String mt=_servletContext.getMimeType(pathInContext);
|
||||
if (mt!=null)
|
||||
response.setContentType(mt);
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<name>Jetty :: SPDY :: Parent</name>
|
||||
|
||||
<properties>
|
||||
<npn.version>1.0.0.v20120402</npn.version>
|
||||
<npn.version>1.1.0.v20120525</npn.version>
|
||||
</properties>
|
||||
|
||||
<modules>
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright (c) 2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.eclipse.jetty.spdy;
|
||||
|
||||
import org.eclipse.jetty.spdy.api.DataInfo;
|
||||
|
||||
// TODO: add methods that tell how much written and whether we're TCP congested ?
|
||||
public interface FlowControlStrategy
|
||||
{
|
||||
public int getWindowSize(ISession session);
|
||||
|
||||
public void setWindowSize(ISession session, int windowSize);
|
||||
|
||||
public void onNewStream(ISession session, IStream stream);
|
||||
|
||||
public void onWindowUpdate(ISession session, IStream stream, int delta);
|
||||
|
||||
public void updateWindow(ISession session, IStream stream, int delta);
|
||||
|
||||
public void onDataReceived(ISession session, IStream stream, DataInfo dataInfo);
|
||||
|
||||
public void onDataConsumed(ISession session, IStream stream, DataInfo dataInfo, int delta);
|
||||
|
||||
public static class None implements FlowControlStrategy
|
||||
{
|
||||
private volatile int windowSize;
|
||||
|
||||
public None()
|
||||
{
|
||||
this(65536);
|
||||
}
|
||||
|
||||
public None(int windowSize)
|
||||
{
|
||||
this.windowSize = windowSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWindowSize(ISession session)
|
||||
{
|
||||
return windowSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWindowSize(ISession session, int windowSize)
|
||||
{
|
||||
this.windowSize = windowSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewStream(ISession session, IStream stream)
|
||||
{
|
||||
stream.updateWindowSize(windowSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowUpdate(ISession session, IStream stream, int delta)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateWindow(ISession session, IStream stream, int delta)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataReceived(ISession session, IStream stream, DataInfo dataInfo)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataConsumed(ISession session, IStream stream, DataInfo dataInfo, int delta)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,14 +16,12 @@
|
|||
|
||||
package org.eclipse.jetty.spdy;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jetty.spdy.api.DataInfo;
|
||||
import org.eclipse.jetty.spdy.api.SessionFrameListener;
|
||||
import org.eclipse.jetty.spdy.api.Stream;
|
||||
import org.eclipse.jetty.spdy.api.StreamFrameListener;
|
||||
import org.eclipse.jetty.spdy.api.SynInfo;
|
||||
import org.eclipse.jetty.spdy.frames.ControlFrame;
|
||||
import org.eclipse.jetty.spdy.frames.DataFrame;
|
||||
|
||||
/**
|
||||
* <p>The internal interface that represents a stream.</p>
|
||||
|
@ -77,37 +75,36 @@ public interface IStream extends Stream
|
|||
* for example by updating the stream's state or by calling listeners.</p>
|
||||
*
|
||||
* @param frame the control frame to process
|
||||
* @see #process(DataFrame, ByteBuffer)
|
||||
* @see #process(DataInfo)
|
||||
*/
|
||||
public void process(ControlFrame frame);
|
||||
|
||||
/**
|
||||
* <p>Processes the given data frame along with the given byte buffer,
|
||||
* <p>Processes the given {@code dataInfo},
|
||||
* for example by updating the stream's state or by calling listeners.</p>
|
||||
*
|
||||
* @param frame the data frame to process
|
||||
* @param data the byte buffer to process
|
||||
* @param dataInfo the DataInfo to process
|
||||
* @see #process(ControlFrame)
|
||||
*/
|
||||
public void process(DataFrame frame, ByteBuffer data);
|
||||
|
||||
public void process(DataInfo dataInfo);
|
||||
|
||||
/**
|
||||
* <p>Associate the given {@link IStream} to this {@link IStream}.</p>
|
||||
*
|
||||
*
|
||||
* @param stream the stream to associate with this stream
|
||||
*/
|
||||
public void associate(IStream stream);
|
||||
|
||||
|
||||
/**
|
||||
* <p>remove the given associated {@link IStream} from this stream</p>
|
||||
*
|
||||
*
|
||||
* @param stream the stream to be removed
|
||||
*/
|
||||
public void disassociate(IStream stream);
|
||||
|
||||
|
||||
/**
|
||||
* <p>Overrides Stream.getAssociatedStream() to return an instance of IStream instead of Stream
|
||||
*
|
||||
*
|
||||
* @see Stream#getAssociatedStream()
|
||||
*/
|
||||
@Override
|
||||
|
|
|
@ -44,7 +44,8 @@ public class Promise<T> implements Handler<T>, Future<T>
|
|||
latch.countDown();
|
||||
}
|
||||
|
||||
public void failed(Throwable x)
|
||||
@Override
|
||||
public void failed(T context, Throwable x)
|
||||
{
|
||||
this.failure = x;
|
||||
latch.countDown();
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright (c) 2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.eclipse.jetty.spdy;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.spdy.api.DataInfo;
|
||||
import org.eclipse.jetty.spdy.api.Stream;
|
||||
import org.eclipse.jetty.spdy.frames.WindowUpdateFrame;
|
||||
|
||||
public class SPDYv3FlowControlStrategy implements FlowControlStrategy
|
||||
{
|
||||
private volatile int windowSize;
|
||||
|
||||
@Override
|
||||
public int getWindowSize(ISession session)
|
||||
{
|
||||
return windowSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWindowSize(ISession session, int windowSize)
|
||||
{
|
||||
int prevWindowSize = this.windowSize;
|
||||
this.windowSize = windowSize;
|
||||
for (Stream stream : session.getStreams())
|
||||
((IStream)stream).updateWindowSize(windowSize - prevWindowSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewStream(ISession session, IStream stream)
|
||||
{
|
||||
stream.updateWindowSize(windowSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowUpdate(ISession session, IStream stream, int delta)
|
||||
{
|
||||
if (stream != null)
|
||||
stream.updateWindowSize(delta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateWindow(ISession session, IStream stream, int delta)
|
||||
{
|
||||
stream.updateWindowSize(delta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataReceived(ISession session, IStream stream, DataInfo dataInfo)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataConsumed(ISession session, IStream stream, DataInfo dataInfo, int delta)
|
||||
{
|
||||
// This is the algorithm for flow control.
|
||||
// This method may be called multiple times with delta=1, but we only send a window
|
||||
// update when the whole dataInfo has been consumed.
|
||||
// Other policies may be to send window updates when consumed() is greater than
|
||||
// a certain threshold, etc. but for now the policy is not pluggable for simplicity.
|
||||
// Note that the frequency of window updates depends on the read buffer, that
|
||||
// should not be too smaller than the window size to avoid frequent window updates.
|
||||
// Therefore, a pluggable policy should be able to modify the read buffer capacity.
|
||||
int length = dataInfo.length();
|
||||
if (dataInfo.consumed() == length && !stream.isClosed() && length > 0)
|
||||
{
|
||||
WindowUpdateFrame windowUpdateFrame = new WindowUpdateFrame(session.getVersion(), stream.getId(), length);
|
||||
session.control(stream, windowUpdateFrame, 0, TimeUnit.MILLISECONDS, null, null);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,7 +20,6 @@ import org.eclipse.jetty.spdy.api.SessionStatus;
|
|||
|
||||
public class SessionException extends RuntimeException
|
||||
{
|
||||
|
||||
private final SessionStatus sessionStatus;
|
||||
|
||||
public SessionException(SessionStatus sessionStatus)
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.eclipse.jetty.spdy;
|
|||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.InterruptedByTimeoutException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
@ -33,6 +34,7 @@ import java.util.concurrent.TimeUnit;
|
|||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
|
||||
import org.eclipse.jetty.spdy.api.DataInfo;
|
||||
import org.eclipse.jetty.spdy.api.GoAwayInfo;
|
||||
import org.eclipse.jetty.spdy.api.Handler;
|
||||
|
@ -50,6 +52,7 @@ import org.eclipse.jetty.spdy.api.StreamStatus;
|
|||
import org.eclipse.jetty.spdy.api.SynInfo;
|
||||
import org.eclipse.jetty.spdy.frames.ControlFrame;
|
||||
import org.eclipse.jetty.spdy.frames.ControlFrameType;
|
||||
import org.eclipse.jetty.spdy.frames.CredentialFrame;
|
||||
import org.eclipse.jetty.spdy.frames.DataFrame;
|
||||
import org.eclipse.jetty.spdy.frames.GoAwayFrame;
|
||||
import org.eclipse.jetty.spdy.frames.HeadersFrame;
|
||||
|
@ -92,11 +95,13 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
private final AtomicBoolean goAwaySent = new AtomicBoolean();
|
||||
private final AtomicBoolean goAwayReceived = new AtomicBoolean();
|
||||
private final AtomicInteger lastStreamId = new AtomicInteger();
|
||||
private final FlowControlStrategy flowControlStrategy;
|
||||
private boolean flushing;
|
||||
private volatile int windowSize = 65536;
|
||||
private boolean failed = false;
|
||||
|
||||
public StandardSession(short version, ByteBufferPool bufferPool, Executor threadPool, ScheduledExecutorService scheduler,
|
||||
Controller<FrameBytes> controller, IdleListener idleListener, int initialStreamId, SessionFrameListener listener, Generator generator)
|
||||
Controller<FrameBytes> controller, IdleListener idleListener, int initialStreamId, SessionFrameListener listener,
|
||||
Generator generator, FlowControlStrategy flowControlStrategy)
|
||||
{
|
||||
this.version = version;
|
||||
this.bufferPool = bufferPool;
|
||||
|
@ -108,6 +113,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
this.pingIds = new AtomicInteger(initialStreamId);
|
||||
this.listener = listener;
|
||||
this.generator = generator;
|
||||
this.flowControlStrategy = flowControlStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -152,7 +158,8 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
synchronized (this)
|
||||
{
|
||||
int streamId = streamIds.getAndAdd(2);
|
||||
SynStreamFrame synStream = new SynStreamFrame(version, synInfo.getFlags(), streamId, associatedStreamId, synInfo.getPriority(), synInfo.getHeaders());
|
||||
// TODO: for SPDYv3 we need to support the "slot" argument
|
||||
SynStreamFrame synStream = new SynStreamFrame(version, synInfo.getFlags(), streamId, associatedStreamId, synInfo.getPriority(), (short)0, synInfo.getHeaders());
|
||||
IStream stream = createStream(synStream, listener, true);
|
||||
generateAndEnqueueControlFrame(stream, synStream, timeout, unit, handler, stream);
|
||||
}
|
||||
|
@ -265,14 +272,14 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
@Override
|
||||
public void onControlFrame(ControlFrame frame)
|
||||
{
|
||||
notifyIdle(idleListener,false);
|
||||
notifyIdle(idleListener, false);
|
||||
try
|
||||
{
|
||||
logger.debug("Processing {}",frame);
|
||||
logger.debug("Processing {}", frame);
|
||||
|
||||
if (goAwaySent.get())
|
||||
{
|
||||
logger.debug("Skipped processing of {}",frame);
|
||||
logger.debug("Skipped processing of {}", frame);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -323,6 +330,11 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
onWindowUpdate((WindowUpdateFrame)frame);
|
||||
break;
|
||||
}
|
||||
case CREDENTIAL:
|
||||
{
|
||||
onCredential((CredentialFrame)frame);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new IllegalStateException();
|
||||
|
@ -331,7 +343,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
}
|
||||
finally
|
||||
{
|
||||
notifyIdle(idleListener,true);
|
||||
notifyIdle(idleListener, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -341,11 +353,11 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
notifyIdle(idleListener, false);
|
||||
try
|
||||
{
|
||||
logger.debug("Processing {}, {} data bytes",frame,data.remaining());
|
||||
logger.debug("Processing {}, {} data bytes", frame, data.remaining());
|
||||
|
||||
if (goAwaySent.get())
|
||||
{
|
||||
logger.debug("Skipped processing of {}",frame);
|
||||
logger.debug("Skipped processing of {}", frame);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -353,18 +365,18 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
IStream stream = streams.get(streamId);
|
||||
if (stream == null)
|
||||
{
|
||||
RstInfo rstInfo = new RstInfo(streamId,StreamStatus.INVALID_STREAM);
|
||||
logger.debug("Unknown stream {}",rstInfo);
|
||||
RstInfo rstInfo = new RstInfo(streamId, StreamStatus.INVALID_STREAM);
|
||||
logger.debug("Unknown stream {}", rstInfo);
|
||||
rst(rstInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
processData(stream,frame,data);
|
||||
processData(stream, frame, data);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
notifyIdle(idleListener,true);
|
||||
notifyIdle(idleListener, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -374,9 +386,19 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
listener.onIdle(idle);
|
||||
}
|
||||
|
||||
private void processData(IStream stream, DataFrame frame, ByteBuffer data)
|
||||
private void processData(final IStream stream, DataFrame frame, ByteBuffer data)
|
||||
{
|
||||
stream.process(frame,data);
|
||||
ByteBufferDataInfo dataInfo = new ByteBufferDataInfo(data, frame.isClose(), frame.isCompress())
|
||||
{
|
||||
@Override
|
||||
public void consume(int delta)
|
||||
{
|
||||
super.consume(delta);
|
||||
flowControlStrategy.onDataConsumed(StandardSession.this, stream, this, delta);
|
||||
}
|
||||
};
|
||||
flowControlStrategy.onDataReceived(this, stream, dataInfo);
|
||||
stream.process(dataInfo);
|
||||
updateLastStreamId(stream);
|
||||
if (stream.isClosed())
|
||||
removeStream(stream);
|
||||
|
@ -452,7 +474,9 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
private IStream newStream(SynStreamFrame frame)
|
||||
{
|
||||
IStream associatedStream = streams.get(frame.getAssociatedStreamId());
|
||||
return new StandardStream(frame, this, windowSize, associatedStream);
|
||||
IStream stream = new StandardStream(frame, this, associatedStream);
|
||||
flowControlStrategy.onNewStream(this, stream);
|
||||
return stream;
|
||||
}
|
||||
|
||||
private void notifyStreamCreated(IStream stream)
|
||||
|
@ -547,15 +571,12 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
Settings.Setting windowSizeSetting = frame.getSettings().get(Settings.ID.INITIAL_WINDOW_SIZE);
|
||||
if (windowSizeSetting != null)
|
||||
{
|
||||
int prevWindowSize = windowSize;
|
||||
windowSize = windowSizeSetting.value();
|
||||
for (IStream stream : streams.values())
|
||||
stream.updateWindowSize(windowSize - prevWindowSize);
|
||||
logger.debug("Updated window size to {}",windowSize);
|
||||
int windowSize = windowSizeSetting.value();
|
||||
setWindowSize(windowSize);
|
||||
logger.debug("Updated session window size to {}", windowSize);
|
||||
}
|
||||
|
||||
SettingsInfo settingsInfo = new SettingsInfo(frame.getSettings(),frame.isClearPersisted());
|
||||
notifyOnSettings(listener,settingsInfo);
|
||||
SettingsInfo settingsInfo = new SettingsInfo(frame.getSettings(), frame.isClearPersisted());
|
||||
notifyOnSettings(listener, settingsInfo);
|
||||
flush();
|
||||
}
|
||||
|
||||
|
@ -615,8 +636,14 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
{
|
||||
int streamId = frame.getStreamId();
|
||||
IStream stream = streams.get(streamId);
|
||||
if (stream != null)
|
||||
stream.process(frame);
|
||||
flowControlStrategy.onWindowUpdate(this, stream, frame.getWindowDelta());
|
||||
flush();
|
||||
}
|
||||
|
||||
private void onCredential(CredentialFrame frame)
|
||||
{
|
||||
logger.warn("{} frame not yet supported", ControlFrameType.CREDENTIAL);
|
||||
flush();
|
||||
}
|
||||
|
||||
protected void close()
|
||||
|
@ -735,11 +762,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
try
|
||||
{
|
||||
if (stream != null)
|
||||
{
|
||||
updateLastStreamId(stream);
|
||||
if (stream.isClosed())
|
||||
removeStream(stream);
|
||||
}
|
||||
|
||||
// Synchronization is necessary, since we may have concurrent replies
|
||||
// and those needs to be generated and enqueued atomically in order
|
||||
|
@ -747,10 +770,10 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
synchronized (this)
|
||||
{
|
||||
ByteBuffer buffer = generator.control(frame);
|
||||
logger.debug("Queuing {} on {}",frame,stream);
|
||||
ControlFrameBytes<C> frameBytes = new ControlFrameBytes<>(stream,handler,context,frame,buffer);
|
||||
logger.debug("Queuing {} on {}", frame, stream);
|
||||
ControlFrameBytes<C> frameBytes = new ControlFrameBytes<>(stream, handler, context, frame, buffer);
|
||||
if (timeout > 0)
|
||||
frameBytes.task = scheduler.schedule(frameBytes,timeout,unit);
|
||||
frameBytes.task = scheduler.schedule(frameBytes, timeout, unit);
|
||||
|
||||
// Special handling for PING frames, they must be sent as soon as possible
|
||||
if (ControlFrameType.PING == frame.getType())
|
||||
|
@ -759,9 +782,9 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
append(frameBytes);
|
||||
}
|
||||
}
|
||||
catch (Throwable x)
|
||||
catch (Exception x)
|
||||
{
|
||||
notifyHandlerFailed(handler, x);
|
||||
notifyHandlerFailed(handler, context, x);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -787,9 +810,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
logger.debug("Queuing {} on {}",dataInfo,stream);
|
||||
DataFrameBytes<C> frameBytes = new DataFrameBytes<>(stream,handler,context,dataInfo);
|
||||
if (timeout > 0)
|
||||
{
|
||||
frameBytes.task = scheduler.schedule(frameBytes,timeout,unit);
|
||||
}
|
||||
append(frameBytes);
|
||||
flush();
|
||||
}
|
||||
|
@ -822,9 +843,11 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
if (buffer != null)
|
||||
{
|
||||
queue.remove(i);
|
||||
// TODO: stream.isUniDirectional() check here is only needed for pushStreams which send a syn with close=true --> find a better solution
|
||||
if (stream != null && !streams.containsValue(stream) && !stream.isUnidirectional())
|
||||
if (stream != null && stream.isReset())
|
||||
{
|
||||
frameBytes.fail(new StreamException(stream.getId(),StreamStatus.INVALID_STREAM));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -847,34 +870,50 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
|
||||
private void append(FrameBytes frameBytes)
|
||||
{
|
||||
boolean fail;
|
||||
synchronized (queue)
|
||||
{
|
||||
int index = queue.size();
|
||||
while (index > 0)
|
||||
fail = failed;
|
||||
if (!fail)
|
||||
{
|
||||
FrameBytes element = queue.get(index - 1);
|
||||
if (element.compareTo(frameBytes) >= 0)
|
||||
break;
|
||||
--index;
|
||||
int index = queue.size();
|
||||
while (index > 0)
|
||||
{
|
||||
FrameBytes element = queue.get(index - 1);
|
||||
if (element.compareTo(frameBytes) >= 0)
|
||||
break;
|
||||
--index;
|
||||
}
|
||||
queue.add(index,frameBytes);
|
||||
}
|
||||
queue.add(index,frameBytes);
|
||||
}
|
||||
|
||||
if (fail)
|
||||
frameBytes.fail(new SPDYException("Session failed"));
|
||||
}
|
||||
|
||||
private void prepend(FrameBytes frameBytes)
|
||||
{
|
||||
boolean fail;
|
||||
synchronized (queue)
|
||||
{
|
||||
int index = 0;
|
||||
while (index < queue.size())
|
||||
fail = failed;
|
||||
if (!fail)
|
||||
{
|
||||
FrameBytes element = queue.get(index);
|
||||
if (element.compareTo(frameBytes) <= 0)
|
||||
break;
|
||||
++index;
|
||||
int index = 0;
|
||||
while (index < queue.size())
|
||||
{
|
||||
FrameBytes element = queue.get(index);
|
||||
if (element.compareTo(frameBytes) <= 0)
|
||||
break;
|
||||
++index;
|
||||
}
|
||||
queue.add(index,frameBytes);
|
||||
}
|
||||
queue.add(index,frameBytes);
|
||||
}
|
||||
|
||||
if (fail)
|
||||
frameBytes.fail(new SPDYException("Session failed"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -889,9 +928,23 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable x)
|
||||
public void failed(FrameBytes frameBytes, Throwable x)
|
||||
{
|
||||
throw new SPDYException(x);
|
||||
List<FrameBytes> frameBytesToFail = new ArrayList<>();
|
||||
frameBytesToFail.add(frameBytes);
|
||||
|
||||
synchronized (queue)
|
||||
{
|
||||
failed = true;
|
||||
String logMessage = String.format("Failed write of %s, failing all %d frame(s) in queue",frameBytes,queue.size());
|
||||
logger.debug(logMessage,x);
|
||||
frameBytesToFail.addAll(queue);
|
||||
queue.clear();
|
||||
flushing = false;
|
||||
}
|
||||
|
||||
for (FrameBytes fb : frameBytesToFail)
|
||||
fb.fail(x);
|
||||
}
|
||||
|
||||
protected void write(ByteBuffer buffer, Handler<FrameBytes> handler, FrameBytes frameBytes)
|
||||
|
@ -951,12 +1004,12 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
}
|
||||
}
|
||||
|
||||
private <C> void notifyHandlerFailed(Handler<C> handler, Throwable x)
|
||||
private <C> void notifyHandlerFailed(Handler<C> handler, C context, Throwable x)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (handler != null)
|
||||
handler.failed(x);
|
||||
handler.failed(context, x);
|
||||
}
|
||||
catch (Exception xx)
|
||||
{
|
||||
|
@ -964,6 +1017,16 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
}
|
||||
}
|
||||
|
||||
public int getWindowSize()
|
||||
{
|
||||
return flowControlStrategy.getWindowSize(this);
|
||||
}
|
||||
|
||||
public void setWindowSize(int initialWindowSize)
|
||||
{
|
||||
flowControlStrategy.setWindowSize(this, initialWindowSize);
|
||||
}
|
||||
|
||||
public interface FrameBytes extends Comparable<FrameBytes>
|
||||
{
|
||||
public IStream getStream();
|
||||
|
@ -998,8 +1061,16 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
@Override
|
||||
public int compareTo(FrameBytes that)
|
||||
{
|
||||
// If this.stream.priority > that.stream.priority => -1 (this.stream has less priority than that.stream)
|
||||
return that.getStream().getPriority() - getStream().getPriority();
|
||||
// FrameBytes may have or not have a related stream (for example, PING do not have a related stream)
|
||||
// FrameBytes without related streams have higher priority
|
||||
IStream thisStream = getStream();
|
||||
IStream thatStream = that.getStream();
|
||||
if (thisStream == null)
|
||||
return thatStream == null ? 0 : -1;
|
||||
if (thatStream == null)
|
||||
return 1;
|
||||
// If this.stream.priority > that.stream.priority => this.stream has less priority than that.stream
|
||||
return thatStream.getPriority() - thisStream.getPriority();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1013,7 +1084,8 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
public void fail(Throwable x)
|
||||
{
|
||||
cancelTask();
|
||||
notifyHandlerFailed(handler,x);
|
||||
notifyHandlerFailed(handler,context,x);
|
||||
StandardSession.this.flush();
|
||||
}
|
||||
|
||||
private void cancelTask()
|
||||
|
@ -1062,6 +1134,9 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
// Recipients will know the last good stream id and act accordingly.
|
||||
close();
|
||||
}
|
||||
IStream stream = getStream();
|
||||
if (stream != null && stream.isClosed())
|
||||
removeStream(stream);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1112,14 +1187,14 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
|
|||
{
|
||||
bufferPool.release(buffer);
|
||||
IStream stream = getStream();
|
||||
stream.updateWindowSize(-size);
|
||||
|
||||
flowControlStrategy.updateWindow(StandardSession.this, stream, -size);
|
||||
if (dataInfo.available() > 0)
|
||||
{
|
||||
// We have written a frame out of this DataInfo, but there is more to write.
|
||||
// We need to keep the correct ordering of frames, to avoid that another
|
||||
// DataInfo for the same stream is written before this one is finished.
|
||||
prepend(this);
|
||||
flush();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package org.eclipse.jetty.spdy;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -25,7 +24,6 @@ import java.util.concurrent.Future;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
|
||||
import org.eclipse.jetty.spdy.api.DataInfo;
|
||||
import org.eclipse.jetty.spdy.api.Handler;
|
||||
import org.eclipse.jetty.spdy.api.HeadersInfo;
|
||||
|
@ -37,11 +35,9 @@ import org.eclipse.jetty.spdy.api.StreamFrameListener;
|
|||
import org.eclipse.jetty.spdy.api.StreamStatus;
|
||||
import org.eclipse.jetty.spdy.api.SynInfo;
|
||||
import org.eclipse.jetty.spdy.frames.ControlFrame;
|
||||
import org.eclipse.jetty.spdy.frames.DataFrame;
|
||||
import org.eclipse.jetty.spdy.frames.HeadersFrame;
|
||||
import org.eclipse.jetty.spdy.frames.SynReplyFrame;
|
||||
import org.eclipse.jetty.spdy.frames.SynStreamFrame;
|
||||
import org.eclipse.jetty.spdy.frames.WindowUpdateFrame;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
|
@ -52,18 +48,17 @@ public class StandardStream implements IStream
|
|||
private final IStream associatedStream;
|
||||
private final SynStreamFrame frame;
|
||||
private final ISession session;
|
||||
private final AtomicInteger windowSize;
|
||||
private final AtomicInteger windowSize = new AtomicInteger();
|
||||
private final Set<Stream> pushedStreams = Collections.newSetFromMap(new ConcurrentHashMap<Stream, Boolean>());
|
||||
private volatile StreamFrameListener listener;
|
||||
private volatile OpenState openState = OpenState.SYN_SENT;
|
||||
private volatile CloseState closeState = CloseState.OPENED;
|
||||
private volatile boolean reset = false;
|
||||
|
||||
public StandardStream(SynStreamFrame frame, ISession session, int windowSize, IStream associatedStream)
|
||||
public StandardStream(SynStreamFrame frame, ISession session, IStream associatedStream)
|
||||
{
|
||||
this.frame = frame;
|
||||
this.session = session;
|
||||
this.windowSize = new AtomicInteger(windowSize);
|
||||
this.associatedStream = associatedStream;
|
||||
}
|
||||
|
||||
|
@ -113,7 +108,7 @@ public class StandardStream implements IStream
|
|||
public void updateWindowSize(int delta)
|
||||
{
|
||||
int size = windowSize.addAndGet(delta);
|
||||
logger.debug("Updated window size by {}, new window size {}",delta,size);
|
||||
logger.debug("Updated window size {} -> {} for {}", size - delta, size, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -209,12 +204,6 @@ public class StandardStream implements IStream
|
|||
notifyOnHeaders(headersInfo);
|
||||
break;
|
||||
}
|
||||
case WINDOW_UPDATE:
|
||||
{
|
||||
WindowUpdateFrame windowUpdate = (WindowUpdateFrame)frame;
|
||||
updateWindowSize(windowUpdate.getWindowDelta());
|
||||
break;
|
||||
}
|
||||
case RST_STREAM:
|
||||
{
|
||||
reset = true;
|
||||
|
@ -229,57 +218,28 @@ public class StandardStream implements IStream
|
|||
}
|
||||
|
||||
@Override
|
||||
public void process(DataFrame frame, ByteBuffer data)
|
||||
public void process(DataInfo dataInfo)
|
||||
{
|
||||
// TODO: in v3 we need to send a rst instead of just ignoring
|
||||
// ignore data frame if this stream is remotelyClosed already
|
||||
if (isHalfClosed() && !isLocallyClosed())
|
||||
if (isRemotelyClosed())
|
||||
{
|
||||
logger.debug("Ignoring received dataFrame as this stream is remotely closed: " + frame);
|
||||
logger.debug("Stream is remotely closed, ignoring {}", dataInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!canReceive())
|
||||
{
|
||||
logger.debug("Can't receive. Sending rst: " + frame);
|
||||
session.rst(new RstInfo(getId(),StreamStatus.PROTOCOL_ERROR));
|
||||
logger.debug("Protocol error receiving {}, resetting" + dataInfo);
|
||||
session.rst(new RstInfo(getId(), StreamStatus.PROTOCOL_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
updateCloseState(frame.isClose(),false);
|
||||
|
||||
ByteBufferDataInfo dataInfo = new ByteBufferDataInfo(data,frame.isClose(),frame.isCompress())
|
||||
{
|
||||
@Override
|
||||
public void consume(int delta)
|
||||
{
|
||||
super.consume(delta);
|
||||
|
||||
// This is the algorithm for flow control.
|
||||
// This method may be called multiple times with delta=1, but we only send a window
|
||||
// update when the whole dataInfo has been consumed.
|
||||
// Other policies may be to send window updates when consumed() is greater than
|
||||
// a certain threshold, etc. but for now the policy is not pluggable for simplicity.
|
||||
// Note that the frequency of window updates depends on the read buffer, that
|
||||
// should not be too smaller than the window size to avoid frequent window updates.
|
||||
// Therefore, a pluggable policy should be able to modify the read buffer capacity.
|
||||
if (consumed() == length() && !isClosed())
|
||||
windowUpdate(length());
|
||||
}
|
||||
};
|
||||
updateCloseState(dataInfo.isClose(), false);
|
||||
notifyOnData(dataInfo);
|
||||
session.flush();
|
||||
}
|
||||
|
||||
private void windowUpdate(int delta)
|
||||
{
|
||||
if (delta > 0)
|
||||
{
|
||||
WindowUpdateFrame windowUpdateFrame = new WindowUpdateFrame(session.getVersion(),getId(),delta);
|
||||
session.control(this,windowUpdateFrame,0,TimeUnit.MILLISECONDS,null,null);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyOnReply(ReplyInfo replyInfo)
|
||||
{
|
||||
final StreamFrameListener listener = this.listener;
|
||||
|
@ -305,7 +265,7 @@ public class StandardStream implements IStream
|
|||
if (listener != null)
|
||||
{
|
||||
logger.debug("Invoking headers callback with {} on listener {}",frame,listener);
|
||||
listener.onHeaders(this,headersInfo);
|
||||
listener.onHeaders(this, headersInfo);
|
||||
}
|
||||
}
|
||||
catch (Exception x)
|
||||
|
@ -322,8 +282,8 @@ public class StandardStream implements IStream
|
|||
if (listener != null)
|
||||
{
|
||||
logger.debug("Invoking data callback with {} on listener {}",dataInfo,listener);
|
||||
listener.onData(this,dataInfo);
|
||||
logger.debug("Invoked data callback with {} on listener {}",dataInfo,listener);
|
||||
listener.onData(this, dataInfo);
|
||||
logger.debug("Invoked data callback with {} on listener {}", dataInfo, listener);
|
||||
}
|
||||
}
|
||||
catch (Exception x)
|
||||
|
@ -345,7 +305,7 @@ public class StandardStream implements IStream
|
|||
{
|
||||
if (isClosed() || isReset())
|
||||
{
|
||||
handler.failed(new StreamException(getId(),StreamStatus.STREAM_ALREADY_CLOSED));
|
||||
handler.failed(this, new StreamException(getId(),StreamStatus.STREAM_ALREADY_CLOSED));
|
||||
return;
|
||||
}
|
||||
PushSynInfo pushSynInfo = new PushSynInfo(getId(),synInfo);
|
||||
|
@ -456,6 +416,12 @@ public class StandardStream implements IStream
|
|||
return closeState == CloseState.LOCALLY_CLOSED || closeState == CloseState.CLOSED;
|
||||
}
|
||||
|
||||
private boolean isRemotelyClosed()
|
||||
{
|
||||
CloseState closeState = this.closeState;
|
||||
return closeState == CloseState.REMOTELY_CLOSED || closeState == CloseState.CLOSED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
|
|
|
@ -23,39 +23,44 @@ import java.nio.ByteBuffer;
|
|||
*/
|
||||
public class BytesDataInfo extends DataInfo
|
||||
{
|
||||
private byte[] bytes;
|
||||
private int offset;
|
||||
private final byte[] bytes;
|
||||
private final int offset;
|
||||
private final int length;
|
||||
private int index;
|
||||
|
||||
public BytesDataInfo(byte[] bytes, boolean close)
|
||||
{
|
||||
this(bytes, close, false);
|
||||
this(bytes, 0, bytes.length, close);
|
||||
}
|
||||
|
||||
public BytesDataInfo(byte[] bytes, boolean close, boolean compress)
|
||||
public BytesDataInfo(byte[] bytes, int offset, int length, boolean close)
|
||||
{
|
||||
super(close, compress);
|
||||
super(close, false);
|
||||
this.bytes = bytes;
|
||||
this.offset = offset;
|
||||
this.length = length;
|
||||
this.index = offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length()
|
||||
{
|
||||
return bytes.length;
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available()
|
||||
{
|
||||
return length() - offset;
|
||||
return length - index + offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readInto(ByteBuffer output)
|
||||
{
|
||||
int space = output.remaining();
|
||||
int length = Math.min(available(), space);
|
||||
output.put(bytes, offset, length);
|
||||
offset += length;
|
||||
return length;
|
||||
int chunk = Math.min(available(), space);
|
||||
output.put(bytes, index, chunk);
|
||||
index += chunk;
|
||||
return chunk;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,16 +28,16 @@ public interface Handler<C>
|
|||
* <p>Callback invoked when the operation completes.</p>
|
||||
*
|
||||
* @param context the context
|
||||
* @see #failed(Throwable)
|
||||
* @see #failed(Object, Throwable)
|
||||
*/
|
||||
public abstract void completed(C context);
|
||||
|
||||
/**
|
||||
* <p>Callback invoked when the operation fails.</p>
|
||||
*
|
||||
* @param context the context
|
||||
* @param x the reason for the operation failure
|
||||
*/
|
||||
public void failed(Throwable x);
|
||||
public void failed(C context, Throwable x);
|
||||
|
||||
/**
|
||||
* <p>Empty implementation of {@link Handler}</p>
|
||||
|
@ -52,9 +52,8 @@ public interface Handler<C>
|
|||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable x)
|
||||
public void failed(C context, Throwable x)
|
||||
{
|
||||
throw new SPDYException(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,11 +25,6 @@ public class StringDataInfo extends BytesDataInfo
|
|||
{
|
||||
public StringDataInfo(String string, boolean close)
|
||||
{
|
||||
this(string, close, false);
|
||||
}
|
||||
|
||||
public StringDataInfo(String string, boolean close, boolean compress)
|
||||
{
|
||||
super(string.getBytes(Charset.forName("UTF-8")), close, compress);
|
||||
super(string.getBytes(Charset.forName("UTF-8")), close);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,8 @@ public enum ControlFrameType
|
|||
PING((short)6),
|
||||
GO_AWAY((short)7),
|
||||
HEADERS((short)8),
|
||||
WINDOW_UPDATE((short)9);
|
||||
WINDOW_UPDATE((short)9),
|
||||
CREDENTIAL((short)10);
|
||||
|
||||
public static ControlFrameType from(short code)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright (c) 2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.eclipse.jetty.spdy.frames;
|
||||
|
||||
import java.security.cert.Certificate;
|
||||
|
||||
public class CredentialFrame extends ControlFrame
|
||||
{
|
||||
private final short slot;
|
||||
private final byte[] proof;
|
||||
private final Certificate[] certificateChain;
|
||||
|
||||
public CredentialFrame(short version, short slot, byte[] proof, Certificate[] certificateChain)
|
||||
{
|
||||
super(version, ControlFrameType.CREDENTIAL, (byte)0);
|
||||
this.slot = slot;
|
||||
this.proof = proof;
|
||||
this.certificateChain = certificateChain;
|
||||
}
|
||||
|
||||
public short getSlot()
|
||||
{
|
||||
return slot;
|
||||
}
|
||||
|
||||
public byte[] getProof()
|
||||
{
|
||||
return proof;
|
||||
}
|
||||
|
||||
public Certificate[] getCertificateChain()
|
||||
{
|
||||
return certificateChain;
|
||||
}
|
||||
}
|
|
@ -25,14 +25,16 @@ public class SynStreamFrame extends ControlFrame
|
|||
private final int streamId;
|
||||
private final int associatedStreamId;
|
||||
private final byte priority;
|
||||
private final short slot;
|
||||
private final Headers headers;
|
||||
|
||||
public SynStreamFrame(short version, byte flags, int streamId, int associatedStreamId, byte priority, Headers headers)
|
||||
public SynStreamFrame(short version, byte flags, int streamId, int associatedStreamId, byte priority, short slot, Headers headers)
|
||||
{
|
||||
super(version, ControlFrameType.SYN_STREAM, flags);
|
||||
this.streamId = streamId;
|
||||
this.associatedStreamId = associatedStreamId;
|
||||
this.priority = priority;
|
||||
this.slot = slot;
|
||||
this.headers = headers;
|
||||
}
|
||||
|
||||
|
@ -51,6 +53,11 @@ public class SynStreamFrame extends ControlFrame
|
|||
return priority;
|
||||
}
|
||||
|
||||
public short getSlot()
|
||||
{
|
||||
return slot;
|
||||
}
|
||||
|
||||
public Headers getHeaders()
|
||||
{
|
||||
return headers;
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Copyright (c) 2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.eclipse.jetty.spdy.generator;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jetty.spdy.ByteBufferPool;
|
||||
import org.eclipse.jetty.spdy.SessionException;
|
||||
import org.eclipse.jetty.spdy.api.SessionStatus;
|
||||
import org.eclipse.jetty.spdy.frames.ControlFrame;
|
||||
import org.eclipse.jetty.spdy.frames.CredentialFrame;
|
||||
|
||||
public class CredentialGenerator extends ControlFrameGenerator
|
||||
{
|
||||
public CredentialGenerator(ByteBufferPool bufferPool)
|
||||
{
|
||||
super(bufferPool);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer generate(ControlFrame frame)
|
||||
{
|
||||
CredentialFrame credential = (CredentialFrame)frame;
|
||||
|
||||
byte[] proof = credential.getProof();
|
||||
|
||||
List<byte[]> certificates = serializeCertificates(credential.getCertificateChain());
|
||||
int certificatesLength = 0;
|
||||
for (byte[] certificate : certificates)
|
||||
certificatesLength += certificate.length;
|
||||
|
||||
int frameBodyLength = 2 + 4 + proof.length + certificates.size() * 4 + certificatesLength;
|
||||
|
||||
int totalLength = ControlFrame.HEADER_LENGTH + frameBodyLength;
|
||||
ByteBuffer buffer = getByteBufferPool().acquire(totalLength, true);
|
||||
generateControlFrameHeader(credential, frameBodyLength, buffer);
|
||||
|
||||
buffer.putShort(credential.getSlot());
|
||||
buffer.putInt(proof.length);
|
||||
buffer.put(proof);
|
||||
for (byte[] certificate : certificates)
|
||||
{
|
||||
buffer.putInt(certificate.length);
|
||||
buffer.put(certificate);
|
||||
}
|
||||
|
||||
buffer.flip();
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private List<byte[]> serializeCertificates(Certificate[] certificates)
|
||||
{
|
||||
try
|
||||
{
|
||||
List<byte[]> result = new ArrayList<>(certificates.length);
|
||||
for (Certificate certificate : certificates)
|
||||
result.add(certificate.getEncoded());
|
||||
return result;
|
||||
}
|
||||
catch (CertificateEncodingException x)
|
||||
{
|
||||
throw new SessionException(SessionStatus.PROTOCOL_ERROR, x);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -42,6 +42,7 @@ public class Generator
|
|||
generators.put(ControlFrameType.GO_AWAY, new GoAwayGenerator(bufferPool));
|
||||
generators.put(ControlFrameType.HEADERS, new HeadersGenerator(bufferPool, headersBlockGenerator));
|
||||
generators.put(ControlFrameType.WINDOW_UPDATE, new WindowUpdateGenerator(bufferPool));
|
||||
generators.put(ControlFrameType.CREDENTIAL, new CredentialGenerator(bufferPool));
|
||||
|
||||
dataFrameGenerator = new DataFrameGenerator(bufferPool);
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.nio.ByteBuffer;
|
|||
|
||||
import org.eclipse.jetty.spdy.ByteBufferPool;
|
||||
import org.eclipse.jetty.spdy.SessionException;
|
||||
import org.eclipse.jetty.spdy.api.SPDY;
|
||||
import org.eclipse.jetty.spdy.api.SessionStatus;
|
||||
import org.eclipse.jetty.spdy.frames.ControlFrame;
|
||||
import org.eclipse.jetty.spdy.frames.HeadersFrame;
|
||||
|
@ -43,6 +44,8 @@ public class HeadersGenerator extends ControlFrameGenerator
|
|||
ByteBuffer headersBuffer = headersBlockGenerator.generate(version, headers.getHeaders());
|
||||
|
||||
int frameBodyLength = 4;
|
||||
if (frame.getVersion() == SPDY.V2)
|
||||
frameBodyLength += 2;
|
||||
|
||||
int frameLength = frameBodyLength + headersBuffer.remaining();
|
||||
if (frameLength > 0xFF_FF_FF)
|
||||
|
@ -58,6 +61,8 @@ public class HeadersGenerator extends ControlFrameGenerator
|
|||
generateControlFrameHeader(headers, frameLength, buffer);
|
||||
|
||||
buffer.putInt(headers.getStreamId() & 0x7F_FF_FF_FF);
|
||||
if (frame.getVersion() == SPDY.V2)
|
||||
buffer.putShort((short)0);
|
||||
|
||||
buffer.put(headersBuffer);
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@ public class SynStreamGenerator extends ControlFrameGenerator
|
|||
buffer.putInt(streamId & 0x7F_FF_FF_FF);
|
||||
buffer.putInt(synStream.getAssociatedStreamId() & 0x7F_FF_FF_FF);
|
||||
writePriority(streamId, version, synStream.getPriority(), buffer);
|
||||
buffer.put((byte)synStream.getSlot());
|
||||
|
||||
buffer.put(headersBuffer);
|
||||
|
||||
|
@ -85,6 +86,5 @@ public class SynStreamGenerator extends ControlFrameGenerator
|
|||
throw new StreamException(streamId, StreamStatus.UNSUPPORTED_VERSION);
|
||||
}
|
||||
buffer.put(priority);
|
||||
buffer.put((byte)0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ public abstract class ControlFrameParser
|
|||
parsers.put(ControlFrameType.GO_AWAY, new GoAwayBodyParser(this));
|
||||
parsers.put(ControlFrameType.HEADERS, new HeadersBodyParser(decompressor, this));
|
||||
parsers.put(ControlFrameType.WINDOW_UPDATE, new WindowUpdateBodyParser(this));
|
||||
parsers.put(ControlFrameType.CREDENTIAL, new CredentialBodyParser(this));
|
||||
}
|
||||
|
||||
public short getVersion()
|
||||
|
|
|
@ -0,0 +1,272 @@
|
|||
/*
|
||||
* Copyright (c) 2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.eclipse.jetty.spdy.parser;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jetty.spdy.SessionException;
|
||||
import org.eclipse.jetty.spdy.api.SessionStatus;
|
||||
import org.eclipse.jetty.spdy.frames.ControlFrameType;
|
||||
import org.eclipse.jetty.spdy.frames.CredentialFrame;
|
||||
|
||||
public class CredentialBodyParser extends ControlFrameBodyParser
|
||||
{
|
||||
private final List<Certificate> certificates = new ArrayList<>();
|
||||
private final ControlFrameParser controlFrameParser;
|
||||
private State state = State.SLOT;
|
||||
private int totalLength;
|
||||
private int cursor;
|
||||
private short slot;
|
||||
private int proofLength;
|
||||
private byte[] proof;
|
||||
private int certificateLength;
|
||||
private byte[] certificate;
|
||||
|
||||
public CredentialBodyParser(ControlFrameParser controlFrameParser)
|
||||
{
|
||||
this.controlFrameParser = controlFrameParser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean parse(ByteBuffer buffer)
|
||||
{
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case SLOT:
|
||||
{
|
||||
if (buffer.remaining() >= 2)
|
||||
{
|
||||
slot = buffer.getShort();
|
||||
checkSlotValid();
|
||||
state = State.PROOF_LENGTH;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = State.SLOT_BYTES;
|
||||
cursor = 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SLOT_BYTES:
|
||||
{
|
||||
byte currByte = buffer.get();
|
||||
--cursor;
|
||||
slot += (currByte & 0xFF) << 8 * cursor;
|
||||
if (cursor == 0)
|
||||
{
|
||||
checkSlotValid();
|
||||
state = State.PROOF_LENGTH;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PROOF_LENGTH:
|
||||
{
|
||||
if (buffer.remaining() >= 4)
|
||||
{
|
||||
proofLength = buffer.getInt() & 0x7F_FF_FF_FF;
|
||||
state = State.PROOF;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = State.PROOF_LENGTH_BYTES;
|
||||
cursor = 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PROOF_LENGTH_BYTES:
|
||||
{
|
||||
byte currByte = buffer.get();
|
||||
--cursor;
|
||||
proofLength += (currByte & 0xFF) << 8 * cursor;
|
||||
if (cursor == 0)
|
||||
{
|
||||
proofLength &= 0x7F_FF_FF_FF;
|
||||
state = State.PROOF;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PROOF:
|
||||
{
|
||||
totalLength = controlFrameParser.getLength() - 2 - 4 - proofLength;
|
||||
proof = new byte[proofLength];
|
||||
if (buffer.remaining() >= proofLength)
|
||||
{
|
||||
buffer.get(proof);
|
||||
state = State.CERTIFICATE_LENGTH;
|
||||
if (totalLength == 0)
|
||||
{
|
||||
onCredential();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state = State.PROOF_BYTES;
|
||||
cursor = proofLength;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PROOF_BYTES:
|
||||
{
|
||||
proof[proofLength - cursor] = buffer.get();
|
||||
--cursor;
|
||||
if (cursor == 0)
|
||||
{
|
||||
state = State.CERTIFICATE_LENGTH;
|
||||
if (totalLength == 0)
|
||||
{
|
||||
onCredential();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CERTIFICATE_LENGTH:
|
||||
{
|
||||
if (buffer.remaining() >= 4)
|
||||
{
|
||||
certificateLength = buffer.getInt() & 0x7F_FF_FF_FF;
|
||||
state = State.CERTIFICATE;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = State.CERTIFICATE_LENGTH_BYTES;
|
||||
cursor = 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CERTIFICATE_LENGTH_BYTES:
|
||||
{
|
||||
byte currByte = buffer.get();
|
||||
--cursor;
|
||||
certificateLength += (currByte & 0xFF) << 8 * cursor;
|
||||
if (cursor == 0)
|
||||
{
|
||||
certificateLength &= 0x7F_FF_FF_FF;
|
||||
state = State.CERTIFICATE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CERTIFICATE:
|
||||
{
|
||||
totalLength -= 4 + certificateLength;
|
||||
certificate = new byte[certificateLength];
|
||||
if (buffer.remaining() >= certificateLength)
|
||||
{
|
||||
buffer.get(certificate);
|
||||
if (onCertificate())
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = State.CERTIFICATE_BYTES;
|
||||
cursor = certificateLength;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CERTIFICATE_BYTES:
|
||||
{
|
||||
certificate[certificateLength - cursor] = buffer.get();
|
||||
--cursor;
|
||||
if (cursor == 0)
|
||||
{
|
||||
if (onCertificate())
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void checkSlotValid()
|
||||
{
|
||||
if (slot <= 0)
|
||||
throw new SessionException(SessionStatus.PROTOCOL_ERROR,
|
||||
"Invalid slot " + slot + " for " + ControlFrameType.CREDENTIAL + " frame");
|
||||
}
|
||||
|
||||
private boolean onCertificate()
|
||||
{
|
||||
certificates.add(deserializeCertificate(certificate));
|
||||
if (totalLength == 0)
|
||||
{
|
||||
onCredential();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
certificateLength = 0;
|
||||
state = State.CERTIFICATE_LENGTH;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Certificate deserializeCertificate(byte[] bytes)
|
||||
{
|
||||
try
|
||||
{
|
||||
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
|
||||
return certificateFactory.generateCertificate(new ByteArrayInputStream(bytes));
|
||||
}
|
||||
catch (CertificateException x)
|
||||
{
|
||||
throw new SessionException(SessionStatus.PROTOCOL_ERROR, x);
|
||||
}
|
||||
}
|
||||
|
||||
private void onCredential()
|
||||
{
|
||||
CredentialFrame frame = new CredentialFrame(controlFrameParser.getVersion(), slot,
|
||||
Arrays.copyOf(proof, proof.length), certificates.toArray(new Certificate[certificates.size()]));
|
||||
controlFrameParser.onControlFrame(frame);
|
||||
reset();
|
||||
}
|
||||
|
||||
private void reset()
|
||||
{
|
||||
state = State.SLOT;
|
||||
totalLength = 0;
|
||||
cursor = 0;
|
||||
slot = 0;
|
||||
proofLength = 0;
|
||||
proof = null;
|
||||
certificateLength = 0;
|
||||
certificate = null;
|
||||
certificates.clear();
|
||||
}
|
||||
|
||||
public enum State
|
||||
{
|
||||
SLOT, SLOT_BYTES, PROOF_LENGTH, PROOF_LENGTH_BYTES, PROOF, PROOF_BYTES,
|
||||
CERTIFICATE_LENGTH, CERTIFICATE_LENGTH_BYTES, CERTIFICATE, CERTIFICATE_BYTES
|
||||
}
|
||||
}
|
|
@ -24,7 +24,7 @@ import org.eclipse.jetty.spdy.frames.GoAwayFrame;
|
|||
public class GoAwayBodyParser extends ControlFrameBodyParser
|
||||
{
|
||||
private final ControlFrameParser controlFrameParser;
|
||||
private State state = State.LAST_STREAM_ID;
|
||||
private State state = State.LAST_GOOD_STREAM_ID;
|
||||
private int cursor;
|
||||
private int lastStreamId;
|
||||
private int statusCode;
|
||||
|
@ -41,7 +41,7 @@ public class GoAwayBodyParser extends ControlFrameBodyParser
|
|||
{
|
||||
switch (state)
|
||||
{
|
||||
case LAST_STREAM_ID:
|
||||
case LAST_GOOD_STREAM_ID:
|
||||
{
|
||||
if (buffer.remaining() >= 4)
|
||||
{
|
||||
|
@ -66,12 +66,12 @@ public class GoAwayBodyParser extends ControlFrameBodyParser
|
|||
}
|
||||
else
|
||||
{
|
||||
state = State.LAST_STREAM_ID_BYTES;
|
||||
state = State.LAST_GOOD_STREAM_ID_BYTES;
|
||||
cursor = 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LAST_STREAM_ID_BYTES:
|
||||
case LAST_GOOD_STREAM_ID_BYTES:
|
||||
{
|
||||
byte currByte = buffer.get();
|
||||
--cursor;
|
||||
|
@ -144,7 +144,7 @@ public class GoAwayBodyParser extends ControlFrameBodyParser
|
|||
|
||||
private void reset()
|
||||
{
|
||||
state = State.LAST_STREAM_ID;
|
||||
state = State.LAST_GOOD_STREAM_ID;
|
||||
cursor = 0;
|
||||
lastStreamId = 0;
|
||||
statusCode = 0;
|
||||
|
@ -152,6 +152,6 @@ public class GoAwayBodyParser extends ControlFrameBodyParser
|
|||
|
||||
private enum State
|
||||
{
|
||||
LAST_STREAM_ID, LAST_STREAM_ID_BYTES, STATUS_CODE, STATUS_CODE_BYTES
|
||||
LAST_GOOD_STREAM_ID, LAST_GOOD_STREAM_ID_BYTES, STATUS_CODE, STATUS_CODE_BYTES
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.nio.ByteBuffer;
|
|||
import org.eclipse.jetty.spdy.CompressionFactory;
|
||||
import org.eclipse.jetty.spdy.api.Headers;
|
||||
import org.eclipse.jetty.spdy.api.HeadersInfo;
|
||||
import org.eclipse.jetty.spdy.api.SPDY;
|
||||
import org.eclipse.jetty.spdy.frames.ControlFrameType;
|
||||
import org.eclipse.jetty.spdy.frames.HeadersFrame;
|
||||
|
||||
|
@ -51,7 +52,7 @@ public class HeadersBodyParser extends ControlFrameBodyParser
|
|||
if (buffer.remaining() >= 4)
|
||||
{
|
||||
streamId = buffer.getInt() & 0x7F_FF_FF_FF;
|
||||
state = State.HEADERS;
|
||||
state = State.ADDITIONAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -68,14 +69,55 @@ public class HeadersBodyParser extends ControlFrameBodyParser
|
|||
if (cursor == 0)
|
||||
{
|
||||
streamId &= 0x7F_FF_FF_FF;
|
||||
state = State.HEADERS;
|
||||
state = State.ADDITIONAL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ADDITIONAL:
|
||||
{
|
||||
switch (controlFrameParser.getVersion())
|
||||
{
|
||||
case SPDY.V2:
|
||||
{
|
||||
if (buffer.remaining() >= 2)
|
||||
{
|
||||
buffer.getShort();
|
||||
state = State.HEADERS;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = State.ADDITIONAL_BYTES;
|
||||
cursor = 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SPDY.V3:
|
||||
{
|
||||
state = State.HEADERS;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ADDITIONAL_BYTES:
|
||||
{
|
||||
assert controlFrameParser.getVersion() == SPDY.V2;
|
||||
buffer.get();
|
||||
--cursor;
|
||||
if (cursor == 0)
|
||||
state = State.HEADERS;
|
||||
break;
|
||||
}
|
||||
case HEADERS:
|
||||
{
|
||||
short version = controlFrameParser.getVersion();
|
||||
int length = controlFrameParser.getLength() - 4;
|
||||
if (version == SPDY.V2)
|
||||
length -= 2;
|
||||
if (headersBlockParser.parse(streamId, version, length, buffer))
|
||||
{
|
||||
byte flags = controlFrameParser.getFlags();
|
||||
|
@ -109,7 +151,7 @@ public class HeadersBodyParser extends ControlFrameBodyParser
|
|||
|
||||
private enum State
|
||||
{
|
||||
STREAM_ID, STREAM_ID_BYTES, HEADERS
|
||||
STREAM_ID, STREAM_ID_BYTES, ADDITIONAL, ADDITIONAL_BYTES, HEADERS
|
||||
}
|
||||
|
||||
private class HeadersHeadersBlockParser extends HeadersBlockParser
|
||||
|
|
|
@ -38,6 +38,7 @@ public class SynStreamBodyParser extends ControlFrameBodyParser
|
|||
private int streamId;
|
||||
private int associatedStreamId;
|
||||
private byte priority;
|
||||
private short slot;
|
||||
|
||||
public SynStreamBodyParser(CompressionFactory.Decompressor decompressor, ControlFrameParser controlFrameParser)
|
||||
{
|
||||
|
@ -118,7 +119,9 @@ public class SynStreamBodyParser extends ControlFrameBodyParser
|
|||
}
|
||||
else
|
||||
{
|
||||
// Unused byte after priority, skip it
|
||||
slot = (short)(currByte & 0xFF);
|
||||
if (slot < 0)
|
||||
throw new StreamException(streamId, StreamStatus.INVALID_CREDENTIALS);
|
||||
cursor = 0;
|
||||
state = State.HEADERS;
|
||||
}
|
||||
|
@ -134,7 +137,7 @@ public class SynStreamBodyParser extends ControlFrameBodyParser
|
|||
if (flags > (SynInfo.FLAG_CLOSE | PushSynInfo.FLAG_UNIDIRECTIONAL))
|
||||
throw new IllegalArgumentException("Invalid flag " + flags + " for frame " + ControlFrameType.SYN_STREAM);
|
||||
|
||||
SynStreamFrame frame = new SynStreamFrame(version, flags, streamId, associatedStreamId, priority, new Headers(headers, true));
|
||||
SynStreamFrame frame = new SynStreamFrame(version, flags, streamId, associatedStreamId, priority, slot, new Headers(headers, true));
|
||||
controlFrameParser.onControlFrame(frame);
|
||||
|
||||
reset();
|
||||
|
|
|
@ -46,7 +46,7 @@ public class AsyncTimeoutTest
|
|||
Executor threadPool = Executors.newCachedThreadPool();
|
||||
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
|
||||
Generator generator = new Generator(new StandardByteBufferPool(), new StandardCompressionFactory.StandardCompressor());
|
||||
Session session = new StandardSession(SPDY.V2, bufferPool, threadPool, scheduler, new TestController(), null, 1, null, generator)
|
||||
Session session = new StandardSession(SPDY.V2, bufferPool, threadPool, scheduler, new TestController(), null, 1, null, generator, new FlowControlStrategy.None())
|
||||
{
|
||||
@Override
|
||||
public void flush()
|
||||
|
@ -72,7 +72,7 @@ public class AsyncTimeoutTest
|
|||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable x)
|
||||
public void failed(Stream stream, Throwable x)
|
||||
{
|
||||
failedLatch.countDown();
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ public class AsyncTimeoutTest
|
|||
Executor threadPool = Executors.newCachedThreadPool();
|
||||
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
|
||||
Generator generator = new Generator(new StandardByteBufferPool(), new StandardCompressionFactory.StandardCompressor());
|
||||
Session session = new StandardSession(SPDY.V2, bufferPool, threadPool, scheduler, new TestController(), null, 1, null, generator)
|
||||
Session session = new StandardSession(SPDY.V2, bufferPool, threadPool, scheduler, new TestController(), null, 1, null, generator, new FlowControlStrategy.None())
|
||||
{
|
||||
@Override
|
||||
protected void write(ByteBuffer buffer, Handler<FrameBytes> handler, FrameBytes frameBytes)
|
||||
|
@ -120,7 +120,7 @@ public class AsyncTimeoutTest
|
|||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable x)
|
||||
public void failed(Void context, Throwable x)
|
||||
{
|
||||
failedLatch.countDown();
|
||||
}
|
||||
|
|
|
@ -16,16 +16,8 @@
|
|||
|
||||
package org.eclipse.jetty.spdy;
|
||||
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
|
@ -34,6 +26,8 @@ import java.util.concurrent.ScheduledExecutorService;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.eclipse.jetty.spdy.StandardSession.FrameBytes;
|
||||
import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
|
||||
import org.eclipse.jetty.spdy.api.DataInfo;
|
||||
import org.eclipse.jetty.spdy.api.Handler;
|
||||
import org.eclipse.jetty.spdy.api.Headers;
|
||||
|
@ -55,13 +49,25 @@ import org.junit.Ignore;
|
|||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class StandardSessionTest
|
||||
{
|
||||
@Mock
|
||||
private ISession sessionMock;
|
||||
private Controller<FrameBytes> controller;
|
||||
|
||||
private ByteBufferPool bufferPool;
|
||||
private Executor threadPool;
|
||||
private StandardSession session;
|
||||
|
@ -76,13 +82,36 @@ public class StandardSessionTest
|
|||
threadPool = Executors.newCachedThreadPool();
|
||||
scheduler = Executors.newSingleThreadScheduledExecutor();
|
||||
generator = new Generator(new StandardByteBufferPool(),new StandardCompressionFactory.StandardCompressor());
|
||||
session = new StandardSession(SPDY.V2,bufferPool,threadPool,scheduler,new TestController(),null,1,null,generator);
|
||||
session = new StandardSession(SPDY.V2,bufferPool,threadPool,scheduler,controller,null,1,null,generator,new FlowControlStrategy.None());
|
||||
headers = new Headers();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void setControllerWriteExpectationToFail(final boolean fail)
|
||||
{
|
||||
when(controller.write(any(ByteBuffer.class),any(Handler.class),any(StandardSession.FrameBytes.class))).thenAnswer(new Answer<Integer>()
|
||||
{
|
||||
public Integer answer(InvocationOnMock invocation)
|
||||
{
|
||||
Object[] args = invocation.getArguments();
|
||||
|
||||
Handler<StandardSession.FrameBytes> handler = (Handler<FrameBytes>)args[1];
|
||||
FrameBytes context = (FrameBytes)args[2];
|
||||
|
||||
if (fail)
|
||||
handler.failed(context,new ClosedChannelException());
|
||||
else
|
||||
handler.completed(context);
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStreamIsRemovedFromSessionWhenReset() throws InterruptedException, ExecutionException, TimeoutException
|
||||
{
|
||||
setControllerWriteExpectationToFail(false);
|
||||
|
||||
IStream stream = createStream();
|
||||
assertThatStreamIsInSession(stream);
|
||||
assertThat("stream is not reset",stream.isReset(),is(false));
|
||||
|
@ -94,6 +123,8 @@ public class StandardSessionTest
|
|||
@Test
|
||||
public void testStreamIsAddedAndRemovedFromSession() throws InterruptedException, ExecutionException, TimeoutException
|
||||
{
|
||||
setControllerWriteExpectationToFail(false);
|
||||
|
||||
IStream stream = createStream();
|
||||
assertThatStreamIsInSession(stream);
|
||||
stream.updateCloseState(true,true);
|
||||
|
@ -105,6 +136,8 @@ public class StandardSessionTest
|
|||
@Test
|
||||
public void testStreamIsRemovedWhenHeadersWithCloseFlagAreSent() throws InterruptedException, ExecutionException, TimeoutException
|
||||
{
|
||||
setControllerWriteExpectationToFail(false);
|
||||
|
||||
IStream stream = createStream();
|
||||
assertThatStreamIsInSession(stream);
|
||||
stream.updateCloseState(true,false);
|
||||
|
@ -116,6 +149,8 @@ public class StandardSessionTest
|
|||
@Test
|
||||
public void testStreamIsUnidirectional() throws InterruptedException, ExecutionException, TimeoutException
|
||||
{
|
||||
setControllerWriteExpectationToFail(false);
|
||||
|
||||
IStream stream = createStream();
|
||||
assertThat("stream is not unidirectional",stream.isUnidirectional(),not(true));
|
||||
Stream pushStream = createPushStream(stream);
|
||||
|
@ -125,6 +160,8 @@ public class StandardSessionTest
|
|||
@Test
|
||||
public void testPushStreamCreation() throws InterruptedException, ExecutionException, TimeoutException
|
||||
{
|
||||
setControllerWriteExpectationToFail(false);
|
||||
|
||||
Stream stream = createStream();
|
||||
IStream pushStream = createPushStream(stream);
|
||||
assertThat("Push stream must be associated to the first stream created",pushStream.getAssociatedStream().getId(),is(stream.getId()));
|
||||
|
@ -134,6 +171,8 @@ public class StandardSessionTest
|
|||
@Test
|
||||
public void testPushStreamIsNotClosedWhenAssociatedStreamIsClosed() throws InterruptedException, ExecutionException, TimeoutException
|
||||
{
|
||||
setControllerWriteExpectationToFail(false);
|
||||
|
||||
IStream stream = createStream();
|
||||
Stream pushStream = createPushStream(stream);
|
||||
assertThatStreamIsNotHalfClosed(stream);
|
||||
|
@ -155,6 +194,8 @@ public class StandardSessionTest
|
|||
@Test
|
||||
public void testCreatePushStreamOnClosedStream() throws InterruptedException, ExecutionException, TimeoutException
|
||||
{
|
||||
setControllerWriteExpectationToFail(false);
|
||||
|
||||
IStream stream = createStream();
|
||||
stream.updateCloseState(true,true);
|
||||
assertThatStreamIsHalfClosed(stream);
|
||||
|
@ -167,15 +208,10 @@ public class StandardSessionTest
|
|||
{
|
||||
final CountDownLatch failedLatch = new CountDownLatch(1);
|
||||
SynInfo synInfo = new SynInfo(headers,false,stream.getPriority());
|
||||
stream.syn(synInfo,5,TimeUnit.SECONDS,new Handler<Stream>()
|
||||
stream.syn(synInfo,5,TimeUnit.SECONDS,new Handler.Adapter<Stream>()
|
||||
{
|
||||
@Override
|
||||
public void completed(Stream context)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable x)
|
||||
public void failed(Stream stream, Throwable x)
|
||||
{
|
||||
failedLatch.countDown();
|
||||
}
|
||||
|
@ -186,6 +222,8 @@ public class StandardSessionTest
|
|||
@Test
|
||||
public void testPushStreamIsAddedAndRemovedFromParentAndSessionWhenClosed() throws InterruptedException, ExecutionException, TimeoutException
|
||||
{
|
||||
setControllerWriteExpectationToFail(false);
|
||||
|
||||
IStream stream = createStream();
|
||||
IStream pushStream = createPushStream(stream);
|
||||
assertThatPushStreamIsHalfClosed(pushStream);
|
||||
|
@ -200,6 +238,8 @@ public class StandardSessionTest
|
|||
@Test
|
||||
public void testPushStreamIsRemovedWhenReset() throws InterruptedException, ExecutionException, TimeoutException
|
||||
{
|
||||
setControllerWriteExpectationToFail(false);
|
||||
|
||||
IStream stream = createStream();
|
||||
IStream pushStream = (IStream)stream.syn(new SynInfo(false)).get();
|
||||
assertThatPushStreamIsInSession(pushStream);
|
||||
|
@ -212,6 +252,8 @@ public class StandardSessionTest
|
|||
@Test
|
||||
public void testPushStreamWithSynInfoClosedTrue() throws InterruptedException, ExecutionException, TimeoutException
|
||||
{
|
||||
setControllerWriteExpectationToFail(false);
|
||||
|
||||
IStream stream = createStream();
|
||||
SynInfo synInfo = new SynInfo(headers,true,stream.getPriority());
|
||||
IStream pushStream = (IStream)stream.syn(synInfo).get(5,TimeUnit.SECONDS);
|
||||
|
@ -225,6 +267,8 @@ public class StandardSessionTest
|
|||
public void testPushStreamSendHeadersWithCloseFlagIsRemovedFromSessionAndDisassociateFromParent() throws InterruptedException, ExecutionException,
|
||||
TimeoutException
|
||||
{
|
||||
setControllerWriteExpectationToFail(false);
|
||||
|
||||
IStream stream = createStream();
|
||||
SynInfo synInfo = new SynInfo(headers,false,stream.getPriority());
|
||||
IStream pushStream = (IStream)stream.syn(synInfo).get(5,TimeUnit.SECONDS);
|
||||
|
@ -240,6 +284,8 @@ public class StandardSessionTest
|
|||
@Test
|
||||
public void testCreatedAndClosedListenersAreCalledForNewStream() throws InterruptedException, ExecutionException, TimeoutException
|
||||
{
|
||||
setControllerWriteExpectationToFail(false);
|
||||
|
||||
final CountDownLatch createdListenerCalledLatch = new CountDownLatch(1);
|
||||
final CountDownLatch closedListenerCalledLatch = new CountDownLatch(1);
|
||||
session.addListener(new TestStreamListener(createdListenerCalledLatch,closedListenerCalledLatch));
|
||||
|
@ -253,6 +299,8 @@ public class StandardSessionTest
|
|||
@Test
|
||||
public void testListenerIsCalledForResetStream() throws InterruptedException, ExecutionException, TimeoutException
|
||||
{
|
||||
setControllerWriteExpectationToFail(false);
|
||||
|
||||
final CountDownLatch closedListenerCalledLatch = new CountDownLatch(1);
|
||||
session.addListener(new TestStreamListener(null,closedListenerCalledLatch));
|
||||
IStream stream = createStream();
|
||||
|
@ -263,6 +311,8 @@ public class StandardSessionTest
|
|||
@Test
|
||||
public void testCreatedAndClosedListenersAreCalledForNewPushStream() throws InterruptedException, ExecutionException, TimeoutException
|
||||
{
|
||||
setControllerWriteExpectationToFail(false);
|
||||
|
||||
final CountDownLatch createdListenerCalledLatch = new CountDownLatch(2);
|
||||
final CountDownLatch closedListenerCalledLatch = new CountDownLatch(1);
|
||||
session.addListener(new TestStreamListener(createdListenerCalledLatch,closedListenerCalledLatch));
|
||||
|
@ -277,6 +327,8 @@ public class StandardSessionTest
|
|||
@Test
|
||||
public void testListenerIsCalledForResetPushStream() throws InterruptedException, ExecutionException, TimeoutException
|
||||
{
|
||||
setControllerWriteExpectationToFail(false);
|
||||
|
||||
final CountDownLatch closedListenerCalledLatch = new CountDownLatch(1);
|
||||
session.addListener(new TestStreamListener(null,closedListenerCalledLatch));
|
||||
IStream stream = createStream();
|
||||
|
@ -313,31 +365,23 @@ public class StandardSessionTest
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testSendDataOnHalfClosedStream() throws InterruptedException, ExecutionException, TimeoutException
|
||||
{
|
||||
SynStreamFrame synStreamFrame = new SynStreamFrame(SPDY.V2,SynInfo.FLAG_CLOSE,1,0,(byte)0,null);
|
||||
IStream stream = new StandardStream(synStreamFrame,sessionMock,8184,null);
|
||||
stream.updateCloseState(synStreamFrame.isClose(),true);
|
||||
assertThat("stream is half closed",stream.isHalfClosed(),is(true));
|
||||
stream.data(new StringDataInfo("data on half closed stream",true));
|
||||
verify(sessionMock,never()).data(any(IStream.class),any(DataInfo.class),anyInt(),any(TimeUnit.class),any(Handler.class),any(void.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("In V3 we need to rst the stream if we receive data on a remotely half closed stream.")
|
||||
public void receiveDataOnRemotelyHalfClosedStreamResetsStreamInV3() throws InterruptedException, ExecutionException
|
||||
{
|
||||
setControllerWriteExpectationToFail(false);
|
||||
|
||||
IStream stream = (IStream)session.syn(new SynInfo(false),new StreamFrameListener.Adapter()).get();
|
||||
stream.updateCloseState(true,false);
|
||||
assertThat("stream is half closed from remote side",stream.isHalfClosed(),is(true));
|
||||
stream.process(new DataFrame(stream.getId(),(byte)0,256),ByteBuffer.allocate(256));
|
||||
stream.process(new ByteBufferDataInfo(ByteBuffer.allocate(256), true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReceiveDataOnRemotelyClosedStreamIsIgnored() throws InterruptedException, ExecutionException, TimeoutException
|
||||
{
|
||||
setControllerWriteExpectationToFail(false);
|
||||
|
||||
final CountDownLatch onDataCalledLatch = new CountDownLatch(1);
|
||||
Stream stream = session.syn(new SynInfo(false),new StreamFrameListener.Adapter()
|
||||
{
|
||||
|
@ -353,10 +397,38 @@ public class StandardSessionTest
|
|||
assertThat("onData is never called",onDataCalledLatch.await(1,TimeUnit.SECONDS),not(true));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testControllerWriteFailsInEndPointFlush() throws InterruptedException
|
||||
{
|
||||
setControllerWriteExpectationToFail(true);
|
||||
|
||||
final CountDownLatch failedCalledLatch = new CountDownLatch(2);
|
||||
SynStreamFrame synStreamFrame = new SynStreamFrame(SPDY.V2, SynInfo.FLAG_CLOSE, 1, 0, (byte)0, (short)0, null);
|
||||
IStream stream = new StandardStream(synStreamFrame, session, null);
|
||||
stream.updateWindowSize(8192);
|
||||
Handler.Adapter<Void> handler = new Handler.Adapter<Void>()
|
||||
{
|
||||
@Override
|
||||
public void failed(Void context, Throwable x)
|
||||
{
|
||||
failedCalledLatch.countDown();
|
||||
}
|
||||
};
|
||||
|
||||
// first data frame should fail on controller.write()
|
||||
stream.data(new StringDataInfo("data", false), 5, TimeUnit.SECONDS, handler);
|
||||
// second data frame should fail without controller.writer() as the connection is expected to be broken after first controller.write() call failed.
|
||||
stream.data(new StringDataInfo("data", false), 5, TimeUnit.SECONDS, handler);
|
||||
|
||||
verify(controller, times(1)).write(any(ByteBuffer.class), any(Handler.class), any(FrameBytes.class));
|
||||
assertThat("Handler.failed has been called twice", failedCalledLatch.await(5, TimeUnit.SECONDS), is(true));
|
||||
}
|
||||
|
||||
private IStream createStream() throws InterruptedException, ExecutionException, TimeoutException
|
||||
{
|
||||
SynInfo synInfo = new SynInfo(headers,false,(byte)0);
|
||||
return (IStream)session.syn(synInfo,new StreamFrameListener.Adapter()).get(5,TimeUnit.SECONDS);
|
||||
return (IStream)session.syn(synInfo,new StreamFrameListener.Adapter()).get(50,TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private IStream createPushStream(Stream stream) throws InterruptedException, ExecutionException, TimeoutException
|
||||
|
@ -365,21 +437,6 @@ public class StandardSessionTest
|
|||
return (IStream)stream.syn(synInfo).get(5,TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private static class TestController implements Controller<StandardSession.FrameBytes>
|
||||
{
|
||||
@Override
|
||||
public int write(ByteBuffer buffer, Handler<StandardSession.FrameBytes> handler, StandardSession.FrameBytes context)
|
||||
{
|
||||
handler.completed(context);
|
||||
return buffer.remaining();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(boolean onlyOutput)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void assertThatStreamIsClosed(IStream stream)
|
||||
{
|
||||
assertThat("stream is closed",stream.isClosed(),is(true));
|
||||
|
|
|
@ -4,33 +4,29 @@
|
|||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
// The Eclipse Public License is available at
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
|
||||
|
||||
package org.eclipse.jetty.spdy;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyLong;
|
||||
import static org.mockito.Matchers.argThat;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.eclipse.jetty.spdy.api.DataInfo;
|
||||
import org.eclipse.jetty.spdy.api.Handler;
|
||||
import org.eclipse.jetty.spdy.api.SPDY;
|
||||
import org.eclipse.jetty.spdy.api.Stream;
|
||||
import org.eclipse.jetty.spdy.api.StreamFrameListener;
|
||||
import org.eclipse.jetty.spdy.api.StringDataInfo;
|
||||
import org.eclipse.jetty.spdy.api.SynInfo;
|
||||
import org.eclipse.jetty.spdy.frames.SynStreamFrame;
|
||||
import org.junit.Test;
|
||||
|
@ -39,6 +35,17 @@ import org.mockito.ArgumentMatcher;
|
|||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.anyLong;
|
||||
import static org.mockito.Matchers.argThat;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
|
@ -48,7 +55,7 @@ public class StandardStreamTest
|
|||
{
|
||||
@Mock private ISession session;
|
||||
@Mock private SynStreamFrame synStreamFrame;
|
||||
|
||||
|
||||
/**
|
||||
* Test method for {@link org.eclipse.jetty.spdy.StandardStream#syn(org.eclipse.jetty.spdy.api.SynInfo)}.
|
||||
*/
|
||||
|
@ -56,7 +63,7 @@ public class StandardStreamTest
|
|||
@Test
|
||||
public void testSyn()
|
||||
{
|
||||
Stream stream = new StandardStream(synStreamFrame,session,0,null);
|
||||
Stream stream = new StandardStream(synStreamFrame,session,null);
|
||||
Set<Stream> streams = new HashSet<>();
|
||||
streams.add(stream);
|
||||
when(synStreamFrame.isClose()).thenReturn(false);
|
||||
|
@ -65,11 +72,11 @@ public class StandardStreamTest
|
|||
stream.syn(synInfo);
|
||||
verify(session).syn(argThat(new PushSynInfoMatcher(stream.getId(),synInfo)),any(StreamFrameListener.class),anyLong(),any(TimeUnit.class),any(Handler.class));
|
||||
}
|
||||
|
||||
|
||||
private class PushSynInfoMatcher extends ArgumentMatcher<PushSynInfo>{
|
||||
int associatedStreamId;
|
||||
SynInfo synInfo;
|
||||
|
||||
|
||||
public PushSynInfoMatcher(int associatedStreamId, SynInfo synInfo)
|
||||
{
|
||||
this.associatedStreamId = associatedStreamId;
|
||||
|
@ -93,7 +100,7 @@ public class StandardStreamTest
|
|||
|
||||
@Test
|
||||
public void testSynOnClosedStream(){
|
||||
IStream stream = new StandardStream(synStreamFrame,session,0,null);
|
||||
IStream stream = new StandardStream(synStreamFrame,session,null);
|
||||
stream.updateCloseState(true,true);
|
||||
stream.updateCloseState(true,false);
|
||||
assertThat("stream expected to be closed",stream.isClosed(),is(true));
|
||||
|
@ -101,7 +108,7 @@ public class StandardStreamTest
|
|||
stream.syn(new SynInfo(false),1,TimeUnit.SECONDS,new Handler.Adapter<Stream>()
|
||||
{
|
||||
@Override
|
||||
public void failed(Throwable x)
|
||||
public void failed(Stream stream, Throwable x)
|
||||
{
|
||||
failedLatch.countDown();
|
||||
}
|
||||
|
@ -109,4 +116,16 @@ public class StandardStreamTest
|
|||
assertThat("PushStream creation failed", failedLatch.getCount(), equalTo(0L));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testSendDataOnHalfClosedStream() throws InterruptedException, ExecutionException, TimeoutException
|
||||
{
|
||||
SynStreamFrame synStreamFrame = new SynStreamFrame(SPDY.V2, SynInfo.FLAG_CLOSE, 1, 0, (byte)0, (short)0, null);
|
||||
IStream stream = new StandardStream(synStreamFrame, session, null);
|
||||
stream.updateWindowSize(8192);
|
||||
stream.updateCloseState(synStreamFrame.isClose(), true);
|
||||
assertThat("stream is half closed", stream.isHalfClosed(), is(true));
|
||||
stream.data(new StringDataInfo("data on half closed stream", true));
|
||||
verify(session, never()).data(any(IStream.class), any(DataInfo.class), anyInt(), any(TimeUnit.class), any(Handler.class), any(void.class));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ public class ClientUsageTest
|
|||
@Test
|
||||
public void testClientRequestResponseNoBody() throws Exception
|
||||
{
|
||||
Session session = new StandardSession(SPDY.V2, null, null, null, null, null, 1, null, null);
|
||||
Session session = new StandardSession(SPDY.V2, null, null, null, null, null, 1, null, null, null);
|
||||
|
||||
session.syn(new SynInfo(true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
|
@ -48,7 +48,7 @@ public class ClientUsageTest
|
|||
@Test
|
||||
public void testClientRequestWithBodyResponseNoBody() throws Exception
|
||||
{
|
||||
Session session = new StandardSession(SPDY.V2, null, null, null, null, null, 1, null, null);
|
||||
Session session = new StandardSession(SPDY.V2, null, null, null, null, null, 1, null, null, null);
|
||||
|
||||
Stream stream = session.syn(new SynInfo(false), new StreamFrameListener.Adapter()
|
||||
{
|
||||
|
@ -69,7 +69,7 @@ public class ClientUsageTest
|
|||
@Test
|
||||
public void testAsyncClientRequestWithBodyResponseNoBody() throws Exception
|
||||
{
|
||||
Session session = new StandardSession(SPDY.V2, null, null, null, null, null, 1, null, null);
|
||||
Session session = new StandardSession(SPDY.V2, null, null, null, null, null, 1, null, null, null);
|
||||
|
||||
final String context = "context";
|
||||
session.syn(new SynInfo(false), new StreamFrameListener.Adapter()
|
||||
|
@ -104,7 +104,7 @@ public class ClientUsageTest
|
|||
@Test
|
||||
public void testAsyncClientRequestWithBodyAndResponseWithBody() throws Exception
|
||||
{
|
||||
Session session = new StandardSession(SPDY.V2, null, null, null, null, null, 1, null, null);
|
||||
Session session = new StandardSession(SPDY.V2, null, null, null, null, null, 1, null, null, null);
|
||||
|
||||
session.syn(new SynInfo(false), new StreamFrameListener.Adapter()
|
||||
{
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright (c) 2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.eclipse.jetty.spdy.frames;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.Certificate;
|
||||
|
||||
import org.eclipse.jetty.spdy.StandardByteBufferPool;
|
||||
import org.eclipse.jetty.spdy.StandardCompressionFactory;
|
||||
import org.eclipse.jetty.spdy.api.SPDY;
|
||||
import org.eclipse.jetty.spdy.generator.Generator;
|
||||
import org.eclipse.jetty.spdy.parser.Parser;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class CredentialGenerateParseTest
|
||||
{
|
||||
@Test
|
||||
public void testGenerateParse() throws Exception
|
||||
{
|
||||
short slot = 1;
|
||||
byte[] proof = new byte[]{0, 1, 2};
|
||||
Certificate[] temp = loadCertificates();
|
||||
Certificate[] certificates = new Certificate[temp.length * 2];
|
||||
System.arraycopy(temp, 0, certificates, 0, temp.length);
|
||||
System.arraycopy(temp, 0, certificates, temp.length, temp.length);
|
||||
CredentialFrame frame1 = new CredentialFrame(SPDY.V3, slot, proof, certificates);
|
||||
Generator generator = new Generator(new StandardByteBufferPool(), new StandardCompressionFactory().newCompressor());
|
||||
ByteBuffer buffer = generator.control(frame1);
|
||||
|
||||
Assert.assertNotNull(buffer);
|
||||
|
||||
TestSPDYParserListener listener = new TestSPDYParserListener();
|
||||
Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
|
||||
parser.addListener(listener);
|
||||
parser.parse(buffer);
|
||||
ControlFrame frame2 = listener.getControlFrame();
|
||||
|
||||
Assert.assertNotNull(frame2);
|
||||
Assert.assertEquals(ControlFrameType.CREDENTIAL, frame2.getType());
|
||||
CredentialFrame credential = (CredentialFrame)frame2;
|
||||
Assert.assertEquals(SPDY.V3, credential.getVersion());
|
||||
Assert.assertEquals(0, credential.getFlags());
|
||||
Assert.assertEquals(slot, credential.getSlot());
|
||||
Assert.assertArrayEquals(proof, credential.getProof());
|
||||
Assert.assertArrayEquals(certificates, credential.getCertificateChain());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateParseOneByteAtATime() throws Exception
|
||||
{
|
||||
short slot = 1;
|
||||
byte[] proof = new byte[]{0, 1, 2};
|
||||
Certificate[] certificates = loadCertificates();
|
||||
CredentialFrame frame1 = new CredentialFrame(SPDY.V3, slot, proof, certificates);
|
||||
Generator generator = new Generator(new StandardByteBufferPool(), new StandardCompressionFactory().newCompressor());
|
||||
ByteBuffer buffer = generator.control(frame1);
|
||||
|
||||
Assert.assertNotNull(buffer);
|
||||
|
||||
TestSPDYParserListener listener = new TestSPDYParserListener();
|
||||
Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
|
||||
parser.addListener(listener);
|
||||
while (buffer.hasRemaining())
|
||||
parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
|
||||
ControlFrame frame2 = listener.getControlFrame();
|
||||
|
||||
Assert.assertNotNull(frame2);
|
||||
Assert.assertEquals(ControlFrameType.CREDENTIAL, frame2.getType());
|
||||
CredentialFrame credential = (CredentialFrame)frame2;
|
||||
Assert.assertEquals(SPDY.V3, credential.getVersion());
|
||||
Assert.assertEquals(0, credential.getFlags());
|
||||
Assert.assertEquals(slot, credential.getSlot());
|
||||
Assert.assertArrayEquals(proof, credential.getProof());
|
||||
Assert.assertArrayEquals(certificates, credential.getCertificateChain());
|
||||
}
|
||||
|
||||
private Certificate[] loadCertificates() throws Exception
|
||||
{
|
||||
KeyStore keyStore = KeyStore.getInstance("JKS");
|
||||
InputStream keyStoreStream = Resource.newResource("src/test/resources/keystore.jks").getInputStream();
|
||||
keyStore.load(keyStoreStream, "storepwd".toCharArray());
|
||||
return keyStore.getCertificateChain("mykey");
|
||||
}
|
||||
}
|
|
@ -37,10 +37,11 @@ public class SynStreamGenerateParseTest
|
|||
int streamId = 13;
|
||||
int associatedStreamId = 11;
|
||||
byte priority = 3;
|
||||
short slot = 5;
|
||||
Headers headers = new Headers();
|
||||
headers.put("a", "b");
|
||||
headers.put("c", "d");
|
||||
SynStreamFrame frame1 = new SynStreamFrame(SPDY.V2, flags, streamId, associatedStreamId, priority, headers);
|
||||
SynStreamFrame frame1 = new SynStreamFrame(SPDY.V2, flags, streamId, associatedStreamId, priority, slot, headers);
|
||||
Generator generator = new Generator(new StandardByteBufferPool(), new StandardCompressionFactory().newCompressor());
|
||||
ByteBuffer buffer = generator.control(frame1);
|
||||
|
||||
|
@ -60,6 +61,7 @@ public class SynStreamGenerateParseTest
|
|||
Assert.assertEquals(associatedStreamId, synStream.getAssociatedStreamId());
|
||||
Assert.assertEquals(flags, synStream.getFlags());
|
||||
Assert.assertEquals(priority, synStream.getPriority());
|
||||
Assert.assertEquals(slot, synStream.getSlot());
|
||||
Assert.assertEquals(headers, synStream.getHeaders());
|
||||
}
|
||||
|
||||
|
@ -70,10 +72,11 @@ public class SynStreamGenerateParseTest
|
|||
int streamId = 13;
|
||||
int associatedStreamId = 11;
|
||||
byte priority = 3;
|
||||
short slot = 5;
|
||||
Headers headers = new Headers();
|
||||
headers.put("a", "b");
|
||||
headers.put("c", "d");
|
||||
SynStreamFrame frame1 = new SynStreamFrame(SPDY.V2, flags, streamId, associatedStreamId, priority, headers);
|
||||
SynStreamFrame frame1 = new SynStreamFrame(SPDY.V2, flags, streamId, associatedStreamId, priority, slot, headers);
|
||||
Generator generator = new Generator(new StandardByteBufferPool(), new StandardCompressionFactory().newCompressor());
|
||||
ByteBuffer buffer = generator.control(frame1);
|
||||
|
||||
|
@ -94,6 +97,7 @@ public class SynStreamGenerateParseTest
|
|||
Assert.assertEquals(associatedStreamId, synStream.getAssociatedStreamId());
|
||||
Assert.assertEquals(flags, synStream.getFlags());
|
||||
Assert.assertEquals(priority, synStream.getPriority());
|
||||
Assert.assertEquals(slot, synStream.getSlot());
|
||||
Assert.assertEquals(headers, synStream.getHeaders());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ public class UnknownControlFrameTest
|
|||
@Test
|
||||
public void testUnknownControlFrame() throws Exception
|
||||
{
|
||||
SynStreamFrame frame = new SynStreamFrame(SPDY.V2, SynInfo.FLAG_CLOSE, 1, 0, (byte)0, new Headers());
|
||||
SynStreamFrame frame = new SynStreamFrame(SPDY.V2, SynInfo.FLAG_CLOSE, 1, 0, (byte)0, (short)0, new Headers());
|
||||
Generator generator = new Generator(new StandardByteBufferPool(), new StandardCompressionFactory.StandardCompressor());
|
||||
ByteBuffer buffer = generator.control(frame);
|
||||
// Change the frame type to unknown
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -39,7 +39,7 @@
|
|||
<stopKey>quit</stopKey>
|
||||
<jvmArgs>
|
||||
-Dlog4j.configuration=file://${basedir}/src/main/resources/log4j.properties
|
||||
-Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${project.version}/npn-boot-${project.version}.jar
|
||||
-Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${npn.version}/npn-boot-${npn.version}.jar
|
||||
</jvmArgs>
|
||||
<jettyXml>${basedir}/src/main/config/etc/jetty-spdy.xml</jettyXml>
|
||||
<contextPath>/</contextPath>
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
<Set name="protocol">TLSv1</Set>
|
||||
</New>
|
||||
|
||||
<!--<Set class="org.eclipse.jetty.npn.NextProtoNego" name="debug" type="boolean">true</Set>-->
|
||||
|
||||
<Call name="addConnector">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.spdy.http.HTTPSPDYServerConnector">
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (c) 2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.eclipse.jetty.spdy.http;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jetty.spdy.api.SPDY;
|
||||
|
||||
public enum HTTPSPDYHeader
|
||||
{
|
||||
METHOD("method", ":method"),
|
||||
URI("url", ":path"),
|
||||
VERSION("version", ":version"),
|
||||
SCHEME("scheme", ":scheme"),
|
||||
HOST("host", ":host"),
|
||||
STATUS("status", ":status");
|
||||
|
||||
public static HTTPSPDYHeader from(short version, String name)
|
||||
{
|
||||
switch (version)
|
||||
{
|
||||
case SPDY.V2:
|
||||
return Names.v2Names.get(name);
|
||||
case SPDY.V3:
|
||||
return Names.v3Names.get(name);
|
||||
default:
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
private final String v2Name;
|
||||
private final String v3Name;
|
||||
|
||||
private HTTPSPDYHeader(String v2Name, String v3Name)
|
||||
{
|
||||
this.v2Name = v2Name;
|
||||
Names.v2Names.put(v2Name, this);
|
||||
this.v3Name = v3Name;
|
||||
Names.v3Names.put(v3Name, this);
|
||||
}
|
||||
|
||||
public String name(short version)
|
||||
{
|
||||
switch (version)
|
||||
{
|
||||
case SPDY.V2:
|
||||
return v2Name;
|
||||
case SPDY.V3:
|
||||
return v3Name;
|
||||
default:
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
private static class Names
|
||||
{
|
||||
private static final Map<String, HTTPSPDYHeader> v2Names = new HashMap<>();
|
||||
private static final Map<String, HTTPSPDYHeader> v3Names = new HashMap<>();
|
||||
}
|
||||
}
|
|
@ -21,42 +21,40 @@ import java.io.IOException;
|
|||
import org.eclipse.jetty.http.HttpSchemes;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.spdy.AsyncConnectionFactory;
|
||||
import org.eclipse.jetty.spdy.SPDYServerConnector;
|
||||
import org.eclipse.jetty.spdy.api.SPDY;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
|
||||
public class HTTPSPDYServerConnector extends SPDYServerConnector
|
||||
{
|
||||
private final AsyncConnectionFactory defaultConnectionFactory;
|
||||
private final PushStrategy pushStrategy = new PushStrategy.None();
|
||||
|
||||
public HTTPSPDYServerConnector()
|
||||
{
|
||||
this(null);
|
||||
this(null, new PushStrategy.None());
|
||||
}
|
||||
|
||||
public HTTPSPDYServerConnector(PushStrategy pushStrategy)
|
||||
{
|
||||
this(null, pushStrategy);
|
||||
}
|
||||
|
||||
public HTTPSPDYServerConnector(SslContextFactory sslContextFactory)
|
||||
{
|
||||
super(null, sslContextFactory);
|
||||
// Override the default connection factory for non-SSL connections
|
||||
defaultConnectionFactory = new ServerHTTPAsyncConnectionFactory(this);
|
||||
this(sslContextFactory, new PushStrategy.None());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStart() throws Exception
|
||||
public HTTPSPDYServerConnector(SslContextFactory sslContextFactory, PushStrategy pushStrategy)
|
||||
{
|
||||
super.doStart();
|
||||
// We pass a null ServerSessionFrameListener because for
|
||||
// HTTP over SPDY we need one that references the endPoint
|
||||
super(null, sslContextFactory);
|
||||
// Override the "spdy/3" protocol by handling HTTP over SPDY
|
||||
putAsyncConnectionFactory("spdy/3", new ServerHTTPSPDYAsyncConnectionFactory(SPDY.V3, getByteBufferPool(), getExecutor(), getScheduler(), this, pushStrategy));
|
||||
// Override the "spdy/2" protocol by handling HTTP over SPDY
|
||||
putAsyncConnectionFactory("spdy/2", new ServerHTTPSPDYAsyncConnectionFactory(SPDY.V2, getByteBufferPool(), getExecutor(), getScheduler(), this, pushStrategy));
|
||||
// Add the "http/1.1" protocol for browsers that do not support NPN
|
||||
// Add the "http/1.1" protocol for browsers that support NPN but not SPDY
|
||||
putAsyncConnectionFactory("http/1.1", new ServerHTTPAsyncConnectionFactory(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AsyncConnectionFactory getDefaultAsyncConnectionFactory()
|
||||
{
|
||||
return defaultConnectionFactory;
|
||||
// Override the default connection factory for non-SSL connections to speak plain HTTP
|
||||
setDefaultAsyncConnectionFactory(getAsyncConnectionFactory("http/1.1"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -24,8 +24,10 @@ import java.util.LinkedList;
|
|||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.eclipse.jetty.http.HttpException;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
|
@ -47,6 +49,7 @@ import org.eclipse.jetty.server.Connector;
|
|||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.spdy.SPDYAsyncConnection;
|
||||
import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
|
||||
import org.eclipse.jetty.spdy.api.BytesDataInfo;
|
||||
import org.eclipse.jetty.spdy.api.DataInfo;
|
||||
import org.eclipse.jetty.spdy.api.Handler;
|
||||
import org.eclipse.jetty.spdy.api.Headers;
|
||||
|
@ -66,6 +69,7 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
|
||||
private final Queue<Runnable> tasks = new LinkedList<>();
|
||||
private final BlockingQueue<DataInfo> dataInfos = new LinkedBlockingQueue<>();
|
||||
private final short version;
|
||||
private final SPDYAsyncConnection connection;
|
||||
private final PushStrategy pushStrategy;
|
||||
private final Stream stream;
|
||||
|
@ -75,9 +79,10 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
private volatile State state = State.INITIAL;
|
||||
private boolean dispatched; // Guarded by synchronization on tasks
|
||||
|
||||
public ServerHTTPSPDYAsyncConnection(Connector connector, AsyncEndPoint endPoint, Server server, SPDYAsyncConnection connection, PushStrategy pushStrategy, Stream stream)
|
||||
public ServerHTTPSPDYAsyncConnection(Connector connector, AsyncEndPoint endPoint, Server server, short version, SPDYAsyncConnection connection, PushStrategy pushStrategy, Stream stream)
|
||||
{
|
||||
super(connector, endPoint, server);
|
||||
this.version = version;
|
||||
this.connection = connection;
|
||||
this.pushStrategy = pushStrategy;
|
||||
this.stream = stream;
|
||||
|
@ -159,9 +164,9 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
}
|
||||
case REQUEST:
|
||||
{
|
||||
Headers.Header method = headers.get("method");
|
||||
Headers.Header uri = headers.get("url");
|
||||
Headers.Header version = headers.get("version");
|
||||
Headers.Header method = headers.get(HTTPSPDYHeader.METHOD.name(version));
|
||||
Headers.Header uri = headers.get(HTTPSPDYHeader.URI.name(version));
|
||||
Headers.Header version = headers.get(HTTPSPDYHeader.VERSION.name(this.version));
|
||||
|
||||
if (method == null || uri == null || version == null)
|
||||
throw new HttpException(HttpStatus.BAD_REQUEST_400);
|
||||
|
@ -181,15 +186,19 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
for (Headers.Header header : headers)
|
||||
{
|
||||
String name = header.name();
|
||||
|
||||
// Skip special SPDY headers, unless it's the "host" header
|
||||
HTTPSPDYHeader specialHeader = HTTPSPDYHeader.from(version, name);
|
||||
if (specialHeader != null)
|
||||
{
|
||||
if (specialHeader == HTTPSPDYHeader.HOST)
|
||||
name = "host";
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (name)
|
||||
{
|
||||
case "method":
|
||||
case "version":
|
||||
case "url":
|
||||
{
|
||||
// Skip request line headers
|
||||
continue;
|
||||
}
|
||||
case "connection":
|
||||
case "keep-alive":
|
||||
case "proxy-connection":
|
||||
|
@ -264,8 +273,8 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
else
|
||||
{
|
||||
Headers headers = new Headers();
|
||||
headers.put("status", String.valueOf(status));
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put(HTTPSPDYHeader.STATUS.name(version), String.valueOf(status));
|
||||
headers.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
|
||||
stream.reply(new ReplyInfo(headers, true));
|
||||
}
|
||||
}
|
||||
|
@ -393,21 +402,22 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
{
|
||||
if (!stream.isUnidirectional())
|
||||
stream.reply(replyInfo);
|
||||
if (replyInfo.getHeaders().get("status").value().startsWith("200") && !stream.isClosed() && !isIfModifiedSinceHeaderPresent())
|
||||
if (replyInfo.getHeaders().get(HTTPSPDYHeader.STATUS.name(version)).value().startsWith("200") &&
|
||||
!stream.isClosed() && !isIfModifiedSinceHeaderPresent())
|
||||
{
|
||||
// We have a 200 OK with some content to send
|
||||
|
||||
Headers.Header scheme = headers.get("scheme");
|
||||
Headers.Header host = headers.get("host");
|
||||
Headers.Header url = headers.get("url");
|
||||
Set<String> pushResources = pushStrategy.apply(stream, this.headers, replyInfo.getHeaders());
|
||||
String referrer = new StringBuilder(scheme.value()).append("://").append(host.value()).append(url.value()).toString();
|
||||
Headers.Header scheme = headers.get(HTTPSPDYHeader.SCHEME.name(version));
|
||||
Headers.Header host = headers.get(HTTPSPDYHeader.HOST.name(version));
|
||||
Headers.Header uri = headers.get(HTTPSPDYHeader.URI.name(version));
|
||||
Set<String> pushResources = pushStrategy.apply(stream, headers, replyInfo.getHeaders());
|
||||
String referrer = new StringBuilder(scheme.value()).append("://").append(host.value()).append(uri.value()).toString();
|
||||
for (String pushURL : pushResources)
|
||||
{
|
||||
final Headers pushHeaders = new Headers();
|
||||
pushHeaders.put("method", "GET");
|
||||
pushHeaders.put("url", pushURL);
|
||||
pushHeaders.put("version", "HTTP/1.1");
|
||||
pushHeaders.put(HTTPSPDYHeader.METHOD.name(version), "GET");
|
||||
pushHeaders.put(HTTPSPDYHeader.URI.name(version), pushURL);
|
||||
pushHeaders.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
|
||||
pushHeaders.put(scheme);
|
||||
pushHeaders.put(host);
|
||||
pushHeaders.put("referer", referrer);
|
||||
|
@ -418,7 +428,7 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
@Override
|
||||
public void completed(Stream pushStream)
|
||||
{
|
||||
Synchronous pushConnection = new Synchronous(getConnector(), getEndPoint(), getServer(), connection, pushStrategy, pushStream);
|
||||
Synchronous pushConnection = new Synchronous(getConnector(), getEndPoint(), getServer(), version, connection, pushStrategy, pushStream);
|
||||
pushConnection.beginRequest(pushHeaders, true);
|
||||
}
|
||||
});
|
||||
|
@ -427,12 +437,10 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
}
|
||||
|
||||
private boolean isIfModifiedSinceHeaderPresent()
|
||||
{
|
||||
if (headers.get("if-modified-since") != null)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
return headers.get("if-modified-since") != null;
|
||||
}
|
||||
|
||||
private Buffer consumeContent(long maxIdleTime) throws IOException, InterruptedException
|
||||
{
|
||||
while (true)
|
||||
|
@ -614,11 +622,11 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
{
|
||||
Headers headers = new Headers();
|
||||
String version = "HTTP/1.1";
|
||||
headers.put("version", version);
|
||||
headers.put(HTTPSPDYHeader.VERSION.name(ServerHTTPSPDYAsyncConnection.this.version), version);
|
||||
StringBuilder status = new StringBuilder().append(_status);
|
||||
if (_reason != null)
|
||||
status.append(" ").append(_reason.toString("UTF-8"));
|
||||
headers.put("status", status.toString());
|
||||
headers.put(HTTPSPDYHeader.STATUS.name(ServerHTTPSPDYAsyncConnection.this.version), status.toString());
|
||||
logger.debug("HTTP < {} {}", version, status);
|
||||
|
||||
if (fields != null)
|
||||
|
@ -634,18 +642,13 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
}
|
||||
|
||||
// We have to query the HttpGenerator and its buffers to know
|
||||
// whether there is content buffered; if so, send the data frame
|
||||
// whether there is content buffered and update the generator state
|
||||
Buffer content = getContentBuffer();
|
||||
reply(stream, new ReplyInfo(headers, content == null));
|
||||
if (content != null)
|
||||
{
|
||||
closed = allContentAdded || isAllContentWritten();
|
||||
ByteBuffer buffer = ByteBuffer.wrap(content.asArray());
|
||||
logger.debug("HTTP < {} bytes of content", buffer.remaining());
|
||||
// Send the data frame
|
||||
stream.data(new ByteBufferDataInfo(buffer, closed));
|
||||
// Update HttpGenerator fields so that they remain consistent
|
||||
content.clear();
|
||||
_state = closed ? HttpGenerator.STATE_END : HttpGenerator.STATE_CONTENT;
|
||||
}
|
||||
else
|
||||
|
@ -660,7 +663,7 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
{
|
||||
if (_buffer != null && _buffer.length() > 0)
|
||||
return _buffer;
|
||||
if (_bypass && _content != null && _content.length() > 0)
|
||||
if (_content != null && _content.length() > 0)
|
||||
return _content;
|
||||
return null;
|
||||
}
|
||||
|
@ -685,22 +688,46 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
@Override
|
||||
public void flush(long maxIdleTime) throws IOException
|
||||
{
|
||||
while (_content != null && _content.length() > 0)
|
||||
try
|
||||
{
|
||||
_content.skip(_buffer.put(_content));
|
||||
ByteBuffer buffer = ByteBuffer.wrap(_buffer.asArray());
|
||||
logger.debug("HTTP < {} bytes of content", buffer.remaining());
|
||||
_buffer.clear();
|
||||
closed = _content.length() == 0 && _last;
|
||||
stream.data(new ByteBufferDataInfo(buffer, closed));
|
||||
|
||||
boolean expired = !connection.getEndPoint().blockWritable(maxIdleTime);
|
||||
if (expired)
|
||||
Buffer content = getContentBuffer();
|
||||
if (content != null)
|
||||
{
|
||||
stream.getSession().goAway();
|
||||
throw new EOFException("write timeout");
|
||||
DataInfo dataInfo = toDataInfo(content, closed);
|
||||
logger.debug("HTTP < {} bytes of content", dataInfo.length());
|
||||
stream.data(dataInfo).get(maxIdleTime, TimeUnit.MILLISECONDS);
|
||||
content.clear();
|
||||
}
|
||||
}
|
||||
catch (TimeoutException x)
|
||||
{
|
||||
stream.getSession().goAway();
|
||||
throw new EOFException("write timeout");
|
||||
}
|
||||
catch (InterruptedException x)
|
||||
{
|
||||
throw new InterruptedIOException();
|
||||
}
|
||||
catch (ExecutionException x)
|
||||
{
|
||||
throw new IOException(x.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
private DataInfo toDataInfo(Buffer buffer, boolean close)
|
||||
{
|
||||
if (buffer instanceof ByteArrayBuffer)
|
||||
return new BytesDataInfo(buffer.array(), buffer.getIndex(), buffer.length(), close);
|
||||
|
||||
if (buffer instanceof NIOBuffer)
|
||||
{
|
||||
ByteBuffer byteBuffer = ((NIOBuffer)buffer).getByteBuffer();
|
||||
byteBuffer.limit(buffer.putIndex());
|
||||
byteBuffer.position(buffer.getIndex());
|
||||
return new ByteBufferDataInfo(byteBuffer, close);
|
||||
}
|
||||
|
||||
return new BytesDataInfo(buffer.asArray(), close);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -727,19 +754,15 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
Buffer content = getContentBuffer();
|
||||
if (content != null)
|
||||
{
|
||||
ByteBuffer buffer = ByteBuffer.wrap(content.asArray());
|
||||
logger.debug("HTTP < {} bytes of content", buffer.remaining());
|
||||
// Update HttpGenerator fields so that they remain consistent
|
||||
content.clear();
|
||||
closed = true;
|
||||
_state = STATE_END;
|
||||
// Send the data frame
|
||||
stream.data(new ByteBufferDataInfo(buffer, true));
|
||||
flush(getMaxIdleTime());
|
||||
}
|
||||
else if (!closed)
|
||||
{
|
||||
closed = true;
|
||||
_state = STATE_END;
|
||||
// Send the data frame
|
||||
// Send the last, empty, data frame
|
||||
stream.data(new ByteBufferDataInfo(ZERO_BYTES, true));
|
||||
}
|
||||
}
|
||||
|
@ -747,9 +770,9 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
|
||||
private static class Synchronous extends ServerHTTPSPDYAsyncConnection
|
||||
{
|
||||
private Synchronous(Connector connector, AsyncEndPoint endPoint, Server server, SPDYAsyncConnection connection, PushStrategy pushStrategy, Stream stream)
|
||||
private Synchronous(Connector connector, AsyncEndPoint endPoint, Server server, short version, SPDYAsyncConnection connection, PushStrategy pushStrategy, Stream stream)
|
||||
{
|
||||
super(connector, endPoint, server, connection, pushStrategy, stream);
|
||||
super(connector, endPoint, server, version, connection, pushStrategy, stream);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -52,7 +52,7 @@ public class ServerHTTPSPDYAsyncConnectionFactory extends ServerSPDYAsyncConnect
|
|||
}
|
||||
|
||||
@Override
|
||||
protected ServerSessionFrameListener newServerSessionFrameListener(AsyncEndPoint endPoint, Object attachment)
|
||||
protected ServerSessionFrameListener provideServerSessionFrameListener(AsyncEndPoint endPoint, Object attachment)
|
||||
{
|
||||
return new HTTPServerFrameListener(endPoint);
|
||||
}
|
||||
|
@ -78,8 +78,8 @@ public class ServerHTTPSPDYAsyncConnectionFactory extends ServerSPDYAsyncConnect
|
|||
logger.debug("Received {} on {}", synInfo, stream);
|
||||
|
||||
HTTPSPDYAsyncEndPoint asyncEndPoint = new HTTPSPDYAsyncEndPoint(endPoint, stream);
|
||||
ServerHTTPSPDYAsyncConnection connection = new ServerHTTPSPDYAsyncConnection(connector,
|
||||
asyncEndPoint, connector.getServer(), (SPDYAsyncConnection)endPoint.getConnection(),
|
||||
ServerHTTPSPDYAsyncConnection connection = new ServerHTTPSPDYAsyncConnection(connector, asyncEndPoint,
|
||||
connector.getServer(), getVersion(), (SPDYAsyncConnection)endPoint.getConnection(),
|
||||
pushStrategy, stream);
|
||||
asyncEndPoint.setConnection(connection);
|
||||
stream.setAttribute(CONNECTION_ATTRIBUTE, connection);
|
||||
|
|
|
@ -54,9 +54,14 @@ public abstract class AbstractHTTPSPDYTest
|
|||
protected SPDYServerConnector connector;
|
||||
|
||||
protected InetSocketAddress startHTTPServer(Handler handler) throws Exception
|
||||
{
|
||||
return startHTTPServer(SPDY.V2, handler);
|
||||
}
|
||||
|
||||
protected InetSocketAddress startHTTPServer(short version, Handler handler) throws Exception
|
||||
{
|
||||
server = new Server();
|
||||
connector = newHTTPSPDYServerConnector();
|
||||
connector = newHTTPSPDYServerConnector(version);
|
||||
connector.setPort(0);
|
||||
server.addConnector(connector);
|
||||
server.setHandler(handler);
|
||||
|
@ -64,20 +69,21 @@ public abstract class AbstractHTTPSPDYTest
|
|||
return new InetSocketAddress("localhost", connector.getLocalPort());
|
||||
}
|
||||
|
||||
protected SPDYServerConnector newHTTPSPDYServerConnector()
|
||||
protected SPDYServerConnector newHTTPSPDYServerConnector(short version)
|
||||
{
|
||||
// For these tests, we need the connector to speak HTTP over SPDY even in non-SSL
|
||||
return new HTTPSPDYServerConnector()
|
||||
{
|
||||
@Override
|
||||
protected AsyncConnectionFactory getDefaultAsyncConnectionFactory()
|
||||
{
|
||||
return new ServerHTTPSPDYAsyncConnectionFactory(SPDY.V2, getByteBufferPool(), getExecutor(), getScheduler(), this, new PushStrategy.None());
|
||||
}
|
||||
};
|
||||
SPDYServerConnector connector = new HTTPSPDYServerConnector();
|
||||
AsyncConnectionFactory defaultFactory = new ServerHTTPSPDYAsyncConnectionFactory(version, connector.getByteBufferPool(), connector.getExecutor(), connector.getScheduler(), connector, new PushStrategy.None());
|
||||
connector.setDefaultAsyncConnectionFactory(defaultFactory);
|
||||
return connector;
|
||||
}
|
||||
|
||||
protected Session startClient(InetSocketAddress socketAddress, SessionFrameListener listener) throws Exception
|
||||
{
|
||||
return startClient(SPDY.V2, socketAddress, listener);
|
||||
}
|
||||
|
||||
protected Session startClient(short version, InetSocketAddress socketAddress, SessionFrameListener listener) throws Exception
|
||||
{
|
||||
if (clientFactory == null)
|
||||
{
|
||||
|
@ -86,7 +92,7 @@ public abstract class AbstractHTTPSPDYTest
|
|||
clientFactory = newSPDYClientFactory(threadPool);
|
||||
clientFactory.start();
|
||||
}
|
||||
return clientFactory.newSPDYClient(SPDY.V2).connect(socketAddress, listener).get(5, TimeUnit.SECONDS);
|
||||
return clientFactory.newSPDYClient(version).connect(socketAddress, listener).get(5, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
protected SPDYClient.Factory newSPDYClientFactory(Executor threadPool)
|
||||
|
|
|
@ -27,7 +27,6 @@ import javax.net.ssl.SSLSocket;
|
|||
|
||||
import org.eclipse.jetty.npn.NextProtoNego;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.spdy.AsyncConnectionFactory;
|
||||
import org.eclipse.jetty.spdy.SPDYServerConnector;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.junit.Assert;
|
||||
|
@ -109,9 +108,8 @@ public class ProtocolNegotiationTest
|
|||
public String selectProtocol(List<String> strings)
|
||||
{
|
||||
Assert.assertNotNull(strings);
|
||||
Assert.assertEquals(1, strings.size());
|
||||
String protocol = strings.get(0);
|
||||
Assert.assertEquals("http/1.1", protocol);
|
||||
String protocol = "http/1.1";
|
||||
Assert.assertTrue(strings.contains(protocol));
|
||||
return protocol;
|
||||
}
|
||||
});
|
||||
|
@ -166,11 +164,11 @@ public class ProtocolNegotiationTest
|
|||
public String selectProtocol(List<String> strings)
|
||||
{
|
||||
Assert.assertNotNull(strings);
|
||||
Assert.assertEquals(2, strings.size());
|
||||
String spdyProtocol = strings.get(0);
|
||||
Assert.assertEquals("spdy/2", spdyProtocol);
|
||||
String httpProtocol = strings.get(1);
|
||||
Assert.assertEquals("http/1.1", httpProtocol);
|
||||
String spdyProtocol = "spdy/2";
|
||||
Assert.assertTrue(strings.contains(spdyProtocol));
|
||||
String httpProtocol = "http/1.1";
|
||||
Assert.assertTrue(strings.contains(httpProtocol));
|
||||
Assert.assertTrue(strings.indexOf(spdyProtocol) < strings.indexOf(httpProtocol));
|
||||
return httpProtocol;
|
||||
}
|
||||
});
|
||||
|
@ -198,14 +196,9 @@ public class ProtocolNegotiationTest
|
|||
@Test
|
||||
public void testServerAdvertisingSPDYAndHTTPSpeaksDefaultProtocolWhenNPNMissing() throws Exception
|
||||
{
|
||||
InetSocketAddress address = startServer(new SPDYServerConnector(null, newSslContextFactory())
|
||||
{
|
||||
@Override
|
||||
protected AsyncConnectionFactory getDefaultAsyncConnectionFactory()
|
||||
{
|
||||
return new ServerHTTPAsyncConnectionFactory(connector);
|
||||
}
|
||||
});
|
||||
SPDYServerConnector connector = new SPDYServerConnector(null, newSslContextFactory());
|
||||
connector.setDefaultAsyncConnectionFactory(new ServerHTTPAsyncConnectionFactory(connector));
|
||||
InetSocketAddress address = startServer(connector);
|
||||
connector.putAsyncConnectionFactory("http/1.1", new ServerHTTPAsyncConnectionFactory(connector));
|
||||
|
||||
SslContextFactory sslContextFactory = newSslContextFactory();
|
||||
|
|
|
@ -16,7 +16,6 @@ import org.eclipse.jetty.spdy.SPDYServerConnector;
|
|||
import org.eclipse.jetty.spdy.api.DataInfo;
|
||||
import org.eclipse.jetty.spdy.api.Headers;
|
||||
import org.eclipse.jetty.spdy.api.ReplyInfo;
|
||||
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.Stream;
|
||||
|
@ -28,19 +27,12 @@ import org.junit.Test;
|
|||
public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
|
||||
{
|
||||
@Override
|
||||
protected SPDYServerConnector newHTTPSPDYServerConnector()
|
||||
protected SPDYServerConnector newHTTPSPDYServerConnector(short version)
|
||||
{
|
||||
return new HTTPSPDYServerConnector()
|
||||
{
|
||||
private final AsyncConnectionFactory defaultAsyncConnectionFactory =
|
||||
new ServerHTTPSPDYAsyncConnectionFactory(SPDY.V2, getByteBufferPool(), getExecutor(), getScheduler(), this, new ReferrerPushStrategy());
|
||||
|
||||
@Override
|
||||
protected AsyncConnectionFactory getDefaultAsyncConnectionFactory()
|
||||
{
|
||||
return defaultAsyncConnectionFactory;
|
||||
}
|
||||
};
|
||||
SPDYServerConnector connector = super.newHTTPSPDYServerConnector(version);
|
||||
AsyncConnectionFactory defaultFactory = new ServerHTTPSPDYAsyncConnectionFactory(version, connector.getByteBufferPool(), connector.getExecutor(), connector.getScheduler(), connector, new ReferrerPushStrategy());
|
||||
connector.setDefaultAsyncConnectionFactory(defaultFactory);
|
||||
return connector;
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -363,7 +355,7 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
|
|||
Assert.assertTrue(mainStreamLatch.await(5, TimeUnit.SECONDS));
|
||||
Assert.assertFalse(pushLatch.await(1, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testRequestWithIfModifiedSinceHeaderPreventsPush() throws Exception
|
||||
{
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package org.eclipse.jetty.spdy.http;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
@ -40,6 +41,7 @@ import org.eclipse.jetty.spdy.api.BytesDataInfo;
|
|||
import org.eclipse.jetty.spdy.api.DataInfo;
|
||||
import org.eclipse.jetty.spdy.api.Headers;
|
||||
import org.eclipse.jetty.spdy.api.ReplyInfo;
|
||||
import org.eclipse.jetty.spdy.api.SPDY;
|
||||
import org.eclipse.jetty.spdy.api.Session;
|
||||
import org.eclipse.jetty.spdy.api.Stream;
|
||||
import org.eclipse.jetty.spdy.api.StreamFrameListener;
|
||||
|
@ -48,14 +50,19 @@ import org.eclipse.jetty.spdy.api.SynInfo;
|
|||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
||||
public class ServerHTTPSPDYv2Test extends AbstractHTTPSPDYTest
|
||||
{
|
||||
protected short version()
|
||||
{
|
||||
return SPDY.V2;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleGET() throws Exception
|
||||
{
|
||||
final String path = "/foo";
|
||||
final CountDownLatch handlerLatch = new CountDownLatch(1);
|
||||
Session session = startClient(startHTTPServer(new AbstractHandler()
|
||||
Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
|
||||
|
@ -71,11 +78,11 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}), null);
|
||||
|
||||
Headers headers = new Headers();
|
||||
headers.put("method", "GET");
|
||||
headers.put("url", path);
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
|
||||
headers.put(HTTPSPDYHeader.URI.name(version()), path);
|
||||
headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
|
@ -84,7 +91,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
{
|
||||
Assert.assertTrue(replyInfo.isClose());
|
||||
Headers replyHeaders = replyInfo.getHeaders();
|
||||
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
|
||||
Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
|
||||
replyLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
@ -99,7 +106,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
final String query = "p=1";
|
||||
final String uri = path + "?" + query;
|
||||
final CountDownLatch handlerLatch = new CountDownLatch(1);
|
||||
Session session = startClient(startHTTPServer(new AbstractHandler()
|
||||
Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
|
||||
|
@ -115,11 +122,11 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}), null);
|
||||
|
||||
Headers headers = new Headers();
|
||||
headers.put("method", "GET");
|
||||
headers.put("url", uri);
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
|
||||
headers.put(HTTPSPDYHeader.URI.name(version()), uri);
|
||||
headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
|
@ -128,7 +135,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
{
|
||||
Assert.assertTrue(replyInfo.isClose());
|
||||
Headers replyHeaders = replyInfo.getHeaders();
|
||||
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
|
||||
Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
|
||||
replyLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
@ -141,7 +148,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
{
|
||||
final String path = "/foo";
|
||||
final CountDownLatch handlerLatch = new CountDownLatch(1);
|
||||
Session session = startClient(startHTTPServer(new AbstractHandler()
|
||||
Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
|
||||
|
@ -156,11 +163,11 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}), null);
|
||||
|
||||
Headers headers = new Headers();
|
||||
headers.put("method", "HEAD");
|
||||
headers.put("url", path);
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
headers.put(HTTPSPDYHeader.METHOD.name(version()), "HEAD");
|
||||
headers.put(HTTPSPDYHeader.URI.name(version()), path);
|
||||
headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
|
@ -169,7 +176,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
{
|
||||
Assert.assertTrue(replyInfo.isClose());
|
||||
Headers replyHeaders = replyInfo.getHeaders();
|
||||
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
|
||||
Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
|
||||
replyLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
@ -183,7 +190,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
final String path = "/foo";
|
||||
final String data = "a=1&b=2";
|
||||
final CountDownLatch handlerLatch = new CountDownLatch(1);
|
||||
Session session = startClient(startHTTPServer(new AbstractHandler()
|
||||
Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
|
||||
|
@ -206,11 +213,11 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}), null);
|
||||
|
||||
Headers headers = new Headers();
|
||||
headers.put("method", "POST");
|
||||
headers.put("url", path);
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
headers.put(HTTPSPDYHeader.METHOD.name(version()), "POST");
|
||||
headers.put(HTTPSPDYHeader.URI.name(version()), path);
|
||||
headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
|
||||
headers.put("content-type", "application/x-www-form-urlencoded");
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
|
||||
|
@ -220,7 +227,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
{
|
||||
Assert.assertTrue(replyInfo.isClose());
|
||||
Headers replyHeaders = replyInfo.getHeaders();
|
||||
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
|
||||
Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
|
||||
replyLatch.countDown();
|
||||
}
|
||||
}).get(5, TimeUnit.SECONDS);
|
||||
|
@ -237,7 +244,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
final String data1 = "a=1&";
|
||||
final String data2 = "b=2";
|
||||
final CountDownLatch handlerLatch = new CountDownLatch(1);
|
||||
Session session = startClient(startHTTPServer(new AbstractHandler()
|
||||
Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
|
||||
|
@ -252,11 +259,11 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}), null);
|
||||
|
||||
Headers headers = new Headers();
|
||||
headers.put("method", "POST");
|
||||
headers.put("url", path);
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
headers.put(HTTPSPDYHeader.METHOD.name(version()), "POST");
|
||||
headers.put(HTTPSPDYHeader.URI.name(version()), path);
|
||||
headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
|
||||
headers.put("content-type", "application/x-www-form-urlencoded");
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
|
||||
|
@ -266,7 +273,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
{
|
||||
Assert.assertTrue(replyInfo.isClose());
|
||||
Headers replyHeaders = replyInfo.getHeaders();
|
||||
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
|
||||
Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
|
||||
replyLatch.countDown();
|
||||
}
|
||||
}).get(5, TimeUnit.SECONDS);
|
||||
|
@ -286,7 +293,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
final String data1 = "a=1&";
|
||||
final String data2 = "b=2";
|
||||
final CountDownLatch handlerLatch = new CountDownLatch(1);
|
||||
Session session = startClient(startHTTPServer(new AbstractHandler()
|
||||
Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
|
||||
|
@ -301,11 +308,11 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}), null);
|
||||
|
||||
Headers headers = new Headers();
|
||||
headers.put("method", "POST");
|
||||
headers.put("url", path);
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
headers.put(HTTPSPDYHeader.METHOD.name(version()), "POST");
|
||||
headers.put(HTTPSPDYHeader.URI.name(version()), path);
|
||||
headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
|
||||
headers.put("content-type", "application/x-www-form-urlencoded");
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
|
||||
|
@ -315,7 +322,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
{
|
||||
Assert.assertTrue(replyInfo.isClose());
|
||||
Headers replyHeaders = replyInfo.getHeaders();
|
||||
Assert.assertTrue(replyHeaders.toString(), replyHeaders.get("status").value().contains("200"));
|
||||
Assert.assertTrue(replyHeaders.toString(), replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
|
||||
replyLatch.countDown();
|
||||
}
|
||||
}).get(5, TimeUnit.SECONDS);
|
||||
|
@ -332,7 +339,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
{
|
||||
final String data = "0123456789ABCDEF";
|
||||
final CountDownLatch handlerLatch = new CountDownLatch(1);
|
||||
Session session = startClient(startHTTPServer(new AbstractHandler()
|
||||
Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
|
||||
|
@ -347,11 +354,11 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}), null);
|
||||
|
||||
Headers headers = new Headers();
|
||||
headers.put("method", "GET");
|
||||
headers.put("url", "/foo");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
|
||||
headers.put(HTTPSPDYHeader.URI.name(version()), "/foo");
|
||||
headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
|
@ -361,7 +368,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
{
|
||||
Assert.assertFalse(replyInfo.isClose());
|
||||
Headers replyHeaders = replyInfo.getHeaders();
|
||||
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
|
||||
Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
|
||||
replyLatch.countDown();
|
||||
}
|
||||
|
||||
|
@ -383,7 +390,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
{
|
||||
final char data = 'x';
|
||||
final CountDownLatch handlerLatch = new CountDownLatch(1);
|
||||
Session session = startClient(startHTTPServer(new AbstractHandler()
|
||||
Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
|
||||
|
@ -398,11 +405,11 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}), null);
|
||||
|
||||
Headers headers = new Headers();
|
||||
headers.put("method", "GET");
|
||||
headers.put("url", "/foo");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
|
||||
headers.put(HTTPSPDYHeader.URI.name(version()), "/foo");
|
||||
headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
|
@ -412,7 +419,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
{
|
||||
Assert.assertFalse(replyInfo.isClose());
|
||||
Headers replyHeaders = replyInfo.getHeaders();
|
||||
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
|
||||
Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
|
||||
replyLatch.countDown();
|
||||
}
|
||||
|
||||
|
@ -437,7 +444,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
final String data1 = "0123456789ABCDEF";
|
||||
final String data2 = "FEDCBA9876543210";
|
||||
final CountDownLatch handlerLatch = new CountDownLatch(1);
|
||||
Session session = startClient(startHTTPServer(new AbstractHandler()
|
||||
Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
|
||||
|
@ -454,11 +461,11 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}), null);
|
||||
|
||||
Headers headers = new Headers();
|
||||
headers.put("method", "GET");
|
||||
headers.put("url", "/foo");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
|
||||
headers.put(HTTPSPDYHeader.URI.name(version()), "/foo");
|
||||
headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(2);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
|
@ -472,7 +479,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
Assert.assertEquals(1, replyFrames.incrementAndGet());
|
||||
Assert.assertFalse(replyInfo.isClose());
|
||||
Headers replyHeaders = replyInfo.getHeaders();
|
||||
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
|
||||
Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
|
||||
replyLatch.countDown();
|
||||
}
|
||||
|
||||
|
@ -499,7 +506,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
final byte[] data = new byte[128 * 1024];
|
||||
Arrays.fill(data, (byte)'x');
|
||||
final CountDownLatch handlerLatch = new CountDownLatch(1);
|
||||
Session session = startClient(startHTTPServer(new AbstractHandler()
|
||||
Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
|
||||
|
@ -514,11 +521,11 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}), null);
|
||||
|
||||
Headers headers = new Headers();
|
||||
headers.put("method", "GET");
|
||||
headers.put("url", "/foo");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
|
||||
headers.put(HTTPSPDYHeader.URI.name(version()), "/foo");
|
||||
headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
|
@ -530,7 +537,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
{
|
||||
Assert.assertFalse(replyInfo.isClose());
|
||||
Headers replyHeaders = replyInfo.getHeaders();
|
||||
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
|
||||
Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
|
||||
replyLatch.countDown();
|
||||
}
|
||||
|
||||
|
@ -556,7 +563,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
final byte[] data = new byte[128 * 1024];
|
||||
Arrays.fill(data, (byte)'y');
|
||||
final CountDownLatch handlerLatch = new CountDownLatch(1);
|
||||
Session session = startClient(startHTTPServer(new AbstractHandler()
|
||||
Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
|
||||
|
@ -572,11 +579,11 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}), null);
|
||||
|
||||
Headers headers = new Headers();
|
||||
headers.put("method", "GET");
|
||||
headers.put("url", "/foo");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
|
||||
headers.put(HTTPSPDYHeader.URI.name(version()), "/foo");
|
||||
headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
|
@ -588,7 +595,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
{
|
||||
Assert.assertFalse(replyInfo.isClose());
|
||||
Headers replyHeaders = replyInfo.getHeaders();
|
||||
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
|
||||
Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
|
||||
replyLatch.countDown();
|
||||
}
|
||||
|
||||
|
@ -613,7 +620,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
{
|
||||
final String data = "0123456789ABCDEF";
|
||||
final CountDownLatch handlerLatch = new CountDownLatch(1);
|
||||
Session session = startClient(startHTTPServer(new AbstractHandler()
|
||||
Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
|
||||
|
@ -630,11 +637,11 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}), null);
|
||||
|
||||
Headers headers = new Headers();
|
||||
headers.put("method", "GET");
|
||||
headers.put("url", "/foo");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
|
||||
headers.put(HTTPSPDYHeader.URI.name(version()), "/foo");
|
||||
headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
|
@ -646,7 +653,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
{
|
||||
Assert.assertFalse(replyInfo.isClose());
|
||||
Headers replyHeaders = replyInfo.getHeaders();
|
||||
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
|
||||
Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
|
||||
replyLatch.countDown();
|
||||
}
|
||||
|
||||
|
@ -674,7 +681,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
final String data1 = "0123456789ABCDEF";
|
||||
final String data2 = "FEDCBA9876543210";
|
||||
final CountDownLatch handlerLatch = new CountDownLatch(1);
|
||||
Session session = startClient(startHTTPServer(new AbstractHandler()
|
||||
Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
|
||||
|
@ -693,11 +700,11 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}), null);
|
||||
|
||||
Headers headers = new Headers();
|
||||
headers.put("method", "GET");
|
||||
headers.put("url", "/foo");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
|
||||
headers.put(HTTPSPDYHeader.URI.name(version()), "/foo");
|
||||
headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
|
@ -709,7 +716,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
{
|
||||
Assert.assertFalse(replyInfo.isClose());
|
||||
Headers replyHeaders = replyInfo.getHeaders();
|
||||
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
|
||||
Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
|
||||
replyLatch.countDown();
|
||||
}
|
||||
|
||||
|
@ -736,7 +743,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
{
|
||||
final String suffix = "/redirect";
|
||||
final CountDownLatch handlerLatch = new CountDownLatch(1);
|
||||
Session session = startClient(startHTTPServer(new AbstractHandler()
|
||||
Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
|
||||
|
@ -751,11 +758,11 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}), null);
|
||||
|
||||
Headers headers = new Headers();
|
||||
headers.put("method", "GET");
|
||||
headers.put("url", "/foo");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
|
||||
headers.put(HTTPSPDYHeader.URI.name(version()), "/foo");
|
||||
headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
|
@ -767,7 +774,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
Assert.assertEquals(1, replies.incrementAndGet());
|
||||
Assert.assertTrue(replyInfo.isClose());
|
||||
Headers replyHeaders = replyInfo.getHeaders();
|
||||
Assert.assertTrue(replyHeaders.get("status").value().contains("302"));
|
||||
Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("302"));
|
||||
Assert.assertTrue(replyHeaders.get("location").value().endsWith(suffix));
|
||||
replyLatch.countDown();
|
||||
}
|
||||
|
@ -780,7 +787,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
public void testGETWithSendError() throws Exception
|
||||
{
|
||||
final CountDownLatch handlerLatch = new CountDownLatch(1);
|
||||
Session session = startClient(startHTTPServer(new AbstractHandler()
|
||||
Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
|
||||
|
@ -793,11 +800,11 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}), null);
|
||||
|
||||
Headers headers = new Headers();
|
||||
headers.put("method", "GET");
|
||||
headers.put("url", "/foo");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
|
||||
headers.put(HTTPSPDYHeader.URI.name(version()), "/foo");
|
||||
headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
|
@ -810,7 +817,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
Assert.assertEquals(1, replies.incrementAndGet());
|
||||
Assert.assertFalse(replyInfo.isClose());
|
||||
Headers replyHeaders = replyInfo.getHeaders();
|
||||
Assert.assertTrue(replyHeaders.get("status").value().contains("404"));
|
||||
Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("404"));
|
||||
replyLatch.countDown();
|
||||
}
|
||||
|
||||
|
@ -829,7 +836,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
@Test
|
||||
public void testGETWithException() throws Exception
|
||||
{
|
||||
Session session = startClient(startHTTPServer(new AbstractHandler()
|
||||
Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
|
||||
|
@ -840,11 +847,11 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}), null);
|
||||
|
||||
Headers headers = new Headers();
|
||||
headers.put("method", "GET");
|
||||
headers.put("url", "/foo");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
|
||||
headers.put(HTTPSPDYHeader.URI.name(version()), "/foo");
|
||||
headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
|
@ -856,7 +863,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
Assert.assertEquals(1, replies.incrementAndGet());
|
||||
Assert.assertTrue(replyInfo.isClose());
|
||||
Headers replyHeaders = replyInfo.getHeaders();
|
||||
Assert.assertTrue(replyHeaders.get("status").value().contains("500"));
|
||||
Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("500"));
|
||||
replyLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
@ -869,7 +876,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
final String pangram1 = "the quick brown fox jumps over the lazy dog";
|
||||
final String pangram2 = "qualche vago ione tipo zolfo, bromo, sodio";
|
||||
final CountDownLatch handlerLatch = new CountDownLatch(1);
|
||||
Session session = startClient(startHTTPServer(new AbstractHandler()
|
||||
Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
|
||||
|
@ -887,11 +894,11 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}), null);
|
||||
|
||||
Headers headers = new Headers();
|
||||
headers.put("method", "GET");
|
||||
headers.put("url", "/foo");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
|
||||
headers.put(HTTPSPDYHeader.URI.name(version()), "/foo");
|
||||
headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(2);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
|
@ -905,7 +912,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
Assert.assertEquals(1, replyFrames.incrementAndGet());
|
||||
Assert.assertFalse(replyInfo.isClose());
|
||||
Headers replyHeaders = replyInfo.getHeaders();
|
||||
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
|
||||
Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
|
||||
Assert.assertTrue(replyHeaders.get("extra").value().contains("X"));
|
||||
replyLatch.countDown();
|
||||
}
|
||||
|
@ -933,11 +940,30 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testGETWithMediumContentByPassed() throws Exception
|
||||
public void testGETWithMediumContentAsInputStreamByPassed() throws Exception
|
||||
{
|
||||
byte[] data = new byte[2048];
|
||||
testGETWithContentByPassed(new ByteArrayInputStream(data), data.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGETWithBigContentAsInputStreamByPassed() throws Exception
|
||||
{
|
||||
byte[] data = new byte[128 * 1024];
|
||||
testGETWithContentByPassed(new ByteArrayInputStream(data), data.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGETWithMediumContentAsBufferByPassed() throws Exception
|
||||
{
|
||||
byte[] data = new byte[2048];
|
||||
testGETWithContentByPassed(new ByteArrayBuffer(data), data.length);
|
||||
}
|
||||
|
||||
private void testGETWithContentByPassed(final Object content, final int length) throws Exception
|
||||
{
|
||||
final byte[] data = new byte[2048];
|
||||
final CountDownLatch handlerLatch = new CountDownLatch(1);
|
||||
Session session = startClient(startHTTPServer(new AbstractHandler()
|
||||
Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
|
||||
|
@ -947,23 +973,23 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
// We use this trick that's present in Jetty code: if we add a request attribute
|
||||
// called "org.eclipse.jetty.server.sendContent", then it will trigger the
|
||||
// content bypass that we want to test
|
||||
request.setAttribute("org.eclipse.jetty.server.sendContent", new ByteArrayBuffer(data));
|
||||
request.setAttribute("org.eclipse.jetty.server.sendContent", content);
|
||||
handlerLatch.countDown();
|
||||
}
|
||||
}), null);
|
||||
|
||||
Headers headers = new Headers();
|
||||
headers.put("method", "GET");
|
||||
headers.put("url", "/foo");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
|
||||
headers.put(HTTPSPDYHeader.URI.name(version()), "/foo");
|
||||
headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
private final AtomicInteger replyFrames = new AtomicInteger();
|
||||
private final AtomicInteger dataFrames = new AtomicInteger();
|
||||
private final AtomicInteger contentLength = new AtomicInteger();
|
||||
|
||||
@Override
|
||||
public void onReply(Stream stream, ReplyInfo replyInfo)
|
||||
|
@ -971,17 +997,19 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
Assert.assertEquals(1, replyFrames.incrementAndGet());
|
||||
Assert.assertFalse(replyInfo.isClose());
|
||||
Headers replyHeaders = replyInfo.getHeaders();
|
||||
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
|
||||
Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
|
||||
replyLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
Assert.assertEquals(1, dataFrames.incrementAndGet());
|
||||
Assert.assertTrue(dataInfo.isClose());
|
||||
Assert.assertArrayEquals(data, dataInfo.asBytes(true));
|
||||
dataLatch.countDown();
|
||||
contentLength.addAndGet(dataInfo.asBytes(true).length);
|
||||
if (dataInfo.isClose())
|
||||
{
|
||||
Assert.assertEquals(length, contentLength.get());
|
||||
dataLatch.countDown();
|
||||
}
|
||||
}
|
||||
});
|
||||
Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
|
||||
|
@ -994,7 +1022,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
{
|
||||
final byte[] data = new byte[2000];
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
Session session = startClient(startHTTPServer(new AbstractHandler()
|
||||
Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, final Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
|
||||
|
@ -1030,11 +1058,11 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}), null);
|
||||
|
||||
Headers headers = new Headers();
|
||||
headers.put("method", "POST");
|
||||
headers.put("url", "/foo");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
headers.put(HTTPSPDYHeader.METHOD.name(version()), "POST");
|
||||
headers.put(HTTPSPDYHeader.URI.name(version()), "/foo");
|
||||
headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
|
||||
{
|
||||
|
@ -1042,7 +1070,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
public void onReply(Stream stream, ReplyInfo replyInfo)
|
||||
{
|
||||
Headers replyHeaders = replyInfo.getHeaders();
|
||||
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
|
||||
Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
|
||||
replyLatch.countDown();
|
||||
}
|
||||
}).get(5, TimeUnit.SECONDS);
|
||||
|
@ -1057,7 +1085,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
{
|
||||
final byte[] data = new byte[2000];
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
Session session = startClient(startHTTPServer(new AbstractHandler()
|
||||
Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, final Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
|
||||
|
@ -1093,11 +1121,11 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}), null);
|
||||
|
||||
Headers headers = new Headers();
|
||||
headers.put("method", "POST");
|
||||
headers.put("url", "/foo");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
headers.put(HTTPSPDYHeader.METHOD.name(version()), "POST");
|
||||
headers.put(HTTPSPDYHeader.URI.name(version()), "/foo");
|
||||
headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
|
||||
{
|
||||
|
@ -1105,7 +1133,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
public void onReply(Stream stream, ReplyInfo replyInfo)
|
||||
{
|
||||
Headers replyHeaders = replyInfo.getHeaders();
|
||||
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
|
||||
Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
|
||||
replyLatch.countDown();
|
||||
}
|
||||
}).get(5, TimeUnit.SECONDS);
|
||||
|
@ -1121,7 +1149,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
{
|
||||
final byte[] data = new byte[1000];
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
Session session = startClient(startHTTPServer(new AbstractHandler()
|
||||
Session session = startClient(version(), startHTTPServer(version(), new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, final Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
|
||||
|
@ -1166,11 +1194,11 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}), null);
|
||||
|
||||
Headers headers = new Headers();
|
||||
headers.put("method", "POST");
|
||||
headers.put("url", "/foo");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("scheme", "http");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
headers.put(HTTPSPDYHeader.METHOD.name(version()), "POST");
|
||||
headers.put(HTTPSPDYHeader.URI.name(version()), "/foo");
|
||||
headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
|
||||
final CountDownLatch responseLatch = new CountDownLatch(2);
|
||||
Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
|
||||
{
|
||||
|
@ -1178,7 +1206,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
public void onReply(Stream stream, ReplyInfo replyInfo)
|
||||
{
|
||||
Headers replyHeaders = replyInfo.getHeaders();
|
||||
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
|
||||
Assert.assertTrue(replyHeaders.get(HTTPSPDYHeader.STATUS.name(version())).value().contains("200"));
|
||||
responseLatch.countDown();
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (c) 2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.eclipse.jetty.spdy.http;
|
||||
|
||||
import org.eclipse.jetty.spdy.api.SPDY;
|
||||
|
||||
public class ServerHTTPSPDYv3Test extends ServerHTTPSPDYv2Test
|
||||
{
|
||||
@Override
|
||||
protected short version()
|
||||
{
|
||||
return SPDY.V3;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (c) 2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.eclipse.jetty.spdy;
|
||||
|
||||
import org.eclipse.jetty.spdy.api.SPDY;
|
||||
|
||||
public class FlowControlStrategyFactory
|
||||
{
|
||||
private FlowControlStrategyFactory()
|
||||
{
|
||||
}
|
||||
|
||||
public static FlowControlStrategy newFlowControlStrategy(short version)
|
||||
{
|
||||
switch (version)
|
||||
{
|
||||
case SPDY.V2:
|
||||
return new FlowControlStrategy.None();
|
||||
case SPDY.V3:
|
||||
return new SPDYv3FlowControlStrategy();
|
||||
default:
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -122,7 +122,8 @@ public class SPDYAsyncConnection extends AbstractConnection implements AsyncConn
|
|||
catch (Exception x)
|
||||
{
|
||||
close(false);
|
||||
handler.failed(x);
|
||||
handler.failed(context, x);
|
||||
return -1;
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -222,6 +223,7 @@ public class SPDYAsyncConnection extends AbstractConnection implements AsyncConn
|
|||
@Override
|
||||
public void onIdleExpired(long idleForMs)
|
||||
{
|
||||
logger.debug("Idle timeout expired for {}", getEndPoint());
|
||||
session.goAway();
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,6 @@ import java.util.concurrent.Executors;
|
|||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
|
@ -64,7 +63,8 @@ public class SPDYClient
|
|||
private final short version;
|
||||
private final Factory factory;
|
||||
private SocketAddress bindAddress;
|
||||
private long maxIdleTime;
|
||||
private long maxIdleTime = -1;
|
||||
private volatile int initialWindowSize = 65536;
|
||||
|
||||
protected SPDYClient(short version, Factory factory)
|
||||
{
|
||||
|
@ -119,6 +119,16 @@ public class SPDYClient
|
|||
this.maxIdleTime = maxIdleTime;
|
||||
}
|
||||
|
||||
public int getInitialWindowSize()
|
||||
{
|
||||
return initialWindowSize;
|
||||
}
|
||||
|
||||
public void setInitialWindowSize(int initialWindowSize)
|
||||
{
|
||||
this.initialWindowSize = initialWindowSize;
|
||||
}
|
||||
|
||||
protected String selectProtocol(List<String> serverProtocols)
|
||||
{
|
||||
if (serverProtocols == null)
|
||||
|
@ -173,6 +183,11 @@ public class SPDYClient
|
|||
return engine;
|
||||
}
|
||||
|
||||
protected FlowControlStrategy newFlowControlStrategy()
|
||||
{
|
||||
return FlowControlStrategyFactory.newFlowControlStrategy(version);
|
||||
}
|
||||
|
||||
public static class Factory extends AggregateLifeCycle
|
||||
{
|
||||
private final Map<String, AsyncConnectionFactory> factories = new ConcurrentHashMap<>();
|
||||
|
@ -314,7 +329,7 @@ public class SPDYClient
|
|||
}
|
||||
|
||||
@Override
|
||||
public AsyncConnection newConnection(final SocketChannel channel, AsyncEndPoint endPoint, Object attachment)
|
||||
public AsyncConnection newConnection(final SocketChannel channel, AsyncEndPoint endPoint, final Object attachment)
|
||||
{
|
||||
SessionPromise sessionPromise = (SessionPromise)attachment;
|
||||
final SPDYClient client = sessionPromise.client;
|
||||
|
@ -323,31 +338,18 @@ public class SPDYClient
|
|||
{
|
||||
if (sslContextFactory != null)
|
||||
{
|
||||
final AtomicReference<AsyncEndPoint> sslEndPointRef = new AtomicReference<>();
|
||||
final AtomicReference<Object> attachmentRef = new AtomicReference<>(attachment);
|
||||
SSLEngine engine = client.newSSLEngine(sslContextFactory, channel);
|
||||
final SSLEngine engine = client.newSSLEngine(sslContextFactory, channel);
|
||||
SslConnection sslConnection = new SslConnection(engine, endPoint)
|
||||
{
|
||||
@Override
|
||||
public void onClose()
|
||||
{
|
||||
sslEndPointRef.set(null);
|
||||
attachmentRef.set(null);
|
||||
NextProtoNego.remove(engine);
|
||||
super.onClose();
|
||||
}
|
||||
};
|
||||
endPoint.setConnection(sslConnection);
|
||||
AsyncEndPoint sslEndPoint = sslConnection.getSslEndPoint();
|
||||
sslEndPointRef.set(sslEndPoint);
|
||||
|
||||
// Instances of the ClientProvider inner class strong reference the
|
||||
// SslEndPoint (via lexical scoping), which strong references the SSLEngine.
|
||||
// Since NextProtoNego stores in a WeakHashMap the SSLEngine as key
|
||||
// and this instance as value, we are in the situation where the value
|
||||
// of a WeakHashMap refers indirectly to the key, which is bad because
|
||||
// the entry will never be removed from the WeakHashMap.
|
||||
// We use AtomicReferences to be captured via lexical scoping,
|
||||
// and we null them out above when the connection is closed.
|
||||
final AsyncEndPoint sslEndPoint = sslConnection.getSslEndPoint();
|
||||
NextProtoNego.put(engine, new NextProtoNego.ClientProvider()
|
||||
{
|
||||
@Override
|
||||
|
@ -361,8 +363,7 @@ public class SPDYClient
|
|||
{
|
||||
// Server does not support NPN, but this is a SPDY client, so hardcode SPDY
|
||||
ClientSPDYAsyncConnectionFactory connectionFactory = new ClientSPDYAsyncConnectionFactory();
|
||||
AsyncEndPoint sslEndPoint = sslEndPointRef.get();
|
||||
AsyncConnection connection = connectionFactory.newAsyncConnection(channel, sslEndPoint, attachmentRef.get());
|
||||
AsyncConnection connection = connectionFactory.newAsyncConnection(channel, sslEndPoint, attachment);
|
||||
sslEndPoint.setConnection(connection);
|
||||
}
|
||||
|
||||
|
@ -374,8 +375,7 @@ public class SPDYClient
|
|||
return null;
|
||||
|
||||
AsyncConnectionFactory connectionFactory = client.getAsyncConnectionFactory(protocol);
|
||||
AsyncEndPoint sslEndPoint = sslEndPointRef.get();
|
||||
AsyncConnection connection = connectionFactory.newAsyncConnection(channel, sslEndPoint, attachmentRef.get());
|
||||
AsyncConnection connection = connectionFactory.newAsyncConnection(channel, sslEndPoint, attachment);
|
||||
sslEndPoint.setConnection(connection);
|
||||
return protocol;
|
||||
}
|
||||
|
@ -398,7 +398,7 @@ public class SPDYClient
|
|||
}
|
||||
catch (RuntimeException x)
|
||||
{
|
||||
sessionPromise.failed(x);
|
||||
sessionPromise.failed(null,x);
|
||||
throw x;
|
||||
}
|
||||
}
|
||||
|
@ -435,7 +435,8 @@ public class SPDYClient
|
|||
public AsyncConnection newAsyncConnection(SocketChannel channel, AsyncEndPoint endPoint, Object attachment)
|
||||
{
|
||||
SessionPromise sessionPromise = (SessionPromise)attachment;
|
||||
Factory factory = sessionPromise.client.factory;
|
||||
SPDYClient client = sessionPromise.client;
|
||||
Factory factory = client.factory;
|
||||
|
||||
CompressionFactory compressionFactory = new StandardCompressionFactory();
|
||||
Parser parser = new Parser(compressionFactory.newDecompressor());
|
||||
|
@ -444,7 +445,10 @@ public class SPDYClient
|
|||
SPDYAsyncConnection connection = new ClientSPDYAsyncConnection(endPoint, factory.bufferPool, parser, factory);
|
||||
endPoint.setConnection(connection);
|
||||
|
||||
StandardSession session = new StandardSession(sessionPromise.client.version, factory.bufferPool, factory.threadPool, factory.scheduler, connection, connection, 1, sessionPromise.listener, generator);
|
||||
FlowControlStrategy flowControlStrategy = client.newFlowControlStrategy();
|
||||
|
||||
StandardSession session = new StandardSession(client.version, factory.bufferPool, factory.threadPool, factory.scheduler, connection, connection, 1, sessionPromise.listener, generator, flowControlStrategy);
|
||||
session.setWindowSize(client.getInitialWindowSize());
|
||||
parser.addListener(session);
|
||||
sessionPromise.completed(session);
|
||||
connection.setSession(session);
|
||||
|
|
|
@ -27,9 +27,9 @@ import java.util.Queue;
|
|||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
|
@ -54,10 +54,12 @@ public class SPDYServerConnector extends SelectChannelConnector
|
|||
private final Map<String, AsyncConnectionFactory> factories = new LinkedHashMap<>();
|
||||
private final Queue<Session> sessions = new ConcurrentLinkedQueue<>();
|
||||
private final ByteBufferPool bufferPool = new StandardByteBufferPool();
|
||||
private final Executor executor = new LazyExecutor();
|
||||
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
|
||||
private final ServerSessionFrameListener listener;
|
||||
private final SslContextFactory sslContextFactory;
|
||||
private AsyncConnectionFactory defaultConnectionFactory;
|
||||
private volatile AsyncConnectionFactory defaultConnectionFactory;
|
||||
private volatile int initialWindowSize = 65536;
|
||||
|
||||
public SPDYServerConnector(ServerSessionFrameListener listener)
|
||||
{
|
||||
|
@ -70,6 +72,9 @@ public class SPDYServerConnector extends SelectChannelConnector
|
|||
this.sslContextFactory = sslContextFactory;
|
||||
if (sslContextFactory != null)
|
||||
addBean(sslContextFactory);
|
||||
putAsyncConnectionFactory("spdy/3", new ServerSPDYAsyncConnectionFactory(SPDY.V3, bufferPool, executor, scheduler, listener));
|
||||
putAsyncConnectionFactory("spdy/2", new ServerSPDYAsyncConnectionFactory(SPDY.V2, bufferPool, executor, scheduler, listener));
|
||||
setDefaultAsyncConnectionFactory(getAsyncConnectionFactory("spdy/2"));
|
||||
}
|
||||
|
||||
public ByteBufferPool getByteBufferPool()
|
||||
|
@ -79,17 +84,7 @@ public class SPDYServerConnector extends SelectChannelConnector
|
|||
|
||||
public Executor getExecutor()
|
||||
{
|
||||
final ThreadPool threadPool = getThreadPool();
|
||||
if (threadPool instanceof Executor)
|
||||
return (Executor)threadPool;
|
||||
return new Executor()
|
||||
{
|
||||
@Override
|
||||
public void execute(Runnable command)
|
||||
{
|
||||
threadPool.dispatch(command);
|
||||
}
|
||||
};
|
||||
return executor;
|
||||
}
|
||||
|
||||
public ScheduledExecutorService getScheduler()
|
||||
|
@ -97,6 +92,11 @@ public class SPDYServerConnector extends SelectChannelConnector
|
|||
return scheduler;
|
||||
}
|
||||
|
||||
public ServerSessionFrameListener getServerSessionFrameListener()
|
||||
{
|
||||
return listener;
|
||||
}
|
||||
|
||||
public SslContextFactory getSslContextFactory()
|
||||
{
|
||||
return sslContextFactory;
|
||||
|
@ -106,8 +106,6 @@ public class SPDYServerConnector extends SelectChannelConnector
|
|||
protected void doStart() throws Exception
|
||||
{
|
||||
super.doStart();
|
||||
defaultConnectionFactory = new ServerSPDYAsyncConnectionFactory(SPDY.V2, getByteBufferPool(), getExecutor(), scheduler, listener);
|
||||
putAsyncConnectionFactory("spdy/2", defaultConnectionFactory);
|
||||
logger.info("SPDY support is experimental. Please report feedback at jetty-dev@eclipse.org");
|
||||
}
|
||||
|
||||
|
@ -166,46 +164,39 @@ public class SPDYServerConnector extends SelectChannelConnector
|
|||
}
|
||||
}
|
||||
|
||||
protected AsyncConnectionFactory getDefaultAsyncConnectionFactory()
|
||||
public AsyncConnectionFactory getDefaultAsyncConnectionFactory()
|
||||
{
|
||||
return defaultConnectionFactory;
|
||||
}
|
||||
|
||||
public void setDefaultAsyncConnectionFactory(AsyncConnectionFactory defaultConnectionFactory)
|
||||
{
|
||||
this.defaultConnectionFactory = defaultConnectionFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AsyncConnection newConnection(final SocketChannel channel, AsyncEndPoint endPoint)
|
||||
{
|
||||
if (sslContextFactory != null)
|
||||
{
|
||||
SSLEngine engine = newSSLEngine(sslContextFactory, channel);
|
||||
final AtomicReference<AsyncEndPoint> sslEndPointRef = new AtomicReference<>();
|
||||
final SSLEngine engine = newSSLEngine(sslContextFactory, channel);
|
||||
SslConnection sslConnection = new SslConnection(engine, endPoint)
|
||||
{
|
||||
@Override
|
||||
public void onClose()
|
||||
{
|
||||
sslEndPointRef.set(null);
|
||||
NextProtoNego.remove(engine);
|
||||
super.onClose();
|
||||
}
|
||||
};
|
||||
endPoint.setConnection(sslConnection);
|
||||
AsyncEndPoint sslEndPoint = sslConnection.getSslEndPoint();
|
||||
sslEndPointRef.set(sslEndPoint);
|
||||
|
||||
// Instances of the ServerProvider inner class strong reference the
|
||||
// SslEndPoint (via lexical scoping), which strong references the SSLEngine.
|
||||
// Since NextProtoNego stores in a WeakHashMap the SSLEngine as key
|
||||
// and this instance as value, we are in the situation where the value
|
||||
// of a WeakHashMap refers indirectly to the key, which is bad because
|
||||
// the entry will never be removed from the WeakHashMap.
|
||||
// We use AtomicReferences to be captured via lexical scoping,
|
||||
// and we null them out above when the connection is closed.
|
||||
final AsyncEndPoint sslEndPoint = sslConnection.getSslEndPoint();
|
||||
NextProtoNego.put(engine, new NextProtoNego.ServerProvider()
|
||||
{
|
||||
@Override
|
||||
public void unsupported()
|
||||
{
|
||||
AsyncConnectionFactory connectionFactory = getDefaultAsyncConnectionFactory();
|
||||
AsyncEndPoint sslEndPoint = sslEndPointRef.get();
|
||||
AsyncConnection connection = connectionFactory.newAsyncConnection(channel, sslEndPoint, SPDYServerConnector.this);
|
||||
sslEndPoint.setConnection(connection);
|
||||
}
|
||||
|
@ -220,7 +211,6 @@ public class SPDYServerConnector extends SelectChannelConnector
|
|||
public void protocolSelected(String protocol)
|
||||
{
|
||||
AsyncConnectionFactory connectionFactory = getAsyncConnectionFactory(protocol);
|
||||
AsyncEndPoint sslEndPoint = sslEndPointRef.get();
|
||||
AsyncConnection connection = connectionFactory.newAsyncConnection(channel, sslEndPoint, SPDYServerConnector.this);
|
||||
sslEndPoint.setConnection(connection);
|
||||
}
|
||||
|
@ -242,6 +232,11 @@ public class SPDYServerConnector extends SelectChannelConnector
|
|||
}
|
||||
}
|
||||
|
||||
protected FlowControlStrategy newFlowControlStrategy(short version)
|
||||
{
|
||||
return FlowControlStrategyFactory.newFlowControlStrategy(version);
|
||||
}
|
||||
|
||||
protected SSLEngine newSSLEngine(SslContextFactory sslContextFactory, SocketChannel channel)
|
||||
{
|
||||
String peerHost = channel.socket().getInetAddress().getHostAddress();
|
||||
|
@ -287,4 +282,26 @@ public class SPDYServerConnector extends SelectChannelConnector
|
|||
{
|
||||
return Collections.unmodifiableCollection(sessions);
|
||||
}
|
||||
|
||||
public int getInitialWindowSize()
|
||||
{
|
||||
return initialWindowSize;
|
||||
}
|
||||
|
||||
public void setInitialWindowSize(int initialWindowSize)
|
||||
{
|
||||
this.initialWindowSize = initialWindowSize;
|
||||
}
|
||||
|
||||
private class LazyExecutor implements Executor
|
||||
{
|
||||
@Override
|
||||
public void execute(Runnable command)
|
||||
{
|
||||
ThreadPool threadPool = getThreadPool();
|
||||
if (threadPool == null)
|
||||
throw new RejectedExecutionException();
|
||||
threadPool.dispatch(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,6 +50,11 @@ public class ServerSPDYAsyncConnectionFactory implements AsyncConnectionFactory
|
|||
this.listener = listener;
|
||||
}
|
||||
|
||||
public short getVersion()
|
||||
{
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncConnection newAsyncConnection(SocketChannel channel, AsyncEndPoint endPoint, Object attachment)
|
||||
{
|
||||
|
@ -57,16 +62,16 @@ public class ServerSPDYAsyncConnectionFactory implements AsyncConnectionFactory
|
|||
Parser parser = new Parser(compressionFactory.newDecompressor());
|
||||
Generator generator = new Generator(bufferPool, compressionFactory.newCompressor());
|
||||
|
||||
ServerSessionFrameListener listener = this.listener;
|
||||
if (listener == null)
|
||||
listener = newServerSessionFrameListener(endPoint, attachment);
|
||||
|
||||
SPDYServerConnector connector = (SPDYServerConnector)attachment;
|
||||
|
||||
ServerSessionFrameListener listener = provideServerSessionFrameListener(endPoint, attachment);
|
||||
SPDYAsyncConnection connection = new ServerSPDYAsyncConnection(endPoint, bufferPool, parser, listener, connector);
|
||||
endPoint.setConnection(connection);
|
||||
|
||||
final StandardSession session = new StandardSession(version, bufferPool, threadPool, scheduler, connection, connection, 2, listener, generator);
|
||||
FlowControlStrategy flowControlStrategy = connector.newFlowControlStrategy(version);
|
||||
|
||||
StandardSession session = new StandardSession(version, bufferPool, threadPool, scheduler, connection, connection, 2, listener, generator, flowControlStrategy);
|
||||
session.setWindowSize(connector.getInitialWindowSize());
|
||||
parser.addListener(session);
|
||||
connection.setSession(session);
|
||||
|
||||
|
@ -75,7 +80,7 @@ public class ServerSPDYAsyncConnectionFactory implements AsyncConnectionFactory
|
|||
return connection;
|
||||
}
|
||||
|
||||
protected ServerSessionFrameListener newServerSessionFrameListener(AsyncEndPoint endPoint, Object attachment)
|
||||
protected ServerSessionFrameListener provideServerSessionFrameListener(AsyncEndPoint endPoint, Object attachment)
|
||||
{
|
||||
return listener;
|
||||
}
|
||||
|
|
|
@ -52,9 +52,17 @@ public abstract class AbstractTest
|
|||
protected SPDYServerConnector connector;
|
||||
|
||||
protected InetSocketAddress startServer(ServerSessionFrameListener listener) throws Exception
|
||||
{
|
||||
return startServer(SPDY.V2, listener);
|
||||
}
|
||||
|
||||
protected InetSocketAddress startServer(short version, ServerSessionFrameListener listener) throws Exception
|
||||
{
|
||||
if (connector == null)
|
||||
connector = newSPDYServerConnector(listener);
|
||||
if (listener == null)
|
||||
listener = connector.getServerSessionFrameListener();
|
||||
connector.setDefaultAsyncConnectionFactory(new ServerSPDYAsyncConnectionFactory(version, connector.getByteBufferPool(), connector.getExecutor(), connector.getScheduler(), listener));
|
||||
connector.setPort(0);
|
||||
server = new Server();
|
||||
server.addConnector(connector);
|
||||
|
@ -68,6 +76,11 @@ public abstract class AbstractTest
|
|||
}
|
||||
|
||||
protected Session startClient(InetSocketAddress socketAddress, SessionFrameListener listener) throws Exception
|
||||
{
|
||||
return startClient(SPDY.V2, socketAddress, listener);
|
||||
}
|
||||
|
||||
protected Session startClient(short version, InetSocketAddress socketAddress, SessionFrameListener listener) throws Exception
|
||||
{
|
||||
if (clientFactory == null)
|
||||
{
|
||||
|
@ -76,7 +89,7 @@ public abstract class AbstractTest
|
|||
clientFactory = newSPDYClientFactory(threadPool);
|
||||
clientFactory.start();
|
||||
}
|
||||
return clientFactory.newSPDYClient(SPDY.V2).connect(socketAddress, listener).get(5, TimeUnit.SECONDS);
|
||||
return clientFactory.newSPDYClient(version).connect(socketAddress, listener).get(5, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
protected SPDYClient.Factory newSPDYClientFactory(Executor threadPool)
|
||||
|
|
|
@ -38,7 +38,6 @@ import org.eclipse.jetty.spdy.api.StringDataInfo;
|
|||
import org.eclipse.jetty.spdy.api.SynInfo;
|
||||
import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
|
||||
import org.eclipse.jetty.spdy.frames.ControlFrame;
|
||||
import org.eclipse.jetty.spdy.frames.DataFrame;
|
||||
import org.eclipse.jetty.spdy.frames.GoAwayFrame;
|
||||
import org.eclipse.jetty.spdy.frames.RstStreamFrame;
|
||||
import org.eclipse.jetty.spdy.frames.SynReplyFrame;
|
||||
|
@ -145,14 +144,12 @@ public class ClosedStreamTest extends AbstractTest
|
|||
public void onReply(Stream stream, ReplyInfo replyInfo)
|
||||
{
|
||||
replyReceivedLatch.countDown();
|
||||
super.onReply(stream,replyInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
clientReceivedDataLatch.countDown();
|
||||
super.onData(stream,dataInfo);
|
||||
}
|
||||
}).get();
|
||||
assertThat("reply has been received by client",replyReceivedLatch.await(5,TimeUnit.SECONDS),is(true));
|
||||
|
@ -204,7 +201,6 @@ public class ClosedStreamTest extends AbstractTest
|
|||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
serverDataReceivedLatch.countDown();
|
||||
super.onData(stream,dataInfo);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -217,7 +213,7 @@ public class ClosedStreamTest extends AbstractTest
|
|||
|
||||
final Generator generator = new Generator(new StandardByteBufferPool(),new StandardCompressionFactory().newCompressor());
|
||||
int streamId = 1;
|
||||
ByteBuffer synData = generator.control(new SynStreamFrame(version,SynInfo.FLAG_CLOSE, streamId,0,(byte)0,new Headers()));
|
||||
ByteBuffer synData = generator.control(new SynStreamFrame(version,SynInfo.FLAG_CLOSE, streamId,0,(byte)0,(short)0,new Headers()));
|
||||
|
||||
final SocketChannel socketChannel = SocketChannel.open(startServer);
|
||||
socketChannel.write(synData);
|
||||
|
@ -250,13 +246,6 @@ public class ClosedStreamTest extends AbstractTest
|
|||
{
|
||||
clientResetReceivedLatch.countDown();
|
||||
}
|
||||
super.onControlFrame(frame);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataFrame(DataFrame frame, ByteBuffer data)
|
||||
{
|
||||
super.onDataFrame(frame,data);
|
||||
}
|
||||
});
|
||||
ByteBuffer response = ByteBuffer.allocate(28);
|
||||
|
@ -272,7 +261,7 @@ public class ClosedStreamTest extends AbstractTest
|
|||
Assert.assertThat(buffer.hasRemaining(), is(false));
|
||||
|
||||
assertThat("GoAway frame is received by server", goAwayReceivedLatch.await(5,TimeUnit.SECONDS), is(true));
|
||||
|
||||
|
||||
socketChannel.close();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,9 +25,11 @@ import java.util.concurrent.TimeoutException;
|
|||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
|
||||
import org.eclipse.jetty.spdy.api.BytesDataInfo;
|
||||
import org.eclipse.jetty.spdy.api.DataInfo;
|
||||
import org.eclipse.jetty.spdy.api.ReplyInfo;
|
||||
import org.eclipse.jetty.spdy.api.SPDY;
|
||||
import org.eclipse.jetty.spdy.api.SPDYException;
|
||||
import org.eclipse.jetty.spdy.api.Session;
|
||||
import org.eclipse.jetty.spdy.api.SessionFrameListener;
|
||||
|
@ -40,6 +42,9 @@ import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
|
|||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class FlowControlTest extends AbstractTest
|
||||
{
|
||||
@Test
|
||||
|
@ -53,7 +58,7 @@ public class FlowControlTest extends AbstractTest
|
|||
final AtomicReference<DataInfo> dataInfoRef = new AtomicReference<>();
|
||||
final CountDownLatch dataLatch = new CountDownLatch(2);
|
||||
final CountDownLatch settingsLatch = new CountDownLatch(1);
|
||||
Session session = startClient(startServer(new ServerSessionFrameListener.Adapter()
|
||||
Session session = startClient(SPDY.V3, startServer(SPDY.V3, new ServerSessionFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
|
||||
|
@ -112,7 +117,7 @@ public class FlowControlTest extends AbstractTest
|
|||
final int windowSize = 1536;
|
||||
final int length = 5 * windowSize;
|
||||
final CountDownLatch settingsLatch = new CountDownLatch(1);
|
||||
Session session = startClient(startServer(new ServerSessionFrameListener.Adapter()
|
||||
Session session = startClient(SPDY.V3, startServer(SPDY.V3, new ServerSessionFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onSettings(Session session, SettingsInfo settingsInfo)
|
||||
|
@ -183,43 +188,22 @@ public class FlowControlTest extends AbstractTest
|
|||
});
|
||||
|
||||
DataInfo dataInfo = exchanger.exchange(null, 5, TimeUnit.SECONDS);
|
||||
// Check that we are flow control stalled
|
||||
expectException(TimeoutException.class, new Callable<DataInfo>()
|
||||
{
|
||||
@Override
|
||||
public DataInfo call() throws Exception
|
||||
{
|
||||
return exchanger.exchange(null, 1, TimeUnit.SECONDS);
|
||||
}
|
||||
});
|
||||
checkThatWeAreFlowControlStalled(exchanger);
|
||||
|
||||
Assert.assertEquals(windowSize, dataInfo.available());
|
||||
Assert.assertEquals(0, dataInfo.consumed());
|
||||
dataInfo.asByteBuffer(true);
|
||||
|
||||
dataInfo = exchanger.exchange(null, 5, TimeUnit.SECONDS);
|
||||
// Check that we are flow control stalled
|
||||
expectException(TimeoutException.class, new Callable<DataInfo>()
|
||||
{
|
||||
@Override
|
||||
public DataInfo call() throws Exception
|
||||
{
|
||||
return exchanger.exchange(null, 1, TimeUnit.SECONDS);
|
||||
}
|
||||
});
|
||||
checkThatWeAreFlowControlStalled(exchanger);
|
||||
|
||||
Assert.assertEquals(0, dataInfo.available());
|
||||
Assert.assertEquals(0, dataInfo.consumed());
|
||||
dataInfo.consume(dataInfo.length());
|
||||
|
||||
dataInfo = exchanger.exchange(null, 5, TimeUnit.SECONDS);
|
||||
// Check that we are flow control stalled
|
||||
expectException(TimeoutException.class, new Callable<DataInfo>()
|
||||
{
|
||||
@Override
|
||||
public DataInfo call() throws Exception
|
||||
{
|
||||
return exchanger.exchange(null, 1, TimeUnit.SECONDS);
|
||||
}
|
||||
});
|
||||
checkThatWeAreFlowControlStalled(exchanger);
|
||||
|
||||
Assert.assertEquals(dataInfo.length() / 2, dataInfo.consumed());
|
||||
dataInfo.asByteBuffer(true);
|
||||
|
||||
|
@ -236,7 +220,7 @@ public class FlowControlTest extends AbstractTest
|
|||
final int windowSize = 1536;
|
||||
final Exchanger<DataInfo> exchanger = new Exchanger<>();
|
||||
final CountDownLatch settingsLatch = new CountDownLatch(1);
|
||||
Session session = startClient(startServer(new ServerSessionFrameListener.Adapter()
|
||||
Session session = startClient(SPDY.V3, startServer(SPDY.V3, new ServerSessionFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onConnect(Session session)
|
||||
|
@ -312,43 +296,22 @@ public class FlowControlTest extends AbstractTest
|
|||
stream.data(new BytesDataInfo(new byte[length], true));
|
||||
|
||||
DataInfo dataInfo = exchanger.exchange(null, 5, TimeUnit.SECONDS);
|
||||
// Check that we are flow control stalled
|
||||
expectException(TimeoutException.class, new Callable<DataInfo>()
|
||||
{
|
||||
@Override
|
||||
public DataInfo call() throws Exception
|
||||
{
|
||||
return exchanger.exchange(null, 1, TimeUnit.SECONDS);
|
||||
}
|
||||
});
|
||||
checkThatWeAreFlowControlStalled(exchanger);
|
||||
|
||||
Assert.assertEquals(windowSize, dataInfo.available());
|
||||
Assert.assertEquals(0, dataInfo.consumed());
|
||||
dataInfo.asByteBuffer(true);
|
||||
|
||||
dataInfo = exchanger.exchange(null, 5, TimeUnit.SECONDS);
|
||||
// Check that we are flow control stalled
|
||||
expectException(TimeoutException.class, new Callable<DataInfo>()
|
||||
{
|
||||
@Override
|
||||
public DataInfo call() throws Exception
|
||||
{
|
||||
return exchanger.exchange(null, 1, TimeUnit.SECONDS);
|
||||
}
|
||||
});
|
||||
checkThatWeAreFlowControlStalled(exchanger);
|
||||
|
||||
Assert.assertEquals(0, dataInfo.available());
|
||||
Assert.assertEquals(0, dataInfo.consumed());
|
||||
dataInfo.consume(dataInfo.length());
|
||||
|
||||
dataInfo = exchanger.exchange(null, 5, TimeUnit.SECONDS);
|
||||
// Check that we are flow control stalled
|
||||
expectException(TimeoutException.class, new Callable<DataInfo>()
|
||||
{
|
||||
@Override
|
||||
public DataInfo call() throws Exception
|
||||
{
|
||||
return exchanger.exchange(null, 1, TimeUnit.SECONDS);
|
||||
}
|
||||
});
|
||||
checkThatWeAreFlowControlStalled(exchanger);
|
||||
|
||||
Assert.assertEquals(dataInfo.length() / 2, dataInfo.consumed());
|
||||
dataInfo.asByteBuffer(true);
|
||||
|
||||
|
@ -364,7 +327,7 @@ public class FlowControlTest extends AbstractTest
|
|||
{
|
||||
final int windowSize = 1024;
|
||||
final CountDownLatch settingsLatch = new CountDownLatch(1);
|
||||
Session session = startClient(startServer(new ServerSessionFrameListener.Adapter()
|
||||
Session session = startClient(SPDY.V3, startServer(SPDY.V3, new ServerSessionFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onSettings(Session session, SettingsInfo settingsInfo)
|
||||
|
@ -451,6 +414,64 @@ public class FlowControlTest extends AbstractTest
|
|||
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendBigFileWithoutFlowControl() throws Exception
|
||||
{
|
||||
testSendBigFile(SPDY.V2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendBigFileWithFlowControl() throws Exception
|
||||
{
|
||||
testSendBigFile(SPDY.V3);
|
||||
}
|
||||
|
||||
private void testSendBigFile(short version) throws Exception
|
||||
{
|
||||
final int dataSize = 1024 * 1024;
|
||||
final ByteBufferDataInfo bigByteBufferDataInfo = new ByteBufferDataInfo(ByteBuffer.allocate(dataSize),false);
|
||||
final CountDownLatch allDataReceivedLatch = new CountDownLatch(1);
|
||||
|
||||
Session session = startClient(version, startServer(version, new ServerSessionFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
|
||||
{
|
||||
stream.reply(new ReplyInfo(false));
|
||||
stream.data(bigByteBufferDataInfo);
|
||||
return null;
|
||||
}
|
||||
}),new SessionFrameListener.Adapter());
|
||||
|
||||
session.syn(new SynInfo(false),new StreamFrameListener.Adapter()
|
||||
{
|
||||
private int dataBytesReceived;
|
||||
|
||||
@Override
|
||||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
dataBytesReceived = dataBytesReceived + dataInfo.length();
|
||||
dataInfo.consume(dataInfo.length());
|
||||
if (dataBytesReceived == dataSize)
|
||||
allDataReceivedLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
assertThat("all data bytes have been received by the client", allDataReceivedLatch.await(5, TimeUnit.SECONDS), is(true));
|
||||
}
|
||||
|
||||
private void checkThatWeAreFlowControlStalled(final Exchanger<DataInfo> exchanger)
|
||||
{
|
||||
expectException(TimeoutException.class, new Callable<DataInfo>()
|
||||
{
|
||||
@Override
|
||||
public DataInfo call() throws Exception
|
||||
{
|
||||
return exchanger.exchange(null, 1, TimeUnit.SECONDS);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void expectException(Class<? extends Exception> exception, Callable<DataInfo> command)
|
||||
{
|
||||
try
|
||||
|
|
|
@ -20,7 +20,6 @@ import java.net.InetSocketAddress;
|
|||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.spdy.api.GoAwayInfo;
|
||||
import org.eclipse.jetty.spdy.api.ReplyInfo;
|
||||
import org.eclipse.jetty.spdy.api.SPDY;
|
||||
|
@ -39,7 +38,6 @@ public class IdleTimeoutTest extends AbstractTest
|
|||
@Test
|
||||
public void testServerEnforcingIdleTimeout() throws Exception
|
||||
{
|
||||
server = new Server();
|
||||
connector = newSPDYServerConnector(new ServerSessionFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
|
@ -49,13 +47,11 @@ public class IdleTimeoutTest extends AbstractTest
|
|||
return null;
|
||||
}
|
||||
});
|
||||
server.addConnector(connector);
|
||||
int maxIdleTime = 1000;
|
||||
connector.setMaxIdleTime(maxIdleTime);
|
||||
server.start();
|
||||
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
Session session = startClient(new InetSocketAddress("localhost", connector.getLocalPort()), new SessionFrameListener.Adapter()
|
||||
Session session = startClient(startServer(null), new SessionFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onGoAway(Session session, GoAwayInfo goAwayInfo)
|
||||
|
@ -72,15 +68,12 @@ public class IdleTimeoutTest extends AbstractTest
|
|||
@Test
|
||||
public void testServerEnforcingIdleTimeoutWithUnrespondedStream() throws Exception
|
||||
{
|
||||
server = new Server();
|
||||
connector = newSPDYServerConnector(null);
|
||||
server.addConnector(connector);
|
||||
int maxIdleTime = 1000;
|
||||
connector.setMaxIdleTime(maxIdleTime);
|
||||
server.start();
|
||||
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
Session session = startClient(new InetSocketAddress("localhost", connector.getLocalPort()), new SessionFrameListener.Adapter()
|
||||
Session session = startClient(startServer(null), new SessionFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onGoAway(Session session, GoAwayInfo goAwayInfo)
|
||||
|
@ -99,7 +92,6 @@ public class IdleTimeoutTest extends AbstractTest
|
|||
public void testServerNotEnforcingIdleTimeoutWithPendingStream() throws Exception
|
||||
{
|
||||
final int maxIdleTime = 1000;
|
||||
server = new Server();
|
||||
connector = newSPDYServerConnector(new ServerSessionFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
|
@ -118,12 +110,10 @@ public class IdleTimeoutTest extends AbstractTest
|
|||
}
|
||||
}
|
||||
});
|
||||
server.addConnector(connector);
|
||||
connector.setMaxIdleTime(maxIdleTime);
|
||||
server.start();
|
||||
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
Session session = startClient(new InetSocketAddress("localhost", connector.getLocalPort()), new SessionFrameListener.Adapter()
|
||||
Session session = startClient(startServer(null), new SessionFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onGoAway(Session session, GoAwayInfo goAwayInfo)
|
||||
|
|
|
@ -16,26 +16,47 @@
|
|||
|
||||
package org.eclipse.jetty.spdy;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.Exchanger;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.eclipse.jetty.spdy.api.BytesDataInfo;
|
||||
import org.eclipse.jetty.spdy.api.DataInfo;
|
||||
import org.eclipse.jetty.spdy.api.GoAwayInfo;
|
||||
import org.eclipse.jetty.spdy.api.Handler;
|
||||
import org.eclipse.jetty.spdy.api.Headers;
|
||||
import org.eclipse.jetty.spdy.api.ReplyInfo;
|
||||
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.SessionStatus;
|
||||
import org.eclipse.jetty.spdy.api.Stream;
|
||||
import org.eclipse.jetty.spdy.api.StreamFrameListener;
|
||||
import org.eclipse.jetty.spdy.api.StreamStatus;
|
||||
import org.eclipse.jetty.spdy.api.StringDataInfo;
|
||||
import org.eclipse.jetty.spdy.api.SynInfo;
|
||||
import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
|
||||
import org.eclipse.jetty.spdy.frames.ControlFrame;
|
||||
import org.eclipse.jetty.spdy.frames.DataFrame;
|
||||
import org.eclipse.jetty.spdy.frames.GoAwayFrame;
|
||||
import org.eclipse.jetty.spdy.frames.RstStreamFrame;
|
||||
import org.eclipse.jetty.spdy.frames.SynStreamFrame;
|
||||
import org.eclipse.jetty.spdy.frames.WindowUpdateFrame;
|
||||
import org.eclipse.jetty.spdy.generator.Generator;
|
||||
import org.eclipse.jetty.spdy.parser.Parser;
|
||||
import org.eclipse.jetty.spdy.parser.Parser.Listener;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
@ -66,10 +87,10 @@ public class PushStreamTest extends AbstractTest
|
|||
@Override
|
||||
public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
|
||||
{
|
||||
assertThat("streamId is even", stream.getId() % 2, is(0));
|
||||
assertThat("stream is unidirectional", stream.isUnidirectional(), is(true));
|
||||
assertThat("stream is closed", stream.isClosed(), is(true));
|
||||
assertThat("stream has associated stream", stream.getAssociatedStream(), notNullValue());
|
||||
assertThat("streamId is even",stream.getId() % 2,is(0));
|
||||
assertThat("stream is unidirectional",stream.isUnidirectional(),is(true));
|
||||
assertThat("stream is closed",stream.isClosed(),is(true));
|
||||
assertThat("stream has associated stream",stream.getAssociatedStream(),notNullValue());
|
||||
try
|
||||
{
|
||||
stream.reply(new ReplyInfo(false));
|
||||
|
@ -85,10 +106,10 @@ public class PushStreamTest extends AbstractTest
|
|||
}
|
||||
});
|
||||
|
||||
Stream stream = clientSession.syn(new SynInfo(true), null).get();
|
||||
assertThat("onSyn has been called", pushStreamLatch.await(5, TimeUnit.SECONDS), is(true));
|
||||
Stream stream = clientSession.syn(new SynInfo(true),null).get();
|
||||
assertThat("onSyn has been called",pushStreamLatch.await(5,TimeUnit.SECONDS),is(true));
|
||||
Stream pushStream = pushStreamRef.get();
|
||||
assertThat("main stream and associated stream are the same", stream, sameInstance(pushStream.getAssociatedStream()));
|
||||
assertThat("main stream and associated stream are the same",stream,sameInstance(pushStream.getAssociatedStream()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -221,7 +242,7 @@ public class PushStreamTest extends AbstractTest
|
|||
stream.syn(new SynInfo(false),1,TimeUnit.SECONDS,new Handler.Adapter<Stream>()
|
||||
{
|
||||
@Override
|
||||
public void failed(Throwable x)
|
||||
public void failed(Stream stream, Throwable x)
|
||||
{
|
||||
pushStreamFailedLatch.countDown();
|
||||
}
|
||||
|
@ -321,6 +342,170 @@ public class PushStreamTest extends AbstractTest
|
|||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testClientResetsStreamAfterPushSynDoesPreventSendingDataFramesWithFlowControl() throws Exception
|
||||
{
|
||||
final boolean flowControl = true;
|
||||
testNoMoreFramesAreSentOnPushStreamAfterClientResetsThePushStream(flowControl);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClientResetsStreamAfterPushSynDoesPreventSendingDataFramesWithoutFlowControl() throws Exception
|
||||
{
|
||||
final boolean flowControl = false;
|
||||
testNoMoreFramesAreSentOnPushStreamAfterClientResetsThePushStream(flowControl);
|
||||
}
|
||||
|
||||
private volatile boolean read = true;
|
||||
private void testNoMoreFramesAreSentOnPushStreamAfterClientResetsThePushStream(final boolean flowControl) throws Exception, IOException, InterruptedException
|
||||
{
|
||||
final short version = SPDY.V3;
|
||||
final AtomicBoolean unexpectedExceptionOccured = new AtomicBoolean(false);
|
||||
final CountDownLatch resetReceivedLatch = new CountDownLatch(1);
|
||||
final CountDownLatch allDataFramesReceivedLatch = new CountDownLatch(1);
|
||||
final CountDownLatch goAwayReceivedLatch = new CountDownLatch(1);
|
||||
final int dataSizeInBytes = 1024 * 256;
|
||||
final byte[] transferBytes = createHugeByteArray(dataSizeInBytes);
|
||||
|
||||
InetSocketAddress serverAddress = startServer(new ServerSessionFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public StreamFrameListener onSyn(final Stream stream, SynInfo synInfo)
|
||||
{
|
||||
new Thread(new Runnable()
|
||||
{
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
Stream pushStream=null;
|
||||
try
|
||||
{
|
||||
stream.reply(new ReplyInfo(false));
|
||||
pushStream = stream.syn(new SynInfo(false)).get();
|
||||
resetReceivedLatch.await(5,TimeUnit.SECONDS);
|
||||
}
|
||||
catch (InterruptedException | ExecutionException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
unexpectedExceptionOccured.set(true);
|
||||
}
|
||||
pushStream.data(new BytesDataInfo(transferBytes,true));
|
||||
stream.data(new StringDataInfo("close",true));
|
||||
}
|
||||
}).start();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRst(Session session, RstInfo rstInfo)
|
||||
{
|
||||
resetReceivedLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGoAway(Session session, GoAwayInfo goAwayInfo)
|
||||
{
|
||||
goAwayReceivedLatch.countDown();
|
||||
}
|
||||
}/*TODO, flowControl*/);
|
||||
|
||||
final SocketChannel channel = SocketChannel.open(serverAddress);
|
||||
final Generator generator = new Generator(new StandardByteBufferPool(),new StandardCompressionFactory.StandardCompressor());
|
||||
int streamId = 1;
|
||||
ByteBuffer writeBuffer = generator.control(new SynStreamFrame(version,(byte)0,streamId,0,(byte)0,(short)0,new Headers()));
|
||||
channel.write(writeBuffer);
|
||||
assertThat("writeBuffer is fully written",writeBuffer.hasRemaining(), is(false));
|
||||
|
||||
final Parser parser = new Parser(new StandardCompressionFactory.StandardDecompressor());
|
||||
parser.addListener(new Listener.Adapter()
|
||||
{
|
||||
int bytesRead = 0;
|
||||
|
||||
@Override
|
||||
public void onControlFrame(ControlFrame frame)
|
||||
{
|
||||
if(frame instanceof SynStreamFrame){
|
||||
int pushStreamId = ((SynStreamFrame)frame).getStreamId();
|
||||
ByteBuffer writeBuffer = generator.control(new RstStreamFrame(version,pushStreamId,StreamStatus.CANCEL_STREAM.getCode(version)));
|
||||
try
|
||||
{
|
||||
channel.write(writeBuffer);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
unexpectedExceptionOccured.set(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataFrame(DataFrame frame, ByteBuffer data)
|
||||
{
|
||||
if(frame.getStreamId() == 2)
|
||||
bytesRead = bytesRead + frame.getLength();
|
||||
if(bytesRead == dataSizeInBytes){
|
||||
allDataFramesReceivedLatch.countDown();
|
||||
return;
|
||||
}
|
||||
if (flowControl)
|
||||
{
|
||||
ByteBuffer writeBuffer = generator.control(new WindowUpdateFrame(version,frame.getStreamId(),frame.getLength()));
|
||||
try
|
||||
{
|
||||
channel.write(writeBuffer);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
unexpectedExceptionOccured.set(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Thread reader = new Thread(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
ByteBuffer readBuffer = ByteBuffer.allocate(dataSizeInBytes*2);
|
||||
while (read)
|
||||
{
|
||||
try
|
||||
{
|
||||
channel.read(readBuffer);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
unexpectedExceptionOccured.set(true);
|
||||
}
|
||||
readBuffer.flip();
|
||||
parser.parse(readBuffer);
|
||||
readBuffer.clear();
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
reader.start();
|
||||
read = false;
|
||||
|
||||
assertThat("no unexpected exceptions occured", unexpectedExceptionOccured.get(), is(false));
|
||||
assertThat("not all dataframes have been received as the pushstream has been reset by the client.",allDataFramesReceivedLatch.await(streamId,TimeUnit.SECONDS),is(false));
|
||||
|
||||
|
||||
ByteBuffer buffer = generator.control(new GoAwayFrame(version, streamId, SessionStatus.OK.getCode()));
|
||||
channel.write(buffer);
|
||||
Assert.assertThat(buffer.hasRemaining(), is(false));
|
||||
|
||||
assertThat("GoAway frame is received by server", goAwayReceivedLatch.await(5,TimeUnit.SECONDS), is(true));
|
||||
channel.shutdownOutput();
|
||||
channel.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOddEvenStreamIds() throws Exception
|
||||
{
|
||||
|
@ -367,6 +552,6 @@ public class PushStreamTest extends AbstractTest
|
|||
|
||||
private void assertThatNoExceptionOccured(final CountDownLatch exceptionCountDownLatch) throws InterruptedException
|
||||
{
|
||||
assertThat("No exception occured", exceptionCountDownLatch.await(1,TimeUnit.SECONDS),is(false));
|
||||
assertThat("No exception occured",exceptionCountDownLatch.await(1,TimeUnit.SECONDS),is(false));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
package org.eclipse.jetty.spdy;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
@ -23,12 +17,18 @@ import org.eclipse.jetty.spdy.api.SynInfo;
|
|||
import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ResetStreamTest extends AbstractTest
|
||||
{
|
||||
@Test
|
||||
public void testResetStreamIsRemoved() throws Exception
|
||||
{
|
||||
Session session = startClient(startServer(new ServerSessionFrameListener.Adapter()),null);
|
||||
Session session = startClient(startServer(new ServerSessionFrameListener.Adapter()/*TODO, true*/),null);
|
||||
|
||||
Stream stream = session.syn(new SynInfo(false),null).get(5,TimeUnit.SECONDS);
|
||||
session.rst(new RstInfo(stream.getId(),StreamStatus.CANCEL_STREAM)).get(5,TimeUnit.SECONDS);
|
||||
|
@ -169,7 +169,7 @@ public class ResetStreamTest extends AbstractTest
|
|||
stream.data(new StringDataInfo("2nd dataframe",false),5L,TimeUnit.SECONDS,new Handler.Adapter<Void>()
|
||||
{
|
||||
@Override
|
||||
public void failed(Throwable x)
|
||||
public void failed(Void context, Throwable x)
|
||||
{
|
||||
failLatch.countDown();
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ public class UnsupportedVersionTest extends AbstractTest
|
|||
}
|
||||
});
|
||||
|
||||
SynStreamFrame frame = new SynStreamFrame(SPDY.V2, SynInfo.FLAG_CLOSE, 1, 0, (byte)0, new Headers());
|
||||
SynStreamFrame frame = new SynStreamFrame(SPDY.V2, SynInfo.FLAG_CLOSE, 1, 0, (byte)0, (short)0, new Headers());
|
||||
Generator generator = new Generator(new StandardByteBufferPool(), new StandardCompressionFactory.StandardCompressor());
|
||||
ByteBuffer buffer = generator.control(frame);
|
||||
// Replace the version byte with an unsupported version
|
||||
|
|
|
@ -31,6 +31,7 @@ import java.lang.reflect.Method;
|
|||
import java.net.ConnectException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
@ -53,6 +54,9 @@ import java.util.Set;
|
|||
*/
|
||||
public class Main
|
||||
{
|
||||
private static final String START_LOG_FILENAME = "start.log";
|
||||
private static final SimpleDateFormat START_LOG_ROLLOVER_DATEFORMAT = new SimpleDateFormat("yyyy_MM_dd-HHmmSSSSS.'" + START_LOG_FILENAME + "'");
|
||||
|
||||
private static final int EXIT_USAGE = 1;
|
||||
private static final int ERR_LOGGING = -1;
|
||||
private static final int ERR_INVOKE_MAIN = -2;
|
||||
|
@ -71,7 +75,7 @@ public class Main
|
|||
|
||||
private String _jettyHome;
|
||||
|
||||
public static void main(String[] args)
|
||||
public static void main(String[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -107,7 +111,6 @@ public class Main
|
|||
if (arg.length() > 6)
|
||||
{
|
||||
arguments.addAll(loadStartIni(new File(arg.substring(6))));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (arg.startsWith("--config="))
|
||||
|
@ -152,7 +155,7 @@ public class Main
|
|||
}
|
||||
return ini_args;
|
||||
}
|
||||
|
||||
|
||||
public List<String> processCommandLine(List<String> arguments) throws Exception
|
||||
{
|
||||
// The XML Configuration Files to initialize with
|
||||
|
@ -212,7 +215,9 @@ public class Main
|
|||
File startDir = new File(System.getProperty("jetty.logs","logs"));
|
||||
if (!startDir.exists() || !startDir.canWrite())
|
||||
startDir = new File(".");
|
||||
File startLog = new File(startDir,"start.log");
|
||||
|
||||
File startLog = new File(startDir, START_LOG_ROLLOVER_DATEFORMAT.format(new Date()));
|
||||
|
||||
if (!startLog.exists() && !startLog.createNewFile())
|
||||
{
|
||||
// Output about error is lost in majority of cases.
|
||||
|
@ -231,7 +236,7 @@ public class Main
|
|||
PrintStream logger = new PrintStream(new FileOutputStream(startLog,false));
|
||||
System.setOut(logger);
|
||||
System.setErr(logger);
|
||||
System.out.println("Establishing start.log on " + new Date());
|
||||
System.out.println("Establishing "+ START_LOG_FILENAME + " on " + new Date());
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -475,7 +480,7 @@ public class Main
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void start(List<String> xmls) throws FileNotFoundException, IOException, InterruptedException
|
||||
public void start(List<String> xmls) throws IOException, InterruptedException
|
||||
{
|
||||
// Setup Start / Stop Monitoring
|
||||
int port = Integer.parseInt(Config.getProperty("STOP.PORT","-1"));
|
||||
|
@ -528,7 +533,7 @@ public class Main
|
|||
// Show all options with version information
|
||||
if (_listOptions)
|
||||
{
|
||||
showAllOptionsWithVersions(classpath);
|
||||
showAllOptionsWithVersions();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -550,7 +555,7 @@ public class Main
|
|||
if (_exec)
|
||||
{
|
||||
CommandLineBuilder cmd = buildCommandLine(classpath,configuredXmls);
|
||||
|
||||
|
||||
ProcessBuilder pbuilder = new ProcessBuilder(cmd.getArgs());
|
||||
final Process process = pbuilder.start();
|
||||
Runtime.getRuntime().addShutdownHook(new Thread()
|
||||
|
@ -562,13 +567,13 @@ public class Main
|
|||
process.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
copyInThread(process.getErrorStream(),System.err);
|
||||
copyInThread(process.getInputStream(),System.out);
|
||||
copyInThread(System.in,process.getOutputStream());
|
||||
monitor.setProcess(process);
|
||||
process.waitFor();
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -734,7 +739,7 @@ public class Main
|
|||
return exe;
|
||||
}
|
||||
|
||||
private void showAllOptionsWithVersions(Classpath classpath)
|
||||
private void showAllOptionsWithVersions()
|
||||
{
|
||||
Set<String> sectionIds = _config.getSectionIds();
|
||||
|
||||
|
@ -1046,19 +1051,15 @@ public class Main
|
|||
System.err.println(" java -jar start.jar --help # for more information");
|
||||
System.exit(exit);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a start.ini format file into an argument list.
|
||||
*/
|
||||
static List<String> loadStartIni(File ini)
|
||||
{
|
||||
File startIniFile = ini;
|
||||
if (!startIniFile.exists())
|
||||
if (!ini.exists())
|
||||
{
|
||||
if (ini != null)
|
||||
{
|
||||
System.err.println("Warning - can't find ini file: " + ini);
|
||||
}
|
||||
System.err.println("Warning - can't find ini file: " + ini);
|
||||
// No start.ini found, skip load.
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
|
|
@ -99,7 +99,10 @@ public class Monitor extends Thread
|
|||
new LineNumberReader(new InputStreamReader(socket.getInputStream()));
|
||||
String key=lin.readLine();
|
||||
if (!_key.equals(key))
|
||||
{
|
||||
System.err.println("Ignoring command with incorrect key");
|
||||
continue;
|
||||
}
|
||||
|
||||
String cmd=lin.readLine();
|
||||
Config.debug("command=" + cmd);
|
||||
|
|
|
@ -121,8 +121,11 @@ public class ShutdownThread extends Thread
|
|||
{
|
||||
try
|
||||
{
|
||||
lifeCycle.stop();
|
||||
LOG.debug("Stopped {}",lifeCycle);
|
||||
if (lifeCycle.isStarted())
|
||||
{
|
||||
lifeCycle.stop();
|
||||
LOG.debug("Stopped {}",lifeCycle);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
|
@ -76,7 +76,9 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
public final static String SERVER_CONFIG = "org.eclipse.jetty.webapp.configuration";
|
||||
public final static String SERVER_SYS_CLASSES = "org.eclipse.jetty.webapp.systemClasses";
|
||||
public final static String SERVER_SRV_CLASSES = "org.eclipse.jetty.webapp.serverClasses";
|
||||
|
||||
|
||||
private String[] __dftProtectedTargets = {"/web-inf", "/meta-inf"};
|
||||
|
||||
private static String[] __dftConfigurationClasses =
|
||||
{
|
||||
"org.eclipse.jetty.webapp.WebInfConfiguration",
|
||||
|
@ -151,6 +153,8 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
private boolean _configurationsSet=false;
|
||||
private boolean _allowDuplicateFragmentNames = false;
|
||||
private boolean _throwUnavailableOnStartupException = false;
|
||||
|
||||
|
||||
|
||||
private MetaData _metadata=new MetaData();
|
||||
|
||||
|
@ -172,6 +176,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
super(SESSIONS|SECURITY);
|
||||
_scontext=new Context();
|
||||
setErrorHandler(new ErrorPageErrorHandler());
|
||||
setProtectedTargets(__dftProtectedTargets);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -186,6 +191,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
setContextPath(contextPath);
|
||||
setWar(webApp);
|
||||
setErrorHandler(new ErrorPageErrorHandler());
|
||||
setProtectedTargets(__dftProtectedTargets);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -200,6 +206,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
_scontext=new Context();
|
||||
setWar(webApp);
|
||||
setErrorHandler(new ErrorPageErrorHandler());
|
||||
setProtectedTargets(__dftProtectedTargets);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -216,6 +223,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
super(null, sessionHandler, securityHandler, servletHandler, errorHandler);
|
||||
_scontext = new Context();
|
||||
setErrorHandler(errorHandler != null ? errorHandler : new ErrorPageErrorHandler());
|
||||
setProtectedTargets(__dftProtectedTargets);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -833,16 +841,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
protected boolean isProtectedTarget(String target)
|
||||
{
|
||||
while (target.startsWith("//"))
|
||||
target=URIUtil.compactPath(target);
|
||||
|
||||
return StringUtil.startsWithIgnoreCase(target, "/web-inf") || StringUtil.startsWithIgnoreCase(target, "/meta-inf");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
// ========================================================================
|
||||
package org.eclipse.jetty.webapp;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
@ -168,6 +169,16 @@ public class WebAppContextTest
|
|||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testIsProtected() throws Exception
|
||||
{
|
||||
WebAppContext context = new WebAppContext();
|
||||
assertTrue(context.isProtectedTarget("/web-inf/lib/foo.jar"));
|
||||
assertTrue(context.isProtectedTarget("/meta-inf/readme.txt"));
|
||||
assertFalse(context.isProtectedTarget("/something-else/web-inf"));
|
||||
}
|
||||
|
||||
class ServletA extends GenericServlet
|
||||
{
|
||||
@Override
|
||||
|
|
|
@ -16,6 +16,7 @@ package org.eclipse.jetty.xml;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.StringReader;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
@ -25,20 +26,21 @@ import java.net.InetAddress;
|
|||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.UnknownHostException;
|
||||
import java.security.AccessControlException;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.eclipse.jetty.util.ArrayQueue;
|
||||
import org.eclipse.jetty.util.LazyList;
|
||||
import org.eclipse.jetty.util.Loader;
|
||||
import org.eclipse.jetty.util.TypeUtil;
|
||||
|
@ -46,6 +48,7 @@ import org.eclipse.jetty.util.component.LifeCycle;
|
|||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.xml.XmlParser.Node;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
|
@ -69,8 +72,14 @@ public class XmlConfiguration
|
|||
private static final Class<?>[] __primitiveHolders =
|
||||
{ Boolean.class, Character.class, Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class, Void.class };
|
||||
private static final Integer ZERO = new Integer(0);
|
||||
|
||||
|
||||
private static final Class<?>[] __supportedCollections =
|
||||
{ ArrayList.class,ArrayQueue.class,HashSet.class,Queue.class,List.class,Set.class,Collection.class,};
|
||||
|
||||
private static final Iterable<?> __factoryLoader;
|
||||
|
||||
private static final XmlParser __parser = initParser();
|
||||
|
||||
static
|
||||
{
|
||||
Iterable<?> loader=null;
|
||||
|
@ -93,46 +102,41 @@ public class XmlConfiguration
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
private static XmlParser __parser;
|
||||
private URL _url;
|
||||
private XmlParser.Node _config;
|
||||
private String _dtd;
|
||||
private ConfigurationProcessor _processor;
|
||||
private final Map<String, Object> _idMap = new HashMap<String, Object>();
|
||||
private final Map<String, String> _propertyMap = new HashMap<String, String>();
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
private synchronized static void initParser() throws IOException
|
||||
private synchronized static XmlParser initParser()
|
||||
{
|
||||
if (__parser != null)
|
||||
return;
|
||||
|
||||
__parser = new XmlParser();
|
||||
XmlParser parser = new XmlParser();
|
||||
try
|
||||
{
|
||||
URL config60 = Loader.getResource(XmlConfiguration.class,"org/eclipse/jetty/xml/configure_6_0.dtd",true);
|
||||
URL config76 = Loader.getResource(XmlConfiguration.class,"org/eclipse/jetty/xml/configure_7_6.dtd",true);
|
||||
__parser.redirectEntity("configure.dtd",config76);
|
||||
__parser.redirectEntity("configure_1_0.dtd",config60);
|
||||
__parser.redirectEntity("configure_1_1.dtd",config60);
|
||||
__parser.redirectEntity("configure_1_2.dtd",config60);
|
||||
__parser.redirectEntity("configure_1_3.dtd",config60);
|
||||
__parser.redirectEntity("configure_6_0.dtd",config60);
|
||||
__parser.redirectEntity("configure_7_6.dtd",config76);
|
||||
parser.redirectEntity("configure.dtd",config76);
|
||||
parser.redirectEntity("configure_1_0.dtd",config60);
|
||||
parser.redirectEntity("configure_1_1.dtd",config60);
|
||||
parser.redirectEntity("configure_1_2.dtd",config60);
|
||||
parser.redirectEntity("configure_1_3.dtd",config60);
|
||||
parser.redirectEntity("configure_6_0.dtd",config60);
|
||||
parser.redirectEntity("configure_7_6.dtd",config76);
|
||||
|
||||
|
||||
__parser.redirectEntity("http://jetty.mortbay.org/configure.dtd",config76);
|
||||
__parser.redirectEntity("http://jetty.eclipse.org/configure.dtd",config76);
|
||||
__parser.redirectEntity("http://www.eclipse.org/jetty/configure.dtd",config76);
|
||||
parser.redirectEntity("http://jetty.mortbay.org/configure.dtd",config76);
|
||||
parser.redirectEntity("http://jetty.eclipse.org/configure.dtd",config76);
|
||||
parser.redirectEntity("http://www.eclipse.org/jetty/configure.dtd",config76);
|
||||
|
||||
__parser.redirectEntity("-//Mort Bay Consulting//DTD Configure//EN",config76);
|
||||
__parser.redirectEntity("-//Jetty//Configure//EN",config76);
|
||||
parser.redirectEntity("-//Mort Bay Consulting//DTD Configure//EN",config76);
|
||||
parser.redirectEntity("-//Jetty//Configure//EN",config76);
|
||||
}
|
||||
catch (ClassNotFoundException e)
|
||||
{
|
||||
LOG.warn(e.toString());
|
||||
LOG.debug(e);
|
||||
}
|
||||
return parser;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -143,7 +147,6 @@ public class XmlConfiguration
|
|||
*/
|
||||
public XmlConfiguration(URL configuration) throws SAXException, IOException
|
||||
{
|
||||
initParser();
|
||||
synchronized (__parser)
|
||||
{
|
||||
_url=configuration;
|
||||
|
@ -163,7 +166,6 @@ public class XmlConfiguration
|
|||
*/
|
||||
public XmlConfiguration(String configuration) throws SAXException, IOException
|
||||
{
|
||||
initParser();
|
||||
configuration = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<!DOCTYPE Configure PUBLIC \"-//Mort Bay Consulting//DTD Configure 1.2//EN\" \"http://jetty.eclipse.org/configure_1_2.dtd\">"
|
||||
+ configuration;
|
||||
InputSource source = new InputSource(new StringReader(configuration));
|
||||
|
@ -185,7 +187,6 @@ public class XmlConfiguration
|
|||
*/
|
||||
public XmlConfiguration(InputStream configuration) throws SAXException, IOException
|
||||
{
|
||||
initParser();
|
||||
InputSource source = new InputSource(configuration);
|
||||
synchronized (__parser)
|
||||
{
|
||||
|
@ -197,7 +198,6 @@ public class XmlConfiguration
|
|||
/* ------------------------------------------------------------ */
|
||||
private void setConfig(XmlParser.Node config)
|
||||
{
|
||||
_config=config;
|
||||
if ("Configure".equals(config.getTag()))
|
||||
{
|
||||
_processor=new JettyXmlConfiguration();
|
||||
|
@ -228,7 +228,7 @@ public class XmlConfiguration
|
|||
{
|
||||
throw new IllegalArgumentException("Unknown XML tag:"+config.getTag());
|
||||
}
|
||||
_processor.init(_url,_config,_idMap, _propertyMap);
|
||||
_processor.init(_url,config,_idMap, _propertyMap);
|
||||
}
|
||||
|
||||
|
||||
|
@ -242,6 +242,7 @@ public class XmlConfiguration
|
|||
/**
|
||||
* @deprecated use {@link #getIdMap()}.put(...)
|
||||
*/
|
||||
@Deprecated
|
||||
public void setIdMap(Map<String, Object> map)
|
||||
{
|
||||
_idMap.clear();
|
||||
|
@ -252,6 +253,7 @@ public class XmlConfiguration
|
|||
/**
|
||||
* @deprecated use {@link #getProperties()}.putAll(...)
|
||||
*/
|
||||
@Deprecated
|
||||
public void setProperties(Map<String, String> map)
|
||||
{
|
||||
_propertyMap.clear();
|
||||
|
@ -311,7 +313,7 @@ public class XmlConfiguration
|
|||
public Object configure(Object obj) throws Exception
|
||||
{
|
||||
// Check the class of the object
|
||||
Class<?> oClass = (Class<?>)nodeClass(_config);
|
||||
Class<?> oClass = nodeClass(_config);
|
||||
if (oClass != null && !oClass.isInstance(obj))
|
||||
{
|
||||
String loaders = (oClass.getClassLoader()==obj.getClass().getClassLoader())?"":"Object Class and type Class are from different loaders.";
|
||||
|
@ -324,7 +326,7 @@ public class XmlConfiguration
|
|||
/* ------------------------------------------------------------ */
|
||||
public Object configure() throws Exception
|
||||
{
|
||||
Class<?> oClass = (Class<?>)nodeClass(_config);
|
||||
Class<?> oClass = nodeClass(_config);
|
||||
|
||||
String id = _config.getAttribute("id");
|
||||
Object obj = id == null?null:_idMap.get(id);
|
||||
|
@ -340,7 +342,7 @@ public class XmlConfiguration
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
private Class<?> nodeClass(XmlParser.Node node) throws ClassNotFoundException
|
||||
private static Class<?> nodeClass(XmlParser.Node node) throws ClassNotFoundException
|
||||
{
|
||||
String className = node.getAttribute("class");
|
||||
if (className == null)
|
||||
|
@ -389,7 +391,7 @@ public class XmlConfiguration
|
|||
else if ("Ref".equals(tag))
|
||||
refObj(obj,node);
|
||||
else if ("Property".equals(tag))
|
||||
propertyObj(obj,node);
|
||||
propertyObj(node);
|
||||
else
|
||||
throw new IllegalStateException("Unknown tag: " + tag);
|
||||
}
|
||||
|
@ -417,13 +419,13 @@ public class XmlConfiguration
|
|||
Object[] arg =
|
||||
{ value };
|
||||
|
||||
Class oClass = nodeClass(node);
|
||||
Class<?> oClass = nodeClass(node);
|
||||
if (oClass != null)
|
||||
obj = null;
|
||||
else
|
||||
oClass = obj.getClass();
|
||||
|
||||
Class[] vClass =
|
||||
Class<?>[] vClass =
|
||||
{ Object.class };
|
||||
if (value != null)
|
||||
vClass[0] = value.getClass();
|
||||
|
@ -455,7 +457,7 @@ public class XmlConfiguration
|
|||
try
|
||||
{
|
||||
Field type = vClass[0].getField("TYPE");
|
||||
vClass[0] = (Class)type.get(null);
|
||||
vClass[0] = (Class<?>)type.get(null);
|
||||
Method set = oClass.getMethod(name,vClass);
|
||||
set.invoke(obj,arg);
|
||||
return;
|
||||
|
@ -497,7 +499,6 @@ public class XmlConfiguration
|
|||
Method set = null;
|
||||
for (int s = 0; sets != null && s < sets.length; s++)
|
||||
{
|
||||
|
||||
Class<?>[] paramTypes = sets[s].getParameterTypes();
|
||||
if (name.equals(sets[s].getName()) && paramTypes.length == 1)
|
||||
{
|
||||
|
@ -518,27 +519,18 @@ public class XmlConfiguration
|
|||
LOG.ignore(e);
|
||||
}
|
||||
|
||||
// Can we convert to a collection
|
||||
if (paramTypes[0].isAssignableFrom(Collection.class) && value.getClass().isArray())
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
if (paramTypes[0].isAssignableFrom(Set.class))
|
||||
sets[s].invoke(obj,new Object[]
|
||||
{ new HashSet<Object>(Arrays.asList((Object[])value)) });
|
||||
else
|
||||
sets[s].invoke(obj,new Object[]
|
||||
{ Arrays.asList((Object[])value) });
|
||||
return;
|
||||
}
|
||||
catch (IllegalArgumentException e)
|
||||
{
|
||||
LOG.ignore(e);
|
||||
}
|
||||
catch (IllegalAccessException e)
|
||||
{
|
||||
LOG.ignore(e);
|
||||
}
|
||||
for (Class<?> c : __supportedCollections)
|
||||
if (paramTypes[0].isAssignableFrom(c))
|
||||
{
|
||||
sets[s].invoke(obj,convertArrayToCollection(value,c));
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (IllegalAccessException e)
|
||||
{
|
||||
LOG.ignore(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -548,7 +540,7 @@ public class XmlConfiguration
|
|||
{
|
||||
try
|
||||
{
|
||||
Class sClass = set.getParameterTypes()[0];
|
||||
Class<?> sClass = set.getParameterTypes()[0];
|
||||
if (sClass.isPrimitive())
|
||||
{
|
||||
for (int t = 0; t < __primitives.length; t++)
|
||||
|
@ -560,7 +552,7 @@ public class XmlConfiguration
|
|||
}
|
||||
}
|
||||
}
|
||||
Constructor cons = sClass.getConstructor(vClass);
|
||||
Constructor<?> cons = sClass.getConstructor(vClass);
|
||||
arg[0] = cons.newInstance(arg);
|
||||
set.invoke(obj,arg);
|
||||
return;
|
||||
|
@ -583,6 +575,40 @@ public class XmlConfiguration
|
|||
throw new NoSuchMethodException(oClass + "." + name + "(" + vClass[0] + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a collection if compareValueToClass is a Set or List. null if that's not the case or value can't be converted to a Collection
|
||||
*/
|
||||
private static Collection<?> convertArrayToCollection(Object array, Class<?> collectionType)
|
||||
{
|
||||
Collection<?> collection = null;
|
||||
if (array.getClass().isArray())
|
||||
{
|
||||
if (collectionType.isAssignableFrom(ArrayList.class))
|
||||
collection = convertArrayToArrayList(array);
|
||||
else if (collectionType.isAssignableFrom(HashSet.class))
|
||||
collection = new HashSet<Object>(convertArrayToArrayList(array));
|
||||
else if (collectionType.isAssignableFrom(ArrayQueue.class))
|
||||
{
|
||||
ArrayQueue<Object> q= new ArrayQueue<Object>();
|
||||
q.addAll(convertArrayToArrayList(array));
|
||||
collection=q;
|
||||
}
|
||||
}
|
||||
if (collection==null)
|
||||
throw new IllegalArgumentException("Can't convert \"" + array.getClass() + "\" to " + collectionType);
|
||||
return collection;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
private static ArrayList<Object> convertArrayToArrayList(Object array)
|
||||
{
|
||||
int length = Array.getLength(array);
|
||||
ArrayList<Object> list = new ArrayList<Object>(length);
|
||||
for (int i = 0; i < length; i++)
|
||||
list.add(Array.get(array,i));
|
||||
return list;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/*
|
||||
* Call a put method.
|
||||
|
@ -593,6 +619,7 @@ public class XmlConfiguration
|
|||
{
|
||||
if (!(obj instanceof Map))
|
||||
throw new IllegalArgumentException("Object for put is not a Map: " + obj);
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<Object, Object> map = (Map<Object, Object>)obj;
|
||||
|
||||
String name = node.getAttribute("name");
|
||||
|
@ -610,7 +637,7 @@ public class XmlConfiguration
|
|||
*/
|
||||
private Object get(Object obj, XmlParser.Node node) throws Exception
|
||||
{
|
||||
Class oClass = nodeClass(node);
|
||||
Class<?> oClass = nodeClass(node);
|
||||
if (oClass != null)
|
||||
obj = null;
|
||||
else
|
||||
|
@ -657,7 +684,7 @@ public class XmlConfiguration
|
|||
private Object call(Object obj, XmlParser.Node node) throws Exception
|
||||
{
|
||||
String id = node.getAttribute("id");
|
||||
Class oClass = nodeClass(node);
|
||||
Class<?> oClass = nodeClass(node);
|
||||
if (oClass != null)
|
||||
obj = null;
|
||||
else if (obj != null)
|
||||
|
@ -718,7 +745,7 @@ public class XmlConfiguration
|
|||
*/
|
||||
private Object newObj(Object obj, XmlParser.Node node) throws Exception
|
||||
{
|
||||
Class oClass = nodeClass(node);
|
||||
Class<?> oClass = nodeClass(node);
|
||||
String id = node.getAttribute("id");
|
||||
int size = 0;
|
||||
int argi = node.size();
|
||||
|
@ -748,7 +775,7 @@ public class XmlConfiguration
|
|||
LOG.debug("XML new " + oClass);
|
||||
|
||||
// Lets just try all constructors for now
|
||||
Constructor[] constructors = oClass.getConstructors();
|
||||
Constructor<?>[] constructors = oClass.getConstructors();
|
||||
for (int c = 0; constructors != null && c < constructors.length; c++)
|
||||
{
|
||||
if (constructors[c].getParameterTypes().length != size)
|
||||
|
@ -809,7 +836,7 @@ public class XmlConfiguration
|
|||
{
|
||||
|
||||
// Get the type
|
||||
Class aClass = java.lang.Object.class;
|
||||
Class<?> aClass = java.lang.Object.class;
|
||||
String type = node.getAttribute("type");
|
||||
final String id = node.getAttribute("id");
|
||||
if (type != null)
|
||||
|
@ -830,10 +857,9 @@ public class XmlConfiguration
|
|||
|
||||
Object al = null;
|
||||
|
||||
Iterator iter = node.iterator("Item");
|
||||
while (iter.hasNext())
|
||||
for (Object nodeObject : node)
|
||||
{
|
||||
XmlParser.Node item = (XmlParser.Node)iter.next();
|
||||
XmlParser.Node item = (Node)nodeObject;
|
||||
String nid = item.getAttribute("id");
|
||||
Object v = value(obj,item);
|
||||
al = LazyList.add(al,(v == null && aClass.isPrimitive())?ZERO:v);
|
||||
|
@ -859,9 +885,8 @@ public class XmlConfiguration
|
|||
if (id != null)
|
||||
_idMap.put(id,map);
|
||||
|
||||
for (int i = 0; i < node.size(); i++)
|
||||
for (Object o : node)
|
||||
{
|
||||
Object o = node.get(i);
|
||||
if (o instanceof String)
|
||||
continue;
|
||||
XmlParser.Node entry = (XmlParser.Node)o;
|
||||
|
@ -871,12 +896,11 @@ public class XmlConfiguration
|
|||
XmlParser.Node key = null;
|
||||
XmlParser.Node value = null;
|
||||
|
||||
for (int j = 0; j < entry.size(); j++)
|
||||
for (Object object : node)
|
||||
{
|
||||
o = entry.get(j);
|
||||
if (o instanceof String)
|
||||
if (object instanceof String)
|
||||
continue;
|
||||
XmlParser.Node item = (XmlParser.Node)o;
|
||||
XmlParser.Node item = (XmlParser.Node)object;
|
||||
if (!item.getTag().equals("Item"))
|
||||
throw new IllegalStateException("Not an Item");
|
||||
if (key == null)
|
||||
|
@ -907,9 +931,11 @@ public class XmlConfiguration
|
|||
/*
|
||||
* Get a Property.
|
||||
*
|
||||
* @param obj @param node @return @exception Exception
|
||||
* @param node
|
||||
* @return
|
||||
* @exception Exception
|
||||
*/
|
||||
private Object propertyObj(Object obj, XmlParser.Node node) throws Exception
|
||||
private Object propertyObj(XmlParser.Node node) throws Exception
|
||||
{
|
||||
String id = node.getAttribute("id");
|
||||
String name = node.getAttribute("name");
|
||||
|
@ -1019,19 +1045,19 @@ public class XmlConfiguration
|
|||
// Try to type the object
|
||||
if (type == null)
|
||||
{
|
||||
if (value != null && value instanceof String)
|
||||
if (value instanceof String)
|
||||
return ((String)value).trim();
|
||||
return value;
|
||||
}
|
||||
|
||||
if ("String".equals(type) || "java.lang.String".equals(type))
|
||||
if (isTypeMatchingClass(type,String.class))
|
||||
return value.toString();
|
||||
|
||||
Class<?> pClass = TypeUtil.fromName(type);
|
||||
if (pClass != null)
|
||||
return TypeUtil.valueOf(pClass,value.toString());
|
||||
|
||||
if ("URL".equals(type) || "java.net.URL".equals(type))
|
||||
if (isTypeMatchingClass(type,URL.class))
|
||||
{
|
||||
if (value instanceof URL)
|
||||
return value;
|
||||
|
@ -1045,7 +1071,7 @@ public class XmlConfiguration
|
|||
}
|
||||
}
|
||||
|
||||
if ("InetAddress".equals(type) || "java.net.InetAddress".equals(type))
|
||||
if (isTypeMatchingClass(type,InetAddress.class))
|
||||
{
|
||||
if (value instanceof InetAddress)
|
||||
return value;
|
||||
|
@ -1058,9 +1084,22 @@ public class XmlConfiguration
|
|||
throw new InvocationTargetException(e);
|
||||
}
|
||||
}
|
||||
|
||||
for (Class<?> collectionClass : __supportedCollections)
|
||||
{
|
||||
if (isTypeMatchingClass(type,collectionClass))
|
||||
return convertArrayToCollection(value,collectionClass);
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Unknown type " + type);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
private static boolean isTypeMatchingClass(String type, Class<?> classToMatch)
|
||||
{
|
||||
boolean match = classToMatch.getSimpleName().equalsIgnoreCase(type) || classToMatch.getName().equals(type);
|
||||
return match;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/*
|
||||
|
@ -1087,7 +1126,7 @@ public class XmlConfiguration
|
|||
if ("Map".equals(tag))
|
||||
return newMap(obj,node);
|
||||
if ("Property".equals(tag))
|
||||
return propertyObj(obj,node);
|
||||
return propertyObj(node);
|
||||
|
||||
if ("SystemProperty".equals(tag))
|
||||
{
|
||||
|
@ -1129,7 +1168,6 @@ public class XmlConfiguration
|
|||
* @param args
|
||||
* array of property and xml configuration filenames or {@link Resource}s.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void main(final String[] args) throws Exception
|
||||
{
|
||||
|
||||
|
@ -1198,7 +1236,7 @@ public class XmlConfiguration
|
|||
{
|
||||
props.put(key.toString(),String.valueOf(properties.get(key)));
|
||||
}
|
||||
configuration.setProperties(props);
|
||||
configuration.getProperties().putAll(props);
|
||||
}
|
||||
obj[i] = configuration.configure();
|
||||
last = configuration;
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 2009-2009 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.xml;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
*/
|
||||
public class ConstructorArgTestClass
|
||||
{
|
||||
@SuppressWarnings("rawtypes")
|
||||
private List list;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private ArrayList arrayList;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private Set set;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public ConstructorArgTestClass(LinkedList list)
|
||||
{
|
||||
// not supported yet
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public ConstructorArgTestClass(ArrayList arrayList, List list)
|
||||
{
|
||||
this.arrayList = arrayList;
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public ConstructorArgTestClass(ArrayList list)
|
||||
{
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public ConstructorArgTestClass(Set set)
|
||||
{
|
||||
this.set = set;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public List getList()
|
||||
{
|
||||
return list;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public ArrayList getArrayList()
|
||||
{
|
||||
return arrayList;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public Set getSet()
|
||||
{
|
||||
return set;
|
||||
}
|
||||
}
|
|
@ -14,7 +14,11 @@
|
|||
package org.eclipse.jetty.xml;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Ignore;
|
||||
|
||||
|
@ -33,6 +37,11 @@ public class TestConfiguration extends HashMap<String,Object>
|
|||
public int testField1;
|
||||
public int testField2;
|
||||
public int propValue;
|
||||
@SuppressWarnings("rawtypes")
|
||||
private List list;
|
||||
@SuppressWarnings("rawtypes")
|
||||
private Set set;
|
||||
private ConstructorArgTestClass constructorArgTestClass;
|
||||
|
||||
public void setTest(Object value)
|
||||
{
|
||||
|
@ -87,4 +96,49 @@ public class TestConfiguration extends HashMap<String,Object>
|
|||
{
|
||||
this.ia=ia;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public List getList()
|
||||
{
|
||||
if (constructorArgTestClass != null)
|
||||
return constructorArgTestClass.getList();
|
||||
return list;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void setList(List list)
|
||||
{
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void setLinkedList(LinkedList list)
|
||||
{
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void setArrayList(ArrayList list)
|
||||
{
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public Set getSet()
|
||||
{
|
||||
if (constructorArgTestClass != null)
|
||||
return constructorArgTestClass.getSet();
|
||||
return set;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void setSet(Set set)
|
||||
{
|
||||
this.set = set;
|
||||
}
|
||||
|
||||
public void setConstructorArgTestClass(ConstructorArgTestClass constructorArgTestClass)
|
||||
{
|
||||
this.constructorArgTestClass = constructorArgTestClass;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
package org.eclipse.jetty.xml;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
|
@ -25,13 +26,16 @@ import org.junit.Test;
|
|||
public class XmlConfigurationTest
|
||||
{
|
||||
protected String _configure="org/eclipse/jetty/xml/configure.xml";
|
||||
|
||||
private static final String STRING_ARRAY_XML = "<Array type=\"String\"><Item type=\"String\">String1</Item><Item type=\"String\">String2</Item></Array>";
|
||||
private static final String INT_ARRAY_XML = "<Array type=\"int\"><Item type=\"int\">1</Item><Item type=\"int\">2</Item></Array>";
|
||||
|
||||
@Test
|
||||
public void testMortBay() throws Exception
|
||||
{
|
||||
URL url = XmlConfigurationTest.class.getClassLoader().getResource("org/eclipse/jetty/xml/mortbay.xml");
|
||||
XmlConfiguration configuration = new XmlConfiguration(url);
|
||||
Object o=configuration.configure();
|
||||
configuration.configure();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -185,6 +189,129 @@ public class XmlConfigurationTest
|
|||
configuration.configure(tc);
|
||||
assertEquals("Set String 3","SetValue",tc.testObject);
|
||||
assertEquals("Set Type 3",2,tc.testInt);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListConstructorArg() throws Exception
|
||||
{
|
||||
XmlConfiguration xmlConfiguration = new XmlConfiguration("<Configure class=\"org.eclipse.jetty.xml.TestConfiguration\">"
|
||||
+ "<Set name=\"constructorArgTestClass\"><New class=\"org.eclipse.jetty.xml.ConstructorArgTestClass\"><Arg type=\"List\">"
|
||||
+ STRING_ARRAY_XML + "</Arg></New></Set></Configure>");
|
||||
TestConfiguration tc = new TestConfiguration();
|
||||
assertThat("tc.getList() returns null as it's not configured yet",tc.getList(),is(nullValue()));
|
||||
xmlConfiguration.configure(tc);
|
||||
assertThat("tc.getList() returns not null",tc.getList(),not(nullValue()));
|
||||
assertThat("tc.getList() has two entries as specified in the xml",tc.getList().size(),is(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTwoArgumentListConstructorArg() throws Exception
|
||||
{
|
||||
XmlConfiguration xmlConfiguration = new XmlConfiguration("<Configure class=\"org.eclipse.jetty.xml.TestConfiguration\">"
|
||||
+ "<Set name=\"constructorArgTestClass\"><New class=\"org.eclipse.jetty.xml.ConstructorArgTestClass\">"
|
||||
+ "<Arg type=\"List\">" + STRING_ARRAY_XML + "</Arg>"
|
||||
+ "<Arg type=\"List\">" + STRING_ARRAY_XML + "</Arg>"
|
||||
+ "</New></Set></Configure>");
|
||||
TestConfiguration tc = new TestConfiguration();
|
||||
assertThat("tc.getList() returns null as it's not configured yet",tc.getList(),is(nullValue()));
|
||||
xmlConfiguration.configure(tc);
|
||||
assertThat("tc.getList() returns not null",tc.getList(),not(nullValue()));
|
||||
assertThat("tc.getList() has two entries as specified in the xml",tc.getList().size(),is(2));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testListNotContainingArray() throws Exception
|
||||
{
|
||||
XmlConfiguration xmlConfiguration = new XmlConfiguration("<Configure class=\"org.eclipse.jetty.xml.TestConfiguration\">"
|
||||
+ "<New class=\"org.eclipse.jetty.xml.ConstructorArgTestClass\"><Arg type=\"List\">Some String</Arg></New></Configure>");
|
||||
TestConfiguration tc = new TestConfiguration();
|
||||
xmlConfiguration.configure(tc);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetConstructorArg() throws Exception
|
||||
{
|
||||
XmlConfiguration xmlConfiguration = new XmlConfiguration("<Configure class=\"org.eclipse.jetty.xml.TestConfiguration\">"
|
||||
+ "<Set name=\"constructorArgTestClass\"><New class=\"org.eclipse.jetty.xml.ConstructorArgTestClass\"><Arg type=\"Set\">"
|
||||
+ STRING_ARRAY_XML + "</Arg></New></Set></Configure>");
|
||||
TestConfiguration tc = new TestConfiguration();
|
||||
assertThat("tc.getList() returns null as it's not configured yet",tc.getSet(),is(nullValue()));
|
||||
xmlConfiguration.configure(tc);
|
||||
assertThat("tc.getList() returns not null",tc.getSet(),not(nullValue()));
|
||||
assertThat("tc.getList() has two entries as specified in the xml",tc.getSet().size(),is(2));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testSetNotContainingArray() throws Exception
|
||||
{
|
||||
XmlConfiguration xmlConfiguration = new XmlConfiguration("<Configure class=\"org.eclipse.jetty.xml.TestConfiguration\">"
|
||||
+ "<New class=\"org.eclipse.jetty.xml.ConstructorArgTestClass\"><Arg type=\"Set\">Some String</Arg></New></Configure>");
|
||||
TestConfiguration tc = new TestConfiguration();
|
||||
xmlConfiguration.configure(tc);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListSetterWithStringArray() throws Exception
|
||||
{
|
||||
XmlConfiguration xmlConfiguration = new XmlConfiguration("<Configure class=\"org.eclipse.jetty.xml.TestConfiguration\"><Set name=\"List\">"
|
||||
+ STRING_ARRAY_XML + "</Set></Configure>");
|
||||
TestConfiguration tc = new TestConfiguration();
|
||||
assertThat("tc.getList() returns null as it's not configured yet",tc.getList(),is(nullValue()));
|
||||
xmlConfiguration.configure(tc);
|
||||
assertThat("tc.getList() has two entries as specified in the xml",tc.getList().size(),is(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListSetterWithPrimitiveArray() throws Exception
|
||||
{
|
||||
XmlConfiguration xmlConfiguration = new XmlConfiguration("<Configure class=\"org.eclipse.jetty.xml.TestConfiguration\"><Set name=\"List\">"
|
||||
+ INT_ARRAY_XML + "</Set></Configure>");
|
||||
TestConfiguration tc = new TestConfiguration();
|
||||
assertThat("tc.getList() returns null as it's not configured yet",tc.getList(),is(nullValue()));
|
||||
xmlConfiguration.configure(tc);
|
||||
assertThat("tc.getList() has two entries as specified in the xml",tc.getList().size(),is(2));
|
||||
}
|
||||
|
||||
@Test(expected=NoSuchMethodException.class)
|
||||
public void testNotSupportedLinkedListSetter() throws Exception
|
||||
{
|
||||
XmlConfiguration xmlConfiguration = new XmlConfiguration("<Configure class=\"org.eclipse.jetty.xml.TestConfiguration\"><Set name=\"LinkedList\">"
|
||||
+ INT_ARRAY_XML + "</Set></Configure>");
|
||||
TestConfiguration tc = new TestConfiguration();
|
||||
assertThat("tc.getSet() returns null as it's not configured yet",tc.getList(),is(nullValue()));
|
||||
xmlConfiguration.configure(tc);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testArrayListSetter() throws Exception
|
||||
{
|
||||
XmlConfiguration xmlConfiguration = new XmlConfiguration("<Configure class=\"org.eclipse.jetty.xml.TestConfiguration\"><Set name=\"ArrayList\">"
|
||||
+ INT_ARRAY_XML + "</Set></Configure>");
|
||||
TestConfiguration tc = new TestConfiguration();
|
||||
assertThat("tc.getSet() returns null as it's not configured yet",tc.getList(),is(nullValue()));
|
||||
xmlConfiguration.configure(tc);
|
||||
assertThat("tc.getSet() has two entries as specified in the xml",tc.getList().size(),is(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetSetter() throws Exception
|
||||
{
|
||||
XmlConfiguration xmlConfiguration = new XmlConfiguration("<Configure class=\"org.eclipse.jetty.xml.TestConfiguration\"><Set name=\"Set\">"
|
||||
+ STRING_ARRAY_XML + "</Set></Configure>");
|
||||
TestConfiguration tc = new TestConfiguration();
|
||||
assertThat("tc.getSet() returns null as it's not configured yet",tc.getSet(),is(nullValue()));
|
||||
xmlConfiguration.configure(tc);
|
||||
assertThat("tc.getSet() has two entries as specified in the xml",tc.getSet().size(),is(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetSetterWithPrimitiveArray() throws Exception
|
||||
{
|
||||
XmlConfiguration xmlConfiguration = new XmlConfiguration("<Configure class=\"org.eclipse.jetty.xml.TestConfiguration\"><Set name=\"Set\">"
|
||||
+ INT_ARRAY_XML + "</Set></Configure>");
|
||||
TestConfiguration tc = new TestConfiguration();
|
||||
assertThat("tc.getSet() returns null as it's not configured yet",tc.getSet(),is(nullValue()));
|
||||
xmlConfiguration.configure(tc);
|
||||
assertThat("tc.getSet() has two entries as specified in the xml",tc.getSet().size(),is(2));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -111,11 +111,11 @@ public abstract class AbstractSessionMigrationTest
|
|||
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
HttpSession session = request.getSession(false);
|
||||
if (session == null) session = request.getSession(true);
|
||||
|
||||
String action = request.getParameter("action");
|
||||
if ("set".equals(action))
|
||||
{
|
||||
if (session == null) session = request.getSession(true);
|
||||
int value = Integer.parseInt(request.getParameter("value"));
|
||||
session.setAttribute("value", value);
|
||||
PrintWriter writer = response.getWriter();
|
||||
|
@ -125,6 +125,8 @@ public abstract class AbstractSessionMigrationTest
|
|||
else if ("get".equals(action))
|
||||
{
|
||||
int value = (Integer)session.getAttribute("value");
|
||||
int x = ((AbstractSession)session).getMaxInactiveInterval();
|
||||
assertTrue(x > 0);
|
||||
PrintWriter writer = response.getWriter();
|
||||
writer.println(value);
|
||||
writer.flush();
|
||||
|
|
Loading…
Reference in New Issue