我们知道,HTTP2.0使用header桢表达HTTP header+request line,data frame表达Body。header桢和data桢使用相同的stream id组成一个完整的HTTP请求/响应包。这里的stream描述了一次请求和响应,相当于完成了一次HTTP/1.x的短连接请求和响应。
Http1.x 的http request 在完成对请求头编码后,接下了就时对请求体编码为Data Frame了,如果时get请求,没有请求体,那就不需要发送data frame,所以我们可以知道,只有post请求才会发data frame。
在把请求体编码成data frame时,一个重要的flag就时end_stream,需要告诉服务端这是不是该stream上的最后一个frame,因为在header frame 发送时,介绍了,
有data frame,header frame 的end_stream 肯定为false,所以大部分在发送请求体即data frame时,end_stream为true,除非有trailers header,因为发完数据,还要发trailers header,那就时最后一个header frame,而且end_stream为true。
编码pipeline
HttpToHttp2ConnectionHandler->DefaultHttp2ConnectionEncoder->DefaultHttp2FrameWriter
发送data frame时,需要判断end stream 是否为true,如果分
//endStream 如果为ture,就不需要发data frame了。
if (!endStream && msg instanceof HttpContent) {
boolean isLastContent = false;
HttpHeaders trailers = EmptyHttpHeaders.INSTANCE;
Http2Headers http2Trailers = EmptyHttp2Headers.INSTANCE;
if (msg instanceof LastHttpContent) {
isLastContent = true;
// Convert any trailing headers.
final LastHttpContent lastContent = (LastHttpContent) msg;
trailers = lastContent.trailingHeaders();
http2Trailers = HttpConversionUtil.toHttp2Headers(trailers, validateHeaders);
}
// Write the data
final ByteBuf content = ((HttpContent) msg).content();
endStream = isLastContent && trailers.isEmpty();
release = false;
encoder.writeData(ctx, currentStreamId, content, 0, endStream, promiseAggregator.newPromise());
if (!trailers.isEmpty()) {
// Write trailing headers.
writeHeaders(ctx, encoder, currentStreamId, trailers, http2Trailers, true, promiseAggregator);
}
}
encoder data frame 时,直接添加到stream对应的流控队列,代码如下:
@Override
public ChannelFuture writeData(final ChannelHandlerContext ctx, final int streamId, ByteBuf data, int padding,
final boolean endOfStream, ChannelPromise promise) {
final Http2Stream stream;
try {
stream = requireStream(streamId);
// Verify that the stream is in the appropriate state for sending DATA frames.
switch (stream.state()) {
case OPEN:
case HALF_CLOSED_REMOTE:
// Allowed sending DATA frames in these states.
break;
default:
throw new IllegalStateException("Stream " + stream.id() + " in unexpected state " + stream.state());
}
} catch (Throwable e) {
data.release();
return promise.setFailure(e);
}
// Hand control of the frame to the flow controller.
flowController().addFlowControlled(stream,
new FlowControlledData(stream, data, padding, endOfStream, promise));
return promise;
}
添加到队列后,代表write操作就结束咯,后面在flush时先把流控队列里的数据写到netty的循环队列,后面的 ctx.flush(); 会从循环队列写到os的写缓冲区。
@Override
public void flush(ChannelHandlerContext ctx) {
try {
// Trigger pending writes in the remote flow controller.
//这里时流控机制,需要选择哪些frame可以发送,通过后面的流控来具体分析
encoder.flowController().writePendingBytes();
ctx.flush();
} catch (Http2Exception e) {
onError(ctx, true, e);
} catch (Throwable cause) {
onError(ctx, true, connectionError(INTERNAL_ERROR, cause, "Error flushing"));
}
}