diff options
author | Andreas Kling <kling@serenityos.org> | 2020-02-09 11:27:36 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-02-09 14:15:55 +0100 |
commit | 3a7e49fe0760552338f2c644d72b467f10f840bc (patch) | |
tree | e137a183f709f87a7ea555ede2af2b37cdadd7ca | |
parent | a189285658d2988a44b589b097395b024dba5a32 (diff) | |
download | serenity-3a7e49fe0760552338f2c644d72b467f10f840bc.zip |
LibCore: Allow constructing a Core::HttpRequest from a raw HTTP request
This patch adds a very simple HTTP request parser that can be useful
for implementing say.. a web server. :^)
-rw-r--r-- | Libraries/LibCore/HttpRequest.cpp | 106 | ||||
-rw-r--r-- | Libraries/LibCore/HttpRequest.h | 13 |
2 files changed, 119 insertions, 0 deletions
diff --git a/Libraries/LibCore/HttpRequest.cpp b/Libraries/LibCore/HttpRequest.cpp index 006fea5be2..c1209b0ea5 100644 --- a/Libraries/LibCore/HttpRequest.cpp +++ b/Libraries/LibCore/HttpRequest.cpp @@ -71,4 +71,110 @@ ByteBuffer HttpRequest::to_raw_request() const return builder.to_byte_buffer(); } +Optional<HttpRequest> HttpRequest::from_raw_request(const ByteBuffer& raw_request) +{ + enum class State { + InMethod, + InResource, + InProtocol, + InHeaderName, + InHeaderValue, + }; + + State state { State::InMethod }; + int index = 0; + + auto peek = [&](int offset = 0) -> u8 { + if (index + offset >= raw_request.size()) + return 0; + return raw_request[index + offset]; + }; + + auto consume = [&]() -> u8 { + ASSERT(index < raw_request.size()); + return raw_request[index++]; + }; + + Vector<u8, 256> buffer; + + String method; + String resource; + String protocol; + Vector<Header> headers; + Header current_header; + + auto commit_and_advance_to = [&](auto& output, State new_state) { + output = String::copy(buffer); + buffer.clear(); + state = new_state; + }; + + while (index < raw_request.size()) { + // FIXME: Figure out what the appropriate limitations should be. + if (buffer.size() > 65536) + return {}; + switch (state) { + case State::InMethod: + if (peek() == ' ') { + consume(); + commit_and_advance_to(method, State::InResource); + break; + } + buffer.append(consume()); + break; + case State::InResource: + if (peek() == ' ') { + consume(); + commit_and_advance_to(resource, State::InProtocol); + break; + } + buffer.append(consume()); + break; + case State::InProtocol: + if (peek(0) == '\r' && peek(1) == '\n') { + consume(); + consume(); + commit_and_advance_to(protocol, State::InHeaderName); + break; + } + buffer.append(consume()); + break; + case State::InHeaderName: + if (peek(0) == ':' && peek(1) == ' ') { + consume(); + consume(); + commit_and_advance_to(current_header.name, State::InHeaderValue); + break; + } + buffer.append(consume()); + break; + case State::InHeaderValue: + if (peek(0) == '\r' && peek(1) == '\n') { + consume(); + consume(); + commit_and_advance_to(current_header.value, State::InHeaderName); + headers.append(move(current_header)); + break; + } + buffer.append(consume()); + break; + } + } + + HttpRequest request; + if (method == "GET") + request.m_method = Method::GET; + else if (method == "HEAD") + request.m_method = Method::HEAD; + else if (method == "POST") + request.m_method = Method::POST; + else + return {}; + + request.m_resource = resource; + request.m_headers = move(headers); + + return request; +} + } diff --git a/Libraries/LibCore/HttpRequest.h b/Libraries/LibCore/HttpRequest.h index fe4feafd14..b707ae22ef 100644 --- a/Libraries/LibCore/HttpRequest.h +++ b/Libraries/LibCore/HttpRequest.h @@ -26,6 +26,7 @@ #pragma once +#include <AK/Optional.h> #include <AK/String.h> #include <AK/URL.h> @@ -42,9 +43,17 @@ public: POST }; + struct Header { + String name; + String value; + }; + HttpRequest(); ~HttpRequest(); + const String& resource() const { return m_resource; } + const Vector<Header>& headers() const { return m_headers; } + const URL& url() const { return m_url; } void set_url(const URL& url) { m_url = url; } @@ -56,9 +65,13 @@ public: RefPtr<NetworkJob> schedule(); + static Optional<HttpRequest> from_raw_request(const ByteBuffer&); + private: URL m_url; + String m_resource; Method m_method { GET }; + Vector<Header> m_headers; }; } |