static ngx_int_t prepare_request_buffer_construction(ngx_http_request_t *r, passenger_context_t *context, buffer_construction_state *state) { unsigned int len; ngx_uint_t port; struct sockaddr_in *sin; #if (NGX_HAVE_INET6) struct sockaddr_in6 *sin6; #endif switch (r->method) { case NGX_HTTP_GET: SET_NGX_STR(&state->method, "GET "); break; case NGX_HTTP_HEAD: SET_NGX_STR(&state->method, "HEAD "); break; case NGX_HTTP_POST: SET_NGX_STR(&state->method, "POST "); break; case NGX_HTTP_PUT: SET_NGX_STR(&state->method, "PUT "); break; case NGX_HTTP_DELETE: SET_NGX_STR(&state->method, "DELETE "); break; case NGX_HTTP_MKCOL: SET_NGX_STR(&state->method, "MKCOL "); break; case NGX_HTTP_COPY: SET_NGX_STR(&state->method, "COPY "); break; case NGX_HTTP_MOVE: SET_NGX_STR(&state->method, "MOVE "); break; case NGX_HTTP_OPTIONS: SET_NGX_STR(&state->method, "OPTIONS "); break; case NGX_HTTP_PROPFIND: SET_NGX_STR(&state->method, "PROPFIND "); break; case NGX_HTTP_PROPPATCH: SET_NGX_STR(&state->method, "PROPPATCH "); break; case NGX_HTTP_LOCK: SET_NGX_STR(&state->method, "LOCK "); break; case NGX_HTTP_UNLOCK: SET_NGX_STR(&state->method, "UNLOCK "); break; case NGX_HTTP_PATCH: SET_NGX_STR(&state->method, "PATCH "); break; case NGX_HTTP_TRACE: SET_NGX_STR(&state->method, "TRACE "); break; default: SET_NGX_STR(&state->method, "UNKNOWN "); break; } state->app_type.data = (u_char *) pp_get_app_type_name(context->app_type); /* Include null terminator */ state->app_type.len = strlen((const char *) state->app_type.data); /* * Nginx unescapes URI's before passing them to Phusion Passenger, * but backend processes expect the escaped version. * http://code.google.com/p/phusion-passenger/issues/detail?id=404 * * Here we check whether Nginx has rewritten the URI or not. If not, * we can use the raw, unparsed URI as sent by the client. */ if (r->valid_unparsed_uri && r->main) { state->escaped_uri = r->unparsed_uri; const char *pos = memchr((const char *) r->unparsed_uri.data, '?', r->unparsed_uri.len); if (pos != NULL) { state->escaped_uri.len = pos - (const char *) r->unparsed_uri.data; } } else { state->escaped_uri.len = 2 * ngx_escape_uri(NULL, r->uri.data, r->uri.len, NGX_ESCAPE_URI) + r->uri.len; state->escaped_uri.data = ngx_pnalloc(r->pool, state->escaped_uri.len); if (state->escaped_uri.data == NULL) { return NGX_ERROR; } ngx_escape_uri(state->escaped_uri.data, r->uri.data, r->uri.len, NGX_ESCAPE_URI); } if (r->headers_in.chunked) { /* If the request body is chunked, then Nginx sets r->headers_in.content_length_n * but does not set r->headers_in.headers, so we add this header ourselves. */ state->content_length.data = ngx_pnalloc(r->pool, sizeof("4294967295") - 1); state->content_length.len = ngx_snprintf(state->content_length.data, sizeof("4294967295") - 1, "%O", r->headers_in.content_length_n) - state->content_length.data; } state->core_password.data = (u_char *) pp_agents_starter_get_core_password( pp_agents_starter, &len); state->core_password.len = len; switch (r->connection->sockaddr->sa_family) { #if (NGX_HAVE_INET6) case AF_INET6: sin6 = (struct sockaddr_in6 *) r->connection->sockaddr; port = ntohs(sin6->sin6_port); break; #endif #if (NGX_HAVE_UNIX_DOMAIN) case AF_UNIX: port = 0; break; #endif default: /* AF_INET */ sin = (struct sockaddr_in *) r->connection->sockaddr; port = ntohs(sin->sin_port); break; } state->remote_port.data = ngx_pnalloc(r->pool, sizeof("65535") - 1); if (state->remote_port.data == NULL) { return NGX_ERROR; } if (port > 0 && port < 65536) { state->remote_port.len = ngx_snprintf(state->remote_port.data, sizeof("65535") - 1, "%ui", port) - state->remote_port.data; } else { state->remote_port.len = 0; } return NGX_OK; }
static ngx_int_t create_request(ngx_http_request_t *r) { u_char ch; const char * helper_agent_request_socket_password_data; unsigned int helper_agent_request_socket_password_len; u_char buf[sizeof("4294967296") + 1]; size_t len, size, key_len, val_len; const u_char *app_type_string; size_t app_type_string_len; int server_name_len; ngx_str_t escaped_uri; ngx_str_t *union_station_filters = NULL; void *tmp; ngx_uint_t i, n; ngx_buf_t *b; ngx_chain_t *cl, *body; ngx_list_part_t *part; ngx_table_elt_t *header; ngx_http_script_code_pt code; ngx_http_script_engine_t e, le; ngx_http_core_srv_conf_t *cscf; passenger_loc_conf_t *slcf; passenger_context_t *context; ngx_http_script_len_code_pt lcode; cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); slcf = ngx_http_get_module_loc_conf(r, ngx_http_passenger_module); context = ngx_http_get_module_ctx(r, ngx_http_passenger_module); if (context == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } app_type_string = (const u_char *) pp_get_app_type_name(context->app_type); app_type_string_len = strlen((const char *) app_type_string) + 1; /* include null terminator */ /* * Nginx unescapes URI's before passing them to Phusion Passenger, * but backend processes expect the escaped version. * http://code.google.com/p/phusion-passenger/issues/detail?id=404 */ escaped_uri.len = 2 * ngx_escape_uri(NULL, r->uri.data, r->uri.len, NGX_ESCAPE_URI) + r->uri.len; escaped_uri.data = ngx_pnalloc(r->pool, escaped_uri.len + 1); escaped_uri.data[escaped_uri.len] = '\0'; ngx_escape_uri(escaped_uri.data, r->uri.data, r->uri.len, NGX_ESCAPE_URI); /************************************************** * Determine the request header length. **************************************************/ len = 0; /* Length of the Content-Length header. A value of -1 means that the content * length is unspecified, which is the case for e.g. WebSocket requests. */ if (r->headers_in.content_length_n >= 0) { len += sizeof("CONTENT_LENGTH") + uint_to_str(r->headers_in.content_length_n, buf, sizeof(buf)) + 1; /* +1 for trailing null */ } /* DOCUMENT_ROOT, SCRIPT_NAME, RAILS_RELATIVE_URL_ROOT, PATH_INFO and REQUEST_URI. */ len += sizeof("DOCUMENT_ROOT") + context->public_dir.len + 1; if (context->base_uri.len > 0) { len += sizeof("SCRIPT_NAME") + context->base_uri.len + 1; len += sizeof("RAILS_RELATIVE_URL_ROOT") + context->base_uri.len + 1; len += sizeof("PATH_INFO") + escaped_uri.len - context->base_uri.len + 1; } else { len += sizeof("SCRIPT_NAME") + sizeof(""); len += sizeof("PATH_INFO") + escaped_uri.len + 1; } len += sizeof("REQUEST_URI") + escaped_uri.len + 1; if (r->args.len > 0) { len += 1 + r->args.len; } /* SERVER_NAME; must be equal to HTTP_HOST without the port part */ if (r->headers_in.host != NULL) { tmp = memchr(r->headers_in.host->value.data, ':', r->headers_in.host->value.len); if (tmp == NULL) { server_name_len = r->headers_in.host->value.len; } else { server_name_len = (int) ((const u_char *) tmp - r->headers_in.host->value.data); } } else { server_name_len = cscf->server_name.len; } len += sizeof("SERVER_NAME") + server_name_len + 1; /* Various other HTTP headers. */ if (r->headers_in.content_type != NULL && r->headers_in.content_type->value.len > 0) { len += sizeof("CONTENT_TYPE") + r->headers_in.content_type->value.len + 1; } #if (NGX_HTTP_SSL) if (r->http_connection->ssl) { len += sizeof("HTTPS") + sizeof("on"); } #endif /* Lengths of Passenger application pool options. */ len += slcf->options_cache.len; len += sizeof("PASSENGER_APP_TYPE") + app_type_string_len; if (slcf->union_station_filters != NGX_CONF_UNSET_PTR && slcf->union_station_filters->nelts > 0) { len += sizeof("UNION_STATION_FILTERS"); union_station_filters = (ngx_str_t *) slcf->union_station_filters->elts; for (i = 0; i < slcf->union_station_filters->nelts; i++) { if (i != 0) { len++; } len += union_station_filters[i].len; } len++; } /***********************/ /***********************/ /* Lengths of various CGI variables. */ if (slcf->vars_len) { ngx_memzero(&le, sizeof(ngx_http_script_engine_t)); ngx_http_script_flush_no_cacheable_variables(r, slcf->flushes); le.flushed = 1; le.ip = slcf->vars_len->elts; le.request = r; while (*(uintptr_t *) le.ip) { lcode = *(ngx_http_script_len_code_pt *) le.ip; key_len = lcode(&le); for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { lcode = *(ngx_http_script_len_code_pt *) le.ip; } le.ip += sizeof(uintptr_t); len += key_len + val_len; } } /* Lengths of HTTP headers. */ if (slcf->upstream_config.pass_request_headers) { part = &r->headers_in.headers.part; header = part->elts; for (i = 0; /* void */; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; header = part->elts; i = 0; } if (!header_is_transfer_encoding(&header[i].key)) { len += sizeof("HTTP_") - 1 + header[i].key.len + 1 + header[i].value.len + 1; } } } /************************************************** * Build the request header data. **************************************************/ helper_agent_request_socket_password_data = pp_agents_starter_get_request_socket_password(pp_agents_starter, &helper_agent_request_socket_password_len); size = helper_agent_request_socket_password_len + /* netstring length + ":" + trailing "," */ /* note: 10 == sizeof("4294967296") - 1 */ len + 10 + 1 + 1; b = ngx_create_temp_buf(r->pool, size); if (b == NULL) { return NGX_ERROR; } cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NGX_ERROR; } cl->buf = b; /* Build SCGI header netstring length part. */ b->last = ngx_copy(b->last, helper_agent_request_socket_password_data, helper_agent_request_socket_password_len); b->last = ngx_snprintf(b->last, 10, "%ui", len); *b->last++ = (u_char) ':'; if (r->headers_in.content_length_n >= 0) { b->last = ngx_copy(b->last, "CONTENT_LENGTH", sizeof("CONTENT_LENGTH")); b->last = ngx_snprintf(b->last, 10, "%ui", r->headers_in.content_length_n); *b->last++ = (u_char) 0; } /* Build DOCUMENT_ROOT, SCRIPT_NAME, RAILS_RELATIVE_URL_ROOT, PATH_INFO and REQUEST_URI. */ b->last = ngx_copy(b->last, "DOCUMENT_ROOT", sizeof("DOCUMENT_ROOT")); b->last = ngx_copy(b->last, context->public_dir.data, context->public_dir.len + 1); if (context->base_uri.len > 0) { b->last = ngx_copy(b->last, "SCRIPT_NAME", sizeof("SCRIPT_NAME")); b->last = ngx_copy(b->last, context->base_uri.data, context->base_uri.len + 1); b->last = ngx_copy(b->last, "RAILS_RELATIVE_URL_ROOT", sizeof("RAILS_RELATIVE_URL_ROOT")); b->last = ngx_copy(b->last, context->base_uri.data, context->base_uri.len + 1); b->last = ngx_copy(b->last, "PATH_INFO", sizeof("PATH_INFO")); b->last = ngx_copy(b->last, escaped_uri.data + context->base_uri.len, escaped_uri.len - context->base_uri.len); b->last = ngx_copy(b->last, "", 1); } else { b->last = ngx_copy(b->last, "SCRIPT_NAME", sizeof("SCRIPT_NAME")); b->last = ngx_copy(b->last, "", sizeof("")); b->last = ngx_copy(b->last, "PATH_INFO", sizeof("PATH_INFO")); b->last = ngx_copy(b->last, escaped_uri.data, escaped_uri.len); b->last = ngx_copy(b->last, "", 1); } b->last = ngx_copy(b->last, "REQUEST_URI", sizeof("REQUEST_URI")); b->last = ngx_copy(b->last, escaped_uri.data, escaped_uri.len); if (r->args.len > 0) { b->last = ngx_copy(b->last, "?", 1); b->last = ngx_copy(b->last, r->args.data, r->args.len); } b->last = ngx_copy(b->last, "", 1); /* SERVER_NAME */ b->last = ngx_copy(b->last, "SERVER_NAME", sizeof("SERVER_NAME")); if (r->headers_in.host != NULL) { b->last = ngx_copy(b->last, r->headers_in.host->value.data, server_name_len); } else { b->last = ngx_copy(b->last, cscf->server_name.data, server_name_len); } b->last = ngx_copy(b->last, "", 1); /* Various other HTTP headers. */ if (r->headers_in.content_type != NULL && r->headers_in.content_type->value.len > 0) { b->last = ngx_copy(b->last, "CONTENT_TYPE", sizeof("CONTENT_TYPE")); b->last = ngx_copy(b->last, r->headers_in.content_type->value.data, r->headers_in.content_type->value.len); b->last = ngx_copy(b->last, "", 1); } #if (NGX_HTTP_SSL) if (r->http_connection->ssl) { b->last = ngx_copy(b->last, "HTTPS", sizeof("HTTPS")); b->last = ngx_copy(b->last, "on", sizeof("on")); } #endif /* Build Passenger application pool option headers. */ b->last = ngx_copy(b->last, slcf->options_cache.data, slcf->options_cache.len); b->last = ngx_copy(b->last, "PASSENGER_APP_TYPE", sizeof("PASSENGER_APP_TYPE")); b->last = ngx_copy(b->last, app_type_string, app_type_string_len); if (slcf->union_station_filters != NGX_CONF_UNSET_PTR && slcf->union_station_filters->nelts > 0) { b->last = ngx_copy(b->last, "UNION_STATION_FILTERS", sizeof("UNION_STATION_FILTERS")); for (i = 0; i < slcf->union_station_filters->nelts; i++) { if (i != 0) { b->last = ngx_copy(b->last, "\1", 1); } b->last = ngx_copy(b->last, union_station_filters[i].data, union_station_filters[i].len); } b->last = ngx_copy(b->last, "\0", 1); } /***********************/ /***********************/ if (slcf->vars_len) { ngx_memzero(&e, sizeof(ngx_http_script_engine_t)); e.ip = slcf->vars->elts; e.pos = b->last; e.request = r; e.flushed = 1; le.ip = slcf->vars_len->elts; while (*(uintptr_t *) le.ip) { lcode = *(ngx_http_script_len_code_pt *) le.ip; (void) lcode(&le); for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { lcode = *(ngx_http_script_len_code_pt *) le.ip; } le.ip += sizeof(uintptr_t); while (*(uintptr_t *) e.ip) { code = *(ngx_http_script_code_pt *) e.ip; code((ngx_http_script_engine_t *) &e); } e.ip += sizeof(uintptr_t); } b->last = e.pos; } if (slcf->upstream_config.pass_request_headers) { part = &r->headers_in.headers.part; header = part->elts; for (i = 0; /* void */; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; header = part->elts; i = 0; } if (header_is_transfer_encoding(&header[i].key)) { continue; } b->last = ngx_cpymem(b->last, "HTTP_", sizeof("HTTP_") - 1); for (n = 0; n < header[i].key.len; n++) { ch = header[i].key.data[n]; if (ch >= 'a' && ch <= 'z') { ch &= ~0x20; } else if (ch == '-') { ch = '_'; } *b->last++ = ch; } *b->last++ = (u_char) 0; b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len); *b->last++ = (u_char) 0; } } *b->last++ = (u_char) ','; if (slcf->upstream_config.pass_request_body) { body = r->upstream->request_bufs; r->upstream->request_bufs = cl; while (body) { b = ngx_alloc_buf(r->pool); if (b == NULL) { return NGX_ERROR; } ngx_memcpy(b, body->buf, sizeof(ngx_buf_t)); cl->next = ngx_alloc_chain_link(r->pool); if (cl->next == NULL) { return NGX_ERROR; } cl = cl->next; cl->buf = b; body = body->next; } b->flush = 1; } else { r->upstream->request_bufs = cl; } cl->next = NULL; return NGX_OK; }