-
-
Notifications
You must be signed in to change notification settings - Fork 15.8k
/
Http2Exception.java
335 lines (289 loc) · 13.9 KB
/
Http2Exception.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License, version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package io.netty.handler.codec.http2;
import io.netty.util.internal.ThrowableUtil;
import io.netty.util.internal.UnstableApi;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import static io.netty.handler.codec.http2.Http2CodecUtil.CONNECTION_STREAM_ID;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
/**
* Exception thrown when an HTTP/2 error was encountered.
*/
@UnstableApi
public class Http2Exception extends Exception {
private static final long serialVersionUID = -6941186345430164209L;
private final Http2Error error;
private final ShutdownHint shutdownHint;
public Http2Exception(Http2Error error) {
this(error, ShutdownHint.HARD_SHUTDOWN);
}
public Http2Exception(Http2Error error, ShutdownHint shutdownHint) {
this.error = checkNotNull(error, "error");
this.shutdownHint = checkNotNull(shutdownHint, "shutdownHint");
}
public Http2Exception(Http2Error error, String message) {
this(error, message, ShutdownHint.HARD_SHUTDOWN);
}
public Http2Exception(Http2Error error, String message, ShutdownHint shutdownHint) {
super(message);
this.error = checkNotNull(error, "error");
this.shutdownHint = checkNotNull(shutdownHint, "shutdownHint");
}
public Http2Exception(Http2Error error, String message, Throwable cause) {
this(error, message, cause, ShutdownHint.HARD_SHUTDOWN);
}
public Http2Exception(Http2Error error, String message, Throwable cause, ShutdownHint shutdownHint) {
super(message, cause);
this.error = checkNotNull(error, "error");
this.shutdownHint = checkNotNull(shutdownHint, "shutdownHint");
}
static Http2Exception newStatic(Http2Error error, String message, ShutdownHint shutdownHint,
Class<?> clazz, String method) {
final Http2Exception exception = new StacklessHttp2Exception(error, message, shutdownHint, true);
return ThrowableUtil.unknownStackTrace(exception, clazz, method);
}
private Http2Exception(Http2Error error, String message, ShutdownHint shutdownHint, boolean shared) {
super(message, null, false, true);
assert shared;
this.error = checkNotNull(error, "error");
this.shutdownHint = checkNotNull(shutdownHint, "shutdownHint");
}
public Http2Error error() {
return error;
}
/**
* Provide a hint as to what type of shutdown should be executed. Note this hint may be ignored.
*/
public ShutdownHint shutdownHint() {
return shutdownHint;
}
/**
* Use if an error has occurred which can not be isolated to a single stream, but instead applies
* to the entire connection.
* @param error The type of error as defined by the HTTP/2 specification.
* @param fmt String with the content and format for the additional debug data.
* @param args Objects which fit into the format defined by {@code fmt}.
* @return An exception which can be translated into an HTTP/2 error.
*/
public static Http2Exception connectionError(Http2Error error, String fmt, Object... args) {
return new Http2Exception(error, formatErrorMessage(fmt, args));
}
/**
* Use if an error has occurred which can not be isolated to a single stream, but instead applies
* to the entire connection.
* @param error The type of error as defined by the HTTP/2 specification.
* @param cause The object which caused the error.
* @param fmt String with the content and format for the additional debug data.
* @param args Objects which fit into the format defined by {@code fmt}.
* @return An exception which can be translated into an HTTP/2 error.
*/
public static Http2Exception connectionError(Http2Error error, Throwable cause,
String fmt, Object... args) {
return new Http2Exception(error, formatErrorMessage(fmt, args), cause);
}
/**
* Use if an error has occurred which can not be isolated to a single stream, but instead applies
* to the entire connection.
* @param error The type of error as defined by the HTTP/2 specification.
* @param fmt String with the content and format for the additional debug data.
* @param args Objects which fit into the format defined by {@code fmt}.
* @return An exception which can be translated into an HTTP/2 error.
*/
public static Http2Exception closedStreamError(Http2Error error, String fmt, Object... args) {
return new ClosedStreamCreationException(error, formatErrorMessage(fmt, args));
}
/**
* Use if an error which can be isolated to a single stream has occurred. If the {@code id} is not
* {@link Http2CodecUtil#CONNECTION_STREAM_ID} then a {@link StreamException} will be returned.
* Otherwise the error is considered a connection error and a {@link Http2Exception} is returned.
* @param id The stream id for which the error is isolated to.
* @param error The type of error as defined by the HTTP/2 specification.
* @param fmt String with the content and format for the additional debug data.
* @param args Objects which fit into the format defined by {@code fmt}.
* @return If the {@code id} is not
* {@link Http2CodecUtil#CONNECTION_STREAM_ID} then a {@link StreamException} will be returned.
* Otherwise the error is considered a connection error and a {@link Http2Exception} is returned.
*/
public static Http2Exception streamError(int id, Http2Error error, String fmt, Object... args) {
return CONNECTION_STREAM_ID == id ?
connectionError(error, fmt, args) :
new StreamException(id, error, formatErrorMessage(fmt, args));
}
/**
* Use if an error which can be isolated to a single stream has occurred. If the {@code id} is not
* {@link Http2CodecUtil#CONNECTION_STREAM_ID} then a {@link StreamException} will be returned.
* Otherwise the error is considered a connection error and a {@link Http2Exception} is returned.
* @param id The stream id for which the error is isolated to.
* @param error The type of error as defined by the HTTP/2 specification.
* @param cause The object which caused the error.
* @param fmt String with the content and format for the additional debug data.
* @param args Objects which fit into the format defined by {@code fmt}.
* @return If the {@code id} is not
* {@link Http2CodecUtil#CONNECTION_STREAM_ID} then a {@link StreamException} will be returned.
* Otherwise the error is considered a connection error and a {@link Http2Exception} is returned.
*/
public static Http2Exception streamError(int id, Http2Error error, Throwable cause,
String fmt, Object... args) {
return CONNECTION_STREAM_ID == id ?
connectionError(error, cause, fmt, args) :
new StreamException(id, error, formatErrorMessage(fmt, args), cause);
}
/**
* A specific stream error resulting from failing to decode headers that exceeds the max header size list.
* If the {@code id} is not {@link Http2CodecUtil#CONNECTION_STREAM_ID} then a
* {@link StreamException} will be returned. Otherwise the error is considered a
* connection error and a {@link Http2Exception} is returned.
* @param id The stream id for which the error is isolated to.
* @param error The type of error as defined by the HTTP/2 specification.
* @param onDecode Whether this error was caught while decoding headers
* @param fmt String with the content and format for the additional debug data.
* @param args Objects which fit into the format defined by {@code fmt}.
* @return If the {@code id} is not
* {@link Http2CodecUtil#CONNECTION_STREAM_ID} then a {@link HeaderListSizeException}
* will be returned. Otherwise the error is considered a connection error and a {@link Http2Exception} is
* returned.
*/
public static Http2Exception headerListSizeError(int id, Http2Error error, boolean onDecode,
String fmt, Object... args) {
return CONNECTION_STREAM_ID == id ?
connectionError(error, fmt, args) :
new HeaderListSizeException(id, error, formatErrorMessage(fmt, args), onDecode);
}
private static String formatErrorMessage(String fmt, Object[] args) {
if (fmt == null) {
if (args == null || args.length == 0) {
return "Unexpected error";
}
return "Unexpected error: " + Arrays.toString(args);
}
return String.format(fmt, args);
}
/**
* Check if an exception is isolated to a single stream or the entire connection.
* @param e The exception to check.
* @return {@code true} if {@code e} is an instance of {@link StreamException}.
* {@code false} otherwise.
*/
public static boolean isStreamError(Http2Exception e) {
return e instanceof StreamException;
}
/**
* Get the stream id associated with an exception.
* @param e The exception to get the stream id for.
* @return {@link Http2CodecUtil#CONNECTION_STREAM_ID} if {@code e} is a connection error.
* Otherwise the stream id associated with the stream error.
*/
public static int streamId(Http2Exception e) {
return isStreamError(e) ? ((StreamException) e).streamId() : CONNECTION_STREAM_ID;
}
/**
* Provides a hint as to if shutdown is justified, what type of shutdown should be executed.
*/
public enum ShutdownHint {
/**
* Do not shutdown the underlying channel.
*/
NO_SHUTDOWN,
/**
* Attempt to execute a "graceful" shutdown. The definition of "graceful" is left to the implementation.
* An example of "graceful" would be wait for some amount of time until all active streams are closed.
*/
GRACEFUL_SHUTDOWN,
/**
* Close the channel immediately after a {@code GOAWAY} is sent.
*/
HARD_SHUTDOWN
}
/**
* Used when a stream creation attempt fails but may be because the stream was previously closed.
*/
public static final class ClosedStreamCreationException extends Http2Exception {
private static final long serialVersionUID = -6746542974372246206L;
public ClosedStreamCreationException(Http2Error error) {
super(error);
}
public ClosedStreamCreationException(Http2Error error, String message) {
super(error, message);
}
public ClosedStreamCreationException(Http2Error error, String message, Throwable cause) {
super(error, message, cause);
}
}
/**
* Represents an exception that can be isolated to a single stream (as opposed to the entire connection).
*/
public static class StreamException extends Http2Exception {
private static final long serialVersionUID = 602472544416984384L;
private final int streamId;
StreamException(int streamId, Http2Error error, String message) {
super(error, message, ShutdownHint.NO_SHUTDOWN);
this.streamId = streamId;
}
StreamException(int streamId, Http2Error error, String message, Throwable cause) {
super(error, message, cause, ShutdownHint.NO_SHUTDOWN);
this.streamId = streamId;
}
public int streamId() {
return streamId;
}
}
public static final class HeaderListSizeException extends StreamException {
private static final long serialVersionUID = -8807603212183882637L;
private final boolean decode;
HeaderListSizeException(int streamId, Http2Error error, String message, boolean decode) {
super(streamId, error, message);
this.decode = decode;
}
public boolean duringDecode() {
return decode;
}
}
/**
* Provides the ability to handle multiple stream exceptions with one throw statement.
*/
public static final class CompositeStreamException extends Http2Exception implements Iterable<StreamException> {
private static final long serialVersionUID = 7091134858213711015L;
private final List<StreamException> exceptions;
public CompositeStreamException(Http2Error error, int initialCapacity) {
super(error, ShutdownHint.NO_SHUTDOWN);
exceptions = new ArrayList<StreamException>(initialCapacity);
}
public void add(StreamException e) {
exceptions.add(e);
}
@Override
public Iterator<StreamException> iterator() {
return exceptions.iterator();
}
}
private static final class StacklessHttp2Exception extends Http2Exception {
private static final long serialVersionUID = 1077888485687219443L;
StacklessHttp2Exception(Http2Error error, String message, ShutdownHint shutdownHint) {
super(error, message, shutdownHint);
}
StacklessHttp2Exception(Http2Error error, String message, ShutdownHint shutdownHint, boolean shared) {
super(error, message, shutdownHint, shared);
}
// Override fillInStackTrace() so we not populate the backtrace via a native call and so leak the
// Classloader.
@Override
public Throwable fillInStackTrace() {
return this;
}
}
}