1818
1919static std::vector<char *> make_envp (std::vector<std::string>& env);
2020
21- CgiHandler::CgiHandler (const std::string& path, const HttpRequest& saved_request,
22- const RouteConfig& config)
23- : path_(path),
24- saved_request_(saved_request),
25- config_(config),
26- headers_off_(0 ),
27- output_body_off_(0 ),
28- bytes_written_body_(0 ),
29- body_length_(saved_request.content_length),
30- headers_parsed_(false ),
31- headers_sent_(false ),
32- eob_reached_(false ),
33- eof_reached_(false ),
34- eoo_reached_(false ),
35- forced_response_(false ),
36- child_reaped_(false ),
37- pid_(-1 )
21+ void CgiHandler::init_state ()
3822{
39- // setting default values for pipes
4023 input_fd_[0 ] = -1 ;
4124 input_fd_[1 ] = -1 ;
4225 output_fd_[0 ] = -1 ;
4326 output_fd_[1 ] = -1 ;
27+ }
4428
45- // STEP 0 - Check that file exisst and is executable
29+ bool CgiHandler::validate_cgi_target (bool is_interpreter_cgi)
30+ {
4631 struct stat sb;
47- bool is_interpreter_cgi = !config_.config .cgi .exec_path .empty ();
4832
4933 // Script must exist and be a regular file
50- if (stat (path .c_str (), &sb) == -1 || !S_ISREG (sb.st_mode )) {
34+ if (stat (path_ .c_str (), &sb) == -1 || !S_ISREG (sb.st_mode )) {
5135 set_res_and_quit (HttpResponse::kStatusForbidden );
52- return ;
36+ return false ;
5337 }
5438
5539 if (!is_interpreter_cgi) {
5640 // Direct CGI → script itself must be executable
57- if (access (path .c_str (), X_OK) != 0 ) {
41+ if (access (path_ .c_str (), X_OK) != 0 ) {
5842 set_res_and_quit (HttpResponse::kStatusForbidden );
59- return ;
43+ return false ;
6044 }
6145 }
6246 else {
6347 // Interpreter CGI → interpreter must be executable
6448 if (access (config_.config .cgi .exec_path .c_str (), X_OK) != 0 ) {
6549 set_res_and_quit (HttpResponse::kStatusInternalServerError );
66- return ;
50+ return false ;
6751 }
6852 }
53+ return true ;
54+ }
6955
70- // STEP 1 - Prepare key=value pair vector to be passed to child process to set env var
71- std::vector<std::string> env_strings = build_env_strings ();
72- std::vector<char *> envp = make_envp (env_strings);
73- char * argv[3 ];
56+ static std::vector<char *> make_envp (std::vector<std::string>& env)
57+ {
58+ std::vector<char *> envp;
59+ envp.reserve (env.size () + 1 );
60+
61+ for (size_t i = 0 ; i < env.size (); ++i)
62+ envp.push_back (const_cast <char *>(env[i].c_str ()));
63+
64+ envp.push_back (NULL );
65+ return envp;
66+ }
67+
68+ std::vector<std::string> CgiHandler::build_env_strings () const
69+ {
70+ std::vector<std::string> env;
71+
72+ env.push_back (" REQUEST_METHOD=" + saved_request_.method );
73+ env.push_back (" SERVER_PROTOCOL=HTTP/1.1" );
74+ env.push_back (" PATH_INFO=/" );
75+ env.push_back (" SCRIPT_NAME=" + saved_request_.path );
76+ env.push_back (" QUERY_STRING=" + saved_request_.query_string );
77+ env.push_back (" CONTENT_LENGTH=" + to_string (saved_request_.content_length ));
78+ if (saved_request_.method == " POST" ) {
79+ env.push_back (" CONTENT_TYPE=application/x-www-form-urlencoded" );
80+ }
81+ env.push_back (" SERVER_NAME=localhost" ); // to be updated based on config
82+
83+ env.push_back (" SERVER_PORT=" + to_string (WEBSERV_DEFAULT_PORT));
84+ env.push_back (" PATH=/usr/bin:/bin" );
85+
86+ env.push_back (" GATEWAY_INTERFACE=CGI/1.1" );
87+
88+ env.push_back (" REQUEST_URI=" + saved_request_.path +
89+ (saved_request_.query_string .empty () ? " " : " ?" + saved_request_.query_string ));
90+ env.push_back (" REDIRECT_STATUS=200" ); // VERY IMPORTANT
91+
92+ LOG (DEBUG) << " saved_request_.path = " << saved_request_.path ;
93+
94+ for (size_t i = 0 ; i < env.size (); ++i) {
95+ LOG (DEBUG) << " CGI ENV: " << env[i];
96+ }
97+
98+ return (env);
99+ }
100+
101+ void CgiHandler::build_exec_context (char ** argv, bool is_interpreter_cgi)
102+ {
103+ env_strings_ = build_env_strings ();
104+ envp_ = make_envp (env_strings_);
74105
75106 if (is_interpreter_cgi) {
76107 // Interpreter-based CGI
@@ -83,20 +114,25 @@ CgiHandler::CgiHandler(const std::string& path, const HttpRequest& saved_request
83114 argv[0 ] = const_cast <char *>(path_.c_str ());
84115 argv[1 ] = NULL ;
85116 }
117+ }
86118
87- // STEP 2 - set up pipes
119+ bool CgiHandler::setup_pipes ()
120+ {
88121 if (saved_request_.method == " POST" ) {
89122 if (pipe (input_fd_) == -1 ) {
90123 set_res_and_quit (HttpResponse::kStatusInternalServerError );
91- return ;
124+ return false ;
92125 }
93126 }
94127 if (pipe (output_fd_) == -1 ) {
95128 set_res_and_quit (HttpResponse::kStatusInternalServerError );
96- return ;
129+ return false ;
97130 }
131+ return true ;
132+ }
98133
99- // STEP 3 - Fork child process
134+ void CgiHandler::spawn_child (char ** argv)
135+ {
100136 pid_ = fork ();
101137 if (pid_ == -1 ) {
102138 set_res_and_quit (HttpResponse::kStatusInternalServerError );
@@ -114,7 +150,7 @@ CgiHandler::CgiHandler(const std::string& path, const HttpRequest& saved_request
114150 close (output_fd_[0 ]);
115151 dup2 (output_fd_[1 ], STDOUT_FILENO);
116152 close (output_fd_[1 ]); // as now duplicated to STDOUT
117- execve (argv[0 ], argv, envp .data ());
153+ execve (argv[0 ], argv, envp_ .data ());
118154 // execve(argv[0], argv, envp.data());
119155 _exit (1 );
120156 }
@@ -131,6 +167,44 @@ CgiHandler::CgiHandler(const std::string& path, const HttpRequest& saved_request
131167 }
132168}
133169
170+ CgiHandler::CgiHandler (const std::string& path, const HttpRequest& saved_request,
171+ const RouteConfig& config)
172+ : path_(path),
173+ saved_request_(saved_request),
174+ config_(config),
175+ headers_off_(0 ),
176+ output_body_off_(0 ),
177+ bytes_written_body_(0 ),
178+ body_length_(saved_request.content_length),
179+ headers_parsed_(false ),
180+ headers_sent_(false ),
181+ eob_reached_(false ),
182+ eof_reached_(false ),
183+ eoo_reached_(false ),
184+ forced_response_(false ),
185+ child_reaped_(false ),
186+ pid_(-1 )
187+ {
188+ // STEP 0 - Set pipe default values
189+ init_state ();
190+
191+ // STEP 1 - Check that file exisst and is executable
192+ bool is_interpreter_cgi = !config_.config .cgi .exec_path .empty ();
193+ if (validate_cgi_target (is_interpreter_cgi) != true )
194+ return ;
195+
196+ // STEP 2 - prepare environment variables and argv for child process
197+ char * argv[3 ];
198+ build_exec_context (argv, is_interpreter_cgi);
199+
200+ // STEP 3 - set up pipes
201+ if (setup_pipes () != true )
202+ return ;
203+
204+ // STEP 4 - Fork child process
205+ spawn_child (argv);
206+ }
207+
134208CgiHandler::~CgiHandler ()
135209{
136210 if (input_fd_[1 ] != -1 ) {
@@ -152,51 +226,6 @@ void CgiHandler::set_res_and_quit(HttpResponse::Status status)
152226 forced_response_ = true ;
153227}
154228
155- static std::vector<char *> make_envp (std::vector<std::string>& env)
156- {
157- std::vector<char *> envp;
158- envp.reserve (env.size () + 1 );
159-
160- for (size_t i = 0 ; i < env.size (); ++i)
161- envp.push_back (const_cast <char *>(env[i].c_str ()));
162-
163- envp.push_back (NULL );
164- return envp;
165- }
166-
167- std::vector<std::string> CgiHandler::build_env_strings () const
168- {
169- std::vector<std::string> env;
170-
171- env.push_back (" REQUEST_METHOD=" + saved_request_.method );
172- env.push_back (" SERVER_PROTOCOL=HTTP/1.1" );
173- env.push_back (" PATH_INFO=/" );
174- env.push_back (" SCRIPT_NAME=" + saved_request_.path );
175- env.push_back (" QUERY_STRING=" + saved_request_.query_string );
176- env.push_back (" CONTENT_LENGTH=" + to_string (saved_request_.content_length ));
177- if (saved_request_.method == " POST" ) {
178- env.push_back (" CONTENT_TYPE=application/x-www-form-urlencoded" );
179- }
180- env.push_back (" SERVER_NAME=localhost" ); // to be updated based on config
181-
182- env.push_back (" SERVER_PORT=" + to_string (WEBSERV_DEFAULT_PORT));
183- env.push_back (" PATH=/usr/bin:/bin" );
184-
185- env.push_back (" GATEWAY_INTERFACE=CGI/1.1" );
186-
187- env.push_back (" REQUEST_URI=" + saved_request_.path +
188- (saved_request_.query_string .empty () ? " " : " ?" + saved_request_.query_string ));
189- env.push_back (" REDIRECT_STATUS=200" ); // VERY IMPORTANT
190-
191- LOG (DEBUG) << " saved_request_.path = " << saved_request_.path ;
192-
193- for (size_t i = 0 ; i < env.size (); ++i) {
194- LOG (DEBUG) << " CGI ENV: " << env[i];
195- }
196-
197- return (env);
198- }
199-
200229bool CgiHandler::child_reaped (void ) const
201230{
202231 if (child_reaped_) {
0 commit comments