78 |
79 |
79 param_free(pblock_remove("content-type", rq->srvhdrs)); |
80 param_free(pblock_remove("content-type", rq->srvhdrs)); |
80 |
81 |
81 const char *args = pblock_findval("query", rq->reqpb); |
82 const char *args = pblock_findval("query", rq->reqpb); |
82 char **argv = cgi_create_argv(path, NULL, args); |
83 char **argv = cgi_create_argv(path, NULL, args); |
|
84 if(!argv) { |
|
85 return REQ_ABORTED; |
|
86 } |
83 |
87 |
84 char **env = http_hdrs2env(rq->headers); |
88 char **env = http_hdrs2env(rq->headers); |
85 env = cgi_common_vars(sn, rq, env); |
89 env = cgi_common_vars(sn, rq, env); |
86 env = cgi_specific_vars(sn, rq, args, env, 1); |
90 env = cgi_specific_vars(sn, rq, args, env, 1); |
87 |
91 |
88 CGIProcess cgip; |
92 // event handler object for non-blocking io event handler |
89 int ret = cgi_start(&cgip, path, argv, env); |
93 CGIHandler *handler = pool_malloc(sn->pool, sizeof(CGIHandler)); |
|
94 if(!handler) { |
|
95 return REQ_ABORTED; |
|
96 } |
|
97 ZERO(handler, sizeof(CGIHandler)); |
|
98 handler->path = path; |
|
99 |
|
100 int ret = cgi_start(&handler->process, path, argv, env); |
90 if(ret != REQ_PROCEED) { |
101 if(ret != REQ_PROCEED) { |
91 util_env_free(env); |
102 util_env_free(env); |
92 cgi_free_argv(argv); |
103 cgi_free_argv(argv); |
93 return ret; |
104 return ret; |
94 } |
105 } |
95 |
106 |
96 util_env_free(env); |
107 util_env_free(env); |
97 cgi_free_argv(argv); |
108 cgi_free_argv(argv); |
98 |
109 |
99 char buf[4096]; // I/O buffer |
110 char buf[4096]; // I/O buffer |
100 ssize_t r; |
111 ssize_t r; |
101 |
112 |
102 if(content_length > 0) { |
113 if(content_length > 0) { |
103 ssize_t n = 0; |
114 ssize_t n = 0; |
107 // TODO: handle error |
118 // TODO: handle error |
108 log_ereport( |
119 log_ereport( |
109 LOG_FAILURE, |
120 LOG_FAILURE, |
110 "send-cgi: script: %s: cannot read request body", |
121 "send-cgi: script: %s: cannot read request body", |
111 path); |
122 path); |
112 kill(cgip.pid, SIGTERM); |
123 kill(handler->process.pid, SIGTERM); |
113 cgi_close(&cgip); |
124 cgi_close(&handler->process); |
114 return REQ_ABORTED; |
125 return REQ_ABORTED; |
115 } |
126 } |
116 ssize_t w = write(cgip.in[1], buf, r); |
127 ssize_t w = write(handler->process.in[1], buf, r); |
117 if(w <= 0) { |
128 if(w <= 0) { |
118 // TODO: handle error |
129 // TODO: handle error |
119 log_ereport( |
130 log_ereport( |
120 LOG_FAILURE, |
131 LOG_FAILURE, |
121 "send-cgi: script: %s: cannot send request body to cgi process", |
132 "send-cgi: script: %s: cannot send request body to cgi process", |
122 path); |
133 path); |
123 kill(cgip.pid, SIGKILL); |
134 kill(handler->process.pid, SIGKILL); |
124 cgi_close(&cgip); |
135 cgi_close(&handler->process); |
125 return REQ_ABORTED; |
136 return REQ_ABORTED; |
126 } |
137 } |
127 n += r; |
138 n += r; |
128 } |
139 } |
129 } |
140 } |
130 system_close(cgip.in[1]); |
141 system_close(handler->process.in[1]); |
131 cgip.in[1] = -1; |
142 handler->process.in[1] = -1; |
132 |
143 |
133 // read from child |
144 handler->parser = cgi_parser_new(sn, rq); |
134 CGIResponseParser *parser = cgi_parser_new(sn, rq); |
145 |
135 WSBool cgiheader = TRUE; |
146 Event *readev = pool_malloc(sn->pool, sizeof(Event)); |
|
147 ZERO(readev, sizeof(Event)); |
|
148 readev->cookie = handler; |
|
149 readev->fn = cgi_stdout_readevent; |
|
150 readev->finish = cgi_event_finish; |
|
151 |
|
152 Event *stderr_readev = pool_malloc(sn->pool, sizeof(Event)); |
|
153 ZERO(stderr_readev, sizeof(Event)); |
|
154 stderr_readev->cookie = handler; |
|
155 stderr_readev->fn = cgi_stderr_readevent; |
|
156 stderr_readev->finish = NULL; |
|
157 |
|
158 Event *writeev = pool_malloc(sn->pool, sizeof(Event)); |
|
159 ZERO(writeev, sizeof(Event)); |
|
160 writeev->cookie = handler; |
|
161 // TODO: fn |
|
162 |
|
163 handler->writeev = writeev; |
|
164 |
|
165 // add poll events for cgi stdout/stderr |
|
166 int error = 0; |
|
167 if(ev_pollin(sn->ev, handler->process.err[0], stderr_readev)) { |
|
168 log_ereport(LOG_FAILURE, "send-cgi: stderr ev_pollin failed"); |
|
169 error = 1; |
|
170 } |
|
171 if(ev_pollin(sn->ev, handler->process.out[0], readev)) { |
|
172 log_ereport(LOG_FAILURE, "send-cgi: stdout ev_pollin failed"); |
|
173 error = 1; |
|
174 } |
|
175 |
|
176 if(error) { |
|
177 log_ereport(LOG_FAILURE, "cgi-send: kill script: %s", path); |
|
178 kill(handler->process.pid, SIGKILL); |
|
179 cgi_parser_free(handler->parser); |
|
180 return REQ_ABORTED; |
|
181 } |
|
182 |
|
183 return REQ_PROCESSING; |
|
184 } |
|
185 |
|
186 int cgi_stdout_readevent(EventHandler *ev, Event *event) { |
|
187 CGIHandler *handler = event->cookie; |
|
188 CGIResponseParser *parser = handler->parser; |
|
189 Session *sn = parser->sn; |
|
190 Request *rq = parser->rq; |
|
191 |
|
192 char buf[4096]; // I/O buffer |
|
193 ssize_t r; |
136 ssize_t wr = 0; |
194 ssize_t wr = 0; |
137 int result = REQ_PROCEED; |
195 |
138 size_t response_length = 0; |
196 handler->result = REQ_PROCEED; |
139 while((r = read(cgip.out[0], buf, 4096)) > 0) { |
197 while((r = read(handler->process.out[0], buf, 4096)) > 0) { |
140 if(cgiheader) { |
198 if(parser->cgiheader) { |
141 size_t pos; |
199 size_t pos; |
142 ret = cgi_parse_response(parser, buf, r, &pos); |
200 int ret = cgi_parse_response(parser, buf, r, &pos); |
143 if(ret == -1) { |
201 if(ret == -1) { |
144 log_ereport( |
202 log_ereport( |
145 LOG_FAILURE, |
203 LOG_FAILURE, |
146 "broken cgi script response: path: %s", path); |
204 "broken cgi script response: path: %s", handler->path); |
147 protocol_status(sn, rq, 500, NULL); |
205 protocol_status(sn, rq, 500, NULL); |
148 result = REQ_ABORTED; |
206 handler->result = REQ_ABORTED; |
149 break; |
207 break; |
150 } else if(ret == 1) { |
208 } else if(ret == 1) { |
151 cgiheader = FALSE; |
209 parser->cgiheader = FALSE; |
152 if(parser->status > 0) { |
210 if(parser->status > 0) { |
153 protocol_status(sn, rq, parser->status, parser->msg); |
211 protocol_status(sn, rq, parser->status, parser->msg); |
154 } |
212 } |
155 http_start_response(sn, rq); |
213 http_start_response(sn, rq); |
156 if(pos < r) { |
214 if(pos < r) { |
157 response_length += r-pos; |
215 parser->response_length += r-pos; |
158 wr = net_write(sn->csd, &buf[pos], r-pos); |
216 wr = net_write(sn->csd, &buf[pos], r-pos); |
159 if(wr <= 0) { |
217 if(wr <= 0) { |
160 result = REQ_ABORTED; |
218 handler->result = REQ_ABORTED; |
161 break; |
219 break; |
162 } |
220 } |
163 } |
221 } |
164 } |
222 } |
165 } else { |
223 } else { |
166 response_length += r; |
224 parser->response_length += r; |
167 wr = net_write(sn->csd, buf, r); |
225 wr = net_write(sn->csd, buf, r); |
168 if(wr <= 0) { |
226 if(wr <= 0) { |
169 result = REQ_ABORTED; |
227 handler->result = REQ_ABORTED; |
170 break; |
228 break; |
171 } |
229 } |
172 } |
230 } |
|
231 } |
|
232 if(r < 0 && errno == EWOULDBLOCK) { |
|
233 event->events = EVENT_POLLIN; |
|
234 return 1; |
173 } |
235 } |
174 |
236 |
175 char *ctlen_header = pblock_findkeyval(pb_key_content_length, rq->srvhdrs); |
237 char *ctlen_header = pblock_findkeyval(pb_key_content_length, rq->srvhdrs); |
176 if(ctlen_header) { |
238 if(ctlen_header) { |
177 int64_t ctlenhdr; |
239 int64_t ctlenhdr; |
178 if(util_strtoint(ctlen_header, &ctlenhdr)) { |
240 if(util_strtoint(ctlen_header, &ctlenhdr)) { |
179 if(ctlenhdr != response_length) { |
241 if(ctlenhdr != parser->response_length) { |
180 log_ereport( |
242 log_ereport( |
181 LOG_FAILURE, |
243 LOG_FAILURE, |
182 "cgi-send: script: %s: content length mismatch", |
244 "cgi-send: script: %s: content length mismatch", |
183 path); |
245 handler->path); |
184 rq->rq_attr.keep_alive = 0; |
246 rq->rq_attr.keep_alive = 0; |
185 result = REQ_ABORTED; |
247 handler->result = REQ_ABORTED; |
186 } |
248 } |
187 } |
249 } |
188 } |
250 } |
189 |
251 |
190 if(result == REQ_ABORTED) { |
252 return 0; |
191 log_ereport(LOG_FAILURE, "cgi-send: kill script: %s", path); |
253 } |
192 kill(cgip.pid, SIGKILL); |
254 |
193 } |
255 int cgi_stderr_readevent(EventHandler *ev, Event *event) { |
194 |
256 CGIHandler *handler = event->cookie; |
195 int exit_code = cgi_close(&cgip); |
257 |
|
258 char buf[4096]; |
|
259 ssize_t r = read(handler->process.err[0], buf, 4096); |
|
260 log_ereport(LOG_INFORM, "cgi pid %d %s stderr: %.*s", (int)handler->process.pid, handler->path, (int)r, buf); |
|
261 |
|
262 return 0; |
|
263 } |
|
264 |
|
265 int cgi_event_finish(EventHandler *ev, Event *event) { |
|
266 CGIHandler *handler = event->cookie; |
|
267 CGIResponseParser *parser = handler->parser; |
|
268 Session *sn = parser->sn; |
|
269 Request *rq = parser->rq; |
|
270 |
|
271 if(handler->result == REQ_ABORTED) { |
|
272 log_ereport(LOG_FAILURE, "cgi-send: kill script: %s", handler->path); |
|
273 kill(handler->process.pid, SIGKILL); |
|
274 } |
|
275 |
|
276 int exit_code = cgi_close(&handler->process); |
196 if(exit_code != 0) { |
277 if(exit_code != 0) { |
197 log_ereport(LOG_FAILURE, "send-cgi: script: %s exited with code %d", path, exit_code); |
278 log_ereport(LOG_FAILURE, "send-cgi: script: %s exited with code %d", handler->path, exit_code); |
198 ret = REQ_ABORTED; |
279 handler->result = REQ_ABORTED; |
199 } |
280 } |
200 |
281 |
201 cgi_parser_free(parser); |
282 cgi_parser_free(parser); |
202 return result; |
283 // return to nsapi loop |
|
284 nsapi_function_return(sn, rq, handler->result); |
|
285 return 0; |
203 } |
286 } |
204 |
287 |
205 int cgi_start(CGIProcess *p, char *path, char *const argv[], char *const envp[]) { |
288 int cgi_start(CGIProcess *p, char *path, char *const argv[], char *const envp[]) { |
206 if(pipe(p->in) || pipe(p->out)) { |
289 if(pipe(p->in) || pipe(p->out) || pipe(p->err)) { |
207 log_ereport( |
290 log_ereport( |
208 LOG_FAILURE, |
291 LOG_FAILURE, |
209 "send-cgi: cannot create pipe: %s", |
292 "send-cgi: cannot create pipe: %s", |
210 strerror(errno)); |
293 strerror(errno)); |
211 return REQ_ABORTED; |
294 return REQ_ABORTED; |