View Javadoc
1   /*
2    * Copyright 2021 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.handler.codec.http3;
17  
18  import io.netty.buffer.ByteBuf;
19  import io.netty.channel.ChannelHandlerContext;
20  import io.netty.channel.socket.ChannelInputShutdownEvent;
21  import io.netty.handler.codec.ByteToMessageDecoder;
22  
23  import java.util.List;
24  
25  import static io.netty.handler.codec.http3.Http3CodecUtils.connectionError;
26  import static io.netty.handler.codec.http3.Http3ErrorCode.QPACK_DECODER_STREAM_ERROR;
27  import static io.netty.handler.codec.http3.QpackUtil.decodePrefixedIntegerAsInt;
28  
29  final class QpackDecoderHandler extends ByteToMessageDecoder {
30  
31      private boolean discard;
32      private final QpackEncoder qpackEncoder;
33  
34      QpackDecoderHandler(QpackEncoder qpackEncoder) {
35          this.qpackEncoder = qpackEncoder;
36      }
37  
38      @Override
39      protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
40          if (!in.isReadable()) {
41              return;
42          }
43          if (discard) {
44              in.skipBytes(in.readableBytes());
45              return;
46          }
47  
48          byte b = in.getByte(in.readerIndex());
49  
50          // 4.4.1. Section Acknowledgment
51          //
52          //   0   1   2   3   4   5   6   7
53          // +---+---+---+---+---+---+---+---+
54          // | 1 |      Stream ID (7+)       |
55          // +---+---------------------------+
56          if ((b & 0b1000_0000) == 0b1000_0000) {
57              long streamId = QpackUtil.decodePrefixedInteger(in, 7);
58              if (streamId < 0) {
59                  // Not enough readable bytes
60                  return;
61              }
62              try {
63                  qpackEncoder.sectionAcknowledgment(streamId);
64              } catch (QpackException e) {
65                  connectionError(ctx, new Http3Exception(QPACK_DECODER_STREAM_ERROR,
66                                  "Section acknowledgment decode failed.", e), true);
67              }
68              return;
69          }
70  
71          // 4.4.2. Stream Cancellation
72          //
73          //   0   1   2   3   4   5   6   7
74          // +---+---+---+---+---+---+---+---+
75          // | 0 | 1 |     Stream ID (6+)    |
76          // +---+---+-----------------------+
77          if ((b & 0b1100_0000) == 0b0100_0000) {
78              long streamId = QpackUtil.decodePrefixedInteger(in, 6);
79              if (streamId < 0) {
80                  // Not enough readable bytes
81                  return;
82              }
83              try {
84                  qpackEncoder.streamCancellation(streamId);
85              } catch (QpackException e) {
86                  connectionError(ctx, new Http3Exception(QPACK_DECODER_STREAM_ERROR,
87                          "Stream cancellation decode failed.", e), true);
88              }
89              return;
90          }
91  
92          // 4.4.3. Insert Count Increment
93          //
94          //   0   1   2   3   4   5   6   7
95          // +---+---+---+---+---+---+---+---+
96          // | 0 | 0 |     Increment (6+)    |
97          // +---+---+-----------------------+
98          if ((b & 0b1100_0000) == 0b0000_0000) {
99              int increment = decodePrefixedIntegerAsInt(in, 6);
100             if (increment == 0) {
101                 discard = true;
102                 // Zero is not allowed as an increment
103                 // https://www.rfc-editor.org/rfc/rfc9204.html#name-insert-count-increment
104                 // Increment is an unsigned integer, so only 0 is the invalid value.
105                 // https://www.rfc-editor.org/rfc/rfc7541#section-5
106                 connectionError(ctx, QPACK_DECODER_STREAM_ERROR,
107                         "Invalid increment '" + increment + "'.",  false);
108                 return;
109             }
110             if (increment < 0) {
111                 // Not enough readable bytes
112                 return;
113             }
114             try {
115                 qpackEncoder.insertCountIncrement(increment);
116             } catch (QpackException e) {
117                 connectionError(ctx, new Http3Exception(QPACK_DECODER_STREAM_ERROR,
118                         "Insert count increment decode failed.", e), true);
119             }
120             return;
121         }
122         // unknown frame
123         discard = true;
124         connectionError(ctx, QPACK_DECODER_STREAM_ERROR,
125                 "Unknown decoder instruction '" + b + "'.",  false);
126     }
127 
128     @Override
129     public void channelReadComplete(ChannelHandlerContext ctx) {
130         ctx.fireChannelReadComplete();
131 
132         // QPACK streams should always be processed, no matter what the user is doing in terms of configuration
133         // and AUTO_READ.
134         Http3CodecUtils.readIfNoAutoRead(ctx);
135     }
136 
137     @Override
138     public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
139         if (evt instanceof ChannelInputShutdownEvent) {
140             // See https://www.rfc-editor.org/rfc/rfc9204.html#name-encoder-and-decoder-streams
141             Http3CodecUtils.criticalStreamClosed(ctx);
142         }
143         ctx.fireUserEventTriggered(evt);
144     }
145 
146     @Override
147     public void channelInactive(ChannelHandlerContext ctx) {
148         // See https://www.rfc-editor.org/rfc/rfc9204.html#name-encoder-and-decoder-streams
149         Http3CodecUtils.criticalStreamClosed(ctx);
150         ctx.fireChannelInactive();
151     }
152 }