📄 mod_proxy_ajp.c
字号:
/* 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. *//* AJP routines for Apache proxy */#include "mod_proxy.h"#include "ajp.h"module AP_MODULE_DECLARE_DATA proxy_ajp_module;/* * Canonicalise http-like URLs. * scheme is the scheme for the URL * url is the URL starting with the first '/' * def_port is the default port for this scheme. */static int proxy_ajp_canon(request_rec *r, char *url){ char *host, *path, sport[7]; char *search = NULL; const char *err; apr_port_t port = AJP13_DEF_PORT; /* ap_port_of_scheme() */ if (strncasecmp(url, "ajp:", 4) == 0) { url += 4; } else { return DECLINED; } ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "proxy: AJP: canonicalising URL %s", url); /* * do syntactic check. * We break the URL into host, port, path, search */ err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port); if (err) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "error parsing URL %s: %s", url, err); return HTTP_BAD_REQUEST; } /* * now parse path/search args, according to rfc1738: * process the path. With proxy-noncanon set (by * mod_proxy) we use the raw, unparsed uri */ if (apr_table_get(r->notes, "proxy-nocanon")) { path = url; /* this is the raw path */ } else { path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0, r->proxyreq); search = r->args; } if (path == NULL) return HTTP_BAD_REQUEST; apr_snprintf(sport, sizeof(sport), ":%d", port); if (ap_strchr_c(host, ':')) { /* if literal IPv6 address */ host = apr_pstrcat(r->pool, "[", host, "]", NULL); } r->filename = apr_pstrcat(r->pool, "proxy:ajp://", host, sport, "/", path, (search) ? "?" : "", (search) ? search : "", NULL); return OK;}#define METHOD_NON_IDEMPOTENT 0#define METHOD_IDEMPOTENT 1#define METHOD_IDEMPOTENT_WITH_ARGS 2static int is_idempotent(request_rec *r){ /* * RFC2616 (9.1.2): GET, HEAD, PUT, DELETE, OPTIONS, TRACE are considered * idempotent. Hint: HEAD requests use M_GET as method number as well. */ switch (r->method_number) { case M_GET: case M_DELETE: case M_PUT: case M_OPTIONS: case M_TRACE: /* * If the request has arguments it might have side-effects and thus * it might be undesirable to resent it to a backend again * automatically. */ if (r->args) { return METHOD_IDEMPOTENT_WITH_ARGS; } return METHOD_IDEMPOTENT; /* Everything else is not considered idempotent. */ default: return METHOD_NON_IDEMPOTENT; }}static apr_off_t get_content_length(request_rec * r){ apr_off_t len = 0; if (r->clength > 0) { return r->clength; } else if (r->main == NULL) { const char *clp = apr_table_get(r->headers_in, "Content-Length"); if (clp) { char *errp; if (apr_strtoff(&len, clp, &errp, 10) || *errp || len < 0) { len = 0; /* parse error */ } } } return len;}/* * XXX: AJP Auto Flushing * * When processing CMD_AJP13_SEND_BODY_CHUNK AJP messages we will do a poll * with FLUSH_WAIT miliseconds timeout to determine if more data is currently * available at the backend. If there is no more data available, we flush * the data to the client by adding a flush bucket to the brigade we pass * up the filter chain. * This is only a bandaid to fix the AJP/1.3 protocol shortcoming of not * sending (actually not having defined) a flush message, when the data * should be flushed to the client. As soon as this protocol shortcoming is * fixed this code should be removed. * * For further discussion see PR37100. * http://issues.apache.org/bugzilla/show_bug.cgi?id=37100 *//* * process the request and write the response. */static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r, proxy_conn_rec *conn, conn_rec *origin, proxy_dir_conf *conf, apr_uri_t *uri, char *url, char *server_portstr){ apr_status_t status; int result; apr_bucket *e; apr_bucket_brigade *input_brigade; apr_bucket_brigade *output_brigade; ajp_msg_t *msg; apr_size_t bufsiz = 0; char *buff; char *send_body_chunk_buff; apr_uint16_t size; const char *tenc; int havebody = 1; int output_failed = 0; int backend_failed = 0; apr_off_t bb_len; int data_sent = 0; int headers_sent = 0; int rv = 0; apr_int32_t conn_poll_fd; apr_pollfd_t *conn_poll; proxy_server_conf *psf = ap_get_module_config(r->server->module_config, &proxy_module); apr_size_t maxsize = AJP_MSG_BUFFER_SZ; int send_body = 0; apr_off_t content_length = 0; if (psf->io_buffer_size_set) maxsize = psf->io_buffer_size; if (maxsize > AJP_MAX_BUFFER_SZ) maxsize = AJP_MAX_BUFFER_SZ; else if (maxsize < AJP_MSG_BUFFER_SZ) maxsize = AJP_MSG_BUFFER_SZ; maxsize = APR_ALIGN(maxsize, 1024); /* * Send the AJP request to the remote server */ /* send request headers */ status = ajp_send_header(conn->sock, r, maxsize, uri); if (status != APR_SUCCESS) { conn->close++; ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server, "proxy: AJP: request failed to %pI (%s)", conn->worker->cp->addr, conn->worker->hostname); if (status == AJP_EOVERFLOW) return HTTP_BAD_REQUEST; else { /* * This is only non fatal when the method is idempotent. In this * case we can dare to retry it with a different worker if we are * a balancer member. */ if (is_idempotent(r) == METHOD_IDEMPOTENT) { return HTTP_SERVICE_UNAVAILABLE; } return HTTP_INTERNAL_SERVER_ERROR; } } /* allocate an AJP message to store the data of the buckets */ bufsiz = maxsize; status = ajp_alloc_data_msg(r->pool, &buff, &bufsiz, &msg); if (status != APR_SUCCESS) { /* We had a failure: Close connection to backend */ conn->close++; ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "proxy: ajp_alloc_data_msg failed"); return HTTP_INTERNAL_SERVER_ERROR; } /* read the first bloc of data */ input_brigade = apr_brigade_create(p, r->connection->bucket_alloc); tenc = apr_table_get(r->headers_in, "Transfer-Encoding"); if (tenc && (strcasecmp(tenc, "chunked") == 0)) { /* The AJP protocol does not want body data yet */ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "proxy: request is chunked"); } else { /* Get client provided Content-Length header */ content_length = get_content_length(r); status = ap_get_brigade(r->input_filters, input_brigade, AP_MODE_READBYTES, APR_BLOCK_READ, maxsize - AJP_HEADER_SZ); if (status != APR_SUCCESS) { /* We had a failure: Close connection to backend */ conn->close++; ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "proxy: ap_get_brigade failed"); apr_brigade_destroy(input_brigade); return HTTP_INTERNAL_SERVER_ERROR; } /* have something */ if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "proxy: APR_BUCKET_IS_EOS"); } /* Try to send something */ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "proxy: data to read (max %" APR_SIZE_T_FMT " at %" APR_SIZE_T_FMT ")", bufsiz, msg->pos); status = apr_brigade_flatten(input_brigade, buff, &bufsiz); if (status != APR_SUCCESS) { /* We had a failure: Close connection to backend */ conn->close++; apr_brigade_destroy(input_brigade); ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server, "proxy: apr_brigade_flatten"); return HTTP_INTERNAL_SERVER_ERROR; } apr_brigade_cleanup(input_brigade); ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "proxy: got %" APR_SIZE_T_FMT " bytes of data", bufsiz); if (bufsiz > 0) { status = ajp_send_data_msg(conn->sock, msg, bufsiz); if (status != APR_SUCCESS) { /* We had a failure: Close connection to backend */ conn->close++; apr_brigade_destroy(input_brigade); ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server, "proxy: send failed to %pI (%s)", conn->worker->cp->addr, conn->worker->hostname); /* * It is fatal when we failed to send a (part) of the request * body. */ return HTTP_INTERNAL_SERVER_ERROR; } conn->worker->s->transferred += bufsiz; send_body = 1; } else if (content_length > 0) { ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server, "proxy: read zero bytes, expecting" " %" APR_OFF_T_FMT " bytes", content_length); status = ajp_send_data_msg(conn->sock, msg, 0); if (status != APR_SUCCESS) { /* We had a failure: Close connection to backend */ conn->close++; ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server, "proxy: send failed to %pI (%s)", conn->worker->cp->addr, conn->worker->hostname); return HTTP_INTERNAL_SERVER_ERROR; } else { /* Client send zero bytes with C-L > 0 */ return HTTP_BAD_REQUEST; } } } /* read the response */ conn->data = NULL; status = ajp_read_header(conn->sock, r, maxsize, (ajp_msg_t **)&(conn->data)); if (status != APR_SUCCESS) { /* We had a failure: Close connection to backend */ conn->close++; apr_brigade_destroy(input_brigade); ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server, "proxy: read response failed from %pI (%s)", conn->worker->cp->addr, conn->worker->hostname); /* * This is only non fatal when we have not sent (parts) of a possible * request body so far (we do not store it and thus cannot sent it * again) and the method is idempotent. In this case we can dare to * retry it with a different worker if we are a balancer member. */ if (!send_body && (is_idempotent(r) == METHOD_IDEMPOTENT)) { return HTTP_SERVICE_UNAVAILABLE; } return HTTP_INTERNAL_SERVER_ERROR; } /* parse the reponse */ result = ajp_parse_type(r, conn->data); output_brigade = apr_brigade_create(p, r->connection->bucket_alloc); /* * Prepare apr_pollfd_t struct for possible later check if there is currently * data available from the backend (do not flush response to client)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -