private SpdyHeadersFrame readHeaderBlockFrame(ByteBuf buffer) { int streamId; switch (type) { case SPDY_SYN_STREAM_FRAME: if (buffer.readableBytes() < 10) { return null; } int offset = buffer.readerIndex(); streamId = getUnsignedInt(buffer, offset); int associatedToStreamId = getUnsignedInt(buffer, offset + 4); byte priority = (byte) (buffer.getByte(offset + 8) >> 5 & 0x07); buffer.skipBytes(10); length -= 10; SpdySynStreamFrame spdySynStreamFrame = new DefaultSpdySynStreamFrame(streamId, associatedToStreamId, priority); spdySynStreamFrame.setLast((flags & SPDY_FLAG_FIN) != 0); spdySynStreamFrame.setUnidirectional((flags & SPDY_FLAG_UNIDIRECTIONAL) != 0); return spdySynStreamFrame; case SPDY_SYN_REPLY_FRAME: if (buffer.readableBytes() < 4) { return null; } streamId = getUnsignedInt(buffer, buffer.readerIndex()); buffer.skipBytes(4); length -= 4; SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId); spdySynReplyFrame.setLast((flags & SPDY_FLAG_FIN) != 0); return spdySynReplyFrame; case SPDY_HEADERS_FRAME: if (buffer.readableBytes() < 4) { return null; } streamId = getUnsignedInt(buffer, buffer.readerIndex()); buffer.skipBytes(4); length -= 4; SpdyHeadersFrame spdyHeadersFrame = new DefaultSpdyHeadersFrame(streamId); spdyHeadersFrame.setLast((flags & SPDY_FLAG_FIN) != 0); return spdyHeadersFrame; default: throw new Error("Shouldn't reach here."); } }
private SpdySynStreamFrame createSynStreamFrame(HttpMessage httpMessage) throws Exception { boolean chunked = httpMessage.isChunked(); // Get the Stream-ID, Associated-To-Stream-ID, Priority, URL, and scheme from the headers int streamID = SpdyHttpHeaders.getStreamID(httpMessage); int associatedToStreamID = SpdyHttpHeaders.getAssociatedToStreamID(httpMessage); byte priority = SpdyHttpHeaders.getPriority(httpMessage); String URL = SpdyHttpHeaders.getUrl(httpMessage); String scheme = SpdyHttpHeaders.getScheme(httpMessage); SpdyHttpHeaders.removeStreamID(httpMessage); SpdyHttpHeaders.removeAssociatedToStreamID(httpMessage); SpdyHttpHeaders.removePriority(httpMessage); SpdyHttpHeaders.removeUrl(httpMessage); SpdyHttpHeaders.removeScheme(httpMessage); // The Connection, Keep-Alive, Proxy-Connection, and Transfer-Encoding // headers are not valid and MUST not be sent. httpMessage.removeHeader(HttpHeaders.Names.CONNECTION); httpMessage.removeHeader("Keep-Alive"); httpMessage.removeHeader("Proxy-Connection"); httpMessage.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING); SpdySynStreamFrame spdySynStreamFrame = new DefaultSpdySynStreamFrame(streamID, associatedToStreamID, priority); // Unfold the first line of the message into name/value pairs if (httpMessage instanceof HttpRequest) { HttpRequest httpRequest = (HttpRequest) httpMessage; SpdyHeaders.setMethod(spdyVersion, spdySynStreamFrame, httpRequest.getMethod()); SpdyHeaders.setUrl(spdyVersion, spdySynStreamFrame, httpRequest.getUri()); 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); spdySynStreamFrame.setUnidirectional(true); } // Replace the HTTP host header with the SPDY host header if (spdyVersion >= 3) { String host = HttpHeaders.getHost(httpMessage); httpMessage.removeHeader(HttpHeaders.Names.HOST); SpdyHeaders.setHost(spdySynStreamFrame, host); } // Set the SPDY scheme header if (scheme == null) { scheme = "https"; } SpdyHeaders.setScheme(spdyVersion, spdySynStreamFrame, scheme); // Transfer the remaining HTTP headers for (Map.Entry<String, String> entry : httpMessage.getHeaders()) { spdySynStreamFrame.addHeader(entry.getKey(), entry.getValue()); } if (chunked) { currentStreamID = streamID; spdySynStreamFrame.setLast(false); } else { spdySynStreamFrame.setLast(httpMessage.getContent().readableBytes() == 0); } return spdySynStreamFrame; }
private static void testSpdySessionHandler(SpdyVersion version, boolean server) { EmbeddedChannel sessionHandler = new EmbeddedChannel( new SpdySessionHandler(version, server), new EchoHandler(closeSignal, server)); while (sessionHandler.readOutbound() != null) { continue; } int localStreamId = server ? 1 : 2; int remoteStreamId = server ? 2 : 1; SpdySynStreamFrame spdySynStreamFrame = new DefaultSpdySynStreamFrame(localStreamId, 0, (byte) 0); spdySynStreamFrame.headers().set("Compression", "test"); SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(localStreamId); spdyDataFrame.setLast(true); // Check if session handler returns INVALID_STREAM if it receives // a data frame for a Stream-ID that is not open sessionHandler.writeInbound(new DefaultSpdyDataFrame(localStreamId)); assertRstStream(sessionHandler.readOutbound(), localStreamId, SpdyStreamStatus.INVALID_STREAM); assertNull(sessionHandler.readOutbound()); // Check if session handler returns PROTOCOL_ERROR if it receives // a data frame for a Stream-ID before receiving a SYN_REPLY frame sessionHandler.writeInbound(new DefaultSpdyDataFrame(remoteStreamId)); assertRstStream(sessionHandler.readOutbound(), remoteStreamId, SpdyStreamStatus.PROTOCOL_ERROR); assertNull(sessionHandler.readOutbound()); remoteStreamId += 2; // Check if session handler returns PROTOCOL_ERROR if it receives // multiple SYN_REPLY frames for the same active Stream-ID sessionHandler.writeInbound(new DefaultSpdySynReplyFrame(remoteStreamId)); assertNull(sessionHandler.readOutbound()); sessionHandler.writeInbound(new DefaultSpdySynReplyFrame(remoteStreamId)); assertRstStream(sessionHandler.readOutbound(), remoteStreamId, SpdyStreamStatus.STREAM_IN_USE); assertNull(sessionHandler.readOutbound()); remoteStreamId += 2; // Check if frame codec correctly compresses/uncompresses headers sessionHandler.writeInbound(spdySynStreamFrame); assertSynReply( sessionHandler.readOutbound(), localStreamId, false, spdySynStreamFrame.headers()); assertNull(sessionHandler.readOutbound()); SpdyHeadersFrame spdyHeadersFrame = new DefaultSpdyHeadersFrame(localStreamId); spdyHeadersFrame.headers().add("HEADER", "test1"); spdyHeadersFrame.headers().add("HEADER", "test2"); sessionHandler.writeInbound(spdyHeadersFrame); assertHeaders(sessionHandler.readOutbound(), localStreamId, false, spdyHeadersFrame.headers()); assertNull(sessionHandler.readOutbound()); localStreamId += 2; // Check if session handler closed the streams using the number // of concurrent streams and that it returns REFUSED_STREAM // if it receives a SYN_STREAM frame it does not wish to accept spdySynStreamFrame.setStreamId(localStreamId); spdySynStreamFrame.setLast(true); spdySynStreamFrame.setUnidirectional(true); sessionHandler.writeInbound(spdySynStreamFrame); assertRstStream(sessionHandler.readOutbound(), localStreamId, SpdyStreamStatus.REFUSED_STREAM); assertNull(sessionHandler.readOutbound()); // Check if session handler rejects HEADERS for closed streams int testStreamId = spdyDataFrame.getStreamId(); sessionHandler.writeInbound(spdyDataFrame); assertDataFrame(sessionHandler.readOutbound(), testStreamId, spdyDataFrame.isLast()); assertNull(sessionHandler.readOutbound()); spdyHeadersFrame.setStreamId(testStreamId); sessionHandler.writeInbound(spdyHeadersFrame); assertRstStream(sessionHandler.readOutbound(), testStreamId, SpdyStreamStatus.INVALID_STREAM); assertNull(sessionHandler.readOutbound()); // Check if session handler drops active streams if it receives // a RST_STREAM frame for that Stream-ID sessionHandler.writeInbound(new DefaultSpdyRstStreamFrame(remoteStreamId, 3)); assertNull(sessionHandler.readOutbound()); remoteStreamId += 2; // Check if session handler honors UNIDIRECTIONAL streams spdySynStreamFrame.setLast(false); sessionHandler.writeInbound(spdySynStreamFrame); assertNull(sessionHandler.readOutbound()); spdySynStreamFrame.setUnidirectional(false); // Check if session handler returns PROTOCOL_ERROR if it receives // multiple SYN_STREAM frames for the same active Stream-ID sessionHandler.writeInbound(spdySynStreamFrame); assertRstStream(sessionHandler.readOutbound(), localStreamId, SpdyStreamStatus.PROTOCOL_ERROR); assertNull(sessionHandler.readOutbound()); localStreamId += 2; // Check if session handler returns PROTOCOL_ERROR if it receives // a SYN_STREAM frame with an invalid Stream-ID spdySynStreamFrame.setStreamId(localStreamId - 1); sessionHandler.writeInbound(spdySynStreamFrame); assertRstStream( sessionHandler.readOutbound(), localStreamId - 1, SpdyStreamStatus.PROTOCOL_ERROR); assertNull(sessionHandler.readOutbound()); spdySynStreamFrame.setStreamId(localStreamId); // Check if session handler returns PROTOCOL_ERROR if it receives // an invalid HEADERS frame spdyHeadersFrame.setStreamId(localStreamId); spdyHeadersFrame.setInvalid(); sessionHandler.writeInbound(spdyHeadersFrame); assertRstStream(sessionHandler.readOutbound(), localStreamId, SpdyStreamStatus.PROTOCOL_ERROR); assertNull(sessionHandler.readOutbound()); sessionHandler.finish(); }
@SuppressWarnings("deprecation") 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 = httpHeaders.getInt(Names.STREAM_ID); int associatedToStreamId = httpHeaders.getInt(Names.ASSOCIATED_TO_STREAM_ID, 0); byte priority = (byte) httpHeaders.getInt(Names.PRIORITY, 0); String URL = httpHeaders.get(Names.URL); String 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 // headers are not valid and MUST not be sent. httpHeaders.remove(HttpHeaders.Names.CONNECTION); httpHeaders.remove(HttpHeaders.Names.KEEP_ALIVE); httpHeaders.remove(HttpHeaders.Names.PROXY_CONNECTION); httpHeaders.remove(HttpHeaders.Names.TRANSFER_ENCODING); SpdySynStreamFrame spdySynStreamFrame = 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.set(METHOD, httpRequest.method()); frameHeaders.set(PATH, httpRequest.uri()); frameHeaders.set(VERSION, httpMessage.protocolVersion()); } if (httpMessage instanceof HttpResponse) { HttpResponse httpResponse = (HttpResponse) httpMessage; frameHeaders.set(STATUS, httpResponse.status()); frameHeaders.set(PATH, URL); frameHeaders.set(VERSION, httpMessage.protocolVersion()); spdySynStreamFrame.setUnidirectional(true); } // Replace the HTTP host header with the SPDY host header if (spdyVersion >= 3) { CharSequence host = httpHeaders.getUnconverted(HttpHeaders.Names.HOST); httpHeaders.remove(HttpHeaders.Names.HOST); frameHeaders.set(HOST, host); } // Set the SPDY scheme header if (scheme == null) { scheme = "https"; } frameHeaders.set(SCHEME, scheme); // Transfer the remaining HTTP headers for (Map.Entry<String, String> entry : httpHeaders) { frameHeaders.add(entry.getKey(), entry.getValue()); } currentStreamId = spdySynStreamFrame.streamId(); spdySynStreamFrame.setLast(isLast(httpMessage)); return spdySynStreamFrame; }