| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -457,6 +457,8 @@ Http2Session::Callbacks::Callbacks(bool kHasGetPaddingCallback) { | |||
| 457 | 457 | callbacks, OnNghttpError); | |
| 458 | 458 | nghttp2_session_callbacks_set_send_data_callback( | |
| 459 | 459 | callbacks, OnSendData); | |
| 460 | + nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( | ||
| 461 | + callbacks, OnInvalidFrame); | ||
| 460 | 462 | ||
| 461 | 463 | if (kHasGetPaddingCallback) { | |
| 462 | 464 | nghttp2_session_callbacks_set_select_padding_callback( | |
@@ -881,6 +883,31 @@ inline int Http2Session::OnFrameReceive(nghttp2_session* handle, | |||
| 881 | 883 | return 0; | |
| 882 | 884 | } | |
| 883 | 885 | ||
| 886 | + inline int Http2Session::OnInvalidFrame(nghttp2_session* handle, | ||
| 887 | + const nghttp2_frame *frame, | ||
| 888 | + int lib_error_code, | ||
| 889 | + void* user_data) { | ||
| 890 | + Http2Session* session = static_cast<Http2Session*>(user_data); | ||
| 891 | + | ||
| 892 | + DEBUG_HTTP2SESSION2(session, "invalid frame received, code: %d", | ||
| 893 | + lib_error_code); | ||
| 894 | + | ||
| 895 | + // If the error is fatal or if error code is ERR_STREAM_CLOSED... emit error | ||
| 896 | + if (nghttp2_is_fatal(lib_error_code) || | ||
| 897 | + lib_error_code == NGHTTP2_ERR_STREAM_CLOSED) { | ||
| 898 | + Environment* env = session->env(); | ||
| 899 | + Isolate* isolate = env->isolate(); | ||
| 900 | + HandleScope scope(isolate); | ||
| 901 | + Local<Context> context = env->context(); | ||
| 902 | + Context::Scope context_scope(context); | ||
| 903 | + | ||
| 904 | + Local<Value> argv[1] = { | ||
| 905 | + Integer::New(isolate, lib_error_code), | ||
| 906 | + }; | ||
| 907 | + session->MakeCallback(env->error_string(), arraysize(argv), argv); | ||
| 908 | + } | ||
| 909 | + return 0; | ||
| 910 | + } | ||
| 884 | 911 | ||
| 885 | 912 | // If nghttp2 is unable to send a queued up frame, it will call this callback | |
| 886 | 913 | // to let us know. If the failure occurred because we are in the process of | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -992,6 +992,11 @@ class Http2Session : public AsyncWrap { | |||
| 992 | 992 | size_t length, | |
| 993 | 993 | nghttp2_data_source* source, | |
| 994 | 994 | void* user_data); | |
| 995 | + static inline int OnInvalidFrame( | ||
| 996 | + nghttp2_session* session, | ||
| 997 | + const nghttp2_frame *frame, | ||
| 998 | + int lib_error_code, | ||
| 999 | + void* user_data); | ||
| 995 | 1000 | ||
| 996 | 1001 | ||
| 997 | 1002 | static inline ssize_t OnStreamReadFD( | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,129 @@ | |||
| 1 | + /* eslint-disable required-modules */ | ||
| 2 | + 'use strict'; | ||
| 3 | + | ||
| 4 | + // An HTTP/2 testing tool used to create mock frames for direct testing | ||
| 5 | + // of HTTP/2 endpoints. | ||
| 6 | + | ||
| 7 | + const kFrameData = Symbol('frame-data'); | ||
| 8 | + const FLAG_EOS = 0x1; | ||
| 9 | + const FLAG_ACK = 0x1; | ||
| 10 | + const FLAG_EOH = 0x4; | ||
| 11 | + const FLAG_PADDED = 0x8; | ||
| 12 | + const PADDING = Buffer.alloc(255); | ||
| 13 | + | ||
| 14 | + const kClientMagic = Buffer.from('505249202a20485454502f322' + | ||
| 15 | + 'e300d0a0d0a534d0d0a0d0a', 'hex'); | ||
| 16 | + | ||
| 17 | + const kFakeRequestHeaders = Buffer.from('828684410f7777772e65' + | ||
| 18 | + '78616d706c652e636f6d', 'hex'); | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + const kFakeResponseHeaders = Buffer.from('4803333032580770726976617465611d' + | ||
| 22 | + '4d6f6e2c203231204f63742032303133' + | ||
| 23 | + '2032303a31333a323120474d546e1768' + | ||
| 24 | + '747470733a2f2f7777772e6578616d70' + | ||
| 25 | + '6c652e636f6d', 'hex'); | ||
| 26 | + | ||
| 27 | + function isUint32(val) { | ||
| 28 | + return val >>> 0 === val; | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + function isUint24(val) { | ||
| 32 | + return val >>> 0 === val && val <= 0xFFFFFF; | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + function isUint8(val) { | ||
| 36 | + return val >>> 0 === val && val <= 0xFF; | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + function write32BE(array, pos, val) { | ||
| 40 | + if (!isUint32(val)) | ||
| 41 | + throw new RangeError('val is not a 32-bit number'); | ||
| 42 | + array[pos++] = (val >> 24) & 0xff; | ||
| 43 | + array[pos++] = (val >> 16) & 0xff; | ||
| 44 | + array[pos++] = (val >> 8) & 0xff; | ||
| 45 | + array[pos++] = val & 0xff; | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + function write24BE(array, pos, val) { | ||
| 49 | + if (!isUint24(val)) | ||
| 50 | + throw new RangeError('val is not a 24-bit number'); | ||
| 51 | + array[pos++] = (val >> 16) & 0xff; | ||
| 52 | + array[pos++] = (val >> 8) & 0xff; | ||
| 53 | + array[pos++] = val & 0xff; | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + function write8(array, pos, val) { | ||
| 57 | + if (!isUint8(val)) | ||
| 58 | + throw new RangeError('val is not an 8-bit number'); | ||
| 59 | + array[pos] = val; | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + class Frame { | ||
| 63 | + constructor(length, type, flags, id) { | ||
| 64 | + this[kFrameData] = Buffer.alloc(9); | ||
| 65 | + write24BE(this[kFrameData], 0, length); | ||
| 66 | + write8(this[kFrameData], 3, type); | ||
| 67 | + write8(this[kFrameData], 4, flags); | ||
| 68 | + write32BE(this[kFrameData], 5, id); | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + get data() { | ||
| 72 | + return this[kFrameData]; | ||
| 73 | + } | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + class SettingsFrame extends Frame { | ||
| 77 | + constructor(ack = false) { | ||
| 78 | + let flags = 0; | ||
| 79 | + if (ack) | ||
| 80 | + flags |= FLAG_ACK; | ||
| 81 | + super(0, 4, flags, 0); | ||
| 82 | + } | ||
| 83 | + } | ||
| 84 | + | ||
| 85 | + class DataFrame extends Frame { | ||
| 86 | + constructor(id, payload, padlen = 0, final = false) { | ||
| 87 | + let len = payload.length; | ||
| 88 | + let flags = 0; | ||
| 89 | + if (final) flags |= FLAG_EOS; | ||
| 90 | + const buffers = [payload]; | ||
| 91 | + if (padlen > 0) { | ||
| 92 | + buffers.unshift(Buffer.from([padlen])); | ||
| 93 | + buffers.push(PADDING.slice(0, padlen)); | ||
| 94 | + len += padlen + 1; | ||
| 95 | + flags |= FLAG_PADDED; | ||
| 96 | + } | ||
| 97 | + super(len, 0, flags, id); | ||
| 98 | + buffers.unshift(this[kFrameData]); | ||
| 99 | + this[kFrameData] = Buffer.concat(buffers); | ||
| 100 | + } | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + class HeadersFrame extends Frame { | ||
| 104 | + constructor(id, payload, padlen = 0, final = false) { | ||
| 105 | + let len = payload.length; | ||
| 106 | + let flags = FLAG_EOH; | ||
| 107 | + if (final) flags |= FLAG_EOS; | ||
| 108 | + const buffers = [payload]; | ||
| 109 | + if (padlen > 0) { | ||
| 110 | + buffers.unshift(Buffer.from([padlen])); | ||
| 111 | + buffers.push(PADDING.slice(0, padlen)); | ||
| 112 | + len += padlen + 1; | ||
| 113 | + flags |= FLAG_PADDED; | ||
| 114 | + } | ||
| 115 | + super(len, 1, flags, id); | ||
| 116 | + buffers.unshift(this[kFrameData]); | ||
| 117 | + this[kFrameData] = Buffer.concat(buffers); | ||
| 118 | + } | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + module.exports = { | ||
| 122 | + Frame, | ||
| 123 | + DataFrame, | ||
| 124 | + HeadersFrame, | ||
| 125 | + SettingsFrame, | ||
| 126 | + kFakeRequestHeaders, | ||
| 127 | + kFakeResponseHeaders, | ||
| 128 | + kClientMagic | ||
| 129 | + }; | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,59 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + | ||
| 3 | + const common = require('../common'); | ||
| 4 | + | ||
| 5 | + if (!common.hasCrypto) | ||
| 6 | + common.skip('missing crypto'); | ||
| 7 | + | ||
| 8 | + const h2 = require('http2'); | ||
| 9 | + const net = require('net'); | ||
| 10 | + const h2test = require('../common/http2'); | ||
| 11 | + let client; | ||
| 12 | + | ||
| 13 | + const server = h2.createServer(); | ||
| 14 | + server.on('stream', common.mustCall((stream) => { | ||
| 15 | + stream.respond(); | ||
| 16 | + stream.end('ok'); | ||
| 17 | + }, 2)); | ||
| 18 | + server.on('session', common.mustCall((session) => { | ||
| 19 | + session.on('error', common.expectsError({ | ||
| 20 | + code: 'ERR_HTTP2_ERROR', | ||
| 21 | + type: Error, | ||
| 22 | + message: 'Stream was already closed or invalid' | ||
| 23 | + })); | ||
| 24 | + })); | ||
| 25 | + | ||
| 26 | + const settings = new h2test.SettingsFrame(); | ||
| 27 | + const settingsAck = new h2test.SettingsFrame(true); | ||
| 28 | + const head1 = new h2test.HeadersFrame(1, h2test.kFakeRequestHeaders, 0, true); | ||
| 29 | + const head2 = new h2test.HeadersFrame(3, h2test.kFakeRequestHeaders, 0, true); | ||
| 30 | + const head3 = new h2test.HeadersFrame(1, h2test.kFakeRequestHeaders, 0, true); | ||
| 31 | + const head4 = new h2test.HeadersFrame(5, h2test.kFakeRequestHeaders, 0, true); | ||
| 32 | + | ||
| 33 | + server.listen(0, () => { | ||
| 34 | + client = net.connect(server.address().port, () => { | ||
| 35 | + client.write(h2test.kClientMagic, () => { | ||
| 36 | + client.write(settings.data, () => { | ||
| 37 | + client.write(settingsAck.data); | ||
| 38 | + // This will make it ok. | ||
| 39 | + client.write(head1.data, () => { | ||
| 40 | + // This will make it ok. | ||
| 41 | + client.write(head2.data, () => { | ||
| 42 | + // This will cause an error to occur because the client is | ||
| 43 | + // attempting to reuse an already closed stream. This must | ||
| 44 | + // cause the server session to be torn down. | ||
| 45 | + client.write(head3.data, () => { | ||
| 46 | + // This won't ever make it to the server | ||
| 47 | + client.write(head4.data); | ||
| 48 | + }); | ||
| 49 | + }); | ||
| 50 | + }); | ||
| 51 | + }); | ||
| 52 | + }); | ||
| 53 | + }); | ||
| 54 | + | ||
| 55 | + // An error may or may not be emitted on the client side, we don't care | ||
| 56 | + // either way if it is, but we don't want to die if it is. | ||
| 57 | + client.on('error', () => {}); | ||
| 58 | + client.on('close', common.mustCall(() => server.close())); | ||
| 59 | + }); | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments