src/server/safs/cgi.c

changeset 433
39fe86ae4db0
parent 431
032b0ad35ee3
child 450
d7b276de183b
equal deleted inserted replaced
432:7c9137f9e7f9 433:39fe86ae4db0
177 // TODO: fn 177 // TODO: fn
178 178
179 handler->writeev = writeev; 179 handler->writeev = writeev;
180 handler->stderrev = stderr_readev; 180 handler->stderrev = stderr_readev;
181 181
182 net_setnonblock(sn->csd, 1);
183
182 // add poll events for cgi stdout/stderr 184 // add poll events for cgi stdout/stderr
183 int error = 0; 185 int error = 0;
184 if(ev_pollin(sn->ev, handler->process.err[0], stderr_readev)) { 186 if(ev_pollin(sn->ev, handler->process.err[0], stderr_readev)) {
185 log_ereport(LOG_FAILURE, "send-cgi: stderr ev_pollin failed"); 187 log_ereport(LOG_FAILURE, "send-cgi: stderr ev_pollin failed");
186 error = 1; 188 error = 1;
198 } 200 }
199 201
200 return REQ_PROCESSING; 202 return REQ_PROCESSING;
201 } 203 }
202 204
205 static int cgi_try_write_flush(CGIHandler *handler, Session *sn) {
206 ssize_t wr = 0;
207 while(
208 handler->writebuf_size - handler->writebuf_pos > 0 &&
209 (wr = net_write(
210 sn->csd,
211 handler->writebuf + handler->writebuf_pos,
212 handler->writebuf_size - handler->writebuf_pos))
213 > 0)
214 {
215 handler->writebuf_pos += wr;
216 }
217 if(wr < 0) {
218 if(errno != EWOULDBLOCK) {
219 handler->result = REQ_ABORTED;
220 }
221 return 1;
222 }
223 return 0;
224 }
225
226 static int cgi_try_write(CGIHandler *handler, EventHandler *ev, Session *sn, char *buf, size_t size) {
227 size_t pos = 0;
228 ssize_t wr = 0;
229 while(size - pos > 0 && (wr = net_write(sn->csd, buf + pos, size - pos)) > 0) {
230 pos += wr;
231 }
232 if(wr < 0) {
233 if(errno == EWOULDBLOCK) {
234 // copy remaining bytes to the write buffer
235 // we assume there are no remaining bytes in writebuf
236 size_t remaining = size-pos;
237 if(remaining <= handler->writebuf_alloc) {
238 memcpy(handler->writebuf, buf+pos, remaining);
239 } else {
240 handler->writebuf_alloc = size > 4096 ? size : 4096;
241 handler->writebuf = pool_realloc(sn->pool, handler->writebuf, handler->writebuf_alloc);
242 if(!handler->writebuf) {
243 handler->result = REQ_ABORTED;
244 return 1;
245 }
246 }
247 handler->writebuf_size = remaining;
248 handler->writebuf_pos = 0;
249
250 // initialize poll, if it isn't already active
251 if(!handler->poll_out) {
252 if(event_pollout(ev, sn->csd, handler->writeev)) {
253 handler->result = REQ_ABORTED;
254 return 1;
255 }
256 handler->poll_out = TRUE;
257 }
258 return 1;
259 }
260 handler->result = REQ_ABORTED;
261 return 1;
262 }
263
264 return 0;
265 }
266
203 int cgi_stdout_readevent(EventHandler *ev, Event *event) { 267 int cgi_stdout_readevent(EventHandler *ev, Event *event) {
204 CGIHandler *handler = event->cookie; 268 CGIHandler *handler = event->cookie;
269
270 return cgi_read_output(handler, ev);
271 }
272
273 int cgi_writeevent(EventHandler *ev, Event *event) {
274 CGIHandler *handler = event->cookie;
275
276 // cgi_read_output will try to flush the buffer
277 return cgi_read_output(handler, ev);
278 }
279
280
281
282 int cgi_read_output(CGIHandler *handler, EventHandler *ev) {
205 CGIResponseParser *parser = handler->parser; 283 CGIResponseParser *parser = handler->parser;
206 Session *sn = parser->sn; 284 Session *sn = parser->sn;
207 Request *rq = parser->rq; 285 Request *rq = parser->rq;
208 286
287 // try to flush handler->writebuf
288 // if writebuf is empty, this does nothing and returns 0
289 if(cgi_try_write_flush(handler, sn)) {
290 return handler->result == REQ_ABORTED ? 0 : 1;
291 }
292
209 char buf[4096]; // I/O buffer 293 char buf[4096]; // I/O buffer
210 ssize_t r; 294 ssize_t r;
211 ssize_t wr = 0;
212 295
213 handler->result = REQ_PROCEED; 296 handler->result = REQ_PROCEED;
214 while((r = read(handler->process.out[0], buf, 4096)) > 0) { 297 while((r = read(handler->process.out[0], buf, 4096)) > 0) {
215 if(parser->cgiheader) { 298 if(parser->cgiheader) {
216 size_t pos; 299 size_t pos;
219 log_ereport( 302 log_ereport(
220 LOG_FAILURE, 303 LOG_FAILURE,
221 "broken cgi script response: path: %s", handler->path); 304 "broken cgi script response: path: %s", handler->path);
222 protocol_status(sn, rq, 500, NULL); 305 protocol_status(sn, rq, 500, NULL);
223 handler->result = REQ_ABORTED; 306 handler->result = REQ_ABORTED;
224 break; 307 return 0;
225 } else if(ret == 1) { 308 } else if(ret == 1) {
309 WS_ASSERT(pos <= r);
310
311 parser->response_length += r-pos;
312
226 parser->cgiheader = FALSE; 313 parser->cgiheader = FALSE;
227 if(parser->status > 0) { 314 if(parser->status > 0) {
228 protocol_status(sn, rq, parser->status, parser->msg); 315 protocol_status(sn, rq, parser->status, parser->msg);
229 } 316 }
230 http_start_response(sn, rq); 317
318 handler->response = http_create_response(sn, rq);
319 if(!handler->response) {
320 handler->result = REQ_ABORTED;
321 return 0;
322 }
323
324 int send_response = http_send_response(handler->response);
325 if(send_response < 0) {
326 handler->result = REQ_ABORTED;
327 break;
328 } else if(send_response == 1) {
329 // EWOULDBLOCK
330 if(!handler->poll_out) {
331 if(event_pollout(ev, sn->csd, handler->writeev)) {
332 handler->result = REQ_ABORTED;
333 return 0;
334 }
335 handler->poll_out = TRUE;
336 return 1;
337 }
338 } else {
339 handler->response = NULL;
340 }
341
231 if(pos < r) { 342 if(pos < r) {
232 parser->response_length += r-pos; 343 if(cgi_try_write(handler, ev, sn, &buf[pos], r-pos)) {
233 wr = net_write(sn->csd, &buf[pos], r-pos); 344 return handler->result == REQ_ABORTED ? 0 : 1;
234 if(wr <= 0) {
235 handler->result = REQ_ABORTED;
236 break;
237 } 345 }
238 } 346 }
239 } 347 }
240 } else { 348 } else {
241 parser->response_length += r; 349 parser->response_length += r;
242 wr = net_write(sn->csd, buf, r); 350 if(cgi_try_write(handler, ev, sn, buf, r)) {
243 if(wr <= 0) { 351 return handler->result == REQ_ABORTED ? 0 : 1;
244 handler->result = REQ_ABORTED;
245 break;
246 } 352 }
247 } 353 }
248 } 354 }
249 if(r < 0 && errno == EWOULDBLOCK) { 355 if(r < 0 && errno == EWOULDBLOCK) {
250 return 1; 356 return 1;
251 } 357 }
252 358
253 char *ctlen_header = pblock_findkeyval(pb_key_content_length, rq->srvhdrs); 359 handler->read_output_finished = TRUE;
254 if(ctlen_header) {
255 int64_t ctlenhdr;
256 if(util_strtoint(ctlen_header, &ctlenhdr)) {
257 if(ctlenhdr != parser->response_length) {
258 log_ereport(
259 LOG_FAILURE,
260 "cgi-send: script: %s: content length mismatch",
261 handler->path);
262 rq->rq_attr.keep_alive = 0;
263 handler->result = REQ_ABORTED;
264 }
265 }
266 }
267
268 return 0; 360 return 0;
269 } 361 }
270 362
271 int cgi_stderr_readevent(EventHandler *ev, Event *event) { 363 int cgi_stderr_readevent(EventHandler *ev, Event *event) {
272 CGIHandler *handler = event->cookie; 364 CGIHandler *handler = event->cookie;
335 int cgi_event_finish(EventHandler *ev, Event *event) { 427 int cgi_event_finish(EventHandler *ev, Event *event) {
336 CGIHandler *handler = event->cookie; 428 CGIHandler *handler = event->cookie;
337 CGIResponseParser *parser = handler->parser; 429 CGIResponseParser *parser = handler->parser;
338 Session *sn = parser->sn; 430 Session *sn = parser->sn;
339 Request *rq = parser->rq; 431 Request *rq = parser->rq;
340 432
341 if(handler->result == REQ_ABORTED) { 433 if(handler->result == REQ_ABORTED) {
342 log_ereport(LOG_FAILURE, "cgi-send: kill script: %s", handler->path); 434 log_ereport(LOG_FAILURE, "cgi-send: kill script: %s", handler->path);
343 kill(handler->process.pid, SIGKILL); 435 kill(handler->process.pid, SIGKILL);
344 } 436 }
345 437
347 // stderr handler is still active 439 // stderr handler is still active
348 // set stderr event finish function, to run the finish code later 440 // set stderr event finish function, to run the finish code later
349 handler->stderrev->finish = cgi_event_finish; 441 handler->stderrev->finish = cgi_event_finish;
350 return 0; 442 return 0;
351 } 443 }
444 if(handler->poll_out && !handler->send_response_finished) {
445 // send response is still active
446 handler->writeev->finish = cgi_event_finish;
447 return 0;
448 }
352 449
353 int exit_code = cgi_close(&handler->process); 450 int exit_code = cgi_close(&handler->process);
354 if(exit_code != 0) { 451 if(exit_code != 0) {
355 log_ereport(LOG_FAILURE, "send-cgi: script: %s exited with code %d", handler->path, exit_code); 452 log_ereport(LOG_FAILURE, "send-cgi: script: %s exited with code %d", handler->path, exit_code);
356 handler->result = REQ_ABORTED; 453 handler->result = REQ_ABORTED;
357 } 454 }
358 455
359 cgi_parser_free(parser); 456 cgi_parser_free(parser);
457
458 // check if content-length set by the cgi script matches the number
459 // of writes, that were written to the stream
460 // this ensures, that broken cgi scripts don't break the connection
461 char *ctlen_header = pblock_findkeyval(pb_key_content_length, rq->srvhdrs);
462 if(ctlen_header) {
463 int64_t ctlenhdr;
464 if(util_strtoint(ctlen_header, &ctlenhdr)) {
465 if(ctlenhdr != parser->response_length) {
466 log_ereport(
467 LOG_FAILURE,
468 "cgi-send: script: %s: content length mismatch",
469 handler->path);
470 rq->rq_attr.keep_alive = 0;
471 handler->result = REQ_ABORTED;
472 }
473 }
474 }
475
476 net_setnonblock(sn->csd, 0);
477
360 // return to nsapi loop 478 // return to nsapi loop
361 nsapi_function_return(sn, rq, handler->result); 479 nsapi_function_return(sn, rq, handler->result);
362 return 0; 480 return 0;
363 } 481 }
364 482

mercurial