1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.http3;
17
18 import io.netty.channel.ChannelHandlerContext;
19 import io.netty.channel.ChannelOutboundHandlerAdapter;
20 import io.netty.channel.ChannelPromise;
21 import io.netty.handler.codec.http.HttpStatusClass;
22 import org.jetbrains.annotations.Nullable;
23
24 import static io.netty.handler.codec.http3.Http3FrameValidationUtils.frameTypeUnexpected;
25
26 final class Http3RequestStreamEncodeStateValidator extends ChannelOutboundHandlerAdapter
27 implements Http3RequestStreamCodecState {
28 enum State {
29 None,
30 Headers,
31 FinalHeaders,
32 Trailers
33 }
34 private State state = State.None;
35
36 @Override
37 public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
38 if (!(msg instanceof Http3RequestStreamFrame)) {
39 super.write(ctx, msg, promise);
40 return;
41 }
42 final Http3RequestStreamFrame frame = (Http3RequestStreamFrame) msg;
43 final State nextState = evaluateFrame(state, frame);
44 if (nextState == null) {
45 frameTypeUnexpected(ctx, msg);
46 return;
47 }
48 state = nextState;
49 super.write(ctx, msg, promise);
50 }
51
52 @Override
53 public boolean started() {
54 return isStreamStarted(state);
55 }
56
57 @Override
58 public boolean receivedFinalHeaders() {
59 return isFinalHeadersReceived(state);
60 }
61
62 @Override
63 public boolean terminated() {
64 return isTrailersReceived(state);
65 }
66
67
68
69
70
71
72
73
74
75
76
77
78
79 @Nullable
80 static State evaluateFrame(State state, Http3RequestStreamFrame frame) {
81 if (frame instanceof Http3PushPromiseFrame || frame instanceof Http3UnknownFrame) {
82
83 return state;
84 }
85 switch (state) {
86 case None:
87 case Headers:
88 if (!(frame instanceof Http3HeadersFrame)) {
89 return null;
90 }
91 return isInformationalResponse((Http3HeadersFrame) frame) ? State.Headers : State.FinalHeaders;
92 case FinalHeaders:
93 if (frame instanceof Http3HeadersFrame) {
94 if (isInformationalResponse((Http3HeadersFrame) frame)) {
95
96 return null;
97 }
98
99 return State.Trailers;
100 }
101 return state;
102 case Trailers:
103 return null;
104 default:
105 throw new Error();
106 }
107 }
108
109 static boolean isStreamStarted(State state) {
110 return state != State.None;
111 }
112
113 static boolean isFinalHeadersReceived(State state) {
114 return isStreamStarted(state) && state != State.Headers;
115 }
116
117 static boolean isTrailersReceived(State state) {
118 return state == State.Trailers;
119 }
120
121 private static boolean isInformationalResponse(Http3HeadersFrame headersFrame) {
122 return HttpStatusClass.valueOf(headersFrame.headers().status()) == HttpStatusClass.INFORMATIONAL;
123 }
124 }