Merge branch 'master' of ssh://git.eclipse.org/gitroot/jetty/org.eclipse.jetty.project

This commit is contained in:
Jan Bartel 2012-03-09 11:28:41 +11:00
commit c596add40c
22 changed files with 807 additions and 309 deletions

View File

@ -1,22 +1,19 @@
package org.eclipse.jetty.annotations.resources;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import javax.naming.Context;
import javax.naming.InitialContext;
import org.eclipse.jetty.annotations.AnnotationIntrospector;
import org.eclipse.jetty.annotations.AnnotationParser;
import org.eclipse.jetty.annotations.ClassNameResolver;
import org.eclipse.jetty.annotations.ResourceAnnotationHandler;
import org.eclipse.jetty.annotations.ResourcesAnnotationHandler;
import org.eclipse.jetty.plus.annotation.Injection;
import org.eclipse.jetty.plus.annotation.InjectionCollection;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.MetaData;
import org.eclipse.jetty.webapp.WebAppContext;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
@ -24,24 +21,39 @@ import static org.junit.Assert.assertNotNull;
public class TestResourceAnnotations
{
Object objA=new Integer(1000);
Object objB=new Integer(2000);
private Server server;
private WebAppContext wac;
private InjectionCollection injections;
private Context comp;
private Context env;
private Object objA = 1000;
private Object objB = 2000;
@Before
public void init() throws Exception
{
server = new Server();
wac = new WebAppContext();
wac.setServer(server);
injections = new InjectionCollection();
wac.setAttribute(InjectionCollection.INJECTION_COLLECTION, injections);
InitialContext ic = new InitialContext();
comp = (Context)ic.lookup("java:comp");
env = comp.createSubcontext("env");
}
@After
public void destroy() throws Exception
{
comp.destroySubcontext("env");
}
@Test
public void testResourceAnnotations ()
throws Exception
{
Server server = new Server();
WebAppContext wac = new WebAppContext();
wac.setServer(server);
InjectionCollection injections = new InjectionCollection();
wac.setAttribute(InjectionCollection.INJECTION_COLLECTION, injections);
InitialContext ic = new InitialContext();
Context comp = (Context)ic.lookup("java:comp");
Context env = comp.createSubcontext("env");
org.eclipse.jetty.plus.jndi.EnvEntry resourceA = new org.eclipse.jetty.plus.jndi.EnvEntry(server, "resA", objA, false);
org.eclipse.jetty.plus.jndi.EnvEntry resourceB = new org.eclipse.jetty.plus.jndi.EnvEntry(server, "resB", objB, false);
new org.eclipse.jetty.plus.jndi.EnvEntry(server, "resA", objA, false);
new org.eclipse.jetty.plus.jndi.EnvEntry(server, "resB", objB, false);
AnnotationIntrospector parser = new AnnotationIntrospector();
ResourceAnnotationHandler handler = new ResourceAnnotationHandler(wac);
@ -116,25 +128,14 @@ public class TestResourceAnnotations
f = ResourceA.class.getDeclaredField("n");
f.setAccessible(true);
assertEquals(objB, f.get(binst));
comp.destroySubcontext("env");
}
@Test
public void testResourcesAnnotation ()
throws Exception
{
Server server = new Server();
WebAppContext wac = new WebAppContext();
wac.setServer(server);
InjectionCollection injections = new InjectionCollection();
wac.setAttribute(InjectionCollection.INJECTION_COLLECTION, injections);
InitialContext ic = new InitialContext();
Context comp = (Context)ic.lookup("java:comp");
Context env = comp.createSubcontext("env");
org.eclipse.jetty.plus.jndi.EnvEntry resourceA = new org.eclipse.jetty.plus.jndi.EnvEntry(server, "resA", objA, false);
org.eclipse.jetty.plus.jndi.EnvEntry resourceB = new org.eclipse.jetty.plus.jndi.EnvEntry(server, "resB", objB, false);
new org.eclipse.jetty.plus.jndi.EnvEntry(server, "resA", objA, false);
new org.eclipse.jetty.plus.jndi.EnvEntry(server, "resB", objB, false);
AnnotationIntrospector introspector = new AnnotationIntrospector();
ResourcesAnnotationHandler handler = new ResourcesAnnotationHandler(wac);

View File

@ -13,11 +13,15 @@
package org.eclipse.jetty.plus.jndi;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import javax.naming.Binding;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.Name;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.RefAddr;
import javax.naming.Reference;
@ -25,6 +29,7 @@ import javax.naming.Referenceable;
import javax.naming.StringRefAddr;
import javax.naming.spi.ObjectFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@ -125,6 +130,35 @@ public class TestNamingEntries
public void init()
{
this.someObject = new SomeObject(4);
}
/**
* after each test we should scrape out any lingering bindings to prevent cross test pollution
* as observed when running java 7
*
* @throws Exception
*/
@After
public void after() throws Exception
{
InitialContext icontext = new InitialContext();
NamingEnumeration<Binding> bindings = icontext.listBindings("");
List<String> names = new ArrayList<String>();
while (bindings.hasMore())
{
Binding bd = (Binding)bindings.next();
names.add(bd.getName());
}
for (String name : names)
{
icontext.unbind(name);
}
}
@Test
@ -199,6 +233,7 @@ public class TestNamingEntries
ne = NamingEntryUtil.lookupNamingEntry(new ScopeB(), "resourceB");
assertNull(ne);
testLink();
}
@Test

View File

@ -1,8 +1,5 @@
package org.eclipse.jetty.server.handler;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@ -13,7 +10,6 @@ import java.net.Socket;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
@ -31,6 +27,9 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
/**
* @version $Revision$ $Date$
*/
@ -188,7 +187,7 @@ public class ConnectHandlerSSLTest extends AbstractConnectHandlerTest
public X509Certificate[] getAcceptedIssuers()
{
return null;
return new X509Certificate[]{};
}
}

View File

@ -27,7 +27,6 @@ import java.io.OutputStream;
import java.net.Socket;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
@ -36,7 +35,6 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import junit.framework.TestCase;
import org.eclipse.jetty.io.AsyncEndPoint;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Request;
@ -53,17 +51,15 @@ public class SSLCloseTest extends TestCase
{
public X509Certificate[] getAcceptedIssuers()
{
return null;
return new X509Certificate[]{};
}
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException
{
return;
}
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException
{
return;
}
}

View File

@ -20,6 +20,7 @@ import java.io.UnsupportedEncodingException;
import java.util.HashSet;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.regex.Pattern;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
@ -67,12 +68,17 @@ public class GzipFilter extends UserAgentFilter
protected Set<String> _mimeTypes;
protected int _bufferSize=8192;
protected int _minGzipSize=256;
protected Set<String> _excluded;
protected Set<String> _excludedAgents;
protected Set<Pattern> _excludedAgentPatterns;
protected Set<String> _excludedPaths;
protected Set<Pattern> _excludedPathPatterns;
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.servlets.UserAgentFilter#init(javax.servlet.FilterConfig)
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException
{
super.init(filterConfig);
@ -93,14 +99,40 @@ public class GzipFilter extends UserAgentFilter
while (tok.hasMoreTokens())
_mimeTypes.add(tok.nextToken());
}
tmp=filterConfig.getInitParameter("excludedAgents");
if (tmp!=null)
{
_excluded=new HashSet<String>();
_excludedAgents=new HashSet<String>();
StringTokenizer tok = new StringTokenizer(tmp,",",false);
while (tok.hasMoreTokens())
_excluded.add(tok.nextToken());
_excludedAgents.add(tok.nextToken());
}
tmp=filterConfig.getInitParameter("excludeAgentPatterns");
if (tmp!=null)
{
_excludedAgentPatterns=new HashSet<Pattern>();
StringTokenizer tok = new StringTokenizer(tmp,",",false);
while (tok.hasMoreTokens())
_excludedAgentPatterns.add(Pattern.compile(tok.nextToken()));
}
tmp=filterConfig.getInitParameter("excludePaths");
if (tmp!=null)
{
_excludedPaths=new HashSet<String>();
StringTokenizer tok = new StringTokenizer(tmp,",",false);
while (tok.hasMoreTokens())
_excludedPaths.add(tok.nextToken());
}
tmp=filterConfig.getInitParameter("excludePathPatterns");
if (tmp!=null)
{
_excludedPathPatterns=new HashSet<Pattern>();
StringTokenizer tok = new StringTokenizer(tmp,",",false);
while (tok.hasMoreTokens())
_excludedPathPatterns.add(Pattern.compile(tok.nextToken()));
}
}
@ -108,6 +140,7 @@ public class GzipFilter extends UserAgentFilter
/**
* @see org.eclipse.jetty.servlets.UserAgentFilter#destroy()
*/
@Override
public void destroy()
{
}
@ -116,6 +149,7 @@ public class GzipFilter extends UserAgentFilter
/**
* @see org.eclipse.jetty.servlets.UserAgentFilter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
*/
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException
{
@ -125,15 +159,18 @@ public class GzipFilter extends UserAgentFilter
String ae = request.getHeader("accept-encoding");
if (ae != null && ae.indexOf("gzip")>=0 && !response.containsHeader("Content-Encoding")
&& !HttpMethods.HEAD.equalsIgnoreCase(request.getMethod()))
{
if (_excluded!=null)
{
String ua = getUserAgent(request);
if (_excluded.contains(ua))
if (isExcludedAgent(ua))
{
super.doFilter(request,response,chain);
return;
}
String requestURI = request.getRequestURI();
if (isExcludedPath(requestURI))
{
super.doFilter(request,response,chain);
return;
}
final GzipResponseWrapper wrappedResponse=newGzipResponseWrapper(request,response);
@ -182,6 +219,63 @@ public class GzipFilter extends UserAgentFilter
}
}
/**
* Checks to see if the UserAgent is excluded
*
* @param ua
* the user agent
* @return boolean true if excluded
*/
private boolean isExcludedAgent(String ua)
{
if (ua == null)
return false;
if (_excludedAgents != null)
{
if (_excludedAgents.contains(ua))
{
return true;
}
}
else if (_excludedAgentPatterns != null)
{
for (Pattern pattern : _excludedAgentPatterns)
{
if (pattern.matcher(ua).matches())
{
return true;
}
}
}
return false;
}
/**
* Checks to see if the Path is excluded
*
* @param ua
* the request uri
* @return boolean true if excluded
*/
private boolean isExcludedPath(String requestURI)
{
if (requestURI == null)
return false;
if (_excludedPathPatterns != null)
{
for (Pattern pattern : _excludedPathPatterns)
{
if (pattern.matcher(requestURI).matches())
{
return true;
}
}
}
return false;
}
/**
* Allows derived implementations to replace ResponseWrapper implementation.
*

View File

@ -18,7 +18,6 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
@ -52,7 +51,7 @@ public class UserAgentFilter implements Filter
{
private static final String __defaultPattern = "(?:Mozilla[^\\(]*\\(compatible;\\s*+([^;]*);.*)|(?:.*?([^\\s]+/[^\\s]+).*)";
private Pattern _pattern = Pattern.compile(__defaultPattern);
private Map _agentCache = new ConcurrentHashMap();
private Map<String, String> _agentCache = new ConcurrentHashMap<String, String>();
private int _agentCacheSize=1024;
private String _attribute;
@ -115,10 +114,11 @@ public class UserAgentFilter implements Filter
if (ua == null)
return null;
String tag = (String)_agentCache.get(ua);
String tag = _agentCache.get(ua);
if (tag == null)
{
if (_pattern != null)
{
Matcher matcher = _pattern.matcher(ua);
if (matcher.matches())
@ -129,20 +129,24 @@ public class UserAgentFilter implements Filter
{
String group = matcher.group(g);
if (group != null)
tag=tag==null?group:(tag+group);
tag = tag == null ? group : tag + group;
}
}
else
{
tag = matcher.group();
}
else
}
}
if (tag == null)
tag = ua;
if (_agentCache.size() >= _agentCacheSize)
_agentCache.clear();
_agentCache.put(ua, tag);
}
return tag;
}
}

View File

@ -38,4 +38,9 @@ public class SessionException extends RuntimeException
super(cause);
this.sessionStatus = sessionStatus;
}
public SessionStatus getSessionStatus()
{
return sessionStatus;
}
}

View File

@ -135,7 +135,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
}
@Override
public void syn(SynInfo synInfo, StreamFrameListener listener, long timeout, TimeUnit unit, final Handler<Stream> handler)
public void syn(SynInfo synInfo, StreamFrameListener listener, long timeout, TimeUnit unit, Handler<Stream> handler)
{
// Synchronization is necessary.
// SPEC v3, 2.3.1 requires that the stream creation be monotonically crescent
@ -154,7 +154,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
{
int streamId = streamIds.getAndAdd(2);
SynStreamFrame synStream = new SynStreamFrame(version, synInfo.getFlags(), streamId, 0, synInfo.getPriority(), synInfo.getHeaders());
final IStream stream = createStream(synStream, listener);
IStream stream = createStream(synStream, listener);
control(stream, synStream, timeout, unit, handler, stream);
}
}
@ -178,7 +178,11 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
}
else
{
RstStreamFrame frame = new RstStreamFrame(version, rstInfo.getStreamId(), rstInfo.getStreamStatus().getCode(version));
int streamId = rstInfo.getStreamId();
IStream stream = streams.get(streamId);
if (stream != null)
removeStream(stream);
RstStreamFrame frame = new RstStreamFrame(version, streamId, rstInfo.getStreamStatus().getCode(version));
control(null, frame, timeout, unit, handler, null);
}
}
@ -207,7 +211,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
}
@Override
public void ping(long timeout, TimeUnit unit, final Handler<PingInfo> handler)
public void ping(long timeout, TimeUnit unit, Handler<PingInfo> handler)
{
int pingId = pingIds.getAndAdd(2);
PingInfo pingInfo = new PingInfo(pingId);
@ -217,20 +221,30 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
@Override
public Future<Void> goAway()
{
return goAway(SessionStatus.OK);
}
private Future<Void> goAway(SessionStatus sessionStatus)
{
Promise<Void> result = new Promise<>();
goAway(0, TimeUnit.MILLISECONDS, result);
goAway(sessionStatus, 0, TimeUnit.MILLISECONDS, result);
return result;
}
@Override
public void goAway(long timeout, TimeUnit unit, Handler<Void> handler)
{
goAway(SessionStatus.OK, timeout, unit, handler);
}
private void goAway(SessionStatus sessionStatus, long timeout, TimeUnit unit, Handler<Void> handler)
{
if (goAwaySent.compareAndSet(false, true))
{
if (!goAwayReceived.get())
{
GoAwayFrame frame = new GoAwayFrame(version, lastStreamId.get(), SessionStatus.OK.getCode());
GoAwayFrame frame = new GoAwayFrame(version, lastStreamId.get(), sessionStatus.getCode());
control(null, frame, timeout, unit, handler, null);
return;
}
@ -320,7 +334,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
}
@Override
public void onDataFrame(final DataFrame frame, final ByteBuffer data)
public void onDataFrame(DataFrame frame, ByteBuffer data)
{
notifyIdle(idleListener, false);
try
@ -334,7 +348,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
}
int streamId = frame.getStreamId();
final IStream stream = streams.get(streamId);
IStream stream = streams.get(streamId);
if (stream == null)
{
RstInfo rstInfo = new RstInfo(streamId, StreamStatus.INVALID_STREAM);
@ -369,20 +383,21 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
@Override
public void onStreamException(StreamException x)
{
logger.info("Caught stream exception", x);
notifyOnException(listener, x);
rst(new RstInfo(x.getStreamId(), x.getStreamStatus()));
}
@Override
public void onSessionException(SessionException x)
{
logger.info("Caught session exception", x);
goAway();
Throwable cause = x.getCause();
notifyOnException(listener, cause == null ? x : cause);
goAway(x.getSessionStatus());
}
private void onSyn(final SynStreamFrame frame)
private void onSyn(SynStreamFrame frame)
{
final IStream stream = newStream(frame);
IStream stream = newStream(frame);
logger.debug("Opening {}", stream);
int streamId = frame.getStreamId();
IStream existing = streams.putIfAbsent(streamId, stream);
@ -480,10 +495,10 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
}
}
private void onReply(final SynReplyFrame frame)
private void onReply(SynReplyFrame frame)
{
int streamId = frame.getStreamId();
final IStream stream = streams.get(streamId);
IStream stream = streams.get(streamId);
if (stream == null)
{
RstInfo rstInfo = new RstInfo(streamId, StreamStatus.INVALID_STREAM);
@ -503,11 +518,11 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
removeStream(stream);
}
private void onRst(final RstStreamFrame frame)
private void onRst(RstStreamFrame frame)
{
// TODO: implement logic to clean up unidirectional streams associated with this stream
final IStream stream = streams.get(frame.getStreamId());
IStream stream = streams.get(frame.getStreamId());
if (stream != null)
stream.process(frame);
@ -520,7 +535,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
removeStream(stream);
}
private void onSettings(final SettingsFrame frame)
private void onSettings(SettingsFrame frame)
{
Settings.Setting windowSizeSetting = frame.getSettings().get(Settings.ID.INITIAL_WINDOW_SIZE);
if (windowSizeSetting != null)
@ -534,7 +549,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
flush();
}
private void onPing(final PingFrame frame)
private void onPing(PingFrame frame)
{
int pingId = frame.getPingId();
if (pingId % 2 == pingIds.get() % 2)
@ -549,7 +564,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
}
}
private void onGoAway(final GoAwayFrame frame)
private void onGoAway(GoAwayFrame frame)
{
if (goAwayReceived.compareAndSet(false, true))
{
@ -563,10 +578,10 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
}
}
private void onHeaders(final HeadersFrame frame)
private void onHeaders(HeadersFrame frame)
{
int streamId = frame.getStreamId();
final IStream stream = streams.get(streamId);
IStream stream = streams.get(streamId);
if (stream == null)
{
RstInfo rstInfo = new RstInfo(streamId, StreamStatus.INVALID_STREAM);
@ -601,6 +616,22 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
controller.close(false);
}
private void notifyOnException(SessionFrameListener listener, Throwable x)
{
try
{
if (listener != null)
{
logger.debug("Invoking callback with {} on listener {}", x, listener);
listener.onException(x);
}
}
catch (Exception xx)
{
logger.info("Exception while notifying listener " + listener, xx);
}
}
private StreamFrameListener notifyOnSyn(SessionFrameListener listener, Stream stream, SynInfo synInfo)
{
try
@ -650,7 +681,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
}
}
private void notifyOnPing(SessionFrameListener listener, final PingInfo pingInfo)
private void notifyOnPing(SessionFrameListener listener, PingInfo pingInfo)
{
try
{
@ -683,7 +714,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
}
@Override
public <C> void control(IStream stream, ControlFrame frame, long timeout, TimeUnit unit, final Handler<C> handler, C context)
public <C> void control(IStream stream, ControlFrame frame, long timeout, TimeUnit unit, Handler<C> handler, C context)
{
try
{
@ -705,7 +736,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
flush();
}
catch (final Throwable x)
catch (Throwable x)
{
notifyHandlerFailed(handler, x);
}
@ -738,7 +769,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
flush();
}
private void execute(final Runnable task)
private void execute(Runnable task)
{
threadPool.execute(task);
}
@ -813,7 +844,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
throw new SPDYException(x);
}
protected void write(final ByteBuffer buffer, Handler<FrameBytes> handler, FrameBytes frameBytes)
protected void write(ByteBuffer buffer, Handler<FrameBytes> handler, FrameBytes frameBytes)
{
if (controller != null)
controller.write(buffer, handler, frameBytes);
@ -828,7 +859,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
// starvation (for the last frames sent) and stack overflow.
// Therefore every some invocation, we dispatch to a new thread
Integer invocations = handlerInvocations.get();
if (invocations >= 8)
if (invocations >= 4)
{
execute(new Runnable()
{
@ -850,7 +881,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
}
finally
{
handlerInvocations.set(invocations - 1);
handlerInvocations.set(invocations);
}
}
}
@ -910,7 +941,7 @@ public class StandardSession implements ISession, Parser.Listener, Handler<Stand
StandardSession.this.complete(handler, context);
}
protected void fail(final Throwable x)
protected void fail(Throwable x)
{
notifyHandlerFailed(handler, x);
}

View File

@ -36,13 +36,6 @@ public class StreamException extends RuntimeException
this.streamStatus = streamStatus;
}
public StreamException(int streamId, StreamStatus streamStatus, Throwable x)
{
super(x);
this.streamId = streamId;
this.streamStatus = streamStatus;
}
public int getStreamId()
{
return streamId;

View File

@ -18,6 +18,9 @@ package org.eclipse.jetty.spdy.api;
import java.util.EventListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <p>A {@link SessionFrameListener} is the passive counterpart of a {@link Session} and receives events happening
* on a SPDY session.</p>
@ -105,11 +108,22 @@ public interface SessionFrameListener extends EventListener
*/
public void onGoAway(Session session, GoAwayInfo goAwayInfo);
/**
* <p>Callback invoked when an exception is thrown during the processing of an event on a
* SPDY session.</p>
* <p>Examples of such conditions are invalid frames received, corrupted headers compression state, etc.</p>
*
* @param x the exception that caused the event processing failure
*/
public void onException(Throwable x);
/**
* <p>Empty implementation of {@link SessionFrameListener}</p>
*/
public static class Adapter implements SessionFrameListener
{
private static final Logger logger = LoggerFactory.getLogger(Adapter.class);
@Override
public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
{
@ -135,5 +149,11 @@ public interface SessionFrameListener extends EventListener
public void onGoAway(Session session, GoAwayInfo goAwayInfo)
{
}
@Override
public void onException(Throwable x)
{
logger.info("", x);
}
}
}

View File

@ -47,27 +47,27 @@ public enum StreamStatus
/**
* <p>The stream status indicating an implementation error</p>
*/
INTERNAL_ERROR(6, 11),
INTERNAL_ERROR(6, 6),
/**
* <p>The stream status indicating a flow control error</p>
*/
FLOW_CONTROL_ERROR(7, 6),
FLOW_CONTROL_ERROR(7, 7),
/**
* <p>The stream status indicating a stream opened more than once</p>
*/
STREAM_IN_USE(-1, 7),
STREAM_IN_USE(-1, 8),
/**
* <p>The stream status indicating data on a stream already closed</p>
*/
STREAM_ALREADY_CLOSED(-1, 8),
STREAM_ALREADY_CLOSED(-1, 9),
/**
* <p>The stream status indicating credentials not valid</p>
*/
INVALID_CREDENTIALS(-1, 9),
INVALID_CREDENTIALS(-1, 10),
/**
* <p>The stream status indicating that the implementation could not support a frame too large</p>
*/
FRAME_TOO_LARGE(-1, 10);
FRAME_TOO_LARGE(-1, 11);
/**
* @param version the SPDY protocol version

View File

@ -20,9 +20,6 @@ import java.nio.ByteBuffer;
import java.util.EnumMap;
import org.eclipse.jetty.spdy.CompressionFactory;
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.ControlFrameType;
@ -77,7 +74,6 @@ public abstract class ControlFrameParser
if (buffer.remaining() >= 2)
{
version = (short)(buffer.getShort() & 0x7F_FF);
checkVersion(version);
state = State.TYPE;
}
else
@ -95,7 +91,6 @@ public abstract class ControlFrameParser
if (cursor == 0)
{
version &= 0x7F_FF;
checkVersion(version);
state = State.TYPE;
}
break;
@ -171,12 +166,6 @@ public abstract class ControlFrameParser
return false;
}
private void checkVersion(short version)
{
if (version != SPDY.V2 && version != SPDY.V3)
throw new SessionException(SessionStatus.PROTOCOL_ERROR, "Unrecognized version " + version);
}
private void reset()
{
state = State.VERSION;

View File

@ -19,8 +19,10 @@ package org.eclipse.jetty.spdy.parser;
import java.nio.ByteBuffer;
import org.eclipse.jetty.spdy.CompressionFactory;
import org.eclipse.jetty.spdy.StreamException;
import org.eclipse.jetty.spdy.api.Headers;
import org.eclipse.jetty.spdy.api.SPDY;
import org.eclipse.jetty.spdy.api.StreamStatus;
import org.eclipse.jetty.spdy.api.SynInfo;
import org.eclipse.jetty.spdy.frames.ControlFrameType;
import org.eclipse.jetty.spdy.frames.SynStreamFrame;
@ -77,6 +79,10 @@ public class SynStreamBodyParser extends ControlFrameBodyParser
}
case ASSOCIATED_STREAM_ID:
{
// Now we know the streamId, we can do the version check
// and if it is wrong, issue a RST_STREAM
checkVersion(controlFrameParser.getVersion(), streamId);
if (buffer.remaining() >= 4)
{
associatedStreamId = buffer.getInt() & 0x7F_FF_FF_FF;
@ -145,6 +151,12 @@ public class SynStreamBodyParser extends ControlFrameBodyParser
return false;
}
private void checkVersion(short version, int streamId)
{
if (version != SPDY.V2 && version != SPDY.V3)
throw new StreamException(streamId, StreamStatus.UNSUPPORTED_VERSION);
}
private byte readPriority(short version, byte currByte)
{
// Right shift retains the sign bit when operated on a byte,

View File

@ -20,19 +20,51 @@ import java.nio.ByteBuffer;
public class UnknownControlFrameBodyParser extends ControlFrameBodyParser
{
private final ControlFrameParser controlFrameParser;
private State state = State.BODY;
private int remaining;
public UnknownControlFrameBodyParser(ControlFrameParser controlFrameParser)
{
this.remaining = controlFrameParser.getLength();
this.controlFrameParser = controlFrameParser;
}
@Override
public boolean parse(ByteBuffer buffer)
{
int consumed = Math.min(remaining, buffer.remaining());
buffer.position(buffer.position() + consumed);
remaining -= consumed;
return remaining == 0;
switch (state)
{
case BODY:
{
remaining = controlFrameParser.getLength();
state = State.CONSUME;
// Fall down
}
case CONSUME:
{
int consume = Math.min(remaining, buffer.remaining());
buffer.position(buffer.position() + consume);
remaining -= consume;
if (remaining > 0)
return false;
reset();
return true;
}
default:
{
throw new IllegalStateException();
}
}
}
private void reset()
{
state = State.BODY;
remaining = 0;
}
private enum State
{
BODY, CONSUME
}
}

View File

@ -0,0 +1,64 @@
package org.eclipse.jetty.spdy.parser;
import java.nio.ByteBuffer;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.spdy.SessionException;
import org.eclipse.jetty.spdy.StandardByteBufferPool;
import org.eclipse.jetty.spdy.StandardCompressionFactory;
import org.eclipse.jetty.spdy.StreamException;
import org.eclipse.jetty.spdy.api.Headers;
import org.eclipse.jetty.spdy.api.SPDY;
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.SynStreamFrame;
import org.eclipse.jetty.spdy.generator.Generator;
import org.junit.Assert;
import org.junit.Test;
public class UnknownControlFrameTest
{
@Test
public void testUnknownControlFrame() throws Exception
{
SynStreamFrame frame = new SynStreamFrame(SPDY.V2, SynInfo.FLAG_CLOSE, 1, 0, (byte)0, new Headers());
Generator generator = new Generator(new StandardByteBufferPool(), new StandardCompressionFactory.StandardCompressor());
ByteBuffer buffer = generator.control(frame);
// Change the frame type to unknown
buffer.putShort(2, (short)0);
final CountDownLatch latch = new CountDownLatch(1);
Parser parser = new Parser(new StandardCompressionFactory.StandardDecompressor());
parser.addListener(new Parser.Listener.Adapter()
{
@Override
public void onControlFrame(ControlFrame frame)
{
latch.countDown();
}
@Override
public void onDataFrame(DataFrame frame, ByteBuffer data)
{
latch.countDown();
}
@Override
public void onStreamException(StreamException x)
{
latch.countDown();
}
@Override
public void onSessionException(SessionException x)
{
latch.countDown();
}
});
parser.parse(buffer);
Assert.assertFalse(latch.await(1, TimeUnit.SECONDS));
}
}

View File

@ -1,18 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.eclipse.jetty.spdy</groupId>
<artifactId>spdy-parent</artifactId>
<version>7.6.2-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spdy-jetty-http-webapp</artifactId>
<packaging>war</packaging>
<name>Jetty :: SPDY :: Jetty HTTP Web Application</name>
<!--
<build>
<plugins>
<plugin>
@ -44,5 +43,5 @@
</plugin>
</plugins>
</build>
-->
</project>

View File

@ -1,30 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.eclipse.jetty.spdy</groupId>
<artifactId>spdy-parent</artifactId>
<version>7.6.2-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spdy-jetty-http</artifactId>
<name>Jetty :: SPDY :: Jetty HTTP Layer</name>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>
-Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${project.version}/npn-boot-${project.version}.jar
</argLine>
<argLine>-Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${project.version}/npn-boot-${project.version}.jar </argLine>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>eclipse-release</id>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty.spdy</groupId>
@ -48,5 +57,4 @@
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -1,30 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.eclipse.jetty.spdy</groupId>
<artifactId>spdy-parent</artifactId>
<version>7.6.2-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spdy-jetty</artifactId>
<name>Jetty :: SPDY :: Jetty Binding</name>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>
-Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${project.version}/npn-boot-${project.version}.jar
</argLine>
<argLine>-Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${project.version}/npn-boot-${project.version}.jar </argLine>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>eclipse-release</id>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty.spdy</groupId>
@ -53,5 +62,4 @@
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -53,9 +53,10 @@ public abstract class AbstractTest
protected InetSocketAddress startServer(ServerSessionFrameListener listener) throws Exception
{
server = new Server();
if (connector == null)
connector = newSPDYServerConnector(listener);
connector.setPort(0);
server = new Server();
server.addConnector(connector);
server.start();
return new InetSocketAddress("localhost", connector.getLocalPort());

View File

@ -0,0 +1,123 @@
package org.eclipse.jetty.spdy;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.spdy.api.DataInfo;
import org.eclipse.jetty.spdy.api.Handler;
import org.eclipse.jetty.spdy.api.RstInfo;
import org.eclipse.jetty.spdy.api.Session;
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.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.junit.Assert;
import org.junit.Test;
public class ResetStreamTest extends AbstractTest
{
@Test
public void testResetStreamIsRemoved() throws Exception
{
Session session = startClient(startServer(new ServerSessionFrameListener.Adapter()), 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);
Assert.assertEquals(0, session.getStreams().size());
}
@Test
public void testRefusedStreamIsRemoved() throws Exception
{
final AtomicReference<Session> serverSessionRef = new AtomicReference<>();
final CountDownLatch synLatch = new CountDownLatch(1);
final CountDownLatch rstLatch = new CountDownLatch(1);
Session clientSession = startClient(startServer(new ServerSessionFrameListener.Adapter()
{
@Override
public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
{
Session serverSession = stream.getSession();
serverSessionRef.set(serverSession);
serverSession.rst(new RstInfo(stream.getId(), StreamStatus.REFUSED_STREAM));
synLatch.countDown();
return null;
}
}), new SessionFrameListener.Adapter()
{
@Override
public void onRst(Session session, RstInfo rstInfo)
{
rstLatch.countDown();
}
});
clientSession.syn(new SynInfo(false), null).get(5, TimeUnit.SECONDS);
Assert.assertTrue(synLatch.await(5, TimeUnit.SECONDS));
Session serverSession = serverSessionRef.get();
Assert.assertEquals(0, serverSession.getStreams().size());
Assert.assertTrue(rstLatch.await(5, TimeUnit.SECONDS));
Assert.assertEquals(0, clientSession.getStreams().size());
}
@Test
public void testRefusedStreamIgnoresData() throws Exception
{
final CountDownLatch synLatch = new CountDownLatch(1);
final CountDownLatch dataLatch = new CountDownLatch(1);
final CountDownLatch rstLatch = new CountDownLatch(1);
Session session = startClient(startServer(new ServerSessionFrameListener.Adapter()
{
@Override
public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
{
try
{
// Refuse the stream, we must ignore data frames
Assert.assertTrue(synLatch.await(5, TimeUnit.SECONDS));
stream.getSession().rst(new RstInfo(stream.getId(), StreamStatus.REFUSED_STREAM));
return new StreamFrameListener.Adapter()
{
@Override
public void onData(Stream stream, DataInfo dataInfo)
{
dataLatch.countDown();
}
};
}
catch (InterruptedException x)
{
x.printStackTrace();
return null;
}
}
}), new SessionFrameListener.Adapter()
{
@Override
public void onRst(Session session, RstInfo rstInfo)
{
rstLatch.countDown();
}
});
Stream stream = session.syn(new SynInfo(false), null).get(5, TimeUnit.SECONDS);
stream.data(new StringDataInfo("data", true), 5, TimeUnit.SECONDS, new Handler.Adapter<Void>()
{
@Override
public void completed(Void context)
{
synLatch.countDown();
}
});
Assert.assertTrue(rstLatch.await(5, TimeUnit.SECONDS));
Assert.assertFalse(dataLatch.await(1, TimeUnit.SECONDS));
}
}

View File

@ -0,0 +1,79 @@
package org.eclipse.jetty.spdy;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.spdy.api.Headers;
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.StreamStatus;
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.ControlFrameType;
import org.eclipse.jetty.spdy.frames.RstStreamFrame;
import org.eclipse.jetty.spdy.frames.SynStreamFrame;
import org.eclipse.jetty.spdy.generator.Generator;
import org.eclipse.jetty.spdy.parser.Parser;
import org.junit.Assert;
import org.junit.Test;
public class UnsupportedVersionTest extends AbstractTest
{
@Test
public void testSynWithUnsupportedVersion() throws Exception
{
final CountDownLatch synLatch = new CountDownLatch(1);
InetSocketAddress address = startServer(new ServerSessionFrameListener.Adapter()
{
@Override
public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
{
synLatch.countDown();
return null;
}
@Override
public void onException(Throwable x)
{
// Suppress exception logging for this test
}
});
SynStreamFrame frame = new SynStreamFrame(SPDY.V2, SynInfo.FLAG_CLOSE, 1, 0, (byte)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
buffer.putShort(0, (short)0x8001);
SocketChannel channel = SocketChannel.open(address);
channel.write(buffer);
Assert.assertFalse(buffer.hasRemaining());
Assert.assertFalse(synLatch.await(1, TimeUnit.SECONDS));
buffer = ByteBuffer.allocate(1024);
channel.read(buffer);
buffer.flip();
final CountDownLatch rstLatch = new CountDownLatch(1);
Parser parser = new Parser(new StandardCompressionFactory.StandardDecompressor());
parser.addListener(new Parser.Listener.Adapter()
{
@Override
public void onControlFrame(ControlFrame frame)
{
Assert.assertSame(ControlFrameType.RST_STREAM, frame.getType());
Assert.assertEquals(StreamStatus.UNSUPPORTED_VERSION.getCode(frame.getVersion()), ((RstStreamFrame)frame).getStatusCode());
rstLatch.countDown();
}
});
parser.parse(buffer);
Assert.assertTrue(rstLatch.await(5, TimeUnit.SECONDS));
}
}

View File

@ -232,10 +232,15 @@ public class CertificateValidator
}
// Enable On-Line Certificate Status Protocol (OCSP) support
if (_enableOCSP)
{
Security.setProperty("ocsp.enable","true");
}
// Enable Certificate Revocation List Distribution Points (CRLDP) support
if (_enableCRLDP)
{
System.setProperty("com.sun.security.enableCRLDP","true");
}
// Build certification path
CertPathBuilderResult buildResult = CertPathBuilder.getInstance("PKIX").build(pbParams);