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 |