|
8 | 8 |
|
9 | 9 | #include <proxygen/lib/http/session/HTTPUpstreamSession.h> |
10 | 10 |
|
| 11 | +#include <proxygen/lib/http/webtransport/HTTPWebTransport.h> |
| 12 | +#include <proxygen/lib/http/webtransport/WtUtils.h> |
| 13 | + |
11 | 14 | #include <folly/io/async/AsyncSSLSocket.h> |
12 | 15 | #include <wangle/acceptor/ConnectionManager.h> |
13 | 16 |
|
@@ -103,10 +106,7 @@ void HTTPUpstreamSession::startNow() { |
103 | 106 | HTTPTransaction* HTTPUpstreamSession::newTransaction( |
104 | 107 | HTTPTransaction::Handler* handler) { |
105 | 108 | auto txn = newTransactionWithError(handler); |
106 | | - if (txn.hasError()) { |
107 | | - return nullptr; |
108 | | - } |
109 | | - return txn.value(); |
| 109 | + return txn.value_or(nullptr); |
110 | 110 | } |
111 | 111 |
|
112 | 112 | folly::Expected<HTTPTransaction*, HTTPUpstreamSession::NewTransactionError> |
@@ -224,6 +224,109 @@ void HTTPUpstreamSession::detachThreadLocals(bool detachSSLContext) { |
224 | 224 | } |
225 | 225 | } |
226 | 226 |
|
| 227 | +namespace { |
| 228 | + |
| 229 | +constexpr std::string_view kWtNotSupported = "WebTransport not supported"; |
| 230 | +constexpr std::string_view kInvalidWtReq = "Invalid WebTransport request"; |
| 231 | +constexpr std::string_view kStreamFailed = "Failed to create stream"; |
| 232 | + |
| 233 | +folly::exception_wrapper makeHttpEx(const std::string& err) noexcept { |
| 234 | + constexpr auto kExDir = HTTPException::Direction::INGRESS_AND_EGRESS; |
| 235 | + return folly::make_exception_wrapper<HTTPException>(kExDir, err); |
| 236 | +} |
| 237 | + |
| 238 | +using WtReqResult = std::unique_ptr<HTTPMessage>; |
| 239 | +using WtReqResultPromise = folly::Promise<WtReqResult>; |
| 240 | +folly::Promise<WtReqResult> emptyWtReqPromise() noexcept { |
| 241 | + return folly::Promise<WtReqResult>::makeEmpty(); |
| 242 | +} |
| 243 | + |
| 244 | +class WtClientCallback final |
| 245 | + : public HttpWtClientCallbackIf |
| 246 | + , public HTTPTransactionHandler { |
| 247 | + private: |
| 248 | + folly::Promise<WtReqResult> promise{emptyWtReqPromise()}; |
| 249 | + |
| 250 | + public: |
| 251 | + explicit WtClientCallback(WtReqResultPromise p) noexcept |
| 252 | + : promise(std::move(p)) { |
| 253 | + } |
| 254 | + ~WtClientCallback() noexcept override = default; |
| 255 | + folly::Promise<WtReqResult> resetPromise() noexcept { |
| 256 | + return std::exchange(promise, emptyWtReqPromise()); |
| 257 | + } |
| 258 | + /** |
| 259 | + * Either ::onHeaders or ::onErr can be invoked first |
| 260 | + * |
| 261 | + * When ::onHeadersComplete is invoked, resolve the promise with the non-final |
| 262 | + * http headers |
| 263 | + * |
| 264 | + * When ::onError is invoked, resolve the promise with HTTPException |
| 265 | + */ |
| 266 | + void onHeaders(std::unique_ptr<HTTPMessage> msg) noexcept override { |
| 267 | + if (msg->isFinal()) { |
| 268 | + auto p = resetPromise(); |
| 269 | + CHECK(p.valid()); |
| 270 | + p.setValue(std::move(msg)); |
| 271 | + } |
| 272 | + } |
| 273 | + void onErr(const HTTPException& ex) noexcept override { |
| 274 | + auto p = resetPromise(); |
| 275 | + CHECK(p.valid()); |
| 276 | + p.setException(ex); |
| 277 | + } |
| 278 | + |
| 279 | + // **ignored** |
| 280 | + void onHeadersComplete(std::unique_ptr<HTTPMessage>) noexcept override { |
| 281 | + } |
| 282 | + void onError(const HTTPException&) noexcept override { |
| 283 | + } |
| 284 | + void detachTransaction() noexcept override { |
| 285 | + } |
| 286 | + void setTransaction(HTTPTransaction*) noexcept override { |
| 287 | + } |
| 288 | + void onBody(std::unique_ptr<folly::IOBuf>) noexcept override { |
| 289 | + } |
| 290 | + void onTrailers(std::unique_ptr<HTTPHeaders>) noexcept override { |
| 291 | + } |
| 292 | + void onEOM() noexcept override { |
| 293 | + } |
| 294 | + void onUpgrade(UpgradeProtocol) noexcept override { |
| 295 | + } |
| 296 | + void onEgressPaused() noexcept override { |
| 297 | + } |
| 298 | + void onEgressResumed() noexcept override { |
| 299 | + } |
| 300 | +}; |
| 301 | + |
| 302 | +} // namespace |
| 303 | + |
| 304 | +folly::SemiFuture<WtReqResult> HTTPUpstreamSession::sendWebTransportRequest( |
| 305 | + const HTTPMessage& req, WebTransportHandler::Ptr wtHandler) noexcept { |
| 306 | + // both self and peer must indicate support for WebTransport |
| 307 | + const bool supportsWt = proxygen::detail::supportsH2Wt( |
| 308 | + {codec_->getIngressSettings(), codec_->getEgressSettings()}); |
| 309 | + const bool validWtReq = HTTPWebTransport::isConnectMessage(req); |
| 310 | + if (!(supportsWt && validWtReq)) { |
| 311 | + auto err = !validWtReq ? kInvalidWtReq : kWtNotSupported; |
| 312 | + VLOG(6) << __func__ << " err=" << err << "; sess=" << *this; |
| 313 | + return makeHttpEx(std::string(err)); |
| 314 | + } |
| 315 | + |
| 316 | + auto [p, f] = folly::makePromiseContract<WtReqResult>(); |
| 317 | + auto wtClientCb = std::make_unique<WtClientCallback>(std::move(p)); |
| 318 | + |
| 319 | + auto* txn = newTransaction(wtClientCb.get()); |
| 320 | + if (!txn) { |
| 321 | + return makeHttpEx(std::string(kStreamFailed)); |
| 322 | + } |
| 323 | + |
| 324 | + // send wt upgrade req |
| 325 | + txn->setHandler(nullptr); // clear handler, will be replaced by sendWtHeaders |
| 326 | + txn->sendWtHeaders(req, std::move(wtHandler), std::move(wtClientCb)); |
| 327 | + return std::move(f); |
| 328 | +} |
| 329 | + |
227 | 330 | void HTTPUpstreamSession::maybeDetachSSLContext() const { |
228 | 331 | #ifndef NO_ASYNCSSLSOCKET |
229 | 332 | auto sslSocket = sock_->getUnderlyingTransport<folly::AsyncSSLSocket>(); |
|
0 commit comments