this repo has no description
at main 187 lines 6.2 kB view raw
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}