Merge branch 'master' into release-9

This commit is contained in:
Jesse McConnell 2013-05-06 17:38:35 -05:00
commit 573e90c581
180 changed files with 5544 additions and 2450 deletions

View File

@ -1,4 +1,8 @@
branches:
only:
- /^release-.*$/
language: java
script: mvn install
jdk:
- openjdk7
- oraclejdk7

View File

@ -5,6 +5,6 @@ org.eclipse.jetty.SOURCE=false
#org.eclipse.jetty.STACKS=false
#org.eclipse.jetty.spdy.LEVEL=DEBUG
#org.eclipse.jetty.server.LEVEL=DEBUG
org.eclipse.jetty.io.LEVEL=DEBUG
org.eclipse.jetty.io.ssl.LEVEL=DEBUG
#org.eclipse.jetty.io.LEVEL=DEBUG
#org.eclipse.jetty.io.ssl.LEVEL=DEBUG
#org.eclipse.jetty.spdy.server.LEVEL=DEBUG

View File

@ -40,6 +40,7 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
public static final int DEFAULT_MAX_CONTENT_LENGTH = 4096;
public static final Logger LOG = Log.getLogger(AuthenticationProtocolHandler.class);
private static final Pattern AUTHENTICATE_PATTERN = Pattern.compile("([^\\s]+)\\s+realm=\"([^\"]+)\"(.*)", Pattern.CASE_INSENSITIVE);
private static final String AUTHENTICATION_ATTRIBUTE = AuthenticationProtocolHandler.class.getName() + ".authentication";
private final HttpClient client;
private final int maxContentLength;
@ -90,6 +91,15 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
return;
}
HttpConversation conversation = client.getConversation(request.getConversationID(), false);
if (conversation.getAttribute(AUTHENTICATION_ATTRIBUTE) != null)
{
// We have already tried to authenticate, but we failed again
LOG.debug("Bad credentials for {}", request);
forwardSuccessComplete(request, response);
return;
}
HttpHeader header = getAuthenticateHeader();
List<Authentication.HeaderInfo> headerInfos = parseAuthenticateHeader(response, header);
if (headerInfos.isEmpty())
@ -118,7 +128,6 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
return;
}
HttpConversation conversation = client.getConversation(request.getConversationID(), false);
final Authentication.Result authnResult = authentication.authenticate(request, response, headerInfo, conversation);
LOG.debug("Authentication result {}", authnResult);
if (authnResult == null)
@ -127,6 +136,8 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
return;
}
conversation.setAttribute(AUTHENTICATION_ATTRIBUTE, true);
Request newRequest = client.copyRequest(request, request.getURI());
authnResult.apply(newRequest);
newRequest.onResponseSuccess(new Response.SuccessListener()

View File

@ -63,6 +63,7 @@ import org.eclipse.jetty.io.ssl.SslConnection;
import org.eclipse.jetty.util.Jetty;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.SocketAddressResolver;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -362,7 +363,7 @@ public class HttpClient extends ContainerLifeCycle
*/
public Request newRequest(String host, int port)
{
return newRequest(URI.create(address("http", host, port)));
return newRequest(address("http", host, port));
}
/**
@ -417,9 +418,11 @@ public class HttpClient extends ContainerLifeCycle
return newRequest;
}
private String address(String scheme, String host, int port)
protected String address(String scheme, String host, int port)
{
return scheme + "://" + host + ":" + port;
StringBuilder result = new StringBuilder();
URIUtil.appendSchemeHostPort(result, scheme, host, port);
return result.toString();
}
/**
@ -900,6 +903,13 @@ public class HttpClient extends ContainerLifeCycle
return encodingField;
}
protected String normalizeHost(String host)
{
if (host != null && host.matches("\\[.*\\]"))
return host.substring(1, host.length() - 1);
return host;
}
protected int normalizePort(String scheme, int port)
{
return port > 0 ? port : HttpScheme.HTTPS.is(scheme) ? 443 : 80;

View File

@ -18,14 +18,10 @@
package org.eclipse.jetty.client;
import java.io.UnsupportedEncodingException;
import java.net.HttpCookie;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.UnsupportedCharsetException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
@ -35,17 +31,14 @@ import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.ContentProvider;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.Fields;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -166,45 +159,12 @@ public class HttpConnection extends AbstractConnection implements Connection
path = "/";
request.path(path);
}
if (destination.isProxied() && HttpMethod.CONNECT != request.getMethod())
if (destination.isProxied() && HttpMethod.CONNECT != method)
{
path = request.getURI().toString();
request.path(path);
}
Fields fields = request.getParams();
if (!fields.isEmpty())
{
StringBuilder params = new StringBuilder();
for (Iterator<Fields.Field> fieldIterator = fields.iterator(); fieldIterator.hasNext();)
{
Fields.Field field = fieldIterator.next();
String[] values = field.values();
for (int i = 0; i < values.length; ++i)
{
if (i > 0)
params.append("&");
params.append(field.name()).append("=");
params.append(urlEncode(values[i]));
}
if (fieldIterator.hasNext())
params.append("&");
}
// POST with no content, send parameters as body
if (method == HttpMethod.POST && request.getContent() == null)
{
request.header(HttpHeader.CONTENT_TYPE, MimeTypes.Type.FORM_ENCODED.asString());
request.content(new StringContentProvider(params.toString()));
}
else
{
path += "?";
path += params.toString();
request.path(path);
}
}
// If we are HTTP 1.1, add the Host header
if (version.getVersion() > 10)
{
@ -257,19 +217,6 @@ public class HttpConnection extends AbstractConnection implements Connection
}
}
private String urlEncode(String value)
{
String encoding = "UTF-8";
try
{
return URLEncoder.encode(value, encoding);
}
catch (UnsupportedEncodingException e)
{
throw new UnsupportedCharsetException(encoding);
}
}
public HttpExchange getExchange()
{
return exchange.get();

View File

@ -374,10 +374,9 @@ public class HttpReceiver implements HttpParser.ResponseHandler<ByteBuffer>
}
@Override
public boolean earlyEOF()
public void earlyEOF()
{
failAndClose(new EOFException());
return false;
}
private void failAndClose(Throwable failure)

View File

@ -22,10 +22,12 @@ import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.UnsupportedCharsetException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@ -62,6 +64,7 @@ public class HttpRequest implements Request
private URI uri;
private String scheme;
private String path;
private String query;
private HttpMethod method;
private HttpVersion version;
private long idleTimeout;
@ -80,35 +83,15 @@ public class HttpRequest implements Request
this.client = client;
this.conversation = conversation;
scheme = uri.getScheme();
host = uri.getHost();
host = client.normalizeHost(uri.getHost());
port = client.normalizePort(scheme, uri.getPort());
path = uri.getRawPath();
String query = uri.getRawQuery();
if (query != null)
{
for (String nameValue : query.split("&"))
{
String[] parts = nameValue.split("=");
param(parts[0], parts.length < 2 ? "" : urlDecode(parts[1]));
}
}
query = uri.getRawQuery();
extractParams(query);
this.uri = buildURI();
followRedirects(client.isFollowRedirects());
}
private String urlDecode(String value)
{
String charset = "UTF-8";
try
{
return URLDecoder.decode(value, charset);
}
catch (UnsupportedEncodingException x)
{
throw new UnsupportedCharsetException(charset);
}
}
@Override
public long getConversationID()
{
@ -163,11 +146,25 @@ public class HttpRequest implements Request
@Override
public Request path(String path)
{
this.path = path;
URI uri = URI.create(path);
this.path = uri.getRawPath();
String query = uri.getRawQuery();
if (query != null)
{
this.query = query;
params.clear();
extractParams(query);
}
this.uri = buildURI();
return this;
}
@Override
public String getQuery()
{
return query;
}
@Override
public URI getURI()
{
@ -191,13 +188,14 @@ public class HttpRequest implements Request
public Request param(String name, String value)
{
params.add(name, value);
this.query = buildQuery();
return this;
}
@Override
public Fields getParams()
{
return params;
return new Fields(params, true);
}
@Override
@ -496,12 +494,73 @@ public class HttpRequest implements Request
return aborted;
}
private String buildQuery()
{
StringBuilder result = new StringBuilder();
for (Iterator<Fields.Field> iterator = params.iterator(); iterator.hasNext();)
{
Fields.Field field = iterator.next();
String[] values = field.values();
for (int i = 0; i < values.length; ++i)
{
if (i > 0)
result.append("&");
result.append(field.name()).append("=");
result.append(urlEncode(values[i]));
}
if (iterator.hasNext())
result.append("&");
}
return result.toString();
}
private String urlEncode(String value)
{
String encoding = "UTF-8";
try
{
return URLEncoder.encode(value, encoding);
}
catch (UnsupportedEncodingException e)
{
throw new UnsupportedCharsetException(encoding);
}
}
private void extractParams(String query)
{
if (query != null)
{
for (String nameValue : query.split("&"))
{
String[] parts = nameValue.split("=");
param(parts[0], parts.length < 2 ? "" : urlDecode(parts[1]));
}
}
}
private String urlDecode(String value)
{
String charset = "UTF-8";
try
{
return URLDecoder.decode(value, charset);
}
catch (UnsupportedEncodingException x)
{
throw new UnsupportedCharsetException(charset);
}
}
private URI buildURI()
{
String path = getPath();
String query = getQuery();
if (query != null)
path += "?" + query;
URI result = URI.create(path);
if (!result.isAbsolute())
result = URI.create(getScheme() + "://" + getHost() + ":" + getPort() + path);
result = URI.create(client.address(getScheme(), getHost(), getPort()) + path);
return result;
}

View File

@ -172,7 +172,11 @@ public class HttpSender implements AsyncContentProvider.Listener
{
ContentProvider requestContent = request.getContent();
long contentLength = requestContent == null ? -1 : requestContent.getLength();
requestInfo = new HttpGenerator.RequestInfo(request.getVersion(), request.getHeaders(), contentLength, request.getMethod().asString(), request.getPath());
String path = request.getPath();
String query = request.getQuery();
if (query != null)
path += "?" + query;
requestInfo = new HttpGenerator.RequestInfo(request.getVersion(), request.getHeaders(), contentLength, request.getMethod().asString(), path);
break;
}
case NEED_HEADER:

View File

@ -20,6 +20,7 @@ package org.eclipse.jetty.client.api;
import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.file.Path;
import java.util.EventListener;
@ -86,18 +87,33 @@ public interface Request
Request method(HttpMethod method);
/**
* @return the path of this request, such as "/"
* @return the path of this request, such as "/" or "/path" - without the query
* @see #getQuery()
*/
String getPath();
/**
* @param path the path of this request, such as "/"
* Specifies the path - and possibly the query - of this request.
* If the query part is specified, parameter values must be properly
* {@link URLEncoder#encode(String, String) UTF-8 URL encoded}.
* For example, if the parameter value is the euro symbol &euro; then the
* query string must be "param=%E2%82%AC".
* For transparent encoding of parameter values, use {@link #param(String, String)}.
*
* @param path the path of this request, such as "/" or "/path?param=1"
* @return this request object
*/
Request path(String path);
/**
* @return the full URI of this request such as "http://host:port/path"
* @return the query string of this request such as "param=1"
* @see #getPath()
* @see #getParams()
*/
String getQuery();
/**
* @return the full URI of this request such as "http://host:port/path?param=1"
*/
URI getURI();
@ -118,6 +134,9 @@ public interface Request
Fields getParams();
/**
* Adds a query parameter with the given name and value.
* The value is {@link URLEncoder#encode(String, String) UTF-8 URL encoded}.
*
* @param name the name of the query parameter
* @param value the value of the query parameter
* @return this request object

View File

@ -20,7 +20,6 @@ package org.eclipse.jetty.client;
import java.io.IOException;
import java.net.Socket;
import java.net.URI;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@ -94,8 +93,6 @@ public class ExternalSiteTest
{
Assert.assertTrue(result.isSucceeded());
Assert.assertEquals(200, result.getResponse().getStatus());
URI uri = result.getRequest().getURI();
Assert.assertTrue(uri.getPort() > 0);
latch2.countDown();
}
});

View File

@ -304,4 +304,20 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
Assert.assertEquals(401, response.getStatus());
Assert.assertTrue(requests.get().await(5, TimeUnit.SECONDS));
}
@Test
public void test_BasicAuthentication_WithWrongPassword() throws Exception
{
startBasic(new EmptyServerHandler());
AuthenticationStore authenticationStore = client.getAuthenticationStore();
URI uri = URI.create(scheme + "://localhost:" + connector.getLocalPort());
BasicAuthentication authentication = new BasicAuthentication(uri, realm, "basic", "wrong");
authenticationStore.addAuthentication(authentication);
Request request = client.newRequest("localhost", connector.getLocalPort()).scheme(scheme).path("/secure");
ContentResponse response = request.timeout(555, TimeUnit.SECONDS).send();
Assert.assertNotNull(response);
Assert.assertEquals(401, response.getStatus());
}
}

View File

@ -0,0 +1,241 @@
//
// ========================================================================
// 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.client;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.Fields;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.Assert;
import org.junit.Test;
public class HttpClientURITest extends AbstractHttpClientServerTest
{
public HttpClientURITest(SslContextFactory sslContextFactory)
{
super(sslContextFactory);
}
@Test
public void testIPv6Host() throws Exception
{
start(new EmptyServerHandler());
String host = "::1";
Request request = client.newRequest(host, connector.getLocalPort())
.scheme(scheme)
.timeout(5, TimeUnit.SECONDS);
Assert.assertEquals(host, request.getHost());
StringBuilder uri = new StringBuilder();
URIUtil.appendSchemeHostPort(uri, scheme, host, connector.getLocalPort());
Assert.assertEquals(uri.toString(), request.getURI().toString());
Assert.assertEquals(HttpStatus.OK_200, request.send().getStatus());
}
@Test
public void testPath() throws Exception
{
final String path = "/path";
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(path, request.getRequestURI());
}
});
Request request = client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.timeout(5, TimeUnit.SECONDS)
.path(path);
Assert.assertEquals(path, request.getPath());
Assert.assertNull(request.getQuery());
Fields params = request.getParams();
Assert.assertEquals(0, params.size());
Assert.assertTrue(request.getURI().toString().endsWith(path));
ContentResponse response = request.send();
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
}
@Test
public void testPathWithQuery() throws Exception
{
String name = "a";
String value = "1";
final String query = name + "=" + value;
final String path = "/path";
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(path, request.getRequestURI());
Assert.assertEquals(query, request.getQueryString());
}
});
Request request = client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.timeout(5, TimeUnit.SECONDS)
.path(path + "?" + query);
Assert.assertEquals(path, request.getPath());
Assert.assertEquals(query, request.getQuery());
Fields params = request.getParams();
Assert.assertEquals(1, params.size());
Assert.assertEquals(value, params.get(name).value());
ContentResponse response = request.send();
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
}
@Test
public void testPathWithParam() throws Exception
{
String name = "a";
String value = "1";
final String query = name + "=" + value;
final String path = "/path";
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(path, request.getRequestURI());
Assert.assertEquals(query, request.getQueryString());
}
});
Request request = client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.timeout(5, TimeUnit.SECONDS)
.path(path)
.param(name, value);
Assert.assertEquals(path, request.getPath());
Assert.assertEquals(query, request.getQuery());
Fields params = request.getParams();
Assert.assertEquals(1, params.size());
Assert.assertEquals(value, params.get(name).value());
ContentResponse response = request.send();
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
}
@Test
public void testPathWithQueryAndParam() throws Exception
{
String name1 = "a";
String value1 = "1";
String name2 = "b";
String value2 = "2";
final String query = name1 + "=" + value1 + "&" + name2 + "=" + value2;
final String path = "/path";
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(path, request.getRequestURI());
Assert.assertEquals(query, request.getQueryString());
}
});
Request request = client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.timeout(5, TimeUnit.SECONDS)
.path(path + "?" + name1 + "=" + value1)
.param(name2, value2);
Assert.assertEquals(path, request.getPath());
Assert.assertEquals(query, request.getQuery());
Fields params = request.getParams();
Assert.assertEquals(2, params.size());
Assert.assertEquals(value1, params.get(name1).value());
Assert.assertEquals(value2, params.get(name2).value());
ContentResponse response = request.send();
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
}
@Test
public void testPathWithQueryAndParamValueEncoded() throws Exception
{
final String name1 = "a";
final String value1 = "\u20AC";
final String encodedValue1 = URLEncoder.encode(value1, "UTF-8");
final String name2 = "b";
final String value2 = "\u00A5";
String encodedValue2 = URLEncoder.encode(value2, "UTF-8");
final String query = name1 + "=" + encodedValue1 + "&" + name2 + "=" + encodedValue2;
final String path = "/path";
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(path, request.getRequestURI());
Assert.assertEquals(query, request.getQueryString());
Assert.assertEquals(value1, request.getParameter(name1));
Assert.assertEquals(value2, request.getParameter(name2));
}
});
Request request = client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.timeout(5, TimeUnit.SECONDS)
.path(path + "?" + name1 + "=" + encodedValue1)
.param(name2, value2);
Assert.assertEquals(path, request.getPath());
Assert.assertEquals(query, request.getQuery());
Fields params = request.getParams();
Assert.assertEquals(2, params.size());
Assert.assertEquals(value1, params.get(name1).value());
Assert.assertEquals(value2, params.get(name2).value());
ContentResponse response = request.send();
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
}
}

View File

@ -24,6 +24,8 @@ import java.util.Arrays;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ -66,7 +68,10 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
Assert.assertEquals(0, activeConnections.size());
final CountDownLatch headersLatch = new CountDownLatch(1);
final CountDownLatch testLatch = new CountDownLatch(1);
final CountDownLatch successLatch = new CountDownLatch(3);
final AtomicBoolean failed = new AtomicBoolean(false);
client.newRequest(host, port)
.scheme(scheme)
.onRequestSuccess(new Request.SuccessListener()
@ -82,9 +87,15 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
@Override
public void onHeaders(Response response)
{
Assert.assertEquals(0, idleConnections.size());
Assert.assertEquals(1, activeConnections.size());
headersLatch.countDown();
try
{
testLatch.await();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
})
.send(new Response.Listener.Empty()
@ -98,16 +109,21 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
@Override
public void onComplete(Result result)
{
Assert.assertFalse(result.isFailed());
failed.set(result.isFailed());
successLatch.countDown();
}
});
Assert.assertTrue(headersLatch.await(5, TimeUnit.SECONDS));
Assert.assertEquals(0, idleConnections.size());
Assert.assertEquals(1, activeConnections.size());
testLatch.countDown();
Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS));
Assert.assertEquals(1, idleConnections.size());
Assert.assertEquals(0, activeConnections.size());
Assert.assertFalse(failed.get());
}
@Test
@ -127,6 +143,8 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
final CountDownLatch beginLatch = new CountDownLatch(1);
final CountDownLatch failureLatch = new CountDownLatch(2);
final AtomicBoolean failed = new AtomicBoolean(false);
client.newRequest(host, port).scheme(scheme).listener(new Request.Listener.Empty()
{
@Override
@ -146,9 +164,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
@Override
public void onComplete(Result result)
{
Assert.assertTrue(result.isFailed());
Assert.assertEquals(0, idleConnections.size());
Assert.assertEquals(0, activeConnections.size());
failed.set(result.isFailed());
failureLatch.countDown();
}
});
@ -158,6 +174,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
Assert.assertEquals(0, idleConnections.size());
Assert.assertEquals(0, activeConnections.size());
Assert.assertTrue(failed.get());
}
@Test
@ -176,6 +193,9 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
Assert.assertEquals(0, activeConnections.size());
final CountDownLatch successLatch = new CountDownLatch(3);
final AtomicBoolean failed = new AtomicBoolean(false);
final AtomicBoolean four_hundred = new AtomicBoolean(false);
client.newRequest(host, port)
.scheme(scheme)
.listener(new Request.Listener.Empty()
@ -198,16 +218,16 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
@Override
public void onSuccess(Response response)
{
Assert.assertEquals(400, response.getStatus());
// 400 response also come with a Connection: close,
// so the connection is closed and removed
four_hundred.set(response.getStatus()==400);
successLatch.countDown();
}
@Override
public void onComplete(Result result)
{
Assert.assertFalse(result.isFailed());
failed.set(result.isFailed());
successLatch.countDown();
}
});
@ -216,6 +236,8 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
Assert.assertEquals(0, idleConnections.size());
Assert.assertEquals(0, activeConnections.size());
Assert.assertFalse(failed.get());
Assert.assertTrue(four_hundred.get());
}
@Slow
@ -236,6 +258,8 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
final long delay = 1000;
final CountDownLatch successLatch = new CountDownLatch(3);
final AtomicBoolean failed = new AtomicBoolean(false);
final AtomicBoolean four_hundred = new AtomicBoolean(false);
client.newRequest(host, port)
.scheme(scheme)
.listener(new Request.Listener.Empty()
@ -271,16 +295,16 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
@Override
public void onSuccess(Response response)
{
Assert.assertEquals(400, response.getStatus());
// 400 response also come with a Connection: close,
// so the connection is closed and removed
four_hundred.set(response.getStatus()==400);
successLatch.countDown();
}
@Override
public void onComplete(Result result)
{
Assert.assertFalse(result.isFailed());
failed.set(result.isFailed());
successLatch.countDown();
}
});
@ -289,6 +313,8 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
Assert.assertEquals(0, idleConnections.size());
Assert.assertEquals(0, activeConnections.size());
Assert.assertFalse(failed.get());
Assert.assertTrue(four_hundred.get());
}
@Test
@ -309,6 +335,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
server.stop();
final CountDownLatch failureLatch = new CountDownLatch(2);
final AtomicBoolean failed = new AtomicBoolean(false);
client.newRequest(host, port)
.scheme(scheme)
.onRequestFailure(new Request.FailureListener()
@ -324,7 +351,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
@Override
public void onComplete(Result result)
{
Assert.assertTrue(result.isFailed());
failed.set(result.isFailed());
failureLatch.countDown();
}
});
@ -333,6 +360,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
Assert.assertEquals(0, idleConnections.size());
Assert.assertEquals(0, activeConnections.size());
Assert.assertTrue(failed.get());
}
@Test
@ -359,6 +387,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
Assert.assertEquals(0, activeConnections.size());
final CountDownLatch latch = new CountDownLatch(1);
final AtomicBoolean failed = new AtomicBoolean(false);
client.newRequest(host, port)
.scheme(scheme)
.send(new Response.Listener.Empty()
@ -366,9 +395,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
@Override
public void onComplete(Result result)
{
Assert.assertFalse(result.isFailed());
Assert.assertEquals(0, idleConnections.size());
Assert.assertEquals(0, activeConnections.size());
failed.set(result.isFailed());
latch.countDown();
}
});
@ -377,6 +404,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
Assert.assertEquals(0, idleConnections.size());
Assert.assertEquals(0, activeConnections.size());
Assert.assertFalse(failed.get());
}
@Test
@ -410,6 +438,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
Log.getLogger(HttpConnection.class).info("Expecting java.lang.IllegalStateException: HttpParser{s=CLOSED,...");
final CountDownLatch latch = new CountDownLatch(1);
final AtomicBoolean failed = new AtomicBoolean(false);
ByteBuffer buffer = ByteBuffer.allocate(16 * 1024 * 1024);
Arrays.fill(buffer.array(),(byte)'x');
client.newRequest(host, port)
@ -420,8 +449,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
@Override
public void onComplete(Result result)
{
Assert.assertEquals(0, idleConnections.size());
Assert.assertEquals(0, activeConnections.size());
failed.set(result.isFailed());
latch.countDown();
}
});
@ -430,6 +458,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
Assert.assertEquals(0, idleConnections.size());
Assert.assertEquals(0, activeConnections.size());
Assert.assertTrue(failed.get());
server.stop();
}
finally

View File

@ -44,6 +44,7 @@ import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.PathAssert;
import org.eclipse.jetty.toolchain.test.TestingDir;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.xml.XmlConfiguration;
@ -290,9 +291,7 @@ public class XmlConfiguredJetty
public URI getServerURI() throws UnknownHostException
{
StringBuilder uri = new StringBuilder();
uri.append(getScheme()).append("://");
uri.append(InetAddress.getLocalHost().getHostAddress());
uri.append(":").append(getServerPort());
URIUtil.appendSchemeHostPort(uri, getScheme(), InetAddress.getLocalHost().getHostAddress(), getServerPort());
return URI.create(uri.toString());
}

View File

@ -100,9 +100,39 @@
<type>war</type>
<overWrite>true</overWrite>
<includes>**</includes>
<outputDirectory>${assembly-directory}/webapps</outputDirectory>
<outputDirectory>${assembly-directory}/webapps.demo</outputDirectory>
<destFileName>test.war</destFileName>
</artifactItem>
<artifactItem>
<groupId>org.eclipse.jetty.tests</groupId>
<artifactId>test-jaas-webapp</artifactId>
<version>${project.version}</version>
<type>war</type>
<overWrite>true</overWrite>
<includes>**</includes>
<outputDirectory>${assembly-directory}/webapps.demo</outputDirectory>
<destFileName>test-jaas.war</destFileName>
</artifactItem>
<artifactItem>
<groupId>org.eclipse.jetty.tests</groupId>
<artifactId>test-jndi-webapp</artifactId>
<version>${project.version}</version>
<type>war</type>
<overWrite>true</overWrite>
<includes>**</includes>
<outputDirectory>${assembly-directory}/webapps.demo</outputDirectory>
<destFileName>test-jndi.war</destFileName>
</artifactItem>
<artifactItem>
<groupId>org.eclipse.jetty.tests</groupId>
<artifactId>test-spec-webapp</artifactId>
<version>${project.version}</version>
<type>war</type>
<overWrite>true</overWrite>
<includes>**</includes>
<outputDirectory>${assembly-directory}/webapps.demo</outputDirectory>
<destFileName>test-spec.war</destFileName>
</artifactItem>
<artifactItem>
<groupId>org.eclipse.jetty</groupId>
<artifactId>test-proxy-webapp</artifactId>
@ -110,7 +140,7 @@
<type>war</type>
<overWrite>true</overWrite>
<includes>**</includes>
<outputDirectory>${assembly-directory}/webapps</outputDirectory>
<outputDirectory>${assembly-directory}/webapps.demo</outputDirectory>
<destFileName>xref-proxy.war</destFileName>
</artifactItem>
<artifactItem>
@ -120,7 +150,7 @@
<type>war</type>
<overWrite>true</overWrite>
<includes>**</includes>
<outputDirectory>${assembly-directory}/webapps</outputDirectory>
<outputDirectory>${assembly-directory}/webapps.demo</outputDirectory>
<destFileName>async-rest.war</destFileName>
</artifactItem>
<artifactItem>
@ -173,6 +203,7 @@
</artifactItems>
</configuration>
</execution>
<execution>
<id>unpack-setuid-config</id>
<phase>process-resources</phase>
@ -193,6 +224,70 @@
</artifactItems>
</configuration>
</execution>
<execution>
<id>unpack-test-jaas-config</id>
<phase>process-resources</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.eclipse.jetty.tests</groupId>
<artifactId>test-jaas-webapp</artifactId>
<version>${project.version}</version>
<classifier>config</classifier>
<type>jar</type>
<overWrite>true</overWrite>
<outputDirectory>${assembly-directory}</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
<execution>
<id>unpack-test-jndi-config</id>
<phase>process-resources</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.eclipse.jetty.tests</groupId>
<artifactId>test-jndi-webapp</artifactId>
<version>${project.version}</version>
<classifier>config</classifier>
<type>jar</type>
<overWrite>true</overWrite>
<outputDirectory>${assembly-directory}</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
<execution>
<id>unpack-test-spec-config</id>
<phase>process-resources</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.eclipse.jetty.tests</groupId>
<artifactId>test-spec-webapp</artifactId>
<version>${project.version}</version>
<classifier>config</classifier>
<type>jar</type>
<overWrite>true</overWrite>
<outputDirectory>${assembly-directory}</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
<execution>
<id>copy-lib-deps</id>
<phase>generate-resources</phase>

View File

@ -118,9 +118,9 @@ started()
for T in 1 2 3 4 5 6 7 9 10 11 12 13 14 15
do
sleep 4
[ -z "$(grep STARTED $1)" ] || return 0
[ -z "$(grep STOPPED $1)" ] || return 1
[ -z "$(grep FAILED $1)" ] || return 1
[ -z "$(grep STARTED $1 2>/dev/null)" ] || return 0
[ -z "$(grep STOPPED $1 2>/dev/null)" ] || return 1
[ -z "$(grep FAILED $1 2>/dev/null)" ] || return 1
local PID=$(cat "$2" 2>/dev/null) || return 1
kill -0 "$PID" 2>/dev/null || return 1
echo -n ". "
@ -338,13 +338,17 @@ then
fi
#####################################################
# Find a PID for the pid file
# Find a pid and state file
#####################################################
if [ -z "$JETTY_PID" ]
then
JETTY_PID="$JETTY_RUN/jetty.pid"
fi
JETTY_STATE=$(dirname $JETTY_PID)/jetty.state
if [ -z "$JETTY_STATE" ]
then
JETTY_STATE=$JETTY_HOME/jetty.state
fi
JAVA_OPTIONS+=("-Djetty.state=$JETTY_STATE")
rm -f $JETTY_STATE
@ -415,8 +419,8 @@ if (( DEBUG ))
then
echo "JETTY_HOME = $JETTY_HOME"
echo "JETTY_CONF = $JETTY_CONF"
echo "JETTY_RUN = $JETTY_RUN"
echo "JETTY_PID = $JETTY_PID"
echo "JETTY_START = $JETTY_START"
echo "JETTY_ARGS = $JETTY_ARGS"
echo "CONFIGS = ${CONFIGS[*]}"
echo "JAVA_OPTIONS = ${JAVA_OPTIONS[*]}"
@ -566,22 +570,21 @@ case "$ACTION" in
fi
exec "${RUN_CMD[@]}"
;;
check|status)
echo "Checking arguments to Jetty: "
echo "START_INI = $START_INI"
echo "JETTY_HOME = $JETTY_HOME"
echo "JETTY_CONF = $JETTY_CONF"
echo "JETTY_RUN = $JETTY_RUN"
echo "JETTY_PID = $JETTY_PID"
echo "JETTY_PORT = $JETTY_PORT"
echo "JETTY_START = $JETTY_START"
echo "JETTY_LOGS = $JETTY_LOGS"
echo "START_INI = $START_INI"
echo "CONFIGS = ${CONFIGS[*]}"
echo "JAVA_OPTIONS = ${JAVA_OPTIONS[*]}"
echo "JAVA = $JAVA"
echo "CLASSPATH = $CLASSPATH"
echo "JAVA = $JAVA"
echo "JAVA_OPTIONS = ${JAVA_OPTIONS[*]}"
echo "JETTY_ARGS = $JETTY_ARGS"
echo "RUN_CMD = ${RUN_CMD[*]}"
echo

View File

@ -0,0 +1,124 @@
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
<!-- =============================================================== -->
<!-- Configure the demos -->
<!-- =============================================================== -->
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<!-- ============================================================= -->
<!-- Add webapps.demo to deployment manager scans -->
<!-- ============================================================= -->
<Ref refid="DeploymentManager">
<Call id="webappprovider" name="addAppProvider">
<Arg>
<New class="org.eclipse.jetty.deploy.providers.WebAppProvider">
<Set name="monitoredDirName"><Property name="jetty.home" default="." />/webapps.demo</Set>
<Set name="defaultsDescriptor"><Property name="jetty.home" default="." />/etc/webdefault.xml</Set>
<Set name="scanInterval">1</Set>
<Set name="extractWars">true</Set>
<Set name="configurationManager">
<New class="org.eclipse.jetty.deploy.PropertiesConfigurationManager"/>
</Set>
</New>
</Arg>
</Call>
</Ref>
<!-- ============================================================= -->
<!-- Add rewrite rules -->
<!-- ============================================================= -->
<Ref refid="Rewrite">
<!-- Add rule to protect against IE ssl bug -->
<Call name="addRule">
<Arg>
<New class="org.eclipse.jetty.rewrite.handler.MsieSslRule"/>
</Arg>
</Call>
<!-- protect favicon handling -->
<Call name="addRule">
<Arg>
<New class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
<Set name="pattern">/favicon.ico</Set>
<Set name="name">Cache-Control</Set>
<Set name="value">Max-Age=3600,public</Set>
<Set name="terminating">true</Set>
</New>
</Arg>
</Call>
<!-- redirect from the welcome page to a specific page -->
<Call name="addRule">
<Arg>
<New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Set name="pattern">/test/rewrite/</Set>
<Set name="replacement">/test/rewrite/info.html</Set>
</New>
</Arg>
</Call>
<!-- replace the entire request URI -->
<Call name="addRule">
<Arg>
<New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Set name="pattern">/test/some/old/context</Set>
<Set name="replacement">/test/rewritten/newcontext</Set>
</New>
</Arg>
</Call>
<!-- replace the beginning of the request URI -->
<Call name="addRule">
<Arg>
<New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Set name="pattern">/test/rewrite/for/*</Set>
<Set name="replacement">/test/rewritten/</Set>
</New>
</Arg>
</Call>
<!-- reverse the order of the path sections -->
<Call name="addRule">
<Arg>
<New class="org.eclipse.jetty.rewrite.handler.RewriteRegexRule">
<Set name="regex">(.*?)/reverse/([^/]*)/(.*)</Set>
<Set name="replacement">$1/reverse/$3/$2</Set>
</New>
</Arg>
</Call>
<!-- add a cookie to each path visited -->
<Call name="addRule">
<Arg>
<New class="org.eclipse.jetty.rewrite.handler.CookiePatternRule">
<Set name="pattern">/*</Set>
<Set name="name">visited</Set>
<Set name="value">yes</Set>
</New>
</Arg>
</Call>
<!-- actual redirect, instead of internal rewrite -->
<Call name="addRule">
<Arg>
<New class="org.eclipse.jetty.rewrite.handler.RedirectPatternRule">
<Set name="pattern">/test/redirect/*</Set>
<Set name="location">/test/redirected</Set>
</New>
</Arg>
</Call>
<!-- add a response rule -->
<Call name="addRule">
<Arg>
<New class="org.eclipse.jetty.rewrite.handler.ResponsePatternRule">
<Set name="pattern">/400Error</Set>
<Set name="code">400</Set>
<Set name="reason">ResponsePatternRule Demo</Set>
</New>
</Arg>
</Call>
</Ref>
</Configure>

View File

@ -0,0 +1,53 @@
# ===========================================================
# Enable the demonstration web applications
#
# To disable the demos, either delete this file, move it out of
# the start.d directory or rename it to not end with ".ini"
# ===========================================================
# ===========================================================
# Enable rewrite handler
# -----------------------------------------------------------
OPTIONS=rewrite
etc/jetty-rewrite.xml
# ===========================================================
# Add a deploy app provider to scan the webapps.demo directory
# -----------------------------------------------------------
etc/jetty-demo.xml
# ===========================================================
# Enable the Jetty HTTP client APIs for use by demo webapps
# -----------------------------------------------------------
OPTIONS=client
# ===========================================================
# Enable the test-realm login service for use by authentication
# demonstrations
# -----------------------------------------------------------
etc/test-realm.xml
# ===========================================================
# Enable JAAS test webapp
# -----------------------------------------------------------
OPTIONS=jaas
jaas.login.conf=webapps.demo/test-jaas.d/login.conf
etc/jetty-jaas.xml
# ===========================================================
# Enable JNDI test webapp
# -----------------------------------------------------------
OPTIONS=jndi,jndi.demo
# ===========================================================
# Enable additional webapp environment configurators
# -----------------------------------------------------------
OPTIONS=plus
etc/jetty-plus.xml
# ===========================================================
# Enable servlet 3.1 annotations
# -----------------------------------------------------------
OPTIONS=annotations
etc/jetty-annotations.xml

View File

@ -1,5 +1,6 @@
#===========================================================
# Jetty start.jar arguments
#
# The contents of this file, together with the start.ini
# fragments found in start.d directory are used to build
# the classpath and command line on a call to
@ -21,50 +22,34 @@
# + A JVM option like: -Xmx2000m
# + A System Property like: -Dcom.sun.management.jmxremote
#
#-----------------------------------------------------------
#
# NOTE: The lines in this file may be uncommented to activate
# features. Alternately, the lines may be copied to a ini file
# in the start.d directory to enabled configuration without
# editing this file. See start.d/900-demo.ini for an example.
#
# Future releases will switch start.d style configuration for
# all features.
#===========================================================
#===========================================================
# The --exec option should be used if any of the JVM options
# in this file are uncommented (eg -D* or -X*). Because a
# JVM cannot change it's own options, the --exec flag causes
# start.jar to fork a new JVM with the requested arguments.
#
# Alternately, a command line may be generated by running
#
# java -jar start.jar --exec-print
#
# and the results executed to start the jetty server.
# For example --exec can be avoided if jetty is started on unix with
#
# eval $(java -jar start.jar --exec-print)
#
#-----------------------------------------------------------
# --exec
#===========================================================
#===========================================================
# Configure Properties.
# The properties defined here may be used by the
# <Property name="myproperty"/> element in the XML files
# passed to start.jar.
# Alternately a file ending with ".properties" can be
# added that will include multiple properties.
# Properties, unlike SystemProperties, do not need --exec
# to be specified.
#-----------------------------------------------------------
# jetty.home=.
# jetty.logs=./logs
# jetty.host=0.0.0.0
#===========================================================
#===========================================================
# Configure JVM arguments.
# Must be used with --exec or --exec-print
# If JVM args are include in an ini file then --exec is needed
# to start a new JVM from start.jar with the extra args.
# If you wish to avoid an extra JVM running, place JVM args
# on the normal command line and do not use --exec
#-----------------------------------------------------------
# -Dorg.apache.jasper.compiler.disablejsr199=true
# --exec
# -Xmx2000m
# -Xmn512m
# -XX:+UseConcMarkSweepGC
# -XX:ParallelCMSThreads=2
# -XX:+CMSClassUnloadingEnabled
# -XX:+UseCMSCompactAtFullCollection
# -XX:CMSInitiatingOccupancyFraction=80
# -verbose:gc
# -XX:+PrintGCDateStamps
# -XX:+PrintGCTimeStamps
@ -72,12 +57,10 @@
# -XX:+PrintTenuringDistribution
# -XX:+PrintCommandLineFlags
# -XX:+DisableExplicitGC
# -XX:+UseConcMarkSweepGC
# -XX:ParallelCMSThreads=2
# -XX:+CMSClassUnloadingEnabled
# -XX:+UseCMSCompactAtFullCollection
# -XX:CMSInitiatingOccupancyFraction=80
#===========================================================
# -Dorg.apache.jasper.compiler.disablejsr199=true
#===========================================================
# Default Server Options
@ -87,8 +70,50 @@
# Include the core jetty configuration file
#-----------------------------------------------------------
OPTIONS=Server,websocket,resources,ext
threads.min=10
threads.max=200
threads.timeout=60000
#jetty.host=myhost.com
jetty.dump.start=false
jetty.dump.stop=false
etc/jetty.xml
#===========================================================
# JMX Management
# To enable remote JMX access uncomment jmxremote and
# enable --exec
#-----------------------------------------------------------
OPTIONS=jmx
# jetty.jmxrmihost=localhost
# jetty.jmxrmiport=1099
# -Dcom.sun.management.jmxremote
etc/jetty-jmx.xml
#===========================================================
# Java Server Pages
#-----------------------------------------------------------
OPTIONS=jsp
#===========================================================
# Request logger
# Will add a handler to log all HTTP requests to a standard
# request log format file.
#-----------------------------------------------------------
# requestlog.retain=90
# requestlog.append=true
# requestlog.extended=true
# etc/jetty-requestlog.xml
#===========================================================
# stderr/stdout logging.
# The following configuration will redirect stderr and stdout
# to file which is rolled over daily.
#-----------------------------------------------------------
# jetty.log.retain=90
# etc/jetty-logging.xml
#===========================================================
# Enable SetUID
@ -96,59 +121,25 @@ etc/jetty.xml
# starting as root you must change the run privledged to true
#-----------------------------------------------------------
# OPTIONS=setuid
# etc/jetty-setuid.xml
# jetty.startServerAsPrivileged=false
# jetty.username=jetty
# jetty.groupname=jetty
# jetty.umask=002
#===========================================================
# etc/jetty-setuid.xml
#===========================================================
# Server logging.
# The following configuration will redirect stderr and stdout
# to file which is rolled over daily.
#-----------------------------------------------------------
# etc/jetty-logging.xml
#===========================================================
#===========================================================
# JMX Management
# To enable remote JMX access uncomment jmxremote and
# enable --exec or use --exec-print (see above)
#-----------------------------------------------------------
OPTIONS=jmx
# jetty.jmxrmihost=localhost
# jetty.jmxrmiport=1099
# -Dcom.sun.management.jmxremote
etc/jetty-jmx.xml
#===========================================================
#===========================================================
# Java Server Pages
#-----------------------------------------------------------
OPTIONS=jsp
#===========================================================
#===========================================================
# Annotations JNDI JAAS processing
#-----------------------------------------------------------
# OPTIONS=plus
# etc/jetty-plus.xml
# OPTIONS=annotations
# etc/jetty-annotations.xml
#===========================================================
#===========================================================
# HTTP Connector
#-----------------------------------------------------------
# jetty.port=8080
jetty.port=8080
http.timeout=30000
etc/jetty-http.xml
#===========================================================
#===========================================================
# SSL Context
# For use by HTTPS and SPDY
# Create the keystore and trust store for use by
# HTTPS and SPDY
#-----------------------------------------------------------
# jetty.keystore=etc/keystore
# jetty.keystore.password=OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4
@ -157,65 +148,90 @@ etc/jetty-http.xml
# jetty.truststore.password=OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4
# jetty.secure.port=8443
# etc/jetty-ssl.xml
#===========================================================
#===========================================================
# HTTPS Connector
# Must be used with 200-ssl.ini
#-----------------------------------------------------------
# jetty.https.port=8443
# etc/jetty-https.xml
#===========================================================
# NPN Next Protocol Negotiation
#
# The SPDY and HTTP/2.0 connectors require NPN. The jar for
# NPN cannot be downloaded from eclipse. So the --download
# option is used to install the NPN jar if it does not already
# exist
#
#-----------------------------------------------------------
# --exec
# --download=http://repo1.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.5.v20130313/npn-boot-1.1.5.v20130313.jar:lib/npn/npn-boot-1.1.5.v20130313.jar
# -Xbootclasspath/p:lib/npn/npn-boot-1.1.5.v20130313.jar
#===========================================================
# SPDY Connector
#
# SPDY requires the NPN jar which must be separately downloaded:
#
# http://repo1.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.5.v20130313/npn-boot-1.1.5.v20130313.jar
#
# Which should be saved in lib/npn-boot-1.1.5.v20130313.jar
#
# To include the NPN jar on the boot path, you must either:
#
# a) enable --exec above and uncomment the -Xbootclass line
# below
#
# b) Add -Xbootclasspath/p:lib/npn-boot-1.1.5.v20130313.jar
# to the command line when running jetty.
#
# Requires SSL Context and NPN from above
#-----------------------------------------------------------
# OPTIONS=spdy
# -Xbootclasspath/p:lib/npn-boot-1.1.5.v20130313.jar
# jetty.spdy.port=8443
# etc/jetty-spdy.xml
#===========================================================
#===========================================================
# Webapplication Deployer
#-----------------------------------------------------------
etc/jetty-deploy.xml
#===========================================================
# ===========================================================
# Enable JAAS
# -----------------------------------------------------------
# OPTIONS=jaas
# jaas.login.conf=etc/login.conf
# etc/jetty-jaas.xml
# ===========================================================
# Enable JNDI
# -----------------------------------------------------------
# OPTIONS=jndi
# ===========================================================
# Enable additional webapp environment configurators
# -----------------------------------------------------------
# OPTIONS=plus
# etc/jetty-plus.xml
# ===========================================================
# Enable servlet 3.1 annotations
# -----------------------------------------------------------
# OPTIONS=annotations
# etc/jetty-annotations.xml
#===========================================================
# Request logger
# Will add a handler to log all HTTP requests to a standard
# request log format file.
# Other server features
#-----------------------------------------------------------
etc/jetty-requestlog.xml
#===========================================================
#===========================================================
# Additional configurations
# See headers of individual files for explanations
#-----------------------------------------------------------
# etc/jetty-stats.xml
# etc/jetty-debug.xml
# etc/jetty-ipaccess.xml
# etc/jetty-lowresources.xml
#===========================================================
# etc/jetty-stats.xml
#===========================================================
# Lookup additional ini files in start.d
# Low resource managment
#-----------------------------------------------------------
start.d/
# lowresources.period=1050
# lowresources.lowResourcesIdleTimeout=200
# lowresources.monitorThreads=true
# lowresources.maxConnections=0
# lowresources.maxMemory=0
# lowresources.maxLowResourcesTime=5000
# etc/jetty-lowresources.xml
#===========================================================
# The start.d directory contains the active start.ini fragments
start.d/

View File

@ -0,0 +1,7 @@
This directory is scanned by the demo WebAppDeployer provider
created in the etc/jetty-demo.xml file and enabled by the
start.d/900-demo.ini file.
For normal deployment, use the webapps directory.

View File

@ -34,8 +34,9 @@
<ul>
<li><a href="/test/">Test Jetty Webapp</a></li>
<li><a href="/async-rest/">Async Rest</a></li>
<li><a href="/proxy/apidocs/">Transparent Proxy to Javadoc</a></li>
<li><a href="/proxy/xref/">Transparent Proxy to Xref</a></li>
<li><a href="/test-jaas/">JAAS Test</a></li>
<li><a href="/test-jndi/">JNDI Test</a></li>
<li><a href="/test-spec/">Servlet 3.1 Test</a></li>
<li><a href="/oldContextPath/">Redirected Context</a></li>
</ul>
</td>
@ -44,6 +45,8 @@
<ul>
<li><a href="http://www.eclipse.org/jetty/">Jetty @ Eclipse Home</a></li>
<li><a href="http://wiki.eclipse.org/Jetty">Jetty @ Eclipse Doco</a></li>
<li><a href="/proxy/apidocs/">Javadoc</a> (via transparent proxy)</li>
<li><a href="/proxy/xref/">Xref</a> (via transparent proxy)</li>
<li><a
href="http://docs.codehaus.org/display/JETTY/Jetty+Powered">Jetty Powered</a></li>
</ul>

View File

@ -33,7 +33,7 @@ import org.eclipse.jetty.util.Trie;
*/
public class HttpField
{
public final static Trie<HttpField> CACHE = new ArrayTrie<>(1024);
public final static Trie<HttpField> CACHE = new ArrayTrie<>(2048);
public final static Trie<HttpField> CONTENT_TYPE = new ArrayTrie<>(512);
static
@ -77,10 +77,7 @@ public class HttpField
}
// Add headers with null values so HttpParser can avoid looking up name again for unknown values
Set<HttpHeader> headers = new HashSet<>();
for (String key:CACHE.keySet())
headers.add(CACHE.get(key).getHeader());
for (HttpHeader h:headers)
for (HttpHeader h:HttpHeader.values())
if (!CACHE.put(new HttpField(h,(String)null)))
throw new IllegalStateException("CACHE FULL");
// Add some more common headers

View File

@ -110,7 +110,10 @@ public enum HttpMethod
{
if (buffer.hasArray())
return lookAheadGet(buffer.array(),buffer.arrayOffset()+buffer.position(),buffer.arrayOffset()+buffer.limit());
return CACHE.getBest(buffer,0,buffer.remaining());
// TODO use cache and check for space
// return CACHE.getBest(buffer,0,buffer.remaining());
return null;
}
/* ------------------------------------------------------------ */

View File

@ -18,7 +18,6 @@
package org.eclipse.jetty.http;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.eclipse.jetty.http.HttpTokens.EndOfContent;
@ -26,6 +25,7 @@ import org.eclipse.jetty.util.ArrayTernaryTrie;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.Trie;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -80,13 +80,13 @@ public class HttpParser
private String _methodString;
private HttpVersion _version;
private ByteBuffer _uri=ByteBuffer.allocate(INITIAL_URI_LENGTH); // Tune?
private byte _eol;
private EndOfContent _endOfContent;
private long _contentLength;
private long _contentPosition;
private int _chunkLength;
private int _chunkPosition;
private boolean _headResponse;
private boolean _cr;
private ByteBuffer _contentChunk;
private Trie<HttpField> _connectionFields;
@ -204,11 +204,81 @@ public class HttpParser
return _state == state;
}
/* ------------------------------------------------------------------------------- */
private byte next(ByteBuffer buffer)
{
byte ch=buffer.get();
// If not a special character
if (ch>=HttpTokens.SPACE || ch<0)
{
if (_cr)
{
badMessage(buffer,400,"Bad EOL");
return -1;
}
/*
if (ch>HttpTokens.SPACE)
System.err.println("Next "+(char)ch);
else
System.err.println("Next ["+ch+"]");*/
return ch;
}
// Only a LF acceptable after CR
if (_cr)
{
_cr=false;
if (ch==HttpTokens.LINE_FEED)
return ch;
badMessage(buffer,400,"Bad EOL");
return -1;
}
// If it is a CR
if (ch==HttpTokens.CARRIAGE_RETURN)
{
// Skip CR and look for a LF
if (buffer.hasRemaining())
{
if(_maxHeaderBytes>0 && _state.ordinal()<State.END.ordinal())
_headerBytes++;
ch=buffer.get();
if (ch==HttpTokens.LINE_FEED)
return ch;
badMessage(buffer,HttpStatus.BAD_REQUEST_400,null);
return -1;
}
// Defer lookup of LF
_cr=true;
return 0;
}
// Only LF or TAB acceptable special characters
if (ch!=HttpTokens.LINE_FEED && ch!=HttpTokens.TAB)
{
badMessage(buffer,HttpStatus.BAD_REQUEST_400,null);
return -1;
}
/*
if (ch>HttpTokens.SPACE)
System.err.println("Next "+(char)ch);
else
System.err.println("Next ["+ch+"]");
*/
return ch;
}
/* ------------------------------------------------------------------------------- */
/* Quick lookahead for the start state looking for a request method or a HTTP version,
* otherwise skip white space until something else to parse.
*/
private void quickStart(ByteBuffer buffer)
private boolean quickStart(ByteBuffer buffer)
{
// Quick start look
while (_state==State.START && buffer.hasRemaining())
@ -221,7 +291,7 @@ public class HttpParser
_methodString = _method.asString();
buffer.position(buffer.position()+_methodString.length()+1);
setState(State.SPACE1);
return;
return false;
}
}
else if (_responseHandler!=null)
@ -231,27 +301,23 @@ public class HttpParser
{
buffer.position(buffer.position()+_version.asString().length()+1);
setState(State.SPACE1);
return;
return false;
}
}
byte ch=buffer.get();
if (_eol == HttpTokens.CARRIAGE_RETURN && ch == HttpTokens.LINE_FEED)
{
_eol=HttpTokens.LINE_FEED;
continue;
}
_eol=0;
if (ch > HttpTokens.SPACE || ch<0)
byte ch=next(buffer);
if (ch > HttpTokens.SPACE)
{
_string.setLength(0);
_string.append((char)ch);
setState(_requestHandler!=null?State.METHOD:State.RESPONSE_VERSION);
return;
return false;
}
if (ch==-1)
return true;
}
return false;
}
private String takeString()
@ -281,7 +347,11 @@ public class HttpParser
while (_state.ordinal()<State.HEADER.ordinal() && buffer.hasRemaining() && !return_from_parse)
{
// process each character
byte ch=buffer.get();
byte ch=next(buffer);
if (ch==-1)
return true;
if (ch==0)
continue;
if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
{
@ -301,13 +371,6 @@ public class HttpParser
return true;
}
if (_eol == HttpTokens.CARRIAGE_RETURN && ch == HttpTokens.LINE_FEED)
{
_eol=HttpTokens.LINE_FEED;
continue;
}
_eol=0;
switch (_state)
{
case METHOD:
@ -413,7 +476,6 @@ public class HttpParser
else if (ch < HttpTokens.SPACE && ch>=0)
{
return_from_parse|=_responseHandler.startResponse(_version, _responseStatus, null);
_eol=ch;
setState(State.HEADER);
}
else
@ -464,28 +526,39 @@ public class HttpParser
{
setState(State.REQUEST_VERSION);
// try quick look ahead
// try quick look ahead for HTTP Version
if (buffer.position()>0 && buffer.hasArray())
{
_version=HttpVersion.lookAheadGet(buffer.array(),buffer.arrayOffset()+buffer.position()-1,buffer.arrayOffset()+buffer.limit());
if (_version!=null)
HttpVersion version=HttpVersion.lookAheadGet(buffer.array(),buffer.arrayOffset()+buffer.position()-1,buffer.arrayOffset()+buffer.limit());
if (version!=null)
{
_string.setLength(0);
buffer.position(buffer.position()+_version.asString().length()-1);
_eol=buffer.get();
setState(State.HEADER);
_uri.flip();
return_from_parse|=_requestHandler.startRequest(_method,_methodString,_uri, _version);
int pos = buffer.position()+version.asString().length()-1;
if (pos<buffer.limit())
{
byte n=buffer.get(pos);
if (n==HttpTokens.CARRIAGE_RETURN)
{
_cr=true;
_version=version;
_string.setLength(0);
buffer.position(pos+1);
}
else if (n==HttpTokens.LINE_FEED)
{
_version=version;
_string.setLength(0);
buffer.position(pos);
}
}
}
}
}
}
else if (ch < HttpTokens.SPACE)
else if (ch == HttpTokens.LINE_FEED)
{
if (_responseHandler!=null)
{
return_from_parse|=_responseHandler.startResponse(_version, _responseStatus, null);
_eol=ch;
setState(State.HEADER);
}
else
@ -502,10 +575,10 @@ public class HttpParser
break;
case REQUEST_VERSION:
if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
if (ch == HttpTokens.LINE_FEED)
{
String version = takeString();
_version=HttpVersion.CACHE.get(version);
if (_version==null)
_version=HttpVersion.CACHE.get(takeString());
if (_version==null)
{
badMessage(buffer,HttpStatus.BAD_REQUEST_400,"Unknown Version");
@ -513,14 +586,13 @@ public class HttpParser
}
// Should we try to cache header fields?
if (_version.getVersion()>=HttpVersion.HTTP_1_1.getVersion())
if (_connectionFields==null && _version.getVersion()>=HttpVersion.HTTP_1_1.getVersion())
{
int header_cache = _handler.getHeaderCacheSize();
if (header_cache>0)
_connectionFields=new ArrayTernaryTrie<>(header_cache);
}
_eol=ch;
setState(State.HEADER);
_uri.flip();
return_from_parse|=_requestHandler.startRequest(_method,_methodString,_uri, _version);
@ -532,11 +604,10 @@ public class HttpParser
break;
case REASON:
if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
if (ch == HttpTokens.LINE_FEED)
{
String reason=takeLengthString();
_eol=ch;
setState(State.HEADER);
return_from_parse|=_responseHandler.startResponse(_version, _responseStatus, reason);
continue;
@ -609,7 +680,8 @@ public class HttpParser
return true;
}
loop: for (int i = host.length(); i-- > 0;)
int len=host.length();
loop: for (int i = len; i-- > 0;)
{
char c2 = (char)(0xff & host.charAt(i));
switch (c2)
@ -620,6 +692,7 @@ public class HttpParser
case ':':
try
{
len=i;
port = StringUtil.toInt(host.substring(i+1));
}
catch (NumberFormatException e)
@ -628,10 +701,21 @@ public class HttpParser
badMessage(buffer,HttpStatus.BAD_REQUEST_400,"Bad Host header");
return true;
}
host = host.substring(0,i);
break loop;
}
}
if (host.charAt(0)=='[')
{
if (host.charAt(len-1)!=']')
{
badMessage(buffer,HttpStatus.BAD_REQUEST_400,"Bad IPv6 Host header");
return true;
}
host = host.substring(1,len-1);
}
else if (len!=host.length())
host = host.substring(0,len);
if (_requestHandler!=null)
_requestHandler.parsedHostHeader(host,port);
@ -652,6 +736,9 @@ public class HttpParser
case CACHE_CONTROL:
case USER_AGENT:
add_to_connection_trie=_connectionFields!=null && _field==null;
break;
default: break;
}
if (add_to_connection_trie && !_connectionFields.isFull() && _header!=null && _valueString!=null)
@ -676,7 +763,12 @@ public class HttpParser
while (_state.ordinal()<State.END.ordinal() && buffer.hasRemaining() && !return_from_parse)
{
// process each character
byte ch=buffer.get();
byte ch=next(buffer);
if (ch==-1)
return true;
if (ch==0)
continue;
if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
{
LOG.warn("Header is too large >"+_maxHeaderBytes);
@ -684,13 +776,6 @@ public class HttpParser
return true;
}
if (_eol == HttpTokens.CARRIAGE_RETURN && ch == HttpTokens.LINE_FEED)
{
_eol=HttpTokens.LINE_FEED;
continue;
}
_eol=0;
switch (_state)
{
case HEADER:
@ -735,10 +820,8 @@ public class HttpParser
_field=null;
// now handle the ch
if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
if (ch == HttpTokens.LINE_FEED)
{
consumeCRLF(ch,buffer);
_contentPosition=0;
// End of headers!
@ -796,44 +879,53 @@ public class HttpParser
}
else
{
if (buffer.remaining()>6)
if (buffer.hasRemaining())
{
// Try a look ahead for the known header name and value.
_field=_connectionFields==null?null:_connectionFields.getBest(buffer,-1,buffer.remaining());
if (_field==null)
_field=HttpField.CACHE.getBest(buffer,-1,buffer.remaining());
HttpField field=_connectionFields==null?null:_connectionFields.getBest(buffer,-1,buffer.remaining());
if (field==null)
field=HttpField.CACHE.getBest(buffer,-1,buffer.remaining());
if (_field!=null)
if (field!=null)
{
_header=_field.getHeader();
_headerString=_field.getName();
_valueString=_field.getValue();
if (_valueString==null)
String n=field.getName();
String v=field.getValue();
if (v==null)
{
// Header only
_header=field.getHeader();
_headerString=n;
setState(State.HEADER_VALUE);
buffer.position(buffer.position()+_headerString.length()+1);
_string.setLength(0);
_length=0;
_field=null;
buffer.position(buffer.position()+n.length()+1);
break;
}
else
{
setState(State.HEADER_IN_VALUE);
buffer.position(buffer.position()+_headerString.length()+_valueString.length()+1);
}
break;
}
// Header and value
int pos=buffer.position()+n.length()+v.length()+1;
byte b=buffer.get(pos);
// Try a look ahead for the known header name.
_header=HttpHeader.CACHE.getBest(buffer,-1,buffer.remaining());
//_header=HttpHeader.CACHE.getBest(buffer.array(),buffer.arrayOffset()+buffer.position()-1,buffer.remaining()+1);
if (_header!=null)
{
_headerString=_header.asString();
_string.setLength(0);
setState(State.HEADER_IN_NAME);
buffer.position(buffer.position()+_headerString.length()-1);
break;
if (b==HttpTokens.CARRIAGE_RETURN || b==HttpTokens.LINE_FEED)
{
_field=field;
_header=_field.getHeader();
_headerString=n;
_valueString=v;
setState(State.HEADER_IN_VALUE);
if (b==HttpTokens.CARRIAGE_RETURN)
{
_cr=true;
buffer.position(pos+1);
}
else
buffer.position(pos);
break;
}
}
}
}
@ -851,9 +943,7 @@ public class HttpParser
case HEADER_NAME:
switch(ch)
{
case HttpTokens.CARRIAGE_RETURN:
case HttpTokens.LINE_FEED:
consumeCRLF(ch,buffer);
if (_headerString==null)
{
_headerString=takeLengthString();
@ -888,9 +978,7 @@ public class HttpParser
case HEADER_IN_NAME:
switch(ch)
{
case HttpTokens.CARRIAGE_RETURN:
case HttpTokens.LINE_FEED:
consumeCRLF(ch,buffer);
_headerString=takeString();
_length=-1;
_header=HttpHeader.CACHE.get(_headerString);
@ -936,9 +1024,7 @@ public class HttpParser
case HEADER_VALUE:
switch(ch)
{
case HttpTokens.CARRIAGE_RETURN:
case HttpTokens.LINE_FEED:
consumeCRLF(ch,buffer);
if (_length > 0)
{
if (_valueString!=null)
@ -975,9 +1061,7 @@ public class HttpParser
case HEADER_IN_VALUE:
switch(ch)
{
case HttpTokens.CARRIAGE_RETURN:
case HttpTokens.LINE_FEED:
consumeCRLF(ch,buffer);
if (_length > 0)
{
if (HttpHeaderValue.hasKnownValues(_header))
@ -1030,17 +1114,6 @@ public class HttpParser
return return_from_parse;
}
/* ------------------------------------------------------------------------------- */
private void consumeCRLF(byte ch, ByteBuffer buffer)
{
_eol=ch;
if (_eol==HttpTokens.CARRIAGE_RETURN && buffer.hasRemaining() && buffer.get(buffer.position())==HttpTokens.LINE_FEED)
{
buffer.get();
_eol=0;
}
}
/* ------------------------------------------------------------------------------- */
/**
* Parse until next Event.
@ -1059,7 +1132,8 @@ public class HttpParser
_methodString=null;
_endOfContent=EndOfContent.UNKNOWN_CONTENT;
_header=null;
quickStart(buffer);
if(quickStart(buffer))
return true;
break;
case CONTENT:
@ -1077,18 +1151,18 @@ public class HttpParser
case CLOSED:
if (BufferUtil.hasContent(buffer))
{
int len=buffer.remaining();
_headerBytes+=len;
// Just ignore data when closed
_headerBytes+=buffer.remaining();
BufferUtil.clear(buffer);
if (_headerBytes>_maxHeaderBytes)
{
Thread.sleep(100);
String chars = BufferUtil.toDetailString(buffer);
BufferUtil.clear(buffer);
throw new IllegalStateException(String.format("%s %d/%d>%d data when CLOSED:%s",this,len,_headerBytes,_maxHeaderBytes,chars));
// Don't want to waste time reading data of a closed request
throw new IllegalStateException("too much data after closed");
}
BufferUtil.clear(buffer);
}
return false;
default: break;
}
// Request/response line
@ -1113,13 +1187,6 @@ public class HttpParser
byte ch;
while (_state.ordinal() > State.END.ordinal() && buffer.hasRemaining())
{
if (_eol == HttpTokens.CARRIAGE_RETURN && buffer.get(buffer.position()) == HttpTokens.LINE_FEED)
{
_eol=buffer.get();
continue;
}
_eol=0;
switch (_state)
{
case EOF_CONTENT:
@ -1169,31 +1236,24 @@ public class HttpParser
case CHUNKED_CONTENT:
{
ch=buffer.get(buffer.position());
if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
_eol=buffer.get();
else if (ch <= HttpTokens.SPACE)
buffer.get();
else
ch=next(buffer);
if (ch>HttpTokens.SPACE)
{
_chunkLength=0;
_chunkLength=TypeUtil.convertHexDigit(ch);
_chunkPosition=0;
setState(State.CHUNK_SIZE);
}
break;
}
case CHUNK_SIZE:
{
ch=buffer.get();
if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
ch=next(buffer);
if (ch == HttpTokens.LINE_FEED)
{
_eol=ch;
if (_chunkLength == 0)
{
if (_eol==HttpTokens.CARRIAGE_RETURN && buffer.hasRemaining() && buffer.get(buffer.position())==HttpTokens.LINE_FEED)
_eol=buffer.get();
setState(State.END);
if (_handler.messageComplete())
return true;
@ -1203,27 +1263,18 @@ public class HttpParser
}
else if (ch <= HttpTokens.SPACE || ch == HttpTokens.SEMI_COLON)
setState(State.CHUNK_PARAMS);
else if (ch >= '0' && ch <= '9')
_chunkLength=_chunkLength * 16 + (ch - '0');
else if (ch >= 'a' && ch <= 'f')
_chunkLength=_chunkLength * 16 + (10 + ch - 'a');
else if (ch >= 'A' && ch <= 'F')
_chunkLength=_chunkLength * 16 + (10 + ch - 'A');
else
throw new IOException("bad chunk char: " + ch);
else
_chunkLength=_chunkLength * 16 + TypeUtil.convertHexDigit(ch);
break;
}
case CHUNK_PARAMS:
{
ch=buffer.get();
if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
ch=next(buffer);
if (ch == HttpTokens.LINE_FEED)
{
_eol=ch;
if (_chunkLength == 0)
{
if (_eol==HttpTokens.CARRIAGE_RETURN && buffer.hasRemaining() && buffer.get(buffer.position())==HttpTokens.LINE_FEED)
_eol=buffer.get();
setState(State.END);
if (_handler.messageComplete())
return true;
@ -1262,6 +1313,9 @@ public class HttpParser
BufferUtil.clear(buffer);
return false;
}
default:
break;
}
}
@ -1340,8 +1394,19 @@ public class HttpParser
case CLOSED:
case END:
break;
case EOF_CONTENT:
_handler.messageComplete();
break;
default:
LOG.warn("Closing {}",this);
if (_state.ordinal()>State.END.ordinal())
{
_handler.earlyEOF();
_handler.messageComplete();
}
else
LOG.warn("Closing {}",this);
}
setState(State.CLOSED);
_endOfContent=EndOfContent.UNKNOWN_CONTENT;
@ -1369,6 +1434,7 @@ public class HttpParser
/* ------------------------------------------------------------------------------- */
private void setState(State state)
{
// LOG.debug("{} --> {}",_state,state);
_state=state;
}
@ -1405,8 +1471,18 @@ public class HttpParser
*/
public boolean parsedHeader(HttpField field);
public boolean earlyEOF();
/* ------------------------------------------------------------ */
/** Called to signal that an EOF was received unexpectedly
* during the parsing of a HTTP message
* @return True if the parser should return to its caller
*/
public void earlyEOF();
/* ------------------------------------------------------------ */
/** Called to signal that a bad HTTP message has been received.
* @param status The bad status to send
* @param reason The textual reason for badness
*/
public void badMessage(int status, String reason);
/* ------------------------------------------------------------ */
@ -1443,5 +1519,9 @@ public class HttpParser
public abstract boolean startResponse(HttpVersion version, int status, String reason);
}
public Trie<HttpField> getFieldCache()
{
return _connectionFields;
}
}

View File

@ -138,9 +138,8 @@ public class HttpTester
}
@Override
public boolean earlyEOF()
public void earlyEOF()
{
return true;
}
@Override

View File

@ -25,13 +25,12 @@ public interface HttpTokens
{
// Terminal symbols.
static final byte COLON= (byte)':';
static final byte SPACE= 0x20;
static final byte CARRIAGE_RETURN= 0x0D;
static final byte TAB= 0x09;
static final byte LINE_FEED= 0x0A;
static final byte CARRIAGE_RETURN= 0x0D;
static final byte SPACE= 0x20;
static final byte[] CRLF = {CARRIAGE_RETURN,LINE_FEED};
static final byte SEMI_COLON= (byte)';';
static final byte TAB= 0x09;
public enum EndOfContent { UNKNOWN_CONTENT,NO_CONTENT,EOF_CONTENT,CONTENT_LENGTH,CHUNKED_CONTENT,SELF_DEFINING_CONTENT }

View File

@ -546,6 +546,8 @@ public class HttpURI
{
if (_host==_port)
return null;
if (_raw[_host]=='[')
return new String(_raw,_host+1,_port-_host-2,_charset);
return new String(_raw,_host,_port-_host,_charset);
}

View File

@ -51,9 +51,8 @@ public class HttpGeneratorServerTest
}
@Override
public boolean earlyEOF()
public void earlyEOF()
{
return true;
}
@Override

View File

@ -23,6 +23,8 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jetty.http.HttpParser.State;
import org.eclipse.jetty.util.BufferUtil;
@ -50,11 +52,13 @@ public class HttpParserTest
throw new IllegalStateException("!START");
// continue parsing
while (!parser.isState(State.END) && buffer.hasRemaining())
int remaining=buffer.remaining();
while (!parser.isState(State.END) && remaining>0)
{
int remaining=buffer.remaining();
int was_remaining=remaining;
parser.parseNext(buffer);
if (remaining==buffer.remaining())
remaining=buffer.remaining();
if (remaining==was_remaining)
break;
}
}
@ -64,8 +68,8 @@ public class HttpParserTest
{
ByteBuffer buffer= BufferUtil.toBuffer("POST /foo HTTP/1.0\015\012" + "\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
assertEquals("POST", _methodOrVersion);
assertEquals("/foo", _uriOrStatus);
@ -79,8 +83,8 @@ public class HttpParserTest
ByteBuffer buffer= BufferUtil.toBuffer("GET /999\015\012");
_versionOrReason= null;
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
assertEquals("GET", _methodOrVersion);
assertEquals("/999", _uriOrStatus);
@ -94,8 +98,8 @@ public class HttpParserTest
ByteBuffer buffer= BufferUtil.toBuffer("POST /222 \015\012");
_versionOrReason= null;
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
assertEquals("POST", _methodOrVersion);
assertEquals("/222", _uriOrStatus);
@ -108,8 +112,8 @@ public class HttpParserTest
{
ByteBuffer buffer= BufferUtil.toBuffer("POST /fo\u0690 HTTP/1.0\015\012" + "\015\012",StringUtil.__UTF8_CHARSET);
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
assertEquals("POST", _methodOrVersion);
assertEquals("/fo\u0690", _uriOrStatus);
@ -122,8 +126,8 @@ public class HttpParserTest
{
ByteBuffer buffer= BufferUtil.toBuffer("POST /foo?param=\u0690 HTTP/1.0\015\012" + "\015\012",StringUtil.__UTF8_CHARSET);
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
assertEquals("POST", _methodOrVersion);
assertEquals("/foo?param=\u0690", _uriOrStatus);
@ -136,8 +140,8 @@ public class HttpParserTest
{
ByteBuffer buffer= BufferUtil.toBuffer("POST /123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/ HTTP/1.0\015\012" + "\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
assertEquals("POST", _methodOrVersion);
assertEquals("/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/123456789abcdef/", _uriOrStatus);
@ -149,10 +153,9 @@ public class HttpParserTest
public void testConnect() throws Exception
{
ByteBuffer buffer= BufferUtil.toBuffer("CONNECT 192.168.1.2:80 HTTP/1.1\015\012" + "\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
assertTrue(handler.request);
assertEquals("CONNECT", _methodOrVersion);
assertEquals("192.168.1.2:80", _uriOrStatus);
assertEquals("HTTP/1.1", _versionOrReason);
@ -182,8 +185,8 @@ public class HttpParserTest
BufferUtil.put(b0,buffer);
BufferUtil.flipToFlush(buffer,pos);
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
assertEquals("GET", _methodOrVersion);
@ -230,8 +233,8 @@ public class HttpParserTest
"Accept-Encoding: gzip, deflated\015\012" +
"Accept: unknown\015\012" +
"\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
assertEquals("GET", _methodOrVersion);
@ -260,6 +263,8 @@ public class HttpParserTest
assertEquals(9, _h);
}
@Test
public void testHeaderParseLF() throws Exception
{
@ -278,8 +283,8 @@ public class HttpParserTest
"Accept-Encoding: gzip, deflated\n" +
"Accept: unknown\n" +
"\n");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
assertEquals("GET", _methodOrVersion);
@ -328,9 +333,10 @@ public class HttpParserTest
for (int i=0;i<buffer.capacity()-4;i++)
{
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
// System.err.println(BufferUtil.toDetailString(buffer));
buffer.position(2);
buffer.limit(2+i);
@ -377,8 +383,8 @@ public class HttpParserTest
+ "1a\015\012"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ\015\012"
+ "0\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
assertEquals("GET", _methodOrVersion);
@ -421,8 +427,8 @@ public class HttpParserTest
+ "0123456789\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("GET", _methodOrVersion);
assertEquals("/mp", _uriOrStatus);
@ -466,8 +472,8 @@ public class HttpParserTest
+ "\015\012"
+ "0123456789\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.ResponseHandler)handler);
HttpParser.ResponseHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("HTTP/1.1", _methodOrVersion);
assertEquals("200", _uriOrStatus);
@ -485,8 +491,8 @@ public class HttpParserTest
+ "Connection: close\015\012"
+ "\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.ResponseHandler)handler);
HttpParser.ResponseHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("HTTP/1.1", _methodOrVersion);
assertEquals("304", _uriOrStatus);
@ -509,8 +515,8 @@ public class HttpParserTest
+ "\015\012"
+ "0123456789\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.ResponseHandler)handler);
HttpParser.ResponseHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("HTTP/1.1", _methodOrVersion);
assertEquals("204", _uriOrStatus);
@ -542,8 +548,8 @@ public class HttpParserTest
+ "\015\012"
+ "0123456789\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.ResponseHandler)handler);
HttpParser.ResponseHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("HTTP/1.1", _methodOrVersion);
assertEquals("200", _uriOrStatus);
@ -563,8 +569,8 @@ public class HttpParserTest
+ "\015\012"
+ "0123456789\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.ResponseHandler)handler);
HttpParser.ResponseHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("HTTP/1.1", _methodOrVersion);
assertEquals("200", _uriOrStatus);
@ -582,8 +588,8 @@ public class HttpParserTest
+ "Content-Length: 10\015\012"
+ "\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.ResponseHandler)handler);
HttpParser.ResponseHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("HTTP/1.1", _methodOrVersion);
assertEquals("304", _uriOrStatus);
@ -601,8 +607,8 @@ public class HttpParserTest
+ "Transfer-Encoding: chunked\015\012"
+ "\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.ResponseHandler)handler);
HttpParser.ResponseHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("HTTP/1.1", _methodOrVersion);
assertEquals("101", _uriOrStatus);
@ -624,8 +630,8 @@ public class HttpParserTest
+ "HTTP/1.1 400 OK\015\012"); // extra data causes close
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.ResponseHandler)handler);
HttpParser.ResponseHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("HTTP/1.1", _methodOrVersion);
@ -647,8 +653,8 @@ public class HttpParserTest
+ "Connection: close\015\012"
+ "\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals(null,_methodOrVersion);
@ -667,8 +673,8 @@ public class HttpParserTest
+ "Connection: close\015\012"
+ "\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals(null,_methodOrVersion);
@ -686,8 +692,8 @@ public class HttpParserTest
+ "Connection: close\015\012"
+ "\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.ResponseHandler)handler);
HttpParser.ResponseHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals(null,_methodOrVersion);
@ -705,8 +711,8 @@ public class HttpParserTest
+ "Connection: close\015\012"
+ "\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.ResponseHandler)handler);
HttpParser.ResponseHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals(null,_methodOrVersion);
@ -724,9 +730,9 @@ public class HttpParserTest
+ "Connection: close\015\012"
+ "\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.ResponseHandler)handler);
HttpParser.ResponseHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals(null,_methodOrVersion);
assertEquals("No Status",_bad);
@ -743,8 +749,23 @@ public class HttpParserTest
+ "Connection: close\015\012"
+ "\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.ResponseHandler)handler);
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals(null,_methodOrVersion);
assertEquals("Unknown Version",_bad);
assertFalse(buffer.hasRemaining());
assertEquals(HttpParser.State.CLOSED,parser.getState());
buffer= BufferUtil.toBuffer(
"GET / HTTP/1.01\015\012"
+ "Content-Length: 0\015\012"
+ "Connection: close\015\012"
+ "\015\012");
handler = new Handler();handler = new Handler();
parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals(null,_methodOrVersion);
@ -752,6 +773,42 @@ public class HttpParserTest
assertFalse(buffer.hasRemaining());
assertEquals(HttpParser.State.CLOSED,parser.getState());
}
@Test
public void testBadCR() throws Exception
{
ByteBuffer buffer= BufferUtil.toBuffer(
"GET / HTTP/1.0\r\n"
+ "Content-Length: 0\r"
+ "Connection: close\r"
+ "\r");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("Bad EOL",_bad);
assertFalse(buffer.hasRemaining());
assertEquals(HttpParser.State.CLOSED,parser.getState());
buffer= BufferUtil.toBuffer(
"GET / HTTP/1.0\r"
+ "Content-Length: 0\r"
+ "Connection: close\r"
+ "\r");
handler = new Handler();
parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("Bad EOL",_bad);
assertFalse(buffer.hasRemaining());
assertEquals(HttpParser.State.CLOSED,parser.getState());
}
@Test
public void testBadContentLength0() throws Exception
@ -762,8 +819,8 @@ public class HttpParserTest
+ "Connection: close\015\012"
+ "\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("GET",_methodOrVersion);
@ -781,8 +838,8 @@ public class HttpParserTest
+ "Connection: close\015\012"
+ "\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("GET",_methodOrVersion);
@ -800,8 +857,8 @@ public class HttpParserTest
+ "Connection: close\015\012"
+ "\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("GET",_methodOrVersion);
@ -809,7 +866,154 @@ public class HttpParserTest
assertFalse(buffer.hasRemaining());
assertEquals(HttpParser.State.CLOSED,parser.getState());
}
@Test
public void testHost() throws Exception
{
ByteBuffer buffer= BufferUtil.toBuffer(
"GET / HTTP/1.1\015\012"
+ "Host: host\015\012"
+ "Connection: close\015\012"
+ "\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("host",_host);
assertEquals(0,_port);
}
@Test
public void testIPHost() throws Exception
{
ByteBuffer buffer= BufferUtil.toBuffer(
"GET / HTTP/1.1\015\012"
+ "Host: 192.168.0.1\015\012"
+ "Connection: close\015\012"
+ "\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("192.168.0.1",_host);
assertEquals(0,_port);
}
@Test
public void testIPv6Host() throws Exception
{
ByteBuffer buffer= BufferUtil.toBuffer(
"GET / HTTP/1.1\015\012"
+ "Host: [::1]\015\012"
+ "Connection: close\015\012"
+ "\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("::1",_host);
assertEquals(0,_port);
}
@Test
public void testBadIPv6Host() throws Exception
{
ByteBuffer buffer= BufferUtil.toBuffer(
"GET / HTTP/1.1\015\012"
+ "Host: [::1\015\012"
+ "Connection: close\015\012"
+ "\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("Bad IPv6 Host header",_bad);
}
@Test
public void testHostPort() throws Exception
{
ByteBuffer buffer= BufferUtil.toBuffer(
"GET / HTTP/1.1\015\012"
+ "Host: myhost:8888\015\012"
+ "Connection: close\015\012"
+ "\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("myhost",_host);
assertEquals(8888,_port);
}
@Test
public void testHostBadPort() throws Exception
{
ByteBuffer buffer= BufferUtil.toBuffer(
"GET / HTTP/1.1\015\012"
+ "Host: myhost:xxx\015\012"
+ "Connection: close\015\012"
+ "\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("Bad Host header",_bad);
}
@Test
public void testIPHostPort() throws Exception
{
ByteBuffer buffer= BufferUtil.toBuffer(
"GET / HTTP/1.1\015\012"
+ "Host: 192.168.0.1:8888\015\012"
+ "Connection: close\015\012"
+ "\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("192.168.0.1",_host);
assertEquals(8888,_port);
}
@Test
public void testIPv6HostPort() throws Exception
{
ByteBuffer buffer= BufferUtil.toBuffer(
"GET / HTTP/1.1\015\012"
+ "Host: [::1]:8888\015\012"
+ "Connection: close\015\012"
+ "\015\012");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
assertEquals("::1",_host);
assertEquals(8888,_port);
}
@Test
public void testCachedField() throws Exception
{
ByteBuffer buffer= BufferUtil.toBuffer(
"GET / HTTP/1.1\r\n"+
"Host: www.smh.com.au\r\n"+
"\r\n");
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
assertEquals("www.smh.com.au",parser.getFieldCache().get("Host: www.smh.com.au").getValue());
HttpField field=_fields.get(0);
//System.err.println(parser.getFieldCache());
buffer.position(0);
parseAll(parser,buffer);
assertTrue(field==_fields.get(0));
}
@Before
public void init()
@ -826,11 +1030,14 @@ public class HttpParserTest
_messageCompleted=false;
}
private String _host;
private int _port;
private String _bad;
private String _content;
private String _methodOrVersion;
private String _uriOrStatus;
private String _versionOrReason;
private List<HttpField> _fields=new ArrayList<>();
private String[] _hdr;
private String[] _val;
private int _h;
@ -858,6 +1065,7 @@ public class HttpParserTest
@Override
public boolean startRequest(HttpMethod httpMethod, String method, ByteBuffer uri, HttpVersion version)
{
_fields.clear();
request=true;
_h= -1;
_hdr= new String[10];
@ -875,6 +1083,7 @@ public class HttpParserTest
@Override
public boolean parsedHeader(HttpField field)
{
_fields.add(field);
//System.err.println("header "+name+": "+value);
_hdr[++_h]= field.getName();
_val[_h]= field.getValue();
@ -884,7 +1093,8 @@ public class HttpParserTest
@Override
public boolean parsedHostHeader(String host,int port)
{
// TODO test this
_host=host;
_port=port;
return false;
}
@ -921,6 +1131,7 @@ public class HttpParserTest
@Override
public boolean startResponse(HttpVersion version, int status, String reason)
{
_fields.clear();
request=false;
_methodOrVersion = version.asString();
_uriOrStatus = Integer.toString(status);
@ -936,9 +1147,8 @@ public class HttpParserTest
}
@Override
public boolean earlyEOF()
public void earlyEOF()
{
return true;
}
@Override

View File

@ -29,39 +29,51 @@ import org.junit.Test;
/* ------------------------------------------------------------ */
public class HttpURITest
{
public static final String __input = "http://example.com:8080/path/to/context?parameter=%22value%22#fragment";
public static final String __scheme = "http";
public static final String __host = "example.com";
public static final int __port = 8080;
public static final String __path = "/path/to/context";
public static final String __query = "parameter=%22value%22";
public static final String __fragment = "fragment";
String[][] tests=
{
{"/path/to/context",null,null,"-1","/path/to/context",null,null,null},
{"http://example.com/path/to/context;param?query=%22value%22#fragment","http","example.com","-1","/path/to/context","param","query=%22value%22","fragment"},
{"http://[::1]/path/to/context;param?query=%22value%22#fragment","http","::1","-1","/path/to/context","param","query=%22value%22","fragment"},
{"http://example.com:8080/path/to/context;param?query=%22value%22#fragment","http","example.com","8080","/path/to/context","param","query=%22value%22","fragment"},
{"http://[::1]:8080/path/to/context;param?query=%22value%22#fragment","http","::1","8080","/path/to/context","param","query=%22value%22","fragment"},
};
public static int
INPUT=0,SCHEME=1,HOST=2,PORT=3,PATH=4,PARAM=5,QUERY=6,FRAGMENT=7;
/* ------------------------------------------------------------ */
@Test
public void testFromString() throws Exception
{
HttpURI uri = new HttpURI(__input);
for (String[] test:tests)
{
HttpURI uri = new HttpURI(test[INPUT]);
assertEquals(__scheme, uri.getScheme());
assertEquals(__host,uri.getHost());
assertEquals(__port,uri.getPort());
assertEquals(__path,uri.getPath());
assertEquals(__query,uri.getQuery());
assertEquals(__fragment,uri.getFragment());
assertEquals(test[SCHEME], uri.getScheme());
assertEquals(test[HOST], uri.getHost());
assertEquals(Integer.parseInt(test[PORT]), uri.getPort());
assertEquals(test[PATH], uri.getPath());
assertEquals(test[PARAM], uri.getParam());
assertEquals(test[QUERY], uri.getQuery());
assertEquals(test[FRAGMENT], uri.getFragment());
}
}
/* ------------------------------------------------------------ */
@Test
public void testFromURI() throws Exception
{
HttpURI uri = new HttpURI(new URI(__input));
for (String[] test:tests)
{
HttpURI uri = new HttpURI(new URI(test[INPUT]));
assertEquals(__scheme, uri.getScheme());
assertEquals(__host,uri.getHost());
assertEquals(__port,uri.getPort());
assertEquals(__path,uri.getPath());
assertEquals(__query,uri.getQuery());
assertEquals(__fragment,uri.getFragment());
assertEquals(test[SCHEME], uri.getScheme());
assertEquals(test[HOST], uri.getHost());
assertEquals(Integer.parseInt(test[PORT]), uri.getPort());
assertEquals(test[PATH], uri.getPath());
assertEquals(test[PARAM], uri.getParam());
assertEquals(test[QUERY], uri.getQuery());
assertEquals(test[FRAGMENT], uri.getFragment());
}
}
}

View File

@ -24,6 +24,7 @@ import java.util.concurrent.Executor;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.util.BlockingCallback;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -112,13 +113,130 @@ public abstract class AbstractConnection implements Connection
if (_state.compareAndSet(State.FILLING,State.FILLING_INTERESTED))
break loop;
break;
case FILLING_BLOCKED:
if (_state.compareAndSet(State.FILLING_BLOCKED,State.FILLING_BLOCKED_INTERESTED))
break loop;
break;
case BLOCKED:
if (_state.compareAndSet(State.BLOCKED,State.BLOCKED_INTERESTED))
break loop;
break;
case FILLING_BLOCKED_INTERESTED:
case FILLING_INTERESTED:
case BLOCKED_INTERESTED:
case INTERESTED:
break loop;
}
}
}
private void unblock()
{
LOG.debug("unblock {}",this);
loop:while(true)
{
switch(_state.get())
{
case FILLING_BLOCKED:
if (_state.compareAndSet(State.FILLING_BLOCKED,State.FILLING))
break loop;
break;
case FILLING_BLOCKED_INTERESTED:
if (_state.compareAndSet(State.FILLING_BLOCKED_INTERESTED,State.FILLING_INTERESTED))
break loop;
break;
case BLOCKED_INTERESTED:
if (_state.compareAndSet(State.BLOCKED_INTERESTED,State.INTERESTED))
{
getEndPoint().fillInterested(_readCallback);
break loop;
}
break;
case BLOCKED:
if (_state.compareAndSet(State.BLOCKED,State.IDLE))
break loop;
break;
case FILLING:
case IDLE:
case FILLING_INTERESTED:
case INTERESTED:
break loop;
}
}
}
/**
*/
protected void block(final BlockingCallback callback)
{
LOG.debug("block {}",this);
final Callback blocked=new Callback()
{
@Override
public void succeeded()
{
unblock();
callback.succeeded();
}
@Override
public void failed(Throwable x)
{
unblock();
callback.failed(x);
}
};
loop:while(true)
{
switch(_state.get())
{
case IDLE:
if (_state.compareAndSet(State.IDLE,State.BLOCKED))
{
getEndPoint().fillInterested(blocked);
break loop;
}
break;
case FILLING:
if (_state.compareAndSet(State.FILLING,State.FILLING_BLOCKED))
{
getEndPoint().fillInterested(blocked);
break loop;
}
break;
case FILLING_INTERESTED:
if (_state.compareAndSet(State.FILLING_INTERESTED,State.FILLING_BLOCKED_INTERESTED))
{
getEndPoint().fillInterested(blocked);
break loop;
}
break;
case BLOCKED:
case BLOCKED_INTERESTED:
case FILLING_BLOCKED:
case FILLING_BLOCKED_INTERESTED:
throw new IllegalStateException("Already Blocked");
case INTERESTED:
throw new IllegalStateException();
}
}
}
/**
* <p>Callback method invoked when the endpoint is ready to be read.</p>
@ -225,7 +343,7 @@ public abstract class AbstractConnection implements Connection
private enum State
{
IDLE, INTERESTED, FILLING, FILLING_INTERESTED
IDLE, INTERESTED, FILLING, FILLING_INTERESTED, FILLING_BLOCKED, BLOCKED, FILLING_BLOCKED_INTERESTED, BLOCKED_INTERESTED
}
private class ReadCallback implements Callback, Runnable
@ -247,12 +365,25 @@ public abstract class AbstractConnection implements Connection
{
case IDLE:
case INTERESTED:
throw new IllegalStateException();
case BLOCKED:
case BLOCKED_INTERESTED:
LOG.warn(new IllegalStateException());
return;
case FILLING:
if (_state.compareAndSet(State.FILLING,State.IDLE))
break loop;
break;
case FILLING_BLOCKED:
if (_state.compareAndSet(State.FILLING_BLOCKED,State.BLOCKED))
break loop;
break;
case FILLING_BLOCKED_INTERESTED:
if (_state.compareAndSet(State.FILLING_BLOCKED_INTERESTED,State.BLOCKED_INTERESTED))
break loop;
break;
case FILLING_INTERESTED:
if (_state.compareAndSet(State.FILLING_INTERESTED,State.INTERESTED))
@ -266,7 +397,7 @@ public abstract class AbstractConnection implements Connection
}
}
else
LOG.warn(new Throwable());
LOG.warn(new IllegalStateException());
}
@Override

View File

@ -35,7 +35,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@ -167,20 +166,6 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
selector.submit(selector.new Accept(channel));
}
/**
* <p>Registers a channel to perform non-blocking read/write operations.</p>
* <p>This method is called just after a channel has been accepted by {@link ServerSocketChannel#accept()},
* or just after having performed a blocking connect via {@link Socket#connect(SocketAddress, int)}.</p>
*
* @param channel the channel to register
* @param attachment An attachment to be passed via the selection key to the {@link SelectorManager#newConnection(SocketChannel, EndPoint, Object)} method.
*/
public void accept(final SocketChannel channel, Object attachment)
{
final ManagedSelector selector = chooseSelector();
selector.submit(selector.new Accept(channel, attachment));
}
@Override
protected void doStart() throws Exception
{
@ -333,7 +318,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
public class ManagedSelector extends AbstractLifeCycle implements Runnable, Dumpable
{
private final Queue<Runnable> _changes = new ConcurrentArrayQueue<>();
private final int _id;
private Selector _selector;
private volatile Thread _thread;
@ -699,18 +684,10 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
private class Accept implements Runnable
{
private final SocketChannel _channel;
private final Object _attachment;
public Accept(SocketChannel channel)
{
this._channel = channel;
this._attachment = null;
}
public Accept(SocketChannel channel, Object attachment)
{
this._channel = channel;
this._attachment = attachment;
}
@Override
@ -718,7 +695,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
{
try
{
SelectionKey key = _channel.register(_selector, 0, _attachment);
SelectionKey key = _channel.register(_selector, 0, null);
EndPoint endpoint = createEndPoint(_channel, key);
key.attach(endpoint);
}

View File

@ -11,7 +11,7 @@
<!-- ======================================================== -->
<Call class="java.lang.System" name="setProperty">
<Arg>java.security.auth.login.config</Arg>
<Arg><SystemProperty name="jetty.home" default="." />/etc/login.conf</Arg>
<Arg><Property name="jetty.home" default="." />/<Property name="jaas.login.conf" default="etc/login.conf"/></Arg>
</Call>
</Configure>

View File

@ -284,7 +284,7 @@ public abstract class AbstractJettyMojo extends AbstractMojo
/**
* A wrapper for the Server object
*/
protected JettyServer server = JettyServer.getInstance();
protected JettyServer server = new JettyServer();
/**
@ -494,6 +494,8 @@ public abstract class AbstractJettyMojo extends AbstractMojo
String tmp = System.getProperty(PORT_SYSPROPERTY, MavenServerConnector.DEFAULT_PORT_STR);
httpConnector.setPort(Integer.parseInt(tmp.trim()));
}
if (httpConnector.getServer() == null)
httpConnector.setServer(this.server);
this.server.addConnector(httpConnector);
}
@ -504,12 +506,13 @@ public abstract class AbstractJettyMojo extends AbstractMojo
//if <httpConnector> not configured in the pom, create one
if (httpConnector == null)
{
httpConnector = new MavenServerConnector();
httpConnector = new MavenServerConnector();
//use any jetty.port settings provided
String tmp = System.getProperty(PORT_SYSPROPERTY, MavenServerConnector.DEFAULT_PORT_STR);
httpConnector.setPort(Integer.parseInt(tmp.trim()));
}
if (httpConnector.getServer() == null)
httpConnector.setServer(this.server);
this.server.setConnectors(new Connector[] {httpConnector});
}

View File

@ -38,16 +38,6 @@ public class JettyServer extends org.eclipse.jetty.server.Server
{
public static final JettyServer __instance = new JettyServer();
/**
* Singleton instance
* @return
*/
public static JettyServer getInstance()
{
return __instance;
}
private RequestLog requestLog;
private ContextHandlerCollection contexts;
@ -56,7 +46,7 @@ public class JettyServer extends org.eclipse.jetty.server.Server
/**
*
*/
private JettyServer()
public JettyServer()
{
super();
setStopAtShutdown(true);

View File

@ -19,21 +19,259 @@
package org.eclipse.jetty.maven.plugin;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.ConnectionFactory;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.thread.Scheduler;
/**
* MavenServerConnector
*
*
* As the ServerConnector class does not have a no-arg constructor, and moreover requires
* the server instance passed in to all its constructors, it cannot
* be referenced in the pom.xml. This class wraps a ServerConnector, delaying setting the
* server instance. Only a few of the setters from the ServerConnector class are supported.
*/
public class MavenServerConnector extends ServerConnector
public class MavenServerConnector extends AbstractLifeCycle implements Connector
{
public static int DEFAULT_PORT = 8080;
public static String DEFAULT_PORT_STR = String.valueOf(DEFAULT_PORT);
public static int DEFAULT_MAX_IDLE_TIME = 30000;
private Server server;
private ServerConnector delegate;
private String host;
private String name;
private int port;
private long idleTimeout;
private int lingerTime;
public MavenServerConnector()
{
super(JettyServer.getInstance());
}
public void setServer(Server server)
{
this.server = server;
}
public void setHost(String host)
{
this.host = host;
}
public String getHost()
{
return this.host;
}
public void setPort(int port)
{
this.port = port;
}
public int getPort ()
{
return this.port;
}
public void setName (String name)
{
this.name = name;
}
public void setIdleTimeout(long idleTimeout)
{
this.idleTimeout = idleTimeout;
}
public void setSoLingerTime(int lingerTime)
{
this.lingerTime = lingerTime;
}
@Override
protected void doStart() throws Exception
{
if (this.server == null)
throw new IllegalStateException("Server not set for MavenServerConnector");
this.delegate = new ServerConnector(this.server);
this.delegate.setName(this.name);
this.delegate.setPort(this.port);
this.delegate.setHost(this.host);
this.delegate.setIdleTimeout(idleTimeout);
this.delegate.setSoLingerTime(lingerTime);
this.delegate.start();
super.doStart();
}
@Override
protected void doStop() throws Exception
{
this.delegate.stop();
super.doStop();
this.delegate = null;
}
/**
* @see org.eclipse.jetty.util.component.Graceful#shutdown()
*/
@Override
public Future<Void> shutdown()
{
checkDelegate();
return this.delegate.shutdown();
}
/**
* @see org.eclipse.jetty.server.Connector#getServer()
*/
@Override
public Server getServer()
{
return this.server;
}
/**
* @see org.eclipse.jetty.server.Connector#getExecutor()
*/
@Override
public Executor getExecutor()
{
checkDelegate();
return this.delegate.getExecutor();
}
/**
* @see org.eclipse.jetty.server.Connector#getScheduler()
*/
@Override
public Scheduler getScheduler()
{
checkDelegate();
return this.delegate.getScheduler();
}
/**
* @see org.eclipse.jetty.server.Connector#getByteBufferPool()
*/
@Override
public ByteBufferPool getByteBufferPool()
{
checkDelegate();
return this.delegate.getByteBufferPool();
}
/**
* @see org.eclipse.jetty.server.Connector#getConnectionFactory(java.lang.String)
*/
@Override
public ConnectionFactory getConnectionFactory(String nextProtocol)
{
checkDelegate();
return this.delegate.getConnectionFactory(nextProtocol);
}
/**
* @see org.eclipse.jetty.server.Connector#getConnectionFactory(java.lang.Class)
*/
@Override
public <T> T getConnectionFactory(Class<T> factoryType)
{
checkDelegate();
return this.delegate.getConnectionFactory(factoryType);
}
/**
* @see org.eclipse.jetty.server.Connector#getDefaultConnectionFactory()
*/
@Override
public ConnectionFactory getDefaultConnectionFactory()
{
checkDelegate();
return getDefaultConnectionFactory();
}
/**
* @see org.eclipse.jetty.server.Connector#getConnectionFactories()
*/
@Override
public Collection<ConnectionFactory> getConnectionFactories()
{
checkDelegate();
return this.delegate.getConnectionFactories();
}
/**
* @see org.eclipse.jetty.server.Connector#getProtocols()
*/
@Override
public List<String> getProtocols()
{
checkDelegate();
return this.delegate.getProtocols();
}
/**
* @see org.eclipse.jetty.server.Connector#getIdleTimeout()
*/
@Override
@ManagedAttribute("maximum time a connection can be idle before being closed (in ms)")
public long getIdleTimeout()
{
checkDelegate();
return this.delegate.getIdleTimeout();
}
/**
* @see org.eclipse.jetty.server.Connector#getTransport()
*/
@Override
public Object getTransport()
{
checkDelegate();
return this.delegate.getTransport();
}
/**
* @see org.eclipse.jetty.server.Connector#getConnectedEndPoints()
*/
@Override
public Collection<EndPoint> getConnectedEndPoints()
{
checkDelegate();
return this.delegate.getConnectedEndPoints();
}
/**
* @see org.eclipse.jetty.server.Connector#getName()
*/
@Override
public String getName()
{
return this.name;
}
private void checkDelegate() throws IllegalStateException
{
if (this.delegate == null)
throw new IllegalStateException ("MavenServerConnector delegate not ready");
}
}

View File

@ -55,7 +55,7 @@ public class Starter
private List<File> jettyXmls; // list of jetty.xml config files to apply - Mandatory
private File contextXml; //name of context xml file to configure the webapp - Mandatory
private JettyServer server;
private JettyServer server = new JettyServer();
private JettyWebAppContext webApp;
@ -120,8 +120,6 @@ public class Starter
{
LOG.debug("Starting Jetty Server ...");
this.server = JettyServer.getInstance();
//apply any configs from jetty.xml files first
applyJettyXml ();
@ -132,6 +130,7 @@ public class Starter
{
//if a SystemProperty -Djetty.port=<portnum> has been supplied, use that as the default port
MavenServerConnector httpConnector = new MavenServerConnector();
httpConnector.setServer(this.server);
String tmp = System.getProperty(PORT_SYSPROPERTY, MavenServerConnector.DEFAULT_PORT_STR);
httpConnector.setPort(Integer.parseInt(tmp.trim()));
connectors = new Connector[] {httpConnector};

View File

@ -149,7 +149,7 @@ public class MongoSessionManager extends NoSqlSessionManager
}
else
{
version = new Long(((Long)version).intValue() + 1);
version = new Long(((Number)version).longValue() + 1);
update.put("$inc",__version_1);
}

View File

@ -236,7 +236,7 @@ public class DefaultJettyAtJettyHomeHelper
// can define their own configuration.
if ((enUrls == null || !enUrls.hasMoreElements()))
{
String tmp = DEFAULT_JETTYHOME+etcFile;
String tmp = DEFAULT_JETTYHOME+(DEFAULT_JETTYHOME.endsWith("/")?"":"/")+etcFile;
enUrls = BundleFileLocatorHelperFactory.getFactory().getHelper().findEntries(configurationBundle, tmp);
LOG.info("Configuring jetty from bundle: "
+ configurationBundle.getSymbolicName()

View File

@ -95,7 +95,7 @@
<version>${exam.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.runner</groupId>
<artifactId>pax-runner-no-jcl</artifactId>
@ -141,12 +141,14 @@
<scope>test</scope>
</dependency>
<!-- For sane logging -->
<!--
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.1</version>
<scope>test</scope>
</dependency>
-->
<!-- Orbit Servlet Deps -->
<dependency>
<groupId>org.eclipse.jetty.orbit</groupId>
@ -258,7 +260,7 @@
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.spdy</groupId>
<artifactId>spdy-core</artifactId>
@ -296,7 +298,7 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-plus</artifactId>

View File

@ -78,17 +78,21 @@ public class TestJettyOSGiBootContextAsService
// to pick up and deploy
options.add(mavenBundle().groupId("org.eclipse.jetty.osgi").artifactId("test-jetty-osgi-context").versionAsInProject().start());
String logLevel = "WARN";
// Enable Logging
if (LOGGING_ENABLED)
{
options.addAll(Arrays.asList(options(
// install log service using pax runners profile abstraction (there
// are more profiles, like DS)
// logProfile(),
// this is how you set the default log level when using pax logging
// (logProfile)
systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("INFO"))));
}
logLevel = "INFO";
options.addAll(Arrays.asList(options(
// install log service using pax runners profile abstraction (there
// are more profiles, like DS)
// logProfile(),
// this is how you set the default log level when using pax logging
// (logProfile)
systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value(logLevel),
systemProperty("org.eclipse.jetty.LEVEL").value(logLevel))));
return options.toArray(new Option[options.size()]);
}
@ -142,12 +146,14 @@ public class TestJettyOSGiBootContextAsService
ServiceReference[] refs = bundleContext.getServiceReferences(ContextHandler.class.getName(), null);
Assert.assertNotNull(refs);
Assert.assertEquals(1, refs.length);
String[] keys = refs[0].getPropertyKeys();
//uncomment for debugging
/*
String[] keys = refs[0].getPropertyKeys();
if (keys != null)
{
for (String k : keys)
System.err.println("service property: " + k + ", " + refs[0].getProperty(k));
}
}*/
ContextHandler ch = (ContextHandler) bundleContext.getService(refs[0]);
Assert.assertEquals("/acme", ch.getContextPath());

View File

@ -53,7 +53,6 @@ public class TestJettyOSGiBootCore
public Option[] config()
{
VersionResolver resolver = MavenUtils.asInProject();
System.err.println(resolver.getVersion("org.eclipse.jetty", "jetty-server"));
ArrayList<Option> options = new ArrayList<Option>();
TestOSGiUtil.addMoreOSGiContainers(options);
options.addAll(provisionCoreJetty());

View File

@ -19,9 +19,13 @@
package org.eclipse.jetty.osgi.test;
import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
import static org.ops4j.pax.exam.CoreOptions.systemProperty;
import static org.ops4j.pax.exam.CoreOptions.options;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.inject.Inject;
@ -41,7 +45,8 @@ import org.osgi.framework.BundleContext;
@RunWith(JUnit4TestRunner.class)
public class TestJettyOSGiBootSpdy
{
private static final boolean LOGGING_ENABLED = false;
private static final String JETTY_SPDY_PORT = "jetty.spdy.port";
private static final int DEFAULT_JETTY_SPDY_PORT = 9877;
@ -61,6 +66,22 @@ public class TestJettyOSGiBootSpdy
options.add(CoreOptions.junitBundles());
options.addAll(TestJettyOSGiBootCore.httpServiceJetty());
options.addAll(spdyJettyDependencies());
String logLevel = "WARN";
// Enable Logging
if (LOGGING_ENABLED)
logLevel = "INFO";
options.addAll(Arrays.asList(options(
// install log service using pax runners profile abstraction (there
// are more profiles, like DS)
// logProfile(),
// this is how you set the default log level when using pax logging
// (logProfile)
systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value(logLevel),
systemProperty("org.eclipse.jetty.LEVEL").value(logLevel))));
return options.toArray(new Option[options.size()]);
}

View File

@ -77,18 +77,20 @@ public class TestJettyOSGiBootWebAppAsService
options.addAll(configureJettyHomeAndPort("jetty-selector.xml"));
options.add(CoreOptions.bootDelegationPackages("org.xml.sax", "org.xml.*", "org.w3c.*", "javax.xml.*"));
options.addAll(TestJettyOSGiBootCore.coreJettyDependencies());
// Enable Logging
String logLevel = "WARN";
if (LOGGING_ENABLED)
{
options.addAll(Arrays.asList(options(
// install log service using pax runners profile abstraction (there
// are more profiles, like DS)
// logProfile(),
// this is how you set the default log level when using pax logging
// (logProfile)
systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("INFO"))));
}
logLevel = "INFO";
options.addAll(Arrays.asList(options(
// install log service using pax runners profile abstraction (there
// are more profiles, like DS)
// logProfile(),
// this is how you set the default log level when using pax logging
// (logProfile)
systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value(logLevel),
systemProperty("org.eclipse.jetty.LEVEL").value(logLevel))));
options.addAll(jspDependencies());
return options.toArray(new Option[options.size()]);

View File

@ -52,7 +52,7 @@ import org.osgi.framework.BundleContext;
@RunWith(JUnit4TestRunner.class)
public class TestJettyOSGiBootWithJsp
{
private static final boolean LOGGING_ENABLED = true;
private static final boolean LOGGING_ENABLED = false;
private static final boolean REMOTE_DEBUGGING = false;
@ -72,18 +72,21 @@ public class TestJettyOSGiBootWithJsp
options.add(CoreOptions.bootDelegationPackages("org.xml.sax", "org.xml.*", "org.w3c.*", "javax.xml.*"));
options.addAll(TestJettyOSGiBootCore.coreJettyDependencies());
String logLevel = "WARN";
// Enable Logging
if (LOGGING_ENABLED)
{
logLevel = "INFO";
options.addAll(Arrays.asList(options(
// install log service using pax runners profile abstraction (there
// are more profiles, like DS)
// logProfile(),
// this is how you set the default log level when using pax logging
// (logProfile)
systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("INFO"))));
}
systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value(logLevel),
systemProperty("org.eclipse.jetty.LEVEL").value(logLevel))));
options.addAll(jspDependencies());
// Remote JDWP Debugging, this won't work with the forked container.
@ -119,7 +122,6 @@ public class TestJettyOSGiBootWithJsp
+ etc
+ "/jetty-testrealm.xml";
options.add(systemProperty(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS).value(xmlConfigs));
System.err.println(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS+"="+xmlConfigs);
options.add(systemProperty("jetty.port").value(String.valueOf(TestJettyOSGiBootCore.DEFAULT_JETTY_HTTP_PORT)));
options.add(systemProperty("jetty.home").value(etcFolder.getParentFile().getAbsolutePath()));
return options;
@ -163,10 +165,6 @@ public class TestJettyOSGiBootWithJsp
@Test
public void testJspDump() throws Exception
{
// System.err.println("http://127.0.0.1:9876/jsp/dump.jsp sleeping....");
// Thread.currentThread().sleep(5000000);
// now test the jsp/dump.jsp
HttpClient client = new HttpClient();
try
{
@ -175,7 +173,6 @@ public class TestJettyOSGiBootWithJsp
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
String content = new String(response.getContent());
System.err.println("content: " + content);
Assert.assertTrue(content.contains("<tr><th>ServletPath:</th><td>/jsp/dump.jsp</td></tr>"));
}
finally

View File

@ -0,0 +1,13 @@
# LOG4J levels: OFF, FATAL, ERROR, WARN, INFO, DEBUG, ALL
#
log4j.rootLogger=ALL,CONSOLE
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
#log4j.appender.CONSOLE.threshold=INFO
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
#log4j.appender.CONSOLE.layout.ConversionPattern=%d %t [%5p][%c{1}] %m%n
log4j.appender.CONSOLE.layout.ConversionPattern=%d [%5p][%c] %m%n
# Level tuning
log4j.logger.org.eclipse.jetty=INFO
log4j.logger.org.ops4j=WARN

View File

@ -16,101 +16,9 @@
<Set name="handler">
<New id="Rewrite" class="org.eclipse.jetty.rewrite.handler.RewriteHandler">
<Set name="handler"><Ref refid="oldhandler"/></Set>
<Set name="rewriteRequestURI">true</Set>
<Set name="rewritePathInfo">false</Set>
<Set name="originalPathAttribute">requestedPath</Set>
<!-- Add rule to protect against IE ssl bug -->
<Call name="addRule">
<Arg>
<New class="org.eclipse.jetty.rewrite.handler.MsieSslRule"/>
</Arg>
</Call>
<!-- protect favicon handling -->
<Call name="addRule">
<Arg>
<New class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
<Set name="pattern">/favicon.ico</Set>
<Set name="name">Cache-Control</Set>
<Set name="value">Max-Age=3600,public</Set>
<Set name="terminating">true</Set>
</New>
</Arg>
</Call>
<!-- redirect from the welcome page to a specific page -->
<Call name="addRule">
<Arg>
<New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Set name="pattern">/test/rewrite/</Set>
<Set name="replacement">/test/rewrite/info.html</Set>
</New>
</Arg>
</Call>
<!-- replace the entire request URI -->
<Call name="addRule">
<Arg>
<New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Set name="pattern">/test/some/old/context</Set>
<Set name="replacement">/test/rewritten/newcontext</Set>
</New>
</Arg>
</Call>
<!-- replace the beginning of the request URI -->
<Call name="addRule">
<Arg>
<New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Set name="pattern">/test/rewrite/for/*</Set>
<Set name="replacement">/test/rewritten/</Set>
</New>
</Arg>
</Call>
<!-- reverse the order of the path sections -->
<Call name="addRule">
<Arg>
<New class="org.eclipse.jetty.rewrite.handler.RewriteRegexRule">
<Set name="regex">(.*?)/reverse/([^/]*)/(.*)</Set>
<Set name="replacement">$1/reverse/$3/$2</Set>
</New>
</Arg>
</Call>
<!-- add a cookie to each path visited -->
<Call name="addRule">
<Arg>
<New class="org.eclipse.jetty.rewrite.handler.CookiePatternRule">
<Set name="pattern">/*</Set>
<Set name="name">visited</Set>
<Set name="value">yes</Set>
</New>
</Arg>
</Call>
<!-- actual redirect, instead of internal rewrite -->
<Call name="addRule">
<Arg>
<New class="org.eclipse.jetty.rewrite.handler.RedirectPatternRule">
<Set name="pattern">/test/redirect/*</Set>
<Set name="location">/test/redirected</Set>
</New>
</Arg>
</Call>
<!-- add a response rule -->
<Call name="addRule">
<Arg>
<New class="org.eclipse.jetty.rewrite.handler.ResponsePatternRule">
<Set name="pattern">/400Error</Set>
<Set name="code">400</Set>
<Set name="reason">ResponsePatternRule Demo</Set>
</New>
</Arg>
</Call>
<Set name="rewriteRequestURI"><Property name="rewrite.rewriteRequestURI" default="true"/></Set>
<Set name="rewritePathInfo"><Property name="rewrite.rewritePathInfo" default="false"/></Set>
<Set name="originalPathAttribute"><Property name="rewrite.originalPathAttribute" default="requestedPath"/></Set>
</New>
</Set>

View File

@ -27,9 +27,7 @@
</goals>
<configuration>
<includes>**</includes>
<excludes>**/MANIFEST.MF</excludes>
<excludes>**/ECLIPSEF.RSA</excludes>
<excludes>**/ECLIPSEF.SF</excludes>
<excludes>**/MANIFEST.MF,META-INF/*.RSA,META-INF/*.DSA,META-INF/*.SF</excludes>
<outputDirectory>${project.build.directory}/classes</outputDirectory>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>

View File

@ -551,23 +551,20 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
protected void processConstraintMappingWithMethodOmissions (ConstraintMapping mapping, Map<String, RoleInfo> mappings)
{
String[] omissions = mapping.getMethodOmissions();
for (String omission:omissions)
StringBuilder sb = new StringBuilder();
for (int i=0; i<omissions.length; i++)
{
//for each method omission, see if there is already a RoleInfo for it in mappings
RoleInfo ri = mappings.get(omission+OMISSION_SUFFIX);
if (ri == null)
{
//if not, make one
ri = new RoleInfo();
mappings.put(omission+OMISSION_SUFFIX, ri);
}
//initialize RoleInfo or combine from ConstraintMapping
configureRoleInfo(ri, mapping);
if (i > 0)
sb.append(".");
sb.append(omissions[i]);
}
sb.append(OMISSION_SUFFIX);
RoleInfo ri = new RoleInfo();
mappings.put(sb.toString(), ri);
configureRoleInfo(ri, mapping);
}
/* ------------------------------------------------------------ */
/**
@ -630,7 +627,7 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
* <ol>
* <li>A mapping of an exact method name </li>
* <li>A mapping will null key that matches every method name</li>
* <li>Mappings with keys of the form "method.omission" that indicates it will match every method name EXCEPT that given</li>
* <li>Mappings with keys of the form "&lt;method&gt;.&lt;method&gt;.&lt;method&gt;.omission" that indicates it will match every method name EXCEPT those given</li>
* </ol>
*
* @see org.eclipse.jetty.security.SecurityHandler#prepareConstraintInfo(java.lang.String, org.eclipse.jetty.server.Request)
@ -659,7 +656,7 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
//(ie matches because target method is not omitted, hence considered covered by the constraint)
for (Entry<String, RoleInfo> entry: mappings.entrySet())
{
if (entry.getKey() != null && entry.getKey().contains(OMISSION_SUFFIX) && !(httpMethod+OMISSION_SUFFIX).equals(entry.getKey()))
if (entry.getKey() != null && entry.getKey().endsWith(OMISSION_SUFFIX) && ! entry.getKey().contains(httpMethod))
applicableConstraints.add(entry.getValue());
}

View File

@ -21,11 +21,11 @@ package org.eclipse.jetty.security.authentication;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.BitSet;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
@ -53,24 +53,40 @@ import org.eclipse.jetty.util.security.Credential;
* @version $Rev: 4793 $ $Date: 2009-03-19 00:00:01 +0100 (Thu, 19 Mar 2009) $
*
* The nonce max age in ms can be set with the {@link SecurityHandler#setInitParameter(String, String)}
* using the name "maxNonceAge"
* using the name "maxNonceAge". The nonce max count can be set with {@link SecurityHandler#setInitParameter(String, String)}
* using the name "maxNonceCount". When the age or count is exceeded, the nonce is considered stale.
*/
public class DigestAuthenticator extends LoginAuthenticator
{
private static final Logger LOG = Log.getLogger(DigestAuthenticator.class);
SecureRandom _random = new SecureRandom();
private long _maxNonceAgeMs = 60*1000;
private ConcurrentMap<String, Nonce> _nonceCount = new ConcurrentHashMap<String, Nonce>();
private int _maxNC=1024;
private ConcurrentMap<String, Nonce> _nonceMap = new ConcurrentHashMap<String, Nonce>();
private Queue<Nonce> _nonceQueue = new ConcurrentLinkedQueue<Nonce>();
private static class Nonce
{
final String _nonce;
final long _ts;
AtomicInteger _nc=new AtomicInteger();
public Nonce(String nonce, long ts)
final BitSet _seen;
public Nonce(String nonce, long ts, int size)
{
_nonce=nonce;
_ts=ts;
_seen = new BitSet(size);
}
public boolean seen(int count)
{
synchronized (this)
{
if (count>=_seen.size())
return true;
boolean s=_seen.get(count);
_seen.set(count);
return s;
}
}
}
@ -92,11 +108,31 @@ public class DigestAuthenticator extends LoginAuthenticator
String mna=configuration.getInitParameter("maxNonceAge");
if (mna!=null)
{
synchronized (this)
{
_maxNonceAgeMs=Long.valueOf(mna);
}
_maxNonceAgeMs=Long.valueOf(mna);
}
String mnc=configuration.getInitParameter("maxNonceCount");
if (mnc!=null)
{
_maxNC=Integer.valueOf(mnc);
}
}
/* ------------------------------------------------------------ */
public int getMaxNonceCount()
{
return _maxNC;
}
/* ------------------------------------------------------------ */
public void setMaxNonceCount(int maxNC)
{
_maxNC = maxNC;
}
/* ------------------------------------------------------------ */
public long getMaxNonceAge()
{
return _maxNonceAgeMs;
}
/* ------------------------------------------------------------ */
@ -238,9 +274,9 @@ public class DigestAuthenticator extends LoginAuthenticator
byte[] nounce = new byte[24];
_random.nextBytes(nounce);
nonce = new Nonce(new String(B64Code.encode(nounce)),request.getTimeStamp());
nonce = new Nonce(new String(B64Code.encode(nounce)),request.getTimeStamp(),_maxNC);
}
while (_nonceCount.putIfAbsent(nonce._nonce,nonce)!=null);
while (_nonceMap.putIfAbsent(nonce._nonce,nonce)!=null);
_nonceQueue.add(nonce);
return nonce._nonce;
@ -255,34 +291,27 @@ public class DigestAuthenticator extends LoginAuthenticator
private int checkNonce(Digest digest, Request request)
{
// firstly let's expire old nonces
long expired;
synchronized (this)
{
expired = request.getTimeStamp()-_maxNonceAgeMs;
}
long expired = request.getTimeStamp()-_maxNonceAgeMs;
Nonce nonce=_nonceQueue.peek();
while (nonce!=null && nonce._ts<expired)
{
_nonceQueue.remove(nonce);
_nonceCount.remove(nonce._nonce);
_nonceMap.remove(nonce._nonce);
nonce=_nonceQueue.peek();
}
// Now check the requested nonce
try
{
nonce = _nonceCount.get(digest.nonce);
nonce = _nonceMap.get(digest.nonce);
if (nonce==null)
return 0;
long count = Long.parseLong(digest.nc,16);
if (count>Integer.MAX_VALUE)
if (count>=_maxNC)
return 0;
int old=nonce._nc.get();
while (!nonce._nc.compareAndSet(old,(int)count))
old=nonce._nc.get();
if (count<=old)
if (nonce.seen((int)count))
return -1;
return 1;
@ -383,6 +412,7 @@ public class DigestAuthenticator extends LoginAuthenticator
return false;
}
@Override
public String toString()
{
return username + "," + response;

View File

@ -25,6 +25,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.matchers.JUnitMatchers.containsString;
import java.io.IOException;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@ -33,12 +34,15 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.security.authentication.BasicAuthenticator;
import org.eclipse.jetty.security.authentication.DigestAuthenticator;
import org.eclipse.jetty.security.authentication.FormAuthenticator;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpConfiguration;
@ -52,7 +56,10 @@ import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.server.session.SessionHandler;
import org.eclipse.jetty.util.B64Code;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.security.Constraint;
import org.eclipse.jetty.util.security.Credential;
import org.eclipse.jetty.util.security.Password;
import org.junit.After;
import org.junit.Before;
@ -205,7 +212,6 @@ public class ConstraintTest
@Test
public void testBasic() throws Exception
{
List<ConstraintMapping> list = new ArrayList<ConstraintMapping>(_security.getConstraintMappings());
Constraint constraint6 = new Constraint();
@ -250,14 +256,11 @@ public class ConstraintTest
_server.start();
String response;
/*
response = _connector.getResponses("GET /ctx/noauth/info HTTP/1.0\r\n\r\n");
assertThat(response,startsWith("HTTP/1.1 200 OK"));
*/
response = _connector.getResponses("GET /ctx/forbid/info HTTP/1.0\r\n\r\n");
assertThat(response,startsWith("HTTP/1.1 403 Forbidden"));
/*
response = _connector.getResponses("GET /ctx/auth/info HTTP/1.0\r\n\r\n");
assertThat(response,startsWith("HTTP/1.1 401 Unauthorized"));
assertThat(response,containsString("WWW-Authenticate: basic realm=\"TestRealm\""));
@ -272,8 +275,7 @@ public class ConstraintTest
"Authorization: Basic " + B64Code.encode("user:password") + "\r\n" +
"\r\n");
assertThat(response,startsWith("HTTP/1.1 200 OK"));
*/
/*
// test admin
response = _connector.getResponses("GET /ctx/admin/info HTTP/1.0\r\n\r\n");
assertThat(response,startsWith("HTTP/1.1 401 Unauthorized"));
@ -304,28 +306,159 @@ public class ConstraintTest
response = _connector.getResponses("GET /ctx/omit/x HTTP/1.0\r\n" +
"Authorization: Basic " + B64Code.encode("admin:password") + "\r\n" +
"\r\n");
assertTrue(response.startsWith("HTTP/1.1 200 OK"));
assertThat(response,startsWith("HTTP/1.1 200 OK"));
//check POST is in role user
response = _connector.getResponses("POST /ctx/omit/x HTTP/1.0\r\n" +
"Authorization: Basic " + B64Code.encode("user2:password") + "\r\n" +
"\r\n");
assertTrue(response.startsWith("HTTP/1.1 200 OK"));
assertThat(response,startsWith("HTTP/1.1 200 OK"));
//check POST can be in role foo too
response = _connector.getResponses("POST /ctx/omit/x HTTP/1.0\r\n" +
"Authorization: Basic " + B64Code.encode("user3:password") + "\r\n" +
"\r\n");
assertTrue(response.startsWith("HTTP/1.1 200 OK"));
assertThat(response,startsWith("HTTP/1.1 200 OK"));
//check HEAD cannot be in role user
response = _connector.getResponses("HEAD /ctx/omit/x HTTP/1.0\r\n" +
"Authorization: Basic " + B64Code.encode("user2:password") + "\r\n" +
"\r\n");
assertTrue(response.startsWith("HTTP/1.1 200 OK"));*/
assertThat(response,startsWith("HTTP/1.1 403 "));
}
private static String CNONCE="1234567890";
private String digest(String nonce, String username,String password,String uri,String nc) throws Exception
{
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] ha1;
// calc A1 digest
md.update(username.getBytes(StringUtil.__ISO_8859_1));
md.update((byte) ':');
md.update("TestRealm".getBytes(StringUtil.__ISO_8859_1));
md.update((byte) ':');
md.update(password.getBytes(StringUtil.__ISO_8859_1));
ha1 = md.digest();
// calc A2 digest
md.reset();
md.update("GET".getBytes(StringUtil.__ISO_8859_1));
md.update((byte) ':');
md.update(uri.getBytes(StringUtil.__ISO_8859_1));
byte[] ha2 = md.digest();
// calc digest
// request-digest = <"> < KD ( H(A1), unq(nonce-value) ":"
// nc-value ":" unq(cnonce-value) ":" unq(qop-value) ":" H(A2) )
// <">
// request-digest = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2)
// ) > <">
md.update(TypeUtil.toString(ha1, 16).getBytes(StringUtil.__ISO_8859_1));
md.update((byte) ':');
md.update(nonce.getBytes(StringUtil.__ISO_8859_1));
md.update((byte) ':');
md.update(nc.getBytes(StringUtil.__ISO_8859_1));
md.update((byte) ':');
md.update(CNONCE.getBytes(StringUtil.__ISO_8859_1));
md.update((byte) ':');
md.update("auth".getBytes(StringUtil.__ISO_8859_1));
md.update((byte) ':');
md.update(TypeUtil.toString(ha2, 16).getBytes(StringUtil.__ISO_8859_1));
byte[] digest = md.digest();
// check digest
return TypeUtil.toString(digest, 16);
}
@Test
public void testDigest() throws Exception
{
DigestAuthenticator authenticator = new DigestAuthenticator();
authenticator.setMaxNonceCount(5);
_security.setAuthenticator(authenticator);
_security.setStrict(false);
_server.start();
String response;
response = _connector.getResponses("GET /ctx/noauth/info HTTP/1.0\r\n\r\n");
assertThat(response,startsWith("HTTP/1.1 200 OK"));
response = _connector.getResponses("GET /ctx/forbid/info HTTP/1.0\r\n\r\n");
assertThat(response,startsWith("HTTP/1.1 403 Forbidden"));
response = _connector.getResponses("GET /ctx/auth/info HTTP/1.0\r\n\r\n");
assertThat(response,startsWith("HTTP/1.1 401 Unauthorized"));
assertThat(response,containsString("WWW-Authenticate: Digest realm=\"TestRealm\""));
Pattern nonceP = Pattern.compile("nonce=\"([^\"]*)\",");
Matcher matcher = nonceP.matcher(response);
assertTrue(matcher.find());
String nonce=matcher.group(1);
//wrong password
String digest= digest(nonce,"user","WRONG","/ctx/auth/info","1");
response = _connector.getResponses("GET /ctx/auth/info HTTP/1.0\r\n" +
"Authorization: Digest username=\"user\", qop=auth, cnonce=\"1234567890\", uri=\"/ctx/auth/info\", realm=\"TestRealm\", "+
"nc=1, "+
"nonce=\""+nonce+"\", "+
"response=\""+digest+"\"\r\n"+
"\r\n");
assertThat(response,startsWith("HTTP/1.1 401 Unauthorized"));
// right password
digest= digest(nonce,"user","password","/ctx/auth/info","2");
response = _connector.getResponses("GET /ctx/auth/info HTTP/1.0\r\n" +
"Authorization: Digest username=\"user\", qop=auth, cnonce=\"1234567890\", uri=\"/ctx/auth/info\", realm=\"TestRealm\", "+
"nc=2, "+
"nonce=\""+nonce+"\", "+
"response=\""+digest+"\"\r\n"+
"\r\n");
assertThat(response,startsWith("HTTP/1.1 200 OK"));
// once only
digest= digest(nonce,"user","password","/ctx/auth/info","2");
response = _connector.getResponses("GET /ctx/auth/info HTTP/1.0\r\n" +
"Authorization: Digest username=\"user\", qop=auth, cnonce=\"1234567890\", uri=\"/ctx/auth/info\", realm=\"TestRealm\", "+
"nc=2, "+
"nonce=\""+nonce+"\", "+
"response=\""+digest+"\"\r\n"+
"\r\n");
assertThat(response,startsWith("HTTP/1.1 401 Unauthorized"));
// increasing
digest= digest(nonce,"user","password","/ctx/auth/info","4");
response = _connector.getResponses("GET /ctx/auth/info HTTP/1.0\r\n" +
"Authorization: Digest username=\"user\", qop=auth, cnonce=\"1234567890\", uri=\"/ctx/auth/info\", realm=\"TestRealm\", "+
"nc=4, "+
"nonce=\""+nonce+"\", "+
"response=\""+digest+"\"\r\n"+
"\r\n");
assertThat(response,startsWith("HTTP/1.1 200 OK"));
// out of order
digest= digest(nonce,"user","password","/ctx/auth/info","3");
response = _connector.getResponses("GET /ctx/auth/info HTTP/1.0\r\n" +
"Authorization: Digest username=\"user\", qop=auth, cnonce=\"1234567890\", uri=\"/ctx/auth/info\", realm=\"TestRealm\", "+
"nc=3, "+
"nonce=\""+nonce+"\", "+
"response=\""+digest+"\"\r\n"+
"\r\n");
assertThat(response,startsWith("HTTP/1.1 200 OK"));
// stale
digest= digest(nonce,"user","password","/ctx/auth/info","5");
response = _connector.getResponses("GET /ctx/auth/info HTTP/1.0\r\n" +
"Authorization: Digest username=\"user\", qop=auth, cnonce=\"1234567890\", uri=\"/ctx/auth/info\", realm=\"TestRealm\", "+
"nc=5, "+
"nonce=\""+nonce+"\", "+
"response=\""+digest+"\"\r\n"+
"\r\n");
assertThat(response,startsWith("HTTP/1.1 401 Unauthorized"));
assertThat(response,containsString("stale=true"));
}
@Test
public void testFormDispatch() throws Exception
@ -863,32 +996,32 @@ public class ConstraintTest
String response;
response = _connector.getResponses("GET /ctx/data/info HTTP/1.0\r\n\r\n");
assertTrue(response.startsWith("HTTP/1.1 403"));
assertThat(response,startsWith("HTTP/1.1 403"));
_config.setSecurePort(8443);
_config.setSecureScheme("https");
response = _connector.getResponses("GET /ctx/data/info HTTP/1.0\r\n\r\n");
assertTrue(response.startsWith("HTTP/1.1 302 Found"));
assertThat(response,startsWith("HTTP/1.1 302 Found"));
assertTrue(response.indexOf("Location") > 0);
assertTrue(response.indexOf(":8443/ctx/data/info") > 0);
_config.setSecurePort(443);
response = _connector.getResponses("GET /ctx/data/info HTTP/1.0\r\n\r\n");
assertTrue(response.startsWith("HTTP/1.1 302 Found"));
assertThat(response,startsWith("HTTP/1.1 302 Found"));
assertTrue(response.indexOf("Location") > 0);
assertTrue(response.indexOf(":443/ctx/data/info") < 0);
_config.setSecurePort(8443);
response = _connector.getResponses("GET /ctx/data/info HTTP/1.0\r\nHost: wobble.com\r\n\r\n");
assertTrue(response.startsWith("HTTP/1.1 302 Found"));
assertThat(response,startsWith("HTTP/1.1 302 Found"));
assertTrue(response.indexOf("Location") > 0);
assertTrue(response.indexOf("https://wobble.com:8443/ctx/data/info") > 0);
_config.setSecurePort(443);
response = _connector.getResponses("GET /ctx/data/info HTTP/1.0\r\nHost: wobble.com\r\n\r\n");
System.err.println(response);
assertTrue(response.startsWith("HTTP/1.1 302 Found"));
assertThat(response,startsWith("HTTP/1.1 302 Found"));
assertTrue(response.indexOf("Location") > 0);
assertTrue(response.indexOf(":443") < 0);
assertTrue(response.indexOf("https://wobble.com/ctx/data/info") > 0);

View File

@ -33,7 +33,7 @@
</Arg>
<Set name="host"><Property name="jetty.host" /></Set>
<Set name="port"><Property name="jetty.port" default="8080" /></Set>
<Set name="idleTimeout">30000</Set>
<Set name="idleTimeout"><Property name="http.timeout" default="30000"/></Set>
</New>
</Arg>
</Call>

View File

@ -10,12 +10,12 @@
<Arg>
<New class="org.eclipse.jetty.server.LowResourceMonitor">
<Arg name="server"><Ref refid='Server'/></Arg>
<Set name="period">1000</Set>
<Set name="lowResourcesIdleTimeout">200</Set>
<Set name="monitorThreads">true</Set>
<Set name="maxConnections">0</Set>
<Set name="maxMemory">0</Set>
<Set name="maxLowResourcesTime">5000</Set>
<Set name="period"><Property name="lowresources.period" default="1000"/></Set>
<Set name="lowResourcesIdleTimeout"><Property name="lowresources.lowResourcesIdleTimeout" default="200"/></Set>
<Set name="monitorThreads"><Property name="lowresources.monitorThreads" default="true"/></Set>
<Set name="maxConnections"><Property name="lowresources.maxConnections" default="0"/></Set>
<Set name="maxMemory"><Property name="lowresources.maxMemory" default="0"/></Set>
<Set name="maxLowResourcesTime"><Property name="lowresources.maxLowResourcesTime" default="5000"/></Set>
</New>
</Arg>
</Call>

View File

@ -17,9 +17,9 @@
<New id="RequestLogImpl" class="org.eclipse.jetty.server.AsyncNCSARequestLog">
<Set name="filename"><Property name="jetty.logs" default="./logs" />/yyyy_mm_dd.request.log</Set>
<Set name="filenameDateFormat">yyyy_MM_dd</Set>
<Set name="retainDays">90</Set>
<Set name="append">true</Set>
<Set name="extended">true</Set>
<Set name="retainDays"><Property name="requestlog.retain" default="90"/></Set>
<Set name="append"><Property name="requestlog.append" default="false"/></Set>
<Set name="extended"><Property name="requestlog.extended" default="false"/></Set>
<Set name="logCookies">false</Set>
<Set name="LogTimeZone">GMT</Set>
</New>

View File

@ -12,4 +12,7 @@
<Set name="handler"><Ref refid="oldhandler" /></Set>
</New>
</Set>
<Call class="org.eclipse.jetty.server.ConnectorStatistics" name="addToAllConnectors">
<Arg><Ref refid="Server"/></Arg>
</Call>
</Configure>

View File

@ -44,9 +44,9 @@
<!-- =========================================================== -->
<Arg name="threadpool">
<New id="threadpool" class="org.eclipse.jetty.util.thread.QueuedThreadPool">
<Arg name="minThreads" type="int">10</Arg>
<Arg name="maxThreads" type="int">200</Arg>
<Arg name="idleTimeout" type="int">60000</Arg>
<Arg name="minThreads" type="int"><Property name="threads.min" default="10"/></Arg>
<Arg name="maxThreads" type="int"><Property name="threads.max" default="200"/></Arg>
<Arg name="idleTimeout" type="int"><Property name="threads.timeout" default="60000"/></Arg>
<Set name="detailedDump">false</Set>
</New>
</Arg>
@ -124,7 +124,7 @@
<!-- =========================================================== -->
<Set name="stopAtShutdown">true</Set>
<Set name="stopTimeout">5000</Set>
<Set name="dumpAfterStart">false</Set>
<Set name="dumpBeforeStop">false</Set>
<Set name="dumpAfterStart"><Property name="jetty.dump.start" default="false"/></Set>
<Set name="dumpBeforeStop"><Property name="jetty.dump.stop" default="false"/></Set>
</Configure>

View File

@ -0,0 +1,153 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.server;
import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.eclipse.jetty.server.handler.ContextHandler.Context;
import org.eclipse.jetty.util.thread.Scheduler;
public class AsyncContextEvent extends AsyncEvent
{
final private Context _context;
final private AsyncContextState _asyncContext;
volatile HttpChannelState _state;
private ServletContext _dispatchContext;
private String _pathInContext;
private Scheduler.Task _timeoutTask;
private Throwable _throwable;
public AsyncContextEvent(Context context,AsyncContextState asyncContext, HttpChannelState state, Request baseRequest, ServletRequest request, ServletResponse response)
{
super(null,request,response,null);
_context=context;
_asyncContext=asyncContext;
_state=state;
// If we haven't been async dispatched before
if (baseRequest.getAttribute(AsyncContext.ASYNC_REQUEST_URI)==null)
{
// We are setting these attributes during startAsync, when the spec implies that
// they are only available after a call to AsyncContext.dispatch(...);
// have we been forwarded before?
String uri=(String)baseRequest.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
if (uri!=null)
{
baseRequest.setAttribute(AsyncContext.ASYNC_REQUEST_URI,uri);
baseRequest.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH,baseRequest.getAttribute(RequestDispatcher.FORWARD_CONTEXT_PATH));
baseRequest.setAttribute(AsyncContext.ASYNC_SERVLET_PATH,baseRequest.getAttribute(RequestDispatcher.FORWARD_SERVLET_PATH));
baseRequest.setAttribute(AsyncContext.ASYNC_PATH_INFO,baseRequest.getAttribute(RequestDispatcher.FORWARD_PATH_INFO));
baseRequest.setAttribute(AsyncContext.ASYNC_QUERY_STRING,baseRequest.getAttribute(RequestDispatcher.FORWARD_QUERY_STRING));
}
else
{
baseRequest.setAttribute(AsyncContext.ASYNC_REQUEST_URI,baseRequest.getRequestURI());
baseRequest.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH,baseRequest.getContextPath());
baseRequest.setAttribute(AsyncContext.ASYNC_SERVLET_PATH,baseRequest.getServletPath());
baseRequest.setAttribute(AsyncContext.ASYNC_PATH_INFO,baseRequest.getPathInfo());
baseRequest.setAttribute(AsyncContext.ASYNC_QUERY_STRING,baseRequest.getQueryString());
}
}
}
public ServletContext getSuspendedContext()
{
return _context;
}
public Context getContext()
{
return _context;
}
public ServletContext getDispatchContext()
{
return _dispatchContext;
}
public ServletContext getServletContext()
{
return _dispatchContext==null?_context:_dispatchContext;
}
/**
* @return The path in the context
*/
public String getPath()
{
return _pathInContext;
}
public void setTimeoutTask(Scheduler.Task task)
{
_timeoutTask = task;
}
public void cancelTimeoutTask()
{
Scheduler.Task task=_timeoutTask;
_timeoutTask=null;
if (task!=null)
task.cancel();
}
@Override
public AsyncContext getAsyncContext()
{
return _asyncContext;
}
@Override
public Throwable getThrowable()
{
return _throwable;
}
public void setThrowable(Throwable throwable)
{
_throwable=throwable;
}
public void setDispatchTarget(ServletContext context, String path)
{
if (context!=null)
_dispatchContext=context;
if (path!=null)
_pathInContext=path;
}
public void completed()
{
_asyncContext.reset();
}
public HttpChannelState getHttpChannelState()
{
return _state;
}
}

View File

@ -0,0 +1,180 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.server;
import java.io.IOException;
import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class AsyncContextState implements AsyncContext
{
volatile HttpChannelState _state;
public AsyncContextState(HttpChannelState state)
{
_state=state;
}
private HttpChannelState state()
{
HttpChannelState state=_state;
if (state==null)
throw new IllegalStateException("AsyncContext completed");
return state;
}
@Override
public void addListener(final AsyncListener listener, final ServletRequest request, final ServletResponse response)
{
AsyncListener wrap = new AsyncListener()
{
@Override
public void onTimeout(AsyncEvent event) throws IOException
{
listener.onTimeout(new AsyncEvent(event.getAsyncContext(),request,response,event.getThrowable()));
}
@Override
public void onStartAsync(AsyncEvent event) throws IOException
{
listener.onStartAsync(new AsyncEvent(event.getAsyncContext(),request,response,event.getThrowable()));
}
@Override
public void onError(AsyncEvent event) throws IOException
{
listener.onComplete(new AsyncEvent(event.getAsyncContext(),request,response,event.getThrowable()));
}
@Override
public void onComplete(AsyncEvent event) throws IOException
{
listener.onComplete(new AsyncEvent(event.getAsyncContext(),request,response,event.getThrowable()));
}
};
state().addListener(wrap);
}
@Override
public void addListener(AsyncListener listener)
{
state().addListener(listener);
}
@Override
public void complete()
{
state().complete();
}
@Override
public <T extends AsyncListener> T createListener(Class<T> clazz) throws ServletException
{
try
{
return clazz.newInstance();
}
catch(Exception e)
{
throw new ServletException(e);
}
}
@Override
public void dispatch()
{
state().dispatch(null,null);
}
@Override
public void dispatch(String path)
{
state().dispatch(null,path);
}
@Override
public void dispatch(ServletContext context, String path)
{
state().dispatch(context,path);
}
@Override
public ServletRequest getRequest()
{
return state().getAsyncContextEvent().getSuppliedRequest();
}
@Override
public ServletResponse getResponse()
{
return state().getAsyncContextEvent().getSuppliedResponse();
}
@Override
public long getTimeout()
{
return state().getTimeout();
}
@Override
public boolean hasOriginalRequestAndResponse()
{
HttpChannel<?> channel=state().getHttpChannel();
return channel.getRequest()==getRequest() && channel.getResponse()==getResponse();
}
@Override
public void setTimeout(long arg0)
{
state().setTimeout(arg0);
}
@Override
public void start(final Runnable task)
{
state().getHttpChannel().execute(new Runnable()
{
@Override
public void run()
{
state().getAsyncContextEvent().getContext().getContextHandler().handle(task);
}
});
}
public void reset()
{
_state=null;
}
public HttpChannelState getHttpChannelState()
{
return state();
}
}

View File

@ -23,14 +23,22 @@ import java.util.Arrays;
import java.util.concurrent.atomic.AtomicLong;
import org.eclipse.jetty.io.Connection;
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.AbstractLifeCycle;
import org.eclipse.jetty.util.component.Container;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.component.Dumpable;
import org.eclipse.jetty.util.statistic.CounterStatistic;
import org.eclipse.jetty.util.statistic.SampleStatistic;
/* ------------------------------------------------------------ */
/** A Connector.Listener that gathers Connector and Connections Statistics.
* Adding an instance of this class as with {@link AbstractConnector#addBean(Object)}
* will register the listener with all connections accepted by that connector.
*/
@ManagedObject("Connector Statistics")
public class ConnectorStatistics extends AbstractLifeCycle implements Dumpable, Connection.Listener
{
@ -52,78 +60,93 @@ public class ConnectorStatistics extends AbstractLifeCycle implements Dumpable,
connectionClosed(System.currentTimeMillis()-connection.getCreatedTimeStamp(),connection.getMessagesIn(),connection.getMessagesOut());
}
@ManagedAttribute("Total number of bytes received by this connector")
public int getBytesIn()
{
// TODO
return -1;
}
@ManagedAttribute("Total number of bytes sent by this connector")
public int getBytesOut()
{
// TODO
return -1;
}
@ManagedAttribute("Total number of connections seen by this connector")
public int getConnections()
{
return (int)_connectionStats.getTotal();
}
@ManagedAttribute("Connection duraton maximum in ms")
public long getConnectionsDurationMax()
{
return _connectionDurationStats.getMax();
}
@ManagedAttribute("Connection duraton mean in ms")
public double getConnectionsDurationMean()
{
return _connectionDurationStats.getMean();
}
@ManagedAttribute("Connection duraton standard deviation")
public double getConnectionsDurationStdDev()
{
return _connectionDurationStats.getStdDev();
}
@ManagedAttribute("Connection duraton total of all connections in ms")
public long getConnectionsDurationTotal()
{
return _connectionDurationStats.getTotal();
}
public int getConnectionsMessagesInMax()
{
return (int)_messagesIn.getMax();
}
public double getConnectionsMessagesInMean()
{
return _messagesIn.getMean();
}
public double getConnectionsMessagesInStdDev()
{
return _messagesIn.getStdDev();
}
public int getConnectionsOpen()
{
return (int)_connectionStats.getCurrent();
}
public int getConnectionsOpenMax()
{
return (int)_connectionStats.getMax();
}
@ManagedAttribute("Messages In for all connections")
public int getMessagesIn()
{
return (int)_messagesIn.getTotal();
}
@ManagedAttribute("Messages In per connection maximum")
public int getConnectionsMessagesInMax()
{
return (int)_messagesIn.getMax();
}
@ManagedAttribute("Messages In per connection mean")
public double getConnectionsMessagesInMean()
{
return _messagesIn.getMean();
}
@ManagedAttribute("Messages In per connection standard deviation")
public double getConnectionsMessagesInStdDev()
{
return _messagesIn.getStdDev();
}
@ManagedAttribute("Connections open")
public int getConnectionsOpen()
{
return (int)_connectionStats.getCurrent();
}
@ManagedAttribute("Connections open maximum")
public int getConnectionsOpenMax()
{
return (int)_connectionStats.getMax();
}
@ManagedAttribute("Messages Out for all connections")
public int getMessagesOut()
{
return (int)_messagesIn.getTotal();
}
@ManagedAttribute("Connection statistics started ms since epoch")
public long getStartedMillis()
{
long start = _startMillis.get();
@ -141,6 +164,7 @@ public class ConnectorStatistics extends AbstractLifeCycle implements Dumpable,
{
}
@ManagedOperation("Reset the statistics")
public void reset()
{
_startMillis.set(System.currentTimeMillis());
@ -178,7 +202,6 @@ public class ConnectorStatistics extends AbstractLifeCycle implements Dumpable,
}
}
@Override
@ManagedOperation("dump thread state")
public String dump()
@ -192,4 +215,13 @@ public class ConnectorStatistics extends AbstractLifeCycle implements Dumpable,
ContainerLifeCycle.dumpObject(out,this);
ContainerLifeCycle.dump(out,indent,Arrays.asList(new String[]{"connections="+_connectionStats,"duration="+_connectionDurationStats,"in="+_messagesIn,"out="+_messagesOut}));
}
public static void addToAllConnectors(Server server)
{
for (Connector connector : server.getConnectors())
{
if (connector instanceof Container)
((Container)connector).addBean(new ConnectorStatistics());
}
}
}

View File

@ -40,8 +40,10 @@ import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.ChannelEndPoint;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.EofException;
import org.eclipse.jetty.server.HttpChannelState.Next;
import org.eclipse.jetty.server.handler.ErrorHandler;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.URIUtil;
@ -215,6 +217,15 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
@Override
public void run()
{
handle();
}
/* ------------------------------------------------------------ */
/**
* @return True if the channel is ready to continue handling (ie it is not suspended)
*/
public boolean handle()
{
LOG.debug("{} handle enter", this);
@ -227,104 +238,104 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
Thread.currentThread().setName(threadName + " - " + _uri);
}
try
// Loop here to handle async request redispatches.
// The loop is controlled by the call to async.unhandle in the
// finally block below. Unhandle will return false only if an async dispatch has
// already happened when unhandle is called.
HttpChannelState.Next next = _state.handling();
while (next==Next.CONTINUE && getServer().isRunning())
{
// Loop here to handle async request redispatches.
// The loop is controlled by the call to async.unhandle in the
// finally block below. Unhandle will return false only if an async dispatch has
// already happened when unhandle is called.
boolean handling = _state.handling();
while (handling && getServer().isRunning())
try
{
try
{
_request.setHandled(false);
_response.getHttpOutput().reopen();
_request.setHandled(false);
_response.getHttpOutput().reopen();
if (_state.isInitial())
if (_state.isInitial())
{
_request.setTimeStamp(System.currentTimeMillis());
_request.setDispatcherType(DispatcherType.REQUEST);
for (HttpConfiguration.Customizer customizer : _configuration.getCustomizers())
customizer.customize(getConnector(),_configuration,_request);
getServer().handle(this);
}
else
{
if (_request.getHttpChannelState().isExpired())
{
_request.setTimeStamp(System.currentTimeMillis());
_request.setDispatcherType(DispatcherType.REQUEST);
for (HttpConfiguration.Customizer customizer : _configuration.getCustomizers())
customizer.customize(getConnector(),_configuration,_request);
getServer().handle(this);
_request.setDispatcherType(DispatcherType.ERROR);
_request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,new Integer(500));
_request.setAttribute(RequestDispatcher.ERROR_MESSAGE,"Async Timeout");
_request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI,_request.getRequestURI());
_response.setStatusWithReason(500,"Async Timeout");
}
else
{
if (_request.getHttpChannelState().isExpired())
{
_request.setDispatcherType(DispatcherType.ERROR);
_request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,new Integer(500));
_request.setAttribute(RequestDispatcher.ERROR_MESSAGE,"Async Timeout");
_request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI,_request.getRequestURI());
_response.setStatusWithReason(500,"Async Timeout");
}
else
_request.setDispatcherType(DispatcherType.ASYNC);
getServer().handleAsync(this);
}
}
catch (Error e)
{
if ("ContinuationThrowable".equals(e.getClass().getSimpleName()))
LOG.ignore(e);
else
throw e;
}
catch (Exception e)
{
if (e instanceof EofException)
LOG.debug(e);
else
LOG.warn(String.valueOf(_uri), e);
_state.error(e);
_request.setHandled(true);
handleException(e);
}
finally
{
handling = !_state.unhandle();
_request.setDispatcherType(DispatcherType.ASYNC);
getServer().handleAsync(this);
}
}
}
finally
{
if (threadName != null && LOG.isDebugEnabled())
Thread.currentThread().setName(threadName);
setCurrentHttpChannel(null);
if (_state.isCompleting())
catch (Error e)
{
try
{
_state.completed();
if (!_response.isCommitted() && !_request.isHandled())
_response.sendError(404);
// Complete generating the response
_response.complete();
}
catch(EofException e)
{
if ("ContinuationThrowable".equals(e.getClass().getSimpleName()))
LOG.ignore(e);
else
throw e;
}
catch (Exception e)
{
if (e instanceof EofException)
LOG.debug(e);
}
catch(Exception e)
{
LOG.warn(e);
}
finally
{
_request.setHandled(true);
_transport.completed();
}
else
LOG.warn(String.valueOf(_uri), e);
_state.error(e);
_request.setHandled(true);
handleException(e);
}
finally
{
next = _state.unhandle();
}
LOG.debug("{} handle exit", this);
}
if (threadName != null && LOG.isDebugEnabled())
Thread.currentThread().setName(threadName);
setCurrentHttpChannel(null);
if (next==Next.COMPLETE)
{
try
{
_state.completed();
if (!_response.isCommitted() && !_request.isHandled())
_response.sendError(404);
// Complete generating the response
_response.complete();
}
catch(EofException e)
{
LOG.debug(e);
}
catch(Exception e)
{
LOG.warn(e);
}
finally
{
next=Next.RECYCLE;
}
}
if (next==Next.RECYCLE)
{
_request.setHandled(true);
_transport.completed();
}
LOG.debug("{} handle exit", this);
return next!=Next.WAIT;
}
/**
@ -380,11 +391,13 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
@Override
public String toString()
{
return String.format("%s@%x{r=%s,a=%s}",
return String.format("%s@%x{r=%s,a=%s,uri=%s}",
getClass().getSimpleName(),
hashCode(),
_requests,
_state.getState());
_state.getState(),
_state.getState()==HttpChannelState.State.IDLE?"-":_request.getRequestURI()
);
}
@Override
@ -555,10 +568,9 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
}
@Override
public boolean earlyEOF()
public void earlyEOF()
{
_request.getHttpInput().earlyEOF();
return false;
}
@Override
@ -569,10 +581,9 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
try
{
if (_state.handling())
if (_state.handling()==Next.CONTINUE)
{
commitResponse(new ResponseInfo(HttpVersion.HTTP_1_1,new HttpFields(),0,status,reason,false),null,true);
_state.unhandle();
}
}
catch (IOException e)
@ -580,8 +591,9 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
LOG.warn(e);
}
finally
{
_state.completed();
{
if (_state.unhandle()==Next.COMPLETE)
_state.completed();
}
}
@ -664,4 +676,13 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
{
return _connector.getScheduler();
}
/* ------------------------------------------------------------ */
/**
* @return true if the HttpChannel can efficiently use direct buffer (typically this means it is not over SSL or a multiplexed protocol)
*/
public boolean useDirectBuffers()
{
return getEndPoint() instanceof ChannelEndPoint;
}
}

View File

@ -21,19 +21,15 @@ package org.eclipse.jetty.server;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandler.Context;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.Scheduler;
@ -56,7 +52,7 @@ import org.eclipse.jetty.util.thread.Scheduler;
* <tr><th align=right>COMPLETED:</th> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td></tr>
* </table>
*/
public class HttpChannelState implements AsyncContext
public class HttpChannelState
{
private static final Logger LOG = Log.getLogger(HttpChannelState.class);
@ -75,6 +71,14 @@ public class HttpChannelState implements AsyncContext
COMPLETING, // Request is completable
COMPLETED // Request is complete
}
public enum Next
{
CONTINUE, // Continue handling the channel
WAIT, // Wait for further events
COMPLETE, // Complete the channel
RECYCLE, // Channel is completed
}
private final HttpChannel<?> _channel;
private List<AsyncListener> _lastAsyncListeners;
@ -86,7 +90,7 @@ public class HttpChannelState implements AsyncContext
private boolean _expired;
private volatile boolean _responseWrapped;
private long _timeoutMs=DEFAULT_TIMEOUT;
private AsyncEventState _event;
private AsyncContextEvent _event;
protected HttpChannelState(HttpChannel<?> channel)
{
@ -103,7 +107,6 @@ public class HttpChannelState implements AsyncContext
}
}
@Override
public void addListener(AsyncListener listener)
{
synchronized(this)
@ -114,19 +117,6 @@ public class HttpChannelState implements AsyncContext
}
}
@Override
public void addListener(AsyncListener listener,ServletRequest request, ServletResponse response)
{
synchronized(this)
{
if (_asyncListeners==null)
_asyncListeners=new ArrayList<>();
_asyncListeners.add(listener);
}
}
@Override
public void setTimeout(long ms)
{
synchronized(this)
@ -135,7 +125,6 @@ public class HttpChannelState implements AsyncContext
}
}
@Override
public long getTimeout()
{
synchronized(this)
@ -144,7 +133,7 @@ public class HttpChannelState implements AsyncContext
}
}
public AsyncEventState getAsyncEventState()
public AsyncContextEvent getAsyncContextEvent()
{
synchronized(this)
{
@ -173,9 +162,9 @@ public class HttpChannelState implements AsyncContext
}
/**
* @return true if the handling of the request should proceed
* @return Next handling of the request should proceed
*/
protected boolean handling()
protected Next handling()
{
synchronized (this)
{
@ -197,12 +186,16 @@ public class HttpChannelState implements AsyncContext
case COMPLETECALLED:
_state=State.COMPLETING;
return false;
return Next.COMPLETE;
case ASYNCWAIT:
case COMPLETING:
return Next.COMPLETE;
case ASYNCWAIT:
return Next.WAIT;
case COMPLETED:
return false;
return Next.RECYCLE;
case REDISPATCH:
_state=State.REDISPATCHED;
@ -213,12 +206,13 @@ public class HttpChannelState implements AsyncContext
}
_responseWrapped=false;
return true;
return Next.CONTINUE;
}
}
public void startAsync()
public void startAsync(AsyncContextEvent event)
{
synchronized (this)
{
@ -228,56 +222,15 @@ public class HttpChannelState implements AsyncContext
case REDISPATCHED:
_dispatched=false;
_expired=false;
_responseWrapped=event.getSuppliedResponse()!=_channel.getResponse();
_responseWrapped=false;
_event=new AsyncEventState(_channel.getRequest().getServletContext(),_channel.getRequest(),_channel.getResponse());
_event=event;
_state=State.ASYNCSTARTED;
List<AsyncListener> listeners=_lastAsyncListeners;
_lastAsyncListeners=_asyncListeners;
if (listeners!=null)
listeners.clear();
_asyncListeners=listeners;
if (_asyncListeners!=null)
_asyncListeners.clear();
break;
default:
throw new IllegalStateException(this.getStatusString());
}
}
if (_lastAsyncListeners!=null)
{
for (AsyncListener listener : _lastAsyncListeners)
{
try
{
listener.onStartAsync(_event);
}
catch(Exception e)
{
LOG.warn(e);
}
}
}
}
public void startAsync(final ServletContext context,final ServletRequest request,final ServletResponse response)
{
synchronized (this)
{
switch(_state)
{
case DISPATCHED:
case REDISPATCHED:
_dispatched=false;
_expired=false;
_responseWrapped=response!=_channel.getResponse();
_event=new AsyncEventState(context,request,response);
_event._pathInContext = (request instanceof HttpServletRequest)?URIUtil.addPaths(((HttpServletRequest)request).getServletPath(),((HttpServletRequest)request).getPathInfo()):null;
_state=State.ASYNCSTARTED;
List<AsyncListener> listeners=_lastAsyncListeners;
_lastAsyncListeners=_asyncListeners;
_asyncListeners=listeners;
if (_asyncListeners!=null)
_asyncListeners.clear();
break;
default:
@ -306,7 +259,7 @@ public class HttpChannelState implements AsyncContext
synchronized (this)
{
if (_event!=null)
_event._cause=th;
_event.setThrowable(th);
}
}
@ -314,10 +267,10 @@ public class HttpChannelState implements AsyncContext
* Signal that the HttpConnection has finished handling the request.
* For blocking connectors, this call may block if the request has
* been suspended (startAsync called).
* @return true if handling is complete, false if the request should
* @return next actions
* be handled again (eg because of a resume that happened before unhandle was called)
*/
protected boolean unhandle()
protected Next unhandle()
{
synchronized (this)
{
@ -326,7 +279,7 @@ public class HttpChannelState implements AsyncContext
case REDISPATCHED:
case DISPATCHED:
_state=State.COMPLETING;
return true;
return Next.COMPLETE;
case IDLE:
throw new IllegalStateException(this.getStatusString());
@ -335,26 +288,17 @@ public class HttpChannelState implements AsyncContext
_initial=false;
_state=State.ASYNCWAIT;
scheduleTimeout();
if (_state==State.ASYNCWAIT)
return true;
else if (_state==State.COMPLETECALLED)
{
_state=State.COMPLETING;
return true;
}
_initial=false;
_state=State.REDISPATCHED;
return false;
return Next.WAIT;
case REDISPATCHING:
_initial=false;
_state=State.REDISPATCHED;
return false;
return Next.CONTINUE;
case COMPLETECALLED:
_initial=false;
_state=State.COMPLETING;
return true;
return Next.COMPLETE;
default:
throw new IllegalStateException(this.getStatusString());
@ -362,26 +306,29 @@ public class HttpChannelState implements AsyncContext
}
}
@Override
public void dispatch()
public void dispatch(ServletContext context, String path)
{
boolean dispatch;
synchronized (this)
{
switch(_state)
{
case ASYNCSTARTED:
_state=State.REDISPATCHING;
_event.setDispatchTarget(context,path);
_dispatched=true;
return;
case ASYNCWAIT:
dispatch=!_expired;
_state=State.REDISPATCH;
_event.setDispatchTarget(context,path);
_dispatched=true;
break;
case REDISPATCH:
_event.setDispatchTarget(context,path);
return;
default:
@ -407,6 +354,7 @@ public class HttpChannelState implements AsyncContext
protected void expired()
{
final List<AsyncListener> aListeners;
AsyncEvent event;
synchronized (this)
{
switch(_state)
@ -414,6 +362,7 @@ public class HttpChannelState implements AsyncContext
case ASYNCSTARTED:
case ASYNCWAIT:
_expired=true;
event=_event;
aListeners=_asyncListeners;
break;
default:
@ -427,7 +376,7 @@ public class HttpChannelState implements AsyncContext
{
try
{
listener.onTimeout(_event);
listener.onTimeout(event);
}
catch(Exception e)
{
@ -453,11 +402,10 @@ public class HttpChannelState implements AsyncContext
scheduleDispatch();
}
@Override
public void complete()
{
// just like resume, except don't set _dispatched=true;
boolean dispatch;
boolean handle;
synchronized (this)
{
switch(_state)
@ -473,7 +421,7 @@ public class HttpChannelState implements AsyncContext
case ASYNCWAIT:
_state=State.COMPLETECALLED;
dispatch=!_expired;
handle=!_expired;
break;
default:
@ -481,29 +429,21 @@ public class HttpChannelState implements AsyncContext
}
}
if (dispatch)
if (handle)
{
cancelTimeout();
scheduleDispatch();
}
}
@Override
public <T extends AsyncListener> T createListener(Class<T> clazz) throws ServletException
{
try
{
return clazz.newInstance();
}
catch(Exception e)
{
throw new ServletException(e);
ContextHandler handler=getContextHandler();
if (handler!=null)
handler.handle(_channel);
else
_channel.handle();
}
}
protected void completed()
{
final List<AsyncListener> aListeners;
final AsyncContextEvent event;
synchronized (this)
{
switch(_state)
@ -511,6 +451,8 @@ public class HttpChannelState implements AsyncContext
case COMPLETING:
_state=State.COMPLETED;
aListeners=_asyncListeners;
event=_event;
_event=null;
break;
default:
@ -524,14 +466,14 @@ public class HttpChannelState implements AsyncContext
{
try
{
if (_event!=null && _event._cause!=null)
if (event!=null && event.getThrowable()!=null)
{
_event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,_event._cause);
_event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_MESSAGE,_event._cause.getMessage());
listener.onError(_event);
event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,event.getThrowable());
event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_MESSAGE,event.getThrowable().getMessage());
listener.onError(event);
}
else
listener.onComplete(_event);
listener.onComplete(event);
}
catch(Exception e)
{
@ -539,6 +481,9 @@ public class HttpChannelState implements AsyncContext
}
}
}
if (event!=null)
event.completed();
}
protected void recycle()
@ -563,14 +508,6 @@ public class HttpChannelState implements AsyncContext
}
}
public void cancel()
{
synchronized (this)
{
cancelTimeout();
}
}
protected void scheduleDispatch()
{
_channel.execute(_channel);
@ -580,18 +517,14 @@ public class HttpChannelState implements AsyncContext
{
Scheduler scheduler = _channel.getScheduler();
if (scheduler!=null && _timeoutMs>0)
_event._timeout=scheduler.schedule(new AsyncTimeout(),_timeoutMs,TimeUnit.MILLISECONDS);
_event.setTimeoutTask(scheduler.schedule(new AsyncTimeout(),_timeoutMs,TimeUnit.MILLISECONDS));
}
protected void cancelTimeout()
{
AsyncEventState event=_event;
AsyncContextEvent event=_event;
if (event!=null)
{
Scheduler.Task task=event._timeout;
if (task!=null)
task.cancel();
}
event.cancelTimeoutTask();
}
public boolean isExpired()
@ -656,73 +589,25 @@ public class HttpChannelState implements AsyncContext
}
}
@Override
public void dispatch(ServletContext context, String path)
{
_event._dispatchContext=context;
_event._pathInContext=path;
dispatch();
}
@Override
public void dispatch(String path)
{
_event._pathInContext=path;
dispatch();
}
public Request getBaseRequest()
{
return _channel.getRequest();
}
@Override
public ServletRequest getRequest()
public HttpChannel<?> getHttpChannel()
{
if (_event!=null)
return _event.getSuppliedRequest();
return _channel.getRequest();
}
@Override
public ServletResponse getResponse()
{
if (_responseWrapped && _event!=null && _event.getSuppliedResponse()!=null)
return _event.getSuppliedResponse();
return _channel.getResponse();
}
@Override
public void start(final Runnable run)
{
final AsyncEventState event=_event;
if (event!=null)
{
_channel.execute(new Runnable()
{
@Override
public void run()
{
((Context)event.getServletContext()).getContextHandler().handle(run);
}
});
}
}
@Override
public boolean hasOriginalRequestAndResponse()
{
synchronized (this)
{
return (_event!=null && _event.getSuppliedRequest()==_channel.getRequest() && _event.getSuppliedResponse()==_channel.getResponse());
}
return _channel;
}
public ContextHandler getContextHandler()
{
final AsyncEventState event=_event;
final AsyncContextEvent event=_event;
if (event!=null)
return ((Context)event.getServletContext()).getContextHandler();
{
Context context=((Context)event.getServletContext());
if (context!=null)
return context.getContextHandler();
}
return null;
}
@ -757,70 +642,4 @@ public class HttpChannelState implements AsyncContext
}
}
public class AsyncEventState extends AsyncEvent
{
final private ServletContext _suspendedContext;
private String _pathInContext;
private Scheduler.Task _timeout;
private ServletContext _dispatchContext;
private Throwable _cause;
public AsyncEventState(ServletContext context, ServletRequest request, ServletResponse response)
{
super(HttpChannelState.this, request,response);
_suspendedContext=context;
// Get the base request So we can remember the initial paths
Request r=_channel.getRequest();
// If we haven't been async dispatched before
if (r.getAttribute(AsyncContext.ASYNC_REQUEST_URI)==null)
{
// We are setting these attributes during startAsync, when the spec implies that
// they are only available after a call to AsyncContext.dispatch(...);
// have we been forwarded before?
String uri=(String)r.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
if (uri!=null)
{
r.setAttribute(AsyncContext.ASYNC_REQUEST_URI,uri);
r.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH,r.getAttribute(RequestDispatcher.FORWARD_CONTEXT_PATH));
r.setAttribute(AsyncContext.ASYNC_SERVLET_PATH,r.getAttribute(RequestDispatcher.FORWARD_SERVLET_PATH));
r.setAttribute(AsyncContext.ASYNC_PATH_INFO,r.getAttribute(RequestDispatcher.FORWARD_PATH_INFO));
r.setAttribute(AsyncContext.ASYNC_QUERY_STRING,r.getAttribute(RequestDispatcher.FORWARD_QUERY_STRING));
}
else
{
r.setAttribute(AsyncContext.ASYNC_REQUEST_URI,r.getRequestURI());
r.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH,r.getContextPath());
r.setAttribute(AsyncContext.ASYNC_SERVLET_PATH,r.getServletPath());
r.setAttribute(AsyncContext.ASYNC_PATH_INFO,r.getPathInfo());
r.setAttribute(AsyncContext.ASYNC_QUERY_STRING,r.getQueryString());
}
}
}
public ServletContext getSuspendedContext()
{
return _suspendedContext;
}
public ServletContext getDispatchContext()
{
return _dispatchContext;
}
public ServletContext getServletContext()
{
return _dispatchContext==null?_suspendedContext:_dispatchContext;
}
/**
* @return The path in the context
*/
public String getPath()
{
return _pathInContext;
}
}
}

View File

@ -207,8 +207,28 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
// Can the parser progress (even with an empty buffer)
boolean call_channel=_parser.parseNext(_requestBuffer==null?BufferUtil.EMPTY_BUFFER:_requestBuffer);
// If there is a request buffer, we are re-entering here
if (!call_channel && BufferUtil.isEmpty(_requestBuffer))
// Parse the buffer
if (call_channel)
{
// Parse as much content as there is available before calling the channel
// this is both efficient (may queue many chunks), will correctly set available for 100 continues
// and will drive the parser to completion if all content is available.
while (_parser.inContentState())
{
if (!_parser.parseNext(_requestBuffer==null?BufferUtil.EMPTY_BUFFER:_requestBuffer))
break;
}
// The parser returned true, which indicates the channel is ready to handle a request.
// Call the channel and this will either handle the request/response to completion OR,
// if the request suspends, the request/response will be incomplete so the outer loop will exit.
boolean handle=_channel.handle();
// Return if suspended or upgraded
if (!handle || getEndPoint().getConnection()!=this)
return;
}
else if (BufferUtil.isEmpty(_requestBuffer))
{
if (_requestBuffer == null)
_requestBuffer = _bufferPool.acquire(getInputBufferSize(), REQUEST_BUFFER_DIRECT);
@ -242,32 +262,14 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
releaseRequestBuffer();
return;
}
// Parse what we have read
call_channel=_parser.parseNext(_requestBuffer);
}
// Parse the buffer
if (call_channel)
else
{
// Parse as much content as there is available before calling the channel
// this is both efficient (may queue many chunks), will correctly set available for 100 continues
// and will drive the parser to completion if all content is available.
while (_parser.inContentState())
{
if (!_parser.parseNext(_requestBuffer==null?BufferUtil.EMPTY_BUFFER:_requestBuffer))
break;
}
// The parser returned true, which indicates the channel is ready to handle a request.
// Call the channel and this will either handle the request/response to completion OR,
// if the request suspends, the request/response will be incomplete so the outer loop will exit.
_channel.run();
// Return if suspended or upgraded
if (_channel.getState().isSuspended() || getEndPoint().getConnection()!=this)
return;
// TODO work out how we can get here and a better way to handle it
LOG.warn("Unexpected state: "+this+ " "+_channel+" "+_channel.getRequest());
if (!_channel.getState().isSuspended())
getEndPoint().close();
return;
}
}
}
@ -275,7 +277,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
{
LOG.debug(e);
}
catch (IOException e)
catch (Exception e)
{
if (_parser.isIdle())
LOG.debug(e);
@ -283,11 +285,6 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
LOG.warn(this.toString(), e);
close();
}
catch (Exception e)
{
LOG.warn(this.toString(), e);
close();
}
finally
{
setCurrentConnection(null);
@ -511,13 +508,6 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
getEndPoint().close();
}
}
// make sure that an oshut connection is driven towards close
// TODO this is a little ugly
if (getEndPoint().isOpen() && getEndPoint().isOutputShutdown())
{
fillInterested();
}
}
}
@ -559,11 +549,12 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
if (getEndPoint().isInputShutdown())
{
_parser.shutdownInput();
shutdown();
return;
}
// Wait until we can read
getEndPoint().fillInterested(_readBlocker);
block(_readBlocker);
LOG.debug("{} block readable on {}",this,_readBlocker);
_readBlocker.block();
@ -618,7 +609,6 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
*/
}
@Override
protected void onAllContentConsumed()
{
@ -628,6 +618,12 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
*/
releaseRequestBuffer();
}
@Override
public String toString()
{
return super.toString()+"{"+_channel+","+HttpConnection.this+"}";
}
}
private class HttpChannelOverHttp extends HttpChannel<ByteBuffer>

View File

@ -96,32 +96,53 @@ public abstract class HttpInput<T> extends ServletInputStream
T item = null;
synchronized (lock())
{
while (item == null)
// Get the current head of the input Q
item = _inputQ.peekUnsafe();
// Skip empty items at the head of the queue
while (item != null && remaining(item) == 0)
{
_inputQ.pollUnsafe();
onContentConsumed(item);
LOG.debug("{} consumed {}", this, item);
item = _inputQ.peekUnsafe();
while (item != null && remaining(item) == 0)
// If that was the last item then notify
if (item==null)
onAllContentConsumed();
}
// If we have no item
if (item == null)
{
// Was it unexpectedly EOF'd?
if (isEarlyEOF())
throw new EofException();
// check for EOF
if (isShutdown())
{
_inputQ.pollUnsafe();
onContentConsumed(item);
LOG.debug("{} consumed {}", this, item);
item = _inputQ.peekUnsafe();
onEOF();
return -1;
}
if (item == null)
// OK then block for some more content
blockForContent();
// If still not content, we must be closed
item = _inputQ.peekUnsafe();
if (item==null)
{
onAllContentConsumed();
if (isEarlyEOF())
throw new EofException();
// blockForContent will only return with no
// content if it is closed.
if (!isShutdown())
LOG.warn("Unexpected !EOF: "+this);
// check for EOF
if (isShutdown())
{
onEOF();
return -1;
}
blockForContent();
onEOF();
return -1;
}
}
}
@ -152,20 +173,34 @@ public abstract class HttpInput<T> extends ServletInputStream
}
}
/* ------------------------------------------------------------ */
/** Called by this HttpInput to signal new content has been queued
* @param item
*/
protected void onContentQueued(T item)
{
lock().notify();
}
/* ------------------------------------------------------------ */
/** Called by this HttpInput to signal all available content has been consumed
*/
protected void onAllContentConsumed()
{
}
/* ------------------------------------------------------------ */
/** Called by this HttpInput to signal it has reached EOF
*/
protected void onEOF()
{
}
public boolean content(T item)
/* ------------------------------------------------------------ */
/** Add some content to the input stream
* @param item
*/
public void content(T item)
{
synchronized (lock())
{
@ -176,19 +211,26 @@ public abstract class HttpInput<T> extends ServletInputStream
onContentQueued(item);
LOG.debug("{} queued {}", this, item);
}
return true;
}
/* ------------------------------------------------------------ */
/** This method should be called to signal to the HttpInput
* that an EOF has arrived before all the expected content.
* Typically this will result in an EOFException being thrown
* from a subsequent read rather than a -1 return.
*/
public void earlyEOF()
{
synchronized (lock())
{
_earlyEOF = true;
_inputEOF = true;
lock().notify();
LOG.debug("{} early EOF", this);
}
}
/* ------------------------------------------------------------ */
public boolean isEarlyEOF()
{
synchronized (lock())
@ -197,6 +239,7 @@ public abstract class HttpInput<T> extends ServletInputStream
}
}
/* ------------------------------------------------------------ */
public void shutdown()
{
synchronized (lock())
@ -207,6 +250,7 @@ public abstract class HttpInput<T> extends ServletInputStream
}
}
/* ------------------------------------------------------------ */
public boolean isShutdown()
{
synchronized (lock())
@ -215,13 +259,14 @@ public abstract class HttpInput<T> extends ServletInputStream
}
}
/* ------------------------------------------------------------ */
public void consumeAll()
{
synchronized (lock())
{
T item = _inputQ.peekUnsafe();
while (!isShutdown() && !isEarlyEOF())
{
T item = _inputQ.peekUnsafe();
while (item != null)
{
_inputQ.pollUnsafe();
@ -235,6 +280,9 @@ public abstract class HttpInput<T> extends ServletInputStream
try
{
blockForContent();
item = _inputQ.peekUnsafe();
if (item==null)
break;
}
catch (IOException e)
{

View File

@ -47,9 +47,6 @@ import org.eclipse.jetty.util.resource.Resource;
*/
public class HttpOutput extends ServletOutputStream
{
private static final boolean OUTPUT_BUFFER_DIRECT=false;
private static final boolean CHANNEL_BUFFER_DIRECT=true;
private static final boolean STREAM_BUFFER_DIRECT=false;
private static Logger LOG = Log.getLogger(HttpOutput.class);
private final HttpChannel<?> _channel;
private boolean _closed;
@ -165,8 +162,9 @@ public class HttpOutput extends ServletOutputStream
return;
}
// Allocate an aggregate buffer
_aggregate = _channel.getByteBufferPool().acquire(size, OUTPUT_BUFFER_DIRECT);
// Allocate an aggregate buffer.
// Never direct as it is slow to do little writes to a direct buffer.
_aggregate = _channel.getByteBufferPool().acquire(size, false);
}
// Do we have space to aggregate ?
@ -206,8 +204,10 @@ public class HttpOutput extends ServletOutputStream
if (isClosed())
throw new EOFException("Closed");
// Allocate an aggregate buffer.
// Never direct as it is slow to do little writes to a direct buffer.
if (_aggregate == null)
_aggregate = _channel.getByteBufferPool().acquire(getBufferSize(), OUTPUT_BUFFER_DIRECT);
_aggregate = _channel.getByteBufferPool().acquire(getBufferSize(), false);
BufferUtil.append(_aggregate, (byte)b);
_written++;
@ -256,7 +256,7 @@ public class HttpOutput extends ServletOutputStream
if (etag!=null)
response.getHttpFields().put(HttpHeader.ETAG,etag);
content = httpContent.getDirectBuffer();
content = _channel.useDirectBuffers()?httpContent.getDirectBuffer():null;
if (content == null)
content = httpContent.getIndirectBuffer();
if (content == null)
@ -268,7 +268,7 @@ public class HttpOutput extends ServletOutputStream
{
Resource resource = (Resource)content;
_channel.getResponse().getHttpFields().putDateField(HttpHeader.LAST_MODIFIED, resource.lastModified());
content = resource.getInputStream();
content=resource.getInputStream(); // Closed below
}
// Process content.
@ -279,9 +279,8 @@ public class HttpOutput extends ServletOutputStream
}
else if (content instanceof ReadableByteChannel)
{
ReadableByteChannel channel = (ReadableByteChannel)content;
ByteBuffer buffer = _channel.getByteBufferPool().acquire(getBufferSize(), CHANNEL_BUFFER_DIRECT);
try
ByteBuffer buffer = _channel.getByteBufferPool().acquire(getBufferSize(), _channel.useDirectBuffers());
try (ReadableByteChannel channel = (ReadableByteChannel)content;)
{
while(channel.isOpen())
{
@ -301,12 +300,12 @@ public class HttpOutput extends ServletOutputStream
}
else if (content instanceof InputStream)
{
InputStream in = (InputStream)content;
ByteBuffer buffer = _channel.getByteBufferPool().acquire(getBufferSize(), STREAM_BUFFER_DIRECT);
// allocate non direct buffer so array may be directly accessed.
ByteBuffer buffer = _channel.getByteBufferPool().acquire(getBufferSize(), false);
byte[] array = buffer.array();
int offset=buffer.arrayOffset();
int size=array.length-offset;
try
try (InputStream in = (InputStream)content;)
{
while(true)
{

View File

@ -81,7 +81,7 @@ public class InclusiveByteRange
* @param size Size of the resource.
* @return LazyList of satisfiable ranges
*/
public static List satisfiableRanges(Enumeration headers, long size)
public static List<InclusiveByteRange> satisfiableRanges(Enumeration headers, long size)
{
Object satRanges=null;

View File

@ -62,6 +62,7 @@ import org.eclipse.jetty.http.HttpCookie;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.HttpVersion;
@ -114,9 +115,9 @@ import org.eclipse.jetty.util.log.Logger;
*/
public class Request implements HttpServletRequest
{
public static final String __MULTIPART_CONFIG_ELEMENT = "org.eclipse.multipartConfig";
public static final String __MULTIPART_INPUT_STREAM = "org.eclipse.multiPartInputStream";
public static final String __MULTIPART_CONTEXT = "org.eclipse.multiPartContext";
public static final String __MULTIPART_CONFIG_ELEMENT = "org.eclipse.jetty.multipartConfig";
public static final String __MULTIPART_INPUT_STREAM = "org.eclipse.jetty.multiPartInputStream";
public static final String __MULTIPART_CONTEXT = "org.eclipse.jetty.multiPartContext";
private static final Logger LOG = Log.getLogger(Request.class);
private static final Collection<Locale> __defaultLocale = Collections.singleton(Locale.getDefault());
@ -203,7 +204,8 @@ public class Request implements HttpServletRequest
private long _dispatchTime;
private HttpURI _uri;
private MultiPartInputStreamParser _multiPartInputStream; //if the request is a multi-part mime
private AsyncContextState _async;
/* ------------------------------------------------------------ */
public Request(HttpChannel<?> channel, HttpInput<?> input)
{
@ -369,10 +371,11 @@ public class Request implements HttpServletRequest
@Override
public AsyncContext getAsyncContext()
{
HttpChannelState continuation = getHttpChannelState();
if (continuation.isInitial() && !continuation.isAsync())
throw new IllegalStateException(continuation.getStatusString());
return continuation;
HttpChannelState state = getHttpChannelState();
if (_async==null || state.isInitial() && !state.isAsync())
throw new IllegalStateException(state.getStatusString());
return _async;
}
/* ------------------------------------------------------------ */
@ -1027,19 +1030,8 @@ public class Request implements HttpServletRequest
@Override
public StringBuffer getRequestURL()
{
final StringBuffer url = new StringBuffer(48);
String scheme = getScheme();
int port = getServerPort();
url.append(scheme);
url.append("://");
url.append(getServerName());
if (_port > 0 && ((scheme.equalsIgnoreCase(URIUtil.HTTP) && port != 80) || (scheme.equalsIgnoreCase(URIUtil.HTTPS) && port != 443)))
{
url.append(':');
url.append(_port);
}
final StringBuffer url = new StringBuffer(128);
URIUtil.appendSchemeHostPort(url,getScheme(),getServerName(),getServerPort());
url.append(getRequestURI());
return url;
}
@ -1063,19 +1055,8 @@ public class Request implements HttpServletRequest
*/
public StringBuilder getRootURL()
{
StringBuilder url = new StringBuilder(48);
String scheme = getScheme();
int port = getServerPort();
url.append(scheme);
url.append("://");
url.append(getServerName());
if (port > 0 && ((scheme.equalsIgnoreCase("http") && port != 80) || (scheme.equalsIgnoreCase("https") && port != 443)))
{
url.append(':');
url.append(port);
}
StringBuilder url = new StringBuilder(128);
URIUtil.appendSchemeHostPort(url,getScheme(),getServerName(),getServerPort());
return url;
}
@ -1105,41 +1086,58 @@ public class Request implements HttpServletRequest
// Return host from absolute URI
_serverName = _uri.getHost();
_port = _uri.getPort();
if (_serverName != null)
{
_port = _uri.getPort();
return _serverName;
}
// Return host from header field
String hostPort = _fields.getStringField(HttpHeader.HOST);
_port=0;
if (hostPort != null)
{
loop: for (int i = hostPort.length(); i-- > 0;)
int len=hostPort.length();
loop: for (int i = len; i-- > 0;)
{
char ch = (char)(0xff & hostPort.charAt(i));
switch (ch)
char c2 = (char)(0xff & hostPort.charAt(i));
switch (c2)
{
case ']':
break loop;
case ':':
_serverName = hostPort.substring(0,i);
try
{
len=i;
_port = StringUtil.toInt(hostPort.substring(i+1));
}
catch (NumberFormatException e)
{
LOG.warn(e);
_serverName=hostPort;
_port=0;
return _serverName;
}
return _serverName;
break loop;
}
}
if (_serverName == null || _port < 0)
if (hostPort.charAt(0)=='[')
{
_serverName = hostPort;
_port = 0;
if (hostPort.charAt(len-1)!=']')
{
LOG.warn("Bad IPv6 "+hostPort);
_serverName=hostPort;
_port=0;
return _serverName;
}
_serverName = hostPort.substring(1,len-1);
}
else if (len==hostPort.length())
_serverName=hostPort;
else
_serverName = hostPort.substring(0,len);
return _serverName;
}
@ -1506,6 +1504,9 @@ public class Request implements HttpServletRequest
setAuthentication(Authentication.NOT_CHECKED);
getHttpChannelState().recycle();
if (_async!=null)
_async.reset();
_async=null;
_asyncSupported = true;
_handled = false;
if (_context != null)
@ -1989,8 +1990,11 @@ public class Request implements HttpServletRequest
if (!_asyncSupported)
throw new IllegalStateException("!asyncSupported");
HttpChannelState state = getHttpChannelState();
state.startAsync();
return state;
if (_async==null)
_async=new AsyncContextState(state);
AsyncContextEvent event = new AsyncContextEvent(_context,_async,state,this,this,getResponse());
state.startAsync(event);
return _async;
}
/* ------------------------------------------------------------ */
@ -2000,8 +2004,12 @@ public class Request implements HttpServletRequest
if (!_asyncSupported)
throw new IllegalStateException("!asyncSupported");
HttpChannelState state = getHttpChannelState();
state.startAsync(_context, servletRequest, servletResponse);
return state;
if (_async==null)
_async=new AsyncContextState(state);
AsyncContextEvent event = new AsyncContextEvent(_context,_async,state,this,servletRequest,servletResponse);
event.setDispatchTarget(getServletContext(),URIUtil.addPaths(getServletPath(),getPathInfo()));
state.startAsync(event);
return _async;
}
/* ------------------------------------------------------------ */

View File

@ -57,8 +57,8 @@ public class ResourceCache
private final ResourceCache _parent;
private final MimeTypes _mimeTypes;
private final boolean _etagSupported;
private boolean _useFileMappedBuffer=true;
private final boolean _useFileMappedBuffer;
private int _maxCachedFileSize =4*1024*1024;
private int _maxCachedFiles=2048;
private int _maxCacheSize =32*1024*1024;
@ -142,12 +142,6 @@ public class ResourceCache
return _useFileMappedBuffer;
}
/* ------------------------------------------------------------ */
public void setUseFileMappedBuffer(boolean useFileMappedBuffer)
{
_useFileMappedBuffer = useFileMappedBuffer;
}
/* ------------------------------------------------------------ */
public void flushCache()
{

View File

@ -753,6 +753,8 @@ public class Response implements HttpServletResponse
break;
case STREAM:
getOutputStream().close();
break;
default:
}
return true;
}
@ -926,6 +928,7 @@ public class Response implements HttpServletResponse
case TE:
_fields.put(HttpHeader.CONNECTION, HttpHeaderValue.TE.toString());
break;
default:
}
}
}
@ -965,6 +968,8 @@ public class Response implements HttpServletResponse
case STREAM:
case WRITER:
_out.reset();
break;
default:
}
_out.resetBuffer();
@ -1023,7 +1028,7 @@ public class Response implements HttpServletResponse
return _reason;
}
public void complete() throws IOException
public void complete()
{
_out.close();
}

View File

@ -19,11 +19,9 @@
package org.eclipse.jetty.server;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -34,22 +32,18 @@ import java.util.TimerTask;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.validation.Schema;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpGenerator;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.util.Attributes;
@ -282,9 +276,9 @@ public class Server extends HandlerWrapper implements Attributes
{
if (getStopAtShutdown())
{
ShutdownThread.register(this);
ShutdownThread.register(this);
}
ShutdownMonitor.getInstance().start(); // initialize
LOG.info("jetty-"+getVersion());
@ -318,7 +312,7 @@ public class Server extends HandlerWrapper implements Attributes
if (isDumpAfterStart())
dumpStdErr();
// use DateCache timer for Date field reformat
final HttpFields.DateGenerator date = new HttpFields.DateGenerator();
long now=System.currentTimeMillis();
@ -335,8 +329,8 @@ public class Server extends HandlerWrapper implements Attributes
this.cancel();
}
},tick,1000);
mex.ifExceptionThrow();
}
@ -374,7 +368,7 @@ public class Server extends HandlerWrapper implements Attributes
if (stopTimeout>0)
{
long stop_by=System.currentTimeMillis()+stopTimeout;
LOG.info("Graceful shutdown {} by ",this,new Date(stop_by));
LOG.debug("Graceful shutdown {} by ",this,new Date(stop_by));
// Wait for shutdowns
for (Future<Void> future: futures)
@ -476,16 +470,16 @@ public class Server extends HandlerWrapper implements Attributes
*/
public void handleAsync(HttpChannel<?> connection) throws IOException, ServletException
{
final HttpChannelState async = connection.getRequest().getHttpChannelState();
final HttpChannelState.AsyncEventState state = async.getAsyncEventState();
final HttpChannelState state = connection.getRequest().getHttpChannelState();
final AsyncContextEvent event = state.getAsyncContextEvent();
final Request baseRequest=connection.getRequest();
final String path=state.getPath();
final String path=event.getPath();
if (path!=null)
{
// this is a dispatch with a path
ServletContext context=state.getServletContext();
ServletContext context=event.getServletContext();
HttpURI uri = new HttpURI(context==null?path:URIUtil.addPaths(context.getContextPath(),path));
baseRequest.setUri(uri);
baseRequest.setRequestURI(null);
@ -495,8 +489,8 @@ public class Server extends HandlerWrapper implements Attributes
}
final String target=baseRequest.getPathInfo();
final HttpServletRequest request=(HttpServletRequest)async.getRequest();
final HttpServletResponse response=(HttpServletResponse)async.getResponse();
final HttpServletRequest request=(HttpServletRequest)event.getSuppliedRequest();
final HttpServletResponse response=(HttpServletResponse)event.getSuppliedResponse();
if (LOG.isDebugEnabled())
{
@ -509,7 +503,6 @@ public class Server extends HandlerWrapper implements Attributes
}
/* ------------------------------------------------------------ */
public void join() throws InterruptedException
{
@ -607,10 +600,10 @@ public class Server extends HandlerWrapper implements Attributes
break;
}
}
if (connector==null)
return null;
ContextHandler context = getChildHandlerByClass(ContextHandler.class);
try
@ -622,7 +615,7 @@ public class Server extends HandlerWrapper implements Attributes
host=context.getVirtualHosts()[0];
if (host==null)
host=InetAddress.getLocalHost().getHostAddress();
String path=context==null?null:context.getContextPath();
if (path==null)
path="/";
@ -634,7 +627,7 @@ public class Server extends HandlerWrapper implements Attributes
return null;
}
}
/* ------------------------------------------------------------ */
@Override
public String toString()
@ -647,7 +640,7 @@ public class Server extends HandlerWrapper implements Attributes
{
dumpBeans(out,indent,Collections.singleton(new ClassLoaderDump(this.getClass().getClassLoader())));
}
/* ------------------------------------------------------------ */
public static void main(String...args) throws Exception
{

View File

@ -40,7 +40,6 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Future;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.FilterRegistration;
@ -104,11 +103,11 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
{
public static int SERVLET_MAJOR_VERSION=3;
public static int SERVLET_MINOR_VERSION=0;
final private static String __unimplmented="Unimplemented - use org.eclipse.jetty.servlet.ServletContextHandler";
private static final Logger LOG = Log.getLogger(ContextHandler.class);
private static final ThreadLocal<Context> __context = new ThreadLocal<Context>();
@ -141,8 +140,8 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
return c.getContextHandler();
return null;
}
protected Context _scontext;
private final AttributesMap _attributes;
private final Map<String, String> _initParams;
@ -566,7 +565,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
public void addEventListener(EventListener listener)
{
_eventListeners.add(listener);
if (listener instanceof ServletContextListener)
_contextListeners.add((ServletContextListener)listener);
@ -579,7 +578,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
if (listener instanceof ServletRequestAttributeListener)
_requestAttributeListeners.add((ServletRequestAttributeListener)listener);
}
/* ------------------------------------------------------------ */
/**
* Remove a context event listeners.
@ -592,7 +591,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
public void removeEventListener(EventListener listener)
{
_eventListeners.remove(listener);
if (listener instanceof ServletContextListener)
_contextListeners.remove(listener);
@ -616,13 +615,13 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
{
_programmaticListeners.add(listener);
}
/* ------------------------------------------------------------ */
protected boolean isProgrammaticListener(EventListener listener)
{
return _programmaticListeners.contains(listener);
}
/* ------------------------------------------------------------ */
/**
* @return true if this context is accepting new requests
@ -725,7 +724,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
startContext();
_availability = Availability.AVAILABLE;
LOG.info("started {}",this);
LOG.info("Started {}", this);
}
finally
{
@ -771,7 +770,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
ServletContextEvent event = new ServletContextEvent(_scontext);
for (ServletContextListener listener:_contextListeners)
callContextInitialized(listener, event);
}
}
}
/* ------------------------------------------------------------ */
@ -818,7 +817,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
if (!_contextListeners.isEmpty())
{
ServletContextEvent event = new ServletContextEvent(_scontext);
for (int i = _contextListeners.size(); i-->0;)
for (int i = _contextListeners.size(); i-->0;)
callContextDestroyed(_contextListeners.get(i),event);
}
@ -831,14 +830,14 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
String name = e.nextElement();
checkManagedAttribute(name,null);
}
for (EventListener l : _programmaticListeners)
removeEventListener(l);
_programmaticListeners.clear();
}
finally
{
LOG.info("stopped {}",this);
LOG.info("Stopped {}", this);
__context.set(old_context);
// reset the classloader
if (_classLoader != null && current_thread!=null)
@ -894,10 +893,10 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
default:
match = contextVhost.equalsIgnoreCase(vhost);
}
if (match)
break loop;
}
if (!match)
return false;
@ -1657,7 +1656,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
return host;
}
/* ------------------------------------------------------------ */
/**
* Add an AliasCheck instance to possibly permit aliased resources
@ -1667,7 +1666,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
{
_aliasChecks.add(check);
}
/* ------------------------------------------------------------ */
/**
* @return Mutable list of Alias checks
@ -1798,7 +1797,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
return null;
return _mimeTypes.getMimeByExtension(file);
}
/* ------------------------------------------------------------ */
/*
* @see javax.servlet.ServletContext#getRequestDispatcher(java.lang.String)
@ -2182,13 +2181,13 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
return _enabled;
}
}
public static class NoContext extends AttributesMap implements ServletContext
{
private int _effectiveMajorVersion = SERVLET_MAJOR_VERSION;
private int _effectiveMinorVersion = SERVLET_MINOR_VERSION;
/* ------------------------------------------------------------ */
public NoContext()
{
@ -2522,8 +2521,8 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
LOG.warn(__unimplmented);
}
}
/* ------------------------------------------------------------ */
/** Interface to check aliases
*/
@ -2537,7 +2536,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
*/
boolean check(String path, Resource resource);
}
/* ------------------------------------------------------------ */
/** Approve all aliases.
@ -2550,7 +2549,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
return true;
}
}
/* ------------------------------------------------------------ */
/** Approve Aliases with same suffix.
* Eg. a symbolic link from /foobar.html to /somewhere/wibble.html would be
@ -2568,8 +2567,8 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
return resource.getAlias().toString().endsWith(suffix);
}
}
/* ------------------------------------------------------------ */
/** Approve Aliases with a path prefix.
* Eg. a symbolic link from /dirA/foobar.html to /dirB/foobar.html would be
@ -2589,7 +2588,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
}
/* ------------------------------------------------------------ */
/** Approve Aliases of a non existent directory.
* If a directory "/foobar/" does not exist, then the resource is
* If a directory "/foobar/" does not exist, then the resource is
* aliased to "/foobar". Accept such aliases.
*/
public static class ApproveNonExistentDirectoryAliases implements AliasCheck

View File

@ -27,6 +27,9 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.server.AbstractConnector;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.util.DateCache;
@ -40,7 +43,7 @@ import org.eclipse.jetty.util.RolloverFileOutputStream;
* and the current thread name is updated with information that will link
* to the details in that output.
*/
public class DebugHandler extends HandlerWrapper
public class DebugHandler extends HandlerWrapper implements Connection.Listener
{
private DateCache _date=new DateCache("HH:mm:ss", Locale.US);
private OutputStream _out;
@ -69,14 +72,10 @@ public class DebugHandler extends HandlerWrapper
String ex=null;
try
{
long now=System.currentTimeMillis();
final String d=_date.format(now);
final int ms=(int)(now%1000);
if (retry)
_print.println(d+(ms>99?".":(ms>9?".0":".00"))+ms+":"+name+" RETRY");
print(name,"RESUME");
else
_print.println(d+(ms>99?".":(ms>9?".0":".00"))+ms+":"+name+" "+baseRequest.getRemoteAddr()+" "+request.getMethod()+" "+baseRequest.getHeader("Cookie")+"; "+baseRequest.getHeader("User-Agent"));
print(name,"REQUEST "+baseRequest.getRemoteAddr()+" "+request.getMethod()+" "+baseRequest.getHeader("Cookie")+"; "+baseRequest.getHeader("User-Agent"));
thread.setName(name);
getHandler().handle(target,baseRequest,request,response);
@ -104,21 +103,25 @@ public class DebugHandler extends HandlerWrapper
finally
{
thread.setName(old_name);
long now=System.currentTimeMillis();
final String d=_date.format(now);
final int ms=(int)(now%1000);
suspend=baseRequest.getHttpChannelState().isSuspended();
if (suspend)
{
request.setAttribute("org.eclipse.jetty.thread.name",name);
_print.println(d+(ms>99?".":(ms>9?".0":".00"))+ms+":"+name+" SUSPEND");
print(name,"SUSPEND");
}
else
_print.println(d+(ms>99?".":(ms>9?".0":".00"))+ms+":"+name+" "+base_response.getStatus()+
(ex==null?"":("/"+ex))+
" "+base_response.getContentType());
print(name,"RESPONSE "+base_response.getStatus()+(ex==null?"":("/"+ex))+" "+base_response.getContentType());
}
}
private void print(String name,String message)
{
long now=System.currentTimeMillis();
final String d=_date.format(now);
final int ms=(int)(now%1000);
_print.println(d+(ms>99?".":(ms>9?".0":".00"))+ms+":"+name+" "+message);
}
/* (non-Javadoc)
* @see org.eclipse.jetty.server.handler.HandlerWrapper#doStart()
@ -129,6 +132,11 @@ public class DebugHandler extends HandlerWrapper
if (_out==null)
_out=new RolloverFileOutputStream("./logs/yyyy_mm_dd.debug.log",true);
_print=new PrintStream(_out);
for (Connector connector : getServer().getConnectors())
if (connector instanceof AbstractConnector)
((AbstractConnector)connector).addBean(this,false);
super.doStart();
}
@ -140,6 +148,9 @@ public class DebugHandler extends HandlerWrapper
{
super.doStop();
_print.close();
for (Connector connector : getServer().getConnectors())
if (connector instanceof AbstractConnector)
((AbstractConnector)connector).removeBean(this);
}
/**
@ -157,4 +168,17 @@ public class DebugHandler extends HandlerWrapper
{
_out = out;
}
@Override
public void onOpened(Connection connection)
{
print(Thread.currentThread().getName(),"OPENED "+connection.toString());
}
@Override
public void onClosed(Connection connection)
{
print(Thread.currentThread().getName(),"CLOSED "+connection.toString());
}
}

View File

@ -28,6 +28,7 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.AsyncContextEvent;
import org.eclipse.jetty.server.HttpChannelState;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
@ -76,12 +77,13 @@ public class StatisticsHandler extends HandlerWrapper
public void onError(AsyncEvent event) throws IOException
{
}
@Override
public void onComplete(AsyncEvent event) throws IOException
{
HttpChannelState state = (HttpChannelState)event.getAsyncContext();
HttpChannelState state = ((AsyncContextEvent)event).getHttpChannelState();
Request request = state.getBaseRequest();
final long elapsed = System.currentTimeMillis()-request.getTimeStamp();
@ -93,7 +95,7 @@ public class StatisticsHandler extends HandlerWrapper
if (!state.isDispatched())
_asyncWaitStats.decrement();
}
};
/**

View File

@ -323,17 +323,20 @@ public class JDBCSessionManager extends AbstractSessionManager
super.complete();
try
{
if (_dirty)
if (isValid())
{
//The session attributes have changed, write to the db, ensuring
//http passivation/activation listeners called
willPassivate();
updateSession(this);
didActivate();
}
else if ((getAccessed() - _lastSaved) >= (getSaveInterval() * 1000L))
{
updateSessionAccessTime(this);
if (_dirty)
{
//The session attributes have changed, write to the db, ensuring
//http passivation/activation listeners called
willPassivate();
updateSession(this);
didActivate();
}
else if ((getAccessed() - _lastSaved) >= (getSaveInterval() * 1000L))
{
updateSessionAccessTime(this);
}
}
}
catch (Exception e)

View File

@ -18,26 +18,38 @@
package org.eclipse.jetty.server;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Arrays;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Exchanger;
import java.util.concurrent.TimeUnit;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.BlockingArrayQueue;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.StringUtil;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
@ -46,116 +58,129 @@ public class AsyncRequestReadTest
{
private static Server server;
private static ServerConnector connector;
private final static Exchanger<Long> __total=new Exchanger<Long>();
private final static BlockingQueue<Long> __total=new BlockingArrayQueue<>();
@BeforeClass
public static void startServer() throws Exception
@Before
public void startServer() throws Exception
{
server = new Server();
connector = new ServerConnector(server);
connector.setIdleTimeout(10000);
server.addConnector(connector);
server.setHandler(new EmptyHandler());
server.start();
}
@AfterClass
public static void stopServer() throws Exception
@After
public void stopServer() throws Exception
{
server.stop();
server.join();
}
@Test
public void test() throws Exception
public void testPipelined() throws Exception
{
final Socket socket = new Socket("localhost",connector.getLocalPort());
server.setHandler(new AsyncStreamHandler());
server.start();
try (final Socket socket = new Socket("localhost",connector.getLocalPort()))
{
socket.setSoTimeout(1000);
byte[] content = new byte[32*4096];
Arrays.fill(content, (byte)120);
byte[] content = new byte[16*4096];
Arrays.fill(content, (byte)120);
OutputStream out = socket.getOutputStream();
String header=
"POST / HTTP/1.1\r\n"+
"Host: localhost\r\n"+
"Content-Length: "+content.length+"\r\n"+
"Content-Type: bytes\r\n"+
"\r\n";
byte[] h=header.getBytes(StringUtil.__ISO_8859_1);
out.write(h);
out.write(content);
header=
"POST / HTTP/1.1\r\n"+
"Host: localhost\r\n"+
"Content-Length: "+content.length+"\r\n"+
"Content-Type: bytes\r\n"+
"Connection: close\r\n"+
"\r\n";
h=header.getBytes(StringUtil.__ISO_8859_1);
out.write(h);
out.write(content);
out.flush();
OutputStream out = socket.getOutputStream();
String header=
"POST / HTTP/1.1\r\n"+
"Host: localhost\r\n"+
"Content-Length: "+content.length+"\r\n"+
"Content-Type: bytes\r\n"+
"Connection: close\r\n"+
"\r\n";
byte[] h=header.getBytes(StringUtil.__ISO_8859_1);
InputStream in = socket.getInputStream();
String response = IO.toString(in);
assertTrue(response.indexOf("200 OK")>0);
out.write(h);
out.flush();
out.write(content,0,4*4096);
Thread.sleep(100);
out.write(content,8192,4*4096);
Thread.sleep(100);
out.write(content,8*4096,content.length-8*4096);
out.flush();
InputStream in = socket.getInputStream();
String response = IO.toString(in);
assertTrue(response.indexOf("200 OK")>0);
long total=__total.exchange(0L,30,TimeUnit.SECONDS);
assertEquals(content.length, total);
long total=__total.poll(5,TimeUnit.SECONDS);
assertEquals(content.length, total);
total=__total.poll(5,TimeUnit.SECONDS);
assertEquals(content.length, total);
}
}
@Test
@Ignore
public void tests() throws Exception
public void testAsyncReadsWithDelays() throws Exception
{
runTest(64,4,4,20);
runTest(256,16,16,50);
runTest(256,1,128,10);
runTest(128*1024,1,64,10);
runTest(256*1024,5321,10,100);
runTest(512*1024,32*1024,10,10);
server.setHandler(new AsyncStreamHandler());
server.start();
asyncReadTest(64,4,4,20);
asyncReadTest(256,16,16,50);
asyncReadTest(256,1,128,10);
asyncReadTest(128*1024,1,64,10);
asyncReadTest(256*1024,5321,10,100);
asyncReadTest(512*1024,32*1024,10,10);
}
public void runTest(int contentSize, int chunkSize, int chunks, int delayMS) throws Exception
public void asyncReadTest(int contentSize, int chunkSize, int chunks, int delayMS) throws Exception
{
String tst=contentSize+","+chunkSize+","+chunks+","+delayMS;
//System.err.println(tst);
final Socket socket = new Socket("localhost",connector.getLocalPort());
byte[] content = new byte[contentSize];
Arrays.fill(content, (byte)120);
OutputStream out = socket.getOutputStream();
out.write("POST / HTTP/1.1\r\n".getBytes());
out.write("Host: localhost\r\n".getBytes());
out.write(("Content-Length: "+content.length+"\r\n").getBytes());
out.write("Content-Type: bytes\r\n".getBytes());
out.write("Connection: close\r\n".getBytes());
out.write("\r\n".getBytes());
out.flush();
int offset=0;
for (int i=0;i<chunks;i++)
try(final Socket socket = new Socket("localhost",connector.getLocalPort()))
{
out.write(content,offset,chunkSize);
offset+=chunkSize;
Thread.sleep(delayMS);
byte[] content = new byte[contentSize];
Arrays.fill(content, (byte)120);
OutputStream out = socket.getOutputStream();
out.write("POST / HTTP/1.1\r\n".getBytes());
out.write("Host: localhost\r\n".getBytes());
out.write(("Content-Length: "+content.length+"\r\n").getBytes());
out.write("Content-Type: bytes\r\n".getBytes());
out.write("Connection: close\r\n".getBytes());
out.write("\r\n".getBytes());
out.flush();
int offset=0;
for (int i=0;i<chunks;i++)
{
out.write(content,offset,chunkSize);
offset+=chunkSize;
Thread.sleep(delayMS);
}
out.write(content,offset,content.length-offset);
out.flush();
InputStream in = socket.getInputStream();
String response = IO.toString(in);
assertTrue(tst,response.indexOf("200 OK")>0);
long total=__total.poll(30,TimeUnit.SECONDS);
assertEquals(tst,content.length, total);
}
out.write(content,offset,content.length-offset);
out.flush();
InputStream in = socket.getInputStream();
String response = IO.toString(in);
assertTrue(tst,response.indexOf("200 OK")>0);
long total=__total.exchange(0L,30,TimeUnit.SECONDS);
assertEquals(tst,content.length, total);
}
private static class EmptyHandler extends AbstractHandler
private static class AsyncStreamHandler extends AbstractHandler
{
@Override
public void handle(String path, final Request request, HttpServletRequest httpRequest, final HttpServletResponse httpResponse) throws IOException, ServletException
@ -164,6 +189,7 @@ public class AsyncRequestReadTest
request.setHandled(true);
final AsyncContext async = request.startAsync();
// System.err.println("handle "+request.getContentLength());
new Thread()
{
@ -171,9 +197,10 @@ public class AsyncRequestReadTest
public void run()
{
long total=0;
try
try(InputStream in = request.getInputStream();)
{
InputStream in = request.getInputStream();
// System.err.println("reading...");
byte[] b = new byte[4*4096];
int read;
while((read =in.read(b))>=0)
@ -188,17 +215,156 @@ public class AsyncRequestReadTest
{
httpResponse.setStatus(200);
async.complete();
try
{
__total.exchange(total);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
// System.err.println("read "+total);
__total.offer(total);
}
}
}.start();
}
}
@Test
public void testPartialRead() throws Exception
{
server.setHandler(new PartialReaderHandler());
server.start();
try (final Socket socket = new Socket("localhost",connector.getLocalPort()))
{
socket.setSoTimeout(1000);
byte[] content = new byte[32*4096];
Arrays.fill(content, (byte)88);
OutputStream out = socket.getOutputStream();
String header=
"POST /?read=10 HTTP/1.1\r\n"+
"Host: localhost\r\n"+
"Content-Length: "+content.length+"\r\n"+
"Content-Type: bytes\r\n"+
"\r\n";
byte[] h=header.getBytes(StringUtil.__ISO_8859_1);
out.write(h);
out.write(content);
header= "POST /?read=10 HTTP/1.1\r\n"+
"Host: localhost\r\n"+
"Content-Length: "+content.length+"\r\n"+
"Content-Type: bytes\r\n"+
"Connection: close\r\n"+
"\r\n";
h=header.getBytes(StringUtil.__ISO_8859_1);
out.write(h);
out.write(content);
out.flush();
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
assertThat(in.readLine(),containsString("HTTP/1.1 200 OK"));
assertThat(in.readLine(),containsString("Content-Length:"));
assertThat(in.readLine(),containsString("Server:"));
in.readLine();
assertThat(in.readLine(),containsString("XXXXXXX"));
assertThat(in.readLine(),containsString("HTTP/1.1 200 OK"));
assertThat(in.readLine(),containsString("Connection: close"));
assertThat(in.readLine(),containsString("Server:"));
in.readLine();
assertThat(in.readLine(),containsString("XXXXXXX"));
}
}
@Test
public void testPartialReadThenShutdown() throws Exception
{
server.setHandler(new PartialReaderHandler());
server.start();
try (final Socket socket = new Socket("localhost",connector.getLocalPort()))
{
socket.setSoTimeout(10000);
byte[] content = new byte[32*4096];
Arrays.fill(content, (byte)88);
OutputStream out = socket.getOutputStream();
String header=
"POST /?read=10 HTTP/1.1\r\n"+
"Host: localhost\r\n"+
"Content-Length: "+content.length+"\r\n"+
"Content-Type: bytes\r\n"+
"\r\n";
byte[] h=header.getBytes(StringUtil.__ISO_8859_1);
out.write(h);
out.write(content,0,4096);
out.flush();
socket.shutdownOutput();
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
assertThat(in.readLine(),containsString("HTTP/1.1 200 OK"));
assertThat(in.readLine(),containsString("Content-Length:"));
assertThat(in.readLine(),containsString("Server:"));
in.readLine();
assertThat(in.readLine(),containsString("XXXXXXX"));
}
}
@Test
public void testPartialReadThenClose() throws Exception
{
server.setHandler(new PartialReaderHandler());
server.start();
try (final Socket socket = new Socket("localhost",connector.getLocalPort()))
{
socket.setSoTimeout(1000);
byte[] content = new byte[32*4096];
Arrays.fill(content, (byte)88);
OutputStream out = socket.getOutputStream();
String header=
"POST /?read=10 HTTP/1.1\r\n"+
"Host: localhost\r\n"+
"Content-Length: "+content.length+"\r\n"+
"Content-Type: bytes\r\n"+
"\r\n";
byte[] h=header.getBytes(StringUtil.__ISO_8859_1);
out.write(h);
out.write(content,0,4096);
out.flush();
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
assertThat(in.readLine(),containsString("HTTP/1.1 200 OK"));
assertThat(in.readLine(),containsString("Content-Length:"));
assertThat(in.readLine(),containsString("Server:"));
in.readLine();
assertThat(in.readLine(),containsString("XXXXXXX"));
socket.close();
}
}
private static class PartialReaderHandler extends AbstractHandler
{
@Override
public void handle(String path, final Request request, HttpServletRequest httpRequest, final HttpServletResponse httpResponse) throws IOException, ServletException
{
httpResponse.setStatus(200);
request.setHandled(true);
BufferedReader in = request.getReader();
PrintWriter out =httpResponse.getWriter();
int read=Integer.valueOf(request.getParameter("read"));
// System.err.println("read="+read);
for (int i=read;i-->0;)
{
int c=in.read();
if (c<0)
break;
out.write(c);
}
out.println();
}
}
}

View File

@ -23,6 +23,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@ -44,6 +45,7 @@ public class CheckReverseProxyHeadersTest
"X-Forwarded-For: 10.20.30.40\n" +
"X-Forwarded-Host: example.com", new RequestValidator()
{
@Override
public void validate(HttpServletRequest request)
{
assertEquals("example.com", request.getServerName());
@ -55,6 +57,42 @@ public class CheckReverseProxyHeadersTest
assertFalse(request.isSecure());
}
});
// IPv6 ProxyPass from example.com:80 to localhost:8080
testRequest("Host: localhost:8080\n" +
"X-Forwarded-For: 10.20.30.40\n" +
"X-Forwarded-Host: [::1]", new RequestValidator()
{
@Override
public void validate(HttpServletRequest request)
{
assertEquals("::1", request.getServerName());
assertEquals(80, request.getServerPort());
assertEquals("10.20.30.40", request.getRemoteAddr());
assertEquals("10.20.30.40", request.getRemoteHost());
assertEquals("[::1]", request.getHeader("Host"));
assertEquals("http",request.getScheme());
assertFalse(request.isSecure());
}
});
// IPv6 ProxyPass from example.com:80 to localhost:8080
testRequest("Host: localhost:8080\n" +
"X-Forwarded-For: 10.20.30.40\n" +
"X-Forwarded-Host: [::1]:8888", new RequestValidator()
{
@Override
public void validate(HttpServletRequest request)
{
assertEquals("::1", request.getServerName());
assertEquals(8888, request.getServerPort());
assertEquals("10.20.30.40", request.getRemoteAddr());
assertEquals("10.20.30.40", request.getRemoteHost());
assertEquals("[::1]:8888", request.getHeader("Host"));
assertEquals("http",request.getScheme());
assertFalse(request.isSecure());
}
});
// ProxyPass from example.com:81 to localhost:8080
testRequest("Host: localhost:8080\n" +
@ -63,6 +101,7 @@ public class CheckReverseProxyHeadersTest
"X-Forwarded-Server: example.com\n"+
"X-Forwarded-Proto: https", new RequestValidator()
{
@Override
public void validate(HttpServletRequest request)
{
assertEquals("example.com", request.getServerName());
@ -82,6 +121,7 @@ public class CheckReverseProxyHeadersTest
"X-Forwarded-Server: example.com, rp.example.com\n"+
"X-Forwarded-Proto: https, http", new RequestValidator()
{
@Override
public void validate(HttpServletRequest request)
{
assertEquals("example.com", request.getServerName());
@ -111,7 +151,8 @@ public class CheckReverseProxyHeadersTest
try
{
server.start();
connector.getResponses("GET / HTTP/1.1\r\n" +"Connection: close\r\n" + headers + "\r\n\r\n");
connector.getResponses("GET / HTTP/1.1\r\n" +"Connection: close\r\n" + headers + "\r\n\r\n",
1000,TimeUnit.SECONDS);
Error error = validationHandler.getError();

View File

@ -81,7 +81,7 @@ public class HttpConnectionTest
}
@Test
public void testFragmentedChunk()
public void testFragmentedChunk() throws Exception
{
String response=null;
try
@ -119,10 +119,9 @@ public class HttpConnectionTest
}
catch(Exception e)
{
e.printStackTrace();
assertTrue(false);
if (response!=null)
if(response != null)
System.err.println(response);
throw e;
}
}
@ -249,7 +248,7 @@ public class HttpConnectionTest
}
@Test
public void testCharset()
public void testCharset() throws Exception
{
String response=null;
@ -306,10 +305,9 @@ public class HttpConnectionTest
}
catch(Exception e)
{
e.printStackTrace();
assertTrue(false);
if (response!=null)
if(response != null)
System.err.println(response);
throw e;
}
}
@ -391,7 +389,7 @@ public class HttpConnectionTest
}
@Test
public void testConnection()
public void testConnection() throws Exception
{
String response=null;
try
@ -412,10 +410,9 @@ public class HttpConnectionTest
}
catch (Exception e)
{
e.printStackTrace();
assertTrue(false);
if (response!=null)
System.err.println(response);
if(response != null)
System.err.println(response);
throw e;
}
}
@ -525,7 +522,7 @@ public class HttpConnectionTest
}
@Test
public void testAsterisk()
public void testAsterisk() throws Exception
{
String response = null;
@ -572,10 +569,9 @@ public class HttpConnectionTest
}
catch (Exception e)
{
e.printStackTrace();
assertTrue(false);
if (response!=null)
System.err.println(response);
if(response != null)
System.err.println(response);
throw e;
}
finally
{
@ -585,7 +581,7 @@ public class HttpConnectionTest
}
@Test
public void testCONNECT()
public void testCONNECT() throws Exception
{
String response = null;
@ -601,10 +597,9 @@ public class HttpConnectionTest
}
catch (Exception e)
{
e.printStackTrace();
assertTrue(false);
if (response!=null)
System.err.println(response);
if(response != null)
System.err.println(response);
throw e;
}
}

View File

@ -960,7 +960,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
String in = IO.toString(is);
System.err.println(in);
// System.err.println(in);
int index = in.indexOf("123456789");
assertTrue(index > 0);

View File

@ -135,11 +135,11 @@ public class HttpURITest
/*33*/ {"/?abc=test",null, null, null,null,"/", null,"abc=test",null},
/*34*/ {"/#fragment",null, null, null,null,"/", null,null,"fragment"},
/*35*/ {"http://192.0.0.1:8080/","http","//192.0.0.1:8080","192.0.0.1","8080","/",null,null,null},
/*36*/ {"http://[2001:db8::1]:8080/","http","//[2001:db8::1]:8080","[2001:db8::1]","8080","/",null,null,null},
/*37*/ {"http://user@[2001:db8::1]:8080/","http","//user@[2001:db8::1]:8080","[2001:db8::1]","8080","/",null,null,null},
/*38*/ {"http://[2001:db8::1]/","http","//[2001:db8::1]","[2001:db8::1]",null,"/",null,null,null},
/*36*/ {"http://[2001:db8::1]:8080/","http","//[2001:db8::1]:8080","2001:db8::1","8080","/",null,null,null},
/*37*/ {"http://user@[2001:db8::1]:8080/","http","//user@[2001:db8::1]:8080","2001:db8::1","8080","/",null,null,null},
/*38*/ {"http://[2001:db8::1]/","http","//[2001:db8::1]","2001:db8::1",null,"/",null,null,null},
/*39*/ {"//[2001:db8::1]:8080/",null,null,null,null,"//[2001:db8::1]:8080/",null,null,null},
/*40*/ {"http://user@[2001:db8::1]:8080/","http","//user@[2001:db8::1]:8080","[2001:db8::1]","8080","/",null,null,null},
/*40*/ {"http://user@[2001:db8::1]:8080/","http","//user@[2001:db8::1]:8080","2001:db8::1","8080","/",null,null,null},
/*41*/ {"*",null,null,null,null,"*",null, null,null}
};
@ -366,7 +366,7 @@ public class HttpURITest
{
/* 0*/ {" localhost:8080 ","localhost","8080"},
/* 1*/ {" 127.0.0.1:8080 ","127.0.0.1","8080"},
/* 2*/ {" [127::0::0::1]:8080 ","[127::0::0::1]","8080"},
/* 2*/ {" [127::0::0::1]:8080 ","127::0::0::1","8080"},
/* 3*/ {" error ",null,null},
/* 4*/ {" http://localhost:8080/ ",null,null},
};
@ -382,7 +382,7 @@ public class HttpURITest
byte[] buf = connect_tests[i][0].getBytes(StringUtil.__UTF8);
uri.parseConnect(buf,2,buf.length-4);
assertEquals("path"+i,connect_tests[i][1]+":"+connect_tests[i][2],uri.getPath());
assertEquals("path"+i,connect_tests[i][0].trim(),uri.getPath());
assertEquals("host"+i,connect_tests[i][1],uri.getHost());
assertEquals("port"+i,Integer.parseInt(connect_tests[i][2]),uri.getPort());
}

View File

@ -241,7 +241,7 @@ public class RequestTest
multipart;
String responses=_connector.getResponses(request);
System.err.println(responses);
// System.err.println(responses);
assertTrue(responses.startsWith("HTTP/1.1 200"));
}
@ -354,6 +354,7 @@ public class RequestTest
@Override
public boolean check(HttpServletRequest request,HttpServletResponse response)
{
results.add(request.getRequestURL().toString());
results.add(request.getRemoteAddr());
results.add(request.getServerName());
results.add(String.valueOf(request.getServerPort()));
@ -361,71 +362,121 @@ public class RequestTest
}
};
String responses=_connector.getResponses(
results.clear();
_connector.getResponses(
"GET / HTTP/1.1\n"+
"Host: myhost\n"+
"\n"+
"Connection: close\n"+
"\n");
int i=0;
assertEquals("http://myhost/",results.get(i++));
assertEquals("0.0.0.0",results.get(i++));
assertEquals("myhost",results.get(i++));
assertEquals("80",results.get(i++));
results.clear();
_connector.getResponses(
"GET / HTTP/1.1\n"+
"Host: myhost:8888\n"+
"\n"+
"Connection: close\n"+
"\n");
i=0;
assertEquals("http://myhost:8888/",results.get(i++));
assertEquals("0.0.0.0",results.get(i++));
assertEquals("myhost",results.get(i++));
assertEquals("8888",results.get(i++));
results.clear();
_connector.getResponses(
"GET / HTTP/1.1\n"+
"Host: 1.2.3.4\n"+
"\n"+
"Connection: close\n"+
"\n");
i=0;
assertEquals("http://1.2.3.4/",results.get(i++));
assertEquals("0.0.0.0",results.get(i++));
assertEquals("1.2.3.4",results.get(i++));
assertEquals("80",results.get(i++));
results.clear();
_connector.getResponses(
"GET / HTTP/1.1\n"+
"Host: 1.2.3.4:8888\n"+
"\n"+
"Connection: close\n"+
"\n");
i=0;
assertEquals("http://1.2.3.4:8888/",results.get(i++));
assertEquals("0.0.0.0",results.get(i++));
assertEquals("1.2.3.4",results.get(i++));
assertEquals("8888",results.get(i++));
results.clear();
_connector.getResponses(
"GET / HTTP/1.1\n"+
"Host: [::1]\n"+
"\n"+
"Connection: close\n"+
"\n");
i=0;
assertEquals("http://[::1]/",results.get(i++));
assertEquals("0.0.0.0",results.get(i++));
assertEquals("::1",results.get(i++));
assertEquals("80",results.get(i++));
results.clear();
_connector.getResponses(
"GET / HTTP/1.1\n"+
"Host: [::1]:8888\n"+
"\n"+
"Connection: close\n"+
"\n");
i=0;
assertEquals("http://[::1]:8888/",results.get(i++));
assertEquals("0.0.0.0",results.get(i++));
assertEquals("::1",results.get(i++));
assertEquals("8888",results.get(i++));
results.clear();
_connector.getResponses(
"GET / HTTP/1.1\n"+
"Host: [::1]\n"+
"x-forwarded-for: remote\n"+
"x-forwarded-proto: https\n"+
"\n"+
"Connection: close\n"+
"\n");
i=0;
assertEquals("https://[::1]/",results.get(i++));
assertEquals("remote",results.get(i++));
assertEquals("::1",results.get(i++));
assertEquals("443",results.get(i++));
results.clear();
_connector.getResponses(
"GET / HTTP/1.1\n"+
"Host: [::1]:8888\n"+
"Connection: close\n"+
"x-forwarded-for: remote\n"+
"x-forwarded-proto: https\n"+
"\n",10,TimeUnit.SECONDS);
int i=0;
assertEquals("0.0.0.0",results.get(i++));
assertEquals("myhost",results.get(i++));
assertEquals("80",results.get(i++));
assertEquals("0.0.0.0",results.get(i++));
assertEquals("myhost",results.get(i++));
assertEquals("8888",results.get(i++));
assertEquals("0.0.0.0",results.get(i++));
assertEquals("1.2.3.4",results.get(i++));
assertEquals("80",results.get(i++));
assertEquals("0.0.0.0",results.get(i++));
assertEquals("1.2.3.4",results.get(i++));
assertEquals("8888",results.get(i++));
assertEquals("0.0.0.0",results.get(i++));
assertEquals("[::1]",results.get(i++));
assertEquals("80",results.get(i++));
assertEquals("0.0.0.0",results.get(i++));
assertEquals("[::1]",results.get(i++));
assertEquals("8888",results.get(i++));
"\n");
i=0;
assertEquals("https://[::1]:8888/",results.get(i++));
assertEquals("remote",results.get(i++));
assertEquals("[::1]",results.get(i++));
assertEquals("443",results.get(i++));
assertEquals("remote",results.get(i++));
assertEquals("[::1]",results.get(i++));
assertEquals("::1",results.get(i++));
assertEquals("8888",results.get(i++));
}
@Test

View File

@ -432,43 +432,54 @@ public class ResponseTest
String[][] tests = {
// No cookie
{"http://myhost:8888/other/location;jsessionid=12345?name=value","http://myhost:8888/other/location;jsessionid=12345?name=value"},
{"/other/location;jsessionid=12345?name=value","http://myhost:8888/other/location;jsessionid=12345?name=value"},
{"./location;jsessionid=12345?name=value","http://myhost:8888/path/location;jsessionid=12345?name=value"},
{"/other/location;jsessionid=12345?name=value","http://@HOST@@PORT@/other/location;jsessionid=12345?name=value"},
{"./location;jsessionid=12345?name=value","http://@HOST@@PORT@/path/location;jsessionid=12345?name=value"},
// From cookie
{"/other/location","http://myhost:8888/other/location"},
{"/other/l%20cation", "http://myhost:8888/other/l%20cation"},
{"location", "http://myhost:8888/path/location"},
{"./location", "http://myhost:8888/path/location"},
{"../location", "http://myhost:8888/location"},
{"/other/l%20cation", "http://myhost:8888/other/l%20cation"},
{"l%20cation", "http://myhost:8888/path/l%20cation"},
{"./l%20cation", "http://myhost:8888/path/l%20cation"},
{"../l%20cation","http://myhost:8888/l%20cation"},
{"../locati%C3%abn", "http://myhost:8888/locati%C3%ABn"},
{"/other/location","http://@HOST@@PORT@/other/location"},
{"/other/l%20cation", "http://@HOST@@PORT@/other/l%20cation"},
{"location", "http://@HOST@@PORT@/path/location"},
{"./location", "http://@HOST@@PORT@/path/location"},
{"../location", "http://@HOST@@PORT@/location"},
{"/other/l%20cation", "http://@HOST@@PORT@/other/l%20cation"},
{"l%20cation", "http://@HOST@@PORT@/path/l%20cation"},
{"./l%20cation", "http://@HOST@@PORT@/path/l%20cation"},
{"../l%20cation","http://@HOST@@PORT@/l%20cation"},
{"../locati%C3%abn", "http://@HOST@@PORT@/locati%C3%ABn"},
{"http://somehost.com/other/location","http://somehost.com/other/location"},
};
for (int i=0;i<tests.length;i++)
int[] ports=new int[]{8080,80};
String[] hosts=new String[]{"myhost","192.168.0.1","0::1"};
for (int port : ports)
{
Response response = newResponse();
Request request = response.getHttpChannel().getRequest();
for (String host : hosts)
{
for (int i=0;i<tests.length;i++)
{
Response response = newResponse();
Request request = response.getHttpChannel().getRequest();
request.setServerName("myhost");
request.setServerPort(8888);
request.setUri(new HttpURI("/path/info;param;jsessionid=12345?query=0&more=1#target"));
request.setContextPath("/path");
request.setRequestedSessionId("12345");
request.setRequestedSessionIdFromCookie(i>2);
HashSessionManager manager = new HashSessionManager();
manager.setSessionIdManager(new HashSessionIdManager());
request.setSessionManager(manager);
request.setSession(new TestSession(manager, "12345"));
manager.setCheckingRemoteSessionIdEncoding(false);
request.setServerName(host);
request.setServerPort(port);
request.setUri(new HttpURI("/path/info;param;jsessionid=12345?query=0&more=1#target"));
request.setContextPath("/path");
request.setRequestedSessionId("12345");
request.setRequestedSessionIdFromCookie(i>2);
HashSessionManager manager = new HashSessionManager();
manager.setSessionIdManager(new HashSessionIdManager());
request.setSessionManager(manager);
request.setSession(new TestSession(manager, "12345"));
manager.setCheckingRemoteSessionIdEncoding(false);
response.sendRedirect(tests[i][0]);
response.sendRedirect(tests[i][0]);
String location = response.getHeader("Location");
assertEquals("test-"+i,tests[i][1],location);
String location = response.getHeader("Location");
String expected=tests[i][1].replace("@HOST@",host.contains(":")?("["+host+"]"):host).replace("@PORT@",port==80?"":(":"+port));
assertEquals("test-"+i+" "+host+":"+port,expected,location);
}
}
}
}

View File

@ -122,46 +122,46 @@ public class StressTest
q.clear();
}
@Test
public void testMinNonPersistent() throws Throwable
{
assumeTrue(!OS.IS_OSX);
doThreads(10,10,false);
}
@Test
@Stress("Hey, its called StressTest for a reason")
public void testMinNonPersistent() throws Throwable
{
doThreads(2,2,false);
}
@Test
public void testNonPersistent() throws Throwable
{
// TODO needs to be further investigated
assumeTrue(!OS.IS_OSX || PropertyFlag.isEnabled("test.stress"));
assumeTrue(!OS.IS_OSX);
doThreads(10,10,false);
if (PropertyFlag.isEnabled("test.stress"))
{
doThreads(20,20,false);
Thread.sleep(1000);
doThreads(200,10,false);
Thread.sleep(1000);
doThreads(200,200,false);
}
doThreads(20,20,false);
Thread.sleep(1000);
doThreads(200,10,false);
Thread.sleep(1000);
doThreads(200,200,false);
}
@Test
public void testMinPersistent() throws Throwable
{
// TODO needs to be further investigated
assumeTrue(!OS.IS_OSX);
doThreads(10,10,true);
}
@Test
@Stress("Hey, its called StressTest for a reason")
public void testPersistent() throws Throwable
{
// TODO needs to be further investigated
assumeTrue(!OS.IS_OSX || PropertyFlag.isEnabled("test.stress"));
doThreads(10,10,true);
if (PropertyFlag.isEnabled("test.stress"))
{
doThreads(40,40,true);
Thread.sleep(1000);
doThreads(200,10,true);
Thread.sleep(1000);
doThreads(200,200,true);
}
assumeTrue(!OS.IS_OSX);
doThreads(40,40,true);
Thread.sleep(1000);
doThreads(200,10,true);
Thread.sleep(1000);
doThreads(200,200,true);
}
private void doThreads(int threadCount, final int loops, final boolean persistent) throws Throwable
@ -238,7 +238,6 @@ public class StressTest
{
System.err.println("STALLED!!!");
System.err.println(_server.getThreadPool().toString());
((ServerConnector)(_server.getConnectors()[0])).dump();
Thread.sleep(5000);
System.exit(1);
}

View File

@ -18,19 +18,11 @@
package org.eclipse.jetty.server.handler;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
@ -46,6 +38,12 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
public class StatisticsHandlerTest
{
private Server _server;
@ -60,7 +58,7 @@ public class StatisticsHandlerTest
_server = new Server();
_connector = new LocalConnector(_server);
_statistics=new ConnectorStatistics();
_statistics = new ConnectorStatistics();
_connector.addBean(_statistics);
_server.addConnector(_connector);
@ -105,8 +103,8 @@ public class StatisticsHandlerTest
_server.start();
String request = "GET / HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"\r\n";
"Host: localhost\r\n" +
"\r\n";
_connector.executeRequest(request);
barrier[0].await();
@ -174,8 +172,8 @@ public class StatisticsHandlerTest
assertEquals(2, _statsHandler.getResponses2xx());
_latchHandler.reset(2);
barrier[0]=new CyclicBarrier(3);
barrier[1]=new CyclicBarrier(3);
barrier[0] = new CyclicBarrier(3);
barrier[1] = new CyclicBarrier(3);
_connector.executeRequest(request);
_connector.executeRequest(request);
@ -208,37 +206,33 @@ public class StatisticsHandlerTest
assertEquals(0, _statsHandler.getAsyncDispatches());
assertEquals(0, _statsHandler.getExpires());
assertEquals(4, _statsHandler.getResponses2xx());
}
@Test
public void testSuspendResume() throws Exception
{
final long dispatchTime = 10;
final long requestTime = 50;
final AtomicReference<AsyncContext> asyncHolder = new AtomicReference<>();
final CyclicBarrier barrier[] = { new CyclicBarrier(2), new CyclicBarrier(2), new CyclicBarrier(2)};
final CyclicBarrier barrier[] = {new CyclicBarrier(2), new CyclicBarrier(2), new CyclicBarrier(2)};
_statsHandler.setHandler(new AbstractHandler()
{
@Override
public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
{
request.setHandled(true);
try
{
barrier[0].await();
Thread.sleep(10);
Thread.sleep(dispatchTime);
if (asyncHolder.get() == null)
{
asyncHolder.set(request.startAsync());
}
}
catch (Exception x)
{
Thread.currentThread().interrupt();
throw (IOException)new IOException().initCause(x);
throw new ServletException(x);
}
finally
{
@ -246,50 +240,41 @@ public class StatisticsHandlerTest
{
barrier[1].await();
}
catch (Exception x)
catch (Exception ignored)
{
x.printStackTrace();
Thread.currentThread().interrupt();
fail();
}
}
}
});
_server.start();
String request = "GET / HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"\r\n";
"Host: localhost\r\n" +
"\r\n";
_connector.executeRequest(request);
barrier[0].await();
assertEquals(1, _statistics.getConnectionsOpen());
assertEquals(1, _statsHandler.getRequests());
assertEquals(1, _statsHandler.getRequestsActive());
assertEquals(1, _statsHandler.getDispatched());
assertEquals(1, _statsHandler.getDispatchedActive());
barrier[1].await();
assertTrue(_latchHandler.await());
assertNotNull(asyncHolder.get());
assertTrue(asyncHolder.get()!=null);
assertEquals(1, _statsHandler.getRequests());
assertEquals(1, _statsHandler.getRequestsActive());
assertEquals(1, _statsHandler.getDispatched());
assertEquals(0, _statsHandler.getDispatchedActive());
Thread.sleep(10);
_latchHandler.reset();
barrier[0].reset();
barrier[1].reset();
Thread.sleep(50);
Thread.sleep(requestTime);
asyncHolder.get().addListener(new AsyncListener()
{
@ -297,30 +282,34 @@ public class StatisticsHandlerTest
public void onTimeout(AsyncEvent event) throws IOException
{
}
@Override
public void onStartAsync(AsyncEvent event) throws IOException
{
}
@Override
public void onError(AsyncEvent event) throws IOException
{
}
@Override
public void onComplete(AsyncEvent event) throws IOException
{
try { barrier[2].await(); } catch(Exception e) {}
try
{
barrier[2].await();
}
catch (Exception ignored)
{
}
}
});
asyncHolder.get().dispatch();
barrier[0].await(); // entered app handler
assertEquals(1, _statistics.getConnectionsOpen());
assertEquals(1, _statsHandler.getRequests());
assertEquals(1, _statsHandler.getRequestsActive());
assertEquals(2, _statsHandler.getDispatched());
@ -335,50 +324,49 @@ public class StatisticsHandlerTest
assertEquals(2, _statsHandler.getDispatched());
assertEquals(0, _statsHandler.getDispatchedActive());
assertEquals(1, _statsHandler.getAsyncRequests());
assertEquals(1, _statsHandler.getAsyncDispatches());
assertEquals(0, _statsHandler.getExpires());
assertEquals(1, _statsHandler.getResponses2xx());
assertThat(_statsHandler.getRequestTimeTotal(), greaterThanOrEqualTo(requestTime * 3 / 4));
assertEquals(_statsHandler.getRequestTimeTotal(), _statsHandler.getRequestTimeMax());
assertEquals(_statsHandler.getRequestTimeTotal(), _statsHandler.getRequestTimeMean(), 0.01);
assertThat(_statsHandler.getRequestTimeTotal(),greaterThanOrEqualTo(50L));
assertEquals(_statsHandler.getRequestTimeTotal(),_statsHandler.getRequestTimeMax());
assertEquals(_statsHandler.getRequestTimeTotal(),_statsHandler.getRequestTimeMean(), 0.01);
assertTrue(_statsHandler.getDispatchedTimeTotal()>=20);
assertTrue(_statsHandler.getDispatchedTimeMean()+10<=_statsHandler.getDispatchedTimeTotal());
assertTrue(_statsHandler.getDispatchedTimeMax()+10<=_statsHandler.getDispatchedTimeTotal());
assertThat(_statsHandler.getDispatchedTimeTotal(), greaterThanOrEqualTo(dispatchTime * 2 * 3 / 4));
assertTrue(_statsHandler.getDispatchedTimeMean() + dispatchTime <= _statsHandler.getDispatchedTimeTotal());
assertTrue(_statsHandler.getDispatchedTimeMax() + dispatchTime <= _statsHandler.getDispatchedTimeTotal());
}
@Test
public void testSuspendExpire() throws Exception
{
final long dispatchTime = 10;
final long timeout = 100;
final AtomicReference<AsyncContext> asyncHolder = new AtomicReference<>();
final CyclicBarrier barrier[] = { new CyclicBarrier(2), new CyclicBarrier(2), new CyclicBarrier(2)};
final CyclicBarrier barrier[] = {new CyclicBarrier(2), new CyclicBarrier(2), new CyclicBarrier(2)};
_statsHandler.setHandler(new AbstractHandler()
{
@Override
public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
{
request.setHandled(true);
try
{
barrier[0].await();
Thread.sleep(10);
Thread.sleep(dispatchTime);
if (asyncHolder.get() == null)
{
AsyncContext async=request.startAsync();
async.setTimeout(100);
AsyncContext async = request.startAsync();
asyncHolder.set(async);
async.setTimeout(timeout);
}
}
catch (Exception x)
{
Thread.currentThread().interrupt();
throw (IOException)new IOException().initCause(x);
throw new ServletException(x);
}
finally
{
@ -386,28 +374,22 @@ public class StatisticsHandlerTest
{
barrier[1].await();
}
catch (Exception x)
catch (Exception ignored)
{
x.printStackTrace();
Thread.currentThread().interrupt();
fail();
}
}
}
});
_server.start();
String request = "GET / HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"\r\n";
"Host: localhost\r\n" +
"\r\n";
_connector.executeRequest(request);
barrier[0].await();
assertEquals(1, _statistics.getConnectionsOpen());
assertEquals(1, _statsHandler.getRequests());
assertEquals(1, _statsHandler.getRequestsActive());
assertEquals(1, _statsHandler.getDispatched());
@ -423,31 +405,35 @@ public class StatisticsHandlerTest
{
event.getAsyncContext().complete();
}
@Override
public void onStartAsync(AsyncEvent event) throws IOException
{
}
@Override
public void onError(AsyncEvent event) throws IOException
{
}
@Override
public void onComplete(AsyncEvent event) throws IOException
{
try { barrier[2].await(); } catch(Exception e) {}
try
{
barrier[2].await();
}
catch (Exception ignored)
{
}
}
});
assertEquals(1, _statsHandler.getRequests());
assertEquals(1, _statsHandler.getRequestsActive());
assertEquals(1, _statsHandler.getDispatched());
assertEquals(0, _statsHandler.getDispatchedActive());
barrier[2].await();
assertEquals(1, _statsHandler.getRequests());
@ -460,67 +446,42 @@ public class StatisticsHandlerTest
assertEquals(1, _statsHandler.getExpires());
assertEquals(1, _statsHandler.getResponses2xx());
assertTrue(_statsHandler.getRequestTimeTotal() >= (timeout + dispatchTime) * 3 / 4);
assertEquals(_statsHandler.getRequestTimeTotal(), _statsHandler.getRequestTimeMax());
assertEquals(_statsHandler.getRequestTimeTotal(), _statsHandler.getRequestTimeMean(), 0.01);
assertTrue(_statsHandler.getRequestTimeTotal()>=30);
assertEquals(_statsHandler.getRequestTimeTotal(),_statsHandler.getRequestTimeMax());
assertEquals(_statsHandler.getRequestTimeTotal(),_statsHandler.getRequestTimeMean(), 0.01);
assertThat(_statsHandler.getDispatchedTimeTotal(),greaterThanOrEqualTo(10L));
assertThat(_statsHandler.getDispatchedTimeTotal(), greaterThanOrEqualTo(dispatchTime * 3 / 4));
}
@Test
public void testSuspendComplete() throws Exception
{
final long dispatchTime = 10;
final AtomicReference<AsyncContext> asyncHolder = new AtomicReference<>();
final CyclicBarrier barrier[] = { new CyclicBarrier(2), new CyclicBarrier(2), new CyclicBarrier(2)};
final CyclicBarrier barrier[] = {new CyclicBarrier(2), new CyclicBarrier(2)};
final CountDownLatch latch = new CountDownLatch(1);
_statsHandler.setHandler(new AbstractHandler()
{
@Override
public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
{
request.setHandled(true);
try
{
barrier[0].await();
Thread.sleep(10);
Thread.sleep(dispatchTime);
if (asyncHolder.get() == null)
{
AsyncContext async=request.startAsync();
async.setTimeout(1000);
AsyncContext async = request.startAsync();
asyncHolder.set(async);
asyncHolder.get().addListener(new AsyncListener()
{
@Override
public void onTimeout(AsyncEvent event) throws IOException
{
}
@Override
public void onStartAsync(AsyncEvent event) throws IOException
{
}
@Override
public void onError(AsyncEvent event) throws IOException
{
}
@Override
public void onComplete(AsyncEvent event) throws IOException
{
try { barrier[2].await(); } catch(Exception e) {}
}
});
}
}
catch (Exception x)
{
Thread.currentThread().interrupt();
throw (IOException)new IOException().initCause(x);
throw new ServletException(x);
}
finally
{
@ -528,11 +489,8 @@ public class StatisticsHandlerTest
{
barrier[1].await();
}
catch (Exception x)
catch (Exception ignored)
{
x.printStackTrace();
Thread.currentThread().interrupt();
fail();
}
}
@ -541,21 +499,18 @@ public class StatisticsHandlerTest
_server.start();
String request = "GET / HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"\r\n";
"Host: localhost\r\n" +
"\r\n";
_connector.executeRequest(request);
barrier[0].await();
assertEquals(1, _statistics.getConnectionsOpen());
assertEquals(1, _statsHandler.getRequests());
assertEquals(1, _statsHandler.getRequestsActive());
assertEquals(1, _statsHandler.getDispatched());
assertEquals(1, _statsHandler.getDispatchedActive());
barrier[1].await();
assertTrue(_latchHandler.await());
assertNotNull(asyncHolder.get());
@ -565,9 +520,39 @@ public class StatisticsHandlerTest
assertEquals(1, _statsHandler.getDispatched());
assertEquals(0, _statsHandler.getDispatchedActive());
Thread.sleep(10);
asyncHolder.get().addListener(new AsyncListener()
{
@Override
public void onTimeout(AsyncEvent event) throws IOException
{
}
@Override
public void onStartAsync(AsyncEvent event) throws IOException
{
}
@Override
public void onError(AsyncEvent event) throws IOException
{
}
@Override
public void onComplete(AsyncEvent event) throws IOException
{
try
{
latch.countDown();
}
catch (Exception ignored)
{
}
}
});
long requestTime = 20;
Thread.sleep(requestTime);
asyncHolder.get().complete();
barrier[2].await();
latch.await();
assertEquals(1, _statsHandler.getRequests());
assertEquals(0, _statsHandler.getRequestsActive());
@ -579,17 +564,16 @@ public class StatisticsHandlerTest
assertEquals(0, _statsHandler.getExpires());
assertEquals(1, _statsHandler.getResponses2xx());
assertTrue(_statsHandler.getRequestTimeTotal()>=20);
assertEquals(_statsHandler.getRequestTimeTotal(),_statsHandler.getRequestTimeMax());
assertEquals(_statsHandler.getRequestTimeTotal(),_statsHandler.getRequestTimeMean(), 0.01);
assertTrue(_statsHandler.getRequestTimeTotal() >= (dispatchTime + requestTime) * 3 / 4);
assertEquals(_statsHandler.getRequestTimeTotal(), _statsHandler.getRequestTimeMax());
assertEquals(_statsHandler.getRequestTimeTotal(), _statsHandler.getRequestTimeMean(), 0.01);
assertTrue(_statsHandler.getDispatchedTimeTotal()>=10);
assertTrue(_statsHandler.getDispatchedTimeTotal()<_statsHandler.getRequestTimeTotal());
assertEquals(_statsHandler.getDispatchedTimeTotal(),_statsHandler.getDispatchedTimeMax());
assertEquals(_statsHandler.getDispatchedTimeTotal(),_statsHandler.getDispatchedTimeMean(), 0.01);
assertTrue(_statsHandler.getDispatchedTimeTotal() >= dispatchTime * 3 / 4);
assertTrue(_statsHandler.getDispatchedTimeTotal() < _statsHandler.getRequestTimeTotal());
assertEquals(_statsHandler.getDispatchedTimeTotal(), _statsHandler.getDispatchedTimeMax());
assertEquals(_statsHandler.getDispatchedTimeTotal(), _statsHandler.getDispatchedTimeMean(), 0.01);
}
/**
* This handler is external to the statistics handler and it is used to ensure that statistics handler's
* handle() is fully executed before asserting its values in the tests, to avoid race conditions with the
@ -602,7 +586,7 @@ public class StatisticsHandlerTest
@Override
public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
{
final CountDownLatch latch=_latch;
final CountDownLatch latch = _latch;
try
{
super.handle(path, request, httpRequest, httpResponse);
@ -615,12 +599,12 @@ public class StatisticsHandlerTest
private void reset()
{
_latch=new CountDownLatch(1);
_latch = new CountDownLatch(1);
}
private void reset(int count)
{
_latch=new CountDownLatch(count);
_latch = new CountDownLatch(count);
}
private boolean await() throws InterruptedException

View File

@ -125,7 +125,6 @@ public class SSLEngineTest
{
server.setHandler(new HelloWorldHandler());
server.start();
server.dumpStdErr();
SSLContext ctx=SSLContext.getInstance("TLS");
ctx.init(null,SslContextFactory.TRUST_ALL_CERTS,new java.security.SecureRandom());

View File

@ -27,7 +27,6 @@ import java.net.URL;
import java.nio.ByteBuffer;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
@ -42,9 +41,8 @@ import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.http.PathMap.MappedEntry;
import org.eclipse.jetty.io.WriterOutputStream;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpChannel;
import org.eclipse.jetty.server.HttpOutput;
import org.eclipse.jetty.server.InclusiveByteRange;
import org.eclipse.jetty.server.ResourceCache;
@ -160,7 +158,6 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
private ServletHandler _servletHandler;
private ServletHolder _defaultHolder;
/* ------------------------------------------------------------ */
@Override
public void init()
@ -268,7 +265,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
throw new UnavailableException(e.toString());
}
_servletHandler= (ServletHandler) _contextHandler.getChildHandlerByClass(ServletHandler.class);
_servletHandler= _contextHandler.getChildHandlerByClass(ServletHandler.class);
for (ServletHolder h :_servletHandler.getServlets())
if (h.getServletInstance()==this)
_defaultHolder=h;
@ -343,6 +340,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
* @param pathInContext The path to find a resource for.
* @return The resource to serve.
*/
@Override
public Resource getResource(String pathInContext)
{
Resource r=null;
@ -632,7 +630,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
if ((_welcomeServlets || _welcomeExactServlets) && welcome_servlet==null)
{
Map.Entry entry=_servletHandler.getHolderEntry(welcome_in_context);
MappedEntry<?> entry=_servletHandler.getHolderEntry(welcome_in_context);
if (entry!=null && entry.getValue()!=_defaultHolder &&
(_welcomeServlets || (_welcomeExactServlets && entry.getKey().equals(welcome_in_context))))
welcome_servlet=welcome_in_context;
@ -828,24 +826,10 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
boolean include,
Resource resource,
HttpContent content,
Enumeration reqRanges)
Enumeration<String> reqRanges)
throws IOException
{
boolean direct;
long content_length;
if (content==null)
{
direct=false;
content_length=resource.length();
}
else
{
// TODO sometimes we should be direct!
Connector connector = HttpChannel.getCurrentHttpChannel().getConnector();
direct=false;
content_length=content.getContentLength();
}
final long content_length = (content==null)?resource.length():content.getContentLength();
// Get the output stream (or writer)
OutputStream out =null;
@ -884,17 +868,8 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
}
else
{
ByteBuffer buffer = direct?content.getDirectBuffer():content.getIndirectBuffer();
if (buffer!=null)
{
writeHeaders(response,content,content_length);
((HttpOutput)out).sendContent(buffer);
}
else
{
writeHeaders(response,content,content_length);
resource.writeTo(out,0,content_length);
}
writeHeaders(response,content,content_length);
((HttpOutput)out).sendContent(content.getResource());
}
}
else
@ -914,7 +889,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
else
{
// Parse the satisfiable ranges
List ranges =InclusiveByteRange.satisfiableRanges(reqRanges,content_length);
List<InclusiveByteRange> ranges =InclusiveByteRange.satisfiableRanges(reqRanges,content_length);
// if there are no satisfiable ranges, send 416 response
if (ranges==null || ranges.size()==0)
@ -931,8 +906,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
// since were here now), send that range with a 216 response
if ( ranges.size()== 1)
{
InclusiveByteRange singleSatisfiableRange =
(InclusiveByteRange)ranges.get(0);
InclusiveByteRange singleSatisfiableRange = ranges.get(0);
long singleLength = singleSatisfiableRange.getSize(content_length);
writeHeaders(response,content,singleLength );
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
@ -947,7 +921,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
// content-length header
//
writeHeaders(response,content,-1);
String mimetype=(content.getContentType()==null?null:content.getContentType().toString());
String mimetype=(content==null||content.getContentType()==null?null:content.getContentType().toString());
if (mimetype==null)
LOG.warn("Unknown mimetype for "+request.getRequestURI());
MultiPartOutputStream multi = new MultiPartOutputStream(out);
@ -971,7 +945,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
String[] header = new String[ranges.size()];
for (int i=0;i<ranges.size();i++)
{
InclusiveByteRange ibr = (InclusiveByteRange) ranges.get(i);
InclusiveByteRange ibr = ranges.get(i);
header[i]=ibr.toHeaderRangeString(content_length);
length+=
((i>0)?2:0)+
@ -986,7 +960,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
for (int i=0;i<ranges.size();i++)
{
InclusiveByteRange ibr = (InclusiveByteRange) ranges.get(i);
InclusiveByteRange ibr = ranges.get(i);
multi.startPart(mimetype,new String[]{HttpHeader.CONTENT_RANGE+": "+header[i]});
long start=ibr.getFirst(content_length);
@ -1023,7 +997,6 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
/* ------------------------------------------------------------ */
protected void writeHeaders(HttpServletResponse response,HttpContent content,long count)
throws IOException
{
if (content.getContentType()!=null && response.getContentType()==null)
response.setContentType(content.getContentType().toString());

View File

@ -24,6 +24,7 @@ import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.eclipse.jetty.server.Dispatcher;
import org.eclipse.jetty.server.HttpConnection;
@ -96,7 +97,11 @@ public class JspPropertyGroupServlet extends GenericServlet
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
{
Request request=(req instanceof Request)?(Request)req:HttpConnection.getCurrentConnection().getHttpChannel().getRequest();
HttpServletRequest request = null;
if (req instanceof HttpServletRequest)
request = (HttpServletRequest)req;
else
throw new ServletException("Request not HttpServletRequest");
String servletPath=null;
String pathInfo=null;

View File

@ -113,10 +113,10 @@ public class ServletHandler extends ScopedHandler
private MultiMap<FilterMapping> _filterNameMappings;
private final Map<String,ServletHolder> _servletNameMap=new HashMap<>();
private PathMap _servletPathMap;
private PathMap<ServletHolder> _servletPathMap;
protected final ConcurrentMap _chainCache[] = new ConcurrentMap[FilterMapping.ALL];
protected final Queue[] _chainLRU = new Queue[FilterMapping.ALL];
protected final ConcurrentMap<?, ?> _chainCache[] = new ConcurrentMap[FilterMapping.ALL];
protected final Queue<?>[] _chainLRU = new Queue[FilterMapping.ALL];
/* ------------------------------------------------------------ */
@ -208,6 +208,9 @@ public class ServletHandler extends ScopedHandler
_filterNameMappings=null;
_servletPathMap=null;
_matchBeforeIndex=-1;
_matchAfterIndex=-1;
}
/* ------------------------------------------------------------ */
@ -250,7 +253,7 @@ public class ServletHandler extends ScopedHandler
* @param pathInContext Path within _context.
* @return PathMap Entries pathspec to ServletHolder
*/
public PathMap.MappedEntry getHolderEntry(String pathInContext)
public PathMap.MappedEntry<ServletHolder> getHolderEntry(String pathInContext)
{
if (_servletPathMap==null)
return null;
@ -331,10 +334,10 @@ public class ServletHandler extends ScopedHandler
if (target.startsWith("/"))
{
// Look for the servlet by path
PathMap.MappedEntry entry=getHolderEntry(target);
PathMap.MappedEntry<ServletHolder> entry=getHolderEntry(target);
if (entry!=null)
{
servlet_holder=(ServletHolder)entry.getValue();
servlet_holder=entry.getValue();
String servlet_path_spec= entry.getKey();
String servlet_path=entry.getMapped()!=null?entry.getMapped():PathMap.pathMatch(servlet_path_spec,target);
@ -342,8 +345,8 @@ public class ServletHandler extends ScopedHandler
if (DispatcherType.INCLUDE.equals(type))
{
baseRequest.setAttribute(Dispatcher.INCLUDE_SERVLET_PATH,servlet_path);
baseRequest.setAttribute(Dispatcher.INCLUDE_PATH_INFO, path_info);
baseRequest.setAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH,servlet_path);
baseRequest.setAttribute(RequestDispatcher.INCLUDE_PATH_INFO, path_info);
}
else
{
@ -615,8 +618,8 @@ public class ServletHandler extends ScopedHandler
if (filters.size() > 0)
chain= new CachedChain(filters, servletHolder);
final Map<String,FilterChain> cache=_chainCache[dispatch];
final Queue<String> lru=_chainLRU[dispatch];
final Map<String,FilterChain> cache=(Map<String, FilterChain>)_chainCache[dispatch];
final Queue<String> lru=(Queue<String>)_chainLRU[dispatch];
// Do we have too many cached chains?
while (_maxFilterChainsCacheSize>0 && cache.size()>=_maxFilterChainsCacheSize)
@ -718,7 +721,7 @@ public class ServletHandler extends ScopedHandler
{
if (servlet.getClassName() == null && servlet.getForcedPath() != null)
{
ServletHolder forced_holder = (ServletHolder)_servletPathMap.match(servlet.getForcedPath());
ServletHolder forced_holder = _servletPathMap.match(servlet.getForcedPath());
if (forced_holder == null || forced_holder.getClassName() == null)
{
mx.add(new IllegalStateException("No forced path servlet for " + servlet.getForcedPath()));
@ -1053,7 +1056,7 @@ public class ServletHandler extends ScopedHandler
public void addFilterMapping (FilterMapping mapping)
{
if (mapping != null)
{
{
Source source = (mapping.getFilterHolder()==null?null:mapping.getFilterHolder().getSource());
FilterMapping[] mappings =getFilterMappings();
if (mappings==null || mappings.length==0)

View File

@ -385,8 +385,8 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope
if (o==null)
return;
Servlet servlet = ((Servlet)o);
servlet.destroy();
getServletHandler().destroyServlet(servlet);
servlet.destroy();
}
/* ------------------------------------------------------------ */

View File

@ -103,6 +103,7 @@ public class AsyncContextTest
Assert.assertEquals("servlet gets right path", "doGet:getServletPath:/servletPath", br.readLine());
Assert.assertEquals("async context gets right path in get","doGet:async:getServletPath:/servletPath",br.readLine());
Assert.assertEquals("async context gets right path in async","async:run:attr:servletPath:/servletPath",br.readLine());
}
@Test
@ -121,6 +122,16 @@ public class AsyncContextTest
Assert.assertEquals("query string attr is correct","async:run:attr:queryString:dispatch=true",br.readLine());
Assert.assertEquals("context path attr is correct","async:run:attr:contextPath:",br.readLine());
Assert.assertEquals("request uri attr is correct","async:run:attr:requestURI:/servletPath",br.readLine());
try
{
__asyncContext.getRequest();
Assert.fail();
}
catch (IllegalStateException e)
{
}
}
@Test
@ -193,8 +204,11 @@ public class AsyncContextTest
@Test
public void testDispatchRequestResponse() throws Exception
{
String request = "GET /forward?dispatchRequestResponse=true HTTP/1.1\r\n" + "Host: localhost\r\n"
+ "Content-Type: application/x-www-form-urlencoded\r\n" + "Connection: close\r\n" + "\r\n";
String request = "GET /forward?dispatchRequestResponse=true HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"Content-Type: application/x-www-form-urlencoded\r\n" +
"Connection: close\r\n" +
"\r\n";
String responseString = _connector.getResponses(request);
@ -233,10 +247,12 @@ public class AsyncContextTest
}
}
public static volatile AsyncContext __asyncContext;
private class AsyncDispatchingServlet extends HttpServlet
{
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, final HttpServletResponse response) throws ServletException, IOException
{
@ -253,13 +269,14 @@ public class AsyncContextTest
{
wrapped = true;
asyncContext = request.startAsync(request, new Wrapper(response));
__asyncContext=asyncContext;
}
else
{
asyncContext = request.startAsync();
__asyncContext=asyncContext;
}
new Thread(new DispatchingRunnable(asyncContext, wrapped)).start();
}
}
@ -301,12 +318,14 @@ public class AsyncContextTest
if (request.getParameter("dispatch") != null)
{
AsyncContext asyncContext = request.startAsync(request,response);
__asyncContext=asyncContext;
asyncContext.dispatch("/servletPath2");
}
else
{
response.getOutputStream().print("doGet:getServletPath:" + request.getServletPath() + "\n");
AsyncContext asyncContext = request.startAsync(request,response);
__asyncContext=asyncContext;
response.getOutputStream().print("doGet:async:getServletPath:" + ((HttpServletRequest)asyncContext.getRequest()).getServletPath() + "\n");
asyncContext.start(new AsyncRunnable(asyncContext));
@ -323,6 +342,7 @@ public class AsyncContextTest
{
response.getOutputStream().print("doGet:getServletPath:" + request.getServletPath() + "\n");
AsyncContext asyncContext = request.startAsync(request, response);
__asyncContext=asyncContext;
response.getOutputStream().print("doGet:async:getServletPath:" + ((HttpServletRequest)asyncContext.getRequest()).getServletPath() + "\n");
asyncContext.start(new AsyncRunnable(asyncContext));
}

View File

@ -19,7 +19,6 @@
package org.eclipse.jetty.servlet;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.io.InputStream;
@ -345,6 +344,45 @@ public class AsyncServletTest
Assert.assertThat(response,Matchers.not(Matchers.containsString(content)));
}
@Test
public void testAsyncRead() throws Exception
{
String header="GET /ctx/path/info?suspend=2000&resume=1500 HTTP/1.1\r\n"+
"Host: localhost\r\n"+
"Content-Length: 10\r\n"+
"\r\n";
String body="12345678\r\n";
String close="GET /ctx/path/info HTTP/1.1\r\n"+
"Host: localhost\r\n"+
"Connection: close\r\n"+
"\r\n";
try (Socket socket = new Socket("localhost",_port);)
{
socket.setSoTimeout(10000);
socket.getOutputStream().write(header.getBytes("ISO-8859-1"));
Thread.sleep(500);
socket.getOutputStream().write(body.getBytes("ISO-8859-1"),0,2);
Thread.sleep(500);
socket.getOutputStream().write(body.getBytes("ISO-8859-1"),2,8);
socket.getOutputStream().write(close.getBytes("ISO-8859-1"));
String response = IO.toString(socket.getInputStream());
assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
assertContains(
"history: REQUEST\r\n"+
"history: initial\r\n"+
"history: suspend\r\n"+
"history: async-read=10\r\n"+
"history: resume\r\n"+
"history: ASYNC\r\n"+
"history: !initial\r\n"+
"history: onComplete\r\n",response);
}
}
public synchronized String process(String query,String content) throws Exception
{
String request = "GET /ctx/path/info";
@ -364,9 +402,8 @@ public class AsyncServletTest
int port=_port;
String response=null;
try
try (Socket socket = new Socket("localhost",port);)
{
Socket socket = new Socket("localhost",port);
socket.setSoTimeout(1000000);
socket.getOutputStream().write(request.getBytes("UTF-8"));
@ -379,11 +416,10 @@ public class AsyncServletTest
throw e;
}
// System.err.println(response);
return response;
}
private static class AsyncServlet extends HttpServlet
@ -429,7 +465,7 @@ public class AsyncServletTest
if (request.getDispatcherType()==DispatcherType.REQUEST)
{
((HttpServletResponse)response).addHeader("history","initial");
response.addHeader("history","initial");
if (read_before>0)
{
byte[] buf=new byte[read_before];
@ -442,6 +478,30 @@ public class AsyncServletTest
while(b!=-1)
b=in.read();
}
else if (request.getContentLength()>0)
{
new Thread()
{
@Override
public void run()
{
int c=0;
try
{
InputStream in=request.getInputStream();
int b=0;
while(b!=-1)
if((b=in.read())>=0)
c++;
response.addHeader("history","async-read="+c);
}
catch(Exception e)
{
e.printStackTrace();
}
}
}.start();
}
if (suspend_for>=0)
{
@ -449,7 +509,7 @@ public class AsyncServletTest
if (suspend_for>0)
async.setTimeout(suspend_for);
async.addListener(__listener);
((HttpServletResponse)response).addHeader("history","suspend");
response.addHeader("history","suspend");
if (complete_after>0)
{
@ -527,7 +587,7 @@ public class AsyncServletTest
}
else
{
((HttpServletResponse)response).addHeader("history","!initial");
response.addHeader("history","!initial");
if (suspend2_for>=0 && request.getAttribute("2nd")==null)
{
@ -540,7 +600,7 @@ public class AsyncServletTest
async.setTimeout(suspend2_for);
}
// continuation.addContinuationListener(__listener);
((HttpServletResponse)response).addHeader("history","suspend");
response.addHeader("history","suspend");
if (complete2_after>0)
{
@ -581,7 +641,7 @@ public class AsyncServletTest
@Override
public void run()
{
((HttpServletResponse)response).addHeader("history","resume");
response.addHeader("history","resume");
async.dispatch();
}
};
@ -592,7 +652,7 @@ public class AsyncServletTest
}
else if (resume2_after==0)
{
((HttpServletResponse)response).addHeader("history","dispatch");
response.addHeader("history","dispatch");
async.dispatch();
}
}
@ -633,15 +693,11 @@ public class AsyncServletTest
@Override
public void onStartAsync(AsyncEvent event) throws IOException
{
// TODO Auto-generated method stub
}
@Override
public void onError(AsyncEvent event) throws IOException
{
// TODO Auto-generated method stub
}
@Override

View File

@ -89,6 +89,18 @@
<npn.version>1.1.5.v20130313</npn.version>
</properties>
</profile>
<profile>
<id>7u21</id>
<activation>
<property>
<name>java.version</name>
<value>1.7.0_21</value>
</property>
</activation>
<properties>
<npn.version>1.1.5.v20130313</npn.version>
</properties>
</profile>
</profiles>
<modules>

View File

@ -59,7 +59,9 @@ public interface IStream extends Stream, Callback
*/
public void setStreamFrameListener(StreamFrameListener listener);
//TODO: javadoc thomas
/**
* @return the stream frame listener associated to this stream
*/
public StreamFrameListener getStreamFrameListener();
/**

View File

@ -32,7 +32,7 @@ public class PushSynInfo extends SynInfo
private int associatedStreamId;
public PushSynInfo(int associatedStreamId, PushInfo pushInfo){
super(pushInfo.getHeaders(), pushInfo.isClose());
super(pushInfo.getTimeout(), pushInfo.getUnit(), pushInfo.getHeaders(), pushInfo.isClose(), (byte)0);
this.associatedStreamId = associatedStreamId;
}

View File

@ -20,6 +20,7 @@ package org.eclipse.jetty.spdy.server.http;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@ -46,6 +47,7 @@ import org.eclipse.jetty.spdy.api.StreamStatus;
import org.eclipse.jetty.util.BlockingCallback;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.ConcurrentArrayQueue;
import org.eclipse.jetty.util.Fields;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.log.Log;
@ -74,11 +76,21 @@ public class HttpTransportOverSPDY implements HttpTransport
this.requestHeaders = requestHeaders;
}
protected Stream getStream()
{
return stream;
}
protected Fields getRequestHeaders()
{
return requestHeaders;
}
@Override
public void send(HttpGenerator.ResponseInfo info, ByteBuffer content, boolean lastContent, Callback callback)
{
if (LOG.isDebugEnabled())
LOG.debug("send {} {} {} {} last={}", this, stream, info, BufferUtil.toDetailString(content), lastContent);
LOG.debug("Sending {} {} {} {} last={}", this, stream, info, BufferUtil.toDetailString(content), lastContent);
if (stream.isClosed() || stream.isReset())
{
@ -86,7 +98,6 @@ public class HttpTransportOverSPDY implements HttpTransport
callback.failed(exception);
return;
}
// new Throwable().printStackTrace();
// info==null content==null lastContent==false should not happen
// info==null content==null lastContent==true signals no more content - complete
@ -149,7 +160,7 @@ public class HttpTransportOverSPDY implements HttpTransport
{
// Is the stream still open?
if (stream.isClosed() || stream.isReset())
// tell the callback about the EOF
// tell the callback about the EOF
callback.failed(new EofException("stream closed"));
else
// send the data and let it call the callback
@ -171,7 +182,6 @@ public class HttpTransportOverSPDY implements HttpTransport
else
// No data and no close so tell callback we are completed
callback.succeeded();
}
@Override
@ -188,11 +198,10 @@ public class HttpTransportOverSPDY implements HttpTransport
}
}
@Override
public void completed()
{
LOG.debug("completed");
LOG.debug("Completed");
}
private void reply(Stream stream, ReplyInfo replyInfo)
@ -206,65 +215,160 @@ public class HttpTransportOverSPDY implements HttpTransport
short version = stream.getSession().getVersion();
if (responseHeaders.get(HTTPSPDYHeader.STATUS.name(version)).value().startsWith("200") && !stream.isClosed())
{
// We have a 200 OK with some content to send, check the push strategy
Fields.Field scheme = requestHeaders.get(HTTPSPDYHeader.SCHEME.name(version));
Fields.Field host = requestHeaders.get(HTTPSPDYHeader.HOST.name(version));
Fields.Field uri = requestHeaders.get(HTTPSPDYHeader.URI.name(version));
Set<String> pushResources = pushStrategy.apply(stream, requestHeaders, responseHeaders);
for (String pushResource : pushResources)
if (pushResources.size() > 0)
{
Fields pushHeaders = createPushHeaders(scheme, host, pushResource);
final Fields pushRequestHeaders = createRequestHeaders(scheme, host, uri, pushResource);
// TODO: handle the timeout better
stream.push(new PushInfo(0, TimeUnit.MILLISECONDS, pushHeaders, false), new Promise.Adapter<Stream>()
{
@Override
public void succeeded(Stream pushStream)
{
HttpChannelOverSPDY pushChannel = newHttpChannelOverSPDY(pushStream, pushRequestHeaders);
pushChannel.requestStart(pushRequestHeaders, true);
}
});
PushResourceCoordinator pushResourceCoordinator = new PushResourceCoordinator(pushResources);
pushResourceCoordinator.coordinate();
}
}
}
private Fields createRequestHeaders(Fields.Field scheme, Fields.Field host, Fields.Field uri, String pushResourcePath)
private static class PushHttpTransportOverSPDY extends HttpTransportOverSPDY
{
final Fields newRequestHeaders = new Fields(requestHeaders, false);
short version = stream.getSession().getVersion();
newRequestHeaders.put(HTTPSPDYHeader.METHOD.name(version), "GET");
newRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
newRequestHeaders.put(scheme);
newRequestHeaders.put(host);
newRequestHeaders.put(HTTPSPDYHeader.URI.name(version), pushResourcePath);
String referrer = scheme.value() + "://" + host.value() + uri.value();
newRequestHeaders.put("referer", referrer);
newRequestHeaders.put("x-spdy-push", "true");
return newRequestHeaders;
}
private final PushResourceCoordinator coordinator;
private Fields createPushHeaders(Fields.Field scheme, Fields.Field host, String pushResourcePath)
{
final Fields pushHeaders = new Fields();
short version = stream.getSession().getVersion();
if (version == SPDY.V2)
pushHeaders.put(HTTPSPDYHeader.URI.name(version), scheme.value() + "://" + host.value() + pushResourcePath);
else
private PushHttpTransportOverSPDY(Connector connector, HttpConfiguration configuration, EndPoint endPoint,
PushStrategy pushStrategy, Stream stream, Fields requestHeaders,
PushResourceCoordinator coordinator)
{
pushHeaders.put(HTTPSPDYHeader.URI.name(version), pushResourcePath);
pushHeaders.put(scheme);
pushHeaders.put(host);
super(connector, configuration, endPoint, pushStrategy, stream, requestHeaders);
this.coordinator = coordinator;
}
@Override
public void completed()
{
Stream stream = getStream();
LOG.debug("Resource pushed for {} on {}",
getRequestHeaders().get(HTTPSPDYHeader.URI.name(stream.getSession().getVersion())), stream);
coordinator.complete();
}
return pushHeaders;
}
private HttpChannelOverSPDY newHttpChannelOverSPDY(Stream pushStream, Fields pushRequestHeaders)
private class PushResourceCoordinator
{
HttpTransport transport = new HttpTransportOverSPDY(connector, configuration, endPoint, pushStrategy, pushStream, pushRequestHeaders);
HttpInputOverSPDY input = new HttpInputOverSPDY();
return new HttpChannelOverSPDY(connector, configuration, endPoint, transport, input, pushStream);
private final Queue<PushResource> queue = new ConcurrentArrayQueue<>();
private final Set<String> resources;
private boolean active;
private PushResourceCoordinator(Set<String> resources)
{
this.resources = resources;
}
private void coordinate()
{
// Must send all push frames to the client at once before we
// return from this method and send the main resource data
for (String pushResource : resources)
pushResource(pushResource);
}
private void sendNextResourceData()
{
PushResource resource;
synchronized (this)
{
if (active)
return;
resource = queue.poll();
if (resource == null)
return;
active = true;
}
HttpChannelOverSPDY pushChannel = newHttpChannelOverSPDY(resource.getPushStream(), resource.getPushRequestHeaders());
pushChannel.requestStart(resource.getPushRequestHeaders(), true);
}
private HttpChannelOverSPDY newHttpChannelOverSPDY(Stream pushStream, Fields pushRequestHeaders)
{
HttpTransport transport = new PushHttpTransportOverSPDY(connector, configuration, endPoint, pushStrategy,
pushStream, pushRequestHeaders, this);
HttpInputOverSPDY input = new HttpInputOverSPDY();
return new HttpChannelOverSPDY(connector, configuration, endPoint, transport, input, pushStream);
}
private void pushResource(String pushResource)
{
final short version = stream.getSession().getVersion();
Fields.Field scheme = requestHeaders.get(HTTPSPDYHeader.SCHEME.name(version));
Fields.Field host = requestHeaders.get(HTTPSPDYHeader.HOST.name(version));
Fields.Field uri = requestHeaders.get(HTTPSPDYHeader.URI.name(version));
final Fields pushHeaders = createPushHeaders(scheme, host, pushResource);
final Fields pushRequestHeaders = createRequestHeaders(scheme, host, uri, pushResource);
stream.push(new PushInfo(pushHeaders, false), new Promise.Adapter<Stream>()
{
@Override
public void succeeded(Stream pushStream)
{
LOG.debug("Headers pushed for {} on {}", pushHeaders.get(HTTPSPDYHeader.URI.name(version)), pushStream);
queue.offer(new PushResource(pushStream, pushRequestHeaders));
sendNextResourceData();
}
});
}
private Fields createRequestHeaders(Fields.Field scheme, Fields.Field host, Fields.Field uri, String pushResourcePath)
{
final Fields newRequestHeaders = new Fields(requestHeaders, false);
short version = stream.getSession().getVersion();
newRequestHeaders.put(HTTPSPDYHeader.METHOD.name(version), "GET");
newRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
newRequestHeaders.put(scheme);
newRequestHeaders.put(host);
newRequestHeaders.put(HTTPSPDYHeader.URI.name(version), pushResourcePath);
String referrer = scheme.value() + "://" + host.value() + uri.value();
newRequestHeaders.put("referer", referrer);
newRequestHeaders.put("x-spdy-push", "true");
return newRequestHeaders;
}
private Fields createPushHeaders(Fields.Field scheme, Fields.Field host, String pushResourcePath)
{
final Fields pushHeaders = new Fields();
short version = stream.getSession().getVersion();
if (version == SPDY.V2)
pushHeaders.put(HTTPSPDYHeader.URI.name(version), scheme.value() + "://" + host.value() + pushResourcePath);
else
{
pushHeaders.put(HTTPSPDYHeader.URI.name(version), pushResourcePath);
pushHeaders.put(scheme);
pushHeaders.put(host);
}
return pushHeaders;
}
private void complete()
{
synchronized (this)
{
active = false;
}
sendNextResourceData();
}
}
private static class PushResource
{
private final Stream pushStream;
private final Fields pushRequestHeaders;
public PushResource(Stream pushStream, Fields pushRequestHeaders)
{
this.pushStream = pushStream;
this.pushRequestHeaders = pushRequestHeaders;
}
public Stream getPushStream()
{
return pushStream;
}
public Fields getPushRequestHeaders()
{
return pushRequestHeaders;
}
}
}

View File

@ -34,6 +34,8 @@ public interface PushStrategy
/**
* <p>Applies the SPDY push logic for the primary resource.</p>
*
*
*
* @param stream the primary resource stream
* @param requestHeaders the primary resource request headers
* @param responseHeaders the primary resource response headers

Some files were not shown because too many files have changed in this diff Show More