Merge branch 'master' into release-9
This commit is contained in:
commit
c28525ab34
|
@ -18,6 +18,7 @@
|
|||
|
||||
package org.eclipse.jetty.annotations;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
|
@ -770,8 +771,8 @@ public class AnnotationParser
|
|||
else
|
||||
{
|
||||
//we've already verified the directories, so just verify the class file name
|
||||
String filename = res.getFile().getName();
|
||||
if (isValidClassFileName(filename))
|
||||
File file = res.getFile();
|
||||
if (isValidClassFileName((file==null?null:file.getName())))
|
||||
{
|
||||
String name = res.getName();
|
||||
if ((resolver == null)|| (!resolver.isExcluded(name) && (!isParsed(name) || resolver.shouldOverride(name))))
|
||||
|
@ -782,6 +783,10 @@ public class AnnotationParser
|
|||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (LOG.isDebugEnabled()) LOG.debug("Skipping scan on invalid file {}", res);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.io.UnsupportedEncodingException;
|
|||
import java.net.URI;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.UnsupportedCharsetException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
|
@ -43,6 +44,7 @@ import org.eclipse.jetty.client.api.Request;
|
|||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.client.util.FutureResponseListener;
|
||||
import org.eclipse.jetty.client.util.PathContentProvider;
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
|
@ -54,7 +56,7 @@ public class HttpRequest implements Request
|
|||
private static final AtomicLong ids = new AtomicLong();
|
||||
|
||||
private final HttpFields headers = new HttpFields();
|
||||
private final Fields params = new Fields();
|
||||
private final Fields params = new Fields(true);
|
||||
private final Map<String, Object> attributes = new HashMap<>();
|
||||
private final List<RequestListener> requestListeners = new ArrayList<>();
|
||||
private final List<Response.ResponseListener> responseListeners = new ArrayList<>();
|
||||
|
@ -295,93 +297,184 @@ public class HttpRequest implements Request
|
|||
}
|
||||
|
||||
@Override
|
||||
public Request onRequestQueued(QueuedListener listener)
|
||||
public Request onRequestQueued(final QueuedListener listener)
|
||||
{
|
||||
this.requestListeners.add(listener);
|
||||
this.requestListeners.add(new QueuedListener()
|
||||
{
|
||||
@Override
|
||||
public void onQueued(Request request)
|
||||
{
|
||||
listener.onQueued(request);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request onRequestBegin(BeginListener listener)
|
||||
public Request onRequestBegin(final BeginListener listener)
|
||||
{
|
||||
this.requestListeners.add(listener);
|
||||
this.requestListeners.add(new BeginListener()
|
||||
{
|
||||
@Override
|
||||
public void onBegin(Request request)
|
||||
{
|
||||
listener.onBegin(request);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request onRequestHeaders(HeadersListener listener)
|
||||
public Request onRequestHeaders(final HeadersListener listener)
|
||||
{
|
||||
this.requestListeners.add(listener);
|
||||
this.requestListeners.add(new HeadersListener()
|
||||
{
|
||||
@Override
|
||||
public void onHeaders(Request request)
|
||||
{
|
||||
listener.onHeaders(request);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request onRequestCommit(CommitListener listener)
|
||||
public Request onRequestCommit(final CommitListener listener)
|
||||
{
|
||||
this.requestListeners.add(listener);
|
||||
this.requestListeners.add(new CommitListener()
|
||||
{
|
||||
@Override
|
||||
public void onCommit(Request request)
|
||||
{
|
||||
listener.onCommit(request);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request onRequestContent(ContentListener listener)
|
||||
public Request onRequestContent(final ContentListener listener)
|
||||
{
|
||||
this.requestListeners.add(listener);
|
||||
this.requestListeners.add(new ContentListener()
|
||||
{
|
||||
@Override
|
||||
public void onContent(Request request, ByteBuffer content)
|
||||
{
|
||||
listener.onContent(request, content);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request onRequestSuccess(SuccessListener listener)
|
||||
public Request onRequestSuccess(final SuccessListener listener)
|
||||
{
|
||||
this.requestListeners.add(listener);
|
||||
this.requestListeners.add(new SuccessListener()
|
||||
{
|
||||
@Override
|
||||
public void onSuccess(Request request)
|
||||
{
|
||||
listener.onSuccess(request);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request onRequestFailure(FailureListener listener)
|
||||
public Request onRequestFailure(final FailureListener listener)
|
||||
{
|
||||
this.requestListeners.add(listener);
|
||||
this.requestListeners.add(new FailureListener()
|
||||
{
|
||||
@Override
|
||||
public void onFailure(Request request, Throwable failure)
|
||||
{
|
||||
listener.onFailure(request, failure);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request onResponseBegin(Response.BeginListener listener)
|
||||
public Request onResponseBegin(final Response.BeginListener listener)
|
||||
{
|
||||
this.responseListeners.add(listener);
|
||||
this.responseListeners.add(new Response.BeginListener()
|
||||
{
|
||||
@Override
|
||||
public void onBegin(Response response)
|
||||
{
|
||||
listener.onBegin(response);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request onResponseHeader(Response.HeaderListener listener)
|
||||
public Request onResponseHeader(final Response.HeaderListener listener)
|
||||
{
|
||||
this.responseListeners.add(listener);
|
||||
this.responseListeners.add(new Response.HeaderListener()
|
||||
{
|
||||
@Override
|
||||
public boolean onHeader(Response response, HttpField field)
|
||||
{
|
||||
return listener.onHeader(response, field);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request onResponseHeaders(Response.HeadersListener listener)
|
||||
public Request onResponseHeaders(final Response.HeadersListener listener)
|
||||
{
|
||||
this.responseListeners.add(listener);
|
||||
this.responseListeners.add(new Response.HeadersListener()
|
||||
{
|
||||
@Override
|
||||
public void onHeaders(Response response)
|
||||
{
|
||||
listener.onHeaders(response);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request onResponseContent(Response.ContentListener listener)
|
||||
public Request onResponseContent(final Response.ContentListener listener)
|
||||
{
|
||||
this.responseListeners.add(listener);
|
||||
this.responseListeners.add(new Response.ContentListener()
|
||||
{
|
||||
@Override
|
||||
public void onContent(Response response, ByteBuffer content)
|
||||
{
|
||||
listener.onContent(response, content);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request onResponseSuccess(Response.SuccessListener listener)
|
||||
public Request onResponseSuccess(final Response.SuccessListener listener)
|
||||
{
|
||||
this.responseListeners.add(listener);
|
||||
this.responseListeners.add(new Response.SuccessListener()
|
||||
{
|
||||
@Override
|
||||
public void onSuccess(Response response)
|
||||
{
|
||||
listener.onSuccess(response);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request onResponseFailure(Response.FailureListener listener)
|
||||
public Request onResponseFailure(final Response.FailureListener listener)
|
||||
{
|
||||
this.responseListeners.add(listener);
|
||||
this.responseListeners.add(new Response.FailureListener()
|
||||
{
|
||||
@Override
|
||||
public void onFailure(Response response, Throwable failure)
|
||||
{
|
||||
listener.onFailure(response, failure);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -360,7 +360,7 @@ public class HttpSender implements AsyncContentProvider.Listener
|
|||
if (!commit(request))
|
||||
return false;
|
||||
|
||||
if (content != null)
|
||||
if (content != null && content.hasRemaining())
|
||||
{
|
||||
RequestNotifier notifier = connection.getDestination().getRequestNotifier();
|
||||
notifier.notifyContent(request, content);
|
||||
|
|
|
@ -64,21 +64,25 @@ public abstract class BufferingResponseListener extends Response.Listener.Empty
|
|||
HttpFields headers = response.getHeaders();
|
||||
long length = headers.getLongField(HttpHeader.CONTENT_LENGTH.asString());
|
||||
if (length > maxLength)
|
||||
response.abort(new IllegalArgumentException("Buffering capacity exceeded"));
|
||||
|
||||
String contentType = headers.get(HttpHeader.CONTENT_TYPE);
|
||||
if (contentType != null)
|
||||
{
|
||||
String charset = "charset=";
|
||||
int index = contentType.toLowerCase(Locale.ENGLISH).indexOf(charset);
|
||||
if (index > 0)
|
||||
response.abort(new IllegalArgumentException("Buffering capacity exceeded"));
|
||||
}
|
||||
else
|
||||
{
|
||||
String contentType = headers.get(HttpHeader.CONTENT_TYPE);
|
||||
if (contentType != null)
|
||||
{
|
||||
String encoding = contentType.substring(index + charset.length());
|
||||
// Sometimes charsets arrive with an ending semicolon
|
||||
index = encoding.indexOf(';');
|
||||
String charset = "charset=";
|
||||
int index = contentType.toLowerCase(Locale.ENGLISH).indexOf(charset);
|
||||
if (index > 0)
|
||||
encoding = encoding.substring(0, index);
|
||||
this.encoding = encoding;
|
||||
{
|
||||
String encoding = contentType.substring(index + charset.length());
|
||||
// Sometimes charsets arrive with an ending semicolon
|
||||
index = encoding.indexOf(';');
|
||||
if (index > 0)
|
||||
encoding = encoding.substring(0, index);
|
||||
this.encoding = encoding;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -88,12 +92,16 @@ public abstract class BufferingResponseListener extends Response.Listener.Empty
|
|||
{
|
||||
long newLength = buffer.length + content.remaining();
|
||||
if (newLength > maxLength)
|
||||
{
|
||||
response.abort(new IllegalArgumentException("Buffering capacity exceeded"));
|
||||
|
||||
byte[] newBuffer = new byte[(int)newLength];
|
||||
System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
|
||||
content.get(newBuffer, buffer.length, content.remaining());
|
||||
buffer = newBuffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] newBuffer = new byte[(int)newLength];
|
||||
System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
|
||||
content.get(newBuffer, buffer.length, content.remaining());
|
||||
buffer = newBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
package org.eclipse.jetty.client.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Iterator;
|
||||
|
@ -25,6 +26,8 @@ import java.util.NoSuchElementException;
|
|||
|
||||
import org.eclipse.jetty.client.api.ContentProvider;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
/**
|
||||
* A {@link ContentProvider} for an {@link InputStream}.
|
||||
|
@ -33,19 +36,26 @@ import org.eclipse.jetty.util.BufferUtil;
|
|||
* Invocations to the {@link #iterator()} method after the first will return an "empty" iterator
|
||||
* because the stream has been consumed on the first invocation.
|
||||
* <p />
|
||||
* It is possible to specify, at the constructor, a buffer size used to read content from the
|
||||
* stream, by default 4096 bytes.
|
||||
* <p />
|
||||
* However, it is possible for subclasses to override {@link #onRead(byte[], int, int)} to copy
|
||||
* the content read from the stream to another location (for example a file), and be able to
|
||||
* support multiple invocations of {@link #iterator()}, returning the iterator provided by this
|
||||
* class on the first invocation, and an iterator on the bytes copied to the other location
|
||||
* for subsequent invocations.
|
||||
* <p />
|
||||
* It is possible to specify, at the constructor, a buffer size used to read content from the
|
||||
* stream, by default 4096 bytes.
|
||||
* <p />
|
||||
* The {@link InputStream} passed to the constructor is by default closed when is it fully
|
||||
* consumed (or when an exception is thrown while reading it), unless otherwise specified
|
||||
* to the {@link #InputStreamContentProvider(java.io.InputStream, int, boolean) constructor}.
|
||||
*/
|
||||
public class InputStreamContentProvider implements ContentProvider
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(InputStreamContentProvider.class);
|
||||
|
||||
private final InputStream stream;
|
||||
private final int bufferSize;
|
||||
private final boolean autoClose;
|
||||
|
||||
public InputStreamContentProvider(InputStream stream)
|
||||
{
|
||||
|
@ -53,9 +63,15 @@ public class InputStreamContentProvider implements ContentProvider
|
|||
}
|
||||
|
||||
public InputStreamContentProvider(InputStream stream, int bufferSize)
|
||||
{
|
||||
this(stream, bufferSize, true);
|
||||
}
|
||||
|
||||
public InputStreamContentProvider(InputStream stream, int bufferSize, boolean autoClose)
|
||||
{
|
||||
this.stream = stream;
|
||||
this.bufferSize = bufferSize;
|
||||
this.autoClose = autoClose;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -107,6 +123,7 @@ public class InputStreamContentProvider implements ContentProvider
|
|||
}
|
||||
else if (read < 0)
|
||||
{
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
|
@ -122,6 +139,7 @@ public class InputStreamContentProvider implements ContentProvider
|
|||
failure = x;
|
||||
// Signal we have more content to cause a call to
|
||||
// next() which will throw NoSuchElementException.
|
||||
close();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -145,6 +163,21 @@ public class InputStreamContentProvider implements ContentProvider
|
|||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private void close()
|
||||
{
|
||||
if (autoClose)
|
||||
{
|
||||
try
|
||||
{
|
||||
stream.close();
|
||||
}
|
||||
catch (IOException x)
|
||||
{
|
||||
LOG.ignore(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,11 @@ public class StringContentProvider extends BytesContentProvider
|
|||
|
||||
public StringContentProvider(String content, String encoding)
|
||||
{
|
||||
super(content.getBytes(Charset.forName(encoding)));
|
||||
this(content, Charset.forName(encoding));
|
||||
}
|
||||
|
||||
public StringContentProvider(String content, Charset charset)
|
||||
{
|
||||
super(content.getBytes(charset));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,9 @@ import java.nio.channels.UnresolvedAddressException;
|
|||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
@ -52,6 +54,7 @@ import org.eclipse.jetty.client.api.Response;
|
|||
import org.eclipse.jetty.client.api.Result;
|
||||
import org.eclipse.jetty.client.util.BytesContentProvider;
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
|
@ -824,4 +827,181 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
|||
});
|
||||
Assert.assertTrue(latch.await(10, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomUserAgent() throws Exception
|
||||
{
|
||||
final String userAgent = "Test/1.0";
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
ArrayList<String> userAgents = Collections.list(request.getHeaders("User-Agent"));
|
||||
Assert.assertEquals(1, userAgents.size());
|
||||
Assert.assertEquals(userAgent, userAgents.get(0));
|
||||
}
|
||||
});
|
||||
|
||||
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.agent(userAgent)
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
|
||||
response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.header(HttpHeader.USER_AGENT, userAgent)
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestListenerForMultipleEventsIsInvokedOncePerEvent() throws Exception
|
||||
{
|
||||
start(new EmptyServerHandler());
|
||||
|
||||
final AtomicInteger counter = new AtomicInteger();
|
||||
Request.Listener listener = new Request.Listener()
|
||||
{
|
||||
@Override
|
||||
public void onQueued(Request request)
|
||||
{
|
||||
counter.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBegin(Request request)
|
||||
{
|
||||
counter.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHeaders(Request request)
|
||||
{
|
||||
counter.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCommit(Request request)
|
||||
{
|
||||
counter.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContent(Request request, ByteBuffer content)
|
||||
{
|
||||
// Should not be invoked
|
||||
counter.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Request request, Throwable failure)
|
||||
{
|
||||
// Should not be invoked
|
||||
counter.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Request request)
|
||||
{
|
||||
counter.incrementAndGet();
|
||||
}
|
||||
};
|
||||
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.onRequestQueued(listener)
|
||||
.onRequestBegin(listener)
|
||||
.onRequestHeaders(listener)
|
||||
.onRequestCommit(listener)
|
||||
.onRequestContent(listener)
|
||||
.onRequestSuccess(listener)
|
||||
.onRequestFailure(listener)
|
||||
.listener(listener)
|
||||
.send();
|
||||
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
int expectedEventsTriggeredByOnRequestXXXListeners = 5;
|
||||
int expectedEventsTriggeredByListener = 5;
|
||||
int expected = expectedEventsTriggeredByOnRequestXXXListeners + expectedEventsTriggeredByListener;
|
||||
Assert.assertEquals(expected, counter.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResponseListenerForMultipleEventsIsInvokedOncePerEvent() throws Exception
|
||||
{
|
||||
start(new EmptyServerHandler());
|
||||
|
||||
final AtomicInteger counter = new AtomicInteger();
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
Response.Listener listener = new Response.Listener()
|
||||
{
|
||||
@Override
|
||||
public void onBegin(Response response)
|
||||
{
|
||||
counter.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onHeader(Response response, HttpField field)
|
||||
{
|
||||
// Number of header may vary, so don't count
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHeaders(Response response)
|
||||
{
|
||||
counter.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContent(Response response, ByteBuffer content)
|
||||
{
|
||||
// Should not be invoked
|
||||
counter.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Response response)
|
||||
{
|
||||
counter.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Response response, Throwable failure)
|
||||
{
|
||||
// Should not be invoked
|
||||
counter.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
Assert.assertEquals(200, result.getResponse().getStatus());
|
||||
counter.incrementAndGet();
|
||||
latch.countDown();
|
||||
}
|
||||
};
|
||||
client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.onResponseBegin(listener)
|
||||
.onResponseHeader(listener)
|
||||
.onResponseHeaders(listener)
|
||||
.onResponseContent(listener)
|
||||
.onResponseSuccess(listener)
|
||||
.onResponseFailure(listener)
|
||||
.send(listener);
|
||||
|
||||
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
int expectedEventsTriggeredByOnResponseXXXListeners = 3;
|
||||
int expectedEventsTriggeredByCompletionListener = 4;
|
||||
int expected = expectedEventsTriggeredByOnResponseXXXListeners + expectedEventsTriggeredByCompletionListener;
|
||||
Assert.assertEquals(expected, counter.get());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -312,4 +312,30 @@ public class HttpClientURITest extends AbstractHttpClientServerTest
|
|||
|
||||
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCaseSensitiveParameterName() throws Exception
|
||||
{
|
||||
final String name1 = "a";
|
||||
final String name2 = "A";
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
Assert.assertEquals(name1, request.getParameter(name1));
|
||||
Assert.assertEquals(name2, request.getParameter(name2));
|
||||
}
|
||||
});
|
||||
|
||||
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.path("/path?" + name1 + "=" + name1)
|
||||
.param(name2, name2)
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ public class SslBytesClientTest extends SslBytesTest
|
|||
client.start();
|
||||
|
||||
SSLContext sslContext = sslContextFactory.getSslContext();
|
||||
acceptor = (SSLServerSocket)sslContext.getServerSocketFactory().createServerSocket(43191);
|
||||
acceptor = (SSLServerSocket)sslContext.getServerSocketFactory().createServerSocket(0);
|
||||
|
||||
int serverPort = acceptor.getLocalPort();
|
||||
|
||||
|
|
|
@ -84,6 +84,7 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
private ExecutorService threadPool;
|
||||
private Server server;
|
||||
private SslContextFactory sslContextFactory;
|
||||
private int serverPort;
|
||||
private SSLContext sslContext;
|
||||
private SimpleProxy proxy;
|
||||
private Runnable idleHook;
|
||||
|
@ -211,7 +212,7 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
}
|
||||
});
|
||||
server.start();
|
||||
int serverPort = connector.getLocalPort();
|
||||
serverPort = connector.getLocalPort();
|
||||
|
||||
sslContext = sslContextFactory.getSslContext();
|
||||
|
||||
|
@ -292,6 +293,88 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
closeClient(client);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandshakeWithResumedSessionThenClose() throws Exception
|
||||
{
|
||||
// First socket will establish the SSL session
|
||||
SSLSocket client1 = newClient();
|
||||
SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow();
|
||||
client1.startHandshake();
|
||||
client1.close();
|
||||
Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
|
||||
int proxyPort = proxy.getPort();
|
||||
proxy.stop();
|
||||
|
||||
proxy = new SimpleProxy(threadPool, proxyPort, "localhost", serverPort);
|
||||
proxy.start();
|
||||
logger.info("proxy:{} <==> server:{}", proxy.getPort(), serverPort);
|
||||
|
||||
final SSLSocket client2 = newClient(proxy);
|
||||
|
||||
Future<Object> handshake = threadPool.submit(new Callable<Object>()
|
||||
{
|
||||
@Override
|
||||
public Object call() throws Exception
|
||||
{
|
||||
client2.startHandshake();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Client Hello with SessionID
|
||||
TLSRecord record = proxy.readFromClient();
|
||||
Assert.assertNotNull(record);
|
||||
proxy.flushToServer(record);
|
||||
|
||||
// Server Hello
|
||||
record = proxy.readFromServer();
|
||||
Assert.assertNotNull(record);
|
||||
proxy.flushToClient(record);
|
||||
|
||||
// Change Cipher Spec
|
||||
record = proxy.readFromServer();
|
||||
Assert.assertNotNull(record);
|
||||
proxy.flushToClient(record);
|
||||
|
||||
// Server Done
|
||||
record = proxy.readFromServer();
|
||||
Assert.assertNotNull(record);
|
||||
proxy.flushToClient(record);
|
||||
|
||||
// Client Key Exchange
|
||||
record = proxy.readFromClient();
|
||||
Assert.assertNotNull(record);
|
||||
// Client Done
|
||||
TLSRecord doneRecord = proxy.readFromClient();
|
||||
Assert.assertNotNull(doneRecord);
|
||||
// Close
|
||||
client2.close();
|
||||
TLSRecord closeRecord = proxy.readFromClient();
|
||||
Assert.assertNotNull(closeRecord);
|
||||
Assert.assertEquals(TLSRecord.Type.ALERT, closeRecord.getType());
|
||||
// Flush to server Client Key Exchange + Client Done + Close in one chunk
|
||||
byte[] recordBytes = record.getBytes();
|
||||
byte[] doneBytes = doneRecord.getBytes();
|
||||
byte[] closeRecordBytes = closeRecord.getBytes();
|
||||
byte[] chunk = new byte[recordBytes.length + doneBytes.length + closeRecordBytes.length];
|
||||
System.arraycopy(recordBytes, 0, chunk, 0, recordBytes.length);
|
||||
System.arraycopy(doneBytes, 0, chunk, recordBytes.length, doneBytes.length);
|
||||
System.arraycopy(closeRecordBytes, 0, chunk, recordBytes.length + doneBytes.length, closeRecordBytes.length);
|
||||
proxy.flushToServer(0, chunk);
|
||||
// Close the raw socket
|
||||
proxy.flushToServer(null);
|
||||
|
||||
// Expect the server to send a FIN as well
|
||||
record = proxy.readFromServer();
|
||||
Assert.assertNull(record);
|
||||
|
||||
// Check that we did not spin
|
||||
TimeUnit.MILLISECONDS.sleep(500);
|
||||
Assert.assertThat(sslFills.get(), Matchers.lessThan(20));
|
||||
Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20));
|
||||
Assert.assertThat(httpParses.get(), Matchers.lessThan(20));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandshakeWithSplitBoundary() throws Exception
|
||||
{
|
||||
|
@ -1794,6 +1877,11 @@ public class SslBytesServerTest extends SslBytesTest
|
|||
}
|
||||
|
||||
private SSLSocket newClient() throws IOException, InterruptedException
|
||||
{
|
||||
return newClient(proxy);
|
||||
}
|
||||
|
||||
private SSLSocket newClient(SimpleProxy proxy) throws IOException, InterruptedException
|
||||
{
|
||||
SSLSocket client = (SSLSocket)sslContext.getSocketFactory().createSocket("localhost", proxy.getPort());
|
||||
client.setUseClientMode(true);
|
||||
|
|
|
@ -105,6 +105,7 @@ public abstract class SslBytesTest
|
|||
{
|
||||
private final CountDownLatch latch = new CountDownLatch(1);
|
||||
private final ExecutorService threadPool;
|
||||
private final int proxyPort;
|
||||
private final String serverHost;
|
||||
private final int serverPort;
|
||||
private volatile ServerSocket serverSocket;
|
||||
|
@ -112,15 +113,21 @@ public abstract class SslBytesTest
|
|||
private volatile Socket client;
|
||||
|
||||
public SimpleProxy(ExecutorService threadPool, String serverHost, int serverPort)
|
||||
{
|
||||
this(threadPool, 0, serverHost, serverPort);
|
||||
}
|
||||
|
||||
public SimpleProxy(ExecutorService threadPool, int proxyPort, String serverHost, int serverPort)
|
||||
{
|
||||
this.threadPool = threadPool;
|
||||
this.proxyPort = proxyPort;
|
||||
this.serverHost = serverHost;
|
||||
this.serverPort = serverPort;
|
||||
}
|
||||
|
||||
public void start() throws Exception
|
||||
{
|
||||
serverSocket = new ServerSocket(0);
|
||||
serverSocket = new ServerSocket(proxyPort);
|
||||
Thread acceptor = new Thread(this);
|
||||
acceptor.start();
|
||||
server = new Socket(serverHost, serverPort);
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
package org.eclipse.jetty.http;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
@ -39,6 +40,7 @@ import org.eclipse.jetty.util.URIUtil;
|
|||
* /foo/* - a prefix path specification (must end '/*').
|
||||
* *.ext - a suffix path specification.
|
||||
* / - the default path specification.
|
||||
* "" - the / path specification
|
||||
* </PRE>
|
||||
* Matching is performed in the following order <NL>
|
||||
* <LI>Exact match.
|
||||
|
@ -267,20 +269,22 @@ public class PathMap<O> extends HashMap<String,O>
|
|||
/** Get all entries matched by the path.
|
||||
* Best match first.
|
||||
* @param path Path to match
|
||||
* @return LazyList of Map.Entry instances key=pathSpec
|
||||
* @return List of Map.Entry instances key=pathSpec
|
||||
*/
|
||||
public Object getLazyMatches(String path)
|
||||
public List<? extends Map.Entry<String,O>> getMatches(String path)
|
||||
{
|
||||
MappedEntry<O> entry;
|
||||
Object entries=null;
|
||||
List<MappedEntry<O>> entries=new ArrayList<>();
|
||||
|
||||
if (path==null)
|
||||
return LazyList.getList(entries);
|
||||
return entries;
|
||||
if (path.length()==0)
|
||||
return _defaultSingletonList;
|
||||
|
||||
// try exact match
|
||||
entry=_exactMap.get(path);
|
||||
if (entry!=null)
|
||||
entries=LazyList.add(entries,entry);
|
||||
entries.add(entry);
|
||||
|
||||
// prefix search
|
||||
int l=path.length();
|
||||
|
@ -293,14 +297,14 @@ public class PathMap<O> extends HashMap<String,O>
|
|||
break;
|
||||
String key = entry.getKey();
|
||||
if (key.length()-2>=path.length() || path.charAt(key.length()-2)=='/')
|
||||
entries=LazyList.add(entries,entry);
|
||||
entries.add(entry);
|
||||
|
||||
i=key.length()-3;
|
||||
}
|
||||
|
||||
// Prefix Default
|
||||
if (_prefixDefault!=null)
|
||||
entries=LazyList.add(entries,_prefixDefault);
|
||||
entries.add(_prefixDefault);
|
||||
|
||||
// Extension search
|
||||
i=0;
|
||||
|
@ -309,32 +313,24 @@ public class PathMap<O> extends HashMap<String,O>
|
|||
{
|
||||
entry=suffix_map.get(path,i+1,l-i-1);
|
||||
if (entry!=null)
|
||||
entries=LazyList.add(entries,entry);
|
||||
entries.add(entry);
|
||||
}
|
||||
|
||||
// root match
|
||||
if ("/".equals(path))
|
||||
{
|
||||
entry=_exactMap.get("");
|
||||
if (entry!=null)
|
||||
entries.add(entry);
|
||||
}
|
||||
|
||||
// Default
|
||||
if (_default!=null)
|
||||
{
|
||||
// Optimization for just the default
|
||||
if (entries==null)
|
||||
return _defaultSingletonList;
|
||||
|
||||
entries=LazyList.add(entries,_default);
|
||||
}
|
||||
entries.add(_default);
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------- */
|
||||
/** Get all entries matched by the path.
|
||||
* Best match first.
|
||||
* @param path Path to match
|
||||
* @return List of Map.Entry instances key=pathSpec
|
||||
*/
|
||||
public List<Map.Entry<String,O>> getMatches(String path)
|
||||
{
|
||||
return LazyList.getList(getLazyMatches(path));
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------- */
|
||||
/** Return whether the path matches any entries in the PathMap,
|
||||
|
@ -381,6 +377,7 @@ public class PathMap<O> extends HashMap<String,O>
|
|||
_suffixMap=new ArrayTernaryTrie<>(false);
|
||||
_default=null;
|
||||
_defaultSingletonList=null;
|
||||
_prefixDefault=null;
|
||||
super.clear();
|
||||
}
|
||||
|
||||
|
|
|
@ -65,6 +65,7 @@ public class PathMapTest
|
|||
{ "/animal/path.gz", "5"},
|
||||
{ "/Other/path", "8"},
|
||||
{ "/\u20ACuro/path", "11"},
|
||||
{ "/", "10"},
|
||||
};
|
||||
|
||||
for (String[] test : tests)
|
||||
|
@ -78,8 +79,8 @@ public class PathMapTest
|
|||
p.getMatches("/animal/bird/path.tar.gz").toString());
|
||||
assertEquals("Dir matches", "[/animal/fish/*=4, /animal/*=5, /=8]", p.getMatches("/animal/fish/").toString());
|
||||
assertEquals("Dir matches", "[/animal/fish/*=4, /animal/*=5, /=8]", p.getMatches("/animal/fish").toString());
|
||||
assertEquals("Dir matches", "[/=8]", p.getMatches("/").toString());
|
||||
assertEquals("Dir matches", "[=10, /=8]", p.getMatches("").toString());
|
||||
assertEquals("Root matches", "[=10, /=8]",p.getMatches("/").toString());
|
||||
assertEquals("Dir matches", "[/=8]", p.getMatches("").toString());
|
||||
|
||||
assertEquals("pathMatch exact", "/Foo/bar", PathMap.pathMatch("/Foo/bar", "/Foo/bar"));
|
||||
assertEquals("pathMatch prefix", "/Foo", PathMap.pathMatch("/Foo/*", "/Foo/bar"));
|
||||
|
|
|
@ -189,12 +189,12 @@ public class SslConnection extends AbstractConnection
|
|||
// filling.
|
||||
|
||||
if (DEBUG)
|
||||
LOG.debug("onFillable enter {}", getEndPoint());
|
||||
LOG.debug("onFillable enter {}", _decryptedEndPoint);
|
||||
|
||||
// We have received a close handshake, close the end point to send FIN.
|
||||
if (_decryptedEndPoint.isInputShutdown())
|
||||
getEndPoint().close();
|
||||
|
||||
_decryptedEndPoint.close();
|
||||
|
||||
// wake up whoever is doing the fill or the flush so they can
|
||||
// do all the filling, unwrapping, wrapping and flushing
|
||||
_decryptedEndPoint.getFillInterest().fillable();
|
||||
|
@ -210,7 +210,7 @@ public class SslConnection extends AbstractConnection
|
|||
}
|
||||
|
||||
if (DEBUG)
|
||||
LOG.debug("onFillable exit {}", getEndPoint());
|
||||
LOG.debug("onFillable exit {}", _decryptedEndPoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -312,7 +312,7 @@ public class SslConnection extends AbstractConnection
|
|||
fail_filler = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final boolean filler_failed=fail_filler;
|
||||
|
||||
getExecutor().execute(new Runnable()
|
||||
|
@ -508,142 +508,142 @@ public class SslConnection extends AbstractConnection
|
|||
int net_filled = getEndPoint().fill(_encryptedInput);
|
||||
if (DEBUG)
|
||||
LOG.debug("{} filled {} encrypted bytes", SslConnection.this, net_filled);
|
||||
if (net_filled > 0)
|
||||
_underFlown = false;
|
||||
|
||||
// Let's try the SSL thang even if we have no net data because in that
|
||||
// case we want to fall through to the handshake handling
|
||||
int pos = BufferUtil.flipToFill(app_in);
|
||||
|
||||
SSLEngineResult unwrapResult = _sslEngine.unwrap(_encryptedInput, app_in);
|
||||
|
||||
BufferUtil.flipToFlush(app_in, pos);
|
||||
if (DEBUG)
|
||||
LOG.debug("{} unwrap {}", SslConnection.this, unwrapResult);
|
||||
|
||||
Status unwrapResultStatus = unwrapResult.getStatus();
|
||||
HandshakeStatus unwrapHandshakeStatus = unwrapResult.getHandshakeStatus();
|
||||
HandshakeStatus handshakeStatus = _sslEngine.getHandshakeStatus();
|
||||
|
||||
// and deal with the results
|
||||
switch (unwrapResultStatus)
|
||||
decryption: while (true)
|
||||
{
|
||||
case BUFFER_OVERFLOW:
|
||||
throw new IllegalStateException();
|
||||
// Let's unwrap even if we have no net data because in that
|
||||
// case we want to fall through to the handshake handling
|
||||
int pos = BufferUtil.flipToFill(app_in);
|
||||
SSLEngineResult unwrapResult = _sslEngine.unwrap(_encryptedInput, app_in);
|
||||
BufferUtil.flipToFlush(app_in, pos);
|
||||
if (DEBUG)
|
||||
LOG.debug("{} unwrap {}", SslConnection.this, unwrapResult);
|
||||
|
||||
case CLOSED:
|
||||
// Dang! we have to care about the handshake state specially for close
|
||||
switch (handshakeStatus)
|
||||
{
|
||||
case NOT_HANDSHAKING:
|
||||
// We were not handshaking, so just tell the app we are closed
|
||||
return -1;
|
||||
HandshakeStatus handshakeStatus = _sslEngine.getHandshakeStatus();
|
||||
HandshakeStatus unwrapHandshakeStatus = unwrapResult.getHandshakeStatus();
|
||||
Status unwrapResultStatus = unwrapResult.getStatus();
|
||||
|
||||
case NEED_TASK:
|
||||
// run the task
|
||||
_sslEngine.getDelegatedTask().run();
|
||||
continue;
|
||||
_underFlown = unwrapResultStatus == Status.BUFFER_UNDERFLOW;
|
||||
|
||||
case NEED_WRAP:
|
||||
// we need to send some handshake data (probably to send a close handshake).
|
||||
// but that will not enable any extra data to fill, so we just return -1
|
||||
// The wrapping can be done by any output drivers doing flushing or shutdown output.
|
||||
return -1;
|
||||
}
|
||||
throw new IllegalStateException();
|
||||
|
||||
default:
|
||||
if (unwrapHandshakeStatus == HandshakeStatus.FINISHED && !_handshaken)
|
||||
{
|
||||
_handshaken = true;
|
||||
if (DEBUG)
|
||||
LOG.debug("{} handshake completed client-side", SslConnection.this);
|
||||
}
|
||||
|
||||
// Check whether renegotiation is allowed
|
||||
if (_handshaken && handshakeStatus != HandshakeStatus.NOT_HANDSHAKING && !isRenegotiationAllowed())
|
||||
{
|
||||
if (DEBUG)
|
||||
LOG.debug("{} renegotiation denied", SslConnection.this);
|
||||
if (_underFlown)
|
||||
{
|
||||
if (net_filled < 0)
|
||||
closeInbound();
|
||||
return -1;
|
||||
}
|
||||
if (net_filled <= 0)
|
||||
return net_filled;
|
||||
}
|
||||
|
||||
if (unwrapResultStatus == Status.BUFFER_UNDERFLOW)
|
||||
_underFlown = true;
|
||||
|
||||
// If bytes were produced, don't bother with the handshake status;
|
||||
// pass the decrypted data to the application, which will perform
|
||||
// another call to fill() or flush().
|
||||
if (unwrapResult.bytesProduced() > 0)
|
||||
switch (unwrapResultStatus)
|
||||
{
|
||||
case CLOSED:
|
||||
{
|
||||
if (app_in == buffer)
|
||||
return unwrapResult.bytesProduced();
|
||||
return BufferUtil.flipPutFlip(_decryptedInput, buffer);
|
||||
}
|
||||
|
||||
// Dang! we have to care about the handshake state
|
||||
switch (handshakeStatus)
|
||||
{
|
||||
case NOT_HANDSHAKING:
|
||||
// we just didn't read anything.
|
||||
if (net_filled < 0)
|
||||
switch (handshakeStatus)
|
||||
{
|
||||
case NOT_HANDSHAKING:
|
||||
{
|
||||
closeInbound();
|
||||
// We were not handshaking, so just tell the app we are closed
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case NEED_TASK:
|
||||
// run the task
|
||||
_sslEngine.getDelegatedTask().run();
|
||||
continue;
|
||||
|
||||
case NEED_WRAP:
|
||||
// we need to send some handshake data
|
||||
|
||||
// if we are called from flush
|
||||
if (buffer == __FLUSH_CALLED_FILL)
|
||||
return 0; // let it do the wrapping
|
||||
|
||||
_fillRequiresFlushToProgress = true;
|
||||
flush(__FILL_CALLED_FLUSH);
|
||||
if (BufferUtil.isEmpty(_encryptedOutput))
|
||||
case NEED_TASK:
|
||||
{
|
||||
// the flush completed so continue
|
||||
_fillRequiresFlushToProgress = false;
|
||||
_sslEngine.getDelegatedTask().run();
|
||||
continue;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case NEED_UNWRAP:
|
||||
// if we just filled some net data
|
||||
if (net_filled < 0)
|
||||
case NEED_WRAP:
|
||||
{
|
||||
closeInbound();
|
||||
// We need to send some handshake data (probably the close handshake).
|
||||
// We return -1 so that the application can drive the close by flushing
|
||||
// or shutting down the output.
|
||||
return -1;
|
||||
}
|
||||
else if (net_filled > 0)
|
||||
default:
|
||||
{
|
||||
// maybe we will fill some more on a retry
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
}
|
||||
case BUFFER_UNDERFLOW:
|
||||
case OK:
|
||||
{
|
||||
if (unwrapHandshakeStatus == HandshakeStatus.FINISHED && !_handshaken)
|
||||
{
|
||||
_handshaken = true;
|
||||
if (DEBUG)
|
||||
LOG.debug("{} {} handshake completed", SslConnection.this,
|
||||
_sslEngine.getUseClientMode() ? "client-side" : "resumed session server-side");
|
||||
}
|
||||
|
||||
// Check whether renegotiation is allowed
|
||||
if (_handshaken && handshakeStatus != HandshakeStatus.NOT_HANDSHAKING && !isRenegotiationAllowed())
|
||||
{
|
||||
if (DEBUG)
|
||||
LOG.debug("{} renegotiation denied", SslConnection.this);
|
||||
closeInbound();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// If bytes were produced, don't bother with the handshake status;
|
||||
// pass the decrypted data to the application, which will perform
|
||||
// another call to fill() or flush().
|
||||
if (unwrapResult.bytesProduced() > 0)
|
||||
{
|
||||
if (app_in == buffer)
|
||||
return unwrapResult.bytesProduced();
|
||||
return BufferUtil.flipPutFlip(_decryptedInput, buffer);
|
||||
}
|
||||
|
||||
switch (handshakeStatus)
|
||||
{
|
||||
case NOT_HANDSHAKING:
|
||||
{
|
||||
if (_underFlown)
|
||||
break decryption;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
case NEED_TASK:
|
||||
{
|
||||
if (_encryptedInput.hasRemaining())
|
||||
_sslEngine.getDelegatedTask().run();
|
||||
continue;
|
||||
}
|
||||
case NEED_WRAP:
|
||||
{
|
||||
// If we are called from flush()
|
||||
// return to let it do the wrapping.
|
||||
if (buffer == __FLUSH_CALLED_FILL)
|
||||
return 0;
|
||||
|
||||
_fillRequiresFlushToProgress = true;
|
||||
flush(__FILL_CALLED_FLUSH);
|
||||
if (BufferUtil.isEmpty(_encryptedOutput))
|
||||
{
|
||||
// if there are more encrypted bytes,
|
||||
// then we need to unwrap more, we don't
|
||||
// care if net_filled is zero
|
||||
// The flush wrote all the encrypted bytes so continue to fill
|
||||
_fillRequiresFlushToProgress = false;
|
||||
continue;
|
||||
}
|
||||
// we need to wait for more net data
|
||||
return 0;
|
||||
else
|
||||
{
|
||||
// The flush did not complete, return from fill()
|
||||
// and let the write completion mechanism to kick in.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
case FINISHED:
|
||||
throw new IllegalStateException();
|
||||
case NEED_UNWRAP:
|
||||
{
|
||||
if (_underFlown)
|
||||
break decryption;
|
||||
continue;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -778,7 +778,7 @@ public class SslConnection extends AbstractConnection
|
|||
{
|
||||
_handshaken = true;
|
||||
if (DEBUG)
|
||||
LOG.debug("{} handshake completed server-side", SslConnection.this);
|
||||
LOG.debug("{} {} handshake completed", SslConnection.this, "server-side");
|
||||
}
|
||||
|
||||
HandshakeStatus handshakeStatus = _sslEngine.getHandshakeStatus();
|
||||
|
@ -824,7 +824,7 @@ public class SslConnection extends AbstractConnection
|
|||
if (handshakeStatus == HandshakeStatus.NEED_WRAP)
|
||||
continue;
|
||||
}
|
||||
return allConsumed&&BufferUtil.isEmpty(_encryptedOutput);
|
||||
return allConsumed && BufferUtil.isEmpty(_encryptedOutput);
|
||||
|
||||
case FINISHED:
|
||||
throw new IllegalStateException();
|
||||
|
@ -860,17 +860,18 @@ public class SslConnection extends AbstractConnection
|
|||
public void shutdownOutput()
|
||||
{
|
||||
boolean ishut = isInputShutdown();
|
||||
boolean oshut = isOutputShutdown();
|
||||
if (DEBUG)
|
||||
LOG.debug("{} shutdownOutput: oshut={}, ishut={}", SslConnection.this, isOutputShutdown(), ishut);
|
||||
LOG.debug("{} shutdownOutput: oshut={}, ishut={}", SslConnection.this, oshut, ishut);
|
||||
if (ishut)
|
||||
{
|
||||
// Aggressively close, since inbound close alert has already been processed
|
||||
// and the TLS specification allows to close the connection directly, which
|
||||
// is what most other implementations expect: a FIN rather than a TLS close
|
||||
// reply. If a TLS close reply is sent, most implementation send a RST.
|
||||
// reply. If a TLS close reply is sent, most implementations send a RST.
|
||||
getEndPoint().close();
|
||||
}
|
||||
else
|
||||
else if (!oshut)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -895,6 +896,8 @@ public class SslConnection extends AbstractConnection
|
|||
@Override
|
||||
public void close()
|
||||
{
|
||||
// First send the TLS Close Alert, then the FIN
|
||||
shutdownOutput();
|
||||
getEndPoint().close();
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ import javax.security.auth.callback.CallbackHandler;
|
|||
import javax.security.auth.callback.NameCallback;
|
||||
import javax.security.auth.callback.PasswordCallback;
|
||||
import javax.security.auth.callback.UnsupportedCallbackException;
|
||||
import javax.security.auth.login.FailedLoginException;
|
||||
import javax.security.auth.login.LoginException;
|
||||
import javax.security.auth.spi.LoginModule;
|
||||
|
||||
|
@ -199,9 +200,14 @@ public abstract class AbstractLoginModule implements LoginModule
|
|||
callbacks[2] = new PasswordCallback("Enter password", false); //only used if framework does not support the ObjectCallback
|
||||
return callbacks;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean isIgnored ()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public abstract UserInfo getUserInfo (String username) throws Exception;
|
||||
|
||||
|
||||
|
@ -214,7 +220,10 @@ public abstract class AbstractLoginModule implements LoginModule
|
|||
public boolean login() throws LoginException
|
||||
{
|
||||
try
|
||||
{
|
||||
{
|
||||
if (isIgnored())
|
||||
return false;
|
||||
|
||||
if (callbackHandler == null)
|
||||
throw new LoginException ("No callback handler");
|
||||
|
||||
|
@ -231,7 +240,7 @@ public abstract class AbstractLoginModule implements LoginModule
|
|||
if ((webUserName == null) || (webCredential == null))
|
||||
{
|
||||
setAuthenticated(false);
|
||||
return isAuthenticated();
|
||||
throw new FailedLoginException();
|
||||
}
|
||||
|
||||
UserInfo userInfo = getUserInfo(webUserName);
|
||||
|
@ -239,12 +248,16 @@ public abstract class AbstractLoginModule implements LoginModule
|
|||
if (userInfo == null)
|
||||
{
|
||||
setAuthenticated(false);
|
||||
return isAuthenticated();
|
||||
throw new FailedLoginException();
|
||||
}
|
||||
|
||||
currentUser = new JAASUserInfo(userInfo);
|
||||
setAuthenticated(currentUser.checkCredential(webCredential));
|
||||
return isAuthenticated();
|
||||
|
||||
if (isAuthenticated())
|
||||
return true;
|
||||
else
|
||||
throw new FailedLoginException();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
|
@ -256,7 +269,8 @@ public abstract class AbstractLoginModule implements LoginModule
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
if (e instanceof LoginException)
|
||||
throw (LoginException)e;
|
||||
throw new LoginException (e.toString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -112,9 +112,10 @@ public class PropertyFileLoginModule extends AbstractLoginModule
|
|||
PropertyUserStore propertyUserStore = _propertyUserStores.get(_filename);
|
||||
if (propertyUserStore == null)
|
||||
throw new IllegalStateException("PropertyUserStore should never be null here!");
|
||||
|
||||
|
||||
LOG.debug("Checking PropertyUserStore "+_filename+" for "+userName);
|
||||
UserIdentity userIdentity = propertyUserStore.getUserIdentity(userName);
|
||||
if(userIdentity==null)
|
||||
if (userIdentity==null)
|
||||
return null;
|
||||
|
||||
Set<Principal> principals = userIdentity.getSubject().getPrincipals();
|
||||
|
@ -127,7 +128,7 @@ public class PropertyFileLoginModule extends AbstractLoginModule
|
|||
}
|
||||
|
||||
Credential credential = (Credential)userIdentity.getSubject().getPrivateCredentials().iterator().next();
|
||||
LOG.debug("Found: " + userName + " in PropertyUserStore");
|
||||
LOG.debug("Found: " + userName + " in PropertyUserStore "+_filename);
|
||||
return new UserInfo(userName, credential, roles);
|
||||
}
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ import org.eclipse.jetty.util.IO;
|
|||
* </p>
|
||||
*
|
||||
* @goal run-forked
|
||||
* @requiresDependencyResolution compile+runtime
|
||||
* @requiresDependencyResolution test
|
||||
* @execute phase="test-compile"
|
||||
* @description Runs Jetty in forked JVM on an unassembled webapp
|
||||
*
|
||||
|
|
|
@ -29,6 +29,7 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.eclipse.jetty.annotations.AnnotationConfiguration;
|
||||
import org.eclipse.jetty.plus.webapp.EnvConfiguration;
|
||||
import org.eclipse.jetty.servlet.FilterHolder;
|
||||
import org.eclipse.jetty.servlet.FilterMapping;
|
||||
|
@ -110,7 +111,7 @@ public class JettyWebAppContext extends WebAppContext
|
|||
new FragmentConfiguration(),
|
||||
_envConfig = new EnvConfiguration(),
|
||||
new org.eclipse.jetty.plus.webapp.PlusConfiguration(),
|
||||
new MavenAnnotationConfiguration(),
|
||||
new AnnotationConfiguration(),
|
||||
new JettyWebXmlConfiguration()
|
||||
});
|
||||
// Turn off copyWebInf option as it is not applicable for plugin.
|
||||
|
|
|
@ -1,109 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.maven.plugin;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.eclipse.jetty.annotations.AbstractDiscoverableAnnotationHandler;
|
||||
import org.eclipse.jetty.annotations.AnnotationConfiguration;
|
||||
import org.eclipse.jetty.annotations.AnnotationParser;
|
||||
import org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler;
|
||||
import org.eclipse.jetty.annotations.ClassNameResolver;
|
||||
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.webapp.MetaData;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
|
||||
public class MavenAnnotationConfiguration extends AnnotationConfiguration
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(MavenAnnotationConfiguration.class);
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public void parseWebInfClasses(final WebAppContext context, final AnnotationParser parser) throws Exception
|
||||
{
|
||||
JettyWebAppContext jwac = (JettyWebAppContext)context;
|
||||
if (jwac.getClassPathFiles() == null || jwac.getClassPathFiles().size() == 0)
|
||||
super.parseWebInfClasses (context, parser);
|
||||
else
|
||||
{
|
||||
LOG.debug("Scanning classes ");
|
||||
//Look for directories on the classpath and process each one of those
|
||||
|
||||
MetaData metaData = context.getMetaData();
|
||||
if (metaData == null)
|
||||
throw new IllegalStateException ("No metadata");
|
||||
|
||||
parser.clearHandlers();
|
||||
for (DiscoverableAnnotationHandler h:_discoverableAnnotationHandlers)
|
||||
{
|
||||
if (h instanceof AbstractDiscoverableAnnotationHandler)
|
||||
((AbstractDiscoverableAnnotationHandler)h).setResource(null); //
|
||||
}
|
||||
parser.registerHandlers(_discoverableAnnotationHandlers);
|
||||
parser.registerHandler(_classInheritanceHandler);
|
||||
parser.registerHandlers(_containerInitializerAnnotationHandlers);
|
||||
|
||||
|
||||
for (File f:jwac.getClassPathFiles())
|
||||
{
|
||||
//scan the equivalent of the WEB-INF/classes directory that has been synthesised by the plugin
|
||||
if (f.isDirectory() && f.exists())
|
||||
{
|
||||
doParse(context, parser, Resource.newResource(f.toURI()));
|
||||
}
|
||||
}
|
||||
|
||||
//if an actual WEB-INF/classes directory also exists (eg because of overlayed wars) then scan that
|
||||
//too
|
||||
if (context.getWebInf() != null && context.getWebInf().exists())
|
||||
{
|
||||
Resource classesDir = context.getWebInf().addPath("classes/");
|
||||
if (classesDir.exists())
|
||||
{
|
||||
doParse(context, parser, classesDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void doParse (final WebAppContext context, final AnnotationParser parser, Resource resource)
|
||||
throws Exception
|
||||
{
|
||||
parser.parseDir(resource, new ClassNameResolver()
|
||||
{
|
||||
public boolean isExcluded (String name)
|
||||
{
|
||||
if (context.isSystemClass(name)) return true;
|
||||
if (context.isServerClass(name)) return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean shouldOverride (String name)
|
||||
{
|
||||
//looking at webapp classpath, found already-parsed class of same name - did it come from system or duplicate in webapp?
|
||||
if (context.isParentLoaderPriority())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -239,6 +239,45 @@ public class MavenWebInfConfiguration extends WebInfConfiguration
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Add in the classes dirs from test/classes and target/classes
|
||||
* @see org.eclipse.jetty.webapp.WebInfConfiguration#findClassDirs(org.eclipse.jetty.webapp.WebAppContext)
|
||||
*/
|
||||
@Override
|
||||
protected List<Resource> findClassDirs(WebAppContext context) throws Exception
|
||||
{
|
||||
List<Resource> list = new ArrayList<Resource>();
|
||||
|
||||
JettyWebAppContext jwac = (JettyWebAppContext)context;
|
||||
if (jwac.getClassPathFiles() != null)
|
||||
{
|
||||
for (File f: jwac.getClassPathFiles())
|
||||
{
|
||||
if (f.exists() && f.isDirectory())
|
||||
{
|
||||
try
|
||||
{
|
||||
list.add(Resource.newResource(f.toURI()));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn("Bad url ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<Resource> classesDirs = super.findClassDirs(context);
|
||||
if (classesDirs != null)
|
||||
list.addAll(classesDirs);
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
protected Resource unpackOverlay (WebAppContext context, Overlay overlay)
|
||||
throws IOException
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
<Item>127.0.0.2/black.html</Item>
|
||||
</Array>
|
||||
</Set>
|
||||
<Set name="whiteListByPath">false</Set>
|
||||
</New>
|
||||
</Set>
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
|
@ -273,10 +274,27 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
|
|||
if (_request.getHttpChannelState().isExpired())
|
||||
{
|
||||
_request.setDispatcherType(DispatcherType.ERROR);
|
||||
|
||||
Throwable ex=_state.getAsyncContextEvent().getThrowable();
|
||||
String reason="Async Timeout";
|
||||
if (ex!=null)
|
||||
{
|
||||
reason="Async Exception";
|
||||
_request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,ex);
|
||||
}
|
||||
_request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,new Integer(500));
|
||||
_request.setAttribute(RequestDispatcher.ERROR_MESSAGE,"Async Timeout");
|
||||
_request.setAttribute(RequestDispatcher.ERROR_MESSAGE,reason);
|
||||
_request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI,_request.getRequestURI());
|
||||
_response.setStatusWithReason(500,"Async Timeout");
|
||||
|
||||
_response.setStatusWithReason(500,reason);
|
||||
|
||||
ErrorHandler eh = _state.getContextHandler().getErrorHandler();
|
||||
if (eh instanceof ErrorHandler.ErrorPageMapper)
|
||||
{
|
||||
String error_page=((ErrorHandler.ErrorPageMapper)eh).getErrorPage((HttpServletRequest)_state.getAsyncContextEvent().getSuppliedRequest());
|
||||
if (error_page!=null)
|
||||
_state.getAsyncContextEvent().setDispatchTarget(_state.getContextHandler().getServletContext(),error_page);
|
||||
}
|
||||
}
|
||||
else
|
||||
_request.setDispatcherType(DispatcherType.ASYNC);
|
||||
|
@ -362,6 +380,7 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
|
|||
boolean committed = sendResponse(info, null, true);
|
||||
if (!committed)
|
||||
LOG.warn("Could not send response error 500: "+x);
|
||||
_request.getAsyncContext().complete();
|
||||
}
|
||||
else if (isCommitted())
|
||||
{
|
||||
|
|
|
@ -348,7 +348,7 @@ public class HttpChannelState
|
|||
protected void expired()
|
||||
{
|
||||
final List<AsyncListener> aListeners;
|
||||
AsyncEvent event;
|
||||
AsyncContextEvent event;
|
||||
synchronized (this)
|
||||
{
|
||||
switch(_state)
|
||||
|
@ -374,7 +374,10 @@ public class HttpChannelState
|
|||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.warn(e);
|
||||
LOG.debug(e);
|
||||
event.setThrowable(e);
|
||||
_channel.getRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -647,7 +647,8 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
|||
{
|
||||
case NEED_HEADER:
|
||||
{
|
||||
if (_lastContent && _content!=null && BufferUtil.space(_content)>_config.getResponseHeaderSize() && _content.hasArray() )
|
||||
// Look for optimisation to avoid allocating a _header buffer
|
||||
if (_lastContent && _content!=null && !_content.isReadOnly() && _content.hasArray() && BufferUtil.space(_content)>_config.getResponseHeaderSize() )
|
||||
{
|
||||
// use spare space in content buffer for header buffer
|
||||
int p=_content.position();
|
||||
|
|
|
@ -207,7 +207,8 @@ public class HttpOutput extends ServletOutputStream
|
|||
|
||||
// write any remaining content in the buffer directly
|
||||
if (len>0)
|
||||
_channel.write(ByteBuffer.wrap(b, off, len), complete);
|
||||
// pass as readonly to avoid space stealing optimisation in HttpConnection
|
||||
_channel.write(ByteBuffer.wrap(b, off, len).asReadOnlyBuffer(), complete);
|
||||
else if (complete)
|
||||
_channel.write(BufferUtil.EMPTY_BUFFER,complete);
|
||||
|
||||
|
@ -299,12 +300,14 @@ public class HttpOutput extends ServletOutputStream
|
|||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Blocking send of content.
|
||||
* @param content The content to send
|
||||
* @param content The content to send.
|
||||
* @throws IOException
|
||||
*/
|
||||
public void sendContent(ByteBuffer content) throws IOException
|
||||
{
|
||||
final BlockingCallback callback =_channel.getWriteBlockingCallback();
|
||||
if (content.hasArray()&&content.limit()<content.capacity())
|
||||
content=content.asReadOnlyBuffer();
|
||||
_channel.write(content,true,callback);
|
||||
callback.block();
|
||||
}
|
||||
|
@ -346,7 +349,6 @@ public class HttpOutput extends ServletOutputStream
|
|||
callback.block();
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Asynchronous send of content.
|
||||
* @param content The content to send
|
||||
|
@ -354,6 +356,8 @@ public class HttpOutput extends ServletOutputStream
|
|||
*/
|
||||
public void sendContent(ByteBuffer content, final Callback callback)
|
||||
{
|
||||
if (content.hasArray()&&content.limit()<content.capacity())
|
||||
content=content.asReadOnlyBuffer();
|
||||
_channel.write(content,true,new Callback()
|
||||
{
|
||||
@Override
|
||||
|
|
|
@ -1500,6 +1500,9 @@ public class Request implements HttpServletRequest
|
|||
/* ------------------------------------------------------------ */
|
||||
protected void recycle()
|
||||
{
|
||||
if (_context != null)
|
||||
throw new IllegalStateException("Request in context!");
|
||||
|
||||
if (_inputState == __READER)
|
||||
{
|
||||
try
|
||||
|
@ -1515,6 +1518,7 @@ public class Request implements HttpServletRequest
|
|||
}
|
||||
}
|
||||
|
||||
_dispatcherType=null;
|
||||
setAuthentication(Authentication.NOT_CHECKED);
|
||||
getHttpChannelState().recycle();
|
||||
if (_async!=null)
|
||||
|
@ -1522,8 +1526,6 @@ public class Request implements HttpServletRequest
|
|||
_async=null;
|
||||
_asyncSupported = true;
|
||||
_handled = false;
|
||||
if (_context != null)
|
||||
throw new IllegalStateException("Request in context!");
|
||||
if (_attributes != null)
|
||||
_attributes.clearAttributes();
|
||||
_characterEncoding = null;
|
||||
|
@ -1532,7 +1534,9 @@ public class Request implements HttpServletRequest
|
|||
_cookies.reset();
|
||||
_cookiesExtracted = false;
|
||||
_context = null;
|
||||
_newContext=false;
|
||||
_serverName = null;
|
||||
_httpMethod=null;
|
||||
_httpMethodString = null;
|
||||
_pathInfo = null;
|
||||
_port = 0;
|
||||
|
@ -1541,6 +1545,7 @@ public class Request implements HttpServletRequest
|
|||
_queryString = null;
|
||||
_requestedSessionId = null;
|
||||
_requestedSessionIdFromCookie = false;
|
||||
_secure=false;
|
||||
_session = null;
|
||||
_sessionManager = null;
|
||||
_requestURI = null;
|
||||
|
|
|
@ -359,9 +359,10 @@ public class Server extends HandlerWrapper implements Attributes
|
|||
futures.add(connector.shutdown());
|
||||
|
||||
// Then tell the contexts that we are shutting down
|
||||
Handler[] contexts = getChildHandlersByClass(Graceful.class);
|
||||
for (Handler context : contexts)
|
||||
futures.add(((Graceful)context).shutdown());
|
||||
|
||||
Handler[] gracefuls = getChildHandlersByClass(Graceful.class);
|
||||
for (Handler graceful : gracefuls)
|
||||
futures.add(((Graceful)graceful).shutdown());
|
||||
|
||||
// Shall we gracefully wait for zero connections?
|
||||
long stopTimeout = getStopTimeout();
|
||||
|
|
|
@ -1606,27 +1606,9 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
path = URIUtil.canonicalPath(path);
|
||||
Resource resource = _baseResource.addPath(path);
|
||||
|
||||
// Is the resource aliased?
|
||||
if (resource.getAlias() != null)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Aliased resource: " + resource + "~=" + resource.getAlias());
|
||||
|
||||
// alias checks
|
||||
for (Iterator<AliasCheck> i=_aliasChecks.iterator();i.hasNext();)
|
||||
{
|
||||
AliasCheck check = i.next();
|
||||
if (check.check(path,resource))
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Aliased resource: " + resource + " approved by " + check);
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return resource;
|
||||
if (checkAlias(path,resource))
|
||||
return resource;
|
||||
return null;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -1636,6 +1618,31 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
return null;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public boolean checkAlias(String path, Resource resource)
|
||||
{
|
||||
// Is the resource aliased?
|
||||
if (resource.getAlias() != null)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Aliased resource: " + resource + "~=" + resource.getAlias());
|
||||
|
||||
// alias checks
|
||||
for (Iterator<AliasCheck> i=_aliasChecks.iterator();i.hasNext();)
|
||||
{
|
||||
AliasCheck check = i.next();
|
||||
if (check.check(path,resource))
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Aliased resource: " + resource + " approved by " + check);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Convert URL to Resource wrapper for {@link Resource#newResource(URL)} enables extensions to provide alternate resource implementations.
|
||||
|
@ -1722,6 +1729,16 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
{
|
||||
return _aliasChecks;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @param checks list of AliasCheck instances
|
||||
*/
|
||||
public void setAliasChecks(List<AliasCheck> checks)
|
||||
{
|
||||
_aliasChecks.clear();
|
||||
_aliasChecks.addAll(checks);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
|
@ -2663,8 +2680,10 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
|
||||
if (a.length()>r.length())
|
||||
return a.startsWith(r) && a.length()==r.length()+1 && a.endsWith("/");
|
||||
else
|
||||
if (a.length()<r.length())
|
||||
return r.startsWith(a) && r.length()==a.length()+1 && r.endsWith("/");
|
||||
|
||||
return a.equals(r);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,8 @@ import java.io.PrintWriter;
|
|||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
|
||||
import javax.activation.MimeType;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
@ -30,9 +32,12 @@ import org.eclipse.jetty.http.HttpHeader;
|
|||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.MimeTypes;
|
||||
import org.eclipse.jetty.server.Dispatcher;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.util.ByteArrayISO8859Writer;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Handler for Error pages
|
||||
|
@ -42,7 +47,10 @@ import org.eclipse.jetty.util.ByteArrayISO8859Writer;
|
|||
*
|
||||
*/
|
||||
public class ErrorHandler extends AbstractHandler
|
||||
{
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(ErrorHandler.class);
|
||||
public final static String ERROR_PAGE="org.eclipse.jetty.server.error_page";
|
||||
|
||||
boolean _showStacks=true;
|
||||
boolean _showMessageInTitle=true;
|
||||
String _cacheControl="must-revalidate,no-cache,no-store";
|
||||
|
@ -54,11 +62,44 @@ public class ErrorHandler extends AbstractHandler
|
|||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
String method = request.getMethod();
|
||||
if(!HttpMethod.GET.is(method) && !HttpMethod.POST.is(method) && !HttpMethod.HEAD.is(method))
|
||||
if (!HttpMethod.GET.is(method) && !HttpMethod.POST.is(method) && !HttpMethod.HEAD.is(method))
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
return;
|
||||
response.setContentType(MimeTypes.Type.TEXT_HTML_8859_1.asString());
|
||||
}
|
||||
|
||||
if (this instanceof ErrorPageMapper)
|
||||
{
|
||||
String error_page=((ErrorPageMapper)this).getErrorPage(request);
|
||||
if (error_page!=null && request.getServletContext()!=null)
|
||||
{
|
||||
String old_error_page=(String)request.getAttribute(ERROR_PAGE);
|
||||
if (old_error_page==null || !old_error_page.equals(error_page))
|
||||
{
|
||||
request.setAttribute(ERROR_PAGE, error_page);
|
||||
|
||||
Dispatcher dispatcher = (Dispatcher) request.getServletContext().getRequestDispatcher(error_page);
|
||||
try
|
||||
{
|
||||
if(dispatcher!=null)
|
||||
{
|
||||
dispatcher.error(request, response);
|
||||
return;
|
||||
}
|
||||
LOG.warn("No error page "+error_page);
|
||||
}
|
||||
catch (ServletException e)
|
||||
{
|
||||
LOG.warn(Log.EXCEPTION, e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
baseRequest.setHandled(true);
|
||||
response.setContentType(MimeTypes.Type.TEXT_HTML_8859_1.asString());
|
||||
if (_cacheControl!=null)
|
||||
response.setHeader(HttpHeader.CACHE_CONTROL.asString(), _cacheControl);
|
||||
ByteArrayISO8859Writer writer= new ByteArrayISO8859Writer(4096);
|
||||
|
@ -235,4 +276,10 @@ public class ErrorHandler extends AbstractHandler
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public interface ErrorPageMapper
|
||||
{
|
||||
String getErrorPage(HttpServletRequest request);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,8 +20,6 @@ package org.eclipse.jetty.server.handler;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
@ -54,11 +52,17 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
* entries, that are then further refined by several specific black list exceptions
|
||||
* </ul>
|
||||
* <p>
|
||||
* An empty white list is treated as match all. If there is at least one entry in
|
||||
* By default an empty white list is treated as match all. If there is at least one entry in
|
||||
* the white list, then a request must match a white list entry. Black list entries
|
||||
* are always applied, so that even if an entry matches the white list, a black list
|
||||
* entry will override it.
|
||||
* <p>
|
||||
* <p>
|
||||
* You can change white list policy setting whiteListByPath to true. In this mode a request will be white listed
|
||||
* IF it has a matching URL in the white list, otherwise the black list applies, e.g. in default mode when
|
||||
* whiteListByPath = false and wl = "127.0.0.1|/foo", /bar request from 127.0.0.1 will be blacklisted,
|
||||
* if whiteListByPath=true then not.
|
||||
* </p>
|
||||
* Internet addresses may be specified as absolute address or as a combination of
|
||||
* four octet wildcard specifications (a.b.c.d) that are defined as follows.
|
||||
* </p>
|
||||
|
@ -101,9 +105,10 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
public class IPAccessHandler extends HandlerWrapper
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(IPAccessHandler.class);
|
||||
|
||||
IPAddressMap<PathMap> _white = new IPAddressMap<PathMap>();
|
||||
IPAddressMap<PathMap> _black = new IPAddressMap<PathMap>();
|
||||
// true means nodefault match
|
||||
PathMap<IPAddressMap<Boolean>> _white = new PathMap<IPAddressMap<Boolean>>(true);
|
||||
PathMap<IPAddressMap<Boolean>> _black = new PathMap<IPAddressMap<Boolean>>(true);
|
||||
boolean _whiteListByPath = false;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
|
@ -175,6 +180,17 @@ public class IPAccessHandler extends HandlerWrapper
|
|||
set(entries, _black);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Re-initialize the mode of path matching
|
||||
*
|
||||
* @param whiteListByPath matching mode
|
||||
*/
|
||||
public void setWhiteListByPath(boolean whiteListByPath)
|
||||
{
|
||||
this._whiteListByPath = whiteListByPath;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Checks the incoming request against the whitelist and blacklist
|
||||
|
@ -185,7 +201,7 @@ public class IPAccessHandler extends HandlerWrapper
|
|||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
// Get the real remote IP (not the one set by the forwarded headers (which may be forged))
|
||||
HttpChannel channel = baseRequest.getHttpChannel();
|
||||
HttpChannel<?> channel = baseRequest.getHttpChannel();
|
||||
if (channel!=null)
|
||||
{
|
||||
EndPoint endp=channel.getEndPoint();
|
||||
|
@ -213,7 +229,7 @@ public class IPAccessHandler extends HandlerWrapper
|
|||
* @param entry new entry
|
||||
* @param patternMap target address pattern map
|
||||
*/
|
||||
protected void add(String entry, IPAddressMap<PathMap> patternMap)
|
||||
protected void add(String entry, PathMap<IPAddressMap<Boolean>> patternMap)
|
||||
{
|
||||
if (entry != null && entry.length() > 0)
|
||||
{
|
||||
|
@ -237,14 +253,15 @@ public class IPAccessHandler extends HandlerWrapper
|
|||
if (path!=null && (path.startsWith("|") || path.startsWith("/*.")))
|
||||
path=path.substring(1);
|
||||
|
||||
PathMap pathMap = patternMap.get(addr);
|
||||
if (pathMap == null)
|
||||
IPAddressMap<Boolean> addrMap = patternMap.get(path);
|
||||
if (addrMap == null)
|
||||
{
|
||||
pathMap = new PathMap(true);
|
||||
patternMap.put(addr,pathMap);
|
||||
addrMap = new IPAddressMap<Boolean>();
|
||||
patternMap.put(path,addrMap);
|
||||
}
|
||||
if (path != null && !"".equals(path))
|
||||
pathMap.put(path,path);
|
||||
if (addr != null && !"".equals(addr))
|
||||
// MUST NOT BE null
|
||||
addrMap.put(addr, true);
|
||||
|
||||
if (deprecated)
|
||||
LOG.debug(toString() +" - deprecated specification syntax: "+entry);
|
||||
|
@ -259,7 +276,7 @@ public class IPAccessHandler extends HandlerWrapper
|
|||
* @param entries new entries
|
||||
* @param patternMap target address pattern map
|
||||
*/
|
||||
protected void set(String[] entries, IPAddressMap<PathMap> patternMap)
|
||||
protected void set(String[] entries, PathMap<IPAddressMap<Boolean>> patternMap)
|
||||
{
|
||||
patternMap.clear();
|
||||
|
||||
|
@ -286,38 +303,40 @@ public class IPAccessHandler extends HandlerWrapper
|
|||
if (_white.size()>0)
|
||||
{
|
||||
boolean match = false;
|
||||
boolean matchedByPath = false;
|
||||
|
||||
Object whiteObj = _white.getLazyMatches(addr);
|
||||
if (whiteObj != null)
|
||||
for (Map.Entry<String,IPAddressMap<Boolean>> entry : _white.getMatches(path))
|
||||
{
|
||||
List whiteList = (whiteObj instanceof List) ? (List)whiteObj : Collections.singletonList(whiteObj);
|
||||
|
||||
for (Object entry: whiteList)
|
||||
matchedByPath=true;
|
||||
IPAddressMap<Boolean> addrMap = entry.getValue();
|
||||
if ((addrMap!=null && (addrMap.size()==0 || addrMap.match(addr)!=null)))
|
||||
{
|
||||
PathMap pathMap = ((Map.Entry<String,PathMap>)entry).getValue();
|
||||
if (match = (pathMap!=null && (pathMap.size()==0 || pathMap.match(path)!=null)))
|
||||
break;
|
||||
match=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!match)
|
||||
return false;
|
||||
|
||||
if (_whiteListByPath)
|
||||
{
|
||||
if (matchedByPath && !match)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!match)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (_black.size() > 0)
|
||||
{
|
||||
Object blackObj = _black.getLazyMatches(addr);
|
||||
if (blackObj != null)
|
||||
for (Map.Entry<String,IPAddressMap<Boolean>> entry : _black.getMatches(path))
|
||||
{
|
||||
List blackList = (blackObj instanceof List) ? (List)blackObj : Collections.singletonList(blackObj);
|
||||
|
||||
for (Object entry: blackList)
|
||||
{
|
||||
PathMap pathMap = ((Map.Entry<String,PathMap>)entry).getValue();
|
||||
if (pathMap!=null && (pathMap.size()==0 || pathMap.match(path)!=null))
|
||||
return false;
|
||||
}
|
||||
IPAddressMap<Boolean> addrMap = entry.getValue();
|
||||
if (addrMap!=null && (addrMap.size()==0 || addrMap.match(addr)!=null))
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -327,6 +346,7 @@ public class IPAccessHandler extends HandlerWrapper
|
|||
/**
|
||||
* Dump the handler configuration
|
||||
*/
|
||||
@Override
|
||||
public String dump()
|
||||
{
|
||||
StringBuilder buf = new StringBuilder();
|
||||
|
@ -348,11 +368,11 @@ public class IPAccessHandler extends HandlerWrapper
|
|||
* @param buf buffer
|
||||
* @param patternMap pattern map to dump
|
||||
*/
|
||||
protected void dump(StringBuilder buf, IPAddressMap<PathMap> patternMap)
|
||||
protected void dump(StringBuilder buf, PathMap<IPAddressMap<Boolean>> patternMap)
|
||||
{
|
||||
for (String addr: patternMap.keySet())
|
||||
for (String path: patternMap.keySet())
|
||||
{
|
||||
for (Object path: ((PathMap)patternMap.get(addr)).values())
|
||||
for (String addr: patternMap.get(path).keySet())
|
||||
{
|
||||
buf.append("# ");
|
||||
buf.append(addr);
|
||||
|
|
|
@ -19,8 +19,11 @@
|
|||
package org.eclipse.jetty.server.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.servlet.AsyncEvent;
|
||||
import javax.servlet.AsyncListener;
|
||||
|
@ -32,14 +35,16 @@ import org.eclipse.jetty.server.AsyncContextEvent;
|
|||
import org.eclipse.jetty.server.HttpChannelState;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.util.FutureCallback;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
import org.eclipse.jetty.util.annotation.ManagedOperation;
|
||||
import org.eclipse.jetty.util.component.Graceful;
|
||||
import org.eclipse.jetty.util.statistic.CounterStatistic;
|
||||
import org.eclipse.jetty.util.statistic.SampleStatistic;
|
||||
|
||||
@ManagedObject("Request Statistics Gathering")
|
||||
public class StatisticsHandler extends HandlerWrapper
|
||||
public class StatisticsHandler extends HandlerWrapper implements Graceful
|
||||
{
|
||||
private final AtomicLong _statsStartedAt = new AtomicLong();
|
||||
|
||||
|
@ -59,6 +64,8 @@ public class StatisticsHandler extends HandlerWrapper
|
|||
private final AtomicInteger _responses5xx = new AtomicInteger();
|
||||
private final AtomicLong _responsesTotalBytes = new AtomicLong();
|
||||
|
||||
private final AtomicReference<FutureCallback> _shutdown=new AtomicReference<>();
|
||||
|
||||
private final AsyncListener _onCompletion = new AsyncListener()
|
||||
{
|
||||
@Override
|
||||
|
@ -81,21 +88,27 @@ public class StatisticsHandler extends HandlerWrapper
|
|||
@Override
|
||||
public void onComplete(AsyncEvent event) throws IOException
|
||||
{
|
||||
|
||||
HttpChannelState state = ((AsyncContextEvent)event).getHttpChannelState();
|
||||
|
||||
Request request = state.getBaseRequest();
|
||||
final long elapsed = System.currentTimeMillis()-request.getTimeStamp();
|
||||
|
||||
_requestStats.decrement();
|
||||
long d=_requestStats.decrement();
|
||||
_requestTimeStats.set(elapsed);
|
||||
|
||||
updateResponse(request);
|
||||
|
||||
if (!state.isDispatched())
|
||||
_asyncWaitStats.decrement();
|
||||
|
||||
// If we have no more dispatches, should we signal shutdown?
|
||||
if (d==0)
|
||||
{
|
||||
FutureCallback shutdown = _shutdown.get();
|
||||
if (shutdown!=null)
|
||||
shutdown.succeeded();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -164,9 +177,18 @@ public class StatisticsHandler extends HandlerWrapper
|
|||
}
|
||||
else if (state.isInitial())
|
||||
{
|
||||
_requestStats.decrement();
|
||||
long d=_requestStats.decrement();
|
||||
_requestTimeStats.set(dispatched);
|
||||
updateResponse(request);
|
||||
|
||||
// If we have no more dispatches, should we signal shutdown?
|
||||
FutureCallback shutdown = _shutdown.get();
|
||||
if (shutdown!=null)
|
||||
{
|
||||
httpResponse.flushBuffer();
|
||||
if (d==0)
|
||||
shutdown.succeeded();
|
||||
}
|
||||
}
|
||||
// else onCompletion will handle it.
|
||||
}
|
||||
|
@ -207,9 +229,20 @@ public class StatisticsHandler extends HandlerWrapper
|
|||
@Override
|
||||
protected void doStart() throws Exception
|
||||
{
|
||||
_shutdown.set(null);
|
||||
super.doStart();
|
||||
statsReset();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void doStop() throws Exception
|
||||
{
|
||||
super.doStop();
|
||||
FutureCallback shutdown = _shutdown.get();
|
||||
if (shutdown!=null && !shutdown.isDone())
|
||||
shutdown.failed(new TimeoutException());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of requests handled by this handler
|
||||
|
@ -525,4 +558,15 @@ public class StatisticsHandler extends HandlerWrapper
|
|||
return sb.toString();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> shutdown()
|
||||
{
|
||||
FutureCallback shutdown=new FutureCallback(false);
|
||||
_shutdown.compareAndSet(null,shutdown);
|
||||
shutdown=_shutdown.get();
|
||||
if (_dispatchedStats.getCurrent()==0)
|
||||
shutdown.succeeded();
|
||||
return shutdown;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -244,7 +244,7 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
@Override
|
||||
public void setAttribute (String name, Object value)
|
||||
{
|
||||
_dirty=updateAttribute(name, value);
|
||||
_dirty = (updateAttribute(name, value) || _dirty);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.server.handler.StatisticsHandler;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class GracefulStopTest
|
||||
{
|
||||
private Server server;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception
|
||||
{
|
||||
server = new Server(0);
|
||||
StatisticsHandler stats = new StatisticsHandler();
|
||||
TestHandler test=new TestHandler();
|
||||
server.setHandler(stats);
|
||||
stats.setHandler(test);
|
||||
server.setStopTimeout(10 * 1000);
|
||||
|
||||
server.start();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGraceful() throws Exception
|
||||
{
|
||||
new Thread()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
TimeUnit.SECONDS.sleep(1);
|
||||
server.stop();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
|
||||
try(Socket socket = new Socket("localhost",server.getBean(NetworkConnector.class).getLocalPort());)
|
||||
{
|
||||
socket.getOutputStream().write("GET / HTTP/1.0\r\n\r\n".getBytes(StringUtil.__ISO_8859_1_CHARSET));
|
||||
String out = IO.toString(socket.getInputStream());
|
||||
Assert.assertThat(out,Matchers.containsString("200 OK"));
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestHandler extends AbstractHandler
|
||||
{
|
||||
@Override
|
||||
public void handle(final String s, final Request request, final HttpServletRequest httpServletRequest, final HttpServletResponse httpServletResponse)
|
||||
throws IOException, ServletException
|
||||
{
|
||||
try
|
||||
{
|
||||
TimeUnit.SECONDS.sleep(2);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
}
|
||||
|
||||
httpServletResponse.getWriter().write("OK");
|
||||
httpServletResponse.setStatus(200);
|
||||
request.setHandled(true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -19,6 +19,7 @@
|
|||
package org.eclipse.jetty.server;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.FilterInputStream;
|
||||
|
@ -26,6 +27,7 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
@ -35,6 +37,7 @@ import org.eclipse.jetty.server.handler.AbstractHandler;
|
|||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -78,6 +81,26 @@ public class HttpOutputTest
|
|||
assertThat(response,containsString("HTTP/1.1 200 OK"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendArray() throws Exception
|
||||
{
|
||||
byte[] buffer=new byte[16*1024];
|
||||
Arrays.fill(buffer,0,4*1024,(byte)0x99);
|
||||
Arrays.fill(buffer,4*1024,12*1024,(byte)0x58);
|
||||
Arrays.fill(buffer,12*1024,16*1024,(byte)0x66);
|
||||
_handler._content=ByteBuffer.wrap(buffer);
|
||||
_handler._content.limit(12*1024);
|
||||
_handler._content.position(4*1024);
|
||||
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
|
||||
assertThat(response,containsString("HTTP/1.1 200 OK"));
|
||||
assertThat(response,containsString("\r\nXXXXXXXXXXXXXXXXXXXXXXXXXXX"));
|
||||
|
||||
for (int i=0;i<4*1024;i++)
|
||||
assertEquals("i="+i,(byte)0x99,buffer[i]);
|
||||
for (int i=12*1024;i<16*1024;i++)
|
||||
assertEquals("i="+i,(byte)0x66,buffer[i]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendInputStreamSimple() throws Exception
|
||||
{
|
||||
|
@ -195,6 +218,7 @@ public class HttpOutputTest
|
|||
{
|
||||
InputStream _contentInputStream;
|
||||
ReadableByteChannel _contentChannel;
|
||||
ByteBuffer _content;
|
||||
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
|
@ -204,6 +228,17 @@ public class HttpOutputTest
|
|||
|
||||
HttpOutput out = (HttpOutput) response.getOutputStream();
|
||||
|
||||
if (_content!=null)
|
||||
{
|
||||
response.setContentLength(_content.remaining());
|
||||
if (_content.hasArray())
|
||||
out.write(_content.array(),_content.arrayOffset()+_content.position(),_content.remaining());
|
||||
else
|
||||
out.sendContent(_content);
|
||||
_content=null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_contentInputStream!=null)
|
||||
{
|
||||
out.sendContent(_contentInputStream);
|
||||
|
|
|
@ -228,7 +228,6 @@ public class ContextHandlerGetResourceTest
|
|||
assertEquals(docroot,new File(url.toURI()).getParentFile());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testTooNormal() throws Exception
|
||||
{
|
||||
|
@ -293,6 +292,23 @@ public class ContextHandlerGetResourceTest
|
|||
URL url=context.getServletContext().getResource(path);
|
||||
assertNull(url);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSlashSlash() throws Exception
|
||||
{
|
||||
String path="//subdir/data.txt";
|
||||
Resource resource=context.getResource(path);
|
||||
assertNull(resource);
|
||||
URL url=context.getServletContext().getResource(path);
|
||||
assertNull(url);
|
||||
|
||||
path="/subdir//data.txt";
|
||||
resource=context.getResource(path);
|
||||
assertNull(resource);
|
||||
url=context.getServletContext().getResource(path);
|
||||
assertNull(url);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAliasedFile() throws Exception
|
||||
|
|
|
@ -64,6 +64,7 @@ public class IPAccessHandlerTest
|
|||
private String _host;
|
||||
private String _uri;
|
||||
private String _code;
|
||||
private boolean _byPath;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUp()
|
||||
|
@ -76,6 +77,7 @@ public class IPAccessHandlerTest
|
|||
_handler = new IPAccessHandler();
|
||||
_handler.setHandler(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
|
@ -95,13 +97,14 @@ public class IPAccessHandlerTest
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public IPAccessHandlerTest(String white, String black, String host, String uri, String code)
|
||||
public IPAccessHandlerTest(String white, String black, String host, String uri, String code, boolean byPath)
|
||||
{
|
||||
_white = white;
|
||||
_black = black;
|
||||
_host = host;
|
||||
_uri = uri;
|
||||
_code = code;
|
||||
_byPath = byPath;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -111,6 +114,7 @@ public class IPAccessHandlerTest
|
|||
{
|
||||
_handler.setWhite(_white.split(";",-1));
|
||||
_handler.setBlack(_black.split(";",-1));
|
||||
_handler.setWhiteListByPath(_byPath);
|
||||
|
||||
String request = "GET " + _uri + " HTTP/1.1\n" + "Host: "+ _host + "\n\n";
|
||||
Socket socket = new Socket("127.0.0.1", _connector.getLocalPort());
|
||||
|
@ -124,7 +128,10 @@ public class IPAccessHandlerTest
|
|||
output.flush();
|
||||
|
||||
Response response = readResponse(input);
|
||||
assertEquals(_code, response.getCode());
|
||||
Object[] params = new Object[]{
|
||||
"Request WBHUC", _white, _black, _host, _uri, _code,
|
||||
"Response", response.getCode()};
|
||||
assertEquals(Arrays.deepToString(params), _code, response.getCode());
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -244,157 +251,313 @@ public class IPAccessHandlerTest
|
|||
public static Collection<Object[]> data() {
|
||||
Object[][] data = new Object[][] {
|
||||
// Empty lists
|
||||
{"", "", "127.0.0.1", "/", "200"},
|
||||
{"", "", "127.0.0.1", "/dump/info", "200"},
|
||||
{"", "", "127.0.0.1", "/", "200", false},
|
||||
{"", "", "127.0.0.1", "/dump/info", "200", false},
|
||||
|
||||
// White list
|
||||
{"127.0.0.1", "", "127.0.0.1", "/", "200"},
|
||||
{"127.0.0.1", "", "127.0.0.1", "/dispatch", "200"},
|
||||
{"127.0.0.1", "", "127.0.0.1", "/dump/info", "200"},
|
||||
{"127.0.0.1", "", "127.0.0.1", "/", "200", false},
|
||||
{"127.0.0.1", "", "127.0.0.1", "/dispatch", "200", false},
|
||||
{"127.0.0.1", "", "127.0.0.1", "/dump/info", "200", false},
|
||||
|
||||
{"127.0.0.1|/", "", "127.0.0.1", "/", "200"},
|
||||
{"127.0.0.1|/", "", "127.0.0.1", "/dispatch", "403"},
|
||||
{"127.0.0.1|/", "", "127.0.0.1", "/dump/info", "403"},
|
||||
{"127.0.0.1|/", "", "127.0.0.1", "/", "200", false},
|
||||
{"127.0.0.1|/", "", "127.0.0.1", "/dispatch", "403", false},
|
||||
{"127.0.0.1|/", "", "127.0.0.1", "/dump/info", "403", false},
|
||||
|
||||
{"127.0.0.1|/*", "", "127.0.0.1", "/", "200"},
|
||||
{"127.0.0.1|/*", "", "127.0.0.1", "/dispatch", "200"},
|
||||
{"127.0.0.1|/*", "", "127.0.0.1", "/dump/info", "200"},
|
||||
{"127.0.0.1|/*", "", "127.0.0.1", "/", "200", false},
|
||||
{"127.0.0.1|/*", "", "127.0.0.1", "/dispatch", "200", false},
|
||||
{"127.0.0.1|/*", "", "127.0.0.1", "/dump/info", "200", false},
|
||||
|
||||
{"127.0.0.1|/dump/*", "", "127.0.0.1", "/", "403"},
|
||||
{"127.0.0.1|/dump/*", "", "127.0.0.1", "/dispatch", "403"},
|
||||
{"127.0.0.1|/dump/*", "", "127.0.0.1", "/dump/info", "200"},
|
||||
{"127.0.0.1|/dump/*", "", "127.0.0.1", "/dump/test", "200"},
|
||||
{"127.0.0.1|/dump/*", "", "127.0.0.1", "/", "403", false},
|
||||
{"127.0.0.1|/dump/*", "", "127.0.0.1", "/dispatch", "403", false},
|
||||
{"127.0.0.1|/dump/*", "", "127.0.0.1", "/dump/info", "200", false},
|
||||
{"127.0.0.1|/dump/*", "", "127.0.0.1", "/dump/test", "200", false},
|
||||
|
||||
{"127.0.0.1|/dump/info", "", "127.0.0.1", "/", "403"},
|
||||
{"127.0.0.1|/dump/info", "", "127.0.0.1", "/dispatch", "403"},
|
||||
{"127.0.0.1|/dump/info", "", "127.0.0.1", "/dump/info", "200"},
|
||||
{"127.0.0.1|/dump/info", "", "127.0.0.1", "/dump/test", "403"},
|
||||
{"127.0.0.1|/dump/info", "", "127.0.0.1", "/", "403", false},
|
||||
{"127.0.0.1|/dump/info", "", "127.0.0.1", "/dispatch", "403", false},
|
||||
{"127.0.0.1|/dump/info", "", "127.0.0.1", "/dump/info", "200", false},
|
||||
{"127.0.0.1|/dump/info", "", "127.0.0.1", "/dump/test", "403", false},
|
||||
|
||||
{"127.0.0.1|/dump/info;127.0.0.1|/dump/test", "", "127.0.0.1", "/", "403"},
|
||||
{"127.0.0.1|/dump/info;127.0.0.1|/dump/test", "", "127.0.0.1", "/dispatch", "403"},
|
||||
{"127.0.0.1|/dump/info;127.0.0.1|/dump/test", "", "127.0.0.1", "/dump/info", "200"},
|
||||
{"127.0.0.1|/dump/info;127.0.0.1|/dump/test", "", "127.0.0.1", "/dump/test", "200"},
|
||||
{"127.0.0.1|/dump/info;127.0.0.1|/dump/test", "", "127.0.0.1", "/dump/fail", "403"},
|
||||
{"127.0.0.1|/dump/info;127.0.0.1|/dump/test", "", "127.0.0.1", "/", "403", false},
|
||||
{"127.0.0.1|/dump/info;127.0.0.1|/dump/test", "", "127.0.0.1", "/dispatch", "403", false},
|
||||
{"127.0.0.1|/dump/info;127.0.0.1|/dump/test", "", "127.0.0.1", "/dump/info", "200", false},
|
||||
{"127.0.0.1|/dump/info;127.0.0.1|/dump/test", "", "127.0.0.1", "/dump/test", "200", false},
|
||||
{"127.0.0.1|/dump/info;127.0.0.1|/dump/test", "", "127.0.0.1", "/dump/fail", "403", false},
|
||||
|
||||
{"127.0.0.0-2|", "", "127.0.0.1", "/", "200"},
|
||||
{"127.0.0.0-2|", "", "127.0.0.1", "/dump/info", "200"},
|
||||
{"127.0.0.0-2|", "", "127.0.0.1", "/", "200", false},
|
||||
{"127.0.0.0-2|", "", "127.0.0.1", "/dump/info", "403", false},
|
||||
|
||||
{"127.0.0.0-2|/", "", "127.0.0.1", "/", "200"},
|
||||
{"127.0.0.0-2|/", "", "127.0.0.1", "/dispatch", "403"},
|
||||
{"127.0.0.0-2|/", "", "127.0.0.1", "/dump/info", "403"},
|
||||
{"127.0.0.0-2|/", "", "127.0.0.1", "/", "200", false},
|
||||
{"127.0.0.0-2|/", "", "127.0.0.1", "/dispatch", "403", false},
|
||||
{"127.0.0.0-2|/", "", "127.0.0.1", "/dump/info", "403", false},
|
||||
|
||||
{"127.0.0.0-2|/dump/*", "", "127.0.0.1", "/", "403"},
|
||||
{"127.0.0.0-2|/dump/*", "", "127.0.0.1", "/dispatch", "403"},
|
||||
{"127.0.0.0-2|/dump/*", "", "127.0.0.1", "/dump/info", "200"},
|
||||
{"127.0.0.0-2|/dump/*", "", "127.0.0.1", "/", "403", false},
|
||||
{"127.0.0.0-2|/dump/*", "", "127.0.0.1", "/dispatch", "403", false},
|
||||
{"127.0.0.0-2|/dump/*", "", "127.0.0.1", "/dump/info", "200", false},
|
||||
|
||||
{"127.0.0.0-2|/dump/info", "", "127.0.0.1", "/", "403"},
|
||||
{"127.0.0.0-2|/dump/info", "", "127.0.0.1", "/dispatch", "403"},
|
||||
{"127.0.0.0-2|/dump/info", "", "127.0.0.1", "/dump/info", "200"},
|
||||
{"127.0.0.0-2|/dump/info", "", "127.0.0.1", "/dump/test", "403"},
|
||||
{"127.0.0.0-2|/dump/info", "", "127.0.0.1", "/", "403", false},
|
||||
{"127.0.0.0-2|/dump/info", "", "127.0.0.1", "/dispatch", "403", false},
|
||||
{"127.0.0.0-2|/dump/info", "", "127.0.0.1", "/dump/info", "200", false},
|
||||
{"127.0.0.0-2|/dump/info", "", "127.0.0.1", "/dump/test", "403", false},
|
||||
|
||||
{"127.0.0.0-2|/dump/info;127.0.0.0-2|/dump/test", "", "127.0.0.1", "/", "403"},
|
||||
{"127.0.0.0-2|/dump/info;127.0.0.0-2|/dump/test", "", "127.0.0.1", "/dispatch", "403"},
|
||||
{"127.0.0.0-2|/dump/info;127.0.0.0-2|/dump/test", "", "127.0.0.1", "/dump/info", "200"},
|
||||
{"127.0.0.0-2|/dump/info;127.0.0.0-2|/dump/test", "", "127.0.0.1", "/dump/test", "200"},
|
||||
{"127.0.0.0-2|/dump/info;127.0.0.0-2|/dump/test", "", "127.0.0.1", "/dump/fail", "403"},
|
||||
{"127.0.0.0-2|/dump/info;127.0.0.0-2|/dump/test", "", "127.0.0.1", "/", "403", false},
|
||||
{"127.0.0.0-2|/dump/info;127.0.0.0-2|/dump/test", "", "127.0.0.1", "/dispatch", "403", false},
|
||||
{"127.0.0.0-2|/dump/info;127.0.0.0-2|/dump/test", "", "127.0.0.1", "/dump/info", "200", false},
|
||||
{"127.0.0.0-2|/dump/info;127.0.0.0-2|/dump/test", "", "127.0.0.1", "/dump/test", "200", false},
|
||||
{"127.0.0.0-2|/dump/info;127.0.0.0-2|/dump/test", "", "127.0.0.1", "/dump/fail", "403", false},
|
||||
|
||||
// Black list
|
||||
{"", "127.0.0.1", "127.0.0.1", "/", "403"},
|
||||
{"", "127.0.0.1", "127.0.0.1", "/dispatch", "403"},
|
||||
{"", "127.0.0.1", "127.0.0.1", "/dump/info", "403"},
|
||||
{"", "127.0.0.1", "127.0.0.1", "/", "403", false},
|
||||
{"", "127.0.0.1", "127.0.0.1", "/dispatch", "403", false},
|
||||
{"", "127.0.0.1", "127.0.0.1", "/dump/info", "403", false},
|
||||
|
||||
{"", "127.0.0.1|/", "127.0.0.1", "/", "403"},
|
||||
{"", "127.0.0.1|/", "127.0.0.1", "/dispatch", "200"},
|
||||
{"", "127.0.0.1|/", "127.0.0.1", "/dump/info", "200"},
|
||||
{"", "127.0.0.1|/", "127.0.0.1", "/", "403", false},
|
||||
{"", "127.0.0.1|/", "127.0.0.1", "/dispatch", "200", false},
|
||||
{"", "127.0.0.1|/", "127.0.0.1", "/dump/info", "200", false},
|
||||
|
||||
{"", "127.0.0.1|/*", "127.0.0.1", "/", "403"},
|
||||
{"", "127.0.0.1|/*", "127.0.0.1", "/dispatch", "403"},
|
||||
{"", "127.0.0.1|/*", "127.0.0.1", "/dump/info", "403"},
|
||||
{"", "127.0.0.1|/*", "127.0.0.1", "/", "403", false},
|
||||
{"", "127.0.0.1|/*", "127.0.0.1", "/dispatch", "403", false},
|
||||
{"", "127.0.0.1|/*", "127.0.0.1", "/dump/info", "403", false},
|
||||
|
||||
{"", "127.0.0.1|/dump/*", "127.0.0.1", "/", "200"},
|
||||
{"", "127.0.0.1|/dump/*", "127.0.0.1", "/dispatch", "200"},
|
||||
{"", "127.0.0.1|/dump/*", "127.0.0.1", "/dump/info", "403"},
|
||||
{"", "127.0.0.1|/dump/*", "127.0.0.1", "/dump/test", "403"},
|
||||
{"", "127.0.0.1|/dump/*", "127.0.0.1", "/", "200", false},
|
||||
{"", "127.0.0.1|/dump/*", "127.0.0.1", "/dispatch", "200", false},
|
||||
{"", "127.0.0.1|/dump/*", "127.0.0.1", "/dump/info", "403", false},
|
||||
{"", "127.0.0.1|/dump/*", "127.0.0.1", "/dump/test", "403", false},
|
||||
|
||||
{"", "127.0.0.1|/dump/info", "127.0.0.1", "/", "200"},
|
||||
{"", "127.0.0.1|/dump/info", "127.0.0.1", "/dispatch", "200"},
|
||||
{"", "127.0.0.1|/dump/info", "127.0.0.1", "/dump/info", "403"},
|
||||
{"", "127.0.0.1|/dump/info", "127.0.0.1", "/dump/test", "200"},
|
||||
{"", "127.0.0.1|/dump/info", "127.0.0.1", "/", "200", false},
|
||||
{"", "127.0.0.1|/dump/info", "127.0.0.1", "/dispatch", "200", false},
|
||||
{"", "127.0.0.1|/dump/info", "127.0.0.1", "/dump/info", "403", false},
|
||||
{"", "127.0.0.1|/dump/info", "127.0.0.1", "/dump/test", "200", false},
|
||||
|
||||
{"", "127.0.0.1|/dump/info;127.0.0.1|/dump/test", "127.0.0.1", "/", "200"},
|
||||
{"", "127.0.0.1|/dump/info;127.0.0.1|/dump/test", "127.0.0.1", "/dispatch", "200"},
|
||||
{"", "127.0.0.1|/dump/info;127.0.0.1|/dump/test", "127.0.0.1", "/dump/info", "403"},
|
||||
{"", "127.0.0.1|/dump/info;127.0.0.1|/dump/test", "127.0.0.1", "/dump/test", "403"},
|
||||
{"", "127.0.0.1|/dump/info;127.0.0.1|/dump/test", "127.0.0.1", "/dump/fail", "200"},
|
||||
{"", "127.0.0.1|/dump/info;127.0.0.1|/dump/test", "127.0.0.1", "/", "200", false},
|
||||
{"", "127.0.0.1|/dump/info;127.0.0.1|/dump/test", "127.0.0.1", "/dispatch", "200", false},
|
||||
{"", "127.0.0.1|/dump/info;127.0.0.1|/dump/test", "127.0.0.1", "/dump/info", "403", false},
|
||||
{"", "127.0.0.1|/dump/info;127.0.0.1|/dump/test", "127.0.0.1", "/dump/test", "403", false},
|
||||
{"", "127.0.0.1|/dump/info;127.0.0.1|/dump/test", "127.0.0.1", "/dump/fail", "200", false},
|
||||
|
||||
{"", "127.0.0.0-2|", "127.0.0.1", "/", "403"},
|
||||
{"", "127.0.0.0-2|", "127.0.0.1", "/dump/info", "403"},
|
||||
{"", "127.0.0.0-2|", "127.0.0.1", "/", "403", false},
|
||||
{"", "127.0.0.0-2|", "127.0.0.1", "/dump/info", "200", false},
|
||||
|
||||
{"", "127.0.0.0-2|/", "127.0.0.1", "/", "403"},
|
||||
{"", "127.0.0.0-2|/", "127.0.0.1", "/dispatch", "200"},
|
||||
{"", "127.0.0.0-2|/", "127.0.0.1", "/dump/info", "200"},
|
||||
{"", "127.0.0.0-2|/", "127.0.0.1", "/", "403", false},
|
||||
{"", "127.0.0.0-2|/", "127.0.0.1", "/dispatch", "200", false},
|
||||
{"", "127.0.0.0-2|/", "127.0.0.1", "/dump/info", "200", false},
|
||||
|
||||
{"", "127.0.0.0-2|/dump/*", "127.0.0.1", "/", "200"},
|
||||
{"", "127.0.0.0-2|/dump/*", "127.0.0.1", "/dispatch", "200"},
|
||||
{"", "127.0.0.0-2|/dump/*", "127.0.0.1", "/dump/info", "403"},
|
||||
{"", "127.0.0.0-2|/dump/*", "127.0.0.1", "/", "200", false},
|
||||
{"", "127.0.0.0-2|/dump/*", "127.0.0.1", "/dispatch", "200", false},
|
||||
{"", "127.0.0.0-2|/dump/*", "127.0.0.1", "/dump/info", "403", false},
|
||||
|
||||
{"", "127.0.0.0-2|/dump/info", "127.0.0.1", "/", "200"},
|
||||
{"", "127.0.0.0-2|/dump/info", "127.0.0.1", "/dispatch", "200"},
|
||||
{"", "127.0.0.0-2|/dump/info", "127.0.0.1", "/dump/info", "403"},
|
||||
{"", "127.0.0.0-2|/dump/info", "127.0.0.1", "/dump/test", "200"},
|
||||
{"", "127.0.0.0-2|/dump/info", "127.0.0.1", "/", "200", false},
|
||||
{"", "127.0.0.0-2|/dump/info", "127.0.0.1", "/dispatch", "200", false},
|
||||
{"", "127.0.0.0-2|/dump/info", "127.0.0.1", "/dump/info", "403", false},
|
||||
{"", "127.0.0.0-2|/dump/info", "127.0.0.1", "/dump/test", "200", false},
|
||||
|
||||
{"", "127.0.0.0-2|/dump/info;127.0.0.0-2|/dump/test", "127.0.0.1", "/", "200"},
|
||||
{"", "127.0.0.0-2|/dump/info;127.0.0.0-2|/dump/test", "127.0.0.1", "/dispatch", "200"},
|
||||
{"", "127.0.0.0-2|/dump/info;127.0.0.0-2|/dump/test", "127.0.0.1", "/dump/info", "403"},
|
||||
{"", "127.0.0.0-2|/dump/info;127.0.0.0-2|/dump/test", "127.0.0.1", "/dump/test", "403"},
|
||||
{"", "127.0.0.0-2|/dump/info;127.0.0.0-2|/dump/test", "127.0.0.1", "/dump/fail", "200"},
|
||||
{"", "127.0.0.0-2|/dump/info;127.0.0.0-2|/dump/test", "127.0.0.1", "/", "200", false},
|
||||
{"", "127.0.0.0-2|/dump/info;127.0.0.0-2|/dump/test", "127.0.0.1", "/dispatch", "200", false},
|
||||
{"", "127.0.0.0-2|/dump/info;127.0.0.0-2|/dump/test", "127.0.0.1", "/dump/info", "403", false},
|
||||
{"", "127.0.0.0-2|/dump/info;127.0.0.0-2|/dump/test", "127.0.0.1", "/dump/test", "403", false},
|
||||
{"", "127.0.0.0-2|/dump/info;127.0.0.0-2|/dump/test", "127.0.0.1", "/dump/fail", "200", false},
|
||||
|
||||
// Both lists
|
||||
{"127.0.0.1|/dump", "127.0.0.1|/dump/fail", "127.0.0.1", "/dump", "200"},
|
||||
{"127.0.0.1|/dump", "127.0.0.1|/dump/fail", "127.0.0.1", "/dump/info", "403"},
|
||||
{"127.0.0.1|/dump", "127.0.0.1|/dump/fail", "127.0.0.1", "/dump/fail", "403"},
|
||||
{"127.0.0.1|/dump", "127.0.0.1|/dump/fail", "127.0.0.1", "/dump", "200", false},
|
||||
{"127.0.0.1|/dump", "127.0.0.1|/dump/fail", "127.0.0.1", "/dump/info", "403", false},
|
||||
{"127.0.0.1|/dump", "127.0.0.1|/dump/fail", "127.0.0.1", "/dump/fail", "403", false},
|
||||
|
||||
{"127.0.0.1|/dump/*", "127.0.0.1|/dump/fail", "127.0.0.1", "/dump", "200"},
|
||||
{"127.0.0.1|/dump/*", "127.0.0.1|/dump/fail", "127.0.0.1", "/dump/info", "200"},
|
||||
{"127.0.0.1|/dump/*", "127.0.0.1|/dump/fail", "127.0.0.1", "/dump/fail", "403"},
|
||||
{"127.0.0.1|/dump/*", "127.0.0.1|/dump/fail", "127.0.0.1", "/dump", "200", false},
|
||||
{"127.0.0.1|/dump/*", "127.0.0.1|/dump/fail", "127.0.0.1", "/dump/info", "200", false},
|
||||
{"127.0.0.1|/dump/*", "127.0.0.1|/dump/fail", "127.0.0.1", "/dump/fail", "403", false},
|
||||
|
||||
{"127.0.0.1|/dump/*", "127.0.0.1|/dump/test;127.0.0.1|/dump/fail", "127.0.0.1", "/dump", "200"},
|
||||
{"127.0.0.1|/dump/*", "127.0.0.1|/dump/test;127.0.0.1|/dump/fail", "127.0.0.1", "/dump/info", "200"},
|
||||
{"127.0.0.1|/dump/*", "127.0.0.1|/dump/test;127.0.0.1|/dump/fail", "127.0.0.1", "/dump/test", "403"},
|
||||
{"127.0.0.1|/dump/*", "127.0.0.1|/dump/test;127.0.0.1|/dump/fail", "127.0.0.1", "/dump/fail", "403"},
|
||||
{"127.0.0.1|/dump/*", "127.0.0.1|/dump/test;127.0.0.1|/dump/fail", "127.0.0.1", "/dump", "200", false},
|
||||
{"127.0.0.1|/dump/*", "127.0.0.1|/dump/test;127.0.0.1|/dump/fail", "127.0.0.1", "/dump/info", "200", false},
|
||||
{"127.0.0.1|/dump/*", "127.0.0.1|/dump/test;127.0.0.1|/dump/fail", "127.0.0.1", "/dump/test", "403", false},
|
||||
{"127.0.0.1|/dump/*", "127.0.0.1|/dump/test;127.0.0.1|/dump/fail", "127.0.0.1", "/dump/fail", "403", false},
|
||||
|
||||
{"127.0.0.1|/dump/info;127.0.0.1|/dump/test", "127.0.0.1|/dump/test", "127.0.0.1", "/dump", "403"},
|
||||
{"127.0.0.1|/dump/info;127.0.0.1|/dump/test", "127.0.0.1|/dump/test", "127.0.0.1", "/dump/info", "200"},
|
||||
{"127.0.0.1|/dump/info;127.0.0.1|/dump/test", "127.0.0.1|/dump/test", "127.0.0.1", "/dump/test", "403"},
|
||||
{"127.0.0.1|/dump/info;127.0.0.1|/dump/test", "127.0.0.1|/dump/test", "127.0.0.1", "/dump/fail", "403"},
|
||||
{"127.0.0.1|/dump/info;127.0.0.1|/dump/test", "127.0.0.1|/dump/test", "127.0.0.1", "/dump", "403", false},
|
||||
{"127.0.0.1|/dump/info;127.0.0.1|/dump/test", "127.0.0.1|/dump/test", "127.0.0.1", "/dump/info", "200", false},
|
||||
{"127.0.0.1|/dump/info;127.0.0.1|/dump/test", "127.0.0.1|/dump/test", "127.0.0.1", "/dump/test", "403", false},
|
||||
{"127.0.0.1|/dump/info;127.0.0.1|/dump/test", "127.0.0.1|/dump/test", "127.0.0.1", "/dump/fail", "403", false},
|
||||
|
||||
{"127.0.0.1|/;127.0.0.0-2|/dump/*", "127.0.0.0,1|/dump/fail", "127.0.0.1", "/", "200"},
|
||||
{"127.0.0.1|/;127.0.0.0-2|/dump/*", "127.0.0.0,1|/dump/fail", "127.0.0.1", "/dump/info", "200"},
|
||||
{"127.0.0.1|/;127.0.0.0-2|/dump/*", "127.0.0.0,1|/dump/fail", "127.0.0.1", "/dump/fail", "403"},
|
||||
{"127.0.0.1|/;127.0.0.0-2|/dump/*", "127.0.0.0,1|/dump/fail", "127.0.0.1", "/", "200", false},
|
||||
{"127.0.0.1|/;127.0.0.0-2|/dump/*", "127.0.0.0,1|/dump/fail", "127.0.0.1", "/dump/info", "200", false},
|
||||
{"127.0.0.1|/;127.0.0.0-2|/dump/*", "127.0.0.0,1|/dump/fail", "127.0.0.1", "/dump/fail", "403", false},
|
||||
|
||||
// Different address
|
||||
{"127.0.0.2", "", "127.0.0.1", "/", "403"},
|
||||
{"127.0.0.2", "", "127.0.0.1", "/dump/info", "403"},
|
||||
{"127.0.0.2", "", "127.0.0.1", "/", "403", false},
|
||||
{"127.0.0.2", "", "127.0.0.1", "/dump/info", "403", false},
|
||||
|
||||
{"127.0.0.2|/dump/*", "", "127.0.0.1", "/", "403"},
|
||||
{"127.0.0.2|/dump/*", "", "127.0.0.1", "/dump/info", "403"},
|
||||
{"127.0.0.2|/dump/*", "", "127.0.0.1", "/", "403", false},
|
||||
{"127.0.0.2|/dump/*", "", "127.0.0.1", "/dump/info", "403", false},
|
||||
|
||||
{"127.0.0.2|/dump/info", "", "127.0.0.1", "/", "403"},
|
||||
{"127.0.0.2|/dump/info", "", "127.0.0.1", "/dump/info", "403"},
|
||||
{"127.0.0.2|/dump/info", "", "127.0.0.1", "/dump/test", "403"},
|
||||
{"127.0.0.2|/dump/info", "", "127.0.0.1", "/", "403", false},
|
||||
{"127.0.0.2|/dump/info", "", "127.0.0.1", "/dump/info", "403", false},
|
||||
{"127.0.0.2|/dump/info", "", "127.0.0.1", "/dump/test", "403", false},
|
||||
|
||||
{"127.0.0.1|/dump/info;127.0.0.2|/dump/test", "", "127.0.0.1", "/", "403"},
|
||||
{"127.0.0.1|/dump/info;127.0.0.2|/dump/test", "", "127.0.0.1", "/dispatch", "403"},
|
||||
{"127.0.0.1|/dump/info;127.0.0.2|/dump/test", "", "127.0.0.1", "/dump/info", "200"},
|
||||
{"127.0.0.1|/dump/info;127.0.0.2|/dump/test", "", "127.0.0.1", "/dump/test", "403"},
|
||||
{"127.0.0.1|/dump/info;127.0.0.2|/dump/test", "", "127.0.0.1", "/dump/fail", "403"},
|
||||
{"127.0.0.1|/dump/info;127.0.0.2|/dump/test", "", "127.0.0.1", "/", "403", false},
|
||||
{"127.0.0.1|/dump/info;127.0.0.2|/dump/test", "", "127.0.0.1", "/dispatch", "403", false},
|
||||
{"127.0.0.1|/dump/info;127.0.0.2|/dump/test", "", "127.0.0.1", "/dump/info", "200", false},
|
||||
{"127.0.0.1|/dump/info;127.0.0.2|/dump/test", "", "127.0.0.1", "/dump/test", "403", false},
|
||||
{"127.0.0.1|/dump/info;127.0.0.2|/dump/test", "", "127.0.0.1", "/dump/fail", "403", false},
|
||||
|
||||
{"172.0.0.0-255", "", "127.0.0.1", "/", "403"},
|
||||
{"172.0.0.0-255", "", "127.0.0.1", "/dump/info", "403"},
|
||||
{"172.0.0.0-255", "", "127.0.0.1", "/", "403", false},
|
||||
{"172.0.0.0-255", "", "127.0.0.1", "/dump/info", "403", false},
|
||||
|
||||
{"172.0.0.0-255|/dump/*;127.0.0.0-255|/dump/*", "", "127.0.0.1", "/", "403"},
|
||||
{"172.0.0.0-255|/dump/*;127.0.0.0-255|/dump/*", "", "127.0.0.1", "/dispatch", "403"},
|
||||
{"172.0.0.0-255|/dump/*;127.0.0.0-255|/dump/*", "", "127.0.0.1", "/dump/info", "200"},
|
||||
{"172.0.0.0-255|/dump/*;127.0.0.0-255|/dump/*", "", "127.0.0.1", "/", "403", false},
|
||||
{"172.0.0.0-255|/dump/*;127.0.0.0-255|/dump/*", "", "127.0.0.1", "/dispatch", "403", false},
|
||||
{"172.0.0.0-255|/dump/*;127.0.0.0-255|/dump/*", "", "127.0.0.1", "/dump/info", "200", false},
|
||||
|
||||
/*-----------------------------------------------------------------------------------------*/
|
||||
// Match by path starts with [117]
|
||||
// test cases affected by _whiteListByPath highlighted accordingly
|
||||
|
||||
{"", "", "127.0.0.1", "/", "200", true},
|
||||
{"", "", "127.0.0.1", "/dump/info", "200", true},
|
||||
|
||||
// White list
|
||||
{"127.0.0.1", "", "127.0.0.1", "/", "200", true},
|
||||
{"127.0.0.1", "", "127.0.0.1", "/dispatch", "200", true},
|
||||
{"127.0.0.1", "", "127.0.0.1", "/dump/info", "200", true},
|
||||
|
||||
{"127.0.0.1|/", "", "127.0.0.1", "/", "200", true},
|
||||
{"127.0.0.1|/", "", "127.0.0.1", "/dispatch", "200", true}, // _whiteListByPath
|
||||
{"127.0.0.1|/", "", "127.0.0.1", "/dump/info", "200", true}, // _whiteListByPath
|
||||
|
||||
{"127.0.0.1|/*", "", "127.0.0.1", "/", "200", true},
|
||||
{"127.0.0.1|/*", "", "127.0.0.1", "/dispatch", "200", true},
|
||||
{"127.0.0.1|/*", "", "127.0.0.1", "/dump/info", "200", true},
|
||||
|
||||
{"127.0.0.1|/dump/*", "", "127.0.0.1", "/", "200", true}, // _whiteListByPath
|
||||
{"127.0.0.1|/dump/*", "", "127.0.0.1", "/dispatch", "200", true}, // _whiteListByPath
|
||||
{"127.0.0.1|/dump/*", "", "127.0.0.1", "/dump/info", "200", true},
|
||||
{"127.0.0.1|/dump/*", "", "127.0.0.1", "/dump/test", "200", true},
|
||||
|
||||
{"127.0.0.1|/dump/info", "", "127.0.0.1", "/", "200", true}, // _whiteListByPath
|
||||
{"127.0.0.1|/dump/info", "", "127.0.0.1", "/dispatch", "200", true}, // _whiteListByPath
|
||||
{"127.0.0.1|/dump/info", "", "127.0.0.1", "/dump/info", "200", true},
|
||||
{"127.0.0.1|/dump/info", "", "127.0.0.1", "/dump/test", "200", true}, // _whiteListByPath
|
||||
|
||||
{"127.0.0.1|/dump/info;127.0.0.1|/dump/test", "", "127.0.0.1", "/", "200", true}, // _whiteListByPath
|
||||
{"127.0.0.1|/dump/info;127.0.0.1|/dump/test", "", "127.0.0.1", "/dispatch", "200", true}, // _whiteListByPath
|
||||
{"127.0.0.1|/dump/info;127.0.0.1|/dump/test", "", "127.0.0.1", "/dump/info", "200", true},
|
||||
{"127.0.0.1|/dump/info;127.0.0.1|/dump/test", "", "127.0.0.1", "/dump/test", "200", true},
|
||||
{"127.0.0.1|/dump/info;127.0.0.1|/dump/test", "", "127.0.0.1", "/dump/fail", "200", true}, // _whiteListByPath
|
||||
|
||||
{"127.0.0.0-2|", "", "127.0.0.1", "/", "200", true},
|
||||
{"127.0.0.0-2|", "", "127.0.0.1", "/dump/info", "200", true},
|
||||
|
||||
{"127.0.0.0-2|/", "", "127.0.0.1", "/", "200", true},
|
||||
{"127.0.0.0-2|/", "", "127.0.0.1", "/dispatch", "200", true}, // _whiteListByPath
|
||||
{"127.0.0.0-2|/", "", "127.0.0.1", "/dump/info", "200", true}, // _whiteListByPath
|
||||
|
||||
{"127.0.0.0-2|/dump/*", "", "127.0.0.1", "/", "200", true}, // _whiteListByPath
|
||||
{"127.0.0.0-2|/dump/*", "", "127.0.0.1", "/dispatch", "200", true}, // _whiteListByPath
|
||||
{"127.0.0.0-2|/dump/*", "", "127.0.0.1", "/dump/info", "200", true},
|
||||
|
||||
{"127.0.0.0-2|/dump/info", "", "127.0.0.1", "/", "200", true}, // _whiteListByPath
|
||||
{"127.0.0.0-2|/dump/info", "", "127.0.0.1", "/dispatch", "200", true}, // _whiteListByPath
|
||||
{"127.0.0.0-2|/dump/info", "", "127.0.0.1", "/dump/info", "200", true},
|
||||
{"127.0.0.0-2|/dump/info", "", "127.0.0.1", "/dump/test", "200", true}, // _whiteListByPath
|
||||
|
||||
{"127.0.0.0-2|/dump/info;127.0.0.0-2|/dump/test", "", "127.0.0.1", "/", "200", true}, // _whiteListByPath
|
||||
{"127.0.0.0-2|/dump/info;127.0.0.0-2|/dump/test", "", "127.0.0.1", "/dispatch", "200", true}, // _whiteListByPath
|
||||
{"127.0.0.0-2|/dump/info;127.0.0.0-2|/dump/test", "", "127.0.0.1", "/dump/info", "200", true},
|
||||
{"127.0.0.0-2|/dump/info;127.0.0.0-2|/dump/test", "", "127.0.0.1", "/dump/test", "200", true},
|
||||
{"127.0.0.0-2|/dump/info;127.0.0.0-2|/dump/test", "", "127.0.0.1", "/dump/fail", "200", true}, // _whiteListByPath
|
||||
|
||||
// Black list
|
||||
{"", "127.0.0.1", "127.0.0.1", "/", "403", true},
|
||||
{"", "127.0.0.1", "127.0.0.1", "/dispatch", "403", true},
|
||||
{"", "127.0.0.1", "127.0.0.1", "/dump/info", "403", true},
|
||||
|
||||
{"", "127.0.0.1|/", "127.0.0.1", "/", "403", true},
|
||||
{"", "127.0.0.1|/", "127.0.0.1", "/dispatch", "200", true},
|
||||
{"", "127.0.0.1|/", "127.0.0.1", "/dump/info", "200", true},
|
||||
|
||||
{"", "127.0.0.1|/*", "127.0.0.1", "/", "403", true},
|
||||
{"", "127.0.0.1|/*", "127.0.0.1", "/dispatch", "403", true},
|
||||
{"", "127.0.0.1|/*", "127.0.0.1", "/dump/info", "403", true},
|
||||
|
||||
{"", "127.0.0.1|/dump/*", "127.0.0.1", "/", "200", true},
|
||||
{"", "127.0.0.1|/dump/*", "127.0.0.1", "/dispatch", "200", true},
|
||||
{"", "127.0.0.1|/dump/*", "127.0.0.1", "/dump/info", "403", true},
|
||||
{"", "127.0.0.1|/dump/*", "127.0.0.1", "/dump/test", "403", true},
|
||||
|
||||
{"", "127.0.0.1|/dump/info", "127.0.0.1", "/", "200", true},
|
||||
{"", "127.0.0.1|/dump/info", "127.0.0.1", "/dispatch", "200", true},
|
||||
{"", "127.0.0.1|/dump/info", "127.0.0.1", "/dump/info", "403", true},
|
||||
{"", "127.0.0.1|/dump/info", "127.0.0.1", "/dump/test", "200", true},
|
||||
|
||||
{"", "127.0.0.1|/dump/info;127.0.0.1|/dump/test", "127.0.0.1", "/", "200", true},
|
||||
{"", "127.0.0.1|/dump/info;127.0.0.1|/dump/test", "127.0.0.1", "/dispatch", "200", true},
|
||||
{"", "127.0.0.1|/dump/info;127.0.0.1|/dump/test", "127.0.0.1", "/dump/info", "403", true},
|
||||
{"", "127.0.0.1|/dump/info;127.0.0.1|/dump/test", "127.0.0.1", "/dump/test", "403", true},
|
||||
{"", "127.0.0.1|/dump/info;127.0.0.1|/dump/test", "127.0.0.1", "/dump/fail", "200", true},
|
||||
|
||||
{"", "127.0.0.0-2|", "127.0.0.1", "/", "403", true},
|
||||
{"", "127.0.0.0-2|", "127.0.0.1", "/dump/info", "200", true},
|
||||
|
||||
{"", "127.0.0.0-2|/", "127.0.0.1", "/", "403", true},
|
||||
{"", "127.0.0.0-2|/", "127.0.0.1", "/dispatch", "200", true},
|
||||
{"", "127.0.0.0-2|/", "127.0.0.1", "/dump/info", "200", true},
|
||||
|
||||
{"", "127.0.0.0-2|/dump/*", "127.0.0.1", "/", "200", true},
|
||||
{"", "127.0.0.0-2|/dump/*", "127.0.0.1", "/dispatch", "200", true},
|
||||
{"", "127.0.0.0-2|/dump/*", "127.0.0.1", "/dump/info", "403", true},
|
||||
|
||||
{"", "127.0.0.0-2|/dump/info", "127.0.0.1", "/", "200", true},
|
||||
{"", "127.0.0.0-2|/dump/info", "127.0.0.1", "/dispatch", "200", true},
|
||||
{"", "127.0.0.0-2|/dump/info", "127.0.0.1", "/dump/info", "403", true},
|
||||
{"", "127.0.0.0-2|/dump/info", "127.0.0.1", "/dump/test", "200", true},
|
||||
|
||||
{"", "127.0.0.0-2|/dump/info;127.0.0.0-2|/dump/test", "127.0.0.1", "/", "200", true},
|
||||
{"", "127.0.0.0-2|/dump/info;127.0.0.0-2|/dump/test", "127.0.0.1", "/dispatch", "200", true},
|
||||
{"", "127.0.0.0-2|/dump/info;127.0.0.0-2|/dump/test", "127.0.0.1", "/dump/info", "403", true},
|
||||
{"", "127.0.0.0-2|/dump/info;127.0.0.0-2|/dump/test", "127.0.0.1", "/dump/test", "403", true},
|
||||
{"", "127.0.0.0-2|/dump/info;127.0.0.0-2|/dump/test", "127.0.0.1", "/dump/fail", "200", true},
|
||||
|
||||
// Both lists
|
||||
{"127.0.0.1|/dump", "127.0.0.1|/dump/fail", "127.0.0.1", "/dump", "200", true},
|
||||
{"127.0.0.1|/dump", "127.0.0.1|/dump/fail", "127.0.0.1", "/dump/info", "200", true}, // _whiteListByPath
|
||||
{"127.0.0.1|/dump", "127.0.0.1|/dump/fail", "127.0.0.1", "/dump/fail", "403", true},
|
||||
|
||||
{"127.0.0.1|/dump/*", "127.0.0.1|/dump/fail", "127.0.0.1", "/dump", "200", true},
|
||||
{"127.0.0.1|/dump/*", "127.0.0.1|/dump/fail", "127.0.0.1", "/dump/info", "200", true},
|
||||
{"127.0.0.1|/dump/*", "127.0.0.1|/dump/fail", "127.0.0.1", "/dump/fail", "403", true},
|
||||
|
||||
{"127.0.0.1|/dump/*", "127.0.0.1|/dump/test;127.0.0.1|/dump/fail", "127.0.0.1", "/dump", "200", true},
|
||||
{"127.0.0.1|/dump/*", "127.0.0.1|/dump/test;127.0.0.1|/dump/fail", "127.0.0.1", "/dump/info", "200", true},
|
||||
{"127.0.0.1|/dump/*", "127.0.0.1|/dump/test;127.0.0.1|/dump/fail", "127.0.0.1", "/dump/test", "403", true},
|
||||
{"127.0.0.1|/dump/*", "127.0.0.1|/dump/test;127.0.0.1|/dump/fail", "127.0.0.1", "/dump/fail", "403", true},
|
||||
|
||||
{"127.0.0.1|/dump/info;127.0.0.1|/dump/test", "127.0.0.1|/dump/test", "127.0.0.1", "/dump", "200", true}, // _whiteListByPath
|
||||
{"127.0.0.1|/dump/info;127.0.0.1|/dump/test", "127.0.0.1|/dump/test", "127.0.0.1", "/dump/info", "200", true},
|
||||
{"127.0.0.1|/dump/info;127.0.0.1|/dump/test", "127.0.0.1|/dump/test", "127.0.0.1", "/dump/test", "403", true},
|
||||
{"127.0.0.1|/dump/info;127.0.0.1|/dump/test", "127.0.0.1|/dump/test", "127.0.0.1", "/dump/fail", "200", true}, // _whiteListByPath
|
||||
|
||||
{"127.0.0.1|/;127.0.0.0-2|/dump/*", "127.0.0.0,1|/dump/fail", "127.0.0.1", "/", "200", true},
|
||||
{"127.0.0.1|/;127.0.0.0-2|/dump/*", "127.0.0.0,1|/dump/fail", "127.0.0.1", "/dump/info", "200", true},
|
||||
{"127.0.0.1|/;127.0.0.0-2|/dump/*", "127.0.0.0,1|/dump/fail", "127.0.0.1", "/dump/fail", "403", true},
|
||||
|
||||
// Different address
|
||||
{"127.0.0.2", "", "127.0.0.1", "/", "403", true},
|
||||
{"127.0.0.2", "", "127.0.0.1", "/dump/info", "403", true},
|
||||
|
||||
{"127.0.0.2|/dump/*", "", "127.0.0.1", "/", "200", true}, // _whiteListByPath
|
||||
{"127.0.0.2|/dump/*", "", "127.0.0.1", "/dump/info", "403", true},
|
||||
|
||||
{"127.0.0.2|/dump/info", "", "127.0.0.1", "/", "200", true}, // _whiteListByPath
|
||||
{"127.0.0.2|/dump/info", "", "127.0.0.1", "/dump/info", "403", true},
|
||||
{"127.0.0.2|/dump/info", "", "127.0.0.1", "/dump/test", "200", true}, // _whiteListByPath
|
||||
|
||||
{"127.0.0.1|/dump/info;127.0.0.2|/dump/test", "", "127.0.0.1", "/", "200", true}, // _whiteListByPath
|
||||
{"127.0.0.1|/dump/info;127.0.0.2|/dump/test", "", "127.0.0.1", "/dispatch", "200", true}, // _whiteListByPath
|
||||
{"127.0.0.1|/dump/info;127.0.0.2|/dump/test", "", "127.0.0.1", "/dump/info", "200", true},
|
||||
{"127.0.0.1|/dump/info;127.0.0.2|/dump/test", "", "127.0.0.1", "/dump/test", "403", true},
|
||||
{"127.0.0.1|/dump/info;127.0.0.2|/dump/test", "", "127.0.0.1", "/dump/fail", "200", true}, // _whiteListByPath
|
||||
|
||||
{"172.0.0.0-255", "", "127.0.0.1", "/", "403", true},
|
||||
{"172.0.0.0-255", "", "127.0.0.1", "/dump/info", "403", true},
|
||||
|
||||
{"172.0.0.0-255|/dump/*;127.0.0.0-255|/dump/*", "", "127.0.0.1", "/", "200", true}, // _whiteListByPath
|
||||
{"172.0.0.0-255|/dump/*;127.0.0.0-255|/dump/*", "", "127.0.0.1", "/dispatch", "200", true}, // _whiteListByPath
|
||||
{"172.0.0.0-255|/dump/*;127.0.0.0-255|/dump/*", "", "127.0.0.1", "/dump/info", "200", true},
|
||||
};
|
||||
return Arrays.asList(data);
|
||||
};
|
||||
|
|
|
@ -354,6 +354,12 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
if (_resourceBase!=null)
|
||||
{
|
||||
r = _resourceBase.addPath(pathInContext);
|
||||
if (!_contextHandler.checkAlias(pathInContext,r))
|
||||
r=null;
|
||||
}
|
||||
else if (_servletContext instanceof ContextHandler.Context)
|
||||
{
|
||||
r = _contextHandler.getResource(pathInContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -18,24 +18,18 @@
|
|||
|
||||
package org.eclipse.jetty.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.server.Dispatcher;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
import org.eclipse.jetty.server.handler.ErrorHandler;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Error Page Error Handler
|
||||
|
@ -44,11 +38,8 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
* the internal ERROR style of dispatch.
|
||||
*
|
||||
*/
|
||||
public class ErrorPageErrorHandler extends ErrorHandler
|
||||
public class ErrorPageErrorHandler extends ErrorHandler implements ErrorHandler.ErrorPageMapper
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(ErrorPageErrorHandler.class);
|
||||
|
||||
public final static String ERROR_PAGE="org.eclipse.jetty.server.error_page";
|
||||
public final static String GLOBAL_ERROR_PAGE = "org.eclipse.jetty.server.error_page.global";
|
||||
|
||||
protected ServletContext _servletContext;
|
||||
|
@ -59,104 +50,63 @@ public class ErrorPageErrorHandler extends ErrorHandler
|
|||
public ErrorPageErrorHandler()
|
||||
{}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @see org.eclipse.jetty.server.handler.ErrorHandler#handle(String, Request, HttpServletRequest, HttpServletResponse)
|
||||
*/
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
public String getErrorPage(HttpServletRequest request)
|
||||
{
|
||||
String method = request.getMethod();
|
||||
if (!HttpMethod.GET.is(method) && !HttpMethod.POST.is(method) && !HttpMethod.HEAD.is(method))
|
||||
String error_page= null;
|
||||
Class<?> exClass= (Class<?>)request.getAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE);
|
||||
|
||||
if (ServletException.class.equals(exClass))
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
return;
|
||||
error_page= (String)_errorPages.get(exClass.getName());
|
||||
if (error_page == null)
|
||||
{
|
||||
Throwable th= (Throwable)request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
|
||||
while (th instanceof ServletException)
|
||||
th= ((ServletException)th).getRootCause();
|
||||
if (th != null)
|
||||
exClass= th.getClass();
|
||||
}
|
||||
}
|
||||
if (_errorPages!=null)
|
||||
|
||||
while (error_page == null && exClass != null )
|
||||
{
|
||||
String error_page= null;
|
||||
Class<?> exClass= (Class<?>)request.getAttribute(Dispatcher.ERROR_EXCEPTION_TYPE);
|
||||
error_page= (String)_errorPages.get(exClass.getName());
|
||||
exClass= exClass.getSuperclass();
|
||||
}
|
||||
|
||||
if (ServletException.class.equals(exClass))
|
||||
if (error_page == null)
|
||||
{
|
||||
// look for an exact code match
|
||||
Integer code=(Integer)request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
|
||||
if (code!=null)
|
||||
{
|
||||
error_page= (String)_errorPages.get(exClass.getName());
|
||||
if (error_page == null)
|
||||
error_page= (String)_errorPages.get(Integer.toString(code));
|
||||
|
||||
// if still not found
|
||||
if ((error_page == null) && (_errorPageList != null))
|
||||
{
|
||||
Throwable th= (Throwable)request.getAttribute(Dispatcher.ERROR_EXCEPTION);
|
||||
while (th instanceof ServletException)
|
||||
th= ((ServletException)th).getRootCause();
|
||||
if (th != null)
|
||||
exClass= th.getClass();
|
||||
}
|
||||
}
|
||||
|
||||
while (error_page == null && exClass != null )
|
||||
{
|
||||
error_page= (String)_errorPages.get(exClass.getName());
|
||||
exClass= exClass.getSuperclass();
|
||||
}
|
||||
|
||||
if (error_page == null)
|
||||
{
|
||||
// look for an exact code match
|
||||
Integer code=(Integer)request.getAttribute(Dispatcher.ERROR_STATUS_CODE);
|
||||
if (code!=null)
|
||||
{
|
||||
error_page= (String)_errorPages.get(Integer.toString(code));
|
||||
|
||||
// if still not found
|
||||
if ((error_page == null) && (_errorPageList != null))
|
||||
// look for an error code range match.
|
||||
for (int i = 0; i < _errorPageList.size(); i++)
|
||||
{
|
||||
// look for an error code range match.
|
||||
for (int i = 0; i < _errorPageList.size(); i++)
|
||||
ErrorCodeRange errCode = (ErrorCodeRange) _errorPageList.get(i);
|
||||
if (errCode.isInRange(code))
|
||||
{
|
||||
ErrorCodeRange errCode = (ErrorCodeRange) _errorPageList.get(i);
|
||||
if (errCode.isInRange(code))
|
||||
{
|
||||
error_page = errCode.getUri();
|
||||
break;
|
||||
}
|
||||
error_page = errCode.getUri();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//try new servlet 3.0 global error page
|
||||
if (error_page == null)
|
||||
{
|
||||
error_page = _errorPages.get(GLOBAL_ERROR_PAGE);
|
||||
}
|
||||
|
||||
if (error_page!=null)
|
||||
{
|
||||
String old_error_page=(String)request.getAttribute(ERROR_PAGE);
|
||||
if (old_error_page==null || !old_error_page.equals(error_page))
|
||||
{
|
||||
request.setAttribute(ERROR_PAGE, error_page);
|
||||
|
||||
Dispatcher dispatcher = (Dispatcher) _servletContext.getRequestDispatcher(error_page);
|
||||
try
|
||||
{
|
||||
if(dispatcher!=null)
|
||||
{
|
||||
dispatcher.error(request, response);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.warn("No error page "+error_page);
|
||||
}
|
||||
}
|
||||
catch (ServletException e)
|
||||
{
|
||||
LOG.warn(Log.EXCEPTION, e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.handle(target, baseRequest, request, response);
|
||||
//try new servlet 3.0 global error page
|
||||
if (error_page == null)
|
||||
{
|
||||
error_page = _errorPages.get(GLOBAL_ERROR_PAGE);
|
||||
}
|
||||
|
||||
return error_page;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
|
@ -216,6 +216,8 @@ public class Holder<T> extends AbstractLifeCycle implements Dumpable
|
|||
{
|
||||
_className = className;
|
||||
_class=null;
|
||||
if (_name==null)
|
||||
_name=className+"-"+Integer.toHexString(this.hashCode());
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -229,7 +231,7 @@ public class Holder<T> extends AbstractLifeCycle implements Dumpable
|
|||
{
|
||||
_className=held.getName();
|
||||
if (_name==null)
|
||||
_name=held.getName()+"-"+this.hashCode();
|
||||
_name=held.getName()+"-"+Integer.toHexString(this.hashCode());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -569,10 +569,10 @@ public class ServletHandler extends ScopedHandler
|
|||
LOG.debug(request.toString());
|
||||
}
|
||||
|
||||
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,th.getClass());
|
||||
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,th);
|
||||
if (!response.isCommitted())
|
||||
{
|
||||
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,th.getClass());
|
||||
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,th);
|
||||
if (th instanceof UnavailableException)
|
||||
{
|
||||
UnavailableException ue = (UnavailableException)th;
|
||||
|
@ -586,6 +586,10 @@ public class ServletHandler extends ScopedHandler
|
|||
}
|
||||
else
|
||||
LOG.debug("Response already committed for handling "+th);
|
||||
|
||||
// Complete async requests
|
||||
if (request.isAsyncStarted())
|
||||
request.getAsyncContext().complete();
|
||||
}
|
||||
catch(Error e)
|
||||
{
|
||||
|
@ -596,15 +600,16 @@ public class ServletHandler extends ScopedHandler
|
|||
LOG.warn("Error for "+request.getRequestURI(),e);
|
||||
if(LOG.isDebugEnabled())LOG.debug(request.toString());
|
||||
|
||||
// TODO httpResponse.getHttpConnection().forceClose();
|
||||
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,e.getClass());
|
||||
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,e);
|
||||
if (!response.isCommitted())
|
||||
{
|
||||
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,e.getClass());
|
||||
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,e);
|
||||
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
else
|
||||
LOG.debug("Response already committed for handling ",e);
|
||||
|
||||
// Complete async requests
|
||||
if (request.isAsyncStarted())
|
||||
request.getAsyncContext().complete();
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -843,7 +848,6 @@ public class ServletHandler extends ScopedHandler
|
|||
public ServletHolder addServletWithMapping (String className,String pathSpec)
|
||||
{
|
||||
ServletHolder holder = newServletHolder(Holder.Source.EMBEDDED);
|
||||
holder.setName(className+"-"+(_servlets==null?0:_servlets.length));
|
||||
holder.setClassName(className);
|
||||
addServletWithMapping(holder,pathSpec);
|
||||
return holder;
|
||||
|
@ -956,7 +960,6 @@ public class ServletHandler extends ScopedHandler
|
|||
public FilterHolder addFilterWithMapping (String className,String pathSpec,EnumSet<DispatcherType> dispatches)
|
||||
{
|
||||
FilterHolder holder = newFilterHolder(Holder.Source.EMBEDDED);
|
||||
holder.setName(className+"-"+_filters.length);
|
||||
holder.setClassName(className);
|
||||
|
||||
addFilterWithMapping(holder,pathSpec,dispatches);
|
||||
|
@ -1025,7 +1028,6 @@ public class ServletHandler extends ScopedHandler
|
|||
public FilterHolder addFilterWithMapping (String className,String pathSpec,int dispatches)
|
||||
{
|
||||
FilterHolder holder = newFilterHolder(Holder.Source.EMBEDDED);
|
||||
holder.setName(className+"-"+_filters.length);
|
||||
holder.setClassName(className);
|
||||
|
||||
addFilterWithMapping(holder,pathSpec,dispatches);
|
||||
|
|
|
@ -199,8 +199,6 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope
|
|||
int c=(_className!=null && sh._className!=null)?_className.compareTo(sh._className):0;
|
||||
if (c==0)
|
||||
c=_name.compareTo(sh._name);
|
||||
if (c==0)
|
||||
c=this.hashCode()>sh.hashCode()?1:-1;
|
||||
return c;
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,10 @@ import java.io.IOException;
|
|||
import java.io.StringReader;
|
||||
|
||||
import javax.servlet.AsyncContext;
|
||||
import javax.servlet.AsyncEvent;
|
||||
import javax.servlet.AsyncListener;
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
@ -38,6 +41,7 @@ import javax.servlet.http.HttpServletResponseWrapper;
|
|||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.LocalConnector;
|
||||
import org.eclipse.jetty.server.QuietServletException;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.handler.DefaultHandler;
|
||||
|
@ -65,16 +69,25 @@ public class AsyncContextTest
|
|||
_server = new Server();
|
||||
_contextHandler = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
|
||||
_connector = new LocalConnector(_server);
|
||||
_connector.setIdleTimeout(30000);
|
||||
_connector.setIdleTimeout(5000);
|
||||
_server.setConnectors(new Connector[]
|
||||
{ _connector });
|
||||
|
||||
_contextHandler.setContextPath("/");
|
||||
_contextHandler.setContextPath("/ctx");
|
||||
_contextHandler.addServlet(new ServletHolder(new TestServlet()),"/servletPath");
|
||||
_contextHandler.addServlet(new ServletHolder(new TestServlet()),"/path with spaces/servletPath");
|
||||
_contextHandler.addServlet(new ServletHolder(new TestServlet2()),"/servletPath2");
|
||||
_contextHandler.addServlet(new ServletHolder(new TestStartThrowServlet()),"/startthrow/*");
|
||||
_contextHandler.addServlet(new ServletHolder(new ForwardingServlet()),"/forward");
|
||||
_contextHandler.addServlet(new ServletHolder(new AsyncDispatchingServlet()),"/dispatchingServlet");
|
||||
_contextHandler.addServlet(new ServletHolder(new ExpireServlet()),"/expire/*");
|
||||
_contextHandler.addServlet(new ServletHolder(new BadExpireServlet()),"/badexpire/*");
|
||||
_contextHandler.addServlet(new ServletHolder(new ErrorServlet()),"/error/*");
|
||||
|
||||
ErrorPageErrorHandler error_handler = new ErrorPageErrorHandler();
|
||||
_contextHandler.setErrorHandler(error_handler);
|
||||
error_handler.addErrorPage(500,"/error/500");
|
||||
error_handler.addErrorPage(IOException.class.getName(),"/error/IOE");
|
||||
|
||||
HandlerList handlers = new HandlerList();
|
||||
handlers.setHandlers(new Handler[]
|
||||
|
@ -93,7 +106,7 @@ public class AsyncContextTest
|
|||
@Test
|
||||
public void testSimpleAsyncContext() throws Exception
|
||||
{
|
||||
String request = "GET /servletPath HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n"
|
||||
String request = "GET /ctx/servletPath HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n"
|
||||
+ "Connection: close\r\n" + "\r\n";
|
||||
String responseString = _connector.getResponses(request);
|
||||
|
||||
|
@ -106,10 +119,29 @@ public class AsyncContextTest
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartThrow() throws Exception
|
||||
{
|
||||
String request = "GET /ctx/startthrow HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n"
|
||||
+ "Connection: close\r\n" + "\r\n";
|
||||
String responseString = _connector.getResponses(request);
|
||||
|
||||
BufferedReader br = new BufferedReader(new StringReader(responseString));
|
||||
|
||||
assertEquals("HTTP/1.1 500 Server Error",br.readLine());
|
||||
br.readLine();// connection close
|
||||
br.readLine();// server
|
||||
br.readLine();// empty
|
||||
|
||||
Assert.assertEquals("error servlet","ERROR: /error",br.readLine());
|
||||
Assert.assertEquals("error servlet","PathInfo= /IOE",br.readLine());
|
||||
Assert.assertEquals("error servlet","EXCEPTION: java.io.IOException: Test",br.readLine());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDispatchAsyncContext() throws Exception
|
||||
{
|
||||
String request = "GET /servletPath?dispatch=true HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n"
|
||||
String request = "GET /ctx/servletPath?dispatch=true HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n"
|
||||
+ "Connection: close\r\n" + "\r\n";
|
||||
String responseString = _connector.getResponses(request);
|
||||
|
||||
|
@ -120,8 +152,8 @@ public class AsyncContextTest
|
|||
Assert.assertEquals("servlet path attr is original","async:run:attr:servletPath:/servletPath",br.readLine());
|
||||
Assert.assertEquals("path info attr is correct","async:run:attr:pathInfo:null",br.readLine());
|
||||
Assert.assertEquals("query string attr is correct","async:run:attr:queryString:dispatch=true",br.readLine());
|
||||
Assert.assertEquals("context path attr is correct","async:run:attr:contextPath:",br.readLine());
|
||||
Assert.assertEquals("request uri attr is correct","async:run:attr:requestURI:/servletPath",br.readLine());
|
||||
Assert.assertEquals("context path attr is correct","async:run:attr:contextPath:/ctx",br.readLine());
|
||||
Assert.assertEquals("request uri attr is correct","async:run:attr:requestURI:/ctx/servletPath",br.readLine());
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -137,7 +169,7 @@ public class AsyncContextTest
|
|||
@Test
|
||||
public void testDispatchAsyncContextEncodedPathAndQueryString() throws Exception
|
||||
{
|
||||
String request = "GET /path%20with%20spaces/servletPath?dispatch=true&queryStringWithEncoding=space%20space HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n"
|
||||
String request = "GET /ctx/path%20with%20spaces/servletPath?dispatch=true&queryStringWithEncoding=space%20space HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n"
|
||||
+ "Connection: close\r\n" + "\r\n";
|
||||
String responseString = _connector.getResponses(request);
|
||||
|
||||
|
@ -148,16 +180,14 @@ public class AsyncContextTest
|
|||
assertThat("servlet path attr is original",br.readLine(),equalTo("async:run:attr:servletPath:/path with spaces/servletPath"));
|
||||
assertThat("path info attr is correct",br.readLine(),equalTo("async:run:attr:pathInfo:null"));
|
||||
assertThat("query string attr is correct",br.readLine(),equalTo("async:run:attr:queryString:dispatch=true&queryStringWithEncoding=space%20space"));
|
||||
assertThat("context path attr is correct",br.readLine(),equalTo("async:run:attr:contextPath:"));
|
||||
assertThat("request uri attr is correct",br.readLine(),equalTo("async:run:attr:requestURI:/path%20with%20spaces/servletPath"));
|
||||
assertThat("context path attr is correct",br.readLine(),equalTo("async:run:attr:contextPath:/ctx"));
|
||||
assertThat("request uri attr is correct",br.readLine(),equalTo("async:run:attr:requestURI:/ctx/path%20with%20spaces/servletPath"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleWithContextAsyncContext() throws Exception
|
||||
{
|
||||
_contextHandler.setContextPath("/foo");
|
||||
|
||||
String request = "GET /foo/servletPath HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n"
|
||||
String request = "GET /ctx/servletPath HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n"
|
||||
+ "Connection: close\r\n" + "\r\n";
|
||||
|
||||
String responseString = _connector.getResponses(request);
|
||||
|
@ -172,9 +202,7 @@ public class AsyncContextTest
|
|||
@Test
|
||||
public void testDispatchWithContextAsyncContext() throws Exception
|
||||
{
|
||||
_contextHandler.setContextPath("/foo");
|
||||
|
||||
String request = "GET /foo/servletPath?dispatch=true HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n"
|
||||
String request = "GET /ctx/servletPath?dispatch=true HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n"
|
||||
+ "Connection: close\r\n" + "\r\n";
|
||||
|
||||
String responseString = _connector.getResponses(request);
|
||||
|
@ -186,14 +214,14 @@ public class AsyncContextTest
|
|||
Assert.assertEquals("servlet path attr is original","async:run:attr:servletPath:/servletPath",br.readLine());
|
||||
Assert.assertEquals("path info attr is correct","async:run:attr:pathInfo:null",br.readLine());
|
||||
Assert.assertEquals("query string attr is correct","async:run:attr:queryString:dispatch=true",br.readLine());
|
||||
Assert.assertEquals("context path attr is correct","async:run:attr:contextPath:/foo",br.readLine());
|
||||
Assert.assertEquals("request uri attr is correct","async:run:attr:requestURI:/foo/servletPath",br.readLine());
|
||||
Assert.assertEquals("context path attr is correct","async:run:attr:contextPath:/ctx",br.readLine());
|
||||
Assert.assertEquals("request uri attr is correct","async:run:attr:requestURI:/ctx/servletPath",br.readLine());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDispatch() throws Exception
|
||||
{
|
||||
String request = "GET /forward HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "Connection: close\r\n"
|
||||
String request = "GET /ctx/forward HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "Connection: close\r\n"
|
||||
+ "\r\n";
|
||||
|
||||
String responseString = _connector.getResponses(request);
|
||||
|
@ -204,7 +232,7 @@ public class AsyncContextTest
|
|||
@Test
|
||||
public void testDispatchRequestResponse() throws Exception
|
||||
{
|
||||
String request = "GET /forward?dispatchRequestResponse=true HTTP/1.1\r\n" +
|
||||
String request = "GET /ctx/forward?dispatchRequestResponse=true HTTP/1.1\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"Content-Type: application/x-www-form-urlencoded\r\n" +
|
||||
"Connection: close\r\n" +
|
||||
|
@ -282,6 +310,49 @@ public class AsyncContextTest
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpire() throws Exception
|
||||
{
|
||||
String request = "GET /ctx/expire HTTP/1.1\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"Content-Type: application/x-www-form-urlencoded\r\n" +
|
||||
"Connection: close\r\n" +
|
||||
"\r\n";
|
||||
String responseString = _connector.getResponses(request);
|
||||
|
||||
BufferedReader br = new BufferedReader(new StringReader(responseString));
|
||||
|
||||
assertEquals("HTTP/1.1 500 Async Timeout",br.readLine());
|
||||
|
||||
br.readLine();// connection close
|
||||
br.readLine();// server
|
||||
br.readLine();// empty
|
||||
|
||||
Assert.assertEquals("error servlet","ERROR: /error",br.readLine());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadExpire() throws Exception
|
||||
{
|
||||
String request = "GET /ctx/badexpire HTTP/1.1\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"Content-Type: application/x-www-form-urlencoded\r\n" +
|
||||
"Connection: close\r\n" +
|
||||
"\r\n";
|
||||
String responseString = _connector.getResponses(request);
|
||||
|
||||
BufferedReader br = new BufferedReader(new StringReader(responseString));
|
||||
|
||||
assertEquals("HTTP/1.1 500 Async Exception",br.readLine());
|
||||
br.readLine();// connection close
|
||||
br.readLine();// server
|
||||
br.readLine();// empty
|
||||
|
||||
Assert.assertEquals("error servlet","ERROR: /error",br.readLine());
|
||||
Assert.assertEquals("error servlet","PathInfo= /500",br.readLine());
|
||||
Assert.assertEquals("error servlet","EXCEPTION: java.io.IOException: TEST",br.readLine());
|
||||
}
|
||||
|
||||
private class DispatchingRunnable implements Runnable
|
||||
{
|
||||
private AsyncContext asyncContext;
|
||||
|
@ -308,6 +379,74 @@ public class AsyncContextTest
|
|||
_server.join();
|
||||
}
|
||||
|
||||
|
||||
private class ErrorServlet extends HttpServlet
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
response.getOutputStream().print("ERROR: " + request.getServletPath() + "\n");
|
||||
response.getOutputStream().print("PathInfo= " + request.getPathInfo() + "\n");
|
||||
if (request.getAttribute(RequestDispatcher.ERROR_EXCEPTION)!=null)
|
||||
response.getOutputStream().print("EXCEPTION: " + request.getAttribute(RequestDispatcher.ERROR_EXCEPTION) + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
private class ExpireServlet extends HttpServlet
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
if (request.getDispatcherType()==DispatcherType.REQUEST)
|
||||
{
|
||||
AsyncContext asyncContext = request.startAsync();
|
||||
asyncContext.setTimeout(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class BadExpireServlet extends HttpServlet
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
if (request.getDispatcherType()==DispatcherType.REQUEST)
|
||||
{
|
||||
AsyncContext asyncContext = request.startAsync();
|
||||
asyncContext.addListener(new AsyncListener()
|
||||
{
|
||||
@Override
|
||||
public void onTimeout(AsyncEvent event) throws IOException
|
||||
{
|
||||
throw new IOException("TEST");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartAsync(AsyncEvent event) throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(AsyncEvent event) throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete(AsyncEvent event) throws IOException
|
||||
{
|
||||
}
|
||||
});
|
||||
asyncContext.setTimeout(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class TestServlet extends HttpServlet
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
@ -347,6 +486,21 @@ public class AsyncContextTest
|
|||
asyncContext.start(new AsyncRunnable(asyncContext));
|
||||
}
|
||||
}
|
||||
|
||||
private class TestStartThrowServlet extends HttpServlet
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
if (request.getDispatcherType()==DispatcherType.REQUEST)
|
||||
{
|
||||
request.startAsync(request, response);
|
||||
throw new QuietServletException(new IOException("Test"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class AsyncRunnable implements Runnable
|
||||
{
|
||||
|
|
|
@ -23,6 +23,7 @@ import static org.junit.Assert.assertTrue;
|
|||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.EnumSet;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
@ -39,6 +40,7 @@ import org.eclipse.jetty.http.HttpFields;
|
|||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.LocalConnector;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
import org.eclipse.jetty.toolchain.test.FS;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.toolchain.test.OS;
|
||||
|
@ -414,6 +416,40 @@ public class DefaultServletTest
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResourceBase() throws Exception
|
||||
{
|
||||
testdir.ensureEmpty();
|
||||
File resBase = testdir.getFile("docroot");
|
||||
FS.ensureDirExists(resBase);
|
||||
File foobar = new File(resBase, "foobar.txt");
|
||||
File link = new File(resBase, "link.txt");
|
||||
createFile(foobar, "Foo Bar");
|
||||
|
||||
String resBasePath = resBase.getAbsolutePath();
|
||||
|
||||
ServletHolder defholder = context.addServlet(DefaultServlet.class, "/");
|
||||
defholder.setInitParameter("resourceBase", resBasePath);
|
||||
defholder.setInitParameter("gzip", "false");
|
||||
|
||||
String response;
|
||||
|
||||
response = connector.getResponses("GET /context/foobar.txt HTTP/1.0\r\n\r\n");
|
||||
assertResponseContains("Foo Bar", response);
|
||||
|
||||
if (!OS.IS_WINDOWS)
|
||||
{
|
||||
Files.createSymbolicLink(link.toPath(),foobar.toPath());
|
||||
response = connector.getResponses("GET /context/link.txt HTTP/1.0\r\n\r\n");
|
||||
assertResponseContains("404", response);
|
||||
|
||||
context.addAliasCheck(new ContextHandler.ApproveAliases());
|
||||
|
||||
response = connector.getResponses("GET /context/link.txt HTTP/1.0\r\n\r\n");
|
||||
assertResponseContains("Foo Bar", response);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWelcomeExactServlet() throws Exception
|
||||
{
|
||||
|
|
|
@ -91,7 +91,7 @@ public class ErrorPageTest
|
|||
assertThat(response,Matchers.containsString("ERROR_CODE: 599"));
|
||||
assertThat(response,Matchers.containsString("ERROR_EXCEPTION: null"));
|
||||
assertThat(response,Matchers.containsString("ERROR_EXCEPTION_TYPE: null"));
|
||||
assertThat(response,Matchers.containsString("ERROR_SERVLET: org.eclipse.jetty.servlet.ErrorPageTest$FailServlet-1"));
|
||||
assertThat(response,Matchers.containsString("ERROR_SERVLET: org.eclipse.jetty.servlet.ErrorPageTest$FailServlet-"));
|
||||
assertThat(response,Matchers.containsString("ERROR_REQUEST_URI: /fail/code"));
|
||||
}
|
||||
|
||||
|
@ -104,7 +104,7 @@ public class ErrorPageTest
|
|||
assertThat(response,Matchers.containsString("ERROR_CODE: 500"));
|
||||
assertThat(response,Matchers.containsString("ERROR_EXCEPTION: java.lang.IllegalStateException"));
|
||||
assertThat(response,Matchers.containsString("ERROR_EXCEPTION_TYPE: class java.lang.IllegalStateException"));
|
||||
assertThat(response,Matchers.containsString("ERROR_SERVLET: org.eclipse.jetty.servlet.ErrorPageTest$FailServlet-1"));
|
||||
assertThat(response,Matchers.containsString("ERROR_SERVLET: org.eclipse.jetty.servlet.ErrorPageTest$FailServlet-"));
|
||||
assertThat(response,Matchers.containsString("ERROR_REQUEST_URI: /fail/exception"));
|
||||
}
|
||||
|
||||
|
@ -117,7 +117,7 @@ public class ErrorPageTest
|
|||
assertThat(response,Matchers.containsString("ERROR_CODE: 598"));
|
||||
assertThat(response,Matchers.containsString("ERROR_EXCEPTION: null"));
|
||||
assertThat(response,Matchers.containsString("ERROR_EXCEPTION_TYPE: null"));
|
||||
assertThat(response,Matchers.containsString("ERROR_SERVLET: org.eclipse.jetty.servlet.ErrorPageTest$FailServlet-1"));
|
||||
assertThat(response,Matchers.containsString("ERROR_SERVLET: org.eclipse.jetty.servlet.ErrorPageTest$FailServlet-"));
|
||||
assertThat(response,Matchers.containsString("ERROR_REQUEST_URI: /fail/global"));
|
||||
}
|
||||
|
||||
|
@ -130,7 +130,7 @@ public class ErrorPageTest
|
|||
assertThat(response,Matchers.containsString("ERROR_CODE: 500"));
|
||||
assertThat(response,Matchers.containsString("ERROR_EXCEPTION: java.lang.NumberFormatException: For input string: \"NAN\""));
|
||||
assertThat(response,Matchers.containsString("ERROR_EXCEPTION_TYPE: class java.lang.NumberFormatException"));
|
||||
assertThat(response,Matchers.containsString("ERROR_SERVLET: org.eclipse.jetty.servlet.ErrorPageTest$FailServlet-1"));
|
||||
assertThat(response,Matchers.containsString("ERROR_SERVLET: org.eclipse.jetty.servlet.ErrorPageTest$FailServlet-"));
|
||||
assertThat(response,Matchers.containsString("ERROR_REQUEST_URI: /fail/global"));
|
||||
}
|
||||
|
||||
|
|
|
@ -37,8 +37,10 @@ import org.eclipse.jetty.util.Fields;
|
|||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
@Ignore("Reliance on external server fails test")
|
||||
public class SSLExternalServerTest extends AbstractHTTPSPDYTest
|
||||
{
|
||||
public SSLExternalServerTest(short version)
|
||||
|
|
|
@ -20,7 +20,6 @@ package org.eclipse.jetty.spdy.server;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLEngineResult;
|
||||
|
||||
|
@ -63,33 +62,58 @@ public class NextProtoNegoServerConnection extends AbstractConnection implements
|
|||
@Override
|
||||
public void onFillable()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
int filled = fill();
|
||||
if (filled == 0 && nextProtocol == null)
|
||||
fillInterested();
|
||||
if (filled <= 0 || nextProtocol != null)
|
||||
break;
|
||||
}
|
||||
int filled = fill();
|
||||
|
||||
if (nextProtocol == null && engine.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)
|
||||
if (filled == 0)
|
||||
{
|
||||
// The client sent the NPN extension, but did not send the NextProtocol
|
||||
// message with the chosen protocol so we need to close
|
||||
LOG.debug("{} missing next protocol. SSLEngine: {}", this, engine);
|
||||
if (nextProtocol == null)
|
||||
{
|
||||
if (engine.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)
|
||||
{
|
||||
// Here the SSL handshake is finished, but while the client sent
|
||||
// the NPN extension, the server application did not select the
|
||||
// protocol; we need to close as the protocol cannot be negotiated.
|
||||
LOG.debug("{} missing next protocol. SSLEngine: {}", this, engine);
|
||||
close();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Here the SSL handshake is not finished yet but we filled 0 bytes,
|
||||
// so we need to read more.
|
||||
fillInterested();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ConnectionFactory connectionFactory = connector.getConnectionFactory(nextProtocol);
|
||||
if (connectionFactory == null)
|
||||
{
|
||||
LOG.debug("{} application selected protocol '{}', but no correspondent {} has been configured",
|
||||
this, nextProtocol, ConnectionFactory.class.getName());
|
||||
close();
|
||||
}
|
||||
else
|
||||
{
|
||||
EndPoint endPoint = getEndPoint();
|
||||
Connection oldConnection = endPoint.getConnection();
|
||||
Connection newConnection = connectionFactory.newConnection(connector, endPoint);
|
||||
LOG.debug("{} switching from {} to {}", this, oldConnection, newConnection);
|
||||
oldConnection.onClose();
|
||||
endPoint.setConnection(newConnection);
|
||||
getEndPoint().getConnection().onOpen();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (filled < 0)
|
||||
{
|
||||
// Something went bad, we need to close.
|
||||
LOG.debug("{} closing on client close", this);
|
||||
close();
|
||||
}
|
||||
|
||||
if (nextProtocol != null)
|
||||
else
|
||||
{
|
||||
ConnectionFactory connectionFactory = connector.getConnectionFactory(nextProtocol);
|
||||
EndPoint endPoint = getEndPoint();
|
||||
Connection oldConnection = endPoint.getConnection();
|
||||
oldConnection.onClose();
|
||||
Connection connection = connectionFactory.newConnection(connector, endPoint);
|
||||
LOG.debug("{} switching from {} to {}", this, oldConnection, connection);
|
||||
endPoint.setConnection(connection);
|
||||
getEndPoint().getConnection().onOpen();
|
||||
// Must never happen, since we fill using an empty buffer
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,8 +126,7 @@ public class NextProtoNegoServerConnection extends AbstractConnection implements
|
|||
catch (IOException x)
|
||||
{
|
||||
LOG.debug(x);
|
||||
NextProtoNego.remove(engine);
|
||||
getEndPoint().close();
|
||||
close();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@ -127,4 +150,13 @@ public class NextProtoNegoServerConnection extends AbstractConnection implements
|
|||
nextProtocol = protocol != null ? protocol : defaultProtocol;
|
||||
NextProtoNego.remove(engine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
NextProtoNego.remove(engine);
|
||||
EndPoint endPoint = getEndPoint();
|
||||
endPoint.shutdownOutput();
|
||||
endPoint.close();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,14 +19,22 @@
|
|||
|
||||
package org.eclipse.jetty.spdy.server;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
||||
import org.eclipse.jetty.npn.NextProtoNego;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
|
||||
import org.eclipse.jetty.spdy.client.SPDYClient;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class SSLSynReplyTest extends SynReplyTest
|
||||
{
|
||||
|
@ -51,4 +59,119 @@ public class SSLSynReplyTest extends SynReplyTest
|
|||
{
|
||||
NextProtoNego.debug = true;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGentleCloseDuringHandshake() throws Exception
|
||||
{
|
||||
InetSocketAddress address = startServer(version, null);
|
||||
SslContextFactory sslContextFactory = newSslContextFactory();
|
||||
sslContextFactory.start();
|
||||
SSLEngine sslEngine = sslContextFactory.newSSLEngine(address);
|
||||
sslEngine.setUseClientMode(true);
|
||||
NextProtoNego.put(sslEngine, new NextProtoNego.ClientProvider()
|
||||
{
|
||||
@Override
|
||||
public boolean supports()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsupported()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public String selectProtocol(List<String> protocols)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
});
|
||||
sslEngine.beginHandshake();
|
||||
|
||||
ByteBuffer encrypted = ByteBuffer.allocate(sslEngine.getSession().getPacketBufferSize());
|
||||
sslEngine.wrap(BufferUtil.EMPTY_BUFFER, encrypted);
|
||||
encrypted.flip();
|
||||
|
||||
try (SocketChannel channel = SocketChannel.open(address))
|
||||
{
|
||||
// Send ClientHello, immediately followed by TLS Close Alert and then by FIN
|
||||
channel.write(encrypted);
|
||||
sslEngine.closeOutbound();
|
||||
encrypted.clear();
|
||||
sslEngine.wrap(BufferUtil.EMPTY_BUFFER, encrypted);
|
||||
encrypted.flip();
|
||||
channel.write(encrypted);
|
||||
channel.shutdownOutput();
|
||||
|
||||
// Read ServerHello from server
|
||||
encrypted.clear();
|
||||
int read = channel.read(encrypted);
|
||||
encrypted.flip();
|
||||
Assert.assertTrue(read > 0);
|
||||
// Cannot decrypt, as the SSLEngine has been already closed
|
||||
|
||||
// Now if we read more, we should either read the TLS Close Alert, or directly -1
|
||||
encrypted.clear();
|
||||
read = channel.read(encrypted);
|
||||
// Sending a TLS Close Alert during handshake results in an exception when
|
||||
// unwrapping that the server react to by closing the connection abruptly.
|
||||
Assert.assertTrue(read < 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAbruptCloseDuringHandshake() throws Exception
|
||||
{
|
||||
InetSocketAddress address = startServer(version, null);
|
||||
SslContextFactory sslContextFactory = newSslContextFactory();
|
||||
sslContextFactory.start();
|
||||
SSLEngine sslEngine = sslContextFactory.newSSLEngine(address);
|
||||
sslEngine.setUseClientMode(true);
|
||||
NextProtoNego.put(sslEngine, new NextProtoNego.ClientProvider()
|
||||
{
|
||||
@Override
|
||||
public boolean supports()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsupported()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public String selectProtocol(List<String> protocols)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
});
|
||||
sslEngine.beginHandshake();
|
||||
|
||||
ByteBuffer encrypted = ByteBuffer.allocate(sslEngine.getSession().getPacketBufferSize());
|
||||
sslEngine.wrap(BufferUtil.EMPTY_BUFFER, encrypted);
|
||||
encrypted.flip();
|
||||
|
||||
try (SocketChannel channel = SocketChannel.open(address))
|
||||
{
|
||||
// Send ClientHello, immediately followed by FIN (no TLS Close Alert)
|
||||
channel.write(encrypted);
|
||||
channel.shutdownOutput();
|
||||
|
||||
// Read ServerHello from server
|
||||
encrypted.clear();
|
||||
int read = channel.read(encrypted);
|
||||
encrypted.flip();
|
||||
Assert.assertTrue(read > 0);
|
||||
ByteBuffer decrypted = ByteBuffer.allocate(sslEngine.getSession().getApplicationBufferSize());
|
||||
sslEngine.unwrap(encrypted, decrypted);
|
||||
|
||||
// Now if we read more, we should either read the TLS Close Alert, or directly -1
|
||||
encrypted.clear();
|
||||
read = channel.read(encrypted);
|
||||
// Since we have close the connection abruptly, the server also does so
|
||||
Assert.assertTrue(read < 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ Command Line Options:
|
|||
property set and the stop command must have the same property.
|
||||
|
||||
--daemon Start in daemon mode with stderr and stdout
|
||||
redirected to ${jetty.log}/start.log
|
||||
redirected to ${jetty.logs}/start.log
|
||||
|
||||
--config=<file> Specify an alternate start.config file.
|
||||
The default is the start.config file inside
|
||||
|
|
|
@ -29,20 +29,33 @@ import java.util.Set;
|
|||
|
||||
/**
|
||||
* <p>A container for name/value pairs, known as fields.</p>
|
||||
* <p>A {@link Field} is composed of a case-insensitive name string and
|
||||
* <p>A {@link Field} is composed of a name string that can be case-sensitive
|
||||
* or case-insensitive (by specifying the option at the constructor) and
|
||||
* of a case-sensitive set of value strings.</p>
|
||||
* <p>The implementation of this class is not thread safe.</p>
|
||||
*/
|
||||
public class Fields implements Iterable<Fields.Field>
|
||||
{
|
||||
private final boolean caseSensitive;
|
||||
private final Map<String, Field> fields;
|
||||
|
||||
/**
|
||||
* <p>Creates an empty modifiable {@link Fields} instance.</p>
|
||||
* <p>Creates an empty, modifiable, case insensitive {@link Fields} instance.</p>
|
||||
* @see #Fields(Fields, boolean)
|
||||
*/
|
||||
public Fields()
|
||||
{
|
||||
this(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates an empty, modifiable, case insensitive {@link Fields} instance.</p>
|
||||
* @param caseSensitive whether this {@link Fields} instance must be case sensitive
|
||||
* @see #Fields(Fields, boolean)
|
||||
*/
|
||||
public Fields(boolean caseSensitive)
|
||||
{
|
||||
this.caseSensitive = caseSensitive;
|
||||
fields = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
|
@ -55,6 +68,7 @@ public class Fields implements Iterable<Fields.Field>
|
|||
*/
|
||||
public Fields(Fields original, boolean immutable)
|
||||
{
|
||||
this.caseSensitive = original.caseSensitive;
|
||||
Map<String, Field> copy = new LinkedHashMap<>();
|
||||
copy.putAll(original.fields);
|
||||
fields = immutable ? Collections.unmodifiableMap(copy) : copy;
|
||||
|
@ -68,7 +82,18 @@ public class Fields implements Iterable<Fields.Field>
|
|||
if (obj == null || getClass() != obj.getClass())
|
||||
return false;
|
||||
Fields that = (Fields)obj;
|
||||
return fields.equals(that.fields);
|
||||
if (size() != that.size())
|
||||
return false;
|
||||
if (caseSensitive != that.caseSensitive)
|
||||
return false;
|
||||
for (Map.Entry<String, Field> entry : fields.entrySet())
|
||||
{
|
||||
String name = entry.getKey();
|
||||
Field value = entry.getValue();
|
||||
if (!value.equals(that.get(name), caseSensitive))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -88,13 +113,18 @@ public class Fields implements Iterable<Fields.Field>
|
|||
return result;
|
||||
}
|
||||
|
||||
private String normalizeName(String name)
|
||||
{
|
||||
return caseSensitive ? name : name.toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name the field name
|
||||
* @return the {@link Field} with the given name, or null if no such field exists
|
||||
*/
|
||||
public Field get(String name)
|
||||
{
|
||||
return fields.get(name.trim().toLowerCase(Locale.ENGLISH));
|
||||
return fields.get(normalizeName(name));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -105,10 +135,9 @@ public class Fields implements Iterable<Fields.Field>
|
|||
*/
|
||||
public void put(String name, String value)
|
||||
{
|
||||
name = name.trim();
|
||||
// Preserve the case for the field name
|
||||
Field field = new Field(name, value);
|
||||
fields.put(name.toLowerCase(Locale.ENGLISH), field);
|
||||
fields.put(normalizeName(name), field);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -119,7 +148,7 @@ public class Fields implements Iterable<Fields.Field>
|
|||
public void put(Field field)
|
||||
{
|
||||
if (field != null)
|
||||
fields.put(field.name().toLowerCase(Locale.ENGLISH), field);
|
||||
fields.put(normalizeName(field.name()), field);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -131,17 +160,18 @@ public class Fields implements Iterable<Fields.Field>
|
|||
*/
|
||||
public void add(String name, String value)
|
||||
{
|
||||
name = name.trim();
|
||||
Field field = fields.get(name.toLowerCase(Locale.ENGLISH));
|
||||
String key = normalizeName(name);
|
||||
Field field = fields.get(key);
|
||||
if (field == null)
|
||||
{
|
||||
// Preserve the case for the field name
|
||||
field = new Field(name, value);
|
||||
fields.put(name.toLowerCase(Locale.ENGLISH), field);
|
||||
fields.put(key, field);
|
||||
}
|
||||
else
|
||||
{
|
||||
field = new Field(field.name(), field.values(), value);
|
||||
fields.put(name.toLowerCase(Locale.ENGLISH), field);
|
||||
fields.put(key, field);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -153,8 +183,7 @@ public class Fields implements Iterable<Fields.Field>
|
|||
*/
|
||||
public Field remove(String name)
|
||||
{
|
||||
name = name.trim();
|
||||
return fields.remove(name.toLowerCase(Locale.ENGLISH));
|
||||
return fields.remove(normalizeName(name));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -219,6 +248,17 @@ public class Fields implements Iterable<Fields.Field>
|
|||
System.arraycopy(moreValues, 0, this.values, values.length, moreValues.length);
|
||||
}
|
||||
|
||||
public boolean equals(Field that, boolean caseSensitive)
|
||||
{
|
||||
if (this == that)
|
||||
return true;
|
||||
if (that == null)
|
||||
return false;
|
||||
if (caseSensitive)
|
||||
return equals(that);
|
||||
return name.equalsIgnoreCase(that.name) && Arrays.equals(values, that.values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
|
@ -227,15 +267,13 @@ public class Fields implements Iterable<Fields.Field>
|
|||
if (obj == null || getClass() != obj.getClass())
|
||||
return false;
|
||||
Field that = (Field)obj;
|
||||
// Field names must be lowercase, thus we lowercase them before transmission, but keep them as is
|
||||
// internally. That's why we've to compare them case insensitive.
|
||||
return name.equalsIgnoreCase(that.name) && Arrays.equals(values, that.values);
|
||||
return name.equals(that.name) && Arrays.equals(values, that.values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
int result = name.toLowerCase(Locale.ENGLISH).hashCode();
|
||||
int result = name.hashCode();
|
||||
result = 31 * result + Arrays.hashCode(values);
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -35,12 +35,12 @@ public class MultiPartOutputStream extends FilterOutputStream
|
|||
private static final byte[] __CRLF={'\r','\n'};
|
||||
private static final byte[] __DASHDASH={'-','-'};
|
||||
|
||||
public static String MULTIPART_MIXED="multipart/mixed";
|
||||
public static String MULTIPART_X_MIXED_REPLACE="multipart/x-mixed-replace";
|
||||
public static final String MULTIPART_MIXED="multipart/mixed";
|
||||
public static final String MULTIPART_X_MIXED_REPLACE="multipart/x-mixed-replace";
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
private String boundary;
|
||||
private byte[] boundaryBytes;
|
||||
private final String boundary;
|
||||
private final byte[] boundaryBytes;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
private boolean inPart=false;
|
||||
|
@ -54,8 +54,15 @@ public class MultiPartOutputStream extends FilterOutputStream
|
|||
boundary = "jetty"+System.identityHashCode(this)+
|
||||
Long.toString(System.currentTimeMillis(),36);
|
||||
boundaryBytes=boundary.getBytes(StringUtil.__ISO_8859_1);
|
||||
}
|
||||
|
||||
inPart=false;
|
||||
public MultiPartOutputStream(OutputStream out, String boundary)
|
||||
throws IOException
|
||||
{
|
||||
super(out);
|
||||
|
||||
this.boundary = boundary;
|
||||
boundaryBytes=boundary.getBytes(StringUtil.__ISO_8859_1);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -66,14 +73,20 @@ public class MultiPartOutputStream extends FilterOutputStream
|
|||
public void close()
|
||||
throws IOException
|
||||
{
|
||||
if (inPart)
|
||||
try
|
||||
{
|
||||
if (inPart)
|
||||
out.write(__CRLF);
|
||||
out.write(__DASHDASH);
|
||||
out.write(boundaryBytes);
|
||||
out.write(__DASHDASH);
|
||||
out.write(__CRLF);
|
||||
out.write(__DASHDASH);
|
||||
out.write(boundaryBytes);
|
||||
out.write(__DASHDASH);
|
||||
out.write(__CRLF);
|
||||
inPart=false;
|
||||
super.close();
|
||||
inPart=false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
super.close();
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
|
@ -108,8 +108,28 @@ public class FileResource extends Resource
|
|||
{
|
||||
File file=new File(uri);
|
||||
_file=file;
|
||||
URI file_uri=_file.toURI();
|
||||
_uri=normalizeURI(_file,uri);
|
||||
_alias=checkAlias(_file);
|
||||
|
||||
if (!_uri.equals(file_uri) && !_uri.toString().equals(file_uri.toString()))
|
||||
{
|
||||
// URI and File URI are different. Is it just an encoding difference?
|
||||
if (!file_uri.toString().equals(URIUtil.decodePath(uri.toString())))
|
||||
{
|
||||
try
|
||||
{
|
||||
_alias=_file.toURI().toURL();
|
||||
}
|
||||
catch (MalformedURLException e)
|
||||
{
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
else
|
||||
_alias=checkAlias(_file);
|
||||
}
|
||||
else
|
||||
_alias=checkAlias(_file);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------- */
|
||||
|
@ -394,7 +414,7 @@ public class FileResource extends Resource
|
|||
{
|
||||
try
|
||||
{
|
||||
return _file.toURI().toURL();
|
||||
return new URL(_uri);
|
||||
}
|
||||
catch (MalformedURLException e)
|
||||
{
|
||||
|
|
|
@ -55,37 +55,31 @@ public class CounterStatistic
|
|||
/**
|
||||
* @param delta the amount to add to the count
|
||||
*/
|
||||
public void add(final long delta)
|
||||
public long add(final long delta)
|
||||
{
|
||||
long value=_curr.addAndGet(delta);
|
||||
if (delta > 0)
|
||||
{
|
||||
_total.addAndGet(delta);
|
||||
Atomics.updateMax(_max,value);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @param delta the amount to subtract the count by.
|
||||
*/
|
||||
public void subtract(final long delta)
|
||||
{
|
||||
add(-delta);
|
||||
Atomics.updateMax(_max,value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
*/
|
||||
public void increment()
|
||||
public long increment()
|
||||
{
|
||||
add(1);
|
||||
return add(1);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
*/
|
||||
public void decrement()
|
||||
public long decrement()
|
||||
{
|
||||
add(-1);
|
||||
return add(-1);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
|
@ -27,6 +27,7 @@ import java.net.URI;
|
|||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.OS;
|
||||
import org.eclipse.jetty.toolchain.test.TestingDir;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.UrlEncoded;
|
||||
|
@ -34,6 +35,7 @@ import org.junit.Assert;
|
|||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assume.assumeTrue;
|
||||
|
||||
public class FileResourceTest
|
||||
{
|
||||
|
@ -53,6 +55,19 @@ public class FileResourceTest
|
|||
String decoded = UrlEncoded.decodeString(raw,0,raw.length(),StringUtil.__UTF8_CHARSET);
|
||||
return new URL(decoded);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSemicolon() throws Exception
|
||||
{
|
||||
assumeTrue(!OS.IS_WINDOWS);
|
||||
createDummyFile("foo;");
|
||||
|
||||
try(Resource base = new FileResource(testdir.getDir());)
|
||||
{
|
||||
Resource res = base.addPath("foo;");
|
||||
Assert.assertNull(res.getAlias());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExist_Normal() throws Exception
|
||||
|
@ -60,8 +75,10 @@ public class FileResourceTest
|
|||
createDummyFile("a.jsp");
|
||||
|
||||
URI ref = testdir.getDir().toURI().resolve("a.jsp");
|
||||
FileResource fileres = new FileResource(decode(ref.toURL()));
|
||||
Assert.assertThat("FileResource: " + fileres,fileres.exists(),is(true));
|
||||
try(FileResource fileres = new FileResource(decode(ref.toURL()));)
|
||||
{
|
||||
Assert.assertThat("FileResource: " + fileres,fileres.exists(),is(true));
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore("Cannot get null to be seen by FileResource")
|
||||
|
@ -70,12 +87,17 @@ public class FileResourceTest
|
|||
{
|
||||
createDummyFile("a.jsp");
|
||||
|
||||
try {
|
||||
try
|
||||
{
|
||||
// request with null at end
|
||||
URI ref = testdir.getDir().toURI().resolve("a.jsp%00");
|
||||
FileResource fileres = new FileResource(decode(ref.toURL()));
|
||||
Assert.assertThat("FileResource: " + fileres,fileres.exists(),is(false));
|
||||
} catch(URISyntaxException e) {
|
||||
try(FileResource fileres = new FileResource(decode(ref.toURL()));)
|
||||
{
|
||||
Assert.assertThat("FileResource: " + fileres,fileres.exists(),is(false));
|
||||
}
|
||||
}
|
||||
catch(URISyntaxException e)
|
||||
{
|
||||
// Valid path
|
||||
}
|
||||
}
|
||||
|
@ -86,12 +108,17 @@ public class FileResourceTest
|
|||
{
|
||||
createDummyFile("a.jsp");
|
||||
|
||||
try {
|
||||
try
|
||||
{
|
||||
// request with null and x at end
|
||||
URI ref = testdir.getDir().toURI().resolve("a.jsp%00x");
|
||||
FileResource fileres = new FileResource(decode(ref.toURL()));
|
||||
Assert.assertThat("FileResource: " + fileres,fileres.exists(),is(false));
|
||||
} catch(URISyntaxException e) {
|
||||
try(FileResource fileres = new FileResource(decode(ref.toURL()));)
|
||||
{
|
||||
Assert.assertThat("FileResource: " + fileres,fileres.exists(),is(false));
|
||||
}
|
||||
}
|
||||
catch(URISyntaxException e)
|
||||
{
|
||||
// Valid path
|
||||
}
|
||||
}
|
||||
|
|
|
@ -483,10 +483,10 @@ public class ResourceTest
|
|||
assumeTrue(OS.IS_WINDOWS);
|
||||
|
||||
String path = __userURL.toURI().getPath().replace('/','\\')+"resource.txt";
|
||||
System.err.println(path);
|
||||
//System.err.println(path);
|
||||
|
||||
Resource resource = Resource.newResource(path, false);
|
||||
System.err.println(resource);
|
||||
//System.err.println(resource);
|
||||
assertTrue(resource.exists());
|
||||
|
||||
/*
|
||||
|
|
|
@ -181,6 +181,16 @@ public class UpgradeRequest
|
|||
{
|
||||
return Collections.unmodifiableMap(parameters);
|
||||
}
|
||||
|
||||
public String getProtocolVersion()
|
||||
{
|
||||
String version = getHeader("Sec-WebSocket-Version");
|
||||
if (version == null)
|
||||
{
|
||||
return "13"; // Default
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
public String getQueryString()
|
||||
{
|
||||
|
|
|
@ -163,7 +163,7 @@ public class UpgradeResponse
|
|||
/**
|
||||
* Set the list of extensions that are approved for use with this websocket.
|
||||
* <p>
|
||||
* This is Advanced usage of the {@link WebSocketCreator} to allow for a custom set of negotiated extensions.
|
||||
* This is Advanced usage of the WebSocketCreator to allow for a custom set of negotiated extensions.
|
||||
* <p>
|
||||
* Notes:
|
||||
* <ul>
|
||||
|
|
|
@ -209,11 +209,11 @@ public class UpgradeConnection extends AbstractConnection
|
|||
{
|
||||
EndPoint endp = getEndPoint();
|
||||
Executor executor = getExecutor();
|
||||
WebSocketClientConnection connection = new WebSocketClientConnection(endp,executor,connectPromise);
|
||||
|
||||
// Initialize / Negotiate Extensions
|
||||
|
||||
EventDriver websocket = connectPromise.getDriver();
|
||||
WebSocketPolicy policy = connectPromise.getClient().getPolicy();
|
||||
WebSocketPolicy policy = websocket.getPolicy();
|
||||
|
||||
WebSocketClientConnection connection = new WebSocketClientConnection(endp,executor,connectPromise,policy);
|
||||
|
||||
WebSocketSession session = new WebSocketSession(request.getRequestURI(),websocket,connection);
|
||||
session.setPolicy(policy);
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.eclipse.jetty.io.EndPoint;
|
|||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.api.ProtocolException;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
||||
import org.eclipse.jetty.websocket.api.WriteCallback;
|
||||
import org.eclipse.jetty.websocket.api.extensions.Frame;
|
||||
import org.eclipse.jetty.websocket.api.extensions.IncomingFrames;
|
||||
|
@ -45,9 +46,9 @@ public class WebSocketClientConnection extends AbstractWebSocketConnection
|
|||
private final Masker masker;
|
||||
private final AtomicBoolean opened = new AtomicBoolean(false);
|
||||
|
||||
public WebSocketClientConnection(EndPoint endp, Executor executor, ConnectPromise connectPromise)
|
||||
public WebSocketClientConnection(EndPoint endp, Executor executor, ConnectPromise connectPromise, WebSocketPolicy policy)
|
||||
{
|
||||
super(endp,executor,connectPromise.getClient().getScheduler(),connectPromise.getClient().getPolicy(),connectPromise.getClient().getBufferPool());
|
||||
super(endp,executor,connectPromise.getClient().getScheduler(),policy,connectPromise.getClient().getBufferPool());
|
||||
this.connectPromise = connectPromise;
|
||||
this.masker = connectPromise.getMasker();
|
||||
assert (this.masker != null);
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.client;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.EventQueue;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
|
||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||
import org.junit.Assert;
|
||||
|
||||
@WebSocket(maxMessageSize = 100*1024)
|
||||
public class MaxMessageSocket
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(MaxMessageSocket.class);
|
||||
private Session session;
|
||||
public CountDownLatch openLatch = new CountDownLatch(1);
|
||||
public CountDownLatch closeLatch = new CountDownLatch(1);
|
||||
public CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
public EventQueue<String> messageQueue = new EventQueue<>();
|
||||
public EventQueue<Throwable> errorQueue = new EventQueue<>();
|
||||
public int closeCode = -1;
|
||||
public StringBuilder closeMessage = new StringBuilder();
|
||||
|
||||
@OnWebSocketConnect
|
||||
public void onConnect(Session session)
|
||||
{
|
||||
this.session = session;
|
||||
openLatch.countDown();
|
||||
}
|
||||
|
||||
@OnWebSocketClose
|
||||
public void onClose(int statusCode, String reason)
|
||||
{
|
||||
LOG.debug("onWebSocketClose({},{})",statusCode,reason);
|
||||
closeCode = statusCode;
|
||||
closeMessage.append(reason);
|
||||
closeLatch.countDown();
|
||||
}
|
||||
|
||||
@OnWebSocketMessage
|
||||
public void onMessage(String message)
|
||||
{
|
||||
LOG.debug("onWebSocketText({})",message);
|
||||
messageQueue.offer(message);
|
||||
dataLatch.countDown();
|
||||
}
|
||||
|
||||
@OnWebSocketError
|
||||
public void onError(Throwable cause)
|
||||
{
|
||||
LOG.debug("onWebSocketError",cause);
|
||||
Assert.assertThat("Error capture",errorQueue.offer(cause),is(true));
|
||||
}
|
||||
|
||||
public Session getSession()
|
||||
{
|
||||
return this.session;
|
||||
}
|
||||
|
||||
public void awaitConnect(int duration, TimeUnit unit) throws InterruptedException
|
||||
{
|
||||
Assert.assertThat("Client Socket connected",openLatch.await(duration,unit),is(true));
|
||||
}
|
||||
|
||||
public void waitForMessage(int timeoutDuration, TimeUnit timeoutUnit) throws InterruptedException
|
||||
{
|
||||
LOG.debug("Waiting for message");
|
||||
Assert.assertThat("Message Received",dataLatch.await(timeoutDuration,timeoutUnit),is(true));
|
||||
}
|
||||
|
||||
public void assertMessage(String expected)
|
||||
{
|
||||
String actual = messageQueue.poll();
|
||||
Assert.assertEquals("Message",expected,actual);
|
||||
}
|
||||
}
|
|
@ -22,11 +22,13 @@ import static org.hamcrest.Matchers.*;
|
|||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.AdvancedRunner;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.UpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.client.blockhead.BlockheadServer;
|
||||
|
@ -42,14 +44,6 @@ import org.junit.runner.RunWith;
|
|||
public class WebSocketClientTest
|
||||
{
|
||||
private BlockheadServer server;
|
||||
private WebSocketClient client;
|
||||
|
||||
@Before
|
||||
public void startClient() throws Exception
|
||||
{
|
||||
client = new WebSocketClient();
|
||||
client.start();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void startServer() throws Exception
|
||||
|
@ -58,12 +52,6 @@ public class WebSocketClientTest
|
|||
server.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void stopClient() throws Exception
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
|
||||
@After
|
||||
public void stopServer() throws Exception
|
||||
{
|
||||
|
@ -73,79 +61,106 @@ public class WebSocketClientTest
|
|||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testAddExtension_NotInstalled() throws Exception
|
||||
{
|
||||
TrackingSocket cliSock = new TrackingSocket();
|
||||
WebSocketClient client = new WebSocketClient();
|
||||
client.start();
|
||||
try
|
||||
{
|
||||
TrackingSocket cliSock = new TrackingSocket();
|
||||
|
||||
client.getPolicy().setIdleTimeout(10000);
|
||||
client.getPolicy().setIdleTimeout(10000);
|
||||
|
||||
URI wsUri = server.getWsUri();
|
||||
ClientUpgradeRequest request = new ClientUpgradeRequest();
|
||||
request.setSubProtocols("echo");
|
||||
request.addExtensions("x-bad");
|
||||
URI wsUri = server.getWsUri();
|
||||
ClientUpgradeRequest request = new ClientUpgradeRequest();
|
||||
request.setSubProtocols("echo");
|
||||
request.addExtensions("x-bad");
|
||||
|
||||
// Should trigger failure on bad extension
|
||||
client.connect(cliSock,wsUri,request);
|
||||
// Should trigger failure on bad extension
|
||||
client.connect(cliSock,wsUri,request);
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicEcho_FromClient() throws Exception
|
||||
{
|
||||
TrackingSocket cliSock = new TrackingSocket();
|
||||
WebSocketClient client = new WebSocketClient();
|
||||
client.start();
|
||||
try
|
||||
{
|
||||
TrackingSocket cliSock = new TrackingSocket();
|
||||
|
||||
client.getPolicy().setIdleTimeout(10000);
|
||||
client.getPolicy().setIdleTimeout(10000);
|
||||
|
||||
URI wsUri = server.getWsUri();
|
||||
ClientUpgradeRequest request = new ClientUpgradeRequest();
|
||||
request.setSubProtocols("echo");
|
||||
Future<Session> future = client.connect(cliSock,wsUri,request);
|
||||
URI wsUri = server.getWsUri();
|
||||
ClientUpgradeRequest request = new ClientUpgradeRequest();
|
||||
request.setSubProtocols("echo");
|
||||
Future<Session> future = client.connect(cliSock,wsUri,request);
|
||||
|
||||
final ServerConnection srvSock = server.accept();
|
||||
srvSock.upgrade();
|
||||
final ServerConnection srvSock = server.accept();
|
||||
srvSock.upgrade();
|
||||
|
||||
Session sess = future.get(500,TimeUnit.MILLISECONDS);
|
||||
Assert.assertThat("Session",sess,notNullValue());
|
||||
Assert.assertThat("Session.open",sess.isOpen(),is(true));
|
||||
Assert.assertThat("Session.upgradeRequest",sess.getUpgradeRequest(),notNullValue());
|
||||
Assert.assertThat("Session.upgradeResponse",sess.getUpgradeResponse(),notNullValue());
|
||||
Session sess = future.get(500,TimeUnit.MILLISECONDS);
|
||||
Assert.assertThat("Session",sess,notNullValue());
|
||||
Assert.assertThat("Session.open",sess.isOpen(),is(true));
|
||||
Assert.assertThat("Session.upgradeRequest",sess.getUpgradeRequest(),notNullValue());
|
||||
Assert.assertThat("Session.upgradeResponse",sess.getUpgradeResponse(),notNullValue());
|
||||
|
||||
cliSock.assertWasOpened();
|
||||
cliSock.assertNotClosed();
|
||||
cliSock.assertWasOpened();
|
||||
cliSock.assertNotClosed();
|
||||
|
||||
Assert.assertThat("client.connectionManager.sessions.size",client.getConnectionManager().getSessions().size(),is(1));
|
||||
Assert.assertThat("client.connectionManager.sessions.size",client.getConnectionManager().getSessions().size(),is(1));
|
||||
|
||||
cliSock.getSession().getRemote().sendStringByFuture("Hello World!");
|
||||
srvSock.echoMessage(1,TimeUnit.MILLISECONDS,500);
|
||||
// wait for response from server
|
||||
cliSock.waitForMessage(500,TimeUnit.MILLISECONDS);
|
||||
cliSock.getSession().getRemote().sendStringByFuture("Hello World!");
|
||||
srvSock.echoMessage(1,TimeUnit.MILLISECONDS,500);
|
||||
// wait for response from server
|
||||
cliSock.waitForMessage(500,TimeUnit.MILLISECONDS);
|
||||
|
||||
cliSock.assertMessage("Hello World!");
|
||||
cliSock.assertMessage("Hello World!");
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicEcho_FromServer() throws Exception
|
||||
{
|
||||
TrackingSocket wsocket = new TrackingSocket();
|
||||
Future<Session> future = client.connect(wsocket,server.getWsUri());
|
||||
WebSocketClient client = new WebSocketClient();
|
||||
client.start();
|
||||
try
|
||||
{
|
||||
TrackingSocket wsocket = new TrackingSocket();
|
||||
Future<Session> future = client.connect(wsocket,server.getWsUri());
|
||||
|
||||
// Server
|
||||
final ServerConnection srvSock = server.accept();
|
||||
srvSock.upgrade();
|
||||
// Server
|
||||
final ServerConnection srvSock = server.accept();
|
||||
srvSock.upgrade();
|
||||
|
||||
// Validate connect
|
||||
Session sess = future.get(500,TimeUnit.MILLISECONDS);
|
||||
Assert.assertThat("Session",sess,notNullValue());
|
||||
Assert.assertThat("Session.open",sess.isOpen(),is(true));
|
||||
Assert.assertThat("Session.upgradeRequest",sess.getUpgradeRequest(),notNullValue());
|
||||
Assert.assertThat("Session.upgradeResponse",sess.getUpgradeResponse(),notNullValue());
|
||||
// Validate connect
|
||||
Session sess = future.get(500,TimeUnit.MILLISECONDS);
|
||||
Assert.assertThat("Session",sess,notNullValue());
|
||||
Assert.assertThat("Session.open",sess.isOpen(),is(true));
|
||||
Assert.assertThat("Session.upgradeRequest",sess.getUpgradeRequest(),notNullValue());
|
||||
Assert.assertThat("Session.upgradeResponse",sess.getUpgradeResponse(),notNullValue());
|
||||
|
||||
// Have server send initial message
|
||||
srvSock.write(WebSocketFrame.text("Hello World"));
|
||||
// Have server send initial message
|
||||
srvSock.write(WebSocketFrame.text("Hello World"));
|
||||
|
||||
// Verify connect
|
||||
future.get(500,TimeUnit.MILLISECONDS);
|
||||
wsocket.assertWasOpened();
|
||||
wsocket.awaitMessage(1,TimeUnit.SECONDS,2);
|
||||
// Verify connect
|
||||
future.get(500,TimeUnit.MILLISECONDS);
|
||||
wsocket.assertWasOpened();
|
||||
wsocket.awaitMessage(1,TimeUnit.SECONDS,2);
|
||||
|
||||
wsocket.assertMessage("Hello World");
|
||||
wsocket.assertMessage("Hello World");
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -158,7 +173,7 @@ public class WebSocketClientTest
|
|||
TrackingSocket wsocket = new TrackingSocket();
|
||||
|
||||
URI wsUri = server.getWsUri();
|
||||
Future<Session> future = client.connect(wsocket,wsUri);
|
||||
Future<Session> future = fact.connect(wsocket,wsUri);
|
||||
|
||||
ServerConnection ssocket = server.accept();
|
||||
ssocket.upgrade();
|
||||
|
@ -198,7 +213,7 @@ public class WebSocketClientTest
|
|||
TrackingSocket wsocket = new TrackingSocket();
|
||||
|
||||
URI wsUri = server.getWsUri();
|
||||
Future<Session> future = client.connect(wsocket,wsUri);
|
||||
Future<Session> future = factSmall.connect(wsocket,wsUri);
|
||||
|
||||
ServerConnection ssocket = server.accept();
|
||||
ssocket.upgrade();
|
||||
|
@ -226,6 +241,48 @@ public class WebSocketClientTest
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxMessageSize() throws Exception
|
||||
{
|
||||
WebSocketClient client = new WebSocketClient();
|
||||
client.start();
|
||||
try
|
||||
{
|
||||
MaxMessageSocket wsocket = new MaxMessageSocket();
|
||||
|
||||
URI wsUri = server.getWsUri();
|
||||
Future<Session> future = client.connect(wsocket,wsUri);
|
||||
|
||||
ServerConnection ssocket = server.accept();
|
||||
ssocket.upgrade();
|
||||
|
||||
wsocket.awaitConnect(1,TimeUnit.SECONDS);
|
||||
|
||||
Session sess = future.get(500,TimeUnit.MILLISECONDS);
|
||||
Assert.assertThat("Session",sess,notNullValue());
|
||||
Assert.assertThat("Session.open",sess.isOpen(),is(true));
|
||||
|
||||
// Create string that is larger than default size of 64k
|
||||
// but smaller than maxMessageSize of 100k
|
||||
byte buf[] = new byte[80*1024];
|
||||
Arrays.fill(buf,(byte)'x');
|
||||
String msg = StringUtil.toUTF8String(buf,0,buf.length);
|
||||
|
||||
wsocket.getSession().getRemote().sendStringByFuture(msg);
|
||||
ssocket.echoMessage(1,TimeUnit.MILLISECONDS,500);
|
||||
// wait for response from server
|
||||
wsocket.waitForMessage(500,TimeUnit.MILLISECONDS);
|
||||
|
||||
wsocket.assertMessage(msg);
|
||||
|
||||
Assert.assertTrue(wsocket.dataLatch.await(1000,TimeUnit.SECONDS));
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParameterMap() throws Exception
|
||||
{
|
||||
|
@ -236,7 +293,7 @@ public class WebSocketClientTest
|
|||
TrackingSocket wsocket = new TrackingSocket();
|
||||
|
||||
URI wsUri = server.getWsUri().resolve("/test?snack=cashews&amount=handful&brand=off");
|
||||
Future<Session> future = client.connect(wsocket,wsUri);
|
||||
Future<Session> future = fact.connect(wsocket,wsUri);
|
||||
|
||||
ServerConnection ssocket = server.accept();
|
||||
ssocket.upgrade();
|
||||
|
|
|
@ -101,6 +101,7 @@ public class BlockheadServer
|
|||
this.socket = socket;
|
||||
this.incomingFrames = new IncomingFramesCapture();
|
||||
this.policy = WebSocketPolicy.newServerPolicy();
|
||||
this.policy.setMaxMessageSize(100000);
|
||||
this.bufferPool = new MappedByteBufferPool(BUFFER_SIZE);
|
||||
this.parser = new Parser(policy,bufferPool);
|
||||
this.parseCount = new AtomicInteger(0);
|
||||
|
|
|
@ -361,8 +361,6 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Inc
|
|||
|
||||
/**
|
||||
* Open/Activate the session
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void open()
|
||||
{
|
||||
|
@ -423,6 +421,7 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Inc
|
|||
public void setUpgradeRequest(UpgradeRequest request)
|
||||
{
|
||||
this.upgradeRequest = request;
|
||||
this.protocolVersion = request.getProtocolVersion();
|
||||
}
|
||||
|
||||
public void setUpgradeResponse(UpgradeResponse response)
|
||||
|
|
|
@ -22,12 +22,16 @@ import static org.hamcrest.Matchers.*;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.websocket.api.StatusCode;
|
||||
import org.eclipse.jetty.websocket.api.UpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.api.UpgradeResponse;
|
||||
import org.eclipse.jetty.websocket.common.CloseInfo;
|
||||
import org.eclipse.jetty.websocket.common.OpCode;
|
||||
import org.eclipse.jetty.websocket.common.WebSocketFrame;
|
||||
import org.eclipse.jetty.websocket.server.blockhead.BlockheadClient;
|
||||
import org.eclipse.jetty.websocket.server.examples.echo.BigEchoSocket;
|
||||
|
@ -87,7 +91,7 @@ public class AnnotatedMaxMessageSizeTest
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testEcho() throws IOException, Exception
|
||||
public void testEchoGood() throws IOException, Exception
|
||||
{
|
||||
BlockheadClient client = new BlockheadClient(serverUri);
|
||||
try
|
||||
|
@ -111,4 +115,33 @@ public class AnnotatedMaxMessageSizeTest
|
|||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEchoTooBig() throws IOException, Exception
|
||||
{
|
||||
BlockheadClient client = new BlockheadClient(serverUri);
|
||||
try
|
||||
{
|
||||
client.setProtocols("echo");
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
client.expectUpgradeResponse();
|
||||
|
||||
// Generate text frame
|
||||
byte buf[] = new byte[90*1024]; // buffer bigger than maxMessageSize
|
||||
Arrays.fill(buf,(byte)'x');
|
||||
client.write(WebSocketFrame.text().setPayload(buf));
|
||||
|
||||
// Read frame (hopefully close frame saying its too large)
|
||||
IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,500);
|
||||
WebSocketFrame tf = capture.getFrames().poll();
|
||||
Assert.assertThat("Frame is close", tf.getOpCode(), is(OpCode.CLOSE));
|
||||
CloseInfo close = new CloseInfo(tf);
|
||||
Assert.assertThat("Close Code", close.getStatusCode(), is(StatusCode.MESSAGE_TOO_LARGE));
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
14
pom.xml
14
pom.xml
|
@ -741,7 +741,7 @@
|
|||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<profile>
|
||||
<id>7u9</id>
|
||||
<activation>
|
||||
<property>
|
||||
|
@ -849,6 +849,18 @@
|
|||
<npn.version>1.1.6.v20130911</npn.version>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>7u45</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>java.version</name>
|
||||
<value>1.7.0_45</value>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<npn.version>1.1.6.v20130911</npn.version>
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
|
||||
|
|
|
@ -29,6 +29,11 @@ detected.
|
|||
<Set name="defaultsDescriptor"><Property name="jetty.home" default="."/>/etc/webdefault.xml</Set>
|
||||
<Set name="overrideDescriptor"><Property name="jetty.webapps" default="."/>/test.d/override-web.xml</Set>
|
||||
|
||||
<!-- Enable symlinks
|
||||
<Call name="addAliasCheck">
|
||||
<Arg><New class="org.eclipse.jetty.server.handler.AllowSymLinkAliasChecker"/></Arg>
|
||||
</Call>
|
||||
-->
|
||||
|
||||
<!-- virtual hosts
|
||||
<Set name="virtualHosts">
|
||||
|
|
|
@ -668,6 +668,11 @@ public class Dump extends HttpServlet
|
|||
try{pout.write("<td>"+getServletContext().getResourcePaths(res)+"</td>");}
|
||||
catch(Exception e) {pout.write("<td>"+"" +e+"</td>");}
|
||||
|
||||
pout.write("</tr><tr>\n");
|
||||
pout.write("<th align=\"right\">getServletContext().getRealPath(...): </th>");
|
||||
try{pout.write("<td>"+getServletContext().getRealPath(res)+"</td>");}
|
||||
catch(Exception e) {pout.write("<td>"+"" +e+"</td>");}
|
||||
|
||||
pout.write("</tr><tr>\n");
|
||||
pout.write("<th align=\"right\">getServletContext().getContext(...): </th>");
|
||||
|
||||
|
@ -690,8 +695,12 @@ public class Dump extends HttpServlet
|
|||
if (cp==null || "/".equals(cp))
|
||||
cp="";
|
||||
pout.write("</tr><tr>\n");
|
||||
pout.write("<th align=\"right\">getServletContext().getContext(...),getRequestDispatcher(...): </th>");
|
||||
pout.write("<td>"+getServletContext().getContext(res).getRequestDispatcher(res.substring(cp.length()))+"</td>");
|
||||
pout.write("<th align=\"right\">getServletContext().getContext(...).getRequestDispatcher(...): </th>");
|
||||
pout.write("<td>"+context.getRequestDispatcher(res.substring(cp.length()))+"</td>");
|
||||
|
||||
pout.write("</tr><tr>\n");
|
||||
pout.write("<th align=\"right\">getServletContext().getContext(...).getRealPath(...): </th>");
|
||||
pout.write("<td>"+context.getRealPath(res.substring(cp.length()))+"</td>");
|
||||
}
|
||||
|
||||
pout.write("</tr><tr>\n");
|
||||
|
|
Loading…
Reference in New Issue