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.
This commit is contained in:
parent
54291daa9a
commit
017a5ef4e4
@ -19,7 +19,6 @@ import io.netty.channel.ChannelHandlerContext;
|
|||||||
import io.netty.handler.codec.MessageToMessageEncoder;
|
import io.netty.handler.codec.MessageToMessageEncoder;
|
||||||
import io.netty.handler.codec.UnsupportedMessageTypeException;
|
import io.netty.handler.codec.UnsupportedMessageTypeException;
|
||||||
import io.netty.handler.codec.http.FullHttpMessage;
|
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.HttpContent;
|
||||||
import io.netty.handler.codec.http.HttpHeaders;
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
import io.netty.handler.codec.http.HttpMessage;
|
import io.netty.handler.codec.http.HttpMessage;
|
||||||
@ -71,7 +70,7 @@ import java.util.Map;
|
|||||||
*
|
*
|
||||||
* <h3>Pushed Resource Annotations</h3>
|
* <h3>Pushed Resource Annotations</h3>
|
||||||
*
|
*
|
||||||
* SPDY specific headers must be added to pushed {@link HttpResponse}s:
|
* SPDY specific headers must be added to pushed {@link HttpRequest}s:
|
||||||
* <table border=1>
|
* <table border=1>
|
||||||
* <tr>
|
* <tr>
|
||||||
* <th>Header Name</th><th>Header Value</th>
|
* <th>Header Name</th><th>Header Value</th>
|
||||||
@ -92,10 +91,6 @@ import java.util.Map;
|
|||||||
* 0 represents the highest priority and 7 represents the lowest.
|
* 0 represents the highest priority and 7 represents the lowest.
|
||||||
* This header is optional and defaults to 0.</td>
|
* This header is optional and defaults to 0.</td>
|
||||||
* </tr>
|
* </tr>
|
||||||
* <tr>
|
|
||||||
* <td>{@code "X-SPDY-URL"}</td>
|
|
||||||
* <td>The absolute path for the resource being pushed.</td>
|
|
||||||
* </tr>
|
|
||||||
* </table>
|
* </table>
|
||||||
*
|
*
|
||||||
* <h3>Required Annotations</h3>
|
* <h3>Required Annotations</h3>
|
||||||
@ -122,7 +117,6 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
public class SpdyHttpEncoder extends MessageToMessageEncoder<HttpObject> {
|
public class SpdyHttpEncoder extends MessageToMessageEncoder<HttpObject> {
|
||||||
|
|
||||||
private final int spdyVersion;
|
|
||||||
private int currentStreamId;
|
private int currentStreamId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -134,7 +128,6 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<HttpObject> {
|
|||||||
if (version == null) {
|
if (version == null) {
|
||||||
throw new NullPointerException("version");
|
throw new NullPointerException("version");
|
||||||
}
|
}
|
||||||
spdyVersion = version.getVersion();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -149,22 +142,16 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<HttpObject> {
|
|||||||
SpdySynStreamFrame spdySynStreamFrame = createSynStreamFrame(httpRequest);
|
SpdySynStreamFrame spdySynStreamFrame = createSynStreamFrame(httpRequest);
|
||||||
out.add(spdySynStreamFrame);
|
out.add(spdySynStreamFrame);
|
||||||
|
|
||||||
last = spdySynStreamFrame.isLast();
|
last = spdySynStreamFrame.isLast() || spdySynStreamFrame.isUnidirectional();
|
||||||
valid = true;
|
valid = true;
|
||||||
}
|
}
|
||||||
if (msg instanceof HttpResponse) {
|
if (msg instanceof HttpResponse) {
|
||||||
|
|
||||||
HttpResponse httpResponse = (HttpResponse) msg;
|
HttpResponse httpResponse = (HttpResponse) msg;
|
||||||
if (httpResponse.headers().contains(SpdyHttpHeaders.Names.ASSOCIATED_TO_STREAM_ID)) {
|
SpdyHeadersFrame spdyHeadersFrame = createHeadersFrame(httpResponse);
|
||||||
SpdySynStreamFrame spdySynStreamFrame = createSynStreamFrame(httpResponse);
|
out.add(spdyHeadersFrame);
|
||||||
last = spdySynStreamFrame.isLast();
|
|
||||||
out.add(spdySynStreamFrame);
|
|
||||||
} else {
|
|
||||||
SpdySynReplyFrame spdySynReplyFrame = createSynReplyFrame(httpResponse);
|
|
||||||
last = spdySynReplyFrame.isLast();
|
|
||||||
out.add(spdySynReplyFrame);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
last = spdyHeadersFrame.isLast();
|
||||||
valid = true;
|
valid = true;
|
||||||
}
|
}
|
||||||
if (msg instanceof HttpContent && !last) {
|
if (msg instanceof HttpContent && !last) {
|
||||||
@ -173,22 +160,23 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<HttpObject> {
|
|||||||
|
|
||||||
chunk.content().retain();
|
chunk.content().retain();
|
||||||
SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(currentStreamId, chunk.content());
|
SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(currentStreamId, chunk.content());
|
||||||
spdyDataFrame.setLast(chunk instanceof LastHttpContent);
|
|
||||||
if (chunk instanceof LastHttpContent) {
|
if (chunk instanceof LastHttpContent) {
|
||||||
LastHttpContent trailer = (LastHttpContent) chunk;
|
LastHttpContent trailer = (LastHttpContent) chunk;
|
||||||
HttpHeaders trailers = trailer.trailingHeaders();
|
HttpHeaders trailers = trailer.trailingHeaders();
|
||||||
if (trailers.isEmpty()) {
|
if (trailers.isEmpty()) {
|
||||||
|
spdyDataFrame.setLast(true);
|
||||||
out.add(spdyDataFrame);
|
out.add(spdyDataFrame);
|
||||||
} else {
|
} else {
|
||||||
// Create SPDY HEADERS frame out of trailers
|
// Create SPDY HEADERS frame out of trailers
|
||||||
SpdyHeadersFrame spdyHeadersFrame = new DefaultSpdyHeadersFrame(currentStreamId);
|
SpdyHeadersFrame spdyHeadersFrame = new DefaultSpdyHeadersFrame(currentStreamId);
|
||||||
|
spdyHeadersFrame.setLast(true);
|
||||||
for (Map.Entry<String, String> entry: trailers) {
|
for (Map.Entry<String, String> entry: trailers) {
|
||||||
spdyHeadersFrame.headers().add(entry.getKey(), entry.getValue());
|
spdyHeadersFrame.headers().add(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write HEADERS frame and append Data Frame
|
// Write DATA frame and append HEADERS frame
|
||||||
out.add(spdyHeadersFrame);
|
|
||||||
out.add(spdyDataFrame);
|
out.add(spdyDataFrame);
|
||||||
|
out.add(spdyHeadersFrame);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
out.add(spdyDataFrame);
|
out.add(spdyDataFrame);
|
||||||
@ -202,96 +190,94 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<HttpObject> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private SpdySynStreamFrame createSynStreamFrame(HttpMessage httpMessage)
|
@SuppressWarnings("deprecation")
|
||||||
throws Exception {
|
private SpdySynStreamFrame createSynStreamFrame(HttpRequest httpRequest) throws Exception {
|
||||||
// Get the Stream-ID, Associated-To-Stream-ID, Priority, URL, and scheme from the headers
|
// Get the Stream-ID, Associated-To-Stream-ID, Priority, and scheme from the headers
|
||||||
int streamID = SpdyHttpHeaders.getStreamId(httpMessage);
|
final HttpHeaders httpHeaders = httpRequest.headers();
|
||||||
int associatedToStreamId = SpdyHttpHeaders.getAssociatedToStreamId(httpMessage);
|
int streamId = SpdyHttpHeaders.getStreamId(httpRequest);
|
||||||
byte priority = SpdyHttpHeaders.getPriority(httpMessage);
|
int associatedToStreamId = SpdyHttpHeaders.getAssociatedToStreamId(httpRequest);
|
||||||
String URL = SpdyHttpHeaders.getUrl(httpMessage);
|
byte priority = SpdyHttpHeaders.getPriority(httpRequest);
|
||||||
String scheme = SpdyHttpHeaders.getScheme(httpMessage);
|
CharSequence scheme = httpHeaders.get(SpdyHttpHeaders.Names.SCHEME);
|
||||||
SpdyHttpHeaders.removeStreamId(httpMessage);
|
httpHeaders.remove(SpdyHttpHeaders.Names.STREAM_ID);
|
||||||
SpdyHttpHeaders.removeAssociatedToStreamId(httpMessage);
|
httpHeaders.remove(SpdyHttpHeaders.Names.ASSOCIATED_TO_STREAM_ID);
|
||||||
SpdyHttpHeaders.removePriority(httpMessage);
|
httpHeaders.remove(SpdyHttpHeaders.Names.PRIORITY);
|
||||||
SpdyHttpHeaders.removeUrl(httpMessage);
|
httpHeaders.remove(SpdyHttpHeaders.Names.SCHEME);
|
||||||
SpdyHttpHeaders.removeScheme(httpMessage);
|
|
||||||
|
|
||||||
// The Connection, Keep-Alive, Proxy-Connection, and Transfer-Encoding
|
// The Connection, Keep-Alive, Proxy-Connection, and Transfer-Encoding
|
||||||
// headers are not valid and MUST not be sent.
|
// headers are not valid and MUST not be sent.
|
||||||
httpMessage.headers().remove(HttpHeaders.Names.CONNECTION);
|
httpHeaders.remove(HttpHeaders.Names.CONNECTION);
|
||||||
httpMessage.headers().remove("Keep-Alive");
|
httpHeaders.remove("Keep-Alive");
|
||||||
httpMessage.headers().remove("Proxy-Connection");
|
httpHeaders.remove("Proxy-Connection");
|
||||||
httpMessage.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING);
|
httpHeaders.remove(HttpHeaders.Names.TRANSFER_ENCODING);
|
||||||
|
|
||||||
SpdySynStreamFrame spdySynStreamFrame =
|
SpdySynStreamFrame spdySynStreamFrame =
|
||||||
new DefaultSpdySynStreamFrame(streamID, associatedToStreamId, priority);
|
new DefaultSpdySynStreamFrame(streamId, associatedToStreamId, priority);
|
||||||
|
|
||||||
// Unfold the first line of the message into name/value pairs
|
// Unfold the first line of the message into name/value pairs
|
||||||
if (httpMessage instanceof FullHttpRequest) {
|
SpdyHeaders frameHeaders = spdySynStreamFrame.headers();
|
||||||
HttpRequest httpRequest = (HttpRequest) httpMessage;
|
frameHeaders.set(SpdyHeaders.HttpNames.METHOD, httpRequest.getMethod());
|
||||||
SpdyHeaders.setMethod(spdyVersion, spdySynStreamFrame, httpRequest.getMethod());
|
frameHeaders.set(SpdyHeaders.HttpNames.PATH, httpRequest.getUri());
|
||||||
SpdyHeaders.setUrl(spdyVersion, spdySynStreamFrame, httpRequest.getUri());
|
frameHeaders.set(SpdyHeaders.HttpNames.VERSION, httpRequest.getProtocolVersion());
|
||||||
SpdyHeaders.setVersion(spdyVersion, spdySynStreamFrame, httpMessage.getProtocolVersion());
|
|
||||||
}
|
|
||||||
if (httpMessage instanceof HttpResponse) {
|
|
||||||
HttpResponse httpResponse = (HttpResponse) httpMessage;
|
|
||||||
SpdyHeaders.setStatus(spdyVersion, spdySynStreamFrame, httpResponse.getStatus());
|
|
||||||
SpdyHeaders.setUrl(spdyVersion, spdySynStreamFrame, URL);
|
|
||||||
SpdyHeaders.setVersion(spdyVersion, spdySynStreamFrame, httpMessage.getProtocolVersion());
|
|
||||||
spdySynStreamFrame.setUnidirectional(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace the HTTP host header with the SPDY host header
|
// Replace the HTTP host header with the SPDY host header
|
||||||
if (spdyVersion >= 3) {
|
CharSequence host = httpHeaders.get(HttpHeaders.Names.HOST);
|
||||||
String host = HttpHeaders.getHost(httpMessage);
|
httpHeaders.remove(HttpHeaders.Names.HOST);
|
||||||
httpMessage.headers().remove(HttpHeaders.Names.HOST);
|
frameHeaders.set(SpdyHeaders.HttpNames.HOST, host);
|
||||||
SpdyHeaders.setHost(spdySynStreamFrame, host);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the SPDY scheme header
|
// Set the SPDY scheme header
|
||||||
if (scheme == null) {
|
if (scheme == null) {
|
||||||
scheme = "https";
|
scheme = "https";
|
||||||
}
|
}
|
||||||
SpdyHeaders.setScheme(spdyVersion, spdySynStreamFrame, scheme);
|
frameHeaders.set(SpdyHeaders.HttpNames.SCHEME, scheme);
|
||||||
|
|
||||||
// Transfer the remaining HTTP headers
|
// Transfer the remaining HTTP headers
|
||||||
for (Map.Entry<String, String> entry: httpMessage.headers()) {
|
for (Map.Entry<String, String> entry: httpHeaders) {
|
||||||
spdySynStreamFrame.headers().add(entry.getKey(), entry.getValue());
|
frameHeaders.add(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
currentStreamId = spdySynStreamFrame.streamId();
|
currentStreamId = spdySynStreamFrame.streamId();
|
||||||
spdySynStreamFrame.setLast(isLast(httpMessage));
|
if (associatedToStreamId == 0) {
|
||||||
|
spdySynStreamFrame.setLast(isLast(httpRequest));
|
||||||
|
} else {
|
||||||
|
spdySynStreamFrame.setUnidirectional(true);
|
||||||
|
}
|
||||||
|
|
||||||
return spdySynStreamFrame;
|
return spdySynStreamFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SpdySynReplyFrame createSynReplyFrame(HttpResponse httpResponse)
|
@SuppressWarnings("deprecation")
|
||||||
throws Exception {
|
private SpdyHeadersFrame createHeadersFrame(HttpResponse httpResponse) throws Exception {
|
||||||
// Get the Stream-ID from the headers
|
// Get the Stream-ID from the headers
|
||||||
int streamID = SpdyHttpHeaders.getStreamId(httpResponse);
|
final HttpHeaders httpHeaders = httpResponse.headers();
|
||||||
SpdyHttpHeaders.removeStreamId(httpResponse);
|
int streamId = SpdyHttpHeaders.getStreamId(httpResponse);
|
||||||
|
httpHeaders.remove(SpdyHttpHeaders.Names.STREAM_ID);
|
||||||
|
|
||||||
// The Connection, Keep-Alive, Proxy-Connection, and Transfer-Encoding
|
// The Connection, Keep-Alive, Proxy-Connection, and Transfer-Encoding
|
||||||
// headers are not valid and MUST not be sent.
|
// headers are not valid and MUST not be sent.
|
||||||
httpResponse.headers().remove(HttpHeaders.Names.CONNECTION);
|
httpHeaders.remove(HttpHeaders.Names.CONNECTION);
|
||||||
httpResponse.headers().remove("Keep-Alive");
|
httpHeaders.remove("Keep-Alive");
|
||||||
httpResponse.headers().remove("Proxy-Connection");
|
httpHeaders.remove("Proxy-Connection");
|
||||||
httpResponse.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING);
|
httpHeaders.remove(HttpHeaders.Names.TRANSFER_ENCODING);
|
||||||
|
|
||||||
SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamID);
|
|
||||||
|
|
||||||
|
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
|
// Unfold the first line of the response into name/value pairs
|
||||||
SpdyHeaders.setStatus(spdyVersion, spdySynReplyFrame, httpResponse.getStatus());
|
frameHeaders.set(SpdyHeaders.HttpNames.STATUS, httpResponse.getStatus().code());
|
||||||
SpdyHeaders.setVersion(spdyVersion, spdySynReplyFrame, httpResponse.getProtocolVersion());
|
frameHeaders.set(SpdyHeaders.HttpNames.VERSION, httpResponse.getProtocolVersion());
|
||||||
|
|
||||||
// Transfer the remaining HTTP headers
|
// Transfer the remaining HTTP headers
|
||||||
for (Map.Entry<String, String> entry: httpResponse.headers()) {
|
for (Map.Entry<String, String> entry: httpHeaders) {
|
||||||
spdySynReplyFrame.headers().add(entry.getKey(), entry.getValue());
|
spdyHeadersFrame.headers().add(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
currentStreamId = streamID;
|
currentStreamId = streamId;
|
||||||
spdySynReplyFrame.setLast(isLast(httpResponse));
|
spdyHeadersFrame.setLast(isLast(httpResponse));
|
||||||
|
|
||||||
return spdySynReplyFrame;
|
return spdyHeadersFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,10 +40,6 @@ public final class SpdyHttpHeaders {
|
|||||||
* {@code "X-SPDY-Priority"}
|
* {@code "X-SPDY-Priority"}
|
||||||
*/
|
*/
|
||||||
public static final String PRIORITY = "X-SPDY-Priority";
|
public static final String PRIORITY = "X-SPDY-Priority";
|
||||||
/**
|
|
||||||
* {@code "X-SPDY-URL"}
|
|
||||||
*/
|
|
||||||
public static final String URL = "X-SPDY-URL";
|
|
||||||
/**
|
/**
|
||||||
* {@code "X-SPDY-Scheme"}
|
* {@code "X-SPDY-Scheme"}
|
||||||
*/
|
*/
|
||||||
@ -124,27 +120,6 @@ public final class SpdyHttpHeaders {
|
|||||||
HttpHeaders.setIntHeader(message, Names.PRIORITY, priority);
|
HttpHeaders.setIntHeader(message, Names.PRIORITY, priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the {@code "X-SPDY-URL"} header.
|
|
||||||
*/
|
|
||||||
public static void removeUrl(HttpMessage message) {
|
|
||||||
message.headers().remove(Names.URL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the value of the {@code "X-SPDY-URL"} header.
|
|
||||||
*/
|
|
||||||
public static String getUrl(HttpMessage message) {
|
|
||||||
return message.headers().get(Names.URL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the {@code "X-SPDY-URL"} header.
|
|
||||||
*/
|
|
||||||
public static void setUrl(HttpMessage message, String url) {
|
|
||||||
message.headers().set(Names.URL, url);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the {@code "X-SPDY-Scheme"} header.
|
* Removes the {@code "X-SPDY-Scheme"} header.
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user