nicolas-arnaud
2 years ago
15 changed files with 206 additions and 473 deletions
@ -0,0 +1,24 @@ |
|||||
|
#pragma once |
||||
|
#include "webserv.hpp" |
||||
|
|
||||
|
typedef std::map< string, std::vector< string > > request_t; |
||||
|
|
||||
|
class Client { |
||||
|
int _fd; |
||||
|
Master *_parent; |
||||
|
string _header; |
||||
|
string _content; |
||||
|
request_t _request; |
||||
|
|
||||
|
public: |
||||
|
Client(int fd, Master *parent); |
||||
|
~Client(void); |
||||
|
bool getRequest(string paquet); |
||||
|
bool parseHeader(); |
||||
|
int answer(Env *env); |
||||
|
void call_cgi(string cgi, string path); |
||||
|
void send_404(); |
||||
|
void send_answer(string msg); |
||||
|
bool waitHeader(); |
||||
|
friend class Master; |
||||
|
}; |
@ -0,0 +1,25 @@ |
|||||
|
#pragma once |
||||
|
#include "webserv.hpp" |
||||
|
|
||||
|
class Master { |
||||
|
// int _clients_amount;
|
||||
|
int _fd; |
||||
|
std::vector< Client * > _childs; |
||||
|
struct sockaddr_in _address; |
||||
|
|
||||
|
public: |
||||
|
Master(listen_t listen); |
||||
|
Master(int fd, Master *parent); |
||||
|
~Master(void); |
||||
|
|
||||
|
int launch(void); |
||||
|
void set_fds(void); |
||||
|
void refresh(Env *env); |
||||
|
Server *choose_server(Env *env, string host); |
||||
|
|
||||
|
listen_t _listen; |
||||
|
static fd_set _readfds; |
||||
|
static int _max_fd; |
||||
|
static int _min_fd; |
||||
|
static int _amount; |
||||
|
}; |
@ -1,34 +0,0 @@ |
|||||
#pragma once |
|
||||
#include "webserv.hpp" |
|
||||
|
|
||||
class Socket { |
|
||||
int _fd; |
|
||||
//int _clients_amount;
|
|
||||
Socket *_parent; |
|
||||
std::vector<Socket *> _childs; |
|
||||
struct sockaddr_in _address; |
|
||||
string _header; |
|
||||
string _content; |
|
||||
std::map<string,std::vector<string> > _request; |
|
||||
|
|
||||
bool getRequest(string paquet); |
|
||||
bool parseHeader(); |
|
||||
int answer(Env *env); |
|
||||
void send_answer(string msg); |
|
||||
bool waitHeader(); |
|
||||
Server *choose_server(Env *env, string host); |
|
||||
public: |
|
||||
Socket(listen_t listen); |
|
||||
Socket(int fd, Socket *parent); |
|
||||
~Socket(void); |
|
||||
|
|
||||
listen_t _listen; |
|
||||
static fd_set _readfds; |
|
||||
static int _max_fd; |
|
||||
static int _min_fd; |
|
||||
static int _amount; |
|
||||
|
|
||||
int launch(void); |
|
||||
void set_fds(void); |
|
||||
void refresh(Env *env); |
|
||||
}; |
|
@ -1,282 +0,0 @@ |
|||||
#include "webserv.hpp" |
|
||||
|
|
||||
Socket::Socket(listen_t listen) : _listen(listen) {} |
|
||||
Socket::Socket(int fd, Socket *parent) : _fd(fd), _parent(parent) {} |
|
||||
Socket::~Socket(void) { |
|
||||
close(_fd); |
|
||||
cout << "Socket destroyed!\n"; |
|
||||
} |
|
||||
|
|
||||
int Socket::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 Socket::set_fds(void) { |
|
||||
FD_SET(_fd, &_readfds); |
|
||||
|
|
||||
int child_fd; |
|
||||
for (std::vector<Socket *>::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 Socket::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 Socket(new_socket, this)); |
|
||||
} |
|
||||
int child_fd; |
|
||||
for (std::vector<Socket *>::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 *Socket::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(); |
|
||||
} |
|
||||
bool Socket::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; |
|
||||
} |
|
||||
|
|
||||
bool Socket::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; |
|
||||
} |
|
||||
|
|
||||
int Socket::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; |
|
||||
std::stringstream answer; |
|
||||
answer << "HTTP/1.1"; |
|
||||
|
|
||||
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_answer("HTTP/1.1 405 Method Not Allowed\r\n\r\n"); |
|
||||
else if ((allowed = route->_headers).size() > 0) { |
|
||||
if (std::find(allowed.begin(), allowed.end(), method) == allowed.end()) |
|
||||
send_answer("HTTP/1.1 405 Method Not Allowed\r\n\r\n"); |
|
||||
} else if ((allowed = server->_headers).size() > 0) { |
|
||||
if (std::find(allowed.begin(), allowed.end(), method) == allowed.end()) |
|
||||
send_answer("HTTP/1.1 405 Method Not Allowed\r\n\r\n"); |
|
||||
} |
|
||||
|
|
||||
string path = route->correctUri(_request["Method:"].at(1)); |
|
||||
cout << "Path: " << path << "\n"; |
|
||||
string cgi; |
|
||||
if (route->_cgi.size()) |
|
||||
cgi = route->_cgi[get_extension(path)]; |
|
||||
cout << "Cgi:" << cgi << "\n"; |
|
||||
if (cgi != "") { |
|
||||
int status; |
|
||||
int fd[2]; |
|
||||
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); |
|
||||
cout << "Cgi finished\n"; |
|
||||
char buffer[10000]; |
|
||||
size_t len = read(fd[0], buffer, 10000); |
|
||||
buffer[len] = 0; |
|
||||
ret = string(buffer); |
|
||||
len = ret.length() - ret.find("\r\n\r\n") - 4; |
|
||||
answer << " 200 OK\r\nContent-length: " << len << "\r\n"; |
|
||||
answer << ret; |
|
||||
} else { |
|
||||
ret = route->getIndex(_request["Method:"].at(1), path); |
|
||||
if (ret == "") { |
|
||||
cout << "No index: lf file\n"; |
|
||||
ret = read_file(path); |
|
||||
} |
|
||||
answer << (ret == "" ? " 404 Not Found\r\nContent-length: 0\r\n\r\n" |
|
||||
: " 200 OK\r\n") |
|
||||
<< ret; |
|
||||
} |
|
||||
cout << "|===|Answer|===|\n" << answer.str() << "|===|End of answer|===|\n"; |
|
||||
send_answer(answer.str()); |
|
||||
_content = ""; |
|
||||
_header = ""; |
|
||||
return EXIT_SUCCESS; |
|
||||
} |
|
||||
|
|
||||
void Socket::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 |
|
||||
} |
|
@ -1,45 +1,28 @@ |
|||||
#include "webserv.hpp" |
#include "webserv.hpp" |
||||
|
|
||||
fd_set Socket::_readfds; |
fd_set Master::_readfds; |
||||
int Socket::_max_fd; |
int Master::_max_fd; |
||||
int Socket::_min_fd = INT_MAX; |
int Master::_min_fd = INT_MAX; |
||||
int Socket::_amount = 0; |
int Master::_amount = 0; |
||||
|
|
||||
int main(int ac, char **av) { |
int main(int ac, char **av) { |
||||
if (ac <= 2) { |
if (ac != 2) { |
||||
std::string config = "default.json"; |
cout << "Usage:\n./webserv CONF.json\n"; |
||||
if (ac == 2){ |
return EXIT_SUCCESS; |
||||
|
} |
||||
|
|
||||
std::ifstream file(av[1]); |
std::ifstream file(av[1]); |
||||
if (file.good()) |
if (!file.good()) { |
||||
config = av[1]; |
cout << "Error: " << av[1] << " is not a valid file\n"; |
||||
else { |
|
||||
std::cout << "Error: " << av[1] << " is not a valid file" << std::endl; |
|
||||
return EXIT_FAILURE; |
return EXIT_FAILURE; |
||||
} |
} |
||||
config = av[1]; |
|
||||
} |
|
||||
cout << "Parsing configuration file from JSON conf file.\n"; |
cout << "Parsing configuration file from JSON conf file.\n"; |
||||
cout << "You must be sure the syntax is correct\n"; |
cout << "You must be sure the syntax is correct\n"; |
||||
JSONParser parser(config); |
JSONParser parser(av[1]); |
||||
JSONNode *conf = parser.parse(); |
JSONNode *conf = parser.parse(); |
||||
cout << "Initialization of server...\n"; |
cout << "Initialization of server...\n"; |
||||
Env env(conf); |
Env env(conf); |
||||
|
while (1) |
||||
while (1) { |
env.cycle(); |
||||
FD_ZERO(&Socket::_readfds); |
return EXIT_SUCCESS; |
||||
Socket::_max_fd = Socket::_min_fd; |
|
||||
cout << "==> Check sockets still alive to listen\n"; |
|
||||
env.set_fds(); |
|
||||
cout << "|===||===| Waiting some HTTP request... |===||===|\n"; |
|
||||
int activity = select(Socket::_max_fd + Socket::_amount, |
|
||||
&(Socket::_readfds), NULL, NULL, NULL); |
|
||||
if ((activity < 0) && (errno != EINTR)) |
|
||||
cout << "Select: " << strerror(errno) << "\n"; |
|
||||
cout << "==> Handle requests and answers:\n"; |
|
||||
env.refresh(); |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
cout << "Usage:\n./webserv CONF.json\n"; |
|
||||
return (0); |
|
||||
} |
} |
||||
|
Loading…
Reference in new issue