Skip to content

Commit 4432902

Browse files
authored
Merge pull request #7 from copyleftdev/feature/enhances
Auto-merge PR #7: added improvments to code
2 parents 85f38aa + 0845ccb commit 4432902

File tree

1 file changed

+121
-34
lines changed

1 file changed

+121
-34
lines changed

parser.cpp

Lines changed: 121 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,143 @@
11
#include <string>
2+
#include <string_view>
23
#include <unordered_map>
3-
#include <sstream>
4-
#include <vector>
5-
6-
struct HttpRequest {
7-
std::string method;
8-
std::string path;
9-
std::string version;
10-
std::unordered_map<std::string, std::string> headers;
11-
std::string body;
4+
#include <optional>
5+
#include <stdexcept>
6+
#include <algorithm>
7+
#include <memory>
8+
9+
class HttpParserError : public std::runtime_error {
10+
using std::runtime_error::runtime_error;
1211
};
1312

14-
class HttpParser {
13+
class HttpRequest final {
14+
public:
15+
using Headers = std::unordered_map<std::string, std::string, std::hash<std::string>, std::equal_to<>>;
16+
17+
private:
18+
std::string method_;
19+
std::string path_;
20+
std::string version_;
21+
Headers headers_;
22+
std::string body_;
23+
1524
public:
16-
HttpRequest parse(const std::string& raw_request) {
25+
// Use string_view for efficient read-only access
26+
[[nodiscard]] std::string_view method() const noexcept { return method_; }
27+
[[nodiscard]] std::string_view path() const noexcept { return path_; }
28+
[[nodiscard]] std::string_view version() const noexcept { return version_; }
29+
[[nodiscard]] const Headers& headers() const noexcept { return headers_; }
30+
[[nodiscard]] std::string_view body() const noexcept { return body_; }
31+
32+
// Friend declaration for the parser
33+
friend class HttpParser;
34+
};
35+
36+
class HttpParser final {
37+
public:
38+
// Use string_view for efficient parsing without copying
39+
[[nodiscard]] std::optional<HttpRequest> parse(std::string_view raw_request) const {
40+
try {
41+
return parse_impl(raw_request);
42+
} catch (const std::exception&) {
43+
return std::nullopt;
44+
}
45+
}
46+
47+
private:
48+
[[nodiscard]] HttpRequest parse_impl(std::string_view raw_request) const {
1749
HttpRequest request;
18-
std::istringstream stream(raw_request);
19-
std::string line;
50+
size_t pos = 0;
51+
size_t end_pos;
2052

21-
if (std::getline(stream, line)) {
22-
parseRequestLine(line, request);
53+
// Parse request line
54+
if ((end_pos = raw_request.find("\r\n", pos)) == std::string_view::npos) {
55+
throw HttpParserError("Invalid request line");
2356
}
57+
parse_request_line(raw_request.substr(pos, end_pos - pos), request);
58+
pos = end_pos + 2;
59+
60+
// Parse headers
61+
while (pos < raw_request.size()) {
62+
end_pos = raw_request.find("\r\n", pos);
63+
if (end_pos == std::string_view::npos) {
64+
throw HttpParserError("Invalid header format");
65+
}
2466

25-
while (std::getline(stream, line) && line != "\r") {
26-
if (line.empty()) break;
27-
parseHeader(line, request);
67+
// Check for end of headers
68+
if (pos == end_pos) {
69+
pos += 2;
70+
break;
71+
}
72+
73+
parse_header(raw_request.substr(pos, end_pos - pos), request);
74+
pos = end_pos + 2;
2875
}
2976

30-
std::string body;
31-
while (std::getline(stream, line)) {
32-
body += line + "\n";
77+
// Parse body
78+
if (pos < raw_request.size()) {
79+
request.body_ = std::string(raw_request.substr(pos));
3380
}
34-
request.body = body;
3581

3682
return request;
3783
}
3884

39-
private:
40-
void parseRequestLine(const std::string& line, HttpRequest& request) {
41-
std::istringstream lineStream(line);
42-
lineStream >> request.method >> request.path >> request.version;
85+
static void parse_request_line(std::string_view line, HttpRequest& request) {
86+
size_t method_end = line.find(' ');
87+
if (method_end == std::string_view::npos) {
88+
throw HttpParserError("Invalid request line format");
89+
}
90+
91+
size_t path_end = line.find(' ', method_end + 1);
92+
if (path_end == std::string_view::npos) {
93+
throw HttpParserError("Invalid request line format");
94+
}
95+
96+
request.method_ = std::string(line.substr(0, method_end));
97+
request.path_ = std::string(line.substr(method_end + 1, path_end - method_end - 1));
98+
request.version_ = std::string(line.substr(path_end + 1));
99+
100+
// Validate HTTP method
101+
if (!is_valid_method(request.method_)) {
102+
throw HttpParserError("Invalid HTTP method");
103+
}
43104
}
44105

45-
void parseHeader(const std::string& line, HttpRequest& request) {
46-
size_t colonPos = line.find(':');
47-
if (colonPos == std::string::npos) return;
106+
static void parse_header(std::string_view line, HttpRequest& request) {
107+
size_t colon_pos = line.find(':');
108+
if (colon_pos == std::string_view::npos) {
109+
throw HttpParserError("Invalid header format");
110+
}
111+
112+
std::string_view key = line.substr(0, colon_pos);
113+
std::string_view value = line.substr(colon_pos + 1);
114+
115+
// Trim whitespace
116+
value = trim(value);
117+
118+
if (!key.empty() && !value.empty()) {
119+
request.headers_.emplace(
120+
std::string(key),
121+
std::string(value)
122+
);
123+
}
124+
}
48125

49-
std::string key = line.substr(0, colonPos);
50-
std::string value = line.substr(colonPos + 1);
51-
value.erase(0, value.find_first_not_of(" "));
52-
value.erase(value.find_last_not_of("\r") + 1);
126+
[[nodiscard]] static std::string_view trim(std::string_view str) noexcept {
127+
const auto first = str.find_first_not_of(" \t\r\n");
128+
if (first == std::string_view::npos) return {};
129+
130+
const auto last = str.find_last_not_of(" \t\r\n");
131+
return str.substr(first, last - first + 1);
132+
}
53133

54-
request.headers[key] = value;
134+
[[nodiscard]] static bool is_valid_method(const std::string& method) noexcept {
135+
static const auto valid_methods = std::array{
136+
"GET", "POST", "PUT", "DELETE", "HEAD",
137+
"OPTIONS", "PATCH", "TRACE", "CONNECT"
138+
};
139+
140+
return std::find(valid_methods.begin(), valid_methods.end(), method)
141+
!= valid_methods.end();
55142
}
56143
};

0 commit comments

Comments
 (0)