Skip to content

Commit 893040c

Browse files
committed
Enhance HTTP/2 frame handling and add unit tests for settings validation
1 parent 59b460b commit 893040c

File tree

2 files changed

+60
-6
lines changed

2 files changed

+60
-6
lines changed

source/vibe/http/internal/http2/server.d

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -274,10 +274,7 @@ private void handleHTTP2FrameChain(ConnectionStream)(ConnectionStream stream, TC
274274
}
275275
} catch (Exception e) {
276276
logException(e, "Failed to handle HTTP/2 frame chain");
277-
try { connection.close(); } catch (Exception) {}
278-
return true;
279-
} catch (Throwable t) {
280-
logWarn("HTTP/2 frame handler error: %s", t.msg);
277+
try { stream.finalize(); } catch (Exception) {}
281278
try { connection.close(); } catch (Exception) {}
282279
return true;
283280
}
@@ -626,14 +623,16 @@ private bool handleFrameAlloc(ConnectionStream)(ref ConnectionStream stream, TCP
626623
static if (!is(ConnectionStream : TLSStream)) {
627624

628625
if (context.resFrame) {
626+
// h2c upgrade response is always on stream 1 (RFC 7540 §3.2)
627+
enum h2cUpgradeStreamId = 1;
629628
auto l = context.resFrame.takeExactly(3).fromBytes(3) + HTTP2HeaderLength;
630629
auto hpackPayload = context.resFrame[HTTP2HeaderLength .. l];
631630
ubyte isEndStream = (context.resFrame.length > l)
632631
? cast(ubyte) 0x0 : HTTP2FrameFlag.END_STREAM;
633632

634633
try {
635634
stream.write(buildSplitHeaderFrames(hpackPayload,
636-
context.settings.maxFrameSize, 1, isEndStream, alloc));
635+
context.settings.maxFrameSize, h2cUpgradeStreamId, isEndStream, alloc));
637636
} catch (Exception e) {
638637
logWarn("Unable to write HEADERS Frame to stream");
639638
}
@@ -653,7 +652,7 @@ private bool handleFrameAlloc(ConnectionStream)(ref ConnectionStream stream, TCP
653652
assert(false, "TODO");
654653

655654
// create DATA frame header
656-
dataFrame.createHTTP2FrameHeader(cast(uint)resBody.length, HTTP2FrameType.DATA, 0x1, 1);
655+
dataFrame.createHTTP2FrameHeader(cast(uint)resBody.length, HTTP2FrameType.DATA, 0x1, h2cUpgradeStreamId);
657656

658657
// append the DATA body
659658
dataFrame.put(resBody);

source/vibe/http/internal/http2/settings.d

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,61 @@ unittest {
303303
assert(!settings.decode!Base64URL("a|b+*-c"));
304304
}
305305

306+
// unpackSettings rejects enablePush values other than 0 or 1
307+
unittest {
308+
import std.bitmanip : nativeToBigEndian;
309+
310+
// SETTINGS_ENABLE_PUSH (0x2) = 2 (invalid per RFC 7540 §6.5.2)
311+
ubyte[] src = nativeToBigEndian(cast(ushort) 0x2) ~ nativeToBigEndian(cast(uint) 2);
312+
HTTP2Settings settings;
313+
try {
314+
unpackSettings(settings, src);
315+
assert(false, "Expected PROTOCOL_ERROR for enablePush=2");
316+
} catch (HTTP2Exception e) {
317+
assert(e.code == HTTP2Error.PROTOCOL_ERROR);
318+
}
319+
}
320+
321+
// unpackSettings rejects initialWindowSize >= 2^31
322+
unittest {
323+
import std.bitmanip : nativeToBigEndian;
324+
325+
// SETTINGS_INITIAL_WINDOW_SIZE (0x4) = 2^31 (invalid per RFC 7540 §6.5.2)
326+
ubyte[] src = nativeToBigEndian(cast(ushort) 0x4) ~ nativeToBigEndian(cast(uint)(1u << 31));
327+
HTTP2Settings settings;
328+
try {
329+
unpackSettings(settings, src);
330+
assert(false, "Expected FLOW_CONTROL_ERROR for initialWindowSize=2^31");
331+
} catch (HTTP2Exception e) {
332+
assert(e.code == HTTP2Error.FLOW_CONTROL_ERROR);
333+
}
334+
}
335+
336+
// unpackSettings rejects maxFrameSize outside [2^14, 2^24)
337+
unittest {
338+
import std.bitmanip : nativeToBigEndian;
339+
340+
// SETTINGS_MAX_FRAME_SIZE (0x5) = 100 (below 2^14, invalid)
341+
ubyte[] src = nativeToBigEndian(cast(ushort) 0x5) ~ nativeToBigEndian(cast(uint) 100);
342+
HTTP2Settings settings;
343+
try {
344+
unpackSettings(settings, src);
345+
assert(false, "Expected PROTOCOL_ERROR for maxFrameSize=100");
346+
} catch (HTTP2Exception e) {
347+
assert(e.code == HTTP2Error.PROTOCOL_ERROR);
348+
}
349+
350+
// SETTINGS_MAX_FRAME_SIZE (0x5) = 2^24 (at upper bound, invalid)
351+
src = nativeToBigEndian(cast(ushort) 0x5) ~ nativeToBigEndian(cast(uint)(1u << 24));
352+
settings = HTTP2Settings.init;
353+
try {
354+
unpackSettings(settings, src);
355+
assert(false, "Expected PROTOCOL_ERROR for maxFrameSize=2^24");
356+
} catch (HTTP2Exception e) {
357+
assert(e.code == HTTP2Error.PROTOCOL_ERROR);
358+
}
359+
}
360+
306361
/** Context is initialized on each new connection
307362
308363
it MUST remain consistent between streams of the same connection

0 commit comments

Comments
 (0)