OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 package org.chromium.net; | |
6 | |
7 import org.chromium.base.Log; | |
8 | |
9 import java.util.Locale; | |
10 import java.util.Map; | |
11 | |
12 import static io.netty.buffer.Unpooled.copiedBuffer; | |
13 import static io.netty.buffer.Unpooled.unreleasableBuffer; | |
14 import static io.netty.handler.codec.http.HttpResponseStatus.OK; | |
15 import static io.netty.handler.logging.LogLevel.INFO; | |
16 | |
17 import io.netty.buffer.ByteBuf; | |
18 import io.netty.buffer.ByteBufUtil; | |
19 import io.netty.channel.ChannelHandlerContext; | |
20 import io.netty.handler.codec.http2.AbstractHttp2ConnectionHandlerBuilder; | |
21 import io.netty.handler.codec.http2.DefaultHttp2Headers; | |
22 import io.netty.handler.codec.http2.Http2ConnectionDecoder; | |
23 import io.netty.handler.codec.http2.Http2ConnectionEncoder; | |
24 import io.netty.handler.codec.http2.Http2ConnectionHandler; | |
25 import io.netty.handler.codec.http2.Http2Exception; | |
26 import io.netty.handler.codec.http2.Http2Flags; | |
27 import io.netty.handler.codec.http2.Http2FrameListener; | |
28 import io.netty.handler.codec.http2.Http2FrameLogger; | |
29 import io.netty.handler.codec.http2.Http2Headers; | |
30 import io.netty.handler.codec.http2.Http2Settings; | |
31 import io.netty.util.CharsetUtil; | |
32 | |
33 /** | |
34 * HTTP/2 test handler for Cronet BidirectionalStream tests. | |
35 */ | |
36 public final class Http2TestHandler extends Http2ConnectionHandler implements Ht tp2FrameListener { | |
37 // Some Url Paths that have special meaning. | |
38 public static final String ECHO_ALL_HEADERS_PATH = "/echoallheaders"; | |
39 public static final String ECHO_HEADER_PATH = "/echoheader"; | |
40 public static final String ECHO_METHOD_PATH = "/echomethod"; | |
41 public static final String ECHO_STREAM_PATH = "/echostream"; | |
42 public static final String ECHO_TRAILERS_PATH = "/echotrailers"; | |
43 | |
44 private static final String TAG = "cr_Http2TestHandler"; | |
45 private static final Http2FrameLogger sLogger = | |
46 new Http2FrameLogger(INFO, Http2TestHandler.class); | |
47 private static final ByteBuf RESPONSE_BYTES = | |
48 unreleasableBuffer(copiedBuffer("HTTP/2 Test Server", CharsetUtil.UT F_8)); | |
49 private boolean mEchoStream; | |
xunjieli
2016/01/27 22:17:10
Seems like this handler can only handle one active
xunjieli
2016/01/27 22:23:24
Also bug here. If first url is echo stream, and th
mef
2016/01/29 00:15:30
Great catch! Currently there are no tests that use
mef
2016/01/29 00:15:30
Done.
| |
50 private Http2Headers mResponseHeaders; | |
51 | |
52 /** | |
53 * Builder for HTTP/2 test handler. | |
54 */ | |
55 public static final class Builder | |
56 extends AbstractHttp2ConnectionHandlerBuilder<Http2TestHandler, Buil der> { | |
57 public Builder() { | |
58 frameLogger(sLogger); | |
59 } | |
60 | |
61 @Override | |
62 public Http2TestHandler build() { | |
63 return super.build(); | |
64 } | |
65 | |
66 @Override | |
67 protected Http2TestHandler build(Http2ConnectionDecoder decoder, | |
68 Http2ConnectionEncoder encoder, Http2Settings initialSettings) { | |
69 Http2TestHandler handler = new Http2TestHandler(decoder, encoder, in itialSettings); | |
70 frameListener(handler); | |
71 return handler; | |
72 } | |
73 } | |
74 | |
75 private Http2TestHandler(Http2ConnectionDecoder decoder, Http2ConnectionEnco der encoder, | |
76 Http2Settings initialSettings) { | |
77 super(decoder, encoder, initialSettings); | |
78 } | |
79 | |
80 @Override | |
81 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) thro ws Exception { | |
82 super.exceptionCaught(ctx, cause); | |
83 Log.e(TAG, "An exception was caught", cause); | |
84 ctx.close(); | |
85 throw new Exception("Exception Caught", cause); | |
86 } | |
87 | |
88 private static Http2Headers createResponseHeadersFromRequestHeaders( | |
89 Http2Headers requestHeaders) { | |
90 // Create response headers by echoing request headers. | |
91 Http2Headers responseHeaders = new DefaultHttp2Headers().status(OK.codeA sText()); | |
92 for (Map.Entry<CharSequence, CharSequence> header : requestHeaders) { | |
93 if (!header.getKey().toString().startsWith(":")) { | |
94 responseHeaders.add("echo-" + header.getKey(), header.getValue() ); | |
95 } | |
96 } | |
97 | |
98 responseHeaders.add("echo-method", requestHeaders.get(":method").toStrin g()); | |
99 return responseHeaders; | |
100 } | |
101 | |
102 private static String getEchoAllHeadersResponse(Http2Headers headers) { | |
103 StringBuilder response = new StringBuilder(); | |
104 for (Map.Entry<CharSequence, CharSequence> header : headers) { | |
105 response.append(header.getKey() + ": " + header.getValue() + "\r\n") ; | |
106 } | |
107 return response.toString(); | |
108 } | |
109 | |
110 private static String getEchoHeaderResponse(Http2Headers headers) { | |
111 String[] splitPath = headers.path().toString().split("\\?"); | |
112 if (splitPath.length <= 1) return "Header name not found."; | |
113 | |
114 String headerName = splitPath[1].toLowerCase(Locale.US); | |
115 if (headers.get(headerName) == null) return "Header not found:" + header Name; | |
116 | |
117 return headers.get(headerName).toString(); | |
118 } | |
119 | |
120 private void doSendResponse(ChannelHandlerContext ctx, int streamId, ByteBuf payload) { | |
121 // Send a frame for the response status | |
122 encoder().writeHeaders(ctx, streamId, mResponseHeaders, 0, false, ctx.ne wPromise()); | |
123 encoder().writeData(ctx, streamId, payload, 0, true, ctx.newPromise()); | |
124 ctx.flush(); | |
125 } | |
126 | |
127 private void doEchoStream(ChannelHandlerContext ctx, int streamId, Http2Head ers headers, | |
xunjieli
2016/01/27 22:17:10
This method is confusingly named. I was expecting
mef
2016/01/29 00:15:30
Done.
| |
128 int padding, boolean endOfStream) { | |
129 mEchoStream = true; | |
130 // Send a frame for the response headers. | |
131 encoder().writeHeaders(ctx, streamId, mResponseHeaders, 0, endOfStream, ctx.newPromise()); | |
132 ctx.flush(); | |
133 } | |
134 | |
135 private void doEchoTrailers(ChannelHandlerContext ctx, int streamId) { | |
136 Http2Headers responseHeaders = new DefaultHttp2Headers().status(OK.codeA sText()); | |
137 encoder().writeHeaders(ctx, streamId, responseHeaders, 0, false, ctx.new Promise()); | |
138 encoder().writeData(ctx, streamId, RESPONSE_BYTES.duplicate(), 0, false, ctx.newPromise()); | |
139 Http2Headers responseTrailers = mResponseHeaders.add("trailer", "value1" , "Value2"); | |
140 encoder().writeHeaders(ctx, streamId, responseTrailers, 0, true, ctx.new Promise()); | |
141 ctx.flush(); | |
142 } | |
143 | |
144 @Override | |
145 public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, | |
146 boolean endOfStream) throws Http2Exception { | |
147 int processed = data.readableBytes() + padding; | |
148 if (mEchoStream) { | |
149 encoder().writeData(ctx, streamId, data.retain(), 0, endOfStream, ct x.newPromise()); | |
xunjieli
2016/01/27 22:17:10
use doEchoStream? see previous comment.
mef
2016/01/29 00:15:30
Done.
| |
150 ctx.flush(); | |
151 } else if (endOfStream) { | |
152 doSendResponse(ctx, streamId, data.retain()); | |
153 } | |
154 return processed; | |
155 } | |
156 | |
157 @Override | |
158 public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Head ers headers, | |
159 int padding, boolean endOfStream) throws Http2Exception { | |
160 mResponseHeaders = createResponseHeadersFromRequestHeaders(headers); | |
161 String path = headers.path().toString(); | |
162 if (path.startsWith(ECHO_STREAM_PATH)) { | |
163 doEchoStream(ctx, streamId, headers, padding, endOfStream); | |
164 } else if (path.startsWith(ECHO_TRAILERS_PATH)) { | |
165 doEchoTrailers(ctx, streamId); | |
166 } else if (endOfStream) { | |
167 mResponseHeaders = new DefaultHttp2Headers().status(OK.codeAsText()) ; | |
168 ByteBuf content = ctx.alloc().buffer(); | |
169 try { | |
170 if (path.startsWith(ECHO_ALL_HEADERS_PATH)) { | |
171 ByteBufUtil.writeAscii(content, getEchoAllHeadersResponse(he aders)); | |
172 } else if (path.startsWith(ECHO_HEADER_PATH)) { | |
173 ByteBufUtil.writeAscii(content, getEchoHeaderResponse(header s)); | |
174 } else if (path.startsWith(ECHO_METHOD_PATH)) { | |
175 ByteBufUtil.writeAscii(content, headers.method()); | |
176 } else { | |
177 content.writeBytes(RESPONSE_BYTES.duplicate()); | |
178 ByteBufUtil.writeAscii(content, " - via HTTP/2"); | |
179 } | |
180 } catch (Exception e) { | |
181 ByteBufUtil.writeAscii(content, "Exception: " + e.toString()); | |
182 } | |
183 | |
184 doSendResponse(ctx, streamId, content); | |
185 } | |
186 } | |
187 | |
188 @Override | |
189 public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Head ers headers, | |
190 int streamDependency, short weight, boolean exclusive, int padding, boolean endOfStream) | |
191 throws Http2Exception { | |
192 onHeadersRead(ctx, streamId, headers, padding, endOfStream); | |
193 } | |
194 | |
195 @Override | |
196 public void onPriorityRead(ChannelHandlerContext ctx, int streamId, int stre amDependency, | |
197 short weight, boolean exclusive) throws Http2Exception {} | |
198 | |
199 @Override | |
200 public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long er rorCode) | |
201 throws Http2Exception {} | |
202 | |
203 @Override | |
204 public void onSettingsAckRead(ChannelHandlerContext ctx) throws Http2Excepti on {} | |
205 | |
206 @Override | |
207 public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings ) | |
208 throws Http2Exception {} | |
209 | |
210 @Override | |
211 public void onPingRead(ChannelHandlerContext ctx, ByteBuf data) throws Http2 Exception {} | |
212 | |
213 @Override | |
214 public void onPingAckRead(ChannelHandlerContext ctx, ByteBuf data) throws Ht tp2Exception {} | |
215 | |
216 @Override | |
217 public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int p romisedStreamId, | |
218 Http2Headers headers, int padding) throws Http2Exception {} | |
219 | |
220 @Override | |
221 public void onGoAwayRead(ChannelHandlerContext ctx, int lastStreamId, long e rrorCode, | |
222 ByteBuf debugData) throws Http2Exception {} | |
223 | |
224 @Override | |
225 public void onWindowUpdateRead(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement) | |
226 throws Http2Exception {} | |
227 | |
228 @Override | |
229 public void onUnknownFrame(ChannelHandlerContext ctx, byte frameType, int st reamId, | |
230 Http2Flags flags, ByteBuf payload) throws Http2Exception {} | |
231 } | |
OLD | NEW |