1- //
2- // Created by lpcvoid on 25.12.22.
3- //
1+ #pragma once
42
5- #ifndef NETLIB_HTTP_HPP
6- #define NETLIB_HTTP_HPP
3+ #include < string>
4+ #include < system_error>
5+ #include < utility>
6+ #include < vector>
7+ namespace netlib ::http {
78
8- #endif // NETLIB_HTTP_HPP
9+ using http_header_entry = std::pair<std::string, std::string>;
10+ using http_headers = std::vector<http_header_entry>;
11+
12+ struct http_response {
13+ http_headers headers;
14+ uint32_t response_code;
15+ std::pair<uint32_t , uint32_t > version;
16+ std::string body;
17+ inline std::error_condition from_raw_response (const std::string& raw_response) {
18+ // a very rudimentary http response parser
19+ if (raw_response.empty ()) {
20+ return std::errc::no_message;
21+ }
22+
23+ /* strategy:
24+ * split into multiple (at least two) parts, delimited by \r\n\r\n"
25+ * within the first part:
26+ * first, parse the status line
27+ * second, parse the header fields, until we arrive at an empty line (only CR LF)
28+ * last, an optional body
29+ * then, for the rest of the parts, concat into body
30+ */
31+
32+ auto split = [](const std::string& str, const std::string& delimiter) -> std::vector<std::string> {
33+ std::vector<std::string> split_tokens;
34+ std::size_t start;
35+ std::size_t end = 0 ;
36+ while ((start = str.find_first_not_of (delimiter, end)) != std::string::npos)
37+ {
38+ end = str.find (delimiter, start);
39+ split_tokens.push_back (str.substr (start, end - start));
40+ }
41+ return split_tokens;
42+ };
43+
44+ std::vector<std::string> header_body_split = split (raw_response, " \r\n\r\n " );
45+ // split header part of response into response_header_lines
46+ std::vector<std::string> response_header_lines = split (header_body_split.front (), " \r\n " );
47+ // first line should start with "HTTP"
48+ if (!response_header_lines.front ().starts_with (" HTTP" )) {
49+ return std::errc::result_out_of_range;
50+ }
51+ // attempt to parse status line
52+ // split into parts by space
53+ auto status_parts = split (response_header_lines.front (), " " );
54+ if (status_parts.size () < 3 ) {
55+ return std::errc::bad_message;
56+ }
57+ // parse "HTTP/x.x"
58+ auto version_parts = split (status_parts.front (), " /" );
59+ if (version_parts.size () != 2 ) {
60+ return std::errc::bad_message;
61+ }
62+ // parse "x.x"
63+ auto version_components = split (version_parts.back (), " ." );
64+ version.first = std::stoi (version_components.front ());
65+ version.second = std::stoi (version_components.back ());
66+ // parse response code
67+ response_code = std::stoi (status_parts[1 ]);
68+ // there can be an optional code description in the first line, but we ignore that here
69+ // parse the response header lines until the end
70+ // start at second line, first is status
71+ std::for_each (response_header_lines.begin (), response_header_lines.end (), [&](const std::string& header_component){
72+ auto component_parts = split (header_component, " :" );
73+ if (component_parts.size () == 2 ) {
74+ headers.emplace_back (component_parts.front (), component_parts.back ());
75+ }
76+ });
77+
78+ // now, take the body part(s) and concat them
79+ std::for_each (header_body_split.begin () + 1 , header_body_split.end (), [&](const std::string& body_line){
80+ body += body_line;
81+ });
82+
83+ return {};
84+
85+ };
86+ };
87+
88+ }
0 commit comments