Skip to content

Commit de7d764

Browse files
afrindmeta-codesync[bot]
authored andcommitted
Share the h3 wt CONNECT-stream capsule callback
Summary: HqWtSession.cpp had a file-local callback that turned capsules from the http/3 CONNECT stream into H3WtSession calls. The coro http/3 WebTransport session needs the same translation, so rather than write a second copy this moves it next to H3WtSession as `H3WtCapsuleCallback`. Two things change while moving it: `onDatagram` now hands the datagram to the session instead of dropping it, and `onConnectionError` closes the session with a clearer message. Reviewed By: hanidamlaj Differential Revision: D116105652 fbshipit-source-id: d590a3940e82b39afa2fe077ecc2c37a3374dd65
1 parent f5fa9b5 commit de7d764

4 files changed

Lines changed: 124 additions & 61 deletions

File tree

proxygen/lib/http/webtransport/HqWtSession.cpp

Lines changed: 1 addition & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -8,66 +8,6 @@
88

99
#include <proxygen/lib/http/webtransport/HqWtSession.h>
1010

11-
namespace {
12-
using namespace proxygen;
13-
using namespace proxygen::detail;
14-
15-
struct H3CapsuleCodecCb final : public WebTransportCapsuleCodec::Callback {
16-
H3WtSession& wtSess;
17-
explicit H3CapsuleCodecCb(H3WtSession& wtSess) : wtSess(wtSess) {
18-
}
19-
20-
void onMaxData(WTMaxDataCapsule c) noexcept override {
21-
VLOG(4) << __func__;
22-
wtSess.onConnMaxData({.maxData = c.maximumData});
23-
}
24-
void onMaxStreamsBidi(WTMaxStreamsCapsule c) noexcept override {
25-
VLOG(4) << __func__;
26-
wtSess.onMaxStreams(WtStreamManager::MaxStreamsBidi{c.maximumStreams});
27-
}
28-
void onMaxStreamsUni(WTMaxStreamsCapsule c) noexcept override {
29-
VLOG(4) << __func__;
30-
wtSess.onMaxStreams(WtStreamManager::MaxStreamsUni{c.maximumStreams});
31-
}
32-
void onDrainSession(DrainWebTransportSessionCapsule) noexcept override {
33-
VLOG(4) << __func__;
34-
wtSess.onDrainSession({});
35-
}
36-
void onCloseSession(CloseWebTransportSessionCapsule c) noexcept override {
37-
VLOG(4) << __func__;
38-
wtSess.onCloseSession(WtStreamManager::CloseSession{
39-
c.applicationErrorCode, std::move(c.applicationErrorMessage)});
40-
}
41-
void onConnectionError(CapsuleCodec::ErrorCode error) noexcept override {
42-
VLOG(4) << __func__;
43-
onCloseSession({uint8_t(error), "onConnectionError"});
44-
}
45-
46-
// ignored callbacks
47-
void onMaxStreamData(WTMaxStreamDataCapsule) noexcept override {
48-
}
49-
void onResetStream(WTResetStreamCapsule) noexcept override {
50-
}
51-
void onStopSending(WTStopSendingCapsule) noexcept override {
52-
}
53-
void onStream(WTStreamCapsule) noexcept override {
54-
}
55-
void onStreamDataBlocked(WTStreamDataBlockedCapsule) noexcept override {
56-
}
57-
void onStreamsBlockedBidi(WTStreamsBlockedCapsule) noexcept override {
58-
}
59-
void onStreamsBlockedUni(WTStreamsBlockedCapsule) noexcept override {
60-
}
61-
void onPadding(PaddingCapsule) noexcept override {
62-
}
63-
void onDatagram(DatagramCapsule) noexcept override {
64-
}
65-
void onDataBlocked(WTDataBlockedCapsule) noexcept override {
66-
}
67-
};
68-
69-
}; // namespace
70-
7111
namespace proxygen::detail {
7212

7313
struct WtReadLooper : public WtLooper {
@@ -86,7 +26,7 @@ struct WtReadLooper : public WtLooper {
8626
void runLoopCallback() noexcept override;
8727
HqWtSession& wtSess_;
8828
WebTransportTxnHandler& wtTxnHandler_;
89-
H3CapsuleCodecCb codecCb_;
29+
H3WtCapsuleCallback codecCb_;
9030
WebTransportCapsuleCodec codec_;
9131
};
9232

proxygen/lib/http/webtransport/QuicWtSession.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,4 +590,49 @@ void H3ConnectStreamCallback::onEvent(WtStreamManager::Event&& ev) noexcept {
590590
std::visit(visitor, ev);
591591
}
592592

593+
H3WtCapsuleCallback::H3WtCapsuleCallback(H3WtSession& session) noexcept
594+
: h3Wt_(session) {
595+
}
596+
597+
void H3WtCapsuleCallback::onMaxData(WTMaxDataCapsule c) noexcept {
598+
XLOG(DBG6) << __func__ << "; maxData=" << c.maximumData;
599+
h3Wt_.onConnMaxData({.maxData = c.maximumData});
600+
}
601+
602+
void H3WtCapsuleCallback::onMaxStreamsBidi(WTMaxStreamsCapsule c) noexcept {
603+
XLOG(DBG6) << __func__ << "; maxStreams=" << c.maximumStreams;
604+
h3Wt_.onMaxStreams(WtStreamManager::MaxStreamsBidi{c.maximumStreams});
605+
}
606+
607+
void H3WtCapsuleCallback::onMaxStreamsUni(WTMaxStreamsCapsule c) noexcept {
608+
XLOG(DBG6) << __func__ << "; maxStreams=" << c.maximumStreams;
609+
h3Wt_.onMaxStreams(WtStreamManager::MaxStreamsUni{c.maximumStreams});
610+
}
611+
612+
void H3WtCapsuleCallback::onDrainSession(
613+
DrainWebTransportSessionCapsule) noexcept {
614+
XLOG(DBG6) << __func__;
615+
h3Wt_.onDrainSession({});
616+
}
617+
618+
void H3WtCapsuleCallback::onCloseSession(
619+
CloseWebTransportSessionCapsule c) noexcept {
620+
XLOG(DBG6) << __func__ << "; err=" << c.applicationErrorCode
621+
<< "; msg=" << c.applicationErrorMessage;
622+
h3Wt_.onCloseSession(
623+
{.err = c.applicationErrorCode, .msg = c.applicationErrorMessage});
624+
}
625+
626+
void H3WtCapsuleCallback::onDatagram(DatagramCapsule c) noexcept {
627+
XLOG(DBG6) << __func__;
628+
h3Wt_.onDatagram(std::move(c.httpDatagramPayload));
629+
}
630+
631+
void H3WtCapsuleCallback::onConnectionError(
632+
CapsuleCodec::ErrorCode error) noexcept {
633+
XLOG(DBG4) << __func__ << "; err=" << uint64_t(error);
634+
onCloseSession({.applicationErrorCode = uint32_t(error),
635+
.applicationErrorMessage = "capsule parse error"});
636+
}
637+
593638
} // namespace proxygen

proxygen/lib/http/webtransport/QuicWtSession.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,4 +296,54 @@ class H3WtSession final : public QuicWtSessionBase {
296296
uint64_t connectStreamId_;
297297
};
298298

299+
/**
300+
* H3WtCapsuleCallback receives parsed capsule events from the
301+
* WebTransportCapsuleCodec on the http/3 CONNECT stream and forwards the
302+
* relevant subset (MaxData, MaxStreams(Uni|Bidi), DrainSession, CloseSession,
303+
* Datagram) to the owning H3WtSession.
304+
*
305+
* A datagram may reach an http/3 endpoint either as a quic datagram (RFC9297)
306+
* or as a DATAGRAM capsule on the CONNECT stream, so both paths land on
307+
* H3WtSession::onDatagram.
308+
*/
309+
struct H3WtCapsuleCallback : WebTransportCapsuleCodec::Callback {
310+
explicit H3WtCapsuleCallback(H3WtSession& session) noexcept;
311+
312+
void onMaxData(WTMaxDataCapsule c) noexcept override;
313+
void onMaxStreamsBidi(WTMaxStreamsCapsule c) noexcept override;
314+
void onMaxStreamsUni(WTMaxStreamsCapsule c) noexcept override;
315+
void onDrainSession(DrainWebTransportSessionCapsule) noexcept override;
316+
void onCloseSession(CloseWebTransportSessionCapsule c) noexcept override;
317+
void onDatagram(DatagramCapsule c) noexcept override;
318+
void onConnectionError(CapsuleCodec::ErrorCode error) noexcept override;
319+
320+
/**
321+
* Streams and per-stream flow control are carried by real quic streams over
322+
* http/3, so the capsules describing them are only meaningful over http/2.
323+
*/
324+
void onPadding(PaddingCapsule) noexcept override {
325+
}
326+
void onResetStream(WTResetStreamCapsule) noexcept override {
327+
}
328+
void onStopSending(WTStopSendingCapsule) noexcept override {
329+
}
330+
void onStream(WTStreamCapsule) noexcept override {
331+
}
332+
void onMaxStreamData(WTMaxStreamDataCapsule) noexcept override {
333+
}
334+
void onDataBlocked(WTDataBlockedCapsule) noexcept override {
335+
}
336+
void onStreamDataBlocked(WTStreamDataBlockedCapsule) noexcept override {
337+
}
338+
void onStreamsBlockedBidi(WTStreamsBlockedCapsule) noexcept override {
339+
}
340+
void onStreamsBlockedUni(WTStreamsBlockedCapsule) noexcept override {
341+
}
342+
void onCapsule(uint64_t, uint64_t) noexcept override {
343+
}
344+
345+
private:
346+
H3WtSession& h3Wt_;
347+
};
348+
299349
} // namespace proxygen

proxygen/lib/http/webtransport/test/QuicWtSessionTest.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include <folly/portability/GMock.h>
1010
#include <folly/portability/GTest.h>
11+
#include <proxygen/lib/http/codec/webtransport/WebTransportFramer.h>
1112
#include <proxygen/lib/http/session/test/MockQuicSocketDriver.h>
1213
#include <proxygen/lib/http/webtransport/QuicWtSession.h>
1314
#include <proxygen/lib/http/webtransport/test/Mocks.h>
@@ -921,6 +922,33 @@ TEST_F(H3WtSessionTest, CreateUniBidiStream) {
921922
}
922923
}
923924

925+
/**
926+
* A malformed capsule on the CONNECT stream is unrecoverable -- we can no
927+
* longer trust the framing -- so H3WtCapsuleCallback tears the wt session down
928+
* rather than ignoring it.
929+
*/
930+
TEST_F(H3WtSessionTest, CapsuleParseErrorClosesSession) {
931+
expectedWtHandlerErr_ = uint32_t(CapsuleCodec::ErrorCode::PARSE_UNDERFLOW);
932+
933+
H3WtCapsuleCallback capsuleCb{*session_};
934+
WebTransportCapsuleCodec codec{&capsuleCb, CodecVersion::H3};
935+
936+
// a well formed MAX_DATA capsule whose length byte is rewritten to announce
937+
// an 8-byte varint that is not there, so the codec reports a parse error
938+
folly::IOBufQueue queue{folly::IOBufQueue::cacheChainLength()};
939+
writeWTMaxData(queue, WTMaxDataCapsule{100});
940+
auto buf = queue.move();
941+
buf->writableData()[5] = 0xFF;
942+
943+
codec.onIngress(std::move(buf), true);
944+
945+
// the handler is notified via the fixture's onSessionEnd expectation, and the
946+
// session queues a CLOSE_SESSION for the connect stream
947+
EXPECT_FALSE(connectStreamCb_.events.empty());
948+
EXPECT_TRUE(std::holds_alternative<detail::WtStreamManager::CloseSession>(
949+
connectStreamCb_.events.back()));
950+
}
951+
924952
TEST_F(H3WtSessionTest, AcquireIngressStream) {
925953
// client-initiated stream ids (server is the local endpoint)
926954
constexpr uint64_t kClientBidiId = 0;

0 commit comments

Comments
 (0)