Skip to content

Commit a5fdaeb

Browse files
committed
feat: separate router from core
1 parent a2283d0 commit a5fdaeb

File tree

12 files changed

+303
-214
lines changed

12 files changed

+303
-214
lines changed

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@ TARGET = $(BIN_DIR)/$(NAME)
1212

1313
# Source tree (keep in alphabetical order)
1414
SRCS = $(addprefix $(SRC_DIR)/, \
15+
config/ServerConfig.hpp \
1516
core/Client.cpp \
1617
core/Client.hpp \
1718
core/Server.cpp \
1819
core/Server.hpp \
1920
http/HttpResponse.cpp \
2021
http/HttpResponse.hpp \
22+
router/router.cpp \
2123
util/SyscallError.cpp \
2224
util/SyscallError.hpp \
2325
main.cpp \

src/config/ServerConfig.hpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#ifndef CONFIG_SERVER_CONFIG_HPP_
2+
#define CONFIG_SERVER_CONFIG_HPP_
3+
4+
#include <string>
5+
#include <vector>
6+
7+
// for dev purpose only
8+
struct Location {
9+
std::string path;
10+
std::string index;
11+
};
12+
13+
struct ServerConfig {
14+
int listen_port; // port to listen
15+
std::vector<std::string> server_name; // host names
16+
bool default_server; // default server applies to a port
17+
// and if none is flagged default_server,
18+
// then the first one is the default one
19+
std::vector<std::string> methods; // allowed methods
20+
std::string root;
21+
Location default_location;
22+
std::vector<Location> locations;
23+
int domain;
24+
int max_conn;
25+
std::string protocol;
26+
};
27+
28+
#endif // CONFIG_SERVER_CONFIG_HPP_

src/core/Client.hpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
#ifndef CORE_CLIENT_HPP_
22
#define CORE_CLIENT_HPP_
33

4+
#include "http/HttpRequest.hpp"
5+
#include "http/HttpResponse.hpp"
6+
47
#include <netinet/in.h>
58

69
#include <string>
@@ -11,11 +14,19 @@ class Client {
1114
~Client();
1215

1316
void set_nonblocking();
17+
void set_request(const HttpRequest& req) { req_ = req; }
18+
void set_response(const HttpResponse& res) { res_ = res; }
1419
void set_next(Client* conn) { next_ = conn; }
1520
void set_prev(Client* conn) { prev_ = conn; }
1621

1722
int fd() const { return fd_; }
1823
const sockaddr_in& addr() const { return addr_; }
24+
25+
const HttpRequest& req() const { return req_; }
26+
HttpResponse& res() { return res_; }
27+
std::string& recv_buffer() { return recv_buffer_; }
28+
std::string& send_buffer() { return send_buffer_; }
29+
1930
Client* next() { return next_; }
2031
Client* prev() { return prev_; }
2132

@@ -27,6 +38,8 @@ class Client {
2738
private:
2839
int fd_;
2940
sockaddr_in addr_;
41+
HttpRequest req_;
42+
HttpResponse res_;
3043
std::string recv_buffer_;
3144
std::string send_buffer_;
3245
Client* prev_;

src/core/Server.cpp

Lines changed: 68 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
#include "core/Server.hpp"
22

3+
#include "http/HttpRequest.hpp"
34
#include "http/HttpResponse.hpp"
45
#include "util/SyscallError.hpp"
5-
#include "util/structs_dev.hpp"
66

77
#include <errno.h>
88
#include <fcntl.h>
@@ -15,153 +15,8 @@
1515

1616
#include <cstring>
1717
#include <iostream>
18-
#include <sstream>
1918

20-
const char* Server::reason_phrase(int error_code)
21-
{
22-
switch (error_code) {
23-
case 400:
24-
return "Bad request";
25-
case 403:
26-
return "Forbidden";
27-
case 404:
28-
return "Not found";
29-
case 405:
30-
return "Method not allowed";
31-
case 500:
32-
return "Internal server error";
33-
default:
34-
return "Unknown";
35-
}
36-
}
37-
38-
enum ErrorCodes {
39-
kBadRequest = 400,
40-
kForbidden = 403,
41-
kNotFound = 404,
42-
kMethodNotAllowed = 405,
43-
kInternalServerError = 500
44-
};
45-
46-
int Server::prefix_length(const std::string& req_location, const std::string& location)
47-
{
48-
int len_min = std::min(req_location.size(), location.size());
49-
int len_match = 0;
50-
for (int i = 0; i < len_min; ++i) {
51-
if (req_location[i] != location[i])
52-
break;
53-
++len_match;
54-
}
55-
return (len_match);
56-
}
57-
58-
// for the moment it takes some static input
59-
const Location* Server::routing(const std::string& req_location)
60-
{
61-
int longest_match = 0;
62-
const Location* best_match = NULL;
63-
// iterrate through locations to find longest match
64-
for (size_t i = 0; i < config_->locations.size(); i++) {
65-
int len_match = prefix_length(req_location, config_->locations[i].path);
66-
if (len_match > longest_match) {
67-
best_match = &config_->locations[i];
68-
}
69-
}
70-
return (best_match ? best_match : &(config_->default_location));
71-
}
72-
73-
int Server::valid_method(const std::string& method)
74-
{
75-
for (size_t i = 0; i < config_->methods.size(); ++i) {
76-
if (method == config_->methods[i])
77-
return (0);
78-
}
79-
return (1);
80-
}
81-
82-
// Description : build full path by concat 3 elements and consider for leading/trailing backslashes
83-
// TO DO (DHE) : In the config parser -> ensure that root has no trailing /
84-
std::string Server::build_request_path(const HttpRequest& request, const Location* best_match)
85-
{
86-
std::string full_path = config_->root;
87-
std::string folder = static_cast<std::string>(best_match->path);
88-
// std::cout << "root = " << full_path << std::endl;
89-
std::string file =
90-
request.path.substr(best_match->path.size(), request.path.size() - best_match->path.size());
91-
if (full_path[full_path.size() - 1] != '/')
92-
full_path = full_path + "/";
93-
if (folder[0] == '/')
94-
folder.erase(0, 1);
95-
if (folder[folder.size() - 1] != '/')
96-
folder = folder + "/";
97-
if (file[0] == '/')
98-
file.erase(0, 1);
99-
full_path = full_path + folder + file;
100-
101-
if (full_path[full_path.size() - 1] == '/')
102-
full_path = full_path + "index.html";
103-
return (full_path);
104-
}
105-
106-
void Server::send_error(Client* conn, int error_code, std::string body)
107-
{
108-
std::ostringstream oss;
109-
oss << "HTTP/1.1 " << error_code << " " << reason_phrase(error_code) << "\r\n"
110-
<< "Content-Type: text/plain\r\n"
111-
<< "Content-Length: " << body.size() << "\r\n"
112-
<< "\r\n"
113-
<< body;
114-
std::string response = oss.str();
115-
send(conn->fd(), response.c_str(), response.size(),
116-
0); // bytes_sent can be used for debbuging
117-
std::cout << "fd = " << conn->fd() << " : " << error_code << " " << reason_phrase(error_code)
118-
<< std::endl;
119-
epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, conn->fd(), NULL);
120-
remove_connection(conn);
121-
}
122-
123-
void Server::handle_request(Client* conn, const HttpRequest& request)
124-
{
125-
struct stat sb; // needed by stat to check if file exists
126-
if (valid_method(request.method) != 0) {
127-
send_error(conn, kMethodNotAllowed, ""); // in the end should be done
128-
return;
129-
}
130-
if (request.path.empty()) {
131-
send_error(conn, kBadRequest, "");
132-
return;
133-
}
134-
const Location* longest_match = routing(request.path);
135-
if (!longest_match) {
136-
send_error(conn, kNotFound, "");
137-
return;
138-
}
139-
std::cout << "longest_match = " << longest_match->path << std::endl;
140-
std::string full_path = build_request_path(request, longest_match);
141-
std::cout << "full_path = " << full_path << std::endl;
142-
143-
if (request.method == "GET") {
144-
if (stat(full_path.c_str(), &sb) != 0) {
145-
send_error(conn, kNotFound, "");
146-
}
147-
else {
148-
send_response(conn); // here the file needs to be read and send to the socket
149-
}
150-
return;
151-
}
152-
else if (request.method == "DELETE") {
153-
}
154-
else if (request.method == "POST") {
155-
}
156-
else {
157-
send_error(conn, kMethodNotAllowed, "");
158-
return;
159-
}
160-
send_response(
161-
conn); // just for testing to avoid infinite loop as the socket remains ready for read/write
162-
}
163-
164-
Server::Server(in_addr_t ip, in_port_t port, int limit_conn, const ServerConfig* config)
19+
Server::Server(in_addr_t ip, in_port_t port, int limit_conn, ServerConfig& config)
16520
: config_(config)
16621
{
16722
fd_ = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
@@ -237,14 +92,7 @@ void Server::run()
23792
accept_connection();
23893
}
23994
else {
240-
// reading from socket and storing request in Client (TO DO : IBOUKHSS)
241-
// for dev, mimic a http request and call the routing function
242-
struct HttpRequest request;
243-
request.method = "GET";
244-
request.path = "/files/42.txt";
245-
handle_request(conn,
246-
request); // no try catch required except if we define a
247-
// scenario where we want to stop the server
95+
handle_events(conn, events_[i].events);
24896
}
24997
}
25098
}
@@ -289,6 +137,8 @@ void Server::accept_connection()
289137
return;
290138
}
291139

140+
std::cout << "* Connection established\n";
141+
292142
Client* client = new Client(client_fd, addr);
293143

294144
epoll_event ev;
@@ -302,10 +152,72 @@ void Server::accept_connection()
302152
add_connection(client);
303153
}
304154

155+
void Server::handle_events(Client* conn, uint32_t events)
156+
{
157+
if (events & EPOLLIN) {
158+
receive_request(conn);
159+
}
160+
if (events & EPOLLOUT) {
161+
send_response(conn);
162+
}
163+
}
164+
165+
static void print_http_request(const std::string& s)
166+
{
167+
std::cout << "< ";
168+
169+
for (size_t i = 0; i < s.size(); ++i) {
170+
char c = s[i];
171+
if (c == '\r') {
172+
std::cout << "\\r";
173+
}
174+
else if (c == '\n') {
175+
std::cout << "\\n\n";
176+
if (i < s.size() - 1) {
177+
std::cout << "< ";
178+
}
179+
}
180+
else {
181+
std::cout.put(c);
182+
}
183+
}
184+
std::cout.flush();
185+
}
186+
187+
void Server::receive_request(Client* conn)
188+
{
189+
int client_fd = conn->fd();
190+
char buf[4096];
191+
192+
int n_bytes = recv(client_fd, buf, sizeof(buf), 0);
193+
conn->recv_buffer().append(buf, n_bytes);
194+
195+
std::cout << "* Request received\n";
196+
197+
print_http_request(conn->recv_buffer());
198+
199+
epoll_event ev;
200+
ev.events = EPOLLOUT;
201+
ev.data.ptr = conn;
202+
epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, client_fd, &ev);
203+
204+
// reading from socket and storing request in Client (TO DO : IBOUKHSS)
205+
// for dev, mimic a http request and call the routing function
206+
HttpRequest req;
207+
req.method = "GET";
208+
req.path = "/files/42.txt";
209+
210+
conn->set_request(req);
211+
212+
// no try catch required except if we define a
213+
// scenario where we want to stop the server
214+
handle_request(conn, req);
215+
}
216+
305217
void Server::send_response(Client* conn)
306218
{
307219
int client_fd = conn->fd();
308-
HttpResponse res = {200, "text/plain", "Hello, client!\n"};
220+
HttpResponse res = conn->res();
309221
std::string raw = res.to_string();
310222

311223
send(client_fd, raw.data(), raw.size(), 0);

0 commit comments

Comments
 (0)