/* * Copyright (c) 2024, Dan Klishch * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include namespace HTTP { class Http11Connection; class Http11Response; #define ENUMERATE_METHODS(F) \ F(Invalid) \ F(HEAD) \ F(GET) \ F(POST) \ F(DELETE) \ F(PATCH) \ F(OPTIONS) \ F(TRACE) \ F(CONNECT) \ F(PUT) enum class Method { #define ID(x) x, ENUMERATE_METHODS(ID) #undef ID }; struct Header { ByteString header; ByteString value; }; struct RequestData { struct PlainBody { StringView data; }; Method method; StringView url; Vector
headers; Variant body = Empty {}; }; class Http11Response final : public StreamWrapper { public: static Coroutine>> create(Badge, RequestData&& data, AsyncStream& stream); u16 status_code() const { return m_status_code; } Vector
const& headers() const { return m_headers; } AsyncInputStream& body() { return *m_stream; } private: Http11Response(NonnullOwnPtr&& body, u16 status_code, Vector
&& headers) : StreamWrapper(move(body)) , m_status_code(status_code) , m_headers(move(headers)) { } u16 m_status_code { 0 }; Vector
m_headers; }; class Http11Connection final : public StreamWrapper { public: using StreamWrapper::StreamWrapper; template< typename Func, typename T = InvokeResult::ReturnType::ResultType> Coroutine> request(RequestData&& data, Func&& func) { VERIFY(!m_request_in_flight); TemporaryChange request_in_flight { m_request_in_flight, true }; auto response = CO_TRY(co_await Http11Response::create({}, move(data), *m_stream)); auto result = co_await func(*response); if (response->is_open()) { auto close_result = co_await response->close(); if (!result.is_error()) // Preserve callback error in case it has failed. CO_TRY(move(close_result)); } co_return result; } private: bool m_request_in_flight { false }; }; }