diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java index 831cf6d7ed4..c592f3431b6 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java @@ -19,6 +19,7 @@ package org.eclipse.jetty.server; import java.nio.ByteBuffer; +import java.nio.channels.ByteChannel; import java.util.concurrent.RejectedExecutionException; import org.eclipse.jetty.http.HttpGenerator; @@ -86,12 +87,28 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http _config = config; _connector = connector; _bufferPool = _connector.getByteBufferPool(); - _generator = new HttpGenerator(_config.getSendServerVersion(),_config.getSendXPoweredBy()); - _channel = new HttpChannelOverHttp(connector, config, endPoint, this, new HttpInputOverHTTP(this)); + _generator = newHttpGenerator(); + HttpInput input = newHttpInput(); + _channel = newHttpChannel(input); _parser = newHttpParser(); LOG.debug("New HTTP Connection {}", this); } + protected HttpGenerator newHttpGenerator() + { + return new HttpGenerator(_config.getSendServerVersion(),_config.getSendXPoweredBy()); + } + + protected HttpInput newHttpInput() + { + return new HttpInputOverHTTP(this); + } + + protected HttpChannelOverHttp newHttpChannel(HttpInput httpInput) + { + return new HttpChannelOverHttp(_connector, _config, getEndPoint(), this, httpInput); + } + protected HttpParser newHttpParser() { return new HttpParser(newRequestHandler(), getHttpConfiguration().getRequestHeaderSize()); @@ -361,7 +378,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http } } - private class HttpChannelOverHttp extends HttpChannel + protected class HttpChannelOverHttp extends HttpChannel { public HttpChannelOverHttp(Connector connector, HttpConfiguration config, EndPoint endPoint, HttpTransport transport, HttpInput input) { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInputOverHTTP.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInputOverHTTP.java index 53f2c1287c0..fd7933f1a83 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInputOverHTTP.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInputOverHTTP.java @@ -27,7 +27,7 @@ import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; -class HttpInputOverHTTP extends HttpInput implements Callback +public class HttpInputOverHTTP extends HttpInput implements Callback { private static final Logger LOG = Log.getLogger(HttpInputOverHTTP.class); private final BlockingCallback _readBlocker = new BlockingCallback(); @@ -37,7 +37,7 @@ class HttpInputOverHTTP extends HttpInput implements Callback /** * @param httpConnection */ - HttpInputOverHTTP(HttpConnection httpConnection) + public HttpInputOverHTTP(HttpConnection httpConnection) { _httpConnection = httpConnection; } diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ExtendedServerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ExtendedServerTest.java new file mode 100644 index 00000000000..fd0c1cd2c60 --- /dev/null +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ExtendedServerTest.java @@ -0,0 +1,160 @@ +// +// ======================================================================== +// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.server; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.Socket; +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.SocketChannel; +import java.nio.charset.StandardCharsets; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpVersion; +import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.SelectChannelEndPoint; +import org.eclipse.jetty.io.SelectorManager.ManagedSelector; +import org.eclipse.jetty.server.HttpServerTestFixture.HelloWorldHandler; +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.eclipse.jetty.util.thread.Scheduler; +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +/** + * Extended Server Tester. + */ +public class ExtendedServerTest extends HttpServerTestBase +{ + @Before + public void init() throws Exception + { + startServer(new ServerConnector(_server,new HttpConnectionFactory() + { + @Override + public Connection newConnection(Connector connector, EndPoint endPoint) + { + return configure(new ExtendedHttpConnection(getHttpConfiguration(), connector, endPoint), connector, endPoint); + } + }) + { + + @Override + protected SelectChannelEndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey key) throws IOException + { + return new ExtendedEndPoint(channel,selectSet,key, getScheduler(), getIdleTimeout()); + } + + }); + } + + private static class ExtendedEndPoint extends SelectChannelEndPoint + { + private volatile long _lastSelected; + + public ExtendedEndPoint(SocketChannel channel, ManagedSelector selector, SelectionKey key, Scheduler scheduler, long idleTimeout) + { + super(channel,selector,key,scheduler,idleTimeout); + } + + @Override + public void onSelected() + { + _lastSelected=System.currentTimeMillis(); + super.onSelected(); + } + + long getLastSelected() + { + return _lastSelected; + } + } + + private static class ExtendedHttpConnection extends HttpConnection + { + public ExtendedHttpConnection(HttpConfiguration config, Connector connector, EndPoint endPoint) + { + super(config,connector,endPoint); + } + + @Override + protected HttpChannelOverHttp newHttpChannel(HttpInput httpInput) + { + return new HttpChannelOverHttp(getConnector(), getHttpConfiguration(), getEndPoint(), this, httpInput) + { + @Override + public boolean startRequest(HttpMethod httpMethod, String method, ByteBuffer uri, HttpVersion version) + { + getRequest().setAttribute("DispatchedAt",((ExtendedEndPoint)getEndPoint()).getLastSelected()); + return super.startRequest(httpMethod,method,uri,version); + } + }; + } + } + + @Test + public void testExtended() throws Exception + { + configureServer(new DispatchedAtHandler()); + + try (Socket client = newSocket(_serverURI.getHost(), _serverURI.getPort())) + { + OutputStream os = client.getOutputStream(); + + long start=System.currentTimeMillis(); + os.write("GET / HTTP/1.0\r\n".getBytes(StandardCharsets.ISO_8859_1)); + os.flush(); + Thread.sleep(200); + long end=System.currentTimeMillis(); + os.write("\r\n".getBytes(StandardCharsets.ISO_8859_1)); + + // Read the response. + String response = readResponse(client); + + Assert.assertThat(response, Matchers.containsString("HTTP/1.1 200 OK")); + Assert.assertThat(response, Matchers.containsString("DispatchedAt=")); + + String s=response.substring(response.indexOf("DispatchedAt=")+13); + s=s.substring(0,s.indexOf('\n')); + long dispatched=Long.valueOf(s); + + Assert.assertThat(dispatched, Matchers.greaterThanOrEqualTo(start)); + Assert.assertThat(dispatched, Matchers.lessThan(end)); + } + } + + + protected static class DispatchedAtHandler extends AbstractHandler + { + @Override + public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + { + baseRequest.setHandled(true); + response.setStatus(200); + response.getOutputStream().print("DispatchedAt="+request.getAttribute("DispatchedAt")+"\r\n"); + } + } +} diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java index 9e9776e72d6..f2a79f10411 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java @@ -111,6 +111,26 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture "\n" + RESPONSE2_CONTENT; + @Test + public void testSimple() throws Exception + { + configureServer(new HelloWorldHandler()); + + try (Socket client = newSocket(_serverURI.getHost(), _serverURI.getPort())) + { + OutputStream os = client.getOutputStream(); + + os.write("GET / HTTP/1.0\r\n\r\n".getBytes(StandardCharsets.ISO_8859_1)); + os.flush(); + + // Read the response. + String response = readResponse(client); + + Assert.assertThat(response, Matchers.containsString("HTTP/1.1 200 OK")); + Assert.assertThat(response, Matchers.containsString("Hello world")); + } + } + /* * Feed a full header method