Index: components/cronet/android/test/src/org/chromium/net/Http2TestHandler.java |
diff --git a/components/cronet/android/test/src/org/chromium/net/Http2TestHandler.java b/components/cronet/android/test/src/org/chromium/net/Http2TestHandler.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..cdb5d2cb3fc9de2dac58bbdcd10da04d33c00340 |
--- /dev/null |
+++ b/components/cronet/android/test/src/org/chromium/net/Http2TestHandler.java |
@@ -0,0 +1,217 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.net; |
+ |
+import java.util.Locale; |
+import java.util.Map; |
+ |
+import static io.netty.buffer.Unpooled.copiedBuffer; |
+import static io.netty.buffer.Unpooled.unreleasableBuffer; |
+import static io.netty.handler.codec.http.HttpResponseStatus.OK; |
+import static io.netty.handler.logging.LogLevel.INFO; |
+ |
+import io.netty.buffer.ByteBuf; |
+import io.netty.buffer.ByteBufUtil; |
+import io.netty.channel.ChannelHandlerContext; |
+import io.netty.handler.codec.http2.AbstractHttp2ConnectionHandlerBuilder; |
+import io.netty.handler.codec.http2.DefaultHttp2Headers; |
+import io.netty.handler.codec.http2.Http2ConnectionDecoder; |
+import io.netty.handler.codec.http2.Http2ConnectionEncoder; |
+import io.netty.handler.codec.http2.Http2ConnectionHandler; |
+import io.netty.handler.codec.http2.Http2Exception; |
+import io.netty.handler.codec.http2.Http2Flags; |
+import io.netty.handler.codec.http2.Http2FrameListener; |
+import io.netty.handler.codec.http2.Http2FrameLogger; |
+import io.netty.handler.codec.http2.Http2Headers; |
+import io.netty.handler.codec.http2.Http2Settings; |
+import io.netty.util.CharsetUtil; |
+ |
+/** |
+ * HTTP/2 test handler for Cronet BidirectionalStream tests. |
+ */ |
+final class Http2TestHandler extends Http2ConnectionHandler implements Http2FrameListener { |
xunjieli
2016/01/22 16:45:20
Suggest adding public modifier to the package priv
mef
2016/01/22 17:36:07
Done.
|
+ private static final Http2FrameLogger sLogger = |
+ new Http2FrameLogger(INFO, Http2TestHandler.class); |
+ private static final ByteBuf RESPONSE_BYTES = |
+ unreleasableBuffer(copiedBuffer("HTTP/2 Test Server", CharsetUtil.UTF_8)); |
+ private boolean mEchoStream = false; |
xunjieli
2016/01/22 16:45:21
Suggest avoid initializing boolean explicitly here
mef
2016/01/22 17:36:07
Done.
|
+ private Http2Headers mResponseHeaders; |
+ |
+ static final class Builder |
+ extends AbstractHttp2ConnectionHandlerBuilder<Http2TestHandler, Builder> { |
+ public Builder() { |
+ frameLogger(sLogger); |
+ } |
+ |
+ @Override |
+ public Http2TestHandler build() { |
+ return super.build(); |
+ } |
+ |
+ @Override |
+ protected Http2TestHandler build(Http2ConnectionDecoder decoder, |
+ Http2ConnectionEncoder encoder, Http2Settings initialSettings) { |
+ Http2TestHandler handler = new Http2TestHandler(decoder, encoder, initialSettings); |
+ frameListener(handler); |
+ return handler; |
+ } |
+ } |
+ |
+ private Http2TestHandler(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder, |
+ Http2Settings initialSettings) { |
+ super(decoder, encoder, initialSettings); |
+ } |
+ |
+ @Override |
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { |
+ super.exceptionCaught(ctx, cause); |
+ cause.printStackTrace(); |
+ ctx.close(); |
+ } |
+ |
+ private static Http2Headers responseHeadersFromRequestHeaders(Http2Headers requestHeaders) { |
+ // Make response headers by echoing request headers. |
+ Http2Headers responseHeaders = new DefaultHttp2Headers().status(OK.codeAsText()); |
+ for (Map.Entry<CharSequence, CharSequence> header : requestHeaders) { |
+ if (!header.getKey().toString().startsWith(":")) { |
+ responseHeaders.add("echo-" + header.getKey(), header.getValue()); |
+ } |
+ } |
+ |
+ responseHeaders.add("echo-method", requestHeaders.get(":method").toString()); |
+ return responseHeaders; |
+ } |
+ |
+ private void sendResponse(ChannelHandlerContext ctx, int streamId, ByteBuf payload) { |
xunjieli
2016/01/22 16:45:20
Suggest adding a "do" prefix and group this togeth
mef
2016/01/22 17:36:07
Done.
|
+ // Send a frame for the response status |
+ encoder().writeHeaders(ctx, streamId, mResponseHeaders, 0, false, ctx.newPromise()); |
+ encoder().writeData(ctx, streamId, payload, 0, true, ctx.newPromise()); |
+ ctx.flush(); |
+ } |
+ |
+ @Override |
+ public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, |
xunjieli
2016/01/22 16:12:19
Suggest grouping public methods together.
mef
2016/01/22 17:36:07
Done.
|
+ boolean endOfStream) throws Http2Exception { |
+ int processed = data.readableBytes() + padding; |
+ if (mEchoStream) { |
+ encoder().writeData(ctx, streamId, data.retain(), 0, endOfStream, ctx.newPromise()); |
+ ctx.flush(); |
+ } else if (endOfStream) { |
+ sendResponse(ctx, streamId, data.retain()); |
+ } |
+ return processed; |
+ } |
+ |
+ private void doEchoStream(ChannelHandlerContext ctx, int streamId, Http2Headers headers, |
+ int padding, boolean endOfStream) { |
+ mEchoStream = true; |
+ // Send a frame for the response headers. |
+ encoder().writeHeaders(ctx, streamId, mResponseHeaders, 0, endOfStream, ctx.newPromise()); |
+ ctx.flush(); |
+ } |
+ |
+ private void doEchoTrailers(ChannelHandlerContext ctx, int streamId, Http2Headers headers, |
+ int padding, boolean endOfStream) { |
+ Http2Headers responseHeaders = new DefaultHttp2Headers().status(OK.codeAsText()); |
+ encoder().writeHeaders(ctx, streamId, responseHeaders, 0, false, ctx.newPromise()); |
+ encoder().writeData(ctx, streamId, RESPONSE_BYTES.duplicate(), 0, false, ctx.newPromise()); |
+ Http2Headers responseTrailers = mResponseHeaders.add("trailer", "value1", "Value2"); |
+ encoder().writeHeaders(ctx, streamId, responseTrailers, 0, true, ctx.newPromise()); |
+ ctx.flush(); |
+ } |
+ |
+ private String getEchoAllHeadersResponse(Http2Headers headers) { |
xunjieli
2016/01/22 16:45:20
static helper method?
mef
2016/01/22 17:36:07
Done.
|
+ StringBuilder response = new StringBuilder(); |
+ for (Map.Entry<CharSequence, CharSequence> header : headers) { |
+ response.append(header.getKey() + ": " + header.getValue() + "\r\n"); |
+ } |
+ return response.toString(); |
+ } |
+ |
+ private String getEchoHeaderResponse(Http2Headers headers) { |
xunjieli
2016/01/22 16:45:20
static helper method?
mef
2016/01/22 17:36:07
Done.
|
+ String[] split_path = headers.path().toString().split("\\?"); |
+ if (split_path.length <= 1) return "Header name not found."; |
+ |
+ String header_name = split_path[1].toLowerCase(Locale.US); |
+ if (headers.get(header_name) == null) return "Header not found:" + header_name; |
+ |
+ return headers.get(header_name).toString(); |
+ } |
+ |
+ @Override |
+ public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, |
+ int padding, boolean endOfStream) throws Http2Exception { |
+ mResponseHeaders = responseHeadersFromRequestHeaders(headers); |
+ String path = headers.path().toString(); |
+ if (path.startsWith("/echostream")) { |
+ doEchoStream(ctx, streamId, headers, padding, endOfStream); |
+ } else if (path.startsWith("/echotrailers")) { |
+ doEchoTrailers(ctx, streamId, headers, padding, endOfStream); |
+ } else if (endOfStream) { |
+ mResponseHeaders = new DefaultHttp2Headers().status(OK.codeAsText()); |
+ ByteBuf content = ctx.alloc().buffer(); |
+ try { |
+ if (path.startsWith("/echoallheaders")) { |
+ ByteBufUtil.writeAscii(content, getEchoAllHeadersResponse(headers)); |
+ } else if (path.startsWith("/echoheader")) { |
+ ByteBufUtil.writeAscii(content, getEchoHeaderResponse(headers)); |
+ } else if (path.startsWith("/echomethod")) { |
+ ByteBufUtil.writeAscii(content, headers.method()); |
+ } else { |
+ content.writeBytes(RESPONSE_BYTES.duplicate()); |
+ ByteBufUtil.writeAscii(content, " - via HTTP/2"); |
+ } |
+ } catch (Exception e) { |
+ ByteBufUtil.writeAscii(content, "Exception: " + e.toString()); |
+ } |
+ |
+ sendResponse(ctx, streamId, content); |
+ } |
+ } |
+ |
+ @Override |
+ public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, |
+ int streamDependency, short weight, boolean exclusive, int padding, boolean endOfStream) |
+ throws Http2Exception { |
+ onHeadersRead(ctx, streamId, headers, padding, endOfStream); |
+ } |
+ |
+ @Override |
+ public void onPriorityRead(ChannelHandlerContext ctx, int streamId, int streamDependency, |
+ short weight, boolean exclusive) throws Http2Exception {} |
+ |
+ @Override |
+ public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) |
+ throws Http2Exception {} |
+ |
+ @Override |
+ public void onSettingsAckRead(ChannelHandlerContext ctx) throws Http2Exception {} |
+ |
+ @Override |
+ public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) |
+ throws Http2Exception {} |
+ |
+ @Override |
+ public void onPingRead(ChannelHandlerContext ctx, ByteBuf data) throws Http2Exception {} |
+ |
+ @Override |
+ public void onPingAckRead(ChannelHandlerContext ctx, ByteBuf data) throws Http2Exception {} |
+ |
+ @Override |
+ public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int promisedStreamId, |
+ Http2Headers headers, int padding) throws Http2Exception {} |
+ |
+ @Override |
+ public void onGoAwayRead(ChannelHandlerContext ctx, int lastStreamId, long errorCode, |
+ ByteBuf debugData) throws Http2Exception {} |
+ |
+ @Override |
+ public void onWindowUpdateRead(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement) |
+ throws Http2Exception {} |
+ |
+ @Override |
+ public void onUnknownFrame(ChannelHandlerContext ctx, byte frameType, int streamId, |
+ Http2Flags flags, ByteBuf payload) throws Http2Exception {} |
+} |