📄 mod_proxy_http.c
字号:
int pread_len = 0; apr_table_t *save_table; int backend_broke = 0; static const char *hop_by_hop_hdrs[] = {"Keep-Alive", "Proxy-Authenticate", "TE", "Trailer", "Upgrade", NULL}; int i; const char *te = NULL; bb = apr_brigade_create(p, c->bucket_alloc); pass_bb = apr_brigade_create(p, c->bucket_alloc); /* Get response from the remote server, and pass it up the * filter chain */ rp = ap_proxy_make_fake_req(origin, r); /* In case anyone needs to know, this is a fake request that is really a * response. */ rp->proxyreq = PROXYREQ_RESPONSE; tmp_bb = apr_brigade_create(p, c->bucket_alloc); do { apr_status_t rc; apr_brigade_cleanup(bb); rc = ap_proxygetline(tmp_bb, buffer, sizeof(buffer), rp, 0, &len); if (len == 0) { /* handle one potential stray CRLF */ rc = ap_proxygetline(tmp_bb, buffer, sizeof(buffer), rp, 0, &len); } if (len <= 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, "proxy: error reading status line from remote " "server %s", backend->hostname); if (rc == APR_TIMEUP) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "proxy: read timeout"); } /* * If we are a reverse proxy request shutdown the connection * WITHOUT ANY response to trigger a retry by the client * if allowed (as for idempotent requests). * BUT currently we should not do this if the request is the * first request on a keepalive connection as browsers like * seamonkey only display an empty page in this case and do * not do a retry. We should also not do this on a * connection which times out; instead handle as * we normally would handle timeouts */ if (r->proxyreq == PROXYREQ_REVERSE && c->keepalives && rc != APR_TIMEUP) { apr_bucket *eos; ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "proxy: Closing connection to client because" " reading from backend server %s failed. Number" " of keepalives %i", backend->hostname, c->keepalives); ap_proxy_backend_broke(r, bb); /* * Add an EOC bucket to signal the ap_http_header_filter * that it should get out of our way, BUT ensure that the * EOC bucket is inserted BEFORE an EOS bucket in bb as * some resource filters like mod_deflate pass everything * up to the EOS down the chain immediately and sent the * remainder of the brigade later (or even never). But in * this case the ap_http_header_filter does not get out of * our way soon enough. */ e = ap_bucket_eoc_create(c->bucket_alloc); eos = APR_BRIGADE_LAST(bb); while ((APR_BRIGADE_SENTINEL(bb) != eos) && !APR_BUCKET_IS_EOS(eos)) { eos = APR_BUCKET_PREV(eos); } if (eos == APR_BRIGADE_SENTINEL(bb)) { APR_BRIGADE_INSERT_TAIL(bb, e); } else { APR_BUCKET_INSERT_BEFORE(eos, e); } ap_pass_brigade(r->output_filters, bb); /* Need to return OK to avoid sending an error message */ return OK; } else if (!c->keepalives) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "proxy: NOT Closing connection to client" " although reading from backend server %s" " failed.", backend->hostname); } return ap_proxyerror(r, HTTP_BAD_GATEWAY, "Error reading from remote server"); } /* XXX: Is this a real headers length send from remote? */ backend->worker->s->read += len; /* Is it an HTTP/1 response? * This is buggy if we ever see an HTTP/1.10 */ if (apr_date_checkmask(buffer, "HTTP/#.# ###*")) { int major, minor; if (2 != sscanf(buffer, "HTTP/%u.%u", &major, &minor)) { major = 1; minor = 1; } /* If not an HTTP/1 message or * if the status line was > 8192 bytes */ else if ((buffer[5] != '1') || (len >= sizeof(buffer)-1)) { return ap_proxyerror(r, HTTP_BAD_GATEWAY, apr_pstrcat(p, "Corrupt status line returned by remote " "server: ", buffer, NULL)); } backasswards = 0; keepchar = buffer[12]; buffer[12] = '\0'; r->status = atoi(&buffer[9]); if (keepchar != '\0') { buffer[12] = keepchar; } else { /* 2616 requires the space in Status-Line; the origin * server may have sent one but ap_rgetline_core will * have stripped it. */ buffer[12] = ' '; buffer[13] = '\0'; } r->status_line = apr_pstrdup(p, &buffer[9]); /* read the headers. */ /* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers*/ /* Also, take care with headers with multiple occurences. */ /* First, tuck away all already existing cookies */ save_table = apr_table_make(r->pool, 2); apr_table_do(addit_dammit, save_table, r->headers_out, "Set-Cookie", NULL); /* shove the headers direct into r->headers_out */ ap_proxy_read_headers(r, rp, buffer, sizeof(buffer), origin, &pread_len); if (r->headers_out == NULL) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server, "proxy: bad HTTP/%d.%d header " "returned by %s (%s)", major, minor, r->uri, r->method); backend->close += 1; /* * ap_send_error relies on a headers_out to be present. we * are in a bad position here.. so force everything we send out * to have nothing to do with the incoming packet */ r->headers_out = apr_table_make(r->pool,1); r->status = HTTP_BAD_GATEWAY; r->status_line = "bad gateway"; return r->status; } /* Now, add in the just read cookies */ apr_table_do(addit_dammit, save_table, r->headers_out, "Set-Cookie", NULL); /* and now load 'em all in */ if (!apr_is_empty_table(save_table)) { apr_table_unset(r->headers_out, "Set-Cookie"); r->headers_out = apr_table_overlay(r->pool, r->headers_out, save_table); } /* can't have both Content-Length and Transfer-Encoding */ if (apr_table_get(r->headers_out, "Transfer-Encoding") && apr_table_get(r->headers_out, "Content-Length")) { /* * 2616 section 4.4, point 3: "if both Transfer-Encoding * and Content-Length are received, the latter MUST be * ignored"; * * To help mitigate HTTP Splitting, unset Content-Length * and shut down the backend server connection * XXX: We aught to treat such a response as uncachable */ apr_table_unset(r->headers_out, "Content-Length"); ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "proxy: server %s returned Transfer-Encoding" " and Content-Length", backend->hostname); backend->close += 1; } /* * Save a possible Transfer-Encoding header as we need it later for * ap_http_filter to know where to end. */ te = apr_table_get(r->headers_out, "Transfer-Encoding"); /* strip connection listed hop-by-hop headers from response */ backend->close += ap_proxy_liststr(apr_table_get(r->headers_out, "Connection"), "close"); ap_proxy_clear_connection(p, r->headers_out); if ((buf = apr_table_get(r->headers_out, "Content-Type"))) { ap_set_content_type(r, apr_pstrdup(p, buf)); } if (!ap_is_HTTP_INFO(r->status)) { ap_proxy_pre_http_request(origin, rp); } /* Clear hop-by-hop headers */ for (i=0; hop_by_hop_hdrs[i]; ++i) { apr_table_unset(r->headers_out, hop_by_hop_hdrs[i]); } /* Delete warnings with wrong date */ r->headers_out = ap_proxy_clean_warnings(p, r->headers_out); /* handle Via header in response */ if (conf->viaopt != via_off && conf->viaopt != via_block) { const char *server_name = ap_get_server_name(r); /* If USE_CANONICAL_NAME_OFF was configured for the proxy virtual host, * then the server name returned by ap_get_server_name() is the * origin server name (which does make too much sense with Via: headers) * so we use the proxy vhost's name instead. */ if (server_name == r->hostname) server_name = r->server->server_hostname; /* create a "Via:" response header entry and merge it */ apr_table_addn(r->headers_out, "Via", (conf->viaopt == via_full) ? apr_psprintf(p, "%d.%d %s%s (%s)", HTTP_VERSION_MAJOR(r->proto_num), HTTP_VERSION_MINOR(r->proto_num), server_name, server_portstr, AP_SERVER_BASEVERSION) : apr_psprintf(p, "%d.%d %s%s", HTTP_VERSION_MAJOR(r->proto_num), HTTP_VERSION_MINOR(r->proto_num), server_name, server_portstr) ); } /* cancel keepalive if HTTP/1.0 or less */ if ((major < 1) || (minor < 1)) { backend->close += 1; origin->keepalive = AP_CONN_CLOSE; } } else { /* an http/0.9 response */ backasswards = 1; r->status = 200; r->status_line = "200 OK"; backend->close += 1; } if (ap_is_HTTP_INFO(r->status)) { interim_response++; } else { interim_response = 0; } if (interim_response) { /* RFC2616 tells us to forward this. * * OTOH, an interim response here may mean the backend * is playing sillybuggers. The Client didn't ask for * it within the defined HTTP/1.1 mechanisms, and if * it's an extension, it may also be unsupported by us. * * There's also the possibility that changing existing * behaviour here might break something. * * So let's make it configurable. */ const char *policy = apr_table_get(r->subprocess_env, "proxy-interim-response"); ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "proxy: HTTP: received interim %d response", r->status); if (!policy || !strcasecmp(policy, "RFC")) { ap_send_interim_response(r, 1); } /* FIXME: refine this to be able to specify per-response-status * policies and maybe also add option to bail out with 502 */ else if (strcasecmp(policy, "Suppress")) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, "undefined proxy interim response policy"); } } /* Moved the fixups of Date headers and those affected by * ProxyPassReverse/etc from here to ap_proxy_read_headers */ if ((r->status == 401) && (conf->error_override)) { const char *buf; const char *wa = "WWW-Authenticate"; if ((buf = apr_table_get(r->headers_out, wa))) { apr_table_set(r->err_headers_out, wa, buf); } else { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "proxy: origin server sent 401 without WWW-Authenticate header"); } } r->sent_bodyct = 1; /* * Is it an HTTP/0.9 response or did we maybe preread the 1st line of * the response? If so, load the extra data. These are 2 mutually * exclusive possibilities, that just happen to require very * similar behavior. */ if (backasswards || pread_len) { apr_ssize_t cntr = (apr_ssize_t)pread_len; if (backasswards) { /*@@@FIXME: * At this point in response processing of a 0.9 response, * we don't know yet whether data is binary or not. * mod_charset_lite will get control later on, so it cannot * decide on the conversion of this buffer full of data. * However, chances are that we are not really talking to an * HTTP/0.9 server, but to some different protocol, therefore * the best guess IMHO is to always treat the buffer as "text/x": */ ap_xlate_proto_to_ascii(buffer, len); cntr = (apr_ssize_t)len; } e = apr_bucket_heap_create(buffer, cntr, NULL, c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, e); } /* send body - but only if a body is expect
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -