Skip to content

Commit

Permalink
Sample WT handler with new API
Browse files Browse the repository at this point in the history
Summary: Sample code for WebTransport implementing a devious baton endpoint

Reviewed By: lnicco

Differential Revision: D44279876

fbshipit-source-id: 22f851d221bbadb0a2287c7aec5712e6e0e305cf
  • Loading branch information
afrind authored and facebook-github-bot committed Nov 2, 2023
1 parent 569e818 commit 75a66db
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 0 deletions.
1 change: 1 addition & 0 deletions proxygen/httpserver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ if (BUILD_SAMPLES)
proxygencurl
proxygenhttpserver
proxygenhqloggerhelper
proxygendeviousbaton
mvfst::mvfst_transport
mvfst::mvfst_client
mvfst::mvfst_server
Expand Down
1 change: 1 addition & 0 deletions proxygen/httpserver/samples/hq/HQCommandLine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ void initializeTransportSettings(HQToolParams& hqUberParams) {
{.maxReceiveTimestampsPerAck = FLAGS_max_ack_receive_timestamps_to_send,
.receiveTimestampsExponent = kDefaultReceiveTimestampsExponent});
}
hqParams.transportSettings.datagramConfig.enabled = true;
} // initializeTransportSettings

void initializeHttpServerSettings(HQToolServerParams& hqParams) {
Expand Down
5 changes: 5 additions & 0 deletions proxygen/httpserver/samples/hq/HQServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@ class QuicAcceptCB : public quic::QuicSocket::ConnectionSetupCallback {
quicSocket->setConnectionSetupCallback(session);
quicSocket->setConnectionCallback(session);
session->setSocket(std::move(quicSocket));
session->setEgressSettings(
{{proxygen::SettingsId::ENABLE_CONNECT_PROTOCOL, 1},
{proxygen::SettingsId::_HQ_DATAGRAM_DRAFT_8, 1},
{proxygen::SettingsId::ENABLE_WEBTRANSPORT, 1}});

session->startNow();
session->onTransportReady();
Expand Down Expand Up @@ -248,6 +252,7 @@ HQServer::HQServer(
server_->setBindV6Only(false);
server_->setCongestionControllerFactory(
std::make_shared<ServerCongestionControllerFactory>());
params_.transportSettings.datagramConfig.enabled = true;
server_->setTransportSettings(params_.transportSettings);

server_->setQuicServerTransportFactory(
Expand Down
163 changes: 163 additions & 0 deletions proxygen/httpserver/samples/hq/SampleHandlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/split.hpp>
#include <proxygen/lib/utils/Logging.h>
#include <string>

namespace {
Expand Down Expand Up @@ -59,6 +60,11 @@ HTTPTransactionHandler* Dispatcher::getRequestHandler(HTTPMessage* msg) {
return new ServerPushHandler(params_);
}

if (boost::algorithm::starts_with(path, "/webtransport/devious-baton")) {
return new DeviousBatonHandler(
params_, folly::EventBaseManager::get()->getEventBase());
}

if (!FLAGS_static_root.empty()) {
return new StaticFileHandler(params_, FLAGS_static_root);
}
Expand Down Expand Up @@ -287,4 +293,161 @@ void ServerPushHandler::onEOM() noexcept {
void ServerPushHandler::onError(const proxygen::HTTPException& error) noexcept {
VLOG(10) << "ServerPushHandler::onError error=" << error.what();
}

void DeviousBatonHandler::onHeadersComplete(
std::unique_ptr<proxygen::HTTPMessage> msg) noexcept {
VLOG(10) << "WebtransHandler::" << __func__;
msg->dumpMessage(2);

if (msg->getMethod() != proxygen::HTTPMethod::CONNECT) {
LOG(ERROR) << "Method not supported";
proxygen::HTTPMessage resp;
resp.setVersionString(getHttpVersion());
resp.setStatusCode(400);
resp.setStatusMessage("ERROR");
resp.setWantsKeepalive(false);
txn_->sendHeaders(resp);
txn_->sendEOM();
txn_ = nullptr;
return;
}

VLOG(2) << "Received CONNECT request for " << msg->getPathAsStringPiece()
<< " at: "
<< std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::steady_clock::now().time_since_epoch())
.count();

auto status = 500;
auto wt = txn_->getWebTransport();
if (wt) {
devious_.emplace(wt,
devious::DeviousBaton::Mode::SERVER,
[this](WebTransport::StreamReadHandle* readHandle) {
readHandle->awaitNextRead(
evb_, [this](auto readHandle, auto streamData) {
readHandler(readHandle, std::move(streamData));
});
});
auto respCode = devious_->onRequest(*msg);
if (!respCode) {
status = respCode.error();
} else {
status = 200;
}
}

// Send the response to the original get request
proxygen::HTTPMessage resp;
resp.setVersionString(getHttpVersion());
resp.setStatusCode(status);
resp.setIsChunked(true);
if (status / 100 == 2) {
resp.getHeaders().add("sec-webtransport-http3-draft", "draft02");
resp.setWantsKeepalive(true);
} else {
resp.setWantsKeepalive(false);
devious_.reset();
}
resp.dumpMessage(4);
txn_->sendHeaders(resp);

if (devious_) {
devious_->start();
} else {
txn_->sendEOM();
txn_ = nullptr;
}
}

void DeviousBatonHandler::readHandler(
WebTransport::StreamReadHandle* readHandle,
folly::Try<WebTransport::StreamData> streamData) {
if (streamData.hasException()) {
VLOG(4) << "read error=" << streamData.exception().what();
} else {
VLOG(4) << "read data id=" << readHandle->getID();
devious_->onStreamData(readHandle->getID(),
streams_[readHandle->getID()],
std::move(streamData->data),
streamData->fin);
if (!streamData->fin) {
readHandle->awaitNextRead(evb_, [this](auto readHandle, auto streamData) {
readHandler(readHandle, std::move(streamData));
});
}
}
}

void DeviousBatonHandler::onWebTransportBidiStream(
HTTPCodec::StreamID id, WebTransport::BidiStreamHandle stream) noexcept {
VLOG(4) << "New Bidi Stream=" << id;
stream.readHandle->awaitNextRead(
evb_, [this](auto readHandle, auto streamData) {
readHandler(readHandle, std::move(streamData));
});
}

void DeviousBatonHandler::onWebTransportUniStream(
HTTPCodec::StreamID id,
WebTransport::StreamReadHandle* readHandle) noexcept {
VLOG(4) << "New Uni Stream=" << id;
readHandle->awaitNextRead(evb_, [this](auto readHandle, auto streamData) {
readHandler(readHandle, std::move(streamData));
});
}

void DeviousBatonHandler::onWebTransportSessionClose(
folly::Optional<uint32_t> error) noexcept {
VLOG(4) << "Session Close error="
<< (error ? folly::to<std::string>(*error) : std::string("none"));
}

void DeviousBatonHandler::onDatagram(
std::unique_ptr<folly::IOBuf> datagram) noexcept {
VLOG(4) << "DeviousBatonHandler::" << __func__;
}

void DeviousBatonHandler::onBody(std::unique_ptr<folly::IOBuf> body) noexcept {
VLOG(4) << "DeviousBatonHandler::" << __func__;
VLOG(3) << IOBufPrinter::printHexFolly(body.get(), true);
folly::io::Cursor cursor(body.get());

// parse capsules
auto leftToParse = body->computeChainDataLength();
while (leftToParse > 0) {
auto typeRes = quic::decodeQuicInteger(cursor, leftToParse);
if (!typeRes) {
VLOG(2) << "Failed to decode capsule type l=" << leftToParse;
return;
}
leftToParse -= typeRes->first;
auto capLength = quic::decodeQuicInteger(cursor, leftToParse);
if (!capLength) {
VLOG(2) << "Failed to decode capsule length";
return;
}
leftToParse -= capLength->first;
if (capLength->second > leftToParse) {
VLOG(2) << "Derp";
return;
}
VLOG(2) << "Parsed Capsule t=" << typeRes->second
<< " l=" << capLength->second;
cursor.skipAtMost(capLength->second);
leftToParse -= capLength->second;
}
}

void DeviousBatonHandler::onEOM() noexcept {
VLOG(4) << "DeviousBatonHandler::" << __func__;
if (txn_ && !txn_->isEgressEOMSeen()) {
txn_->sendEOM();
}
}

void DeviousBatonHandler::onError(
const proxygen::HTTPException& error) noexcept {
VLOG(4) << "DeviousBatonHandler::onError error=" << error.what();
}
} // namespace quic::samples
39 changes: 39 additions & 0 deletions proxygen/httpserver/samples/hq/SampleHandlers.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include <folly/io/async/AsyncTimeout.h>
#include <folly/io/async/EventBaseManager.h>
#include <proxygen/httpserver/samples/hq/HQServer.h>
#include <proxygen/httpserver/samples/hq/devious/DeviousBaton.h>
#include <proxygen/lib/http/session/HTTPTransaction.h>

namespace quic::samples {
Expand Down Expand Up @@ -780,6 +781,44 @@ class ServerPushHandler : public BaseSampleHandler {
ServerPushTxnHandler pushTxnHandler_;
};

class DeviousBatonHandler : public BaseSampleHandler {
public:
explicit DeviousBatonHandler(const HandlerParams& params,
folly::EventBase* evb)
: BaseSampleHandler(params), evb_(evb) {
}

void onHeadersComplete(
std::unique_ptr<proxygen::HTTPMessage> /* msg */) noexcept override;

void onWebTransportBidiStream(
proxygen::HTTPCodec::StreamID id,
proxygen::WebTransport::BidiStreamHandle stream) noexcept override;
void onWebTransportUniStream(
proxygen::HTTPCodec::StreamID id,
proxygen::WebTransport::StreamReadHandle* readHandle) noexcept override;

void onWebTransportSessionClose(
folly::Optional<uint32_t> error) noexcept override;

void onDatagram(std::unique_ptr<folly::IOBuf> datagram) noexcept override;

void onBody(std::unique_ptr<folly::IOBuf> /* chain */) noexcept override;

void onEOM() noexcept override;

void onError(const proxygen::HTTPException& /*error*/) noexcept override;

void detachTransaction() noexcept override {
}

folly::Optional<devious::DeviousBaton> devious_;
void readHandler(proxygen::WebTransport::StreamReadHandle* readHandle,
folly::Try<proxygen::WebTransport::StreamData> streamData);
folly::EventBase* evb_{nullptr};
std::map<uint64_t, devious::DeviousBaton::BatonMessageState> streams_;
};

class StaticFileHandler : public BaseSampleHandler {
public:
StaticFileHandler(const HandlerParams& params, std::string staticRoot)
Expand Down

0 comments on commit 75a66db

Please sign in to comment.