diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java index 8358243501e..0e39d1e05ce 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java @@ -152,7 +152,7 @@ public abstract class HttpSender implements AsyncContentProvider.Listener } default: { - throw new IllegalStateException(current.toString()); + throw illegalSenderState(current); } } } @@ -178,7 +178,7 @@ public abstract class HttpSender implements AsyncContentProvider.Listener if (expects100Continue(request)) newSenderState = content.hasContent() ? SenderState.EXPECTING_WITH_CONTENT : SenderState.EXPECTING; if (!updateSenderState(SenderState.IDLE, newSenderState)) - throw new IllegalStateException(); + throw illegalSenderState(SenderState.IDLE); // Setting the listener may trigger calls to onContent() by other // threads so we must set it only after the sender state has been updated @@ -462,7 +462,7 @@ public abstract class HttpSender implements AsyncContentProvider.Listener { // There is content to send. if (!updateSenderState(current, SenderState.SENDING)) - throw new IllegalStateException(); + throw illegalSenderState(current); LOG.debug("Proceeding while waiting"); sendContent(exchange, content, contentCallback); // TODO old style usage! return; @@ -471,14 +471,14 @@ public abstract class HttpSender implements AsyncContentProvider.Listener { // No content to send yet - it's deferred. if (!updateSenderState(current, SenderState.IDLE)) - throw new IllegalStateException(); + throw illegalSenderState(current); LOG.debug("Proceeding deferred"); return; } } default: { - throw new IllegalStateException(current.toString()); + throw illegalSenderState(current); } } } @@ -532,6 +532,11 @@ public abstract class HttpSender implements AsyncContentProvider.Listener } } + private RuntimeException illegalSenderState(SenderState current) + { + return new IllegalStateException("Expected " + current + " found " + senderState.get() + " instead"); + } + /** * The request states {@link HttpSender} goes through when sending a request. */ @@ -723,7 +728,7 @@ public abstract class HttpSender implements AsyncContentProvider.Listener } default: { - throw new IllegalStateException(); + throw illegalSenderState(current); } } } @@ -792,11 +797,11 @@ public abstract class HttpSender implements AsyncContentProvider.Listener return Action.SCHEDULED; } } - throw new IllegalStateException(); + throw illegalSenderState(current); } default: { - throw new IllegalStateException(); + throw illegalSenderState(current); } } } diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java index 505d264747f..1b688e16e50 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java @@ -213,7 +213,11 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res @Override public void earlyEOF() { - failAndClose(new EOFException()); + HttpExchange exchange = getHttpExchange(); + if (exchange == null) + getHttpConnection().close(); + else + failAndClose(new EOFException()); } @Override @@ -244,10 +248,8 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res private void failAndClose(Throwable failure) { - // Close the connection anyway, even if responseFailure() returns false. - // This may happen for idle closes (there is no exchange to fail). - responseFailure(failure); - getHttpConnection().close(failure); + if (responseFailure(failure)) + getHttpConnection().close(failure); } @Override diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java index c6789e20cdb..bedd3482776 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java @@ -1201,7 +1201,30 @@ public class HttpClientTest extends AbstractHttpClientServerTest } @Test - public void testContentDelimitedByEOFWithSlowRequest() throws Exception + public void testSmallContentDelimitedByEOFWithSlowRequestHTTP10() throws Exception + { + testContentDelimitedByEOFWithSlowRequest(HttpVersion.HTTP_1_0, 1024); + } + + @Test + public void testBigContentDelimitedByEOFWithSlowRequestHTTP10() throws Exception + { + testContentDelimitedByEOFWithSlowRequest(HttpVersion.HTTP_1_0, 128 * 1024); + } + + @Test + public void testSmallContentDelimitedByEOFWithSlowRequestHTTP11() throws Exception + { + testContentDelimitedByEOFWithSlowRequest(HttpVersion.HTTP_1_1, 1024); + } + + @Test + public void testBigContentDelimitedByEOFWithSlowRequestHTTP11() throws Exception + { + testContentDelimitedByEOFWithSlowRequest(HttpVersion.HTTP_1_1, 128 * 1024); + } + + private void testContentDelimitedByEOFWithSlowRequest(final HttpVersion version, int length) throws Exception { // This test is crafted in a way that the response completes before the request is fully written. // With SSL, the response coming down will close the SSLEngine so it would not be possible to @@ -1210,7 +1233,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest // This is a limit of Java's SSL implementation that does not allow half closes. Assume.assumeTrue(sslContextFactory == null); - final byte[] data = new byte[8 * 1024]; + final byte[] data = new byte[length]; new Random().nextBytes(data); start(new AbstractHandler() { @@ -1218,7 +1241,9 @@ public class HttpClientTest extends AbstractHttpClientServerTest public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); - response.setHeader("Connection", "close"); + // Send Connection: close to avoid that the server chunks the content with HTTP 1.1. + if (version.compareTo(HttpVersion.HTTP_1_0) > 0) + response.setHeader("Connection", "close"); response.getOutputStream().write(data); } }); @@ -1226,6 +1251,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest DeferredContentProvider content = new DeferredContentProvider(ByteBuffer.wrap(new byte[]{0})); Request request = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) + .version(version) .content(content); FutureResponseListener listener = new FutureResponseListener(request); request.send(listener); diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTPTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTPTest.java index 32db8dd5eaf..0be2b913973 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTPTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTPTest.java @@ -23,6 +23,7 @@ import java.io.EOFException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Collections; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -221,8 +222,22 @@ public class HttpReceiverOverHTTPTest "Content-Length: " + gzip.length + "\r\n" + "Content-Encoding: gzip\r\n" + "\r\n"); - HttpExchange exchange = newExchange(); - FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0); + + HttpRequest request = (HttpRequest)client.newRequest("http://localhost"); + final CountDownLatch latch = new CountDownLatch(1); + FutureResponseListener listener = new FutureResponseListener(request) + { + @Override + public void onContent(Response response, ByteBuffer content) + { + super.onContent(response, content); + latch.countDown(); + } + }; + HttpExchange exchange = new HttpExchange(destination, request, Collections.singletonList(listener)); + connection.getHttpChannel().associate(exchange); + exchange.requestComplete(); + exchange.terminateRequest(null); connection.getHttpChannel().receive(); endPoint.reset(); @@ -241,6 +256,7 @@ public class HttpReceiverOverHTTPTest ContentResponse response = listener.get(5, TimeUnit.SECONDS); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); + Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); Assert.assertArrayEquals(data, response.getContent()); } } diff --git a/jetty-distribution/pom.xml b/jetty-distribution/pom.xml index 542484fda82..5003e023f8e 100644 --- a/jetty-distribution/pom.xml +++ b/jetty-distribution/pom.xml @@ -569,7 +569,7 @@ jetty.home=${assembly-directory} jetty.base=${assembly-directory}/demo-base - --add-to-start=server,continuation,deploy,ext,resources,client,annotations,jndi,servlets + --add-to-start=server,continuation,deploy,websocket,ext,resources,client,annotations,jndi,servlets --add-to-startd-ini=jsp,jstl,http,https diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java index b14574c0d82..ee40a74af16 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java @@ -1357,24 +1357,36 @@ public class HttpParser protected boolean parseContent(ByteBuffer buffer) { + int remaining=buffer.remaining(); + if (remaining==0 && _state==State.CONTENT) + { + long content=_contentLength - _contentPosition; + if (content == 0) + { + setState(State.END); + if (_handler.messageComplete()) + return true; + } + } + // Handle _content byte ch; - while (_state.ordinal() < State.END.ordinal() && buffer.hasRemaining()) + while (_state.ordinal() < State.END.ordinal() && remaining>0) { switch (_state) { case EOF_CONTENT: _contentChunk=buffer.asReadOnlyBuffer(); - _contentPosition += _contentChunk.remaining(); - buffer.position(buffer.position()+_contentChunk.remaining()); + _contentPosition += remaining; + buffer.position(buffer.position()+remaining); if (_handler.content(_contentChunk)) return true; break; case CONTENT: { - long remaining=_contentLength - _contentPosition; - if (remaining == 0) + long content=_contentLength - _contentPosition; + if (content == 0) { setState(State.END); if (_handler.messageComplete()) @@ -1385,25 +1397,25 @@ public class HttpParser _contentChunk=buffer.asReadOnlyBuffer(); // limit content by expected size - if (_contentChunk.remaining() > remaining) + if (remaining > content) { // We can cast remaining to an int as we know that it is smaller than // or equal to length which is already an int. - _contentChunk.limit(_contentChunk.position()+(int)remaining); + _contentChunk.limit(_contentChunk.position()+(int)content); } _contentPosition += _contentChunk.remaining(); buffer.position(buffer.position()+_contentChunk.remaining()); - boolean handle=_handler.content(_contentChunk); + if (_handler.content(_contentChunk)) + return true; + if(_contentPosition == _contentLength) { setState(State.END); if (_handler.messageComplete()) return true; } - if (handle) - return true; } break; } @@ -1463,8 +1475,8 @@ public class HttpParser case CHUNK: { - int remaining=_chunkLength - _chunkPosition; - if (remaining == 0) + int chunk=_chunkLength - _chunkPosition; + if (chunk == 0) { setState(State.CHUNKED_CONTENT); } @@ -1472,13 +1484,13 @@ public class HttpParser { _contentChunk=buffer.asReadOnlyBuffer(); - if (_contentChunk.remaining() > remaining) - _contentChunk.limit(_contentChunk.position()+remaining); - remaining=_contentChunk.remaining(); + if (remaining > chunk) + _contentChunk.limit(_contentChunk.position()+chunk); + chunk=_contentChunk.remaining(); - _contentPosition += remaining; - _chunkPosition += remaining; - buffer.position(buffer.position()+remaining); + _contentPosition += chunk; + _chunkPosition += chunk; + buffer.position(buffer.position()+chunk); if (_handler.content(_contentChunk)) return true; } @@ -1493,7 +1505,10 @@ public class HttpParser default: break; + } + + remaining=buffer.remaining(); } return false; } diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/NetworkTrafficSelectChannelEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/NetworkTrafficSelectChannelEndPoint.java index d45902e36dd..a4b6f7d2a16 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/NetworkTrafficSelectChannelEndPoint.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/NetworkTrafficSelectChannelEndPoint.java @@ -19,11 +19,13 @@ package org.eclipse.jetty.io; import java.io.IOException; +import java.net.Socket; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; import java.util.List; +import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.thread.Scheduler; @@ -57,9 +59,11 @@ public class NetworkTrafficSelectChannelEndPoint extends SelectChannelEndPoint if (b.hasRemaining()) { int position = b.position(); + ByteBuffer view=b.slice(); flushed&=super.flush(b); int l=b.position()-position; - notifyOutgoing(b, position, l); + view.limit(view.position()+l); + notifyOutgoing(view); if (!flushed) break; } @@ -67,9 +71,12 @@ public class NetworkTrafficSelectChannelEndPoint extends SelectChannelEndPoint return flushed; } + - public void notifyOpened() + @Override + public void onOpen() { + super.onOpen(); if (listeners != null && !listeners.isEmpty()) { for (NetworkTrafficListener listener : listeners) @@ -86,6 +93,27 @@ public class NetworkTrafficSelectChannelEndPoint extends SelectChannelEndPoint } } + @Override + public void onClose() + { + super.onClose(); + if (listeners != null && !listeners.isEmpty()) + { + for (NetworkTrafficListener listener : listeners) + { + try + { + listener.closed(getSocket()); + } + catch (Exception x) + { + LOG.warn(x); + } + } + } + } + + public void notifyIncoming(ByteBuffer buffer, int read) { if (listeners != null && !listeners.isEmpty() && read > 0) @@ -105,18 +133,16 @@ public class NetworkTrafficSelectChannelEndPoint extends SelectChannelEndPoint } } - public void notifyOutgoing(ByteBuffer buffer, int position, int written) + public void notifyOutgoing(ByteBuffer view) { - if (listeners != null && !listeners.isEmpty() && written > 0) + if (listeners != null && !listeners.isEmpty() && view.hasRemaining()) { + Socket socket=getSocket(); for (NetworkTrafficListener listener : listeners) { try { - ByteBuffer view = buffer.slice(); - view.position(position); - view.limit(position + written); - listener.outgoing(getSocket(), view); + listener.outgoing(socket, view); } catch (Exception x) { @@ -126,21 +152,4 @@ public class NetworkTrafficSelectChannelEndPoint extends SelectChannelEndPoint } } - public void notifyClosed() - { - if (listeners != null && !listeners.isEmpty()) - { - for (NetworkTrafficListener listener : listeners) - { - try - { - listener.closed(getSocket()); - } - catch (Exception x) - { - LOG.warn(x); - } - } - } - } } diff --git a/jetty-osgi/jetty-osgi-httpservice/pom.xml b/jetty-osgi/jetty-osgi-httpservice/pom.xml index 3749cab43e7..91087d7bcca 100644 --- a/jetty-osgi/jetty-osgi-httpservice/pom.xml +++ b/jetty-osgi/jetty-osgi-httpservice/pom.xml @@ -28,6 +28,7 @@ org.eclipse.osgi org.eclipse.osgi + provided javax.servlet diff --git a/jetty-osgi/test-jetty-osgi-context/pom.xml b/jetty-osgi/test-jetty-osgi-context/pom.xml index 4e1c28a342b..941b13bfb3c 100644 --- a/jetty-osgi/test-jetty-osgi-context/pom.xml +++ b/jetty-osgi/test-jetty-osgi-context/pom.xml @@ -21,10 +21,12 @@ org.eclipse.osgi org.eclipse.osgi + provided org.eclipse.osgi org.eclipse.osgi.services + provided org.eclipse.jetty.toolchain diff --git a/jetty-osgi/test-jetty-osgi-webapp/pom.xml b/jetty-osgi/test-jetty-osgi-webapp/pom.xml index 00697afd6dd..1a0f37b6655 100644 --- a/jetty-osgi/test-jetty-osgi-webapp/pom.xml +++ b/jetty-osgi/test-jetty-osgi-webapp/pom.xml @@ -21,10 +21,12 @@ org.eclipse.osgi org.eclipse.osgi + provided org.eclipse.osgi org.eclipse.osgi.services + provided diff --git a/jetty-osgi/test-jetty-osgi/pom.xml b/jetty-osgi/test-jetty-osgi/pom.xml index 5cc1640678c..cf6810a052a 100644 --- a/jetty-osgi/test-jetty-osgi/pom.xml +++ b/jetty-osgi/test-jetty-osgi/pom.xml @@ -14,102 +14,36 @@ ${project.groupId}.boot.test.spdy http://download.eclipse.org/jetty/orbit/ target/distribution - 2.6.0 - 1.4.0 - 1.5.1 - 4.0.3 + 3.4.0 + 1.5.2 1.0 - 1.7.6 + 1.8.5 - - - org.ops4j.pax.swissbox - pax-swissbox-core - ${paxswissbox.version} - test - - - org.ops4j.pax.swissbox - pax-swissbox-extender - ${paxswissbox.version} - test - - - org.ops4j.pax.swissbox - pax-swissbox-lifecycle - ${paxswissbox.version} - test - - - org.ops4j.pax.swissbox - pax-swissbox-framework - ${paxswissbox.version} - test - + org.ops4j.pax.exam pax-exam ${exam.version} test - - org.apache.geronimo.specs - geronimo-atinject_1.0_spec - ${injection.bundle.version} - test - org.ops4j.pax.exam pax-exam-inject ${exam.version} test - - org.apache.aries.spifly - org.apache.aries.spifly.dynamic.bundle - 1.0.0 - test - - - - - org.ops4j.pax.exam pax-exam-container-forked ${exam.version} test - --> - - - org.ops4j.pax.exam - pax-exam-container-paxrunner - ${exam.version} - test - - - - org.ops4j.pax.runner - pax-runner-no-jcl - ${runner.version} - test - - + org.ops4j.pax.exam pax-exam-junit4 @@ -134,28 +68,81 @@ ${url.version} test + + - + + org.eclipse + osgi + 3.9.1-v20140110-1610 + test + + + org.eclipse.osgi + org.eclipse.osgi.services + test + + + + + + org.eclipse.jetty.osgi + jetty-osgi-boot + ${project.version} + test + + + org.eclipse.osgi + org.eclipse.osgi + + + org.eclipse.osgi + org.eclipse.osgi.services + + + + + org.eclipse.jetty.osgi + jetty-osgi-boot-jsp + ${project.version} + test + + + org.eclipse.osgi + org.eclipse.osgi + + + org.eclipse.osgi + org.eclipse.osgi.services + + + + + org.eclipse.jetty.toolchain + jetty-jsp-fragment + 2.3.3 + test + + + org.eclipse.jetty.osgi + jetty-httpservice + ${project.version} + test + + javax.servlet javax.servlet-api @@ -167,47 +154,34 @@ 1.1.1 test - - - org.eclipse.jetty.osgi - jetty-osgi-boot - ${project.version} - provided - - - org.eclipse.jetty.osgi - jetty-osgi-boot-jsp - ${project.version} - provided - - - org.eclipse.jetty.toolchain - jetty-jsp-fragment - 2.3.3 - provided - - - org.eclipse.jetty.osgi - jetty-httpservice - ${project.version} - provided - - + + org.apache.geronimo.specs + geronimo-atinject_1.0_spec + ${injection.bundle.version} + test + + + org.apache.aries.spifly + org.apache.aries.spifly.dynamic.bundle + 1.0.0 + test + + org.ow2.asm asm 4.1 - - + + org.ow2.asm asm-commons 4.1 - - + + org.ow2.asm asm-tree 4.1 - + @@ -386,7 +360,9 @@ ${project.version} runtime + + javax.servlet servlet-api @@ -409,6 +384,9 @@ servlet runtime + +--> + org.eclipse.jetty test-jetty-webapp diff --git a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootContextAsService.java b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootContextAsService.java index 94ecd39df48..0dfc1bb0e18 100644 --- a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootContextAsService.java +++ b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootContextAsService.java @@ -37,12 +37,13 @@ import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.osgi.boot.OSGiServerConstants; import org.eclipse.jetty.server.handler.ContextHandler; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.ops4j.pax.exam.CoreOptions; import org.ops4j.pax.exam.Option; -import org.ops4j.pax.exam.junit.Configuration; -import org.ops4j.pax.exam.junit.JUnit4TestRunner; +import org.ops4j.pax.exam.Configuration; +import org.ops4j.pax.exam.junit.PaxExam; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; @@ -55,12 +56,11 @@ import org.osgi.framework.ServiceReference; * Tests the ServiceContextProvider. * */ -@RunWith(JUnit4TestRunner.class) +@RunWith(PaxExam.class) public class TestJettyOSGiBootContextAsService { - private static final boolean LOGGING_ENABLED = false; + private static final String LOG_LEVEL = "WARN"; - private static final boolean REMOTE_DEBUGGING = false; @Inject BundleContext bundleContext = null; @@ -69,7 +69,6 @@ public class TestJettyOSGiBootContextAsService public static Option[] configure() { ArrayList