src/server/safs/cgi.c

changeset 502
11ac3761c0e3
parent 501
2aa6bd9f166f
child 503
aeaf7db26fac
equal deleted inserted replaced
501:2aa6bd9f166f 502:11ac3761c0e3
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 }
373 } 382 }
374 } 383 }
375 if(r < 0 && errno == EWOULDBLOCK) { 384 if(r < 0 && errno == EWOULDBLOCK) {
376 return 1; 385 return 1;
377 } 386 }
378 387 handler->cgi_eof = TRUE;
379 return 0; 388 return 0;
380 } 389 }
381 390
382 int cgi_stderr_readevent(EventHandler *ev, Event *event) { 391 int cgi_stderr_readevent(EventHandler *ev, Event *event) {
383 CGIHandler *handler = event->cookie; 392 CGIHandler *handler = event->cookie;
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

mercurial