1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 15:17:36 +00:00

LibHTTP+WebDriver+WebServer: Return error from HTTP request parser

This commit is contained in:
Aliaksandr Kalenik 2023-03-23 02:52:06 +03:00 committed by Andreas Kling
parent 5b31d1208f
commit 9220cdc285
6 changed files with 35 additions and 13 deletions

View file

@ -11,7 +11,7 @@
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
{ {
auto request_wrapper = HTTP::HttpRequest::from_raw_request(ReadonlyBytes { data, size }); auto request_wrapper = HTTP::HttpRequest::from_raw_request(ReadonlyBytes { data, size });
if (!request_wrapper.has_value()) if (!request_wrapper.is_error())
return 0; return 0;
auto& request = request_wrapper.value(); auto& request = request_wrapper.value();

View file

@ -75,7 +75,7 @@ ErrorOr<ByteBuffer> HttpRequest::to_raw_request() const
return builder.to_byte_buffer(); return builder.to_byte_buffer();
} }
Optional<HttpRequest> HttpRequest::from_raw_request(ReadonlyBytes raw_request) ErrorOr<HttpRequest, HttpRequest::ParseError> HttpRequest::from_raw_request(ReadonlyBytes raw_request)
{ {
enum class State { enum class State {
InMethod, InMethod,
@ -118,7 +118,7 @@ Optional<HttpRequest> HttpRequest::from_raw_request(ReadonlyBytes raw_request)
while (index < raw_request.size()) { while (index < raw_request.size()) {
// FIXME: Figure out what the appropriate limitations should be. // FIXME: Figure out what the appropriate limitations should be.
if (buffer.size() > 65536) if (buffer.size() > 65536)
return {}; return ParseError::RequestTooLarge;
switch (state) { switch (state) {
case State::InMethod: case State::InMethod:
if (peek() == ' ') { if (peek() == ' ') {
@ -178,9 +178,10 @@ Optional<HttpRequest> HttpRequest::from_raw_request(ReadonlyBytes raw_request)
if (index == raw_request.size()) { if (index == raw_request.size()) {
// End of data, so store the body // End of data, so store the body
auto maybe_body = ByteBuffer::copy(buffer); auto maybe_body = ByteBuffer::copy(buffer);
// FIXME: Propagate this error somehow. if (maybe_body.is_error()) {
if (maybe_body.is_error()) VERIFY(maybe_body.error().code() == ENOMEM);
return {}; return ParseError::OutOfMemory;
}
body = maybe_body.release_value(); body = maybe_body.release_value();
buffer.clear(); buffer.clear();
} }
@ -208,7 +209,7 @@ Optional<HttpRequest> HttpRequest::from_raw_request(ReadonlyBytes raw_request)
else if (method == "PUT") else if (method == "PUT")
request.set_method(HTTP::HttpRequest::Method::PUT); request.set_method(HTTP::HttpRequest::Method::PUT);
else else
return {}; return ParseError::UnsupportedMethod;
request.m_headers = move(headers); request.m_headers = move(headers);
auto url_parts = resource.split_limit('?', 2, SplitBehavior::KeepEmpty); auto url_parts = resource.split_limit('?', 2, SplitBehavior::KeepEmpty);

View file

@ -18,6 +18,26 @@ namespace HTTP {
class HttpRequest { class HttpRequest {
public: public:
enum class ParseError {
RequestTooLarge,
OutOfMemory,
UnsupportedMethod
};
static StringView parse_error_to_string(ParseError error)
{
switch (error) {
case ParseError::RequestTooLarge:
return "Request too large"sv;
case ParseError::OutOfMemory:
return "Out of memory"sv;
case ParseError::UnsupportedMethod:
return "Unsupported method"sv;
default:
VERIFY_NOT_REACHED();
}
}
enum Method { enum Method {
Invalid, Invalid,
HEAD, HEAD,
@ -61,7 +81,7 @@ public:
void set_headers(HashMap<DeprecatedString, DeprecatedString> const&); void set_headers(HashMap<DeprecatedString, DeprecatedString> const&);
static Optional<HttpRequest> from_raw_request(ReadonlyBytes); static ErrorOr<HttpRequest, HttpRequest::ParseError> from_raw_request(ReadonlyBytes);
static Optional<Header> get_http_basic_authentication_header(URL const&); static Optional<Header> get_http_basic_authentication_header(URL const&);
static Optional<BasicAuthenticationCredentials> parse_http_basic_authentication_header(DeprecatedString const&); static Optional<BasicAuthenticationCredentials> parse_http_basic_authentication_header(DeprecatedString const&);

View file

@ -182,6 +182,9 @@ Client::Client(NonnullOwnPtr<Core::BufferedTCPSocket> socket, Core::Object* pare
[](AK::Error const& error) { [](AK::Error const& error) {
warnln("Internal error: {}", error); warnln("Internal error: {}", error);
}, },
[](HTTP::HttpRequest::ParseError const& error) {
warnln("HTTP request parsing error: {}", HTTP::HttpRequest::parse_error_to_string(error));
},
[this](WebDriver::Error const& error) { [this](WebDriver::Error const& error) {
if (send_error_response(error).is_error()) if (send_error_response(error).is_error())
warnln("Could not send error response"); warnln("Could not send error response");
@ -221,9 +224,7 @@ ErrorOr<void, Client::WrappedError> Client::on_ready_to_read()
break; break;
} }
m_request = HTTP::HttpRequest::from_raw_request(TRY(builder.to_byte_buffer())); m_request = TRY(HTTP::HttpRequest::from_raw_request(TRY(builder.to_byte_buffer())));
if (!m_request.has_value())
return {};
auto body = TRY(read_body_as_json()); auto body = TRY(read_body_as_json());
TRY(handle_request(move(body))); TRY(handle_request(move(body)));

View file

@ -109,7 +109,7 @@ protected:
Client(NonnullOwnPtr<Core::BufferedTCPSocket>, Core::Object* parent); Client(NonnullOwnPtr<Core::BufferedTCPSocket>, Core::Object* parent);
private: private:
using WrappedError = Variant<AK::Error, WebDriver::Error>; using WrappedError = Variant<AK::Error, HTTP::HttpRequest::ParseError, WebDriver::Error>;
void die(); void die();
ErrorOr<void, WrappedError> on_ready_to_read(); ErrorOr<void, WrappedError> on_ready_to_read();

View file

@ -97,7 +97,7 @@ void Client::start()
ErrorOr<bool> Client::handle_request(ReadonlyBytes raw_request) ErrorOr<bool> Client::handle_request(ReadonlyBytes raw_request)
{ {
auto request_or_error = HTTP::HttpRequest::from_raw_request(raw_request); auto request_or_error = HTTP::HttpRequest::from_raw_request(raw_request);
if (!request_or_error.has_value()) if (request_or_error.is_error())
return false; return false;
auto& request = request_or_error.value(); auto& request = request_or_error.value();
auto resource_decoded = URL::percent_decode(request.resource()); auto resource_decoded = URL::percent_decode(request.resource());