this repo has no description
1// Project Includes
2#include "http.hpp"
3// Standard Library Includes
4#include <bit>
5#include <expected>
6#include <format>
7#include <iostream>
8#include <print>
9#include <string>
10#include <vector>
11
12HttpRequestParser::HttpRequestParser()
13{
14 llhttp_settings_init(&m_settings);
15
16 /*1*/ m_settings.on_message_complete = HttpRequestParser::on_message_complete;
17 /*2*/ m_settings.on_url = HttpRequestParser::on_url;
18 /*3*/ m_settings.on_method = HttpRequestParser::on_method;
19 /*4*/ m_settings.on_version = HttpRequestParser::on_version;
20 /*5*/ m_settings.on_header_field = HttpRequestParser::on_header_field;
21 /*6*/ m_settings.on_header_value = HttpRequestParser::on_header_value;
22 /*7*/ m_settings.on_body = HttpRequestParser::on_body;
23
24 llhttp_init(&m_parser, HTTP_REQUEST, &m_settings);
25 m_parser.data = this;
26 m_state = State::Idle;
27}
28
29void HttpRequestParser::reset()
30{
31 llhttp_reset(&m_parser);
32 m_parser.data = this;
33 m_completed_requests.clear();
34 m_current_request = HttpRequest{};
35 m_current_header_field.clear();
36 m_current_header_value.clear();
37 m_header_state = HeaderState::None;
38 m_state = State::Idle;
39}
40
41std::expected<void, std::string> HttpRequestParser::parse(std::span<const std::byte> data)
42{
43 if (m_state == State::Error)
44 {
45 std::println(std::cerr, "Parser in error state, resetting");
46 reset();
47 }
48
49 if (data.empty())
50 {
51 if (!m_completed_requests.empty())
52 {
53 m_state = State::Completed;
54 }
55 return {};
56 }
57
58 auto data_ptr = std::bit_cast<const char *>(data.data());
59 auto data_len = data.size();
60
61 llhttp_errno_t status = llhttp_execute(&m_parser, data_ptr, data_len);
62 if (status != HPE_OK && status != HPE_PAUSED && status != HPE_PAUSED_UPGRADE)
63 {
64 const char *reason = llhttp_get_error_reason(&m_parser);
65 std::string error_message = reason ? reason : "Unknown parsing error";
66 reset();
67 m_state = State::Error;
68 return std::unexpected(error_message);
69 }
70
71 if (!m_completed_requests.empty())
72 {
73 m_state = State::Completed;
74 return {};
75 }
76
77 m_state = State::Parsing;
78 return {};
79}
80
81HttpRequestParser &HttpRequestParser::from_llhttp(llhttp_t *parser)
82{
83 return *static_cast<HttpRequestParser *>(parser->data);
84}
85
86void HttpRequestParser::commit_pending_header()
87{
88 if (m_header_state == HeaderState::Value && !m_current_header_field.empty())
89 {
90 m_current_request.headers.emplace_back(std::move(m_current_header_field), std::move(m_current_header_value));
91 m_current_header_field.clear();
92 m_current_header_value.clear();
93 m_header_state = HeaderState::None;
94 }
95}
96
97int HttpRequestParser::on_message_complete(llhttp_t *parser)
98{
99 auto &self = from_llhttp(parser);
100 self.commit_pending_header();
101 self.m_current_request.http_major = parser->http_major;
102 self.m_current_request.http_minor = parser->http_minor;
103 self.m_completed_requests.emplace_back(std::move(self.m_current_request));
104 self.m_current_request = HttpRequest{};
105 self.m_current_header_field.clear();
106 self.m_current_header_value.clear();
107 self.m_header_state = HeaderState::None;
108 self.m_state = State::Completed;
109 return HPE_OK;
110}
111
112int HttpRequestParser::on_url(llhttp_t *parser, const char *ptr, size_t length)
113{
114 auto &self = from_llhttp(parser);
115 self.m_current_request.url.append(ptr, length);
116 return HPE_OK;
117}
118
119int HttpRequestParser::on_method(llhttp_t *parser, const char *ptr, size_t length)
120{
121 auto &self = from_llhttp(parser);
122 self.m_current_request.method.append(ptr, length);
123 return HPE_OK;
124}
125
126int HttpRequestParser::on_version(llhttp_t *parser, const char *ptr, size_t length)
127{
128 static_cast<void>(ptr);
129 static_cast<void>(length);
130
131 auto &self = from_llhttp(parser);
132 self.m_current_request.http_major = parser->http_major;
133 self.m_current_request.http_minor = parser->http_minor;
134 return HPE_OK;
135}
136
137int HttpRequestParser::on_header_field(llhttp_t *parser, const char *ptr, size_t length)
138{
139 auto &self = from_llhttp(parser);
140 if (self.m_header_state == HeaderState::Value)
141 {
142 self.commit_pending_header();
143 }
144 self.m_current_header_field.append(ptr, length);
145 self.m_header_state = HeaderState::Field;
146 return HPE_OK;
147}
148
149int HttpRequestParser::on_header_value(llhttp_t *parser, const char *ptr, size_t length)
150{
151 auto &self = from_llhttp(parser);
152 self.m_current_header_value.append(ptr, length);
153 self.m_header_state = HeaderState::Value;
154 return HPE_OK;
155}
156
157int HttpRequestParser::on_body(llhttp_t *parser, const char *ptr, size_t length)
158{
159 auto &self = from_llhttp(parser);
160 self.commit_pending_header();
161 const auto *data_begin = std::bit_cast<const std::byte *>(ptr);
162 self.m_current_request.body.insert(self.m_current_request.body.end(), data_begin, data_begin + length);
163 return HPE_OK;
164}
165
166void HttpResponse::serialize_into(std::vector<std::byte> &out_bytes) const
167{
168 if (out_bytes.capacity() < out_bytes.size() + 128 + body.size())
169 {
170 out_bytes.reserve(out_bytes.size() + 128 + body.size());
171 }
172 std::string status_line = std::format("HTTP/{}.{} {} {}\r\n", http_major, http_minor, status_code, reason_phrase);
173 std::ranges::copy(std::bit_cast<std::byte *>(status_line.data()),
174 std::bit_cast<std::byte *>(status_line.data() + status_line.size()),
175 std::back_inserter(out_bytes));
176 for (const auto &[header_name, header_value] : headers)
177 {
178 std::string header_line = std::format("{}: {}\r\n", header_name, header_value);
179 std::ranges::copy(std::bit_cast<std::byte *>(header_line.data()),
180 std::bit_cast<std::byte *>(header_line.data() + header_line.size()),
181 std::back_inserter(out_bytes));
182 }
183 constexpr std::string_view crlf = "\r\n";
184 std::ranges::copy(std::bit_cast<std::byte *>(crlf.data()), std::bit_cast<std::byte *>(crlf.data() + crlf.size()),
185 std::back_inserter(out_bytes));
186 std::ranges::copy(body, std::back_inserter(out_bytes));
187}