| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "httpd.h" |
| #include "http_request.h" |
| #include "apr_strings.h" |
| #include "apr_portable.h" |
| #include "apr_pools.h" |
| #include "apr_file_io.h" |
| #include "util_script.h" |
| #include "fcgid_bridge.h" |
| #include "fcgid_pm.h" |
| #include "fcgid_proctbl.h" |
| #include "fcgid_proc.h" |
| #include "fcgid_conf.h" |
| #include "fcgid_spawn_ctl.h" |
| #include "fcgid_protocol.h" |
| #include "fcgid_bucket.h" |
| #define FCGID_APPLY_TRY_COUNT 2 |
| #define FCGID_REQUEST_COUNT 32 |
| #define FCGID_BRIGADE_CLEAN_STEP 32 |
| |
| static fcgid_procnode *apply_free_procnode(request_rec *r, |
| fcgid_command * command) |
| { |
| /* Scan idle list, find a node match inode, deviceid and groupid |
| If there is no one there, return NULL */ |
| fcgid_procnode *previous_node, *current_node, *next_node; |
| fcgid_procnode *busy_list_header, *proc_table; |
| apr_ino_t inode = command->inode; |
| apr_dev_t deviceid = command->deviceid; |
| uid_t uid = command->uid; |
| gid_t gid = command->gid; |
| const char *cmdline = command->cmdline; |
| |
| proc_table = proctable_get_table_array(); |
| previous_node = proctable_get_idle_list(); |
| busy_list_header = proctable_get_busy_list(); |
| |
| proctable_lock(r); |
| current_node = &proc_table[previous_node->next_index]; |
| while (current_node != proc_table) { |
| next_node = &proc_table[current_node->next_index]; |
| |
| if (current_node->inode == inode |
| && current_node->deviceid == deviceid |
| && !strcmp(current_node->cmdline, cmdline) |
| && current_node->vhost_id == command->vhost_id |
| && current_node->uid == uid && current_node->gid == gid) { |
| /* Unlink from idle list */ |
| previous_node->next_index = current_node->next_index; |
| |
| /* Link to busy list */ |
| current_node->next_index = busy_list_header->next_index; |
| busy_list_header->next_index = current_node - proc_table; |
| |
| proctable_unlock(r); |
| return current_node; |
| } |
| else |
| previous_node = current_node; |
| |
| current_node = next_node; |
| } |
| proctable_unlock(r); |
| |
| /* Found nothing */ |
| return NULL; |
| } |
| |
| static void |
| return_procnode(request_rec *r, |
| fcgid_procnode *procnode, int communicate_error) |
| { |
| fcgid_procnode *previous_node, *current_node, *next_node; |
| fcgid_procnode *proc_table = proctable_get_table_array(); |
| fcgid_procnode *error_list_header = proctable_get_error_list(); |
| fcgid_procnode *idle_list_header = proctable_get_idle_list(); |
| fcgid_procnode *busy_list_header = proctable_get_busy_list(); |
| |
| proctable_lock(r); |
| |
| /* Unlink the node from busy list first */ |
| previous_node = busy_list_header; |
| current_node = &proc_table[previous_node->next_index]; |
| while (current_node != proc_table) { |
| next_node = &proc_table[current_node->next_index]; |
| if (current_node == procnode) { |
| /* Unlink from busy list */ |
| previous_node->next_index = current_node->next_index; |
| break; |
| } |
| else |
| previous_node = current_node; |
| current_node = next_node; |
| } |
| |
| /* Return to error list or idle list */ |
| if (communicate_error) { |
| /* Link to error list */ |
| procnode->next_index = error_list_header->next_index; |
| error_list_header->next_index = procnode - proc_table; |
| } |
| else { |
| /* Link to idle list */ |
| procnode->next_index = idle_list_header->next_index; |
| idle_list_header->next_index = procnode - proc_table; |
| } |
| |
| proctable_unlock(r); |
| } |
| |
| static int count_busy_processes(request_rec *r, fcgid_command *command) |
| { |
| int result = 0; |
| fcgid_procnode *previous_node, *current_node, *next_node; |
| fcgid_procnode *proc_table = proctable_get_table_array(); |
| fcgid_procnode *busy_list_header = proctable_get_busy_list(); |
| |
| proctable_lock(r); |
| |
| previous_node = busy_list_header; |
| current_node = &proc_table[previous_node->next_index]; |
| while (current_node != proc_table) { |
| if (current_node->inode == command->inode |
| && current_node->deviceid == command->deviceid |
| && !strcmp(current_node->cmdline, command->cmdline) |
| && current_node->vhost_id == command->vhost_id |
| && current_node->uid == command->uid |
| && current_node->gid == command->gid) { |
| result++; |
| } |
| next_node = &proc_table[current_node->next_index]; |
| current_node = next_node; |
| } |
| |
| proctable_unlock(r); |
| |
| return result; |
| } |
| |
| apr_status_t bucket_ctx_cleanup(void *thectx) |
| { |
| /* Cleanup jobs: |
| 1. Free bucket buffer |
| 2. Return procnode |
| NOTE: ipc will be clean when request pool cleanup, so I don't need to close it here |
| */ |
| fcgid_bucket_ctx *ctx = (fcgid_bucket_ctx *) thectx; |
| request_rec *r = ctx->ipc.request; |
| |
| /* Free bucket buffer */ |
| if (ctx->buffer) { |
| apr_bucket_destroy(ctx->buffer); |
| ctx->buffer = NULL; |
| } |
| |
| /* proc_close_ipc() and ipc_handle_cleanup() do their own sanity |
| * checks, but we'll do our own anyway |
| */ |
| if (ctx->ipc.ipc_handle_info) { |
| proc_close_ipc(&ctx->ipc); |
| ctx->ipc.ipc_handle_info = NULL; |
| } |
| |
| if (ctx->procnode) { |
| ++ctx->procnode->requests_handled; |
| |
| /* Return procnode |
| I will return this slot to idle(or error) list |
| */ |
| if (ctx->procnode->diewhy == FCGID_DIE_BUSY_TIMEOUT) { |
| ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, |
| "mod_fcgid: %s took longer than busy timeout " |
| "(%d secs)", |
| r->uri, |
| ctx->procnode->cmdopts.busy_timeout); |
| return_procnode(r, ctx->procnode, 1 /* busy timeout */ ); |
| } |
| else if (ctx->has_error) { |
| ctx->procnode->diewhy = FCGID_DIE_COMM_ERROR; |
| return_procnode(r, ctx->procnode, 1 /* communication error */ ); |
| } |
| else if (ctx->procnode->cmdopts.max_requests_per_process |
| && ctx->procnode->requests_handled >= |
| ctx->procnode->cmdopts.max_requests_per_process) { |
| ctx->procnode->diewhy = FCGID_DIE_LIFETIME_EXPIRED; |
| return_procnode(r, ctx->procnode, 1 /* handled all requests */ ); |
| } |
| else |
| return_procnode(r, ctx->procnode, 0 /* communication ok */ ); |
| |
| ctx->procnode = NULL; |
| } |
| |
| return APR_SUCCESS; |
| } |
| |
| static int getsfunc_fcgid_BRIGADE(char *buf, int len, void *arg) |
| { |
| apr_bucket_brigade *bb = (apr_bucket_brigade *) arg; |
| const char *dst_end = buf + len - 1; /* leave room for terminating null */ |
| char *dst = buf; |
| apr_bucket *e = APR_BRIGADE_FIRST(bb); |
| apr_status_t rv; |
| int done = 0; |
| int getLF = 0; |
| int getColon = 0; |
| |
| while ((dst < dst_end) && !done && e != APR_BRIGADE_SENTINEL(bb)) { |
| const char *bucket_data; |
| apr_size_t bucket_data_len; |
| const char *src; |
| const char *src_end; |
| apr_bucket *next; |
| |
| rv = apr_bucket_read(e, &bucket_data, &bucket_data_len, |
| APR_BLOCK_READ); |
| if (rv != APR_SUCCESS) { |
| return 0; |
| } |
| |
| /* Move on to next bucket if it's fastcgi header bucket */ |
| if (e->type == &ap_bucket_type_fcgid_header |
| || e->type == &apr_bucket_type_immortal) { |
| next = APR_BUCKET_NEXT(e); |
| apr_bucket_delete(e); |
| e = next; |
| if (getLF) { |
| done = 1; |
| } |
| continue; |
| } |
| |
| if (bucket_data_len == 0) |
| return 0; |
| |
| /* Base on RFC2616 section 4.2 */ |
| src = bucket_data; |
| src_end = bucket_data + bucket_data_len; |
| while ((src < src_end) && (dst < dst_end) && !done) { |
| if (*src == ':') |
| getColon = 1; |
| |
| if (getLF && ((*src != ' ' && *src != '\t') || !getColon)) { |
| done = 1; |
| getColon = 0; |
| break; |
| } |
| else if (getLF && (*src == ' ' || *src == '\t')) { |
| *dst++ = '\r'; |
| *dst++ = '\n'; |
| getLF = 0; |
| } |
| |
| if (*src == '\n') { |
| getLF = 1; |
| } |
| else if (*src != '\r') { |
| *dst++ = *src; |
| } |
| src++; |
| } |
| |
| if (src < src_end) { |
| apr_bucket_split(e, src - bucket_data); |
| } |
| next = APR_BUCKET_NEXT(e); |
| apr_bucket_delete(e); |
| e = next; |
| } |
| *dst = 0; |
| return done; |
| } |
| |
| static int |
| handle_request_ipc(request_rec *r, int role, |
| apr_bucket_brigade *output_brigade, |
| fcgid_bucket_ctx *bucket_ctx, const char **location_ptr) |
| { |
| int cond_status; |
| apr_status_t rv; |
| apr_bucket_brigade *brigade_stdout; |
| char sbuf[MAX_STRING_LEN]; |
| const char *location; |
| |
| /* Write output_brigade to fastcgi server */ |
| if ((rv = proc_write_ipc(&bucket_ctx->ipc, |
| output_brigade)) != APR_SUCCESS) { |
| bucket_ctx->has_error = 1; |
| return HTTP_INTERNAL_SERVER_ERROR; |
| } |
| |
| /* Create brigade */ |
| brigade_stdout = |
| apr_brigade_create(r->pool, r->connection->bucket_alloc); |
| APR_BRIGADE_INSERT_TAIL(brigade_stdout, |
| ap_bucket_fcgid_header_create(r->connection-> |
| bucket_alloc, |
| bucket_ctx)); |
| |
| /* Check the script header first; return immediately on error. */ |
| if ((cond_status = |
| ap_scan_script_header_err_core(r, sbuf, getsfunc_fcgid_BRIGADE, |
| brigade_stdout)) >= 400) { |
| return cond_status; |
| } |
| |
| /* Check redirect */ |
| location = apr_table_get(r->headers_out, "Location"); |
| |
| if (location && location[0] == '/' && r->status == 200) { |
| /* This redirect needs to be a GET no matter what the original |
| * method was. |
| */ |
| r->method = apr_pstrdup(r->pool, "GET"); |
| r->method_number = M_GET; |
| |
| /* We already read the message body (if any), so don't allow |
| * the redirected request to think it has one. We can ignore |
| * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR. |
| */ |
| apr_table_unset(r->headers_in, "Content-Length"); |
| |
| *location_ptr = location; |
| return HTTP_OK; |
| } |
| else if (location && r->status == 200) { |
| /* XX Note that if a script wants to produce its own Redirect |
| * body, it now has to explicitly *say* "Status: 302" |
| */ |
| return HTTP_MOVED_TEMPORARILY; |
| } |
| |
| /* Now pass to output filter */ |
| if (role == FCGI_RESPONDER |
| && (rv = ap_pass_brigade(r->output_filters, |
| brigade_stdout)) != APR_SUCCESS) { |
| if (!APR_STATUS_IS_ECONNABORTED(rv)) { |
| ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r, |
| "mod_fcgid: ap_pass_brigade failed in " |
| "handle_request_ipc function"); |
| } |
| |
| return HTTP_INTERNAL_SERVER_ERROR; |
| } |
| |
| return cond_status; |
| } |
| |
| static int |
| handle_request(request_rec * r, int role, fcgid_cmd_conf *cmd_conf, |
| apr_bucket_brigade * output_brigade) |
| { |
| fcgid_command fcgi_request; |
| fcgid_bucket_ctx *bucket_ctx; |
| int i, j, cond_status; |
| const char *location = NULL; |
| |
| bucket_ctx = apr_pcalloc(r->pool, sizeof(*bucket_ctx)); |
| |
| bucket_ctx->ipc.request = r; |
| apr_pool_cleanup_register(r->pool, bucket_ctx, |
| bucket_ctx_cleanup, apr_pool_cleanup_null); |
| |
| /* Try to get a connected ipc handle */ |
| for (i = 0; i < FCGID_REQUEST_COUNT; i++) { |
| /* Apply a free process slot, send a spawn request if I can't get one */ |
| for (j = 0; j < FCGID_APPLY_TRY_COUNT; j++) { |
| /* Init spawn request */ |
| procmgr_init_spawn_cmd(&fcgi_request, r, cmd_conf); |
| |
| bucket_ctx->ipc.connect_timeout = |
| fcgi_request.cmdopts.ipc_connect_timeout; |
| bucket_ctx->ipc.communation_timeout = |
| fcgi_request.cmdopts.ipc_comm_timeout; |
| |
| /* Apply a process slot */ |
| bucket_ctx->procnode = apply_free_procnode(r, &fcgi_request); |
| if (bucket_ctx->procnode) |
| break; |
| |
| /* Avoid sleeping the very first time through if there are no |
| busy processes; the problem is just that we haven't spawned |
| anything yet, so waiting is pointless */ |
| if (i > 0 || j > 0 || count_busy_processes(r, &fcgi_request)) { |
| apr_sleep(apr_time_from_sec(1)); |
| |
| bucket_ctx->procnode = apply_free_procnode(r, &fcgi_request); |
| if (bucket_ctx->procnode) |
| break; |
| } |
| |
| /* Send a spawn request if I can't get a process slot */ |
| procmgr_post_spawn_cmd(&fcgi_request, r); |
| } |
| |
| /* Connect to the fastcgi server */ |
| if (bucket_ctx->procnode) { |
| if (proc_connect_ipc(bucket_ctx->procnode, |
| &bucket_ctx->ipc) != APR_SUCCESS) { |
| proc_close_ipc(&bucket_ctx->ipc); |
| bucket_ctx->procnode->diewhy = FCGID_DIE_CONNECT_ERROR; |
| return_procnode(r, bucket_ctx->procnode, 1 /* has error */ ); |
| bucket_ctx->procnode = NULL; |
| } |
| else |
| break; |
| } |
| } |
| |
| /* Now I get a connected ipc handle */ |
| if (!bucket_ctx->procnode) { |
| ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, |
| "mod_fcgid: can't apply process slot for %s", |
| cmd_conf->cmdline); |
| return HTTP_SERVICE_UNAVAILABLE; |
| } |
| bucket_ctx->active_time = bucket_ctx->procnode->last_active_time = |
| apr_time_now(); |
| bucket_ctx->procnode->diewhy = FCGID_DIE_KILLSELF; |
| |
| cond_status = handle_request_ipc(r, role, output_brigade, |
| bucket_ctx, &location); |
| |
| /* Release the process ASAP. This may already have been done in |
| * ap_pass_brigade() by fcgid_header_bucket_read(), but not in the |
| * case where handle_request_ipc() returned early without reading |
| * the body of the HTTP response. This could be because of an error, |
| * or because of a role or a status code which permits us to ignore |
| * the message body. |
| * |
| * As an example, when handling a request in the FCGI_AUTHORIZER role, |
| * we don't read through to the end of the response from the process, |
| * we just read the HTTP headers. That means each phase of the |
| * request handling sequence (e.g., authentication, authorization, etc.) |
| * will require its own process unless we make sure to always release |
| * any process we acquired regardless of whether we're reading the |
| * response body. |
| * |
| * As another example, if we perform or cause an internal redirection |
| * (for instance, by returning an error code that invokes a script |
| * handler in ap_die() because of an ErrorDocument configuration), then |
| * we must also release the process we acquired here so that it is |
| * potentially available during the next handling phase. |
| */ |
| |
| apr_pool_cleanup_run(r->pool, bucket_ctx, bucket_ctx_cleanup); |
| |
| /* Perform internal redirection if necessary */ |
| if (location) { |
| ap_internal_redirect_handler(location, r); |
| } |
| |
| /* Retrun condition status */ |
| return cond_status; |
| } |
| |
| static int add_request_body(request_rec *r, apr_pool_t *request_pool, |
| apr_bucket_brigade *output_brigade) |
| { |
| apr_bucket *bucket_input, *bucket_header; |
| apr_file_t *fd = NULL; |
| apr_off_t cur_pos = 0, request_size = 0; |
| apr_status_t rv; |
| FCGI_Header *stdin_request_header; |
| fcgid_server_conf *sconf = ap_get_module_config(r->server->module_config, |
| &fcgid_module); |
| int seen_eos; |
| |
| /* Stdin header and body */ |
| /* I have to read all the request into memory before sending it |
| to fastcgi application server, this prevents slow clients from |
| keeping the server in processing too long. |
| But sometimes it's not acceptable (think about uploading a large attachment) |
| Request will be stored in tmp file if the size larger than max_mem_request_len |
| */ |
| seen_eos = 0; |
| |
| apr_bucket_brigade *input_brigade = apr_brigade_create(request_pool, |
| r->connection-> |
| bucket_alloc); |
| apr_bucket_brigade *tmp_brigade = apr_brigade_create(request_pool, |
| r->connection-> |
| bucket_alloc); |
| |
| do { |
| int loop_counter = 0; |
| |
| if ((rv = ap_get_brigade(r->input_filters, input_brigade, |
| AP_MODE_READBYTES, |
| APR_BLOCK_READ, |
| HUGE_STRING_LEN)) != APR_SUCCESS) { |
| ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r, |
| "mod_fcgid: can't get data from http client"); |
| apr_brigade_destroy(output_brigade); |
| apr_brigade_destroy(tmp_brigade); |
| apr_brigade_destroy(input_brigade); |
| return HTTP_INTERNAL_SERVER_ERROR; |
| } |
| |
| |
| |
| while((bucket_input = APR_BRIGADE_FIRST(input_brigade)) != APR_BRIGADE_SENTINEL(input_brigade)) { |
| |
| ++loop_counter; |
| if((loop_counter % FCGID_BRIGADE_CLEAN_STEP) == 0) { |
| apr_brigade_cleanup(tmp_brigade); |
| } |
| APR_BUCKET_REMOVE(bucket_input); |
| APR_BRIGADE_INSERT_TAIL(tmp_brigade, bucket_input); |
| |
| const char *data; |
| apr_size_t len; |
| apr_bucket *bucket_stdin; |
| |
| if (APR_BUCKET_IS_EOS(bucket_input)) { |
| seen_eos = 1; |
| break; |
| } |
| |
| if (APR_BUCKET_IS_METADATA(bucket_input)) |
| continue; |
| |
| if ((rv = apr_bucket_read(bucket_input, &data, &len, |
| APR_BLOCK_READ)) != APR_SUCCESS) { |
| ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r, |
| "mod_fcgid: can't read request from HTTP client"); |
| apr_brigade_destroy(input_brigade); |
| apr_brigade_destroy(tmp_brigade); |
| apr_brigade_destroy(output_brigade); |
| return HTTP_INTERNAL_SERVER_ERROR; |
| } |
| |
| /* Append a header, and the the bucket */ |
| stdin_request_header = apr_bucket_alloc(sizeof(FCGI_Header), |
| r->connection-> |
| bucket_alloc); |
| bucket_header = |
| apr_bucket_heap_create((const char *) stdin_request_header, |
| sizeof(*stdin_request_header), |
| apr_bucket_free, |
| r->connection->bucket_alloc); |
| |
| request_size += len; |
| if (request_size > sconf->max_request_len) { |
| ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, |
| "mod_fcgid: HTTP request length %" APR_OFF_T_FMT |
| " (so far) exceeds MaxRequestLen (%" |
| APR_OFF_T_FMT ")", request_size, |
| sconf->max_request_len); |
| return HTTP_INTERNAL_SERVER_ERROR; |
| } |
| |
| if (request_size > sconf->max_mem_request_len) { |
| apr_size_t wrote_len; |
| static const char *fd_key = "fcgid_fd"; |
| |
| if (fd == NULL) { |
| void *tmp; |
| apr_pool_userdata_get(&tmp, fd_key, r->connection->pool); |
| fd = tmp; |
| |
| if (fd != NULL) { |
| if ((rv = apr_file_trunc(fd, 0)) != APR_SUCCESS) { |
| ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r, |
| "mod_fcgid: can't truncate existing " |
| "temporary file"); |
| return HTTP_INTERNAL_SERVER_ERROR; |
| } |
| } |
| } |
| |
| if (fd == NULL) { |
| const char *tempdir = NULL; |
| char *template; |
| |
| rv = apr_temp_dir_get(&tempdir, r->pool); |
| if (rv != APR_SUCCESS) { |
| ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r, |
| "mod_fcgid: can't get tmp dir"); |
| return HTTP_INTERNAL_SERVER_ERROR; |
| } |
| |
| apr_filepath_merge(&template, tempdir, |
| "fcgid.tmp.XXXXXX", |
| APR_FILEPATH_NATIVE, r->pool); |
| rv = apr_file_mktemp(&fd, template, 0, |
| r->connection->pool); |
| if (rv != APR_SUCCESS) { |
| ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r, |
| "mod_fcgid: can't open tmp file fot stdin request"); |
| return HTTP_INTERNAL_SERVER_ERROR; |
| } |
| apr_pool_userdata_set((const void *) fd, fd_key, |
| apr_pool_cleanup_null, |
| r->connection->pool); |
| } |
| |
| /* Write request to tmp file */ |
| if ((rv = |
| apr_file_write_full(fd, (const void *) data, len, |
| &wrote_len)) != APR_SUCCESS |
| || len != wrote_len) { |
| ap_log_rerror(APLOG_MARK, APLOG_WARNING, |
| rv, r, |
| "mod_fcgid: can't write tmp file for stdin request"); |
| return HTTP_INTERNAL_SERVER_ERROR; |
| } |
| /* Create file bucket */ |
| bucket_stdin = |
| apr_bucket_file_create(fd, cur_pos, len, r->pool, |
| r->connection->bucket_alloc); |
| cur_pos += len; |
| } |
| else { |
| if (APR_BUCKET_IS_HEAP(bucket_input)) |
| apr_bucket_copy(bucket_input, &bucket_stdin); |
| else { |
| /* mod_ssl have a bug? */ |
| char *pcopydata = |
| apr_bucket_alloc(len, r->connection->bucket_alloc); |
| memcpy(pcopydata, data, len); |
| bucket_stdin = |
| apr_bucket_heap_create(pcopydata, len, |
| apr_bucket_free, |
| r->connection->bucket_alloc); |
| } |
| } |
| |
| if (!init_header(FCGI_STDIN, 1, len, 0, stdin_request_header)) { |
| ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, |
| "mod_fcgid: header overflow"); |
| apr_brigade_destroy(input_brigade); |
| apr_brigade_destroy(tmp_brigade); |
| apr_brigade_destroy(output_brigade); |
| return HTTP_INTERNAL_SERVER_ERROR; |
| } |
| APR_BRIGADE_INSERT_TAIL(output_brigade, bucket_header); |
| APR_BRIGADE_INSERT_TAIL(output_brigade, bucket_stdin); |
| } |
| |
| apr_brigade_cleanup(input_brigade); |
| apr_brigade_cleanup(tmp_brigade); |
| } |
| while (!seen_eos); |
| |
| apr_brigade_destroy(input_brigade); |
| apr_brigade_destroy(tmp_brigade); |
| |
| /* Append an empty body stdin header */ |
| stdin_request_header = apr_bucket_alloc(sizeof(FCGI_Header), |
| r->connection->bucket_alloc); |
| bucket_header = |
| apr_bucket_heap_create((const char *) stdin_request_header, |
| sizeof(*stdin_request_header), |
| apr_bucket_free, r->connection->bucket_alloc); |
| if (!init_header(FCGI_STDIN, 1, 0, 0, stdin_request_header)) { |
| ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, |
| "mod_fcgid: header overflow"); |
| return HTTP_INTERNAL_SERVER_ERROR; |
| } |
| APR_BRIGADE_INSERT_TAIL(output_brigade, bucket_header); |
| |
| return 0; |
| } |
| |
| int bridge_request(request_rec * r, int role, fcgid_cmd_conf *cmd_conf) |
| { |
| apr_bucket_brigade *output_brigade; |
| apr_bucket *bucket_eos; |
| char **envp = ap_create_environment(r->pool, |
| r->subprocess_env); |
| int rc; |
| |
| /* Create brigade for the request to fastcgi server */ |
| output_brigade = |
| apr_brigade_create(r->pool, r->connection->bucket_alloc); |
| |
| /* Build the begin request and environ request, append them to output_brigade */ |
| if (!build_begin_block |
| (role, r, r->connection->bucket_alloc, output_brigade) |
| || !build_env_block(r, envp, r->connection->bucket_alloc, |
| output_brigade)) { |
| ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, |
| "mod_fcgid: can't build begin or env request"); |
| return HTTP_INTERNAL_SERVER_ERROR; |
| } |
| |
| if (role == FCGI_RESPONDER) { |
| rc = add_request_body(r, r->pool, output_brigade); |
| if (rc) { |
| return rc; |
| } |
| } |
| |
| /* The eos bucket now */ |
| bucket_eos = apr_bucket_eos_create(r->connection->bucket_alloc); |
| APR_BRIGADE_INSERT_TAIL(output_brigade, bucket_eos); |
| |
| /* Bridge the request */ |
| return handle_request(r, role, cmd_conf, output_brigade); |
| } |