📄 proxy_http.c
字号:
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? If so, send the extra data */
if (backasswards) {
apr_ssize_t cntr = len;
/*@@@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);
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 expected */
if ((!r->header_only) && /* not HEAD request */
!interim_response && /* not any 1xx response */
(r->status != HTTP_NO_CONTENT) && /* not 204 */
(r->status != HTTP_RESET_CONTENT) && /* not 205 */
(r->status != HTTP_NOT_MODIFIED)) { /* not 304 */
/* We need to copy the output headers and treat them as input
* headers as well. BUT, we need to do this before we remove
* TE, so that they are preserved accordingly for
* ap_http_filter to know where to end.
*/
rp->headers_in = apr_table_copy(r->pool, r->headers_out);
apr_table_unset(r->headers_out,"Transfer-Encoding");
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
"proxy: start body send");
/*
* if we are overriding the errors, we can't put the content
* of the page into the brigade
*/
if (conf->error_override == 0 || ap_is_HTTP_SUCCESS(r->status)) {
/* read the body, pass it to the output filters */
int finish = FALSE;
while (ap_get_brigade(rp->input_filters,
bb,
AP_MODE_READBYTES,
APR_BLOCK_READ,
conf->io_buffer_size) == APR_SUCCESS) {
#if DEBUGGING
{
apr_off_t readbytes;
apr_brigade_length(bb, 0, &readbytes);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
r->server, "proxy (PID %d): readbytes: %#x",
getpid(), readbytes);
}
#endif
/* sanity check */
if (APR_BRIGADE_EMPTY(bb)) {
apr_brigade_cleanup(bb);
break;
}
/* found the last brigade? */
if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
/* if this is the last brigade, cleanup the
* backend connection first to prevent the
* backend server from hanging around waiting
* for a slow client to eat these bytes
*/
backend->close = 1;
/* signal that we must leave */
finish = TRUE;
}
/* try send what we read */
if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS) {
/* Ack! Phbtt! Die! User aborted! */
backend->close = 1; /* this causes socket close below */
finish = TRUE;
}
/* make sure we always clean up after ourselves */
apr_brigade_cleanup(bb);
/* if we are done, leave */
if (TRUE == finish) {
break;
}
}
}
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
"proxy: end body send");
} else {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
"proxy: header only");
}
} while (interim_response);
if (conf->error_override) {
/* the code above this checks for 'OK' which is what the hook expects */
if (ap_is_HTTP_SUCCESS(r->status))
return OK;
else {
/* clear r->status for override error, otherwise ErrorDocument
* thinks that this is a recursive error, and doesn't find the
* custom error page
*/
int status = r->status;
r->status = HTTP_OK;
/* Discard body, if one is expected */
if ((status != HTTP_NO_CONTENT) && /* not 204 */
(status != HTTP_RESET_CONTENT) && /* not 205 */
(status != HTTP_NOT_MODIFIED)) { /* not 304 */
ap_discard_request_body(rp);
}
return status;
}
} else
return OK;
}
static
apr_status_t ap_proxy_http_cleanup(const char *scheme, request_rec *r,
proxy_conn_rec *backend)
{
/* If there are no KeepAlives, or if the connection has been signalled
* to close, close the socket and clean up
*/
/* if the connection is < HTTP/1.1, or Connection: close,
* we close the socket, otherwise we leave it open for KeepAlive support
*/
if (backend->close || (r->proto_num < HTTP_VERSION(1,1))) {
backend->close_on_recycle = 1;
ap_set_module_config(r->connection->conn_config, &proxy_http_module, NULL);
ap_proxy_release_connection(scheme, backend, r->server);
}
return OK;
}
/*
* This handles http:// URLs, and other URLs using a remote proxy over http
* If proxyhost is NULL, then contact the server directly, otherwise
* go via the proxy.
* Note that if a proxy is used, then URLs other than http: can be accessed,
* also, if we have trouble which is clearly specific to the proxy, then
* we return DECLINED so that we can try another proxy. (Or the direct
* route.)
*/
int ap_proxy_http_handler(request_rec *r, proxy_worker *worker,
proxy_server_conf *conf,
char *url, const char *proxyname,
apr_port_t proxyport)
{
int status;
char server_portstr[32];
char *scheme;
const char *proxy_function;
const char *u;
proxy_conn_rec *backend = NULL;
int is_ssl = 0;
/* Note: Memory pool allocation.
* A downstream keepalive connection is always connected to the existence
* (or not) of an upstream keepalive connection. If this is not done then
* load balancing against multiple backend servers breaks (one backend
* server ends up taking 100% of the load), and the risk is run of
* downstream keepalive connections being kept open unnecessarily. This
* keeps webservers busy and ties up resources.
*
* As a result, we allocate all sockets out of the upstream connection
* pool, and when we want to reuse a socket, we check first whether the
* connection ID of the current upstream connection is the same as that
* of the connection when the socket was opened.
*/
apr_pool_t *p = r->connection->pool;
conn_rec *c = r->connection;
apr_uri_t *uri = apr_palloc(r->connection->pool, sizeof(*uri));
/* find the scheme */
u = strchr(url, ':');
if (u == NULL || u[1] != '/' || u[2] != '/' || u[3] == '\0')
return DECLINED;
if ((u - url) > 14)
return HTTP_BAD_REQUEST;
scheme = apr_pstrndup(c->pool, url, u - url);
/* scheme is lowercase */
apr_tolower(scheme);
/* is it for us? */
if (strcmp(scheme, "https") == 0) {
if (!ap_proxy_ssl_enable(NULL)) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
"proxy: HTTPS: declining URL %s"
" (mod_ssl not configured?)", url);
return DECLINED;
}
is_ssl = 1;
proxy_function = "HTTPS";
}
else if (!(strcmp(scheme, "http") == 0 || (strcmp(scheme, "ftp") == 0 && proxyname))) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
"proxy: HTTP: declining URL %s", url);
return DECLINED; /* only interested in HTTP, or FTP via proxy */
}
else {
if (*scheme == 'h')
proxy_function = "HTTP";
else
proxy_function = "FTP";
}
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
"proxy: HTTP: serving URL %s", url);
/* only use stored info for top-level pages. Sub requests don't share
* in keepalives
*/
if (!r->main) {
backend = (proxy_conn_rec *) ap_get_module_config(c->conn_config,
&proxy_http_module);
}
/* create space for state information */
if (!backend) {
status = ap_proxy_acquire_connection(proxy_function, &backend, worker, r->server);
if (status != OK) {
if (backend) {
backend->close_on_recycle = 1;
ap_proxy_release_connection(proxy_function, backend, r->server);
}
return status;
}
if (!r->main) {
ap_set_module_config(c->conn_config, &proxy_http_module, backend);
}
}
backend->is_ssl = is_ssl;
backend->close_on_recycle = 1;
/* Step One: Determine Who To Connect To */
status = ap_proxy_determine_connection(p, r, conf, worker, backend, c->pool,
uri, &url, proxyname, proxyport,
server_portstr,
sizeof(server_portstr));
if ( status != OK ) {
return status;
}
/* Step Two: Make the Connection */
if (ap_proxy_connect_backend(proxy_function, backend, worker, r->server)) {
return HTTP_SERVICE_UNAVAILABLE;
}
/* Step Three: Create conn_rec */
if (!backend->connection) {
status = ap_proxy_connection_create(proxy_function, backend, c, r->server);
if (status != OK)
return status;
}
/* Step Four: Send the Request */
status = ap_proxy_http_request(p, r, backend, backend->connection, conf, uri, url,
server_portstr);
if ( status != OK ) {
return status;
}
/* Step Five: Receive the Response */
status = ap_proxy_http_process_response(p, r, backend, backend->connection, conf,
server_portstr);
if (status != OK) {
/* clean up even if there is an error */
ap_proxy_http_cleanup(proxy_function, r, backend);
return status;
}
/* Step Six: Clean Up */
status = ap_proxy_http_cleanup(proxy_function, r, backend);
if ( status != OK ) {
return status;
}
return OK;
}
static void ap_proxy_http_register_hook(apr_pool_t *p)
{
proxy_hook_scheme_handler(ap_proxy_http_handler, NULL, NULL, APR_HOOK_FIRST);
proxy_hook_canon_handler(ap_proxy_http_canon, NULL, NULL, APR_HOOK_FIRST);
}
module AP_MODULE_DECLARE_DATA proxy_http_module = {
STANDARD20_MODULE_STUFF,
NULL, /* create per-directory config structure */
NULL, /* merge per-directory config structures */
NULL, /* create per-server config structure */
NULL, /* merge per-server config structures */
NULL, /* command apr_table_t */
ap_proxy_http_register_hook/* register hooks */
};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -