From 04dd885421918ec6ad1bdb252439876e08d663a6 Mon Sep 17 00:00:00 2001 From: Jeff Pinner Date: Sat, 10 Jan 2015 10:53:54 -0800 Subject: [PATCH] SPDY: fix support for pushed resources in SpdyHttpEncoder Motivation: The SpdyHttpDecoder was modified to support pushed resources that are divided into multiple frames. The decoder accepts a pushed SpdySynStreamFrame containing the request headers, followed by a SpdyHeadersFrame containing the response headers. Modifications: This commit modifies the SpdyHttpEncoder so that it encodes pushed resources in a format that the SpdyHttpDecoder can decode. The encoder will accept an HttpRequest object containing the request headers, followed by an HttpResponse object containing the response headers. Result: The SpdyHttpEncoder will create a SpdySynStreamFrame followed by a SpdyHeadersFrame when sending pushed resources. --- .../handler/codec/spdy/SpdyHttpEncoder.java | 103 ++++++++---------- .../handler/codec/spdy/SpdyHttpHeaders.java | 4 - 2 files changed, 43 insertions(+), 64 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpEncoder.java index 0433443d04..9891b55227 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpEncoder.java @@ -19,7 +19,6 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageEncoder; import io.netty.handler.codec.UnsupportedMessageTypeException; import io.netty.handler.codec.http.FullHttpMessage; -import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpContent; import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpHeaders; @@ -75,7 +74,7 @@ import static io.netty.handler.codec.spdy.SpdyHeaders.HttpNames.*; * *

Pushed Resource Annotations

* - * SPDY specific headers must be added to pushed {@link HttpResponse}s: + * SPDY specific headers must be added to pushed {@link HttpRequest}s: * * * @@ -96,10 +95,6 @@ import static io.netty.handler.codec.spdy.SpdyHeaders.HttpNames.*; * 0 represents the highest priority and 7 represents the lowest. * This header is optional and defaults to 0. * - * - * - * - * *
Header NameHeader Value
{@code "X-SPDY-URL"}The absolute path for the resource being pushed.
* *

Required Annotations

@@ -126,7 +121,6 @@ import static io.netty.handler.codec.spdy.SpdyHeaders.HttpNames.*; */ public class SpdyHttpEncoder extends MessageToMessageEncoder { - private final int spdyVersion; private int currentStreamId; /** @@ -138,7 +132,6 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder { if (version == null) { throw new NullPointerException("version"); } - spdyVersion = version.getVersion(); } @Override @@ -153,22 +146,16 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder { SpdySynStreamFrame spdySynStreamFrame = createSynStreamFrame(httpRequest); out.add(spdySynStreamFrame); - last = spdySynStreamFrame.isLast(); + last = spdySynStreamFrame.isLast() || spdySynStreamFrame.isUnidirectional(); valid = true; } if (msg instanceof HttpResponse) { HttpResponse httpResponse = (HttpResponse) msg; - if (httpResponse.headers().contains(SpdyHttpHeaders.Names.ASSOCIATED_TO_STREAM_ID)) { - SpdySynStreamFrame spdySynStreamFrame = createSynStreamFrame(httpResponse); - last = spdySynStreamFrame.isLast(); - out.add(spdySynStreamFrame); - } else { - SpdySynReplyFrame spdySynReplyFrame = createSynReplyFrame(httpResponse); - last = spdySynReplyFrame.isLast(); - out.add(spdySynReplyFrame); - } + SpdyHeadersFrame spdyHeadersFrame = createHeadersFrame(httpResponse); + out.add(spdyHeadersFrame); + last = spdyHeadersFrame.isLast(); valid = true; } if (msg instanceof HttpContent && !last) { @@ -177,22 +164,23 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder { chunk.content().retain(); SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(currentStreamId, chunk.content()); - spdyDataFrame.setLast(chunk instanceof LastHttpContent); if (chunk instanceof LastHttpContent) { LastHttpContent trailer = (LastHttpContent) chunk; HttpHeaders trailers = trailer.trailingHeaders(); if (trailers.isEmpty()) { + spdyDataFrame.setLast(true); out.add(spdyDataFrame); } else { // Create SPDY HEADERS frame out of trailers SpdyHeadersFrame spdyHeadersFrame = new DefaultSpdyHeadersFrame(currentStreamId); + spdyHeadersFrame.setLast(true); for (Map.Entry entry: trailers) { spdyHeadersFrame.headers().add(entry.getKey(), entry.getValue()); } - // Write HEADERS frame and append Data Frame - out.add(spdyHeadersFrame); + // Write DATA frame and append HEADERS frame out.add(spdyDataFrame); + out.add(spdyHeadersFrame); } } else { out.add(spdyDataFrame); @@ -206,19 +194,17 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder { } } - private SpdySynStreamFrame createSynStreamFrame(HttpMessage httpMessage) - throws Exception { - // Get the Stream-ID, Associated-To-Stream-ID, Priority, URL, and scheme from the headers - final HttpHeaders httpHeaders = httpMessage.headers(); - int streamID = httpMessage.headers().getInt(Names.STREAM_ID); - int associatedToStreamId = httpMessage.headers().getInt(Names.ASSOCIATED_TO_STREAM_ID, 0); - byte priority = (byte) httpMessage.headers().getInt(Names.PRIORITY, 0); - String URL = httpHeaders.get(Names.URL); - String scheme = httpHeaders.get(Names.SCHEME); + @SuppressWarnings("deprecation") + private SpdySynStreamFrame createSynStreamFrame(HttpRequest httpRequest) throws Exception { + // Get the Stream-ID, Associated-To-Stream-ID, Priority, and scheme from the headers + final HttpHeaders httpHeaders = httpRequest.headers(); + int streamId = httpHeaders.getInt(Names.STREAM_ID); + int associatedToStreamId = httpHeaders.getInt(Names.ASSOCIATED_TO_STREAM_ID, 0); + byte priority = (byte) httpHeaders.getInt(Names.PRIORITY, 0); + CharSequence scheme = httpHeaders.get(Names.SCHEME); httpHeaders.remove(Names.STREAM_ID); httpHeaders.remove(Names.ASSOCIATED_TO_STREAM_ID); httpHeaders.remove(Names.PRIORITY); - httpHeaders.remove(Names.URL); httpHeaders.remove(Names.SCHEME); // The Connection, Keep-Alive, Proxy-Connection, and Transfer-Encoding @@ -229,30 +215,18 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder { httpHeaders.remove(HttpHeaderNames.TRANSFER_ENCODING); SpdySynStreamFrame spdySynStreamFrame = - new DefaultSpdySynStreamFrame(streamID, associatedToStreamId, priority); + new DefaultSpdySynStreamFrame(streamId, associatedToStreamId, priority); // Unfold the first line of the message into name/value pairs SpdyHeaders frameHeaders = spdySynStreamFrame.headers(); - if (httpMessage instanceof FullHttpRequest) { - HttpRequest httpRequest = (HttpRequest) httpMessage; - frameHeaders.setObject(METHOD, httpRequest.method()); - frameHeaders.set(PATH, httpRequest.uri()); - frameHeaders.setObject(VERSION, httpMessage.protocolVersion()); - } - if (httpMessage instanceof HttpResponse) { - HttpResponse httpResponse = (HttpResponse) httpMessage; - frameHeaders.setInt(STATUS, httpResponse.status().code()); - frameHeaders.set(PATH, URL); - frameHeaders.setObject(VERSION, httpMessage.protocolVersion()); - spdySynStreamFrame.setUnidirectional(true); - } + frameHeaders.setObject(METHOD, httpRequest.method()); + frameHeaders.set(PATH, httpRequest.uri()); + frameHeaders.setObject(VERSION, httpRequest.protocolVersion()); // Replace the HTTP host header with the SPDY host header - if (spdyVersion >= 3) { - String host = httpMessage.headers().get(HttpHeaderNames.HOST); - httpHeaders.remove(HttpHeaderNames.HOST); - frameHeaders.set(HOST, host); - } + CharSequence host = httpHeaders.get(HttpHeaderNames.HOST); + httpHeaders.remove(HttpHeaderNames.HOST); + frameHeaders.set(HOST, host); // Set the SPDY scheme header if (scheme == null) { @@ -265,16 +239,20 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder { frameHeaders.add(entry.getKey(), entry.getValue()); } currentStreamId = spdySynStreamFrame.streamId(); - spdySynStreamFrame.setLast(isLast(httpMessage)); + if (associatedToStreamId == 0) { + spdySynStreamFrame.setLast(isLast(httpRequest)); + } else { + spdySynStreamFrame.setUnidirectional(true); + } return spdySynStreamFrame; } - private SpdySynReplyFrame createSynReplyFrame(HttpResponse httpResponse) - throws Exception { + @SuppressWarnings("deprecation") + private SpdyHeadersFrame createHeadersFrame(HttpResponse httpResponse) throws Exception { // Get the Stream-ID from the headers final HttpHeaders httpHeaders = httpResponse.headers(); - int streamID = httpResponse.headers().getInt(Names.STREAM_ID); + int streamId = httpHeaders.getInt(Names.STREAM_ID); httpHeaders.remove(Names.STREAM_ID); // The Connection, Keep-Alive, Proxy-Connection, and Transfer-Encoding @@ -284,21 +262,26 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder { httpHeaders.remove("Proxy-Connection"); httpHeaders.remove(HttpHeaderNames.TRANSFER_ENCODING); - SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamID); - SpdyHeaders frameHeaders = spdySynReplyFrame.headers(); + SpdyHeadersFrame spdyHeadersFrame; + if (SpdyCodecUtil.isServerId(streamId)) { + spdyHeadersFrame = new DefaultSpdyHeadersFrame(streamId); + } else { + spdyHeadersFrame = new DefaultSpdySynReplyFrame(streamId); + } + SpdyHeaders frameHeaders = spdyHeadersFrame.headers(); // Unfold the first line of the response into name/value pairs frameHeaders.setInt(STATUS, httpResponse.status().code()); frameHeaders.setObject(VERSION, httpResponse.protocolVersion()); // Transfer the remaining HTTP headers for (Map.Entry entry: httpHeaders) { - spdySynReplyFrame.headers().add(entry.getKey(), entry.getValue()); + spdyHeadersFrame.headers().add(entry.getKey(), entry.getValue()); } - currentStreamId = streamID; - spdySynReplyFrame.setLast(isLast(httpResponse)); + currentStreamId = streamId; + spdyHeadersFrame.setLast(isLast(httpResponse)); - return spdySynReplyFrame; + return spdyHeadersFrame; } /** diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpHeaders.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpHeaders.java index e28a3be2a6..bd9d11b2c3 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpHeaders.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpHeaders.java @@ -39,10 +39,6 @@ public final class SpdyHttpHeaders { * {@code "X-SPDY-Priority"} */ public static final AsciiString PRIORITY = new AsciiString("X-SPDY-Priority"); - /** - * {@code "X-SPDY-URL"} - */ - public static final AsciiString URL = new AsciiString("X-SPDY-URL"); /** * {@code "X-SPDY-Scheme"} */