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
18 * Patches for Linux provided by
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.
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.
75 #define timersub(a, b, result) \
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) { \
81 (result)->tv_usec += 1000000; \
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 */
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
;
113 int fcgi_pm_pipe
[2] = { -1, -1 };
114 pid_t fcgi_pm_pid
= -1;
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
)
149 fcgi_pm_job
*job
= NULL
;
151 if (!(job
= (fcgi_pm_job
*) malloc(sizeof(fcgi_pm_job
))))
154 static int failed_count
= 0;
156 char buf
[FCGI_MAX_MSG_LEN
];
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
);
167 case FCGI_SERVER_START_JOB
:
168 case FCGI_SERVER_RESTART_JOB
:
171 job
->fs_path
= strdup(fs_path
);
172 job
->user
= strdup(user
);
173 job
->group
= strdup(group
);
175 job
->start_time
= 0L;
177 buflen
= sprintf(buf
, "%c %s %s %s*", id
, fs_path
, user
, group
);
181 case FCGI_REQUEST_TIMEOUT_JOB
:
184 job
->fs_path
= strdup(fs_path
);
185 job
->user
= strdup(user
);
186 job
->group
= strdup(group
);
188 job
->start_time
= 0L;
190 buflen
= sprintf(buf
, "%c %s %s %s*", id
, fs_path
, user
, group
);
194 case FCGI_REQUEST_COMPLETE_JOB
:
197 job
->fs_path
= strdup(fs_path
);
199 job
->start_time
= req_usec
;
200 job
->user
= strdup(user
);
201 job
->group
= strdup(group
);
203 buflen
= sprintf(buf
, "%c %s %s %s %lu %lu*", id
, fs_path
, user
, group
, q_usec
, req_usec
);
209 if (fcgi_pm_add_job(job
)) return;
211 SetEvent(fcgi_event_handles
[MBOX_EVENT
]);
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)");
228 *----------------------------------------------------------------------
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
241 *----------------------------------------------------------------------
244 static apcb_t
init_module(apr_pool_t
* p
, apr_pool_t
* plog
,
245 apr_pool_t
* ptemp
, server_rec
* s
)
247 static apcb_t
init_module(server_rec
*s
, pool
*p
)
252 /* Register to reset to default values when the config pool is cleaned */
254 ap_register_cleanup(p
, NULL
, fcgi_config_reset_globals
, ap_null_cleanup
);
258 ap_add_version_component(p
, "mod_fastcgi/" MOD_FASTCGI_VERSION
);
260 ap_add_version_component("mod_fastcgi/" MOD_FASTCGI_VERSION
);
263 fcgi_config_set_fcgi_uid_n_gid(1);
265 /* keep these handy */
266 fcgi_config_pool
= p
;
267 fcgi_apache_main_server
= s
;
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
);
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
);
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)
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]);
308 static apcb_t
fcgi_child_exit(void * dc
)
310 static apcb_t
fcgi_child_exit(server_rec
*dc0
, pool
*dc1
)
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
);
325 static apcb_t
fcgi_child_init(apr_pool_t
* p
, server_rec
* dc
)
327 static apcb_t
fcgi_child_init(server_rec
*dc
, pool
*p
)
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");
363 apr_pool_cleanup_register(p
, NULL
, fcgi_child_exit
, fcgi_child_exit
);
371 *----------------------------------------------------------------------
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.
393 * Termination byte stored in string.
395 *----------------------------------------------------------------------
397 static char *get_header_line(char *start
, int continuation
)
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') {
407 if(*p
== '\n' && p
[1] != ' ' && p
[1] != '\t')
412 while(*p
!= '\0' && *p
!= '\n') {
418 ap_assert(*p
!= '\0');
423 * Trim any trailing whitespace.
425 while(isspace((unsigned char)p
[-1]) && p
> start
) {
434 *----------------------------------------------------------------------
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
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
;
470 int hasContentType
, hasStatus
, hasLocation
;
472 ap_assert(fr
->parseHeader
== SCAN_CGI_READING_HEADERS
);
474 if (fr
->header
== 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
;
484 while(len
-- && flag
< 2) {
494 name
= "Invalid Character";
504 /* Return (to be called later when we have more data)
505 * if we don't have an entire header. */
510 * Parse all the headers.
512 fr
->parseHeader
= SCAN_CGI_FINISHED
;
513 hasContentType
= hasStatus
= hasLocation
= FALSE
;
514 next
= (char *)fr
->header
->elts
;
516 next
= get_header_line(name
= next
, TRUE
);
520 if ((p
= strchr(name
, ':')) == NULL
) {
524 while (p
!= name
&& isspace((unsigned char)*(p
- 1))) {
531 if (strpbrk(name
, " \t") != NULL
) {
535 while (isspace((unsigned char)*value
)) {
539 if (strcasecmp(name
, "Status") == 0) {
540 int statusValue
= strtol(value
, NULL
, 10);
543 goto DuplicateNotAllowed
;
545 if (statusValue
< 0) {
546 fr
->parseHeader
= SCAN_CGI_BAD_HEADER
;
547 return ap_psprintf(r
->pool
, "invalid Status '%s'", value
);
550 r
->status
= statusValue
;
551 r
->status_line
= ap_pstrdup(r
->pool
, value
);
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
);
565 if (strcasecmp(name
, "Location") == 0) {
567 goto DuplicateNotAllowed
;
570 ap_table_set(r
->headers_out
, "Location", value
);
574 /* If the script wants them merged, it can do it */
575 ap_table_add(r
->err_headers_out
, name
, value
);
579 ap_table_add(fr
->authHeaders
, name
, value
);
583 if (fr
->role
!= FCGI_RESPONDER
)
587 * Who responds, this handler or Apache?
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
604 fr
->parseHeader
= SCAN_CGI_INT_REDIRECT
;
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
;
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
);
630 len
= fr
->header
->nelts
- (next
- fr
->header
->elts
);
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
);
638 int sent
= fcgi_buf_add_block(fr
->clientOutputBuffer
, next
, len
);
639 ap_assert(sent
== len
);
644 /* Log first line of a multi-line header */
645 if ((p
= strpbrk(name
, "\r\n")) != NULL
)
647 fr
->parseHeader
= SCAN_CGI_BAD_HEADER
;
648 return ap_psprintf(r
->pool
, "malformed header '%s'", name
);
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
662 static int read_from_client_n_queue(fcgi_request
*fr
)
668 while (BufferFree(fr
->clientInputBuffer
) > 0 || BufferFree(fr
->serverOutputBuffer
) > 0) {
669 fcgi_protocol_queue_client_buffer(fr
);
671 if (fr
->expectingClientContent
<= 0)
674 fcgi_buf_get_free_block_info(fr
->clientInputBuffer
, &end
, &count
);
678 if ((countRead
= ap_get_client_block(fr
->r
, end
, count
)) < 0)
681 if (countRead
== 0) {
682 fr
->expectingClientContent
= 0;
685 fcgi_buf_add_update(fr
->clientInputBuffer
, countRead
);
686 ap_reset_timeout(fr
->r
);
692 static int write_to_client(fcgi_request
*fr
)
699 apr_bucket_brigade
* bde
;
700 apr_bucket_alloc_t
* const bkt_alloc
= fr
->r
->connection
->bucket_alloc
;
703 fcgi_buf_get_block_info(fr
->clientOutputBuffer
, &begin
, &count
);
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. */
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
);
736 rv
= (ap_bwrite(fr
->r
->connection
->client
, begin
, count
) != count
);
742 ap_log_rerror(FCGI_LOG_INFO_NOERRNO
, fr
->r
,
743 "FastCGI: client stopped connection before send body completed");
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
);
765 rv
= ap_bflush(fr
->r
->connection
->client
);
770 ap_log_rerror(FCGI_LOG_INFO_NOERRNO
, fr
->r
,
771 "FastCGI: client stopped connection before send body completed");
775 ap_reset_timeout(fr
->r
);
778 #endif /* !APACHE2 */
780 fcgi_buf_toss(fr
->clientOutputBuffer
, count
);
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
) {
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, '/');
801 *user
= memcpy(ap_pcalloc(r
->pool
, end
- r
->uri
), r
->uri
+ 1, end
- r
->uri
- 1);
803 *user
= ap_pstrdup(r
->pool
, r
->uri
+ 1);
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
,
823 qtime
.tv_sec
* 1000000 + qtime
.tv_usec
,
824 rtime
.tv_sec
* 1000000 + rtime
.tv_usec
);
830 static int set_nonblocking(const fcgi_request
* fr
, int nonblocking
)
832 if (fr
->using_npipe_io
)
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");
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");
862 static int set_nonblocking(const fcgi_request
* fr
, int nonblocking
)
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)
873 #elif defined(FNDELAY)
876 #error "TODO - don't read from app until all data from client is posted."
879 fd_flags
= (nonblocking
) ? (fd_flags
| nb_flag
) : (fd_flags
& ~nb_flag
);
881 return fcntl(fr
->fd
, F_SETFL
, fd_flags
);
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
)
894 if (fr
->fd
!= INVALID_SOCKET
)
896 set_nonblocking(fr
, FALSE
);
898 if (fr
->using_npipe_io
)
900 CloseHandle((HANDLE
) fr
->fd
);
904 /* abort the connection entirely */
905 struct linger linger
= {0, 0};
906 setsockopt(fr
->fd
, SOL_SOCKET
, SO_LINGER
, (void *) &linger
, sizeof(linger
));
910 fr
->fd
= INVALID_SOCKET
;
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
));
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
)
949 fd_set write_fds
, read_fds
;
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;
957 const char *err
= NULL
;
960 /* Create the connection point */
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);
967 err
= fcgi_util_socket_make_domain_addr(rp
, (struct sockaddr_un
**)&socket_addr
,
968 &socket_addr_len
, socket_path
);
970 ap_log_rerror(FCGI_LOG_ERR
, r
,
971 "FastCGI: failed to connect to server \"%s\": "
972 "%s", fr
->fs_path
, err
);
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
;
987 socket_path
= fr
->fs
->socket_path
;
990 socket_addr
= fr
->fs
->socket_addr
;
992 socket_addr_len
= fr
->fs
->socket_addr_len
;
998 if (fr
->fs
&& fr
->fs
->restartTime
)
1000 struct stat sock_stat
;
1002 if (stat(socket_path
, &sock_stat
) == 0)
1006 if (dynamicAutoUpdate
)
1008 struct stat app_stat
;
1010 /* TODO: follow sym links */
1012 if (stat(fr
->fs_path
, &app_stat
) == 0)
1015 if (fr
->fs
->restartTime
< app_stat
.st_mtime
)
1017 if (sock_stat
.st_mtime
< app_stat
.st_mtime
)
1021 struct timeval tv
= {1, 0};
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);
1031 /* Avoid sleep/alarm interactions */
1032 ap_select(0, NULL
, NULL
, NULL
, &tv
);
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 */
1049 fr
->fs
= fcgi_util_fs_get_by_id(fr
->fs_path
, 0, 0);
1051 if (fr
->fs
&& fr
->fs
->restartTime
)
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)
1073 HANDLE wait_npipe_mutex
;
1075 DWORD max_connect_time
= FCGI_NAMED_PIPE_CONNECT_TIMEOUT
;
1077 fr
->using_npipe_io
= TRUE
;
1081 interval
= dynamicPleaseStartDelay
* 1000;
1083 if (dynamicAppConnectTimeout
) {
1084 max_connect_time
= dynamicAppConnectTimeout
;
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
);
1115 SetLastError(ERROR_SUCCESS
);
1117 rv
= WaitForSingleObject(wait_npipe_mutex
, max_connect_time
* 1000);
1119 if (rv
== WAIT_TIMEOUT
|| rv
== WAIT_FAILED
)
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
);
1133 fcgi_util_ticks(&fr
->queueTime
);
1135 connect_time
= fr
->queueTime
.tv_sec
- fr
->startTime
.tv_sec
;
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
;
1151 interval
-= connect_time
* 1000;
1156 ready
= WaitNamedPipe(socket_path
, interval
);
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
);
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
);
1186 FCGIDBG2("missed npipe connect: %s", fr
->fs_path
);
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
);
1209 ReleaseMutex(wait_npipe_mutex
);
1210 CloseHandle(wait_npipe_mutex
);
1211 fr
->fd
= INVALID_SOCKET
;
1217 /* Create the socket */
1218 fr
->fd
= socket(socket_addr
->sa_family
, SOCK_STREAM
, 0);
1221 if (fr
->fd
== INVALID_SOCKET
) {
1222 errno
= WSAGetLastError(); // Not sure this is going to work as expected
1226 ap_log_rerror(FCGI_LOG_ERR_ERRNO
, r
,
1227 "FastCGI: failed to connect to server \"%s\": "
1228 "socket() failed", fr
->fs_path
);
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
);
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
);
1249 fcgi_util_ticks(&fr
->startTime
);
1253 if (connect(fr
->fd
, (struct sockaddr
*)socket_addr
, socket_addr_len
) == 0)
1254 goto ConnectionComplete
;
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
);
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
);
1286 /* The connect() is non-blocking */
1292 FD_ZERO(&write_fds
);
1293 FD_SET(fr
->fd
, &write_fds
);
1294 read_fds
= write_fds
;
1295 tval
.tv_sec
= dynamicPleaseStartDelay
;
1298 status
= ap_select((fr
->fd
+1), &read_fds
, &write_fds
, NULL
, &tval
);
1302 fcgi_util_ticks(&fr
->queueTime
);
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 */
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
);
1321 tval
.tv_sec
= fr
->fs
->appConnectTimeout
;
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
);
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
);
1340 errno
= WSAGetLastError();
1342 ap_log_rerror(FCGI_LOG_ERR_ERRNO
, r
,
1343 "FastCGI: failed to connect to server \"%s\": "
1344 "select() failed", fr
->fs_path
);
1348 if (FD_ISSET(fr
->fd
, &write_fds
) || FD_ISSET(fr
->fd
, &read_fds
)) {
1350 NET_SIZE_T len
= sizeof(error
);
1352 if (getsockopt(fr
->fd
, SOL_SOCKET
, SO_ERROR
, (char *)&error
, &len
) < 0) {
1353 /* Solaris pending error */
1355 errno
= WSAGetLastError();
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
);
1364 /* Berkeley-derived pending 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
);
1374 errno
= WSAGetLastError();
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
);
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
);
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 */
1393 setsockopt(fr
->fd
, IPPROTO_TCP
, TCP_NODELAY
, (char *)&set
, sizeof(set
));
1400 static void sink_client_data(fcgi_request
*fr
)
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
);
1430 static int npipe_io(fcgi_request
* const fr
)
1432 request_rec
* const r
= fr
->r
;
1443 state
= STATE_ENV_SEND
;
1444 env_status env_status
;
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;
1455 struct timeval timeout
;
1456 struct timeval dynamic_last_io_time
= {0, 0};
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];
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
)
1496 case STATE_ENV_SEND
:
1498 if (fcgi_protocol_queue_env(r
, fr
, &env_status
) == 0)
1503 state
= STATE_CLIENT_RECV
;
1507 case STATE_CLIENT_RECV
:
1509 if (read_from_client_n_queue(fr
) != OK
)
1511 state
= STATE_CLIENT_ERROR
;
1517 state
= STATE_SERVER_SEND
;
1524 case STATE_SERVER_SEND
:
1526 if (! send_pending
&& BufferLength(fr
->serverOutputBuffer
))
1528 Buffer
* b
= fr
->serverOutputBuffer
;
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
)
1544 ap_log_rerror(FCGI_LOG_ERR
, r
, "FastCGI: comm with server "
1545 "\"%s\" aborted: WriteFile() failed", fr
->fs_path
);
1546 state
= STATE_ERROR
;
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
;
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
);
1572 ResetEvent(rov
.hEvent
);
1573 if (dynamic_first_recv
)
1575 dynamic_first_recv
= 0;
1578 else if (GetLastError() == ERROR_IO_PENDING
)
1582 else if (GetLastError() == ERROR_HANDLE_EOF
)
1584 fr
->keepReadingFromFcgiApp
= FALSE
;
1585 state
= STATE_CLIENT_SEND
;
1586 ResetEvent(rov
.hEvent
);
1589 else if (GetLastError() == ERROR_NO_DATA
)
1595 ap_log_rerror(FCGI_LOG_ERR
, r
, "FastCGI: comm with server "
1596 "\"%s\" aborted: ReadFile() failed", fr
->fs_path
);
1597 state
= STATE_ERROR
;
1604 case STATE_CLIENT_SEND
:
1606 if (client_send
|| ! BufferFree(fr
->clientOutputBuffer
))
1608 if (write_to_client(fr
))
1610 state
= STATE_CLIENT_ERROR
;
1624 if (state
== STATE_CLIENT_ERROR
|| state
== STATE_ERROR
)
1629 /* setup the io timeout */
1631 if (BufferLength(fr
->clientOutputBuffer
))
1633 /* don't let client data sit too long, it might be a push */
1635 timeout
.tv_usec
= 100000;
1637 else if (dynamic_first_recv
)
1640 struct timeval qwait
;
1642 fcgi_util_ticks(&fr
->queueTime
);
1646 /* a send() succeeded last pass */
1647 dynamic_last_io_time
= fr
->queueTime
;
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
;
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
);
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
);
1689 timeout
.tv_sec
= idle_timeout
;
1690 timeout
.tv_usec
= 0;
1693 /* require a pended recv otherwise the app can deadlock */
1696 msec_timeout
= timeout
.tv_sec
* 1000 + timeout
.tv_usec
/ 1000;
1698 rv
= WaitForMultipleObjects(2, events
, FALSE
, msec_timeout
);
1700 if (rv
== WAIT_TIMEOUT
)
1704 if (BufferLength(fr
->clientOutputBuffer
))
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;
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
;
1730 int i
= rv
- WAIT_OBJECT_0
;
1738 if (GetOverlappedResult((HANDLE
) fr
->fd
, &sov
, &sent
, FALSE
))
1741 ResetEvent(sov
.hEvent
);
1742 fcgi_buf_removed(fr
->serverOutputBuffer
, sent
);
1746 ap_log_rerror(FCGI_LOG_ERR
, r
, "FastCGI: comm with server "
1747 "\"%s\" aborted: GetOverlappedResult() failed", fr
->fs_path
);
1748 state
= STATE_ERROR
;
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;
1771 ap_log_rerror(FCGI_LOG_ERR
, r
, "FastCGI: comm with server "
1772 "\"%s\" aborted: GetOverlappedResult() failed", fr
->fs_path
);
1773 state
= STATE_ERROR
;
1780 if (fcgi_protocol_dequeue(rp
, fr
))
1782 state
= STATE_ERROR
;
1786 if (fr
->parseHeader
== SCAN_CGI_READING_HEADERS
)
1788 const char * err
= process_headers(r
, fr
);
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
;
1799 if (fr
->exitStatusSet
)
1801 fr
->keepReadingFromFcgiApp
= FALSE
;
1802 state
= STATE_CLIENT_SEND
;
1807 if (! fr
->exitStatusSet
|| ! fr
->eofSent
)
1809 CancelIo((HANDLE
) fr
->fd
);
1812 CloseHandle(rov
.hEvent
);
1813 CloseHandle(sov
.hEvent
);
1815 return (state
== STATE_ERROR
);
1819 static int socket_io(fcgi_request
* const fr
)
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};
1840 int nfds
= fr
->fd
+ 1;
1841 int select_status
= 1;
1844 int dynamic_first_recv
= fr
->dynamic
? 1 : 0;
1845 int client_send
= FALSE
;
1846 int client_recv
= FALSE
;
1850 if (fr
->role
== FCGI_RESPONDER
)
1852 client_recv
= (fr
->expectingClientContent
!= 0);
1855 idle_timeout
= fr
->dynamic
? dynamic_idle_timeout
: fr
->fs
->idle_timeout
;
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
);
1878 FD_ZERO(&write_set
);
1882 case STATE_ENV_SEND
:
1884 if (fcgi_protocol_queue_env(r
, fr
, &env
) == 0)
1889 state
= STATE_CLIENT_RECV
;
1893 case STATE_CLIENT_RECV
:
1895 if (read_from_client_n_queue(fr
))
1897 state
= STATE_CLIENT_ERROR
;
1903 state
= STATE_SERVER_SEND
;
1910 case STATE_SERVER_SEND
:
1912 if (BufferLength(fr
->serverOutputBuffer
))
1914 FD_SET(fr
->fd
, &write_set
);
1918 ap_assert(fr
->eofSent
);
1919 state
= STATE_SERVER_RECV
;
1924 case STATE_SERVER_RECV
:
1926 FD_SET(fr
->fd
, &read_set
);
1930 case STATE_CLIENT_SEND
:
1932 if (client_send
|| ! BufferFree(fr
->clientOutputBuffer
))
1934 if (write_to_client(fr
))
1936 state
= STATE_CLIENT_ERROR
;
1946 case STATE_CLIENT_ERROR
:
1955 if (state
== STATE_CLIENT_ERROR
|| state
== STATE_ERROR
)
1960 /* setup the io timeout */
1962 if (BufferLength(fr
->clientOutputBuffer
))
1964 /* don't let client data sit too long, it might be a push */
1966 timeout
.tv_usec
= 100000;
1968 else if (dynamic_first_recv
)
1971 struct timeval qwait
;
1973 fcgi_util_ticks(&fr
->queueTime
);
1977 /* a send() succeeded last pass */
1978 dynamic_last_io_time
= fr
->queueTime
;
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
;
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
);
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
);
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
;
2037 if (select_status
== 0)
2039 /* select() timeout */
2041 if (BufferLength(fr
->clientOutputBuffer
))
2043 if (fr
->role
== FCGI_RESPONDER
)
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;
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
);
2077 ap_log_rerror(FCGI_LOG_ERR
, r
, "FastCGI: comm with server "
2078 "\"%s\" aborted: write failed", fr
->fs_path
);
2079 state
= STATE_ERROR
;
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
);
2098 ap_log_rerror(FCGI_LOG_ERR
, r
, "FastCGI: comm with server "
2099 "\"%s\" aborted: read failed", fr
->fs_path
);
2100 state
= STATE_ERROR
;
2106 fr
->keepReadingFromFcgiApp
= FALSE
;
2107 state
= STATE_CLIENT_SEND
;
2112 if (fcgi_protocol_dequeue(rp
, fr
))
2114 state
= STATE_ERROR
;
2118 if (fr
->parseHeader
== SCAN_CGI_READING_HEADERS
)
2120 const char * err
= process_headers(r
, fr
);
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
;
2131 if (fr
->exitStatusSet
)
2133 fr
->keepReadingFromFcgiApp
= FALSE
;
2134 state
= STATE_CLIENT_SEND
;
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
)
2152 fcgi_protocol_queue_begin_request(fr
);
2154 if (fr
->role
== FCGI_RESPONDER
)
2156 rv
= ap_setup_client_block(r
, REQUEST_CHUNKED_ERROR
);
2163 fr
->expectingClientContent
= ap_should_client_block(r
);
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
)
2176 return HTTP_INTERNAL_SERVER_ERROR
;
2180 if (fr
->using_npipe_io
)
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
);
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
))
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() */
2239 ap_bgetopt(r
->connection
->client
, BO_BYTECT
, &r
->bytes_sent
);
2245 case SCAN_CGI_INT_REDIRECT
:
2246 case SCAN_CGI_SRV_REDIRECT
:
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
);
2257 case SCAN_CGI_BAD_HEADER
:
2259 rv
= HTTP_INTERNAL_SERVER_ERROR
;
2265 rv
= HTTP_INTERNAL_SERVER_ERROR
;
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
;
2277 fcgi_request
* const fr
= (fcgi_request
*)ap_pcalloc(p
, sizeof(fcgi_request
));
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
);
2292 my_finfo
= &r
->finfo
;
2293 fs_path
= r
->filename
;
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
));
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
);
2305 ap_log_rerror(FCGI_LOG_ERR_NOERRNO
, r
, "FastCGI: invalid (dynamic) server \"%s\": %s", fs_path
, err
);
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
;
2320 fr
->readingEndRequestBody
= FALSE
;
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
;
2329 fr
->fs_path
= fs_path
;
2330 fr
->authHeaders
= ap_make_table(p
, 10);
2332 fr
->fd
= INVALID_SOCKET
;
2333 fr
->dynamic
= ((fs
== NULL
) || (fs
->directive
== APP_CLASS_DYNAMIC
)) ? TRUE
: FALSE
;
2334 fr
->using_npipe_io
= FALSE
;
2336 fr
->dynamic
= (fs
== NULL
) ? TRUE
: FALSE
;
2340 set_uid_n_gid(r
, &fr
->user
, &fr
->group
);
2346 *----------------------------------------------------------------------
2350 * This routine gets called for a request that corresponds to
2351 * a FastCGI connection. It performs the request synchronously.
2354 * Final status of request: OK or NOT_FOUND or HTTP_INTERNAL_SERVER_ERROR.
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.
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
);
2398 case SCAN_CGI_SRV_REDIRECT
:
2399 return HTTP_MOVED_TEMPORARILY
;
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
;
2416 if (strcmp(r
->handler
, "fastcgi-script"))
2419 /* Setup a new FastCGI request */
2420 if ((fr
= create_fcgi_request(r
, r
->filename
)) == NULL
)
2421 return HTTP_INTERNAL_SERVER_ERROR
;
2425 /* Setup a new FastCGI request */
2426 if ((fr
= create_fcgi_request(r
, NULL
)) == NULL
)
2427 return HTTP_INTERNAL_SERVER_ERROR
;
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
)
2442 /* Special case redirects */
2443 ret
= post_process_for_redirects(r
, fr
);
2449 static int post_process_auth_passed_header(table
*t
, const char *key
, const char * const val
)
2451 if (strncasecmp(key
, "Variable-", 9) == 0)
2454 ap_table_setn(t
, key
, val
);
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
);
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
);
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
;
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
);
2485 ap_table_do((int (*)(void *, const char *, const char *))post_process_auth_passed_header
,
2486 (void *)r
->subprocess_env
, fr
->authHeaders
, NULL
);
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
;
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
)
2510 /* Get the user password */
2511 if ((res
= ap_get_basic_auth_pw(r
, &password
)) != OK
)
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
;
2546 AuthenticationFailed
:
2547 if (!(dir_config
->authenticator_options
& FCGI_AUTHORITATIVE
))
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",
2557 r
->connection
->user
, r
->uri
);
2560 return (res
== OK
) ? HTTP_UNAUTHORIZED
: res
;
2563 static int check_user_authorization(request_rec
*r
)
2565 int res
, authorized
= 0;
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
)
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
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
;
2608 AuthorizationFailed
:
2609 if (!(dir_config
->authorizer_options
& FCGI_AUTHORITATIVE
))
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",
2619 r
->connection
->user
, r
->uri
);
2622 return (res
== OK
) ? HTTP_UNAUTHORIZED
: res
;
2625 static int check_access(request_rec
*r
)
2627 int res
, access_allowed
= 0;
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
)
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
)
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
);
2667 if (!(dir_config
->access_checker_options
& FCGI_AUTHORITATIVE
))
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" },
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
},
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 */