196 if(ev_pollin(sn->ev, handler->process.out[0], readev)) { |
196 if(ev_pollin(sn->ev, handler->process.out[0], readev)) { |
197 log_ereport(LOG_FAILURE, "send-cgi: stdout ev_pollin failed"); |
197 log_ereport(LOG_FAILURE, "send-cgi: stdout ev_pollin failed"); |
198 error = 1; |
198 error = 1; |
199 } |
199 } |
200 |
200 |
|
201 handler->wait_read = TRUE; |
201 handler->events = 2; // 2 events (stdout, stderr) |
202 handler->events = 2; // 2 events (stdout, stderr) |
202 |
203 |
203 if(error) { |
204 if(error) { |
204 log_ereport(LOG_FAILURE, "cgi-send: kill script: %s", path); |
205 log_ereport(LOG_FAILURE, "cgi-send: kill script: %s", path); |
205 kill(handler->process.pid, SIGKILL); |
206 kill(handler->process.pid, SIGKILL); |
220 handler->writebuf + handler->writebuf_pos, |
221 handler->writebuf + handler->writebuf_pos, |
221 handler->writebuf_size - handler->writebuf_pos)) |
222 handler->writebuf_size - handler->writebuf_pos)) |
222 > 0) |
223 > 0) |
223 { |
224 { |
224 handler->writebuf_pos += wr; |
225 handler->writebuf_pos += wr; |
225 } |
226 handler->count_write += wr; |
226 if(wr < 0) { |
227 } |
227 if(errno != EWOULDBLOCK) { |
228 if(handler->writebuf_size - handler->writebuf_pos > 0) { |
|
229 if(net_errno(sn->csd) != EWOULDBLOCK) { |
228 handler->result = REQ_ABORTED; |
230 handler->result = REQ_ABORTED; |
229 } |
231 } |
230 handler->wait_write = TRUE; |
232 |
231 return 1; |
233 return 1; |
232 } else { |
|
233 handler->wait_write = FALSE; |
|
234 } |
234 } |
235 return 0; |
235 return 0; |
236 } |
236 } |
237 |
237 |
238 static int cgi_try_write(CGIHandler *handler, EventHandler *ev, Session *sn, char *buf, size_t size) { |
238 static int cgi_try_write(CGIHandler *handler, EventHandler *ev, Session *sn, char *buf, size_t size) { |
239 handler->wait_write = FALSE; |
239 |
240 size_t pos = 0; |
240 size_t pos = 0; |
241 ssize_t wr = 0; |
241 ssize_t wr = 0; |
242 while(size - pos > 0 && (wr = net_write(sn->csd, buf + pos, size - pos)) > 0) { |
242 while(size - pos > 0 && (wr = net_write(sn->csd, buf + pos, size - pos)) > 0) { |
243 pos += wr; |
243 pos += wr; |
244 } |
244 handler->count_write += wr; |
245 if(wr < 0) { |
245 } |
246 if(errno == EWOULDBLOCK) { |
246 |
|
247 if(pos < size) { |
|
248 if(net_errno(sn->csd) == EWOULDBLOCK) { |
247 // copy remaining bytes to the write buffer |
249 // copy remaining bytes to the write buffer |
248 // we assume there are no remaining bytes in writebuf |
250 // we assume there are no remaining bytes in writebuf |
249 size_t remaining = size-pos; |
251 size_t remaining = size-pos; |
250 if(remaining > handler->writebuf_alloc) { |
252 if(remaining > handler->writebuf_alloc) { |
251 handler->writebuf_alloc = size > 4096 ? size : 4096; |
253 handler->writebuf_alloc = size > 4096 ? size : 4096; |
266 return 0; |
268 return 0; |
267 } |
269 } |
268 handler->events++; |
270 handler->events++; |
269 handler->poll_out = TRUE; |
271 handler->poll_out = TRUE; |
270 } |
272 } |
271 handler->wait_write = TRUE; |
|
272 } else { |
273 } else { |
273 handler->result = REQ_ABORTED; |
274 handler->result = REQ_ABORTED; |
274 log_ereport(LOG_FAILURE, "cgi_try_write: %s", strerror(errno)); |
275 log_ereport(LOG_FAILURE, "cgi_try_write: %s", strerror(net_errno(sn->csd))); |
275 } |
276 } |
276 return 1; |
277 return 1; |
277 } |
278 } |
278 |
279 |
279 return 0; |
280 return 0; |
280 } |
281 } |
281 |
282 |
282 int cgi_stdout_readevent(EventHandler *ev, Event *event) { |
283 int cgi_stdout_readevent(EventHandler *ev, Event *event) { |
283 CGIHandler *handler = event->cookie; |
284 CGIHandler *handler = event->cookie; |
284 |
285 |
285 return cgi_read_output(handler, ev); |
286 int ret = cgi_read_output(handler, ev); |
|
287 if(ret == 0) { |
|
288 handler->wait_read = FALSE; |
|
289 } |
|
290 return ret; |
286 } |
291 } |
287 |
292 |
288 int cgi_writeevent(EventHandler *ev, Event *event) { |
293 int cgi_writeevent(EventHandler *ev, Event *event) { |
289 CGIHandler *handler = event->cookie; |
294 CGIHandler *handler = event->cookie; |
290 |
295 |
291 // cgi_read_output will try to flush the buffer |
296 // cgi_read_output will try to flush the buffer |
292 return cgi_read_output(handler, ev); |
297 int ret = cgi_read_output(handler, ev); |
|
298 if(ret == 0) { |
|
299 handler->poll_out = FALSE; |
|
300 } |
|
301 return ret; |
293 } |
302 } |
294 |
303 |
295 |
304 |
296 |
305 |
297 int cgi_read_output(CGIHandler *handler, EventHandler *ev) { |
306 int cgi_read_output(CGIHandler *handler, EventHandler *ev) { |
301 |
310 |
302 // try to flush handler->writebuf |
311 // try to flush handler->writebuf |
303 // if writebuf is empty, this does nothing and returns 0 |
312 // if writebuf is empty, this does nothing and returns 0 |
304 if(cgi_try_write_flush(handler, sn)) { |
313 if(cgi_try_write_flush(handler, sn)) { |
305 if(handler->result == REQ_ABORTED) { |
314 if(handler->result == REQ_ABORTED) { |
306 log_ereport(LOG_DEBUG, "cgi-send: req: %p write failed: %s: abort", strerror(errno), rq); |
315 log_ereport(LOG_DEBUG, "cgi-send: req: %p write failed: %s: abort", handler->parser->rq, strerror(net_errno(sn->csd)), rq); |
307 return 0; |
316 return 0; |
308 } else { |
317 } else { |
309 return 1; |
318 return 1; |
310 } |
319 } |
311 } |
320 } |
471 CGIHandler *handler = event->cookie; |
480 CGIHandler *handler = event->cookie; |
472 CGIResponseParser *parser = handler->parser; |
481 CGIResponseParser *parser = handler->parser; |
473 Session *sn = parser->sn; |
482 Session *sn = parser->sn; |
474 Request *rq = parser->rq; |
483 Request *rq = parser->rq; |
475 |
484 |
|
485 char *event_fn = ""; |
|
486 if(event->fn == cgi_stdout_readevent) { |
|
487 event_fn = "stdout"; |
|
488 } else if(event->fn == cgi_stderr_readevent) { |
|
489 event_fn = "stderr"; |
|
490 } else if(event->fn == cgi_writeevent) { |
|
491 event_fn = "httpout"; |
|
492 } |
|
493 log_ereport(LOG_DEBUG, "cgi-send: req: %p finish: event: %d pollout: %d cgi_eof: %d fn: %s", rq, handler->events, handler->poll_out, handler->cgi_eof, event_fn); |
|
494 |
476 if(--handler->events > 0) { |
495 if(--handler->events > 0) { |
477 if(handler->events == 1 && handler->poll_out && !handler->wait_write) { |
496 if(handler->events == 1) { |
478 // write event registered, however it will not be activated anymore |
497 if(handler->poll_out) { |
479 // we can safely remove the event |
498 // write event registered, however it will not be activated anymore |
480 if(event_removepoll(ev, sn->csd)) { |
499 // we can safely remove the event |
481 log_ereport(LOG_FAILURE, "cgi_event_finish: event_removepoll: %s", strerror(errno)); |
500 log_ereport(LOG_DEBUG, "cgi-send: req: %p finish: event: 1 remove-poll write", rq); |
|
501 if(event_removepoll(ev, sn->csd)) { |
|
502 log_ereport(LOG_FAILURE, "cgi_event_finish: event_removepoll: %s", strerror(errno)); |
|
503 } |
|
504 } else if(handler->cgi_eof && handler->wait_read) { |
|
505 log_ereport(LOG_DEBUG, "cgi-send: req: %p finish: event: 1 remove-poll read", rq); |
|
506 if(ev_remove_poll(ev, handler->process.out[0])) { |
|
507 log_ereport(LOG_FAILURE, "cgi_event_finish: ev_remove_poll: %s", strerror(errno)); |
|
508 } |
|
509 } else { |
|
510 return 0; |
482 } |
511 } |
483 } else { |
512 } else { |
484 return 0; |
513 return 0; |
485 } |
514 } |
486 } |
515 } |
488 if(handler->result == REQ_ABORTED && handler->process.pid != 0) { |
517 if(handler->result == REQ_ABORTED && handler->process.pid != 0) { |
489 log_ereport(LOG_FAILURE, "cgi-send: kill script: %s", handler->path); |
518 log_ereport(LOG_FAILURE, "cgi-send: kill script: %s", handler->path); |
490 killpg(handler->process.pid, SIGTERM); |
519 killpg(handler->process.pid, SIGTERM); |
491 } |
520 } |
492 |
521 |
493 log_ereport(LOG_DEBUG, "cgi-send: req: %p cgi_close", rq); |
522 log_ereport(LOG_DEBUG, "cgi-send: req: %p cgi_close", rq); |
494 |
523 |
495 int exit_code = cgi_close(&handler->process); |
524 int exit_code = cgi_close(&handler->process); |
496 if(exit_code != 0) { |
525 if(exit_code != 0) { |
497 log_ereport(LOG_FAILURE, "send-cgi: script: %s exited with code %d", handler->path, exit_code); |
526 log_ereport(LOG_FAILURE, "send-cgi: script: %s exited with code %d", handler->path, exit_code); |
498 handler->result = REQ_ABORTED; |
527 handler->result = REQ_ABORTED; |
499 } |
528 } |
500 |
529 |
501 cgi_parser_free(parser); |
530 cgi_parser_free(parser); |
502 |
531 |
|
532 WSBool response_length_error = FALSE; |
503 // check if content-length set by the cgi script matches the number |
533 // check if content-length set by the cgi script matches the number |
504 // of writes, that were written to the stream |
534 // of writes, that were written to the stream |
505 // this ensures, that broken cgi scripts don't break the connection |
535 // this ensures, that broken cgi scripts don't break the connection |
506 char *ctlen_header = pblock_findkeyval(pb_key_content_length, rq->srvhdrs); |
536 char *ctlen_header = pblock_findkeyval(pb_key_content_length, rq->srvhdrs); |
507 if(ctlen_header) { |
537 if(ctlen_header) { |
510 if(ctlenhdr != parser->response_length) { |
540 if(ctlenhdr != parser->response_length) { |
511 log_ereport( |
541 log_ereport( |
512 LOG_FAILURE, |
542 LOG_FAILURE, |
513 "cgi-send: script: %s: content length mismatch", |
543 "cgi-send: script: %s: content length mismatch", |
514 handler->path); |
544 handler->path); |
515 rq->rq_attr.keep_alive = 0; |
545 response_length_error = TRUE; |
516 handler->result = REQ_ABORTED; |
546 } |
517 } |
547 } |
518 } |
548 } |
|
549 // make sure we haven't lost any bytes |
|
550 // should not happen unless the non-blocking IO code is buggy |
|
551 if(handler->result != REQ_ABORTED && handler->parser->response_length != handler->count_write) { |
|
552 log_ereport( |
|
553 LOG_FAILURE, |
|
554 "cgi-send: script: %s: IO error: cgi response length != http response length", |
|
555 handler->path); |
|
556 response_length_error = TRUE; |
|
557 } |
|
558 |
|
559 // if the response length is broken, we must close the connection |
|
560 if(response_length_error) { |
|
561 rq->rq_attr.keep_alive = 0; |
|
562 handler->result = REQ_ABORTED; |
519 } |
563 } |
520 |
564 |
521 net_setnonblock(sn->csd, 0); |
565 net_setnonblock(sn->csd, 0); |
522 |
566 |
523 // return to nsapi loop |
567 // return to nsapi loop |