1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.netty.handler.codec.http3;
18
19 import io.netty.channel.ChannelHandlerContext;
20 import io.netty.channel.ChannelPromise;
21 import io.netty.handler.codec.http.HttpHeaderNames;
22 import io.netty.handler.codec.http.HttpHeaderValues;
23 import io.netty.handler.codec.quic.QuicStreamChannel;
24 import io.netty.util.ReferenceCountUtil;
25 import io.netty.util.internal.StringUtil;
26
27 import java.util.function.BooleanSupplier;
28
29 import static io.netty.handler.codec.http.HttpUtil.normalizeAndGetContentLength;
30 import static io.netty.handler.codec.http3.Http3ErrorCode.H3_MESSAGE_ERROR;
31 import static io.netty.handler.codec.http3.Http3FrameValidationUtils.frameTypeUnexpected;
32
33 final class Http3RequestStreamValidationUtils {
34 static final long CONTENT_LENGTH_NOT_MODIFIED = -1;
35 static final long INVALID_FRAME_READ = -2;
36
37 private Http3RequestStreamValidationUtils() {
38
39 }
40
41
42
43
44
45
46
47
48
49
50
51
52 static boolean validateClientWrite(Http3RequestStreamFrame frame, ChannelPromise promise, ChannelHandlerContext ctx,
53 BooleanSupplier goAwayReceivedSupplier,
54 Http3RequestStreamCodecState encodeState) {
55 if (goAwayReceivedSupplier.getAsBoolean() && !encodeState.started()) {
56 String type = StringUtil.simpleClassName(frame);
57 ReferenceCountUtil.release(frame);
58 promise.setFailure(new Http3Exception(Http3ErrorCode.H3_FRAME_UNEXPECTED,
59 "Frame of type " + type + " unexpected as we received a GOAWAY already."));
60 ctx.close();
61 return false;
62 }
63 if (frame instanceof Http3PushPromiseFrame) {
64
65
66 frameTypeUnexpected(promise, frame);
67 return false;
68 }
69 return true;
70 }
71
72 static long validateHeaderFrameRead(Http3HeadersFrame headersFrame, ChannelHandlerContext ctx,
73 Http3RequestStreamCodecState decodeState) {
74 if (headersFrame.headers().contains(HttpHeaderNames.CONNECTION)) {
75 headerUnexpected(ctx, headersFrame, "connection header included");
76 return INVALID_FRAME_READ;
77 }
78 CharSequence value = headersFrame.headers().get(HttpHeaderNames.TE);
79 if (value != null && !HttpHeaderValues.TRAILERS.equals(value)) {
80 headerUnexpected(ctx, headersFrame, "te header field included with invalid value: " + value);
81 return INVALID_FRAME_READ;
82 }
83 if (decodeState.receivedFinalHeaders()) {
84 long length = normalizeAndGetContentLength(
85 headersFrame.headers().getAll(HttpHeaderNames.CONTENT_LENGTH), false, true);
86 if (length != CONTENT_LENGTH_NOT_MODIFIED) {
87 headersFrame.headers().setLong(HttpHeaderNames.CONTENT_LENGTH, length);
88 }
89 return length;
90 }
91 return CONTENT_LENGTH_NOT_MODIFIED;
92 }
93
94 static long validateDataFrameRead(Http3DataFrame dataFrame, ChannelHandlerContext ctx,
95 long expectedLength, long seenLength, boolean clientHeadRequest) {
96 try {
97 return verifyContentLength(dataFrame.content().readableBytes(), expectedLength, seenLength, false,
98 clientHeadRequest);
99 } catch (Http3Exception e) {
100 ReferenceCountUtil.release(dataFrame);
101 failStream(ctx, e);
102 return INVALID_FRAME_READ;
103 }
104 }
105
106 static boolean validateOnStreamClosure(ChannelHandlerContext ctx, long expectedLength, long seenLength,
107 boolean clientHeadRequest) {
108 try {
109 verifyContentLength(0, expectedLength, seenLength, true, clientHeadRequest);
110 return true;
111 } catch (Http3Exception e) {
112 ctx.fireExceptionCaught(e);
113 Http3CodecUtils.streamError(ctx, e.errorCode());
114 return false;
115 }
116 }
117
118 static void sendStreamAbandonedIfRequired(ChannelHandlerContext ctx, QpackAttributes qpackAttributes,
119 QpackDecoder qpackDecoder, Http3RequestStreamCodecState decodeState) {
120 if (!qpackAttributes.dynamicTableDisabled() && !decodeState.terminated()) {
121 final long streamId = ((QuicStreamChannel) ctx.channel()).streamId();
122 if (qpackAttributes.decoderStreamAvailable()) {
123 qpackDecoder.streamAbandoned(qpackAttributes.decoderStream(), streamId);
124 } else {
125 qpackAttributes.whenDecoderStreamAvailable(future -> {
126 if (future.isSuccess()) {
127 qpackDecoder.streamAbandoned(qpackAttributes.decoderStream(), streamId);
128 }
129 });
130 }
131 }
132 }
133
134 private static void headerUnexpected(ChannelHandlerContext ctx, Http3RequestStreamFrame frame, String msg) {
135
136
137 ReferenceCountUtil.release(frame);
138 failStream(ctx, new Http3Exception(H3_MESSAGE_ERROR, msg));
139 }
140
141 private static void failStream(ChannelHandlerContext ctx, Http3Exception cause) {
142 ctx.fireExceptionCaught(cause);
143 Http3CodecUtils.streamError(ctx, cause.errorCode());
144 }
145
146
147 private static long verifyContentLength(int length, long expectedLength, long seenLength, boolean end,
148 boolean clientHeadRequest) throws Http3Exception {
149 seenLength += length;
150 if (expectedLength != -1 && (seenLength > expectedLength ||
151 (!clientHeadRequest && end && seenLength != expectedLength))) {
152 throw new Http3Exception(
153 H3_MESSAGE_ERROR, "Expected content-length " + expectedLength +
154 " != " + seenLength + ".");
155 }
156 return seenLength;
157 }
158 }