From 6b3c8d06a9693b4924b4f9f369e523ee7a01486b Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Tue, 5 Aug 2014 16:14:53 +1000 Subject: [PATCH] partial 100 continues support in http2 --- .../server/HTTP2ServerConnectionFactory.java | 2 +- .../http2/server/HttpChannelOverHTTP2.java | 55 ++++++++++++++++--- .../jetty/server/HttpChannelOverHttp.java | 1 - 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.java index 22a44885926..8b080691518 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.java @@ -100,7 +100,7 @@ public class HTTP2ServerConnectionFactory extends AbstractHTTP2ServerConnectionF HttpChannelOverHTTP2 channel = new HttpChannelOverHTTP2(connector, httpConfiguration, endPoint, transport, input, stream); stream.setAttribute(CHANNEL_ATTRIBUTE, channel); - channel.requestHeaders(frame); + channel.onHeadersFrame(frame); return frame.isEndStream() ? null : this; } diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java index 2e0f6122c01..3ee7c939a7e 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java @@ -18,11 +18,14 @@ package org.eclipse.jetty.http2.server; +import java.io.IOException; import java.nio.ByteBuffer; 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.HttpHeaderValue; import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.http2.api.Stream; import org.eclipse.jetty.http2.frames.DataFrame; @@ -46,6 +49,7 @@ public class HttpChannelOverHTTP2 extends HttpChannel private static final HttpField SERVER_VERSION=new HttpField(HttpHeader.SERVER,HttpConfiguration.SERVER_VERSION); private static final HttpField POWERED_BY=new HttpField(HttpHeader.X_POWERED_BY,HttpConfiguration.SERVER_VERSION); private final Stream stream; // TODO recycle channel for new Stream? + private boolean _expect100Continue = false; public HttpChannelOverHTTP2(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransport transport, HttpInput input, Stream stream) { @@ -53,7 +57,13 @@ public class HttpChannelOverHTTP2 extends HttpChannel this.stream = stream; } - public void requestHeaders(HeadersFrame frame) + @Override + public boolean isExpecting100Continue() + { + return _expect100Continue; + } + + public void onHeadersFrame(HeadersFrame frame) { MetaData metaData = frame.getMetaData(); if (!metaData.isRequest()) @@ -63,22 +73,20 @@ public class HttpChannelOverHTTP2 extends HttpChannel } MetaData.Request request = (MetaData.Request)metaData; - - // The specification says user agents MUST support gzip encoding. - // Based on that, some browser does not send the header, but it's - // important that applications can find it (e.g. GzipFilter). HttpFields fields = request.getFields(); - if (!fields.contains(HttpHeader.ACCEPT_ENCODING, "gzip")) - fields.add(ACCEPT_ENCODING_GZIP); + + _expect100Continue = fields.contains(HttpHeader.EXPECT,HttpHeaderValue.CONTINUE.asString()); // TODO make this a better field for h2 hpack generation + HttpFields response=getResponse().getHttpFields(); if (getHttpConfiguration().getSendServerVersion()) - getResponse().getHttpFields().add(SERVER_VERSION); + response.add(SERVER_VERSION); if (getHttpConfiguration().getSendXPoweredBy()) - getResponse().getHttpFields().add(POWERED_BY); + response.add(POWERED_BY); onRequest(request); + if (frame.isEndStream()) { onRequestComplete(); @@ -127,4 +135,33 @@ public class HttpChannelOverHTTP2 extends HttpChannel onRequestComplete(); } } + + /** + * If the associated response has the Expect header set to 100 Continue, + * then accessing the input stream indicates that the handler/servlet + * is ready for the request body and thus a 100 Continue response is sent. + * + * @throws IOException if the InputStream cannot be created + */ + @Override + public void continue100(int available) throws IOException + { + // If the client is expecting 100 CONTINUE, then send it now. + // TODO: consider using an AtomicBoolean ? + if (isExpecting100Continue()) + { + _expect100Continue = false; + + // is content missing? + if (available == 0) + { + if (getResponse().isCommitted()) + throw new IOException("Committed before 100 Continues"); + + boolean committed = sendResponse(HttpGenerator.CONTINUE_100_INFO, null, false); + if (!committed) + throw new IOException("Concurrent commit while trying to send 100-Continue"); + } + } + } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelOverHttp.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelOverHttp.java index ae8c6143441..625213050eb 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelOverHttp.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelOverHttp.java @@ -199,7 +199,6 @@ class HttpChannelOverHttp extends HttpChannel implements HttpParser.RequestHandl if (getResponse().isCommitted()) throw new IOException("Committed before 100 Continues"); - // TODO: break this dependency with HttpGenerator boolean committed = sendResponse(HttpGenerator.CONTINUE_100_INFO, null, false); if (!committed) throw new IOException("Concurrent commit while trying to send 100-Continue");