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>
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+
305217void 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