initial AP2 support (win)
[mod_fastcgi.git] / mod_fastcgi.c
blob48892a3bec5b1a98712bb8572efaafe9e175cc20
1 /*
2 * mod_fastcgi.c --
4 * Apache server module for FastCGI.
6 * $Id: mod_fastcgi.c,v 1.133 2002/07/23 00:54:18 robs Exp $
8 * Copyright (c) 1995-1996 Open Market, Inc.
10 * See the file "LICENSE.TERMS" for information on usage and redistribution
11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14 * Patches for Apache-1.1 provided by
15 * Ralf S. Engelschall
16 * <rse@en.muc.de>
18 * Patches for Linux provided by
19 * Scott Langley
20 * <langles@vote-smart.org>
22 * Patches for suexec handling by
23 * Brian Grossman <brian@SoftHome.net> and
24 * Rob Saccoccio <robs@ipass.net>
28 * Module design notes.
30 * 1. Restart cleanup.
32 * mod_fastcgi spawns several processes: one process manager process
33 * and several application processes. None of these processes
34 * handle SIGHUP, so they just go away when the Web server performs
35 * a restart (as Apache does every time it starts.)
37 * In order to allow the process manager to properly cleanup the
38 * running fastcgi processes (without being disturbed by Apache),
39 * an intermediate process was introduced. The diagram is as follows;
41 * ApacheWS --> MiddleProc --> ProcMgr --> FCGI processes
43 * On a restart, ApacheWS sends a SIGKILL to MiddleProc and then
44 * collects it via waitpid(). The ProcMgr periodically checks for
45 * its parent (via getppid()) and if it does not have one, as in
46 * case when MiddleProc has terminated, ProcMgr issues a SIGTERM
47 * to all FCGI processes, waitpid()s on them and then exits, so it
48 * can be collected by init(1). Doing it any other way (short of
49 * changing Apache API), results either in inconsistent results or
50 * in generation of zombie processes.
52 * XXX: How does Apache 1.2 implement "gentle" restart
53 * that does not disrupt current connections? How does
54 * gentle restart interact with restart cleanup?
56 * 2. Request timeouts.
58 * Earlier versions of this module used ap_soft_timeout() rather than
59 * ap_hard_timeout() and ate FastCGI server output until it completed.
60 * This precluded the FastCGI server from having to implement a
61 * SIGPIPE handler, but meant hanging the application longer than
62 * necessary. SIGPIPE handler now must be installed in ALL FastCGI
63 * applications. The handler should abort further processing and go
64 * back into the accept() loop.
66 * Although using ap_soft_timeout() is better than ap_hard_timeout()
67 * we have to be more careful about SIGINT handling and subsequent
68 * processing, so, for now, make it hard.
72 #include "fcgi.h"
74 #ifndef timersub
75 #define timersub(a, b, result) \
76 do { \
77 (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
78 (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
79 if ((result)->tv_usec < 0) { \
80 --(result)->tv_sec; \
81 (result)->tv_usec += 1000000; \
82 } \
83 } while (0)
84 #endif
87 * Global variables
90 pool *fcgi_config_pool; /* the config pool */
91 server_rec *fcgi_apache_main_server;
93 const char *fcgi_wrapper = NULL; /* wrapper path */
94 uid_t fcgi_user_id; /* the run uid of Apache & PM */
95 gid_t fcgi_group_id; /* the run gid of Apache & PM */
97 fcgi_server *fcgi_servers = NULL; /* AppClasses */
99 char *fcgi_socket_dir = DEFAULT_SOCK_DIR; /* default FastCgiIpcDir */
101 char *fcgi_dynamic_dir = NULL; /* directory for the dynamic
102 * fastcgi apps' sockets */
104 #ifdef WIN32
106 #pragma warning( disable : 4706 4100 4127)
107 fcgi_pm_job *fcgi_dynamic_mbox = NULL;
108 HANDLE *fcgi_dynamic_mbox_mutex = NULL;
109 HANDLE fcgi_pm_thread = INVALID_HANDLE_VALUE;
111 #else
113 int fcgi_pm_pipe[2] = { -1, -1 };
114 pid_t fcgi_pm_pid = -1;
116 #endif
118 char *fcgi_empty_env = NULL;
120 u_int dynamicMaxProcs = FCGI_DEFAULT_MAX_PROCS;
121 int dynamicMinProcs = FCGI_DEFAULT_MIN_PROCS;
122 int dynamicMaxClassProcs = FCGI_DEFAULT_MAX_CLASS_PROCS;
123 u_int dynamicKillInterval = FCGI_DEFAULT_KILL_INTERVAL;
124 u_int dynamicUpdateInterval = FCGI_DEFAULT_UPDATE_INTERVAL;
125 float dynamicGain = FCGI_DEFAULT_GAIN;
126 int dynamicThreshold1 = FCGI_DEFAULT_THRESHOLD_1;
127 int dynamicThresholdN = FCGI_DEFAULT_THRESHOLD_N;
128 u_int dynamicPleaseStartDelay = FCGI_DEFAULT_START_PROCESS_DELAY;
129 u_int dynamicAppConnectTimeout = FCGI_DEFAULT_APP_CONN_TIMEOUT;
130 char **dynamicEnvp = &fcgi_empty_env;
131 u_int dynamicProcessSlack = FCGI_DEFAULT_PROCESS_SLACK;
132 int dynamicAutoRestart = FCGI_DEFAULT_RESTART_DYNAMIC;
133 int dynamicAutoUpdate = FCGI_DEFAULT_AUTOUPDATE;
134 int dynamicFlush = FCGI_FLUSH;
135 u_int dynamicListenQueueDepth = FCGI_DEFAULT_LISTEN_Q;
136 u_int dynamicInitStartDelay = DEFAULT_INIT_START_DELAY;
137 u_int dynamicRestartDelay = FCGI_DEFAULT_RESTART_DELAY;
138 array_header *dynamic_pass_headers = NULL;
139 u_int dynamic_idle_timeout = FCGI_DEFAULT_IDLE_TIMEOUT;
141 /*******************************************************************************
142 * Construct a message and write it to the pm_pipe.
144 static void send_to_pm(const char id, const char * const fs_path,
145 const char *user, const char * const group, const unsigned long q_usec,
146 const unsigned long req_usec)
148 #ifdef WIN32
149 fcgi_pm_job *job = NULL;
151 if (!(job = (fcgi_pm_job *) malloc(sizeof(fcgi_pm_job))))
152 return;
153 #else
154 static int failed_count = 0;
155 int buflen = 0;
156 char buf[FCGI_MAX_MSG_LEN];
157 #endif
159 if (strlen(fs_path) > FCGI_MAXPATH) {
160 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
161 "FastCGI: the path \"%s\" is too long (>%d) for a dynamic server", fs_path, FCGI_MAXPATH);
162 return;
165 switch(id) {
167 case FCGI_SERVER_START_JOB:
168 case FCGI_SERVER_RESTART_JOB:
169 #ifdef WIN32
170 job->id = id;
171 job->fs_path = strdup(fs_path);
172 job->user = strdup(user);
173 job->group = strdup(group);
174 job->qsec = 0L;
175 job->start_time = 0L;
176 #else
177 buflen = sprintf(buf, "%c %s %s %s*", id, fs_path, user, group);
178 #endif
179 break;
181 case FCGI_REQUEST_TIMEOUT_JOB:
182 #ifdef WIN32
183 job->id = id;
184 job->fs_path = strdup(fs_path);
185 job->user = strdup(user);
186 job->group = strdup(group);
187 job->qsec = 0L;
188 job->start_time = 0L;
189 #else
190 buflen = sprintf(buf, "%c %s %s %s*", id, fs_path, user, group);
191 #endif
192 break;
194 case FCGI_REQUEST_COMPLETE_JOB:
195 #ifdef WIN32
196 job->id = id;
197 job->fs_path = strdup(fs_path);
198 job->qsec = q_usec;
199 job->start_time = req_usec;
200 job->user = strdup(user);
201 job->group = strdup(group);
202 #else
203 buflen = sprintf(buf, "%c %s %s %s %lu %lu*", id, fs_path, user, group, q_usec, req_usec);
204 #endif
205 break;
208 #ifdef WIN32
209 if (fcgi_pm_add_job(job)) return;
211 SetEvent(fcgi_event_handles[MBOX_EVENT]);
212 #else
213 ap_assert(buflen <= FCGI_MAX_MSG_LEN);
215 /* There is no apache flag or function that can be used to id
216 * restart/shutdown pending so ignore the first few failures as
217 * once it breaks it will stay broke */
218 if (write(fcgi_pm_pipe[1], (const void *)buf, buflen) != buflen
219 && failed_count++ > 10)
221 ap_log_error(FCGI_LOG_WARN, fcgi_apache_main_server,
222 "FastCGI: write() to PM failed (ignore if a restart or shutdown is pending)");
224 #endif
228 *----------------------------------------------------------------------
230 * init_module
232 * An Apache module initializer, called by the Apache core
233 * after reading the server config.
235 * Start the process manager no matter what, since there may be a
236 * request for dynamic FastCGI applications without any being
237 * configured as static applications. Also, check for the existence
238 * and create if necessary a subdirectory into which all dynamic
239 * sockets will go.
241 *----------------------------------------------------------------------
243 #ifdef APACHE2
244 static apcb_t init_module(apr_pool_t * p, apr_pool_t * plog,
245 apr_pool_t * ptemp, server_rec * s)
246 #else
247 static apcb_t init_module(server_rec *s, pool *p)
248 #endif
250 const char *err;
252 /* Register to reset to default values when the config pool is cleaned */
253 ap_block_alarms();
254 ap_register_cleanup(p, NULL, fcgi_config_reset_globals, ap_null_cleanup);
255 ap_unblock_alarms();
257 #ifdef APACHE2
258 ap_add_version_component(p, "mod_fastcgi/" MOD_FASTCGI_VERSION);
259 #else
260 ap_add_version_component("mod_fastcgi/" MOD_FASTCGI_VERSION);
261 #endif
263 fcgi_config_set_fcgi_uid_n_gid(1);
265 /* keep these handy */
266 fcgi_config_pool = p;
267 fcgi_apache_main_server = s;
269 #ifndef WIN32
270 /* Create Unix/Domain socket directory */
271 if ((err = fcgi_config_make_dir(p, fcgi_socket_dir)))
272 ap_log_error(FCGI_LOG_ERR, s, "FastCGI: %s", err);
273 #endif
275 /* Create Dynamic directory */
276 if ((err = fcgi_config_make_dynamic_dir(p, 1)))
277 ap_log_error(FCGI_LOG_ERR, s, "FastCGI: %s", err);
279 #ifndef WIN32
280 /* Spawn the PM only once. Under Unix, Apache calls init() routines
281 * twice, once before detach() and once after. Win32 doesn't detach.
282 * Under DSO, DSO modules are unloaded between the two init() calls.
283 * Under Unix, the -X switch causes two calls to init() but no detach
284 * (but all subprocesses are wacked so the PM is toasted anyway)! */
286 if (ap_standalone && ap_restart_time == 0)
287 return;
289 /* Create the pipe for comm with the PM */
290 if (pipe(fcgi_pm_pipe) < 0) {
291 ap_log_error(FCGI_LOG_ERR, s, "FastCGI: pipe() failed");
294 /* Start the Process Manager */
295 fcgi_pm_pid = ap_spawn_child(p, fcgi_pm_main, NULL, kill_only_once, NULL, NULL, NULL);
296 if (fcgi_pm_pid <= 0) {
297 ap_log_error(FCGI_LOG_ALERT, s,
298 "FastCGI: can't start the process manager, spawn_child() failed");
301 close(fcgi_pm_pipe[0]);
302 #endif
304 return APCB_OK;
307 #ifdef APACHE2
308 static apcb_t fcgi_child_exit(void * dc)
309 #else
310 static apcb_t fcgi_child_exit(server_rec *dc0, pool *dc1)
311 #endif
313 #ifdef WIN32
314 /* Signal the PM thread to exit*/
315 SetEvent(fcgi_event_handles[TERM_EVENT]);
317 /* Waiting on pm thread to exit */
318 WaitForSingleObject(fcgi_pm_thread, INFINITE);
319 #endif
321 return APCB_OK;
324 #ifdef APACHE2
325 static apcb_t fcgi_child_init(apr_pool_t * p, server_rec * dc)
326 #else
327 static apcb_t fcgi_child_init(server_rec *dc, pool *p)
328 #endif
330 #ifdef WIN32
331 /* Create the MBOX, TERM, and WAKE event handlers */
332 fcgi_event_handles[0] = CreateEvent(NULL, FALSE, FALSE, NULL);
333 if (fcgi_event_handles[0] == NULL) {
334 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
335 "FastCGI: CreateEvent() failed");
337 fcgi_event_handles[1] = CreateEvent(NULL, FALSE, FALSE, NULL);
338 if (fcgi_event_handles[1] == NULL) {
339 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
340 "FastCGI: CreateEvent() failed");
342 fcgi_event_handles[2] = CreateEvent(NULL, FALSE, FALSE, NULL);
343 if (fcgi_event_handles[2] == NULL) {
344 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
345 "FastCGI: CreateEvent() failed");
348 /* Create the mbox mutex (PM - request threads) */
349 fcgi_dynamic_mbox_mutex = CreateMutex(NULL, FALSE, NULL);
350 if (fcgi_dynamic_mbox_mutex == NULL) {
351 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
352 "FastCGI: CreateMutex() failed");
355 /* Spawn of the process manager thread */
356 fcgi_pm_thread = (HANDLE) _beginthread(fcgi_pm_main, 0, NULL);
357 if (fcgi_pm_thread == (HANDLE) -1) {
358 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
359 "_beginthread() failed to spawn the process manager");
362 #ifdef APACHE2
363 apr_pool_cleanup_register(p, NULL, fcgi_child_exit, fcgi_child_exit);
364 #endif
365 #endif
367 return APCB_OK;
371 *----------------------------------------------------------------------
373 * get_header_line --
375 * Terminate a line: scan to the next newline, scan back to the
376 * first non-space character and store a terminating zero. Return
377 * the next character past the end of the newline.
379 * If the end of the string is reached, ASSERT!
381 * If the FIRST character(s) in the line are '\n' or "\r\n", the
382 * first character is replaced with a NULL and next character
383 * past the newline is returned. NOTE: this condition supercedes
384 * the processing of RFC-822 continuation lines.
386 * If continuation is set to 'TRUE', then it parses a (possible)
387 * sequence of RFC-822 continuation lines.
389 * Results:
390 * As above.
392 * Side effects:
393 * Termination byte stored in string.
395 *----------------------------------------------------------------------
397 static char *get_header_line(char *start, int continuation)
399 char *p = start;
400 char *end = start;
402 if(p[0] == '\r' && p[1] == '\n') { /* If EOL in 1st 2 chars */
403 p++; /* point to \n and stop */
404 } else if(*p != '\n') {
405 if(continuation) {
406 while(*p != '\0') {
407 if(*p == '\n' && p[1] != ' ' && p[1] != '\t')
408 break;
409 p++;
411 } else {
412 while(*p != '\0' && *p != '\n') {
413 p++;
418 ap_assert(*p != '\0');
419 end = p;
420 end++;
423 * Trim any trailing whitespace.
425 while(isspace((unsigned char)p[-1]) && p > start) {
426 p--;
429 *p = '\0';
430 return end;
434 *----------------------------------------------------------------------
436 * process_headers --
438 * Call with r->parseHeader == SCAN_CGI_READING_HEADERS
439 * and initial script output in fr->header.
441 * If the initial script output does not include the header
442 * terminator ("\r\n\r\n") process_headers returns with no side
443 * effects, to be called again when more script output
444 * has been appended to fr->header.
446 * If the initial script output includes the header terminator,
447 * process_headers parses the headers and determines whether or
448 * not the remaining script output will be sent to the client.
449 * If so, process_headers sends the HTTP response headers to the
450 * client and copies any non-header script output to the output
451 * buffer reqOutbuf.
453 * Results:
454 * none.
456 * Side effects:
457 * May set r->parseHeader to:
458 * SCAN_CGI_FINISHED -- headers parsed, returning script response
459 * SCAN_CGI_BAD_HEADER -- malformed header from script
460 * SCAN_CGI_INT_REDIRECT -- handler should perform internal redirect
461 * SCAN_CGI_SRV_REDIRECT -- handler should return REDIRECT
463 *----------------------------------------------------------------------
466 static const char *process_headers(request_rec *r, fcgi_request *fr)
468 char *p, *next, *name, *value;
469 int len, flag;
470 int hasContentType, hasStatus, hasLocation;
472 ap_assert(fr->parseHeader == SCAN_CGI_READING_HEADERS);
474 if (fr->header == NULL)
475 return NULL;
478 * Do we have the entire header? Scan for the blank line that
479 * terminates the header.
481 p = (char *)fr->header->elts;
482 len = fr->header->nelts;
483 flag = 0;
484 while(len-- && flag < 2) {
485 switch(*p) {
486 case '\r':
487 break;
488 case '\n':
489 flag++;
490 break;
491 case '\0':
492 case '\v':
493 case '\f':
494 name = "Invalid Character";
495 goto BadHeader;
496 break;
497 default:
498 flag = 0;
499 break;
501 p++;
504 /* Return (to be called later when we have more data)
505 * if we don't have an entire header. */
506 if (flag < 2)
507 return NULL;
510 * Parse all the headers.
512 fr->parseHeader = SCAN_CGI_FINISHED;
513 hasContentType = hasStatus = hasLocation = FALSE;
514 next = (char *)fr->header->elts;
515 for(;;) {
516 next = get_header_line(name = next, TRUE);
517 if (*name == '\0') {
518 break;
520 if ((p = strchr(name, ':')) == NULL) {
521 goto BadHeader;
523 value = p + 1;
524 while (p != name && isspace((unsigned char)*(p - 1))) {
525 p--;
527 if (p == name) {
528 goto BadHeader;
530 *p = '\0';
531 if (strpbrk(name, " \t") != NULL) {
532 *p = ' ';
533 goto BadHeader;
535 while (isspace((unsigned char)*value)) {
536 value++;
539 if (strcasecmp(name, "Status") == 0) {
540 int statusValue = strtol(value, NULL, 10);
542 if (hasStatus) {
543 goto DuplicateNotAllowed;
545 if (statusValue < 0) {
546 fr->parseHeader = SCAN_CGI_BAD_HEADER;
547 return ap_psprintf(r->pool, "invalid Status '%s'", value);
549 hasStatus = TRUE;
550 r->status = statusValue;
551 r->status_line = ap_pstrdup(r->pool, value);
552 continue;
555 if (fr->role == FCGI_RESPONDER) {
556 if (strcasecmp(name, "Content-type") == 0) {
557 if (hasContentType) {
558 goto DuplicateNotAllowed;
560 hasContentType = TRUE;
561 r->content_type = ap_pstrdup(r->pool, value);
562 continue;
565 if (strcasecmp(name, "Location") == 0) {
566 if (hasLocation) {
567 goto DuplicateNotAllowed;
569 hasLocation = TRUE;
570 ap_table_set(r->headers_out, "Location", value);
571 continue;
574 /* If the script wants them merged, it can do it */
575 ap_table_add(r->err_headers_out, name, value);
576 continue;
578 else {
579 ap_table_add(fr->authHeaders, name, value);
583 if (fr->role != FCGI_RESPONDER)
584 return NULL;
587 * Who responds, this handler or Apache?
589 if (hasLocation) {
590 const char *location = ap_table_get(r->headers_out, "Location");
592 * Based on internal redirect handling in mod_cgi.c...
594 * If a script wants to produce its own Redirect
595 * body, it now has to explicitly *say* "Status: 302"
597 if (r->status == 200) {
598 if(location[0] == '/') {
600 * Location is an relative path. This handler will
601 * consume all script output, then have Apache perform an
602 * internal redirect.
604 fr->parseHeader = SCAN_CGI_INT_REDIRECT;
605 return NULL;
606 } else {
608 * Location is an absolute URL. If the script didn't
609 * produce a Content-type header, this handler will
610 * consume all script output and then have Apache generate
611 * its standard redirect response. Otherwise this handler
612 * will transmit the script's response.
614 fr->parseHeader = SCAN_CGI_SRV_REDIRECT;
615 return NULL;
620 * We're responding. Send headers, buffer excess script output.
622 ap_send_http_header(r);
624 /* We need to reinstate our timeout, send_http_header() kill()s it */
625 ap_hard_timeout("FastCGI request processing", r);
627 if (r->header_only)
628 return NULL;
630 len = fr->header->nelts - (next - fr->header->elts);
631 ap_assert(len >= 0);
632 ap_assert(BufferLength(fr->clientOutputBuffer) == 0);
633 if (BufferFree(fr->clientOutputBuffer) < len) {
634 fr->clientOutputBuffer = fcgi_buf_new(r->pool, len);
636 ap_assert(BufferFree(fr->clientOutputBuffer) >= len);
637 if (len > 0) {
638 int sent = fcgi_buf_add_block(fr->clientOutputBuffer, next, len);
639 ap_assert(sent == len);
641 return NULL;
643 BadHeader:
644 /* Log first line of a multi-line header */
645 if ((p = strpbrk(name, "\r\n")) != NULL)
646 *p = '\0';
647 fr->parseHeader = SCAN_CGI_BAD_HEADER;
648 return ap_psprintf(r->pool, "malformed header '%s'", name);
650 DuplicateNotAllowed:
651 fr->parseHeader = SCAN_CGI_BAD_HEADER;
652 return ap_psprintf(r->pool, "duplicate header '%s'", name);
656 * Read from the client filling both the FastCGI server buffer and the
657 * client buffer with the hopes of buffering the client data before
658 * making the connect() to the FastCGI server. This prevents slow
659 * clients from keeping the FastCGI server in processing longer than is
660 * necessary.
662 static int read_from_client_n_queue(fcgi_request *fr)
664 char *end;
665 int count;
666 long int countRead;
668 while (BufferFree(fr->clientInputBuffer) > 0 || BufferFree(fr->serverOutputBuffer) > 0) {
669 fcgi_protocol_queue_client_buffer(fr);
671 if (fr->expectingClientContent <= 0)
672 return OK;
674 fcgi_buf_get_free_block_info(fr->clientInputBuffer, &end, &count);
675 if (count == 0)
676 return OK;
678 if ((countRead = ap_get_client_block(fr->r, end, count)) < 0)
679 return -1;
681 if (countRead == 0) {
682 fr->expectingClientContent = 0;
684 else {
685 fcgi_buf_add_update(fr->clientInputBuffer, countRead);
686 ap_reset_timeout(fr->r);
689 return OK;
692 static int write_to_client(fcgi_request *fr)
694 char *begin;
695 int count;
696 int rv;
697 #ifdef APACHE2
698 apr_bucket * bkt;
699 apr_bucket_brigade * bde;
700 apr_bucket_alloc_t * const bkt_alloc = fr->r->connection->bucket_alloc;
701 #endif
703 fcgi_buf_get_block_info(fr->clientOutputBuffer, &begin, &count);
704 if (count == 0)
705 return OK;
707 /* If fewer than count bytes are written, an error occured.
708 * ap_bwrite() typically forces a flushed write to the client, this
709 * effectively results in a block (and short packets) - it should
710 * be fixed, but I didn't win much support for the idea on new-httpd.
711 * So, without patching Apache, the best way to deal with this is
712 * to size the fcgi_bufs to hold all of the script output (within
713 * reason) so the script can be released from having to wait around
714 * for the transmission to the client to complete. */
716 #ifdef APACHE2
718 bde = apr_brigade_create(fr->r->pool, bkt_alloc);
719 bkt = apr_bucket_transient_create(begin, count, bkt_alloc);
720 APR_BRIGADE_INSERT_TAIL(bde, bkt);
722 if (fr->fs ? fr->fs->flush : dynamicFlush)
724 bkt = apr_bucket_flush_create(bkt_alloc);
725 APR_BRIGADE_INSERT_TAIL(bde, bkt);
728 rv = ap_pass_brigade(fr->r->output_filters, bde);
730 #elif defined(RUSSIAN_APACHE)
732 rv = (ap_rwrite(begin, count, fr->r) != count);
734 #else
736 rv = (ap_bwrite(fr->r->connection->client, begin, count) != count);
738 #endif
740 if (rv)
742 ap_log_rerror(FCGI_LOG_INFO_NOERRNO, fr->r,
743 "FastCGI: client stopped connection before send body completed");
744 return -1;
747 #ifndef APACHE2
749 ap_reset_timeout(fr->r);
751 /* Don't bother with a wrapped buffer, limiting exposure to slow
752 * clients. The BUFF routines don't allow a writev from above,
753 * and don't always memcpy to minimize small write()s, this should
754 * be fixed, but I didn't win much support for the idea on
755 * new-httpd - I'll have to _prove_ its a problem first.. */
757 /* The default behaviour used to be to flush with every write, but this
758 * can tie up the FastCGI server longer than is necessary so its an option now */
760 if (fr->fs ? fr->fs->flush : dynamicFlush)
762 #ifdef RUSSIAN_APACHE
763 rv = ap_rflush(fr->r);
764 #else
765 rv = ap_bflush(fr->r->connection->client);
766 #endif
768 if (rv)
770 ap_log_rerror(FCGI_LOG_INFO_NOERRNO, fr->r,
771 "FastCGI: client stopped connection before send body completed");
772 return -1;
775 ap_reset_timeout(fr->r);
778 #endif /* !APACHE2 */
780 fcgi_buf_toss(fr->clientOutputBuffer, count);
781 return OK;
784 /*******************************************************************************
785 * Determine the user and group the wrapper should be called with.
786 * Based on code in Apache's create_argv_cmd() (util_script.c).
788 static void set_uid_n_gid(request_rec *r, const char **user, const char **group)
790 if (fcgi_wrapper == NULL) {
791 *user = "-";
792 *group = "-";
793 return;
796 if (strncmp("/~", r->uri, 2) == 0) {
797 /* its a user dir uri, just send the ~user, and leave it to the PM */
798 char *end = strchr(r->uri + 2, '/');
800 if (end)
801 *user = memcpy(ap_pcalloc(r->pool, end - r->uri), r->uri + 1, end - r->uri - 1);
802 else
803 *user = ap_pstrdup(r->pool, r->uri + 1);
804 *group = "-";
806 else {
807 *user = ap_psprintf(r->pool, "%ld", (long) fcgi_util_get_server_uid(r->server));
808 *group = ap_psprintf(r->pool, "%ld", (long) fcgi_util_get_server_gid(r->server));
812 static void send_request_complete(fcgi_request *fr)
814 if (fr->completeTime.tv_sec)
816 struct timeval qtime, rtime;
818 timersub(&fr->queueTime, &fr->startTime, &qtime);
819 timersub(&fr->completeTime, &fr->queueTime, &rtime);
821 send_to_pm(FCGI_REQUEST_COMPLETE_JOB, fr->fs_path,
822 fr->user, fr->group,
823 qtime.tv_sec * 1000000 + qtime.tv_usec,
824 rtime.tv_sec * 1000000 + rtime.tv_usec);
828 #ifdef WIN32
830 static int set_nonblocking(const fcgi_request * fr, int nonblocking)
832 if (fr->using_npipe_io)
834 if (nonblocking)
836 DWORD mode = PIPE_NOWAIT | PIPE_READMODE_BYTE;
837 if (SetNamedPipeHandleState((HANDLE) fr->fd, &mode, NULL, NULL) == 0)
839 ap_log_rerror(FCGI_LOG_ERR, fr->r,
840 "FastCGI: SetNamedPipeHandleState() failed");
841 return -1;
845 else
847 unsigned long ioctl_arg = (nonblocking) ? 1 : 0;
848 if (ioctlsocket(fr->fd, FIONBIO, &ioctl_arg) != 0)
850 errno = WSAGetLastError();
851 ap_log_rerror(FCGI_LOG_ERR_ERRNO, fr->r,
852 "FastCGI: ioctlsocket() failed");
853 return -1;
857 return 0;
860 #else
862 static int set_nonblocking(const fcgi_request * fr, int nonblocking)
864 int nb_flag = 0;
865 int fd_flags = fcntl(fr->fd, F_GETFL, 0);
867 if (fd_flags < 0) return -1;
869 #if defined(O_NONBLOCK)
870 nb_flag = O_NONBLOCK;
871 #elif defined(O_NDELAY)
872 nb_flag = O_NDELAY;
873 #elif defined(FNDELAY)
874 nb_flag = FNDELAY;
875 #else
876 #error "TODO - don't read from app until all data from client is posted."
877 #endif
879 fd_flags = (nonblocking) ? (fd_flags | nb_flag) : (fd_flags & ~nb_flag);
881 return fcntl(fr->fd, F_SETFL, fd_flags);
884 #endif
886 /*******************************************************************************
887 * Close the connection to the FastCGI server. This is normally called by
888 * do_work(), but may also be called as in request pool cleanup.
890 static void close_connection_to_fs(fcgi_request *fr)
892 #ifdef WIN32
894 if (fr->fd != INVALID_SOCKET)
896 set_nonblocking(fr, FALSE);
898 if (fr->using_npipe_io)
900 CloseHandle((HANDLE) fr->fd);
902 else
904 /* abort the connection entirely */
905 struct linger linger = {0, 0};
906 setsockopt(fr->fd, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger));
907 closesocket(fr->fd);
910 fr->fd = INVALID_SOCKET;
912 #else /* ! WIN32 */
914 if (fr->fd >= 0)
916 struct linger linger = {0, 0};
917 set_nonblocking(fr, FALSE);
918 /* abort the connection entirely */
919 setsockopt(fr->fd, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger));
920 closesocket(fr->fd);
921 fr->fd = -1;
923 #endif /* ! WIN32 */
925 if (fr->dynamic && fr->keepReadingFromFcgiApp == FALSE)
927 /* XXX FCGI_REQUEST_COMPLETE_JOB is only sent for requests which complete
928 * normally WRT the fcgi app. There is no data sent for
929 * connect() timeouts or requests which complete abnormally.
930 * KillDynamicProcs() and RemoveRecords() need to be looked at
931 * to be sure they can reasonably handle these cases before
932 * sending these sort of stats - theres some funk in there.
934 if (fcgi_util_ticks(&fr->completeTime) < 0)
936 /* there's no point to aborting the request, just log it */
937 ap_log_error(FCGI_LOG_ERR, fr->r->server, "FastCGI: can't get time of day");
943 /*******************************************************************************
944 * Connect to the FastCGI server.
946 static int open_connection_to_fs(fcgi_request *fr)
948 struct timeval tval;
949 fd_set write_fds, read_fds;
950 int status;
951 request_rec * const r = fr->r;
952 pool * const rp = r->pool;
953 const char *socket_path = NULL;
954 struct sockaddr *socket_addr = NULL;
955 int socket_addr_len = 0;
956 #ifndef WIN32
957 const char *err = NULL;
958 #endif
960 /* Create the connection point */
961 if (fr->dynamic)
963 socket_path = fcgi_util_socket_hash_filename(rp, fr->fs_path, fr->user, fr->group);
964 socket_path = fcgi_util_socket_make_path_absolute(rp, socket_path, 1);
966 #ifndef WIN32
967 err = fcgi_util_socket_make_domain_addr(rp, (struct sockaddr_un **)&socket_addr,
968 &socket_addr_len, socket_path);
969 if (err) {
970 ap_log_rerror(FCGI_LOG_ERR, r,
971 "FastCGI: failed to connect to server \"%s\": "
972 "%s", fr->fs_path, err);
973 return FCGI_FAILED;
975 #endif
977 else
979 #ifdef WIN32
980 if (fr->fs->dest_addr != NULL) {
981 socket_addr = fr->fs->dest_addr;
983 else if (fr->fs->socket_addr) {
984 socket_addr = fr->fs->socket_addr;
986 else {
987 socket_path = fr->fs->socket_path;
989 #else
990 socket_addr = fr->fs->socket_addr;
991 #endif
992 socket_addr_len = fr->fs->socket_addr_len;
995 if (fr->dynamic)
997 #ifdef WIN32
998 if (fr->fs && fr->fs->restartTime)
999 #else
1000 struct stat sock_stat;
1002 if (stat(socket_path, &sock_stat) == 0)
1003 #endif
1005 // It exists
1006 if (dynamicAutoUpdate)
1008 struct stat app_stat;
1010 /* TODO: follow sym links */
1012 if (stat(fr->fs_path, &app_stat) == 0)
1014 #ifdef WIN32
1015 if (fr->fs->restartTime < app_stat.st_mtime)
1016 #else
1017 if (sock_stat.st_mtime < app_stat.st_mtime)
1018 #endif
1020 #ifndef WIN32
1021 struct timeval tv = {1, 0};
1022 #endif
1024 * There's a newer one, request a restart.
1026 send_to_pm(FCGI_SERVER_RESTART_JOB, fr->fs_path, fr->user, fr->group, 0, 0);
1028 #ifdef WIN32
1029 Sleep(1000);
1030 #else
1031 /* Avoid sleep/alarm interactions */
1032 ap_select(0, NULL, NULL, NULL, &tv);
1033 #endif
1038 else
1040 send_to_pm(FCGI_SERVER_START_JOB, fr->fs_path, fr->user, fr->group, 0, 0);
1042 /* Wait until it looks like its running */
1044 for (;;)
1046 #ifdef WIN32
1047 Sleep(1000);
1049 fr->fs = fcgi_util_fs_get_by_id(fr->fs_path, 0, 0);
1051 if (fr->fs && fr->fs->restartTime)
1052 #else
1053 struct timeval tv = {1, 0};
1055 /* Avoid sleep/alarm interactions */
1056 ap_select(0, NULL, NULL, NULL, &tv);
1058 if (stat(socket_path, &sock_stat) == 0)
1059 #endif
1061 break;
1067 #ifdef WIN32
1068 if (socket_path)
1070 BOOL ready;
1071 DWORD connect_time;
1072 int rv;
1073 HANDLE wait_npipe_mutex;
1074 DWORD interval;
1075 DWORD max_connect_time = FCGI_NAMED_PIPE_CONNECT_TIMEOUT;
1077 fr->using_npipe_io = TRUE;
1079 if (fr->dynamic)
1081 interval = dynamicPleaseStartDelay * 1000;
1083 if (dynamicAppConnectTimeout) {
1084 max_connect_time = dynamicAppConnectTimeout;
1087 else
1089 interval = FCGI_NAMED_PIPE_CONNECT_TIMEOUT * 1000;
1091 if (fr->fs->appConnectTimeout) {
1092 max_connect_time = fr->fs->appConnectTimeout;
1096 fcgi_util_ticks(&fr->startTime);
1099 // xxx this handle should live somewhere (see CloseHandle()s below too)
1100 char * wait_npipe_mutex_name, * cp;
1101 wait_npipe_mutex_name = cp = ap_pstrdup(rp, socket_path);
1102 while ((cp = strchr(cp, '\\'))) *cp = '/';
1104 wait_npipe_mutex = CreateMutex(NULL, FALSE, wait_npipe_mutex_name);
1107 if (wait_npipe_mutex == NULL)
1109 ap_log_rerror(FCGI_LOG_ERR, r,
1110 "FastCGI: failed to connect to server \"%s\": "
1111 "can't create the WaitNamedPipe mutex", fr->fs_path);
1112 return FCGI_FAILED;
1115 SetLastError(ERROR_SUCCESS);
1117 rv = WaitForSingleObject(wait_npipe_mutex, max_connect_time * 1000);
1119 if (rv == WAIT_TIMEOUT || rv == WAIT_FAILED)
1121 if (fr->dynamic)
1123 send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0);
1125 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r,
1126 "FastCGI: failed to connect to server \"%s\": "
1127 "wait for a npipe instance failed", fr->fs_path);
1128 FCGIDBG3("interval=%d, max_connect_time=%d", interval, max_connect_time);
1129 CloseHandle(wait_npipe_mutex);
1130 return FCGI_FAILED;
1133 fcgi_util_ticks(&fr->queueTime);
1135 connect_time = fr->queueTime.tv_sec - fr->startTime.tv_sec;
1137 if (fr->dynamic)
1139 if (connect_time >= interval)
1141 send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0);
1142 FCGIDBG4("connect_time=%d, interval=%d, max_connect_time=%d", connect_time, interval, max_connect_time);
1144 if (max_connect_time - connect_time < interval)
1146 interval = max_connect_time - connect_time;
1149 else
1151 interval -= connect_time * 1000;
1154 for (;;)
1156 ready = WaitNamedPipe(socket_path, interval);
1158 if (ready)
1160 fr->fd = (SOCKET) CreateFile(socket_path,
1161 GENERIC_READ | GENERIC_WRITE,
1162 FILE_SHARE_READ | FILE_SHARE_WRITE,
1163 NULL, // no security attributes
1164 OPEN_EXISTING, // opens existing pipe
1165 FILE_FLAG_OVERLAPPED,
1166 NULL); // no template file
1168 if (fr->fd != (SOCKET) INVALID_HANDLE_VALUE)
1170 ReleaseMutex(wait_npipe_mutex);
1171 CloseHandle(wait_npipe_mutex);
1172 fcgi_util_ticks(&fr->queueTime);
1173 FCGIDBG2("got npipe connect: %s", fr->fs_path);
1174 return FCGI_OK;
1177 if (GetLastError() != ERROR_PIPE_BUSY
1178 && GetLastError() != ERROR_FILE_NOT_FOUND)
1180 ap_log_rerror(FCGI_LOG_ERR, r,
1181 "FastCGI: failed to connect to server \"%s\": "
1182 "CreateFile() failed", fr->fs_path);
1183 break;
1186 FCGIDBG2("missed npipe connect: %s", fr->fs_path);
1189 if (fr->dynamic)
1191 send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0);
1194 fcgi_util_ticks(&fr->queueTime);
1196 connect_time = fr->queueTime.tv_sec - fr->startTime.tv_sec;
1198 FCGIDBG5("interval=%d, max_connect_time=%d, connect_time=%d, ready=%d", interval, max_connect_time, connect_time, ready);
1200 if (connect_time >= max_connect_time)
1202 ap_log_rerror(FCGI_LOG_ERR, r,
1203 "FastCGI: failed to connect to server \"%s\": "
1204 "CreateFile()/WaitNamedPipe() timed out", fr->fs_path);
1205 break;
1209 ReleaseMutex(wait_npipe_mutex);
1210 CloseHandle(wait_npipe_mutex);
1211 fr->fd = INVALID_SOCKET;
1212 return FCGI_FAILED;
1215 #endif
1217 /* Create the socket */
1218 fr->fd = socket(socket_addr->sa_family, SOCK_STREAM, 0);
1220 #ifdef WIN32
1221 if (fr->fd == INVALID_SOCKET) {
1222 errno = WSAGetLastError(); // Not sure this is going to work as expected
1223 #else
1224 if (fr->fd < 0) {
1225 #endif
1226 ap_log_rerror(FCGI_LOG_ERR_ERRNO, r,
1227 "FastCGI: failed to connect to server \"%s\": "
1228 "socket() failed", fr->fs_path);
1229 return FCGI_FAILED;
1232 #ifndef WIN32
1233 if (fr->fd >= FD_SETSIZE) {
1234 ap_log_rerror(FCGI_LOG_ERR, r,
1235 "FastCGI: failed to connect to server \"%s\": "
1236 "socket file descriptor (%u) is larger than "
1237 "FD_SETSIZE (%u), you probably need to rebuild Apache with a "
1238 "larger FD_SETSIZE", fr->fs_path, fr->fd, FD_SETSIZE);
1239 return FCGI_FAILED;
1241 #endif
1243 /* If appConnectTimeout is non-zero, setup do a non-blocking connect */
1244 if ((fr->dynamic && dynamicAppConnectTimeout) || (!fr->dynamic && fr->fs->appConnectTimeout)) {
1245 set_nonblocking(fr, TRUE);
1248 if (fr->dynamic) {
1249 fcgi_util_ticks(&fr->startTime);
1252 /* Connect */
1253 if (connect(fr->fd, (struct sockaddr *)socket_addr, socket_addr_len) == 0)
1254 goto ConnectionComplete;
1256 #ifdef WIN32
1258 errno = WSAGetLastError();
1259 if (errno != WSAEWOULDBLOCK) {
1260 ap_log_rerror(FCGI_LOG_ERR_ERRNO, r,
1261 "FastCGI: failed to connect to server \"%s\": "
1262 "connect() failed", fr->fs_path);
1263 return FCGI_FAILED;
1266 #else
1268 /* ECONNREFUSED means the listen queue is full (or there isn't one).
1269 * With dynamic I can at least make sure the PM knows this is occuring */
1270 if (fr->dynamic && errno == ECONNREFUSED) {
1271 /* @@@ This might be better as some other "kind" of message */
1272 send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0);
1274 errno = ECONNREFUSED;
1277 if (errno != EINPROGRESS) {
1278 ap_log_rerror(FCGI_LOG_ERR, r,
1279 "FastCGI: failed to connect to server \"%s\": "
1280 "connect() failed", fr->fs_path);
1281 return FCGI_FAILED;
1284 #endif
1286 /* The connect() is non-blocking */
1288 errno = 0;
1290 if (fr->dynamic) {
1291 do {
1292 FD_ZERO(&write_fds);
1293 FD_SET(fr->fd, &write_fds);
1294 read_fds = write_fds;
1295 tval.tv_sec = dynamicPleaseStartDelay;
1296 tval.tv_usec = 0;
1298 status = ap_select((fr->fd+1), &read_fds, &write_fds, NULL, &tval);
1299 if (status < 0)
1300 break;
1302 fcgi_util_ticks(&fr->queueTime);
1304 if (status > 0)
1305 break;
1307 /* select() timed out */
1308 send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0);
1309 } while ((fr->queueTime.tv_sec - fr->startTime.tv_sec) < (int)dynamicAppConnectTimeout);
1311 /* XXX These can be moved down when dynamic vars live is a struct */
1312 if (status == 0) {
1313 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r,
1314 "FastCGI: failed to connect to server \"%s\": "
1315 "connect() timed out (appConnTimeout=%dsec)",
1316 fr->fs_path, dynamicAppConnectTimeout);
1317 return FCGI_FAILED;
1319 } /* dynamic */
1320 else {
1321 tval.tv_sec = fr->fs->appConnectTimeout;
1322 tval.tv_usec = 0;
1323 FD_ZERO(&write_fds);
1324 FD_SET(fr->fd, &write_fds);
1325 read_fds = write_fds;
1327 status = ap_select((fr->fd+1), &read_fds, &write_fds, NULL, &tval);
1329 if (status == 0) {
1330 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r,
1331 "FastCGI: failed to connect to server \"%s\": "
1332 "connect() timed out (appConnTimeout=%dsec)",
1333 fr->fs_path, dynamicAppConnectTimeout);
1334 return FCGI_FAILED;
1336 } /* !dynamic */
1338 if (status < 0) {
1339 #ifdef WIN32
1340 errno = WSAGetLastError();
1341 #endif
1342 ap_log_rerror(FCGI_LOG_ERR_ERRNO, r,
1343 "FastCGI: failed to connect to server \"%s\": "
1344 "select() failed", fr->fs_path);
1345 return FCGI_FAILED;
1348 if (FD_ISSET(fr->fd, &write_fds) || FD_ISSET(fr->fd, &read_fds)) {
1349 int error = 0;
1350 NET_SIZE_T len = sizeof(error);
1352 if (getsockopt(fr->fd, SOL_SOCKET, SO_ERROR, (char *)&error, &len) < 0) {
1353 /* Solaris pending error */
1354 #ifdef WIN32
1355 errno = WSAGetLastError();
1356 #endif
1357 ap_log_rerror(FCGI_LOG_ERR_ERRNO, r,
1358 "FastCGI: failed to connect to server \"%s\": "
1359 "select() failed (Solaris pending error)", fr->fs_path);
1360 return FCGI_FAILED;
1363 if (error != 0) {
1364 /* Berkeley-derived pending error */
1365 errno = error;
1366 ap_log_rerror(FCGI_LOG_ERR_ERRNO, r,
1367 "FastCGI: failed to connect to server \"%s\": "
1368 "select() failed (pending error)", fr->fs_path);
1369 return FCGI_FAILED;
1372 else {
1373 #ifdef WIN32
1374 errno = WSAGetLastError();
1375 #endif
1376 ap_log_rerror(FCGI_LOG_ERR_ERRNO, r,
1377 "FastCGI: failed to connect to server \"%s\": "
1378 "select() error - THIS CAN'T HAPPEN!", fr->fs_path);
1379 return FCGI_FAILED;
1382 ConnectionComplete:
1383 /* Return to blocking mode if it was set up */
1384 if ((fr->dynamic && dynamicAppConnectTimeout) || (!fr->dynamic && fr->fs->appConnectTimeout)) {
1385 set_nonblocking(fr, FALSE);
1388 #ifdef TCP_NODELAY
1389 if (socket_addr->sa_family == AF_INET) {
1390 /* We shouldn't be sending small packets and there's no application
1391 * level ack of the data we send, so disable Nagle */
1392 int set = 1;
1393 setsockopt(fr->fd, IPPROTO_TCP, TCP_NODELAY, (char *)&set, sizeof(set));
1395 #endif
1397 return FCGI_OK;
1400 static void sink_client_data(fcgi_request *fr)
1402 char *base;
1403 int size;
1405 fcgi_buf_reset(fr->clientInputBuffer);
1406 fcgi_buf_get_free_block_info(fr->clientInputBuffer, &base, &size);
1407 while (ap_get_client_block(fr->r, base, size) > 0);
1410 static apcb_t cleanup(void *data)
1412 fcgi_request * const fr = (fcgi_request *) data;
1414 if (fr == NULL) return APCB_OK;
1416 /* its more than likely already run, but... */
1417 close_connection_to_fs(fr);
1419 send_request_complete(fr);
1421 if (fr->fs_stderr_len) {
1422 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, fr->r,
1423 "FastCGI: server \"%s\" stderr: %s", fr->fs_path, fr->fs_stderr);
1426 return APCB_OK;
1429 #ifdef WIN32
1430 static int npipe_io(fcgi_request * const fr)
1432 request_rec * const r = fr->r;
1433 enum
1435 STATE_ENV_SEND,
1436 STATE_CLIENT_RECV,
1437 STATE_SERVER_SEND,
1438 STATE_SERVER_RECV,
1439 STATE_CLIENT_SEND,
1440 STATE_CLIENT_ERROR,
1441 STATE_ERROR
1443 state = STATE_ENV_SEND;
1444 env_status env_status;
1445 int client_recv;
1446 int dynamic_first_recv = fr->dynamic;
1447 int idle_timeout = fr->dynamic ? dynamic_idle_timeout : fr->fs->idle_timeout;
1448 int send_pending = 0;
1449 int recv_pending = 0;
1450 int client_send = 0;
1451 int rv;
1452 OVERLAPPED rov;
1453 OVERLAPPED sov;
1454 HANDLE events[2];
1455 struct timeval timeout;
1456 struct timeval dynamic_last_io_time = {0, 0};
1457 int did_io = 1;
1458 pool * const rp = r->pool;
1460 DWORD recv_count = 0;
1462 if (fr->role == FCGI_RESPONDER)
1464 client_recv = (fr->expectingClientContent != 0);
1467 idle_timeout = fr->dynamic ? dynamic_idle_timeout : fr->fs->idle_timeout;
1469 env_status.envp = NULL;
1471 events[0] = CreateEvent(NULL, TRUE, FALSE, NULL);
1472 events[1] = CreateEvent(NULL, TRUE, FALSE, NULL);
1473 sov.hEvent = events[0];
1474 rov.hEvent = events[1];
1476 if (fr->dynamic)
1478 dynamic_last_io_time = fr->startTime;
1480 if (dynamicAppConnectTimeout)
1482 struct timeval qwait;
1483 timersub(&fr->queueTime, &fr->startTime, &qwait);
1484 dynamic_first_recv = qwait.tv_sec / dynamicPleaseStartDelay + 1;
1488 ap_hard_timeout("FastCGI request processing", r);
1490 while (state != STATE_CLIENT_SEND)
1492 DWORD msec_timeout;
1494 switch (state)
1496 case STATE_ENV_SEND:
1498 if (fcgi_protocol_queue_env(r, fr, &env_status) == 0)
1500 goto SERVER_SEND;
1503 state = STATE_CLIENT_RECV;
1505 /* fall through */
1507 case STATE_CLIENT_RECV:
1509 if (read_from_client_n_queue(fr) != OK)
1511 state = STATE_CLIENT_ERROR;
1512 break;
1515 if (fr->eofSent)
1517 state = STATE_SERVER_SEND;
1520 /* fall through */
1522 SERVER_SEND:
1524 case STATE_SERVER_SEND:
1526 if (! send_pending && BufferLength(fr->serverOutputBuffer))
1528 Buffer * b = fr->serverOutputBuffer;
1529 DWORD sent, len;
1531 len = min(b->length, b->data + b->size - b->begin);
1533 if (WriteFile((HANDLE) fr->fd, b->begin, len, &sent, &sov))
1535 fcgi_buf_removed(b, sent);
1536 ResetEvent(sov.hEvent);
1538 else if (GetLastError() == ERROR_IO_PENDING)
1540 send_pending = 1;
1542 else
1544 ap_log_rerror(FCGI_LOG_ERR, r, "FastCGI: comm with server "
1545 "\"%s\" aborted: WriteFile() failed", fr->fs_path);
1546 state = STATE_ERROR;
1547 break;
1551 /* fall through */
1553 case STATE_SERVER_RECV:
1556 * Only get more data when the serverInputBuffer is empty.
1557 * Otherwise we may already have the END_REQUEST buffered
1558 * (but not processed) and a read on a closed named pipe
1559 * results in an error that is normally abnormal.
1561 if (! recv_pending && BufferLength(fr->serverInputBuffer) == 0)
1563 Buffer * b = fr->serverInputBuffer;
1564 DWORD rcvd, len;
1566 len = min(b->size - b->length, b->data + b->size - b->end);
1568 if (ReadFile((HANDLE) fr->fd, b->end, len, &rcvd, &rov))
1570 fcgi_buf_added(b, rcvd);
1571 recv_count += rcvd;
1572 ResetEvent(rov.hEvent);
1573 if (dynamic_first_recv)
1575 dynamic_first_recv = 0;
1578 else if (GetLastError() == ERROR_IO_PENDING)
1580 recv_pending = 1;
1582 else if (GetLastError() == ERROR_HANDLE_EOF)
1584 fr->keepReadingFromFcgiApp = FALSE;
1585 state = STATE_CLIENT_SEND;
1586 ResetEvent(rov.hEvent);
1587 break;
1589 else if (GetLastError() == ERROR_NO_DATA)
1591 break;
1593 else
1595 ap_log_rerror(FCGI_LOG_ERR, r, "FastCGI: comm with server "
1596 "\"%s\" aborted: ReadFile() failed", fr->fs_path);
1597 state = STATE_ERROR;
1598 break;
1602 /* fall through */
1604 case STATE_CLIENT_SEND:
1606 if (client_send || ! BufferFree(fr->clientOutputBuffer))
1608 if (write_to_client(fr))
1610 state = STATE_CLIENT_ERROR;
1611 break;
1614 client_send = 0;
1617 break;
1619 default:
1621 ap_assert(0);
1624 if (state == STATE_CLIENT_ERROR || state == STATE_ERROR)
1626 break;
1629 /* setup the io timeout */
1631 if (BufferLength(fr->clientOutputBuffer))
1633 /* don't let client data sit too long, it might be a push */
1634 timeout.tv_sec = 0;
1635 timeout.tv_usec = 100000;
1637 else if (dynamic_first_recv)
1639 int delay;
1640 struct timeval qwait;
1642 fcgi_util_ticks(&fr->queueTime);
1644 if (did_io)
1646 /* a send() succeeded last pass */
1647 dynamic_last_io_time = fr->queueTime;
1649 else
1651 /* timed out last pass */
1652 struct timeval idle_time;
1654 timersub(&fr->queueTime, &dynamic_last_io_time, &idle_time);
1656 if (idle_time.tv_sec > idle_timeout)
1658 send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0);
1659 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: comm "
1660 "with (dynamic) server \"%s\" aborted: (first read) "
1661 "idle timeout (%d sec)", fr->fs_path, idle_timeout);
1662 state = STATE_ERROR;
1663 break;
1667 timersub(&fr->queueTime, &fr->startTime, &qwait);
1669 delay = dynamic_first_recv * dynamicPleaseStartDelay;
1671 if (qwait.tv_sec < delay)
1673 timeout.tv_sec = delay;
1674 timeout.tv_usec = 100000; /* fudge for select() slop */
1675 timersub(&timeout, &qwait, &timeout);
1677 else
1679 /* Killed time somewhere.. client read? */
1680 send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0);
1681 dynamic_first_recv = qwait.tv_sec / dynamicPleaseStartDelay + 1;
1682 timeout.tv_sec = dynamic_first_recv * dynamicPleaseStartDelay;
1683 timeout.tv_usec = 100000; /* fudge for select() slop */
1684 timersub(&timeout, &qwait, &timeout);
1687 else
1689 timeout.tv_sec = idle_timeout;
1690 timeout.tv_usec = 0;
1693 /* require a pended recv otherwise the app can deadlock */
1694 if (recv_pending)
1696 msec_timeout = timeout.tv_sec * 1000 + timeout.tv_usec / 1000;
1698 rv = WaitForMultipleObjects(2, events, FALSE, msec_timeout);
1700 if (rv == WAIT_TIMEOUT)
1702 did_io = 0;
1704 if (BufferLength(fr->clientOutputBuffer))
1706 client_send = 1;
1708 else if (dynamic_first_recv)
1710 struct timeval qwait;
1712 fcgi_util_ticks(&fr->queueTime);
1713 timersub(&fr->queueTime, &fr->startTime, &qwait);
1715 send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0);
1717 dynamic_first_recv = qwait.tv_sec / dynamicPleaseStartDelay + 1;
1719 else
1721 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: comm with "
1722 "server \"%s\" aborted: idle timeout (%d sec)",
1723 fr->fs_path, idle_timeout);
1724 state = STATE_ERROR;
1725 break;
1728 else
1730 int i = rv - WAIT_OBJECT_0;
1732 did_io = 1;
1734 if (i == 0)
1736 DWORD sent;
1738 if (GetOverlappedResult((HANDLE) fr->fd, &sov, &sent, FALSE))
1740 send_pending = 0;
1741 ResetEvent(sov.hEvent);
1742 fcgi_buf_removed(fr->serverOutputBuffer, sent);
1744 else
1746 ap_log_rerror(FCGI_LOG_ERR, r, "FastCGI: comm with server "
1747 "\"%s\" aborted: GetOverlappedResult() failed", fr->fs_path);
1748 state = STATE_ERROR;
1749 break;
1752 else
1754 DWORD rcvd;
1756 ap_assert(i == 1);
1758 recv_pending = 0;
1759 ResetEvent(rov.hEvent);
1761 if (GetOverlappedResult((HANDLE) fr->fd, &rov, &rcvd, FALSE))
1763 fcgi_buf_added(fr->serverInputBuffer, rcvd);
1764 if (dynamic_first_recv)
1766 dynamic_first_recv = 0;
1769 else
1771 ap_log_rerror(FCGI_LOG_ERR, r, "FastCGI: comm with server "
1772 "\"%s\" aborted: GetOverlappedResult() failed", fr->fs_path);
1773 state = STATE_ERROR;
1774 break;
1780 if (fcgi_protocol_dequeue(rp, fr))
1782 state = STATE_ERROR;
1783 break;
1786 if (fr->parseHeader == SCAN_CGI_READING_HEADERS)
1788 const char * err = process_headers(r, fr);
1789 if (err)
1791 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r,
1792 "FastCGI: comm with server \"%s\" aborted: "
1793 "error parsing headers: %s", fr->fs_path, err);
1794 state = STATE_ERROR;
1795 break;
1799 if (fr->exitStatusSet)
1801 fr->keepReadingFromFcgiApp = FALSE;
1802 state = STATE_CLIENT_SEND;
1803 break;
1807 if (! fr->exitStatusSet || ! fr->eofSent)
1809 CancelIo((HANDLE) fr->fd);
1812 CloseHandle(rov.hEvent);
1813 CloseHandle(sov.hEvent);
1815 return (state == STATE_ERROR);
1817 #endif /* WIN32 */
1819 static int socket_io(fcgi_request * const fr)
1821 enum
1823 STATE_SOCKET_NONE,
1824 STATE_ENV_SEND,
1825 STATE_CLIENT_RECV,
1826 STATE_SERVER_SEND,
1827 STATE_SERVER_RECV,
1828 STATE_CLIENT_SEND,
1829 STATE_ERROR,
1830 STATE_CLIENT_ERROR
1832 state = STATE_ENV_SEND;
1834 request_rec * const r = fr->r;
1836 struct timeval timeout;
1837 struct timeval dynamic_last_io_time = {0, 0};
1838 fd_set read_set;
1839 fd_set write_set;
1840 int nfds = fr->fd + 1;
1841 int select_status = 1;
1842 int idle_timeout;
1843 int rv;
1844 int dynamic_first_recv = fr->dynamic ? 1 : 0;
1845 int client_send = FALSE;
1846 int client_recv = FALSE;
1847 env_status env;
1848 pool *rp = r->pool;
1850 if (fr->role == FCGI_RESPONDER)
1852 client_recv = (fr->expectingClientContent != 0);
1855 idle_timeout = fr->dynamic ? dynamic_idle_timeout : fr->fs->idle_timeout;
1857 env.envp = NULL;
1859 if (fr->dynamic)
1861 dynamic_last_io_time = fr->startTime;
1863 if (dynamicAppConnectTimeout)
1865 struct timeval qwait;
1866 timersub(&fr->queueTime, &fr->startTime, &qwait);
1867 dynamic_first_recv = qwait.tv_sec / dynamicPleaseStartDelay + 1;
1871 ap_hard_timeout("FastCGI request processing", r);
1873 set_nonblocking(fr, TRUE);
1875 for (;;)
1877 FD_ZERO(&read_set);
1878 FD_ZERO(&write_set);
1880 switch (state)
1882 case STATE_ENV_SEND:
1884 if (fcgi_protocol_queue_env(r, fr, &env) == 0)
1886 goto SERVER_SEND;
1889 state = STATE_CLIENT_RECV;
1891 /* fall through */
1893 case STATE_CLIENT_RECV:
1895 if (read_from_client_n_queue(fr))
1897 state = STATE_CLIENT_ERROR;
1898 break;
1901 if (fr->eofSent)
1903 state = STATE_SERVER_SEND;
1906 /* fall through */
1908 SERVER_SEND:
1910 case STATE_SERVER_SEND:
1912 if (BufferLength(fr->serverOutputBuffer))
1914 FD_SET(fr->fd, &write_set);
1916 else
1918 ap_assert(fr->eofSent);
1919 state = STATE_SERVER_RECV;
1922 /* fall through */
1924 case STATE_SERVER_RECV:
1926 FD_SET(fr->fd, &read_set);
1928 /* fall through */
1930 case STATE_CLIENT_SEND:
1932 if (client_send || ! BufferFree(fr->clientOutputBuffer))
1934 if (write_to_client(fr))
1936 state = STATE_CLIENT_ERROR;
1937 break;
1940 client_send = 0;
1943 break;
1945 case STATE_ERROR:
1946 case STATE_CLIENT_ERROR:
1948 break;
1950 default:
1952 ap_assert(0);
1955 if (state == STATE_CLIENT_ERROR || state == STATE_ERROR)
1957 break;
1960 /* setup the io timeout */
1962 if (BufferLength(fr->clientOutputBuffer))
1964 /* don't let client data sit too long, it might be a push */
1965 timeout.tv_sec = 0;
1966 timeout.tv_usec = 100000;
1968 else if (dynamic_first_recv)
1970 int delay;
1971 struct timeval qwait;
1973 fcgi_util_ticks(&fr->queueTime);
1975 if (select_status)
1977 /* a send() succeeded last pass */
1978 dynamic_last_io_time = fr->queueTime;
1980 else
1982 /* timed out last pass */
1983 struct timeval idle_time;
1985 timersub(&fr->queueTime, &dynamic_last_io_time, &idle_time);
1987 if (idle_time.tv_sec > idle_timeout)
1989 send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0);
1990 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: comm "
1991 "with (dynamic) server \"%s\" aborted: (first read) "
1992 "idle timeout (%d sec)", fr->fs_path, idle_timeout);
1993 state = STATE_ERROR;
1994 break;
1998 timersub(&fr->queueTime, &fr->startTime, &qwait);
2000 delay = dynamic_first_recv * dynamicPleaseStartDelay;
2002 FCGIDBG5("qwait=%ld.%06ld delay=%d first_recv=%d", qwait.tv_sec, qwait.tv_usec, delay, dynamic_first_recv);
2004 if (qwait.tv_sec < delay)
2006 timeout.tv_sec = delay;
2007 timeout.tv_usec = 100000; /* fudge for select() slop */
2008 timersub(&timeout, &qwait, &timeout);
2010 else
2012 /* Killed time somewhere.. client read? */
2013 send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0);
2014 dynamic_first_recv = qwait.tv_sec / dynamicPleaseStartDelay + 1;
2015 timeout.tv_sec = dynamic_first_recv * dynamicPleaseStartDelay;
2016 timeout.tv_usec = 100000; /* fudge for select() slop */
2017 timersub(&timeout, &qwait, &timeout);
2020 else
2022 timeout.tv_sec = idle_timeout;
2023 timeout.tv_usec = 0;
2026 /* wait on the socket */
2027 select_status = ap_select(nfds, &read_set, &write_set, NULL, &timeout);
2029 if (select_status < 0)
2031 ap_log_rerror(FCGI_LOG_ERR_ERRNO, r, "FastCGI: comm with server "
2032 "\"%s\" aborted: select() failed", fr->fs_path);
2033 state = STATE_ERROR;
2034 break;
2037 if (select_status == 0)
2039 /* select() timeout */
2041 if (BufferLength(fr->clientOutputBuffer))
2043 if (fr->role == FCGI_RESPONDER)
2045 client_send = TRUE;
2048 else if (dynamic_first_recv)
2050 struct timeval qwait;
2052 fcgi_util_ticks(&fr->queueTime);
2053 timersub(&fr->queueTime, &fr->startTime, &qwait);
2055 send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0);
2057 dynamic_first_recv = qwait.tv_sec / dynamicPleaseStartDelay + 1;
2058 continue;
2060 else
2062 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: comm with "
2063 "server \"%s\" aborted: idle timeout (%d sec)",
2064 fr->fs_path, idle_timeout);
2065 state = STATE_ERROR;
2069 if (FD_ISSET(fr->fd, &write_set))
2071 /* send to the server */
2073 rv = fcgi_buf_socket_send(fr->serverOutputBuffer, fr->fd);
2075 if (rv < 0)
2077 ap_log_rerror(FCGI_LOG_ERR, r, "FastCGI: comm with server "
2078 "\"%s\" aborted: write failed", fr->fs_path);
2079 state = STATE_ERROR;
2080 break;
2084 if (FD_ISSET(fr->fd, &read_set))
2086 /* recv from the server */
2088 if (dynamic_first_recv)
2090 dynamic_first_recv = 0;
2091 fcgi_util_ticks(&fr->queueTime);
2094 rv = fcgi_buf_socket_recv(fr->serverInputBuffer, fr->fd);
2096 if (rv < 0)
2098 ap_log_rerror(FCGI_LOG_ERR, r, "FastCGI: comm with server "
2099 "\"%s\" aborted: read failed", fr->fs_path);
2100 state = STATE_ERROR;
2101 break;
2104 if (rv == 0)
2106 fr->keepReadingFromFcgiApp = FALSE;
2107 state = STATE_CLIENT_SEND;
2108 break;
2112 if (fcgi_protocol_dequeue(rp, fr))
2114 state = STATE_ERROR;
2115 break;
2118 if (fr->parseHeader == SCAN_CGI_READING_HEADERS)
2120 const char * err = process_headers(r, fr);
2121 if (err)
2123 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r,
2124 "FastCGI: comm with server \"%s\" aborted: "
2125 "error parsing headers: %s", fr->fs_path, err);
2126 state = STATE_ERROR;
2127 break;
2131 if (fr->exitStatusSet)
2133 fr->keepReadingFromFcgiApp = FALSE;
2134 state = STATE_CLIENT_SEND;
2135 break;
2139 return (state == STATE_ERROR);
2143 /*----------------------------------------------------------------------
2144 * This is the core routine for moving data between the FastCGI
2145 * application and the Web server's client.
2147 static int do_work(request_rec * const r, fcgi_request * const fr)
2149 int rv;
2150 pool *rp = r->pool;
2152 fcgi_protocol_queue_begin_request(fr);
2154 if (fr->role == FCGI_RESPONDER)
2156 rv = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR);
2157 if (rv != OK)
2159 ap_kill_timeout(r);
2160 return rv;
2163 fr->expectingClientContent = ap_should_client_block(r);
2166 ap_block_alarms();
2167 ap_register_cleanup(rp, (void *)fr, cleanup, ap_null_cleanup);
2168 ap_unblock_alarms();
2170 ap_hard_timeout("connect() to FastCGI server", r);
2172 /* Connect to the FastCGI Application */
2173 if (open_connection_to_fs(fr) != FCGI_OK)
2175 ap_kill_timeout(r);
2176 return HTTP_INTERNAL_SERVER_ERROR;
2179 #ifdef WIN32
2180 if (fr->using_npipe_io)
2182 rv = npipe_io(fr);
2184 else
2185 #endif
2187 rv = socket_io(fr);
2190 /* comm with the server is done */
2191 close_connection_to_fs(fr);
2193 if (fr->role == FCGI_RESPONDER)
2195 sink_client_data(fr);
2198 while (rv == 0 && BufferLength(fr->serverInputBuffer) || BufferLength(fr->clientOutputBuffer))
2200 if (fcgi_protocol_dequeue(rp, fr))
2202 rv = HTTP_INTERNAL_SERVER_ERROR;
2205 if (fr->parseHeader == SCAN_CGI_READING_HEADERS)
2207 const char * err = process_headers(r, fr);
2208 if (err)
2210 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r,
2211 "FastCGI: comm with server \"%s\" aborted: "
2212 "error parsing headers: %s", fr->fs_path, err);
2213 rv = HTTP_INTERNAL_SERVER_ERROR;
2217 if (fr->role == FCGI_RESPONDER)
2219 if (write_to_client(fr))
2221 break;
2224 else
2226 fcgi_buf_reset(fr->clientOutputBuffer);
2230 switch (fr->parseHeader)
2232 case SCAN_CGI_FINISHED:
2234 if (fr->role == FCGI_RESPONDER)
2236 /* RUSSIAN_APACHE requires rflush() over bflush() */
2237 ap_rflush(r);
2238 #ifndef APACHE2
2239 ap_bgetopt(r->connection->client, BO_BYTECT, &r->bytes_sent);
2240 #endif
2243 /* fall through */
2245 case SCAN_CGI_INT_REDIRECT:
2246 case SCAN_CGI_SRV_REDIRECT:
2248 break;
2250 case SCAN_CGI_READING_HEADERS:
2252 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: incomplete headers "
2253 "(%d bytes) received from server \"%s\"", fr->header->nelts, fr->fs_path);
2255 /* fall through */
2257 case SCAN_CGI_BAD_HEADER:
2259 rv = HTTP_INTERNAL_SERVER_ERROR;
2260 break;
2262 default:
2264 ap_assert(0);
2265 rv = HTTP_INTERNAL_SERVER_ERROR;
2268 ap_kill_timeout(r);
2269 return rv;
2272 static fcgi_request *create_fcgi_request(request_rec * const r, const char *fs_path)
2274 struct stat *my_finfo;
2275 pool * const p = r->pool;
2276 fcgi_server *fs;
2277 fcgi_request * const fr = (fcgi_request *)ap_pcalloc(p, sizeof(fcgi_request));
2279 #ifndef APACHE2
2280 if (fs_path)
2282 #endif
2283 my_finfo = (struct stat *)ap_palloc(p, sizeof(struct stat));
2284 if (stat(fs_path, my_finfo) < 0) {
2285 ap_log_rerror(FCGI_LOG_ERR_ERRNO, r,
2286 "FastCGI: stat() of \"%s\" failed", fs_path);
2287 return NULL;
2289 #ifndef APACHE2
2291 else {
2292 my_finfo = &r->finfo;
2293 fs_path = r->filename;
2295 #endif
2297 fs = fcgi_util_fs_get_by_id(fs_path, fcgi_util_get_server_uid(r->server),
2298 fcgi_util_get_server_gid(r->server));
2299 if (fs == NULL) {
2300 /* Its a request for a dynamic FastCGI application */
2301 const char * const err =
2302 fcgi_util_fs_is_path_ok(p, fs_path, my_finfo);
2304 if (err) {
2305 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: invalid (dynamic) server \"%s\": %s", fs_path, err);
2306 return NULL;
2310 fr->serverInputBuffer = fcgi_buf_new(p, SERVER_BUFSIZE);
2311 fr->serverOutputBuffer = fcgi_buf_new(p, SERVER_BUFSIZE);
2312 fr->clientInputBuffer = fcgi_buf_new(p, SERVER_BUFSIZE);
2313 fr->clientOutputBuffer = fcgi_buf_new(p, SERVER_BUFSIZE);
2314 fr->erBufPtr = fcgi_buf_new(p, sizeof(FCGI_EndRequestBody) + 1);
2315 fr->gotHeader = FALSE;
2316 fr->parseHeader = SCAN_CGI_READING_HEADERS;
2317 fr->header = ap_make_array(p, 1, 1);
2318 fr->fs_stderr = NULL;
2319 fr->r = r;
2320 fr->readingEndRequestBody = FALSE;
2321 fr->exitStatus = 0;
2322 fr->exitStatusSet = FALSE;
2323 fr->requestId = 1; /* anything but zero is OK here */
2324 fr->eofSent = FALSE;
2325 fr->role = FCGI_RESPONDER;
2326 fr->expectingClientContent = FALSE;
2327 fr->keepReadingFromFcgiApp = TRUE;
2328 fr->fs = fs;
2329 fr->fs_path = fs_path;
2330 fr->authHeaders = ap_make_table(p, 10);
2331 #ifdef WIN32
2332 fr->fd = INVALID_SOCKET;
2333 fr->dynamic = ((fs == NULL) || (fs->directive == APP_CLASS_DYNAMIC)) ? TRUE : FALSE;
2334 fr->using_npipe_io = FALSE;
2335 #else
2336 fr->dynamic = (fs == NULL) ? TRUE : FALSE;
2337 fr->fd = -1;
2338 #endif
2340 set_uid_n_gid(r, &fr->user, &fr->group);
2342 return fr;
2346 *----------------------------------------------------------------------
2348 * handler --
2350 * This routine gets called for a request that corresponds to
2351 * a FastCGI connection. It performs the request synchronously.
2353 * Results:
2354 * Final status of request: OK or NOT_FOUND or HTTP_INTERNAL_SERVER_ERROR.
2356 * Side effects:
2357 * Request performed.
2359 *----------------------------------------------------------------------
2362 /* Stolen from mod_cgi.c..
2363 * KLUDGE --- for back-combatibility, we don't have to check ExecCGI
2364 * in ScriptAliased directories, which means we need to know if this
2365 * request came through ScriptAlias or not... so the Alias module
2366 * leaves a note for us.
2368 static int apache_is_scriptaliased(request_rec *r)
2370 const char *t = ap_table_get(r->notes, "alias-forced-type");
2371 return t && (!strcasecmp(t, "cgi-script"));
2374 /* If a script wants to produce its own Redirect body, it now
2375 * has to explicitly *say* "Status: 302". If it wants to use
2376 * Apache redirects say "Status: 200". See process_headers().
2378 static int post_process_for_redirects(request_rec * const r,
2379 const fcgi_request * const fr)
2381 switch(fr->parseHeader) {
2382 case SCAN_CGI_INT_REDIRECT:
2384 /* @@@ There are still differences between the handling in
2385 * mod_cgi and mod_fastcgi. This needs to be revisited.
2387 /* We already read the message body (if any), so don't allow
2388 * the redirected request to think it has one. We can ignore
2389 * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR.
2391 r->method = "GET";
2392 r->method_number = M_GET;
2393 ap_table_unset(r->headers_in, "Content-length");
2395 ap_internal_redirect_handler(ap_table_get(r->headers_out, "Location"), r);
2396 return OK;
2398 case SCAN_CGI_SRV_REDIRECT:
2399 return HTTP_MOVED_TEMPORARILY;
2401 default:
2402 return OK;
2406 /******************************************************************************
2407 * Process fastcgi-script requests. Based on mod_cgi::cgi_handler().
2409 static int content_handler(request_rec *r)
2411 fcgi_request *fr = NULL;
2412 int ret;
2414 #ifdef APACHE2
2416 if (strcmp(r->handler, "fastcgi-script"))
2417 return DECLINED;
2419 /* Setup a new FastCGI request */
2420 if ((fr = create_fcgi_request(r, r->filename)) == NULL)
2421 return HTTP_INTERNAL_SERVER_ERROR;
2423 #else
2425 /* Setup a new FastCGI request */
2426 if ((fr = create_fcgi_request(r, NULL)) == NULL)
2427 return HTTP_INTERNAL_SERVER_ERROR;
2429 #endif
2431 /* If its a dynamic invocation, make sure scripts are OK here */
2432 if (fr->dynamic && !(ap_allow_options(r) & OPT_EXECCGI) && !apache_is_scriptaliased(r)) {
2433 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r,
2434 "FastCGI: \"ExecCGI Option\" is off in this directory: %s", r->uri);
2435 return HTTP_INTERNAL_SERVER_ERROR;
2438 /* Process the fastcgi-script request */
2439 if ((ret = do_work(r, fr)) != OK)
2440 return ret;
2442 /* Special case redirects */
2443 ret = post_process_for_redirects(r, fr);
2445 return ret;
2449 static int post_process_auth_passed_header(table *t, const char *key, const char * const val)
2451 if (strncasecmp(key, "Variable-", 9) == 0)
2452 key += 9;
2454 ap_table_setn(t, key, val);
2455 return 1;
2458 static int post_process_auth_passed_compat_header(table *t, const char *key, const char * const val)
2460 if (strncasecmp(key, "Variable-", 9) == 0)
2461 ap_table_setn(t, key + 9, val);
2463 return 1;
2466 static int post_process_auth_failed_header(table * const t, const char * const key, const char * const val)
2468 ap_table_setn(t, key, val);
2469 return 1;
2472 static void post_process_auth(fcgi_request * const fr, const int passed)
2474 request_rec * const r = fr->r;
2476 /* Restore the saved subprocess_env because we muddied ours up */
2477 r->subprocess_env = fr->saved_subprocess_env;
2479 if (passed) {
2480 if (fr->auth_compat) {
2481 ap_table_do((int (*)(void *, const char *, const char *))post_process_auth_passed_compat_header,
2482 (void *)r->subprocess_env, fr->authHeaders, NULL);
2484 else {
2485 ap_table_do((int (*)(void *, const char *, const char *))post_process_auth_passed_header,
2486 (void *)r->subprocess_env, fr->authHeaders, NULL);
2489 else {
2490 ap_table_do((int (*)(void *, const char *, const char *))post_process_auth_failed_header,
2491 (void *)r->err_headers_out, fr->authHeaders, NULL);
2494 /* @@@ Restore these.. its a hack until I rewrite the header handling */
2495 r->status = HTTP_OK;
2496 r->status_line = NULL;
2499 static int check_user_authentication(request_rec *r)
2501 int res, authenticated = 0;
2502 const char *password;
2503 fcgi_request *fr;
2504 const fcgi_dir_config * const dir_config =
2505 (const fcgi_dir_config *)ap_get_module_config(r->per_dir_config, &fastcgi_module);
2507 if (dir_config->authenticator == NULL)
2508 return DECLINED;
2510 /* Get the user password */
2511 if ((res = ap_get_basic_auth_pw(r, &password)) != OK)
2512 return res;
2514 if ((fr = create_fcgi_request(r, dir_config->authenticator)) == NULL)
2515 return HTTP_INTERNAL_SERVER_ERROR;
2517 /* Save the existing subprocess_env, because we're gonna muddy it up */
2518 fr->saved_subprocess_env = ap_copy_table(r->pool, r->subprocess_env);
2520 ap_table_setn(r->subprocess_env, "REMOTE_PASSWD", password);
2521 ap_table_setn(r->subprocess_env, "FCGI_APACHE_ROLE", "AUTHENTICATOR");
2523 /* The FastCGI Protocol doesn't differentiate authentication */
2524 fr->role = FCGI_AUTHORIZER;
2526 /* Do we need compatibility mode? */
2527 fr->auth_compat = (dir_config->authenticator_options & FCGI_COMPAT);
2529 if ((res = do_work(r, fr)) != OK)
2530 goto AuthenticationFailed;
2532 authenticated = (r->status == 200);
2533 post_process_auth(fr, authenticated);
2535 /* A redirect shouldn't be allowed during the authentication phase */
2536 if (ap_table_get(r->headers_out, "Location") != NULL) {
2537 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r,
2538 "FastCGI: FastCgiAuthenticator \"%s\" redirected (not allowed)",
2539 dir_config->authenticator);
2540 goto AuthenticationFailed;
2543 if (authenticated)
2544 return OK;
2546 AuthenticationFailed:
2547 if (!(dir_config->authenticator_options & FCGI_AUTHORITATIVE))
2548 return DECLINED;
2550 /* @@@ Probably should support custom_responses */
2551 ap_note_basic_auth_failure(r);
2552 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r,
2553 "FastCGI: authentication failed for user \"%s\": %s",
2554 #ifdef APACHE2
2555 r->user, r->uri);
2556 #else
2557 r->connection->user, r->uri);
2558 #endif
2560 return (res == OK) ? HTTP_UNAUTHORIZED : res;
2563 static int check_user_authorization(request_rec *r)
2565 int res, authorized = 0;
2566 fcgi_request *fr;
2567 const fcgi_dir_config * const dir_config =
2568 (const fcgi_dir_config *)ap_get_module_config(r->per_dir_config, &fastcgi_module);
2570 if (dir_config->authorizer == NULL)
2571 return DECLINED;
2573 /* @@@ We should probably honor the existing parameters to the require directive
2574 * as well as allow the definition of new ones (or use the basename of the
2575 * FastCGI server and pass the rest of the directive line), but for now keep
2576 * it simple. */
2578 if ((fr = create_fcgi_request(r, dir_config->authorizer)) == NULL)
2579 return HTTP_INTERNAL_SERVER_ERROR;
2581 /* Save the existing subprocess_env, because we're gonna muddy it up */
2582 fr->saved_subprocess_env = ap_copy_table(r->pool, r->subprocess_env);
2584 ap_table_setn(r->subprocess_env, "FCGI_APACHE_ROLE", "AUTHORIZER");
2586 fr->role = FCGI_AUTHORIZER;
2588 /* Do we need compatibility mode? */
2589 fr->auth_compat = (dir_config->authenticator_options & FCGI_COMPAT);
2591 if ((res = do_work(r, fr)) != OK)
2592 goto AuthorizationFailed;
2594 authorized = (r->status == 200);
2595 post_process_auth(fr, authorized);
2597 /* A redirect shouldn't be allowed during the authorization phase */
2598 if (ap_table_get(r->headers_out, "Location") != NULL) {
2599 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r,
2600 "FastCGI: FastCgiAuthorizer \"%s\" redirected (not allowed)",
2601 dir_config->authorizer);
2602 goto AuthorizationFailed;
2605 if (authorized)
2606 return OK;
2608 AuthorizationFailed:
2609 if (!(dir_config->authorizer_options & FCGI_AUTHORITATIVE))
2610 return DECLINED;
2612 /* @@@ Probably should support custom_responses */
2613 ap_note_basic_auth_failure(r);
2614 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r,
2615 "FastCGI: authorization failed for user \"%s\": %s",
2616 #ifdef APACHE2
2617 r->user, r->uri);
2618 #else
2619 r->connection->user, r->uri);
2620 #endif
2622 return (res == OK) ? HTTP_UNAUTHORIZED : res;
2625 static int check_access(request_rec *r)
2627 int res, access_allowed = 0;
2628 fcgi_request *fr;
2629 const fcgi_dir_config * const dir_config =
2630 (fcgi_dir_config *)ap_get_module_config(r->per_dir_config, &fastcgi_module);
2632 if (dir_config == NULL || dir_config->access_checker == NULL)
2633 return DECLINED;
2635 if ((fr = create_fcgi_request(r, dir_config->access_checker)) == NULL)
2636 return HTTP_INTERNAL_SERVER_ERROR;
2638 /* Save the existing subprocess_env, because we're gonna muddy it up */
2639 fr->saved_subprocess_env = ap_copy_table(r->pool, r->subprocess_env);
2641 ap_table_setn(r->subprocess_env, "FCGI_APACHE_ROLE", "ACCESS_CHECKER");
2643 /* The FastCGI Protocol doesn't differentiate access control */
2644 fr->role = FCGI_AUTHORIZER;
2646 /* Do we need compatibility mode? */
2647 fr->auth_compat = (dir_config->authenticator_options & FCGI_COMPAT);
2649 if ((res = do_work(r, fr)) != OK)
2650 goto AccessFailed;
2652 access_allowed = (r->status == 200);
2653 post_process_auth(fr, access_allowed);
2655 /* A redirect shouldn't be allowed during the access check phase */
2656 if (ap_table_get(r->headers_out, "Location") != NULL) {
2657 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r,
2658 "FastCGI: FastCgiAccessChecker \"%s\" redirected (not allowed)",
2659 dir_config->access_checker);
2660 goto AccessFailed;
2663 if (access_allowed)
2664 return OK;
2666 AccessFailed:
2667 if (!(dir_config->access_checker_options & FCGI_AUTHORITATIVE))
2668 return DECLINED;
2670 /* @@@ Probably should support custom_responses */
2671 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: access denied: %s", r->uri);
2672 return (res == OK) ? HTTP_FORBIDDEN : res;
2675 static const command_rec fastcgi_cmds[] =
2677 { "AppClass", fcgi_config_new_static_server, NULL, RSRC_CONF, RAW_ARGS, NULL },
2678 { "FastCgiServer", fcgi_config_new_static_server, NULL, RSRC_CONF, RAW_ARGS, NULL },
2680 { "ExternalAppClass", fcgi_config_new_external_server, NULL, RSRC_CONF, RAW_ARGS, NULL },
2681 { "FastCgiExternalServer", fcgi_config_new_external_server, NULL, RSRC_CONF, RAW_ARGS, NULL },
2683 { "FastCgiIpcDir", fcgi_config_set_socket_dir, NULL, RSRC_CONF, TAKE1, NULL },
2685 { "FastCgiSuexec", fcgi_config_set_wrapper, NULL, RSRC_CONF, TAKE1, NULL },
2686 { "FastCgiWrapper", fcgi_config_set_wrapper, NULL, RSRC_CONF, TAKE1, NULL },
2688 { "FCGIConfig", fcgi_config_set_config, NULL, RSRC_CONF, RAW_ARGS, NULL },
2689 { "FastCgiConfig", fcgi_config_set_config, NULL, RSRC_CONF, RAW_ARGS, NULL },
2691 { "FastCgiAuthenticator", fcgi_config_new_auth_server,
2692 (void *)FCGI_AUTH_TYPE_AUTHENTICATOR, ACCESS_CONF, TAKE12,
2693 "a fastcgi-script path (absolute or relative to ServerRoot) followed by an optional -compat" },
2694 { "FastCgiAuthenticatorAuthoritative", fcgi_config_set_authoritative_slot,
2695 (void *)XtOffsetOf(fcgi_dir_config, authenticator_options), ACCESS_CONF, FLAG,
2696 "Set to 'off' to allow authentication to be passed along to lower modules upon failure" },
2698 { "FastCgiAuthorizer", fcgi_config_new_auth_server,
2699 (void *)FCGI_AUTH_TYPE_AUTHORIZER, ACCESS_CONF, TAKE12,
2700 "a fastcgi-script path (absolute or relative to ServerRoot) followed by an optional -compat" },
2701 { "FastCgiAuthorizerAuthoritative", fcgi_config_set_authoritative_slot,
2702 (void *)XtOffsetOf(fcgi_dir_config, authorizer_options), ACCESS_CONF, FLAG,
2703 "Set to 'off' to allow authorization to be passed along to lower modules upon failure" },
2705 { "FastCgiAccessChecker", fcgi_config_new_auth_server,
2706 (void *)FCGI_AUTH_TYPE_ACCESS_CHECKER, ACCESS_CONF, TAKE12,
2707 "a fastcgi-script path (absolute or relative to ServerRoot) followed by an optional -compat" },
2708 { "FastCgiAccessCheckerAuthoritative", fcgi_config_set_authoritative_slot,
2709 (void *)XtOffsetOf(fcgi_dir_config, access_checker_options), ACCESS_CONF, FLAG,
2710 "Set to 'off' to allow access control to be passed along to lower modules upon failure" },
2711 { NULL }
2714 #ifdef APACHE2
2716 static void register_hooks(apr_pool_t * p)
2718 // ap_hook_pre_config(x_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
2719 ap_hook_post_config(init_module, NULL, NULL, APR_HOOK_MIDDLE);
2720 ap_hook_child_init(fcgi_child_init, NULL, NULL, APR_HOOK_MIDDLE);
2721 ap_hook_handler(content_handler, NULL, NULL, APR_HOOK_MIDDLE);
2722 ap_hook_check_user_id(check_user_authentication, NULL, NULL, APR_HOOK_MIDDLE);
2723 ap_hook_access_checker(check_access, NULL, NULL, APR_HOOK_MIDDLE);
2724 ap_hook_auth_checker(check_user_authorization, NULL, NULL, APR_HOOK_MIDDLE);
2727 module AP_MODULE_DECLARE_DATA fastcgi_module =
2729 STANDARD20_MODULE_STUFF,
2730 fcgi_config_create_dir_config, /* per-directory config creator */
2731 NULL, /* dir config merger */
2732 NULL, /* server config creator */
2733 NULL, /* server config merger */
2734 fastcgi_cmds, /* command table */
2735 register_hooks, /* set up other request processing hooks */
2738 #else /* !APACHE2 */
2740 handler_rec fastcgi_handlers[] = {
2741 { FCGI_MAGIC_TYPE, content_handler },
2742 { "fastcgi-script", content_handler },
2743 { NULL }
2746 module MODULE_VAR_EXPORT fastcgi_module = {
2747 STANDARD_MODULE_STUFF,
2748 init_module, /* initializer */
2749 fcgi_config_create_dir_config, /* per-dir config creator */
2750 NULL, /* per-dir config merger (default: override) */
2751 NULL, /* per-server config creator */
2752 NULL, /* per-server config merger (default: override) */
2753 fastcgi_cmds, /* command table */
2754 fastcgi_handlers, /* [9] content handlers */
2755 NULL, /* [2] URI-to-filename translation */
2756 check_user_authentication, /* [5] authenticate user_id */
2757 check_user_authorization, /* [6] authorize user_id */
2758 check_access, /* [4] check access (based on src & http headers) */
2759 NULL, /* [7] check/set MIME type */
2760 NULL, /* [8] fixups */
2761 NULL, /* [10] logger */
2762 NULL, /* [3] header-parser */
2763 fcgi_child_init, /* process initialization */
2764 fcgi_child_exit, /* process exit/cleanup */
2765 NULL /* [1] post read-request handling */
2768 #endif /* !APACHE2 */