1 /* Copyright 1999-2005 The Apache Software Foundation or its licensors, as
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 /* AJP routines for Apache proxy */
19 #include "mod_proxy.h"
22 module AP_MODULE_DECLARE_DATA proxy_ajp_module;
25 * Canonicalise http-like URLs.
26 * scheme is the scheme for the URL
27 * url is the URL starting with the first '/'
28 * def_port is the default port for this scheme.
30 static int proxy_ajp_canon(request_rec *r, char *url)
32 char *host, *path, *search, sport[7];
34 apr_port_t port = AJP13_DEF_PORT;
36 /* ap_port_of_scheme() */
37 if (strncasecmp(url, "ajp:", 4) == 0) {
44 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
45 "proxy: AJP: canonicalising URL %s", url);
49 * We break the URL into host, port, path, search
51 err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
53 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
54 "error parsing URL %s: %s",
56 return HTTP_BAD_REQUEST;
60 * now parse path/search args, according to rfc1738
62 * N.B. if this isn't a true proxy request, then the URL _path_
63 * has already been decoded. True proxy requests have
64 * r->uri == r->unparsed_uri, and no others have that property.
66 if (r->uri == r->unparsed_uri) {
67 search = strchr(url, '?');
75 path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0,
78 return HTTP_BAD_REQUEST;
80 apr_snprintf(sport, sizeof(sport), ":%d", port);
82 if (ap_strchr_c(host, ':')) {
83 /* if literal IPv6 address */
84 host = apr_pstrcat(r->pool, "[", host, "]", NULL);
86 r->filename = apr_pstrcat(r->pool, "proxy:ajp://", host, sport,
87 "/", path, (search) ? "?" : "",
88 (search) ? search : "", NULL);
93 * XXX: Flushing bandaid
95 * When processing CMD_AJP13_SEND_BODY_CHUNK AJP messages we will do a poll
96 * with FLUSH_WAIT miliseconds timeout to determine if more data is currently
97 * available at the backend. If there is no more data available, we flush
98 * the data to the client by adding a flush bucket to the brigade we pass
99 * up the filter chain.
100 * This is only a bandaid to fix the AJP/1.3 protocol shortcoming of not
101 * sending (actually not having defined) a flush message, when the data
102 * should be flushed to the client. As soon as this protocol shortcoming is
103 * fixed this code should be removed.
105 * For further discussion see PR37100.
106 * http://issues.apache.org/bugzilla/show_bug.cgi?id=37100
108 #define FLUSHING_BANDAID 1
110 #ifdef FLUSHING_BANDAID
112 * Wait 10000 microseconds to find out if more data is currently
113 * available at the backend. Just an arbitrary choose.
115 #define FLUSH_WAIT 10000
119 * process the request and write the response.
121 static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r,
122 proxy_conn_rec *conn,
124 proxy_dir_conf *conf,
126 char *url, char *server_portstr)
131 apr_bucket_brigade *input_brigade;
132 apr_bucket_brigade *output_brigade;
143 #ifdef FLUSHING_BANDAID
144 apr_int32_t conn_poll_fd;
145 apr_pollfd_t *conn_poll;
149 * Send the AJP request to the remote server
152 /* send request headers */
153 status = ajp_send_header(conn->sock, r, uri);
154 if (status != APR_SUCCESS) {
156 ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
157 "proxy: AJP: request failed to %pI (%s)",
158 conn->worker->cp->addr,
159 conn->worker->hostname);
160 if (status == AJP_EOVERFLOW)
161 return HTTP_BAD_REQUEST;
163 return HTTP_SERVICE_UNAVAILABLE;
166 /* allocate an AJP message to store the data of the buckets */
167 status = ajp_alloc_data_msg(r->pool, &buff, &bufsiz, &msg);
168 if (status != APR_SUCCESS) {
169 /* We had a failure: Close connection to backend */
171 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
172 "proxy: ajp_alloc_data_msg failed");
173 return HTTP_INTERNAL_SERVER_ERROR;
176 /* read the first bloc of data */
177 input_brigade = apr_brigade_create(p, r->connection->bucket_alloc);
178 tenc = apr_table_get(r->headers_in, "Transfer-Encoding");
179 if (tenc && (strcasecmp(tenc, "chunked") == 0)) {
180 /* The AJP protocol does not want body data yet */
181 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
182 "proxy: request is chunked");
184 status = ap_get_brigade(r->input_filters, input_brigade,
185 AP_MODE_READBYTES, APR_BLOCK_READ,
186 AJP13_MAX_SEND_BODY_SZ);
188 if (status != APR_SUCCESS) {
189 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
190 "proxy: ap_get_brigade failed");
191 apr_brigade_destroy(input_brigade);
192 return HTTP_INTERNAL_SERVER_ERROR;
196 if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
197 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
198 "proxy: APR_BUCKET_IS_EOS");
201 /* Try to send something */
202 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
203 "proxy: data to read (max %" APR_SIZE_T_FMT
204 " at %" APR_SIZE_T_FMT ")", bufsiz, msg->pos);
206 status = apr_brigade_flatten(input_brigade, buff, &bufsiz);
207 if (status != APR_SUCCESS) {
208 /* We had a failure: Close connection to backend */
210 apr_brigade_destroy(input_brigade);
211 ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
212 "proxy: apr_brigade_flatten");
213 return HTTP_INTERNAL_SERVER_ERROR;
215 apr_brigade_cleanup(input_brigade);
217 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
218 "proxy: got %" APR_SIZE_T_FMT " bytes of data", bufsiz);
220 status = ajp_send_data_msg(conn->sock, msg, bufsiz);
221 if (status != APR_SUCCESS) {
222 /* We had a failure: Close connection to backend */
224 apr_brigade_destroy(input_brigade);
225 ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
226 "proxy: send failed to %pI (%s)",
227 conn->worker->cp->addr,
228 conn->worker->hostname);
229 return HTTP_SERVICE_UNAVAILABLE;
231 conn->worker->s->transferred += bufsiz;
235 /* read the response */
237 status = ajp_read_header(conn->sock, r,
238 (ajp_msg_t **)&(conn->data));
239 if (status != APR_SUCCESS) {
240 /* We had a failure: Close connection to backend */
242 apr_brigade_destroy(input_brigade);
243 ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
244 "proxy: read response failed from %pI (%s)",
245 conn->worker->cp->addr,
246 conn->worker->hostname);
247 return HTTP_SERVICE_UNAVAILABLE;
249 /* parse the reponse */
250 result = ajp_parse_type(r, conn->data);
251 output_brigade = apr_brigade_create(p, r->connection->bucket_alloc);
253 #ifdef FLUSHING_BANDAID
255 * Prepare apr_pollfd_t struct for later check if there is currently
256 * data available from the backend (do not flush response to client)
257 * or not (flush response to client)
259 conn_poll = apr_pcalloc(p, sizeof(apr_pollfd_t));
260 conn_poll->reqevents = APR_POLLIN;
261 conn_poll->desc_type = APR_POLL_SOCKET;
262 conn_poll->desc.s = conn->sock;
265 bufsiz = AJP13_MAX_SEND_BODY_SZ;
268 case CMD_AJP13_GET_BODY_CHUNK:
270 if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
271 /* This is the end */
274 ap_log_error(APLOG_MARK, APLOG_DEBUG, status, r->server,
275 "proxy: APR_BUCKET_IS_EOS");
277 status = ap_get_brigade(r->input_filters, input_brigade,
280 AJP13_MAX_SEND_BODY_SZ);
281 if (status != APR_SUCCESS) {
282 ap_log_error(APLOG_MARK, APLOG_DEBUG, status,
284 "ap_get_brigade failed");
287 bufsiz = AJP13_MAX_SEND_BODY_SZ;
288 status = apr_brigade_flatten(input_brigade, buff,
290 apr_brigade_cleanup(input_brigade);
291 if (status != APR_SUCCESS) {
292 ap_log_error(APLOG_MARK, APLOG_DEBUG, status,
294 "apr_brigade_flatten failed");
300 /* will go in ajp_send_data_msg */
301 status = ajp_send_data_msg(conn->sock, msg, bufsiz);
302 if (status != APR_SUCCESS) {
303 ap_log_error(APLOG_MARK, APLOG_DEBUG, status, r->server,
304 "ajp_send_data_msg failed");
307 conn->worker->s->transferred += bufsiz;
310 * something is wrong TC asks for more body but we are
311 * already at the end of the body data
313 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
314 "ap_proxy_ajp_request error read after end");
318 case CMD_AJP13_SEND_HEADERS:
319 /* AJP13_SEND_HEADERS: process them */
320 status = ajp_parse_header(r, conf, conn->data);
321 if (status != APR_SUCCESS) {
325 case CMD_AJP13_SEND_BODY_CHUNK:
326 /* AJP13_SEND_BODY_CHUNK: piece of data */
327 status = ajp_parse_data(r, conn->data, &size, &buff);
328 if (status == APR_SUCCESS) {
329 e = apr_bucket_transient_create(buff, size,
330 r->connection->bucket_alloc);
331 APR_BRIGADE_INSERT_TAIL(output_brigade, e);
333 #ifdef FLUSHING_BANDAID
335 * If there is no more data available from backend side
336 * currently, flush response to client.
338 if (apr_poll(conn_poll, 1, &conn_poll_fd, FLUSH_WAIT)
340 e = apr_bucket_flush_create(r->connection->bucket_alloc);
341 APR_BRIGADE_INSERT_TAIL(output_brigade, e);
344 apr_brigade_length(output_brigade, 0, &bb_len);
346 conn->worker->s->read += bb_len;
347 if (ap_pass_brigade(r->output_filters,
348 output_brigade) != APR_SUCCESS) {
349 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
350 "proxy: error processing body");
354 apr_brigade_cleanup(output_brigade);
360 case CMD_AJP13_END_RESPONSE:
361 e = apr_bucket_eos_create(r->connection->bucket_alloc);
362 APR_BRIGADE_INSERT_TAIL(output_brigade, e);
363 if (ap_pass_brigade(r->output_filters,
364 output_brigade) != APR_SUCCESS) {
365 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
366 "proxy: error processing body");
377 * If connection has been aborted by client: Stop working.
378 * Nevertheless, we regard our operation so far as a success:
379 * So do not set isok to 0 and set result to CMD_AJP13_END_RESPONSE
380 * But: Close this connection to the backend.
382 if (r->connection->aborted) {
384 result = CMD_AJP13_END_RESPONSE;
391 if (result == CMD_AJP13_END_RESPONSE)
394 /* read the response */
395 status = ajp_read_header(conn->sock, r,
396 (ajp_msg_t **)&(conn->data));
397 if (status != APR_SUCCESS) {
399 ap_log_error(APLOG_MARK, APLOG_DEBUG, status, r->server,
400 "ajp_read_header failed");
403 result = ajp_parse_type(r, conn->data);
405 apr_brigade_destroy(input_brigade);
408 * Clear output_brigade to remove possible buckets that remained there
411 apr_brigade_cleanup(output_brigade);
413 if (status != APR_SUCCESS) {
414 /* We had a failure: Close connection to backend */
416 ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
417 "proxy: send body failed to %pI (%s)",
418 conn->worker->cp->addr,
419 conn->worker->hostname);
421 * If we already send data, signal a broken backend connection
422 * upwards in the chain.
425 ap_proxy_backend_broke(r, output_brigade);
426 /* Return DONE to avoid error messages being added to the stream */
429 rv = HTTP_SERVICE_UNAVAILABLE;
433 * Ensure that we sent an EOS bucket thru the filter chain, if we already
434 * have sent some data. Maybe ap_proxy_backend_broke was called and added
435 * one to the brigade already. So we should not do this in this case.
437 if (data_sent && !r->eos_sent && APR_BRIGADE_EMPTY(output_brigade)) {
438 e = apr_bucket_eos_create(r->connection->bucket_alloc);
439 APR_BRIGADE_INSERT_TAIL(output_brigade, e);
442 /* If we have added something to the brigade above, sent it */
443 if (!APR_BRIGADE_EMPTY(output_brigade))
444 ap_pass_brigade(r->output_filters, output_brigade);
446 apr_brigade_destroy(output_brigade);
451 /* Nice we have answer to send to the client */
452 if (result == CMD_AJP13_END_RESPONSE && isok) {
453 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
454 "proxy: got response from %pI (%s)",
455 conn->worker->cp->addr,
456 conn->worker->hostname);
460 ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
461 "proxy: got bad response (%d) from %pI (%s)",
463 conn->worker->cp->addr,
464 conn->worker->hostname);
466 /* We had a failure: Close connection to backend */
468 return HTTP_SERVICE_UNAVAILABLE;
472 * This handles ajp:// URLs
474 static int proxy_ajp_handler(request_rec *r, proxy_worker *worker,
475 proxy_server_conf *conf,
476 char *url, const char *proxyname,
477 apr_port_t proxyport)
480 char server_portstr[32];
481 conn_rec *origin = NULL;
482 proxy_conn_rec *backend = NULL;
483 const char *scheme = "AJP";
484 proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config,
488 * Note: Memory pool allocation.
489 * A downstream keepalive connection is always connected to the existence
490 * (or not) of an upstream keepalive connection. If this is not done then
491 * load balancing against multiple backend servers breaks (one backend
492 * server ends up taking 100% of the load), and the risk is run of
493 * downstream keepalive connections being kept open unnecessarily. This
494 * keeps webservers busy and ties up resources.
496 * As a result, we allocate all sockets out of the upstream connection
497 * pool, and when we want to reuse a socket, we check first whether the
498 * connection ID of the current upstream connection is the same as that
499 * of the connection when the socket was opened.
501 apr_pool_t *p = r->connection->pool;
502 apr_uri_t *uri = apr_palloc(r->connection->pool, sizeof(*uri));
505 if (strncasecmp(url, "ajp:", 4) != 0) {
506 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
507 "proxy: AJP: declining URL %s", url);
510 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
511 "proxy: AJP: serving URL %s", url);
513 /* create space for state information */
515 status = ap_proxy_acquire_connection(scheme, &backend, worker,
519 backend->close_on_recycle = 1;
520 ap_proxy_release_connection(scheme, backend, r->server);
527 backend->close_on_recycle = 0;
529 /* Step One: Determine Who To Connect To */
530 status = ap_proxy_determine_connection(p, r, conf, worker, backend,
531 uri, &url, proxyname, proxyport,
533 sizeof(server_portstr));
538 /* Step Two: Make the Connection */
539 if (ap_proxy_connect_backend(scheme, backend, worker, r->server)) {
540 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
541 "proxy: AJP: failed to make connection to backend: %s",
543 status = HTTP_SERVICE_UNAVAILABLE;
547 /* Step Three: Process the Request */
548 status = ap_proxy_ajp_request(p, r, backend, origin, dconf, uri, url,
552 /* Do not close the socket */
553 ap_proxy_release_connection(scheme, backend, r->server);
557 static void ap_proxy_http_register_hook(apr_pool_t *p)
559 proxy_hook_scheme_handler(proxy_ajp_handler, NULL, NULL, APR_HOOK_FIRST);
560 proxy_hook_canon_handler(proxy_ajp_canon, NULL, NULL, APR_HOOK_FIRST);
563 module AP_MODULE_DECLARE_DATA proxy_ajp_module = {
564 STANDARD20_MODULE_STUFF,
565 NULL, /* create per-directory config structure */
566 NULL, /* merge per-directory config structures */
567 NULL, /* create per-server config structure */
568 NULL, /* merge per-server config structures */
569 NULL, /* command apr_table_t */
570 ap_proxy_http_register_hook /* register hooks */