summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2020-02-09 11:27:36 +0100
committerAndreas Kling <kling@serenityos.org>2020-02-09 14:15:55 +0100
commit3a7e49fe0760552338f2c644d72b467f10f840bc (patch)
treee137a183f709f87a7ea555ede2af2b37cdadd7ca
parenta189285658d2988a44b589b097395b024dba5a32 (diff)
downloadserenity-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.cpp106
-rw-r--r--Libraries/LibCore/HttpRequest.h13
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;
};
}