_exec_script() close fd and reset signal

+ somes more adjustements in _exec_script()
+ rough notes for non blocking CGI
This commit is contained in:
LuckyLaszlo
2022-08-16 06:50:20 +02:00
parent 4602844f5a
commit ff443c80b1
5 changed files with 107 additions and 26 deletions

View File

@@ -10,6 +10,18 @@
- handle redirection (Work, but weird behavior need deeper test)
- Ecrire des tests !
- cgi_cpp_status.cpp with POST dont show error page. Normal or not ?
For non blocking CGI :
// We could maybe,
// add FD_RD_FR_CHLD to epoll,
// return to the main loop,
// read FD_RD_FR_CHLD each time epoll say its ready,
// then try waitpid() with WNOHANG after each read.
// when waitpid() tell us its finish (or maybe when epoll return EPOLLHUP)
// then actually parse the script_output and send it to the client.
----Priorité modérée------------------------
- namespace utils ?
- change "std::string" to reference "std::string &" in most functions

View File

@@ -106,6 +106,7 @@ class Webserv
// close.cpp
void _close_client(int fd);
void _close_all_clients();
void _close_all_clients_fd();
void _close_all_listen_sockets();
void _reopen_lsocket(std::vector<listen_socket>::iterator it);
void _handle_epoll_error_lsocket(uint32_t events, std::vector<listen_socket>::iterator it);
@@ -131,6 +132,16 @@ class Webserv
void _check_script_fields(Client *client, std::string & output);
void _add_script_body_length_header(std::string & output);
void _remove_body_leading_empty_lines(std::string & output);
///////////////////////
class ExecFail : public std::exception
{
public :
virtual const char* what() const throw() {
return ("Exec CGI fail");
};
};
};
#endif

View File

@@ -72,16 +72,24 @@ std::string Webserv::_exec_cgi(Client *client)
char* env_cstr[19] = {NULL};
std::vector<std::string> env_vector;
env_vector.reserve(18);
int i = 0;
_set_env_vector(client, env_vector);
_set_env_cstr(env_cstr, env_vector);
script_output = _exec_script(client, env_cstr);
int i = 0;
while (env_cstr[i] != NULL)
delete[] env_cstr[i++];
try {
_set_env_cstr(env_cstr, env_vector);
script_output = _exec_script(client, env_cstr);
return script_output;
while (env_cstr[i] != NULL)
delete[] env_cstr[i++];
return script_output;
}
catch (const Webserv::ExecFail& e)
{
while (env_cstr[i] != NULL)
delete[] env_cstr[i++];
throw;
}
}
std::string Webserv::_dup_env(std::string var, std::string val = "")
@@ -166,6 +174,8 @@ void Webserv::_set_env_cstr(char *env_cstr[], std::vector<std::string> &env_vect
env_cstr[18] = NULL;
} */
#define STATUS_500 std::string("Status: 500" CRLF CRLF);
std::string Webserv::_exec_script(Client *client, char *env[])
{
#define RD 0
@@ -190,21 +200,43 @@ std::string Webserv::_exec_script(Client *client, char *env[])
pid = fork();
if (pid == -1)
std::cerr << "fork crashed" << std::endl;
perror("err fork()");
else if (pid == 0) // child
{
// _close_all_clients();
close(FD_WR_TO_CHLD);
close(FD_RD_FR_CHLD);
dup2(FD_RD_FR_PRNT, STDIN_FILENO);
dup2(FD_WR_TO_PRNT, STDOUT_FILENO);
path = "." + client->get_rq_script_path();
std::signal(SIGPIPE, SIG_DFL);
std::signal(SIGINT, SIG_DFL);
_close_all_clients_fd();
::close(_epfd);
::close(FD_WR_TO_CHLD);
::close(FD_RD_FR_CHLD);
if (dup2(FD_RD_FR_PRNT, STDIN_FILENO) == -1)
{
perror("err dup2()");
::close(FD_RD_FR_PRNT); // Valgind debug, not essential
::close(FD_WR_TO_PRNT); // Valgind debug, not essential
throw ExecFail();
}
if (dup2(FD_WR_TO_PRNT, STDOUT_FILENO) == -1)
{
perror("err dup2()");
::close(FD_RD_FR_PRNT); // Valgind debug, not essential
::close(FD_WR_TO_PRNT); // Valgind debug, not essential
throw ExecFail();
}
::close(FD_RD_FR_PRNT);
::close(FD_WR_TO_PRNT);
path = "." + client->get_rq_script_path(); // Wut ? Only relative path ?
/*DEBUG*/std::cerr << "execve:[" << path << "]\n";
execve(path.c_str(), nll, env);
// for tests execve crash :
//execve("wrong", nll, env);
std::cerr << "execve crashed.\n";
// TODO HUGO : check errno
if (execve(path.c_str(), nll, env) == -1) // replace path for debug error forcing
{
perror("err execve()");
::close(STDIN_FILENO); // Valgind debug, not essential
::close(STDOUT_FILENO); // Valgind debug, not essential
throw ExecFail();
}
}
else //parent
{
@@ -213,17 +245,30 @@ std::string Webserv::_exec_script(Client *client, char *env[])
write(FD_WR_TO_CHLD, body.c_str(), body.size());
close(FD_WR_TO_CHLD);
waitpid(-1, NULL, 0);
// We could maybe,
// add FD_RD_FR_CHLD to epoll,
// return to the main loop,
// read FD_RD_FR_CHLD each time epoll say its ready,
// then try waitpid() with WNOHANG after each read.
// when waitpid() tell us its finish (or maybe when epoll return EPOLLHUP)
// then actually parse the script_output and send it to the client.
memset(buf, '\0', CGI_BUF_SIZE);
while (read(FD_RD_FR_CHLD, buf, CGI_BUF_SIZE - 1) > 0)
ssize_t ret = 1;
while (ret > 0)
{
script_output += buf;
memset(buf, '\0', CGI_BUF_SIZE);
ret = read(FD_RD_FR_CHLD, buf, CGI_BUF_SIZE);
if (ret == -1)
{
std::perror("err recv()");
script_output = STATUS_500;
break;
}
script_output.append(buf, ret);
}
close(FD_RD_FR_CHLD);
}
if (script_output.empty())
script_output = "Status: 500" CRLF CRLF;
script_output = STATUS_500;
return script_output;
}

View File

@@ -21,13 +21,21 @@ void Webserv::_close_client(int fd)
void Webserv::_close_all_clients()
{
while (!_clients.empty())
_close_all_clients_fd();
_clients.clear();
}
void Webserv::_close_all_clients_fd()
{
std::vector<Client>::iterator it = _clients.begin();
std::vector<Client>::iterator it_end = _clients.end();
while (it != it_end)
{
// _epoll_update(_clients.back().fd, 0, EPOLL_CTL_DEL); // normalement superflu, DEBUG
std::cerr << "close fd " << _clients.back().get_cl_fd() << "\n";
if (::close(_clients.back().get_cl_fd()) == -1)
std::perror("err close()");
_clients.pop_back();
++it;
}
}

View File

@@ -62,6 +62,11 @@ void Webserv::run()
std::vector<Client>().swap(_clients);
break;
}
catch (const Webserv::ExecFail& e)
{
std::cerr << e.what() << '\n';
throw;
}
catch (const std::exception& e) {
std::cerr << e.what() << '\n';
++i;