mirror of
https://github.com/SerenityOS/serenity
synced 2026-05-11 09:26:28 +02:00
There are three compiler bugs that influence this decision: - Clang writing to (validly) destroyed coroutine frame with -O0 and -fsanitize=null,address under some conditions (https://godbolt.org/z/17Efq5Ma5) (AK_COROUTINE_DESTRUCTION_BROKEN); - GCC being unable to handle statement expressions in coroutines (AK_COROUTINE_STATEMENT_EXPRS_BROKEN); - GCC being unable to deduce template type parameter for TryAwaiter with nested CO_TRYs (AK_COROUTINE_TYPE_DEDUCTION_BROKEN). Instead of growing an ifdef soup in AK/Coroutine.h and LibTest/AsyncTestCase.h, define three macros in AK/Platform.h that correspond to these bugs and use them accordingly in the said files.
103 lines
2.5 KiB
C++
103 lines
2.5 KiB
C++
/*
|
|
* Copyright (c) 2024, Dan Klishch <danilklishch@gmail.com>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <AK/AsyncStream.h>
|
|
#include <AK/ByteString.h>
|
|
#include <AK/NonnullOwnPtr.h>
|
|
#include <AK/OwnPtr.h>
|
|
#include <AK/TemporaryChange.h>
|
|
#include <AK/Vector.h>
|
|
|
|
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<Header> headers;
|
|
Variant<Empty, PlainBody> body = Empty {};
|
|
};
|
|
|
|
class Http11Response final : public StreamWrapper<AsyncInputStream> {
|
|
public:
|
|
static Coroutine<ErrorOr<NonnullOwnPtr<Http11Response>>> create(Badge<Http11Connection>, RequestData&& data, AsyncStream& stream);
|
|
|
|
u16 status_code() const { return m_status_code; }
|
|
Vector<Header> const& headers() const { return m_headers; }
|
|
|
|
AsyncInputStream& body() { return *m_stream; }
|
|
|
|
private:
|
|
Http11Response(NonnullOwnPtr<AsyncInputStream>&& body, u16 status_code, Vector<Header>&& headers)
|
|
: StreamWrapper(move(body))
|
|
, m_status_code(status_code)
|
|
, m_headers(move(headers))
|
|
{
|
|
}
|
|
|
|
u16 m_status_code { 0 };
|
|
Vector<Header> m_headers;
|
|
};
|
|
|
|
class Http11Connection final : public StreamWrapper<AsyncStream> {
|
|
public:
|
|
using StreamWrapper::StreamWrapper;
|
|
|
|
template<
|
|
typename Func,
|
|
typename T = InvokeResult<Func, Http11Response&>::ReturnType::ResultType>
|
|
Coroutine<ErrorOr<T>> 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 };
|
|
};
|
|
|
|
}
|