nicolas-arnaud
2 years ago
5 changed files with 304 additions and 8 deletions
@ -0,0 +1,149 @@ |
|||
#include "webserv.hpp" |
|||
|
|||
Client::Client(int fd, Master *parent) : _fd(fd), _parent(parent) {} |
|||
Client::~Client(void) { |
|||
close(_fd); |
|||
cout << "Destroyed client socket\n"; |
|||
} |
|||
|
|||
bool Client::getRequest(string paquet) { |
|||
cout << "|===|paquet|===>" << paquet << "|===||\n"; |
|||
if (paquet.length() < 1) // HTTPS?
|
|||
return false; |
|||
std::vector< string > lines = split(paquet, '\n'); |
|||
long chunk_len = |
|||
(_content.length() > 0 && _request["Transfer-Encoding:"].size() && |
|||
_request["Transfer-Encoding:"].at(0) == "chunked") |
|||
? std::strtol(lines.at(0).substr(1).c_str(), 0, 16) |
|||
: -1; |
|||
cout << "Chunk length: " << chunk_len << "\n"; |
|||
for (std::vector< string >::iterator it = lines.begin(); it < lines.end(); |
|||
it++) { |
|||
if (*it == "\r" && _content.length() == 0) |
|||
this->parseHeader(); |
|||
if (*it != "\r" && _content.length() == 0) |
|||
_header += *it + "\n"; |
|||
else if (chunk_len == -1 || it != lines.begin()) |
|||
_content += *it + "\n"; |
|||
} |
|||
cout << "Header: \n-|" << _header << "|-\n"; |
|||
cout << "Content: \n-|" << _content << "|-\n"; |
|||
if (_content.length() > 0) { |
|||
_content.resize(_content.length() - 1); |
|||
if (_request["Method:"].at(0) == "GET" || |
|||
(chunk_len == 0 || |
|||
std::strtoul(_request["Content-Length:"].at(0).c_str(), 0, 10) <= |
|||
_content.length())) { |
|||
cout << "Request received\n"; |
|||
return true; |
|||
} |
|||
} else |
|||
_header.resize(_header.length() - 1); |
|||
return false; |
|||
} |
|||
|
|||
bool Client::parseHeader() { |
|||
std::vector< string > lines = split(_header, '\n'); |
|||
std::vector< string > line; |
|||
if (lines.size() > 0) { |
|||
for (std::vector< string >::iterator it = lines.begin() + 1; |
|||
it < lines.end(); it++) { |
|||
line = split(*it, ' '); |
|||
cout << line.at(0) << "scraped from header\n"; |
|||
_request[line.at(0)] = |
|||
std::vector< string >(line.begin() + 1, line.end()); |
|||
} |
|||
} |
|||
std::vector< string > method = split(lines.at(0), ' '); |
|||
if (method.at(0) == "POST" && |
|||
_request.find("Content-Length:") == _request.end() && |
|||
_request.find("Transfer-Encoding:") == _request.end()) |
|||
return false; |
|||
_request["Method:"] = method; |
|||
return true; |
|||
} |
|||
|
|||
int Client::answer(Env *env) { |
|||
cout << "Method: " << _request["Method:"].at(0) << "\n"; |
|||
cout << "URI: " << _request["Method:"].at(1) << "\n"; |
|||
cout << "Host: " << _request["Host:"].at(0) << "\n"; |
|||
string ret; |
|||
|
|||
Server *server = _parent->choose_server(env, _request["Host:"].at(0)); |
|||
Route *route = server->choose_route(_request["Method:"].at(1)); |
|||
string method = _request["Method:"].at(0); |
|||
std::vector< string > allowed; |
|||
if (method != "GET" && method != "POST" && method != "DELETE") |
|||
send_error(405); |
|||
else if ((allowed = route->_headers).size() > 0) { |
|||
if (std::find(allowed.begin(), allowed.end(), method) == allowed.end()) |
|||
send_error(405); |
|||
} else if ((allowed = server->_headers).size() > 0) { |
|||
if (std::find(allowed.begin(), allowed.end(), method) == allowed.end()) |
|||
send_error(405); |
|||
} |
|||
|
|||
string path = route->correctUri(_request["Method:"].at(1)); |
|||
string cgi = route->_cgi.size() ? route->_cgi[get_extension(path)] : ""; |
|||
if (cgi == "") { |
|||
if ((ret = route->getIndex(_request["Method:"].at(1), path)) == "" && |
|||
(ret = read_file(path)) == "") |
|||
send_error(404); |
|||
else |
|||
send_answer("HTTP/1.1 200 OK\r\n" + ret); |
|||
} else |
|||
send_cgi(cgi, path); |
|||
_content = ""; |
|||
_header = ""; |
|||
return EXIT_SUCCESS; |
|||
} |
|||
|
|||
void Client::send_cgi(string cgi, string path) { |
|||
int status; |
|||
int fd[2]; |
|||
std::stringstream ss; |
|||
string ret; |
|||
|
|||
if (!std::ifstream(cgi.c_str()).good()) |
|||
send_error(404); // another error else ?
|
|||
pipe(fd); |
|||
int pid = fork(); |
|||
if (pid == 0) { |
|||
const char **args = new const char *[cgi.length() + path.length() + 2]; |
|||
args[0] = cgi.c_str(); |
|||
args[1] = path.c_str(); |
|||
args[2] = NULL; |
|||
dup2(fd[1], STDOUT_FILENO); |
|||
close(fd[1]); |
|||
close(fd[0]); |
|||
execve(cgi.c_str(), (char **)args, NULL); |
|||
} |
|||
close(fd[1]); |
|||
waitpid(pid, &status, 0); |
|||
char buffer[10000]; |
|||
buffer[read(fd[0], buffer, 10000)] = 0; |
|||
ret = string(buffer); |
|||
ss << "HTTP/1.1 200 OK\r\nContent-length: " |
|||
<< ret.length() - ret.find("\r\n\r\n") - 4 << "\r\n" |
|||
<< ret; |
|||
send_answer(ss.str()); |
|||
} |
|||
|
|||
void Client::send_error(int error_code) { |
|||
switch (error_code) { |
|||
case 404: |
|||
return send_answer( |
|||
"HTTP/1.1 404 Not Found\r\nContent-length: 0\r\n\r\n"); |
|||
case 405: |
|||
return send_answer( |
|||
"HTTP/1.1 405 Method Not Allowed\r\nContent-length: 0\r\n\r\n"); |
|||
} |
|||
} |
|||
|
|||
void Client::send_answer(string msg) { |
|||
#ifdef __linux__ |
|||
send(_fd, msg.c_str(), msg.length(), MSG_NOSIGNAL); |
|||
#elif __APPLE__ |
|||
send(_fd, msg.c_str(), msg.length(), 0); |
|||
#endif |
|||
} |
@ -0,0 +1,147 @@ |
|||
#include "webserv.hpp" |
|||
|
|||
Master::Master(listen_t listen) : _listen(listen) {} |
|||
Master::~Master(void) { |
|||
close(_fd); |
|||
cout << "Destroyed master socket\n"; |
|||
} |
|||
|
|||
int Master::launch(void) { |
|||
int opt = 1; |
|||
string ip = _listen.ip; |
|||
int port = _listen.port; |
|||
|
|||
_fd = socket(AF_INET, SOCK_STREAM, 0); |
|||
if (_fd == 0) { |
|||
cout << "Socket creation: " << strerror(errno) << "\n"; |
|||
return (EXIT_FAILURE); |
|||
} |
|||
int opt_ret = |
|||
setsockopt(_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)); |
|||
if (opt_ret < 0) { |
|||
cout << "Sockopt: " << strerror(errno) << "\n"; |
|||
return (EXIT_FAILURE); |
|||
} |
|||
|
|||
_address.sin_family = AF_INET; |
|||
_address.sin_addr.s_addr = inet_addr(ip.c_str()); |
|||
_address.sin_port = htons(port); |
|||
|
|||
if (bind(_fd, (struct sockaddr *)&_address, sizeof(_address)) < 0) { |
|||
cout << "Bind: " << strerror(errno) << "\n"; |
|||
return (EXIT_FAILURE); |
|||
} |
|||
cout << "Listener " << ip << " on port " << port << "\n"; |
|||
|
|||
if (listen(_fd, 3) < 0) { |
|||
cout << "Listen: " << strerror(errno) << "\n"; |
|||
return (EXIT_FAILURE); |
|||
} |
|||
cout << "Master: " << _fd << "\n"; |
|||
if (_fd < _min_fd) |
|||
_min_fd = _fd; |
|||
_amount++; |
|||
return (EXIT_SUCCESS); |
|||
} |
|||
|
|||
void Master::set_fds(void) { |
|||
FD_SET(_fd, &_readfds); |
|||
|
|||
int child_fd; |
|||
for (std::vector< Client * >::iterator it = _childs.begin(); |
|||
it < _childs.end(); it++) { |
|||
child_fd = (*it)->_fd; |
|||
FD_SET(child_fd, &_readfds); |
|||
if (child_fd > _max_fd) |
|||
_max_fd = child_fd; |
|||
} |
|||
} |
|||
|
|||
void Master::refresh(Env *env) { |
|||
int valread; |
|||
int addrlen = sizeof(_address); |
|||
char buffer[10000]; |
|||
if (FD_ISSET(_fd, &_readfds)) { |
|||
int new_socket = |
|||
accept(_fd, (struct sockaddr *)&_address, (socklen_t *)&addrlen); |
|||
if (new_socket < 0) { |
|||
cout << "Accept: " << strerror(errno) << "\n"; |
|||
exit(EXIT_FAILURE); |
|||
} |
|||
#ifdef __APPLE__ |
|||
// fcntl(new_socket, F_GETNOSIGPIPE);
|
|||
fcntl(new_socket, F_SETFL, O_NONBLOCK); |
|||
#endif |
|||
cout << "New connection, socket fd is " << new_socket |
|||
<< ", ip is : " << inet_ntoa(_address.sin_addr) |
|||
<< ", port : " << ntohs(_address.sin_port) << "\n"; |
|||
_childs.push_back(new Client(new_socket, this)); |
|||
} |
|||
int child_fd; |
|||
for (std::vector< Client * >::iterator it = _childs.begin(); |
|||
it < _childs.end(); it++) { |
|||
child_fd = (*it)->_fd; |
|||
if (FD_ISSET(child_fd, &_readfds)) { |
|||
valread = read(child_fd, buffer, 10000); |
|||
if (valread == 0) { |
|||
getpeername(child_fd, (struct sockaddr *)&_address, |
|||
(socklen_t *)&addrlen); |
|||
cout << "Host disconnected, ip " << inet_ntoa(_address.sin_addr) |
|||
<< ", port " << ntohs(_address.sin_port) << "\n"; |
|||
delete (*it); |
|||
_childs.erase(it); |
|||
} else { |
|||
buffer[valread] = '\0'; |
|||
if ((*it)->getRequest(buffer)) |
|||
(*it)->answer(env); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
Server *Master::choose_server(Env *env, string host) { |
|||
std::vector< Server * > exact; |
|||
std::vector< Server * > inrange; |
|||
std::vector< string > ip_listen; |
|||
std::vector< string > ip_required; |
|||
// string ip = inet_ntoa(sock->_address.sin_addr);
|
|||
// int port = ntohs(sock->_address.sin_port);
|
|||
|
|||
// cout << "Which server for " << ip << ":" << port << "?\n";
|
|||
cout << "Socket: " << _listen.ip << ":" << _listen.port << "\n"; |
|||
ip_required = split(_listen.ip, '.'); |
|||
for (std::vector< Server * >::iterator sit = env->_servers.begin(); |
|||
sit < env->_servers.end(); sit++) { |
|||
|
|||
std::vector< listen_t > serv_listens = (*sit)->_listens; |
|||
for (std::vector< listen_t >::iterator it = serv_listens.begin(); |
|||
it < serv_listens.end(); it++) { |
|||
|
|||
if (_listen.port != (*it).port) |
|||
continue; |
|||
if (_listen.ip == (*it).ip) { |
|||
exact.push_back(*sit); |
|||
continue; |
|||
} |
|||
bool is_inrange = true; |
|||
ip_listen = split((*it).ip, '.'); |
|||
std::vector< string >::iterator r = ip_required.begin(); |
|||
for (std::vector< string >::iterator l = ip_listen.begin(); |
|||
l < ip_listen.end(); l++) { |
|||
if (*l != *r && *l != "0") |
|||
is_inrange = false; |
|||
} |
|||
if (is_inrange == true) |
|||
inrange.push_back(*sit); |
|||
} |
|||
} |
|||
if (exact.size() == 0) { |
|||
for (std::vector< Server * >::iterator sit = inrange.begin(); |
|||
sit < inrange.end(); sit++) { |
|||
if (host == (*sit)->getName()) |
|||
return *sit; |
|||
} |
|||
return inrange.front(); |
|||
} else |
|||
return exact.front(); |
|||
} |
Loading…
Reference in new issue