-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathHttpParser.hpp
143 lines (121 loc) · 3.77 KB
/
HttpParser.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#pragma once
#include <string>
#include <string_view>
#include <format>
#include <sstream>
#include <span>
#include <cstddef>
#include <map>
#include <charconv>
#include <expected>
#include "ws_client/errors.hpp"
#include "ws_client/utils/string.hpp"
#include "ws_client/HttpHeader.hpp"
namespace ws_client
{
using std::string;
using std::string_view;
class HttpParser
{
public:
static constexpr string_view header_terminator = "\r\n\r\n";
/**
* Parse HTTP status code, protocol version and message from headers.
* Example: "HTTP/1.1 200 OK"
*/
[[nodiscard]] static expected<HttpStatusLine, WSError> parse_request_status_line(
std::istringstream& stream
) noexcept
{
HttpStatusLine result;
string temp_status_code;
if (!(stream >> result.protocol_version >> temp_status_code))
{
return WS_ERROR(
protocol_error,
"Error parsing HTTP protocol version / status code.",
close_code::not_set
);
}
// parse status code as integer
auto [ptr, ec] = std::from_chars(
temp_status_code.data(),
temp_status_code.data() + temp_status_code.size(),
result.status_code
);
if (ec != std::errc())
{
return WS_ERROR(
protocol_error, "Status code is not a valid integer.", close_code::not_set
);
}
// read the remaining part as the reason text, if present
std::getline(stream, result.reason, '\n');
trim(result.reason);
return result;
}
/**
* Parse HTTP header status line and header fields as vector of key-value pairs.
*
* The passed headers string must start with the HTTP status line, followed by
* one or more header lines.
*/
[[nodiscard]] static expected<HttpHeaderFields, WSError> parse_header_fields(
std::istringstream& stream
) noexcept
{
HttpHeaderFields result;
string line;
while (std::getline(stream, line))
{
trim(line);
// break on empty line (header terminator)
if (line.empty())
break;
auto colon_pos = line.find(':');
if (colon_pos == std::string::npos)
{
return WS_ERROR(
protocol_error,
std::format("Malformed HTTP header line: {}", line),
close_code::not_set
);
}
string header_name = line.substr(0, colon_pos);
string header_value = colon_pos < line.size() - 1 ? line.substr(colon_pos + 1) : "";
// trim whitespace from header value
trim(header_value);
if (header_name.empty())
{
return WS_ERROR(
protocol_error,
std::format("Malformed HTTP header line: {}", line),
close_code::not_set
);
}
result.add(header_name, header_value);
}
return result;
}
/**
* Parse HTTP header status line and header fields as vector of key-value pairs.
*
* The passed headers string must start with the HTTP status line, followed by
* one or more header lines.
*/
[[nodiscard]] static expected<HttpHeaderFields, WSError> parse_header_fields(
const string& stream
)
{
std::istringstream ss(stream);
return parse_header_fields(ss);
}
[[nodiscard]] static expected<HttpStatusLine, WSError> parse_request_status_line(
const string& stream
) noexcept
{
std::istringstream ss(stream);
return parse_request_status_line(ss);
}
};
} // namespace ws_client