| /* |
| * 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 |
| |
| static int g_variables_inited = 0; |
| static int g_busy_timeout; |
| static int g_connect_timeout; |
| static int g_comm_timeout; |
| static int g_max_requests_per_process; |
| |
| static fcgid_procnode *apply_free_procnode(server_rec * main_server, |
| 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; |
| apr_size_t share_grp_id = command->share_grp_id; |
| char *virtualhost = command->virtualhost; |
| |
| proc_table = proctable_get_table_array(); |
| previous_node = proctable_get_idle_list(); |
| busy_list_header = proctable_get_busy_list(); |
| |
| safe_lock(main_server); |
| 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 |
| && current_node->share_grp_id == share_grp_id |
| && current_node->virtualhost == virtualhost |
| && 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; |
| |
| safe_unlock(main_server); |
| return current_node; |
| } else |
| previous_node = current_node; |
| |
| current_node = next_node; |
| } |
| safe_unlock(main_server); |
| |
| /* Found nothing */ |
| return NULL; |
| } |
| |
| static void |
| return_procnode(server_rec * main_server, |
| 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(); |
| |
| safe_lock(main_server); |
| |
| /* 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; |
| } |
| |
| safe_unlock(main_server); |
| } |
| |
| static int |
| count_busy_processes(server_rec * main_server, 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(); |
| |
| safe_lock(main_server); |
| |
| 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 |
| && current_node->share_grp_id == command->share_grp_id |
| && current_node->virtualhost == command->virtualhost |
| && current_node->uid == command->uid |
| && current_node->gid == command->gid) { |
| result++; |
| } |
| next_node = &proc_table[current_node->next_index]; |
| current_node = next_node; |
| } |
| |
| safe_unlock(main_server); |
| |
| 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; |
| server_rec *main_server = ctx->ipc.request->server; |
| |
| /* Free bucket buffer */ |
| if (ctx->buffer) { |
| apr_bucket_destroy(ctx->buffer); |
| ctx->buffer = NULL; |
| } |
| |
| proc_close_ipc(main_server, &ctx->ipc); |
| |
| if (ctx->procnode) { |
| /* Return procnode |
| I will return this slot to idle(or error) list except: |
| I take too much time on this request( greater than get_busy_timeout() ), |
| so the process manager may have put this slot from busy list to error |
| list, and the contain of this slot may have been modified |
| In this case I will do nothing and return, let the process manager |
| do the job |
| */ |
| int dt = |
| apr_time_sec(apr_time_now()) - apr_time_sec(ctx->active_time); |
| if (dt > g_busy_timeout) { |
| /* Do nothing but print log */ |
| ap_log_error(APLOG_MARK, APLOG_INFO, 0, |
| main_server, |
| "mod_fcgid: process busy timeout, take %d seconds for this request", |
| dt); |
| } else if (ctx->has_error) { |
| ctx->procnode->diewhy = FCGID_DIE_COMM_ERROR; |
| return_procnode(main_server, ctx->procnode, |
| 1 /* communication error */ ); |
| } else if (g_max_requests_per_process != -1 |
| && ++ctx->procnode->requests_handled >= |
| g_max_requests_per_process) { |
| ctx->procnode->diewhy = FCGID_DIE_LIFETIME_EXPIRED; |
| return_procnode(main_server, ctx->procnode, |
| 1 /* handled all requests */ ); |
| } else |
| return_procnode(main_server, 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 && !APR_BUCKET_IS_EOS(e)) { |
| 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 1; |
| } |
| |
| static int |
| handle_request(request_rec * r, int role, const char *argv0, |
| fcgid_wrapper_conf * wrapper_conf, |
| apr_bucket_brigade * output_brigade) |
| { |
| apr_pool_t *request_pool = r->main ? r->main->pool : r->pool; |
| server_rec *main_server = r->server; |
| fcgid_command fcgi_request; |
| fcgid_bucket_ctx *bucket_ctx; |
| int i, j, cond_status; |
| apr_status_t rv; |
| apr_bucket_brigade *brigade_stdout; |
| char sbuf[MAX_STRING_LEN]; |
| const char *location; |
| |
| if (!g_variables_inited) { |
| g_connect_timeout = get_ipc_connect_timeout(r->server); |
| g_comm_timeout = get_ipc_comm_timeout(r->server); |
| g_busy_timeout = get_busy_timeout(r->server); |
| g_max_requests_per_process = |
| get_max_requests_per_process(r->server); |
| if (g_comm_timeout == 0) |
| g_comm_timeout = 1; |
| g_variables_inited = 1; |
| } |
| |
| bucket_ctx = apr_pcalloc(request_pool, sizeof(*bucket_ctx)); |
| if (!bucket_ctx) { |
| ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_os_error(), |
| r->server, |
| "mod_fcgid: apr_calloc bucket_ctx failed in handle_request function"); |
| return HTTP_INTERNAL_SERVER_ERROR; |
| } |
| bucket_ctx->ipc.connect_timeout = g_connect_timeout; |
| bucket_ctx->ipc.communation_timeout = g_comm_timeout; |
| bucket_ctx->ipc.request = r; |
| apr_pool_cleanup_register(request_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++) { |
| apr_ino_t inode = |
| wrapper_conf ? wrapper_conf->inode : r->finfo.inode; |
| apr_dev_t deviceid = |
| wrapper_conf ? wrapper_conf->deviceid : r->finfo.device; |
| apr_size_t shareid = |
| wrapper_conf ? wrapper_conf->share_group_id : 0; |
| |
| /* Init spawn request */ |
| procmgr_init_spawn_cmd(&fcgi_request, r, argv0, deviceid, |
| inode, shareid); |
| |
| /* Apply a process slot */ |
| bucket_ctx->procnode = |
| apply_free_procnode(r->server, &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->server, &fcgi_request)) { |
| apr_sleep(apr_time_from_sec(1)); |
| |
| bucket_ctx->procnode = |
| apply_free_procnode(r->server, &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 |
| (r->server, bucket_ctx->procnode, |
| &bucket_ctx->ipc) != APR_SUCCESS) { |
| proc_close_ipc(r->server, &bucket_ctx->ipc); |
| bucket_ctx->procnode->diewhy = FCGID_DIE_CONNECT_ERROR; |
| return_procnode(r->server, 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_error(APLOG_MARK, APLOG_WARNING, 0, r->server, |
| "mod_fcgid: can't apply process slot for %s", argv0); |
| return HTTP_SERVICE_UNAVAILABLE; |
| } |
| bucket_ctx->active_time = bucket_ctx->procnode->last_active_time = |
| apr_time_now(); |
| |
| /* Write output_brigade to fastcgi server */ |
| if ((rv = |
| proc_write_ipc(main_server, &bucket_ctx->ipc, |
| output_brigade)) != APR_SUCCESS) { |
| ap_log_error(APLOG_MARK, APLOG_WARNING, rv, r->server, |
| "mod_fcgid: write data to fastcgi server error"); |
| bucket_ctx->has_error = 1; |
| return HTTP_INTERNAL_SERVER_ERROR; |
| } |
| |
| /* Create brigade */ |
| brigade_stdout = |
| apr_brigade_create(request_pool, r->connection->bucket_alloc); |
| if (!brigade_stdout) { |
| ap_log_error(APLOG_MARK, APLOG_WARNING, rv, r->server, |
| "mod_fcgid: apr_brigade_create failed in handle_request function"); |
| return HTTP_INTERNAL_SERVER_ERROR; |
| } |
| APR_BRIGADE_INSERT_TAIL(brigade_stdout, |
| ap_bucket_fcgid_header_create(r->connection-> |
| bucket_alloc, |
| bucket_ctx)); |
| /*APR_BRIGADE_INSERT_TAIL(brigade_stdout, apr_bucket_flush_create(r->connection->bucket_alloc)); */ |
| |
| /* Check the script header first. If got error, return immediately */ |
| 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"); |
| |
| ap_internal_redirect_handler(location, r); |
| 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) { |
| ap_log_error(APLOG_MARK, APLOG_WARNING, rv, r->server, |
| "mod_fcgid: ap_pass_brigade failed in handle_request function"); |
| return HTTP_INTERNAL_SERVER_ERROR; |
| } |
| |
| /* Retrun condition status */ |
| return cond_status; |
| } |
| |
| int bridge_request(request_rec * r, int role, const char *argv0, |
| fcgid_wrapper_conf * wrapper_conf) |
| { |
| apr_pool_t *request_pool = r->main ? r->main->pool : r->pool; |
| server_rec *main_server = r->server; |
| apr_status_t rv = APR_SUCCESS; |
| int seen_eos; |
| size_t request_size = 0; |
| apr_file_t *fd = NULL; |
| int need_truncate = 1; |
| apr_off_t cur_pos = 0; |
| FCGI_Header *stdin_request_header; |
| apr_bucket_brigade *output_brigade; |
| apr_bucket *bucket_input, *bucket_header, *bucket_eos; |
| size_t max_request_len = get_max_request_len(main_server); |
| size_t max_mem_request_len = get_max_mem_request_len(main_server); |
| char **envp = ap_create_environment(request_pool, |
| r->subprocess_env); |
| |
| /* Create brigade for the request to fastcgi server */ |
| output_brigade = |
| apr_brigade_create(request_pool, r->connection->bucket_alloc); |
| if (!output_brigade) { |
| ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_os_error(), |
| main_server, |
| "mod_fcgid: can't alloc memory for output brigade"); |
| return HTTP_INTERNAL_SERVER_ERROR; |
| } |
| |
| /* Build the begin request and environ request, append them to output_brigade */ |
| if (!build_begin_block |
| (role, r->server, r->connection->bucket_alloc, output_brigade) |
| || !build_env_block(r->server, envp, r->connection->bucket_alloc, |
| output_brigade)) { |
| ap_log_error(APLOG_MARK, APLOG_WARNING, 0, |
| main_server, |
| "mod_fcgid: can't build begin or env request"); |
| return HTTP_INTERNAL_SERVER_ERROR; |
| } |
| |
| /* Stdin header and body */ |
| /* XXX HACK: 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. |
| Buf sometimes it's not acceptable(think about uploading a larage attachment) |
| file_bucket is a better choice in this case... |
| To do, or not to do, that is the question ^_^ |
| */ |
| seen_eos = 0; |
| do { |
| apr_bucket_brigade *input_brigade = |
| apr_brigade_create(request_pool, |
| r->connection->bucket_alloc); |
| |
| if (!input_brigade |
| || (rv = ap_get_brigade(r->input_filters, input_brigade, |
| AP_MODE_READBYTES, |
| APR_BLOCK_READ, |
| HUGE_STRING_LEN)) != APR_SUCCESS) { |
| ap_log_error(APLOG_MARK, APLOG_WARNING, rv, |
| main_server, |
| "mod_fcgid: can't get data from http client"); |
| apr_brigade_destroy(output_brigade); |
| if (input_brigade) |
| apr_brigade_destroy(input_brigade); |
| return HTTP_INTERNAL_SERVER_ERROR; |
| } |
| |
| request_size = 0; |
| for (bucket_input = APR_BRIGADE_FIRST(input_brigade); |
| bucket_input != APR_BRIGADE_SENTINEL(input_brigade); |
| bucket_input = APR_BUCKET_NEXT(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_error(APLOG_MARK, APLOG_WARNING, rv, |
| main_server, |
| "mod_fcgid: can't read request from HTTP client"); |
| apr_brigade_destroy(input_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 > max_request_len) { |
| ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_os_error(), |
| main_server, |
| "mod_fcgid: http request length %d > %d", |
| request_size, max_request_len); |
| return HTTP_INTERNAL_SERVER_ERROR; |
| } |
| |
| if (request_size > max_mem_request_len) { |
| apr_size_t wrote_len; |
| static const char *fd_key = "fcgid_fd"; |
| |
| if (fd == NULL) |
| apr_pool_userdata_get((void **) &fd, fd_key, |
| r->connection->pool); |
| |
| if (fd == NULL) { |
| const char *tempdir = NULL; |
| char *template; |
| |
| rv = apr_temp_dir_get(&tempdir, r->pool); |
| if (rv != APR_SUCCESS) { |
| ap_log_error(APLOG_MARK, APLOG_WARNING, |
| apr_get_os_error(), main_server, |
| "mod_fcigd: 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_error(APLOG_MARK, APLOG_WARNING, |
| apr_get_os_error(), main_server, |
| "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); |
| } else if (need_truncate) { |
| need_truncate = 0; |
| apr_file_trunc(fd, 0); |
| cur_pos = 0; |
| } |
| // 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_error(APLOG_MARK, APLOG_WARNING, |
| apr_get_os_error(), main_server, |
| "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 (!stdin_request_header || !bucket_header || !bucket_stdin |
| || !init_header(FCGI_STDIN, 1, len, 0, |
| stdin_request_header)) { |
| ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_os_error(), |
| main_server, |
| "mod_fcgid: can't alloc memory for stdin request"); |
| apr_brigade_destroy(input_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_destroy(input_brigade); |
| } |
| while (!seen_eos); |
| |
| /* 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 (!stdin_request_header || !bucket_header |
| || !init_header(FCGI_STDIN, 1, 0, 0, stdin_request_header)) { |
| ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_os_error(), |
| main_server, |
| "mod_fcgid: can't alloc memory for stdin request"); |
| return HTTP_INTERNAL_SERVER_ERROR; |
| } |
| APR_BRIGADE_INSERT_TAIL(output_brigade, bucket_header); |
| |
| /* The eos bucket now */ |
| bucket_eos = apr_bucket_eos_create(r->connection->bucket_alloc); |
| if (!bucket_eos) { |
| ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_os_error(), |
| main_server, |
| "mod_fcgid: can't alloc memory for eos bucket"); |
| return HTTP_INTERNAL_SERVER_ERROR; |
| } |
| APR_BRIGADE_INSERT_TAIL(output_brigade, bucket_eos); |
| |
| /* Bridge the request */ |
| return handle_request(r, role, argv0, wrapper_conf, output_brigade); |
| } |