1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package io.netty5.handler.codec.http2;
16
17 import io.netty5.buffer.api.Buffer;
18 import io.netty5.util.internal.UnstableApi;
19
20 import static io.netty5.handler.codec.http2.Http2CodecUtil.DEFAULT_HEADER_LIST_SIZE;
21 import static io.netty5.handler.codec.http2.Http2Error.COMPRESSION_ERROR;
22 import static io.netty5.handler.codec.http2.Http2Error.INTERNAL_ERROR;
23 import static io.netty5.handler.codec.http2.Http2Exception.connectionError;
24 import static java.util.Objects.requireNonNull;
25
26 @UnstableApi
27 public class DefaultHttp2HeadersDecoder implements Http2HeadersDecoder, Http2HeadersDecoder.Configuration {
28 private static final float HEADERS_COUNT_WEIGHT_NEW = 1 / 5f;
29 private static final float HEADERS_COUNT_WEIGHT_HISTORICAL = 1 - HEADERS_COUNT_WEIGHT_NEW;
30
31 private final HpackDecoder hpackDecoder;
32 private final boolean validateHeaders;
33 private long maxHeaderListSizeGoAway;
34
35
36
37
38
39 private float headerArraySizeAccumulator = 8;
40
41 public DefaultHttp2HeadersDecoder() {
42 this(true);
43 }
44
45 public DefaultHttp2HeadersDecoder(boolean validateHeaders) {
46 this(validateHeaders, DEFAULT_HEADER_LIST_SIZE);
47 }
48
49
50
51
52
53
54
55
56
57 public DefaultHttp2HeadersDecoder(boolean validateHeaders, long maxHeaderListSize) {
58 this(validateHeaders, new HpackDecoder(maxHeaderListSize));
59 }
60
61
62
63
64
65 DefaultHttp2HeadersDecoder(boolean validateHeaders, HpackDecoder hpackDecoder) {
66 this.hpackDecoder = requireNonNull(hpackDecoder, "hpackDecoder");
67 this.validateHeaders = validateHeaders;
68 maxHeaderListSizeGoAway =
69 Http2CodecUtil.calculateMaxHeaderListSizeGoAway(hpackDecoder.getMaxHeaderListSize());
70 }
71
72 @Override
73 public void maxHeaderTableSize(long max) throws Http2Exception {
74 hpackDecoder.setMaxHeaderTableSize(max);
75 }
76
77 @Override
78 public long maxHeaderTableSize() {
79 return hpackDecoder.getMaxHeaderTableSize();
80 }
81
82 @Override
83 public void maxHeaderListSize(long max, long goAwayMax) throws Http2Exception {
84 if (goAwayMax < max || goAwayMax < 0) {
85 throw connectionError(INTERNAL_ERROR, "Header List Size GO_AWAY %d must be non-negative and >= %d",
86 goAwayMax, max);
87 }
88 hpackDecoder.setMaxHeaderListSize(max);
89 maxHeaderListSizeGoAway = goAwayMax;
90 }
91
92 @Override
93 public long maxHeaderListSize() {
94 return hpackDecoder.getMaxHeaderListSize();
95 }
96
97 @Override
98 public long maxHeaderListSizeGoAway() {
99 return maxHeaderListSizeGoAway;
100 }
101
102 @Override
103 public Configuration configuration() {
104 return this;
105 }
106
107 @Override
108 public Http2Headers decodeHeaders(int streamId, Buffer headerBlock) throws Http2Exception {
109 try {
110 final Http2Headers headers = newHeaders();
111 hpackDecoder.decode(streamId, headerBlock, headers, validateHeaders);
112 headerArraySizeAccumulator = HEADERS_COUNT_WEIGHT_NEW * headers.size() +
113 HEADERS_COUNT_WEIGHT_HISTORICAL * headerArraySizeAccumulator;
114 return headers;
115 } catch (Http2Exception e) {
116 throw e;
117 } catch (Throwable e) {
118
119
120
121 throw connectionError(COMPRESSION_ERROR, e, "Error decoding headers: %s", e.getMessage());
122 }
123 }
124
125
126
127
128
129 protected final int numberOfHeadersGuess() {
130 return (int) headerArraySizeAccumulator;
131 }
132
133
134
135
136
137 protected final boolean validateHeaders() {
138 return validateHeaders;
139 }
140
141
142
143
144
145 protected Http2Headers newHeaders() {
146 return new DefaultHttp2Headers(validateHeaders, (int) headerArraySizeAccumulator);
147 }
148 }