Skip to content

Commit 69ccf62

Browse files
committed
feat: add HTTP/1.1 keep-alive sessions
1 parent bbf1f86 commit 69ccf62

25 files changed

+350
-197
lines changed

.clang-format

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ IndentAccessModifiers: false
1010
AccessModifierOffset: -4
1111

1212
AllowShortFunctionsOnASingleLine: InlineOnly
13+
# Experimental flag, could fail at anytime
14+
ExperimentalAutoDetectBinPacking: true
15+
1316
BreakConstructorInitializers: BeforeColon
1417
PackConstructorInitializers: Never
1518

.clang-tidy

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ CheckOptions:
3939
value: CamelCase
4040
- key: readability-identifier-naming.EnumConstantPrefix
4141
value: k
42+
- key: readability-identifier-naming.EnumConstantIgnoredRegexp
43+
value: '^k([A-Z][a-z]*)+[0-9]+(_[0-9])+$'
4244
# Global constants
4345
- key: readability-identifier-naming.GlobalConstantCase
4446
value: CamelCase

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ srcs = \
4343
src/http/http_request.hpp \
4444
src/http/http_response.cpp \
4545
src/http/http_response.hpp \
46+
src/http/http_version.cpp \
47+
src/http/http_version.hpp \
4648
src/router/router.cpp \
4749
src/router/router.hpp \
4850
src/util/log_message.cpp \

src/core/client.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
#include <iostream>
1010

1111
Client::Client(uint64_t id, int fd, const sockaddr_in& addr)
12-
: id_(id),
12+
: state_(kReceivingHeaders),
13+
id_(id),
1314
fd_(fd),
1415
addr_(addr),
1516
handler_(NULL)

src/core/client.hpp

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,37 +12,36 @@
1212

1313
class Client {
1414
public:
15+
enum State { kClosingConnection = 0, kReceivingHeaders, kReceivingBody, kSendingResponse };
16+
1517
Client(uint64_t id, int fd, const sockaddr_in& addr);
1618
~Client();
1719

1820
// setters
1921
void set_handler(Handler* handler) { handler_ = handler; }
2022
void set_nonblocking();
21-
void set_request(const HttpRequest& req) { req_ = req; }
22-
void set_response(const HttpResponse& res) { res_ = res; }
23+
void set_state(Client::State state) { state_ = state; }
2324

2425
// getters
26+
Client::State state() { return state_; }
2527
uint64_t id() const { return id_; }
2628
int fd() const { return fd_; }
29+
bool keep_alive() { return parser_.request().keep_alive; }
2730
const sockaddr_in& addr() const { return addr_; }
28-
const HttpRequest& req() const { return req_; }
29-
HttpResponse& res() { return res_; }
30-
3131
std::string& send_buffer() { return send_buffer_; }
32-
Handler* handler() const { return handler_; }
3332
HttpParser& parser() { return parser_; }
33+
Handler* handler() const { return handler_; }
3434

3535
private:
3636
Client();
3737
Client(const Client& other);
3838
Client& operator=(const Client& other);
3939

4040
private:
41+
Client::State state_;
4142
uint64_t id_;
4243
int fd_;
4344
sockaddr_in addr_;
44-
HttpRequest req_;
45-
HttpResponse res_; // TODO: remove
4645
std::string send_buffer_;
4746
HttpParser parser_;
4847
Handler* handler_;

src/core/server.cpp

Lines changed: 113 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -183,89 +183,142 @@ static void print_raw_data(const char* s, size_t n)
183183
std::cout.flush();
184184
}
185185

186-
void Server::handle_events(Client& conn, uint32_t events)
186+
static void log_request_info(uint64_t client_id, const HttpRequest& request)
187+
{
188+
LOG(INFO) << "Client #" << client_id << ": " << request.method << " " << request.path << " "
189+
<< request.http_version;
190+
}
191+
192+
void Server::read_from_socket(Client& conn)
187193
{
194+
if (conn.state() != Client::kReceivingHeaders && conn.state() != Client::kReceivingBody)
195+
return;
196+
188197
char tmp[4096];
189-
uint64_t client_id = conn.id();
198+
ssize_t bytes_received = recv(conn.fd(), tmp, sizeof(tmp), 0);
199+
200+
if (bytes_received == 0) {
201+
LOG(INFO) << "Client #" << conn.id() << ": connection closed by remote client";
202+
conn.set_state(Client::kClosingConnection);
203+
return;
204+
}
205+
190206
HttpParser& parser = conn.parser();
191-
uint32_t next_events = 0;
207+
parser.append_data(tmp, bytes_received);
208+
print_raw_data(tmp, bytes_received);
192209

193-
// Can read from socket
194-
if (events & EPOLLIN) {
210+
if (conn.state() == Client::kReceivingHeaders) {
195211

196-
size_t bytes_received = recv(conn.fd(), tmp, sizeof(tmp), 0);
212+
if (parser.state() == HttpParser::kParsingError) {
213+
LOG(ERROR) << "Client #" << conn.id() << ": invalid HTTP request";
214+
conn.set_state(Client::kClosingConnection);
215+
return;
216+
}
197217

198-
if (bytes_received == 0) {
199-
LOG(INFO) << "Client #" << client_id << ": connection closed by remote client";
200-
close_connection(conn);
218+
if (parser.state() < HttpParser::kParsingBody) {
201219
return;
202220
}
203221

204-
LOG(DEBUG) << "Client #" << client_id << ": " << bytes_received << " bytes received";
205-
print_raw_data(tmp, bytes_received);
222+
LOG(DEBUG) << "WE PARSED ALL HEADERS";
206223

207-
// Feed data to the parser
208-
parser.feed_data(tmp, bytes_received);
224+
// We parsed headers and might have some or all body bytes?
225+
log_request_info(conn.id(), parser.request());
226+
conn.set_handler(router_.handle_request(parser.request()));
209227

210-
if (parser.status() == HttpParser::kError) {
211-
LOG(ERROR) << "Client #" << client_id << ": invalid HTTP request";
212-
close_connection(conn);
213-
return;
228+
if (conn.handler()->needs_input()) {
229+
conn.set_state(Client::kReceivingBody);
214230
}
231+
else {
232+
conn.set_state(Client::kSendingResponse);
233+
}
234+
}
215235

216-
if (!conn.handler() && parser.status() >= HttpParser::kHeadersDone) {
217-
HttpRequest req = parser.request();
218-
LOG(INFO) << "Client #" << client_id << ": " << req.method << " " << req.path << " "
219-
<< req.http_version;
220-
221-
Handler* h = router_.handle_request(req);
222-
conn.set_handler(h);
236+
if (conn.state() == Client::kReceivingBody) {
237+
size_t n = 0;
238+
while ((n = parser.read_body_chunk(tmp, sizeof(tmp))) > 0) {
239+
conn.handler()->write_data(tmp, n);
223240
}
224-
if (conn.handler()) {
225-
if (conn.handler()->needs_input()) {
226-
size_t n = parser.slurp_data(tmp, sizeof(tmp));
227-
(void) conn.handler()->write_data(tmp, n);
228-
}
241+
if (conn.handler()->has_output()) {
242+
conn.set_state(Client::kSendingResponse);
229243
}
230244
}
245+
}
231246

232-
// Can write to socket
233-
if (events & EPOLLOUT) {
234-
if (conn.handler()) {
247+
void Server::write_to_socket(Client& conn)
248+
{
249+
assert(conn.handler() && "Handler must exist when writing to socket");
235250

236-
Handler& handler = *conn.handler();
237-
std::string& send_buffer = conn.send_buffer();
251+
if (conn.state() != Client::kSendingResponse)
252+
return;
238253

239-
if (handler.has_output()) {
240-
size_t n = handler.read_data(tmp, sizeof(tmp));
241-
send_buffer.append(tmp, n);
242-
}
243-
if (!send_buffer.empty()) {
244-
size_t bytes_sent = send(conn.fd(), send_buffer.data(), send_buffer.size(), 0);
245-
if (bytes_sent == 0) {
246-
LOG(WARN) << "Client #" << client_id << ": failed to send any bytes!";
247-
}
248-
else {
249-
send_buffer.erase(0, bytes_sent);
250-
}
251-
}
252-
// Treat all connections as HTTP/1.0 for now (do not keep-alive)
253-
if (send_buffer.empty() && handler.is_done()) {
254-
LOG(DEBUG) << "Client #" << client_id << ": response sent";
255-
close_connection(conn);
256-
return;
257-
}
254+
char tmp[4096];
255+
Handler& handler = *conn.handler();
256+
std::string& send_buffer = conn.send_buffer();
257+
258+
if (handler.has_output()) {
259+
size_t n = handler.read_data(tmp, sizeof(tmp));
260+
send_buffer.append(tmp, n);
261+
}
262+
if (!send_buffer.empty()) {
263+
size_t bytes_sent = send(conn.fd(), send_buffer.data(), send_buffer.size(), 0);
264+
if (bytes_sent == 0) {
265+
LOG(WARN) << "Client #" << conn.id() << ": failed to send any bytes!";
266+
}
267+
else {
268+
send_buffer.erase(0, bytes_sent);
258269
}
259270
}
260271

261-
// Update next event subscription
262-
if (!conn.handler() || conn.handler()->needs_input())
263-
next_events |= EPOLLIN;
264-
if (conn.handler() && (conn.handler()->has_output() || !conn.send_buffer().empty()))
265-
next_events |= EPOLLOUT;
272+
// All data has been written to socket -> either close or keep going
273+
if (handler.is_done() && send_buffer.empty()) {
274+
LOG(DEBUG) << "Client #" << conn.id() << ": response sent";
275+
276+
if (conn.keep_alive()) {
277+
conn.parser().reset_for_next_request();
278+
delete conn.handler();
279+
conn.set_handler(NULL);
280+
conn.set_state(Client::kReceivingHeaders);
281+
LOG(DEBUG) << "WE DELETED THE HANDLER";
282+
}
283+
else {
284+
conn.set_state(Client::kClosingConnection);
285+
}
286+
}
287+
}
288+
289+
void Server::handle_events(Client& conn, uint32_t events)
290+
{
291+
if (events & EPOLLIN) {
292+
read_from_socket(conn);
293+
LOG(DEBUG) << "WE READ FROM SOCKET";
294+
}
295+
if (events & EPOLLOUT) {
296+
write_to_socket(conn);
297+
LOG(DEBUG) << "WE WROTE TO SOCKET";
298+
}
299+
300+
if (conn.state() == Client::kClosingConnection) {
301+
close_connection(conn);
302+
return;
303+
}
266304

267305
epoll_event ev;
268-
ev.data.u64 = client_id;
269-
ev.events = next_events;
306+
ev.data.u64 = conn.id();
307+
308+
if (conn.state() == Client::kReceivingHeaders) {
309+
ev.events = EPOLLIN;
310+
epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, conn.fd(), &ev);
311+
LOG(DEBUG) << "WE REGISTERED EPOLLIN";
312+
return;
313+
}
314+
315+
uint32_t event_mask = 0;
316+
317+
if (conn.handler()->needs_input())
318+
event_mask |= EPOLLIN;
319+
if (conn.handler()->has_output() || !conn.send_buffer().empty())
320+
event_mask |= EPOLLOUT;
321+
322+
ev.events = event_mask;
270323
epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, conn.fd(), &ev);
271324
}

src/core/server.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ class Server {
2727

2828
void accept_connection();
2929
void handle_events(Client& conn, uint32_t events);
30+
void read_from_socket(Client& conn);
31+
void write_to_socket(Client& conn);
3032

3133
uint64_t add_connection(int client_fd, const sockaddr_in& addr);
3234
void close_connection(Client& conn);

src/core/server_defaults.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
#ifndef SERVER_DEFAULTS_HPP_
22
#define SERVER_DEFAULTS_HPP_
33

4+
#include "http/http_version.hpp"
5+
46
#define WEBSERV_DEFAULT_PORT 8080
57
#define WEBSERV_DEFAULT_BACKLOG 128
68
#define WEBSERV_DEFAULT_MAX_BODY_SIZE (1 << 20) // 1MB
79

8-
#define WEBSERV_DEFAULT_HTTP_VERSION "HTTP/1.0"
10+
#define WEBSERV_DEFAULT_HTTP_VERSION kHttpVersion1_1
911

1012
#endif // SERVER_DEFAULTS_HPP_

src/handler/error_handler.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ size_t ErrorHandler::write_data(const char* buf, size_t n)
2424
ErrorHandler::ErrorHandler(HttpResponse::Status code)
2525
: res_sent_(false)
2626
{
27-
res_.http_version = "HTTP/1.1";
2827
res_.code = code;
2928
res_.inline_body = "<h1> some error code to be shown here <h1>";
3029
LOG(DEBUG) << "ErrorHandler constructed with code " << code;

src/handler/error_handler.hpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,17 @@ class ErrorHandler : public Handler {
1414
virtual size_t read_data(char* buf, size_t n);
1515
virtual size_t write_data(const char* buf, size_t n);
1616

17-
virtual bool has_output() const { return (!res_sent_); };
18-
virtual bool needs_input() const { return (false); };
19-
virtual bool is_done() const { return (res_sent_); };
17+
virtual bool has_output() const { return (!res_sent_); }
18+
virtual bool needs_input() const { return (false); }
19+
virtual bool is_done() const { return (res_sent_); }
2020

21-
int error_code() { return res_.code; };
21+
int error_code() { return res_.code; }
2222

2323
private:
2424
ErrorHandler(const ErrorHandler&);
2525
ErrorHandler& operator=(const ErrorHandler&);
2626

27+
// NOTE: HttpResponse is default constructed with HTTP/1.1 + keep-alive
2728
HttpResponse res_;
2829
bool res_sent_;
2930
};

0 commit comments

Comments
 (0)