]> granicus.if.org Git - apache/blob - modules/proxy/mod_proxy_ajp.c
* Correctly signal broken backend connections up the chain also for the ajp
[apache] / modules / proxy / mod_proxy_ajp.c
1 /* Copyright 1999-2005 The Apache Software Foundation or its licensors, as
2  * applicable.
3  *
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
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 /* AJP routines for Apache proxy */
18
19 #include "mod_proxy.h"
20 #include "ajp.h"
21
22 module AP_MODULE_DECLARE_DATA proxy_ajp_module;
23
24 /*
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.
29  */
30 static int proxy_ajp_canon(request_rec *r, char *url)
31 {
32     char *host, *path, *search, sport[7];
33     const char *err;
34     apr_port_t port = AJP13_DEF_PORT;
35
36     /* ap_port_of_scheme() */
37     if (strncasecmp(url, "ajp:", 4) == 0) {
38         url += 4;
39     }
40     else {
41         return DECLINED;
42     }
43
44     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
45              "proxy: AJP: canonicalising URL %s", url);
46
47     /*
48      * do syntactic check.
49      * We break the URL into host, port, path, search
50      */
51     err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
52     if (err) {
53         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
54                       "error parsing URL %s: %s",
55                       url, err);
56         return HTTP_BAD_REQUEST;
57     }
58
59     /*
60      * now parse path/search args, according to rfc1738
61      *
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.
65      */
66     if (r->uri == r->unparsed_uri) {
67         search = strchr(url, '?');
68         if (search != NULL)
69             *(search++) = '\0';
70     }
71     else
72         search = r->args;
73
74     /* process path */
75     path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0,
76                              r->proxyreq);
77     if (path == NULL)
78         return HTTP_BAD_REQUEST;
79
80     apr_snprintf(sport, sizeof(sport), ":%d", port);
81
82     if (ap_strchr_c(host, ':')) {
83         /* if literal IPv6 address */
84         host = apr_pstrcat(r->pool, "[", host, "]", NULL);
85     }
86     r->filename = apr_pstrcat(r->pool, "proxy:ajp://", host, sport,
87                               "/", path, (search) ? "?" : "",
88                               (search) ? search : "", NULL);
89     return OK;
90 }
91
92 /*
93  * XXX: Flushing bandaid
94  *
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.
104  *
105  * For further discussion see PR37100.
106  * http://issues.apache.org/bugzilla/show_bug.cgi?id=37100
107  */
108 #define FLUSHING_BANDAID 1
109
110 #ifdef FLUSHING_BANDAID
111 /*
112  * Wait 10000 microseconds to find out if more data is currently
113  * available at the backend. Just an arbitrary choose.
114  */
115 #define FLUSH_WAIT 10000
116 #endif
117
118 /*
119  * process the request and write the response.
120  */
121 static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r,
122                                 proxy_conn_rec *conn,
123                                 conn_rec *origin,
124                                 proxy_dir_conf *conf,
125                                 apr_uri_t *uri,
126                                 char *url, char *server_portstr)
127 {
128     apr_status_t status;
129     int result;
130     apr_bucket *e;
131     apr_bucket_brigade *input_brigade;
132     apr_bucket_brigade *output_brigade;
133     ajp_msg_t *msg;
134     apr_size_t bufsiz;
135     char *buff;
136     apr_uint16_t size;
137     const char *tenc;
138     int havebody = 1;
139     int isok = 1;
140     apr_off_t bb_len;
141     int data_sent = 0;
142     int rv = 0;
143 #ifdef FLUSHING_BANDAID
144     apr_int32_t conn_poll_fd;
145     apr_pollfd_t *conn_poll;
146 #endif
147
148     /*
149      * Send the AJP request to the remote server
150      */
151
152     /* send request headers */
153     status = ajp_send_header(conn->sock, r, uri);
154     if (status != APR_SUCCESS) {
155         conn->close++;
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;
162         else
163             return HTTP_SERVICE_UNAVAILABLE;
164     }
165
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 */
170         conn->close++;
171         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
172                      "proxy: ajp_alloc_data_msg failed");
173         return HTTP_INTERNAL_SERVER_ERROR;
174     }
175
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");
183     } else {
184         status = ap_get_brigade(r->input_filters, input_brigade,
185                                 AP_MODE_READBYTES, APR_BLOCK_READ,
186                                 AJP13_MAX_SEND_BODY_SZ);
187
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;
193         }
194
195         /* have something */
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");
199         }
200
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);
205
206         status = apr_brigade_flatten(input_brigade, buff, &bufsiz);
207         if (status != APR_SUCCESS) {
208             /* We had a failure: Close connection to backend */
209             conn->close++;
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;
214         }
215         apr_brigade_cleanup(input_brigade);
216
217         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
218                      "proxy: got %" APR_SIZE_T_FMT " bytes of data", bufsiz);
219         if (bufsiz > 0) {
220             status = ajp_send_data_msg(conn->sock, msg, bufsiz);
221             if (status != APR_SUCCESS) {
222                 /* We had a failure: Close connection to backend */
223                 conn->close++;
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;
230             }
231             conn->worker->s->transferred += bufsiz;
232         }
233     }
234
235     /* read the response */
236     conn->data = NULL;
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 */
241         conn->close++;
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;
248     }
249     /* parse the reponse */
250     result = ajp_parse_type(r, conn->data);
251     output_brigade = apr_brigade_create(p, r->connection->bucket_alloc);
252
253 #ifdef FLUSHING_BANDAID
254     /*
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)
258      */
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;
263 #endif
264
265     bufsiz = AJP13_MAX_SEND_BODY_SZ;
266     while (isok) {
267         switch (result) {
268             case CMD_AJP13_GET_BODY_CHUNK:
269                 if (havebody) {
270                     if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
271                         /* This is the end */
272                         bufsiz = 0;
273                         havebody = 0;
274                         ap_log_error(APLOG_MARK, APLOG_DEBUG, status, r->server,
275                                      "proxy: APR_BUCKET_IS_EOS");
276                     } else {
277                         status = ap_get_brigade(r->input_filters, input_brigade,
278                                                 AP_MODE_READBYTES,
279                                                 APR_BLOCK_READ,
280                                                 AJP13_MAX_SEND_BODY_SZ);
281                         if (status != APR_SUCCESS) {
282                             ap_log_error(APLOG_MARK, APLOG_DEBUG, status,
283                                          r->server,
284                                          "ap_get_brigade failed");
285                             break;
286                         }
287                         bufsiz = AJP13_MAX_SEND_BODY_SZ;
288                         status = apr_brigade_flatten(input_brigade, buff,
289                                                      &bufsiz);
290                         apr_brigade_cleanup(input_brigade);
291                         if (status != APR_SUCCESS) {
292                             ap_log_error(APLOG_MARK, APLOG_DEBUG, status,
293                                          r->server,
294                                          "apr_brigade_flatten failed");
295                             break;
296                         }
297                     }
298
299                     ajp_msg_reset(msg);
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");
305                         break;
306                     }
307                     conn->worker->s->transferred += bufsiz;
308                 } else {
309                     /*
310                      * something is wrong TC asks for more body but we are
311                      * already at the end of the body data
312                      */
313                     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
314                                  "ap_proxy_ajp_request error read after end");
315                     isok = 0;
316                 }
317                 break;
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) {
322                     isok = 0;
323                 }
324                 break;
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);
332
333 #ifdef FLUSHING_BANDAID
334                     /*
335                      * If there is no more data available from backend side
336                      * currently, flush response to client.
337                      */
338                     if (apr_poll(conn_poll, 1, &conn_poll_fd, FLUSH_WAIT)
339                         == APR_TIMEUP) {
340                         e = apr_bucket_flush_create(r->connection->bucket_alloc);
341                         APR_BRIGADE_INSERT_TAIL(output_brigade, e);
342                     }
343 #endif
344                     apr_brigade_length(output_brigade, 0, &bb_len);
345                     if (bb_len != -1)
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");
351                         isok = 0;
352                     }
353                     data_sent = 1;
354                     apr_brigade_cleanup(output_brigade);
355                 }
356                 else {
357                     isok = 0;
358                 }
359                 break;
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");
367                     isok = 0;
368                 }
369                 data_sent = 1;
370                 break;
371             default:
372                 isok = 0;
373                 break;
374         }
375
376         /*
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.
381          */
382         if (r->connection->aborted) {
383             conn->close++;
384             result = CMD_AJP13_END_RESPONSE;
385             break;
386         }
387
388         if (!isok)
389             break;
390
391         if (result == CMD_AJP13_END_RESPONSE)
392             break;
393
394         /* read the response */
395         status = ajp_read_header(conn->sock, r,
396                                  (ajp_msg_t **)&(conn->data));
397         if (status != APR_SUCCESS) {
398             isok = 0;
399             ap_log_error(APLOG_MARK, APLOG_DEBUG, status, r->server,
400                          "ajp_read_header failed");
401             break;
402         }
403         result = ajp_parse_type(r, conn->data);
404     }
405     apr_brigade_destroy(input_brigade);
406
407     /*
408      * Clear output_brigade to remove possible buckets that remained there
409      * after an error.
410      */
411     apr_brigade_cleanup(output_brigade);
412
413     if (status != APR_SUCCESS) {
414         /* We had a failure: Close connection to backend */
415         conn->close++;
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);
420         /*
421          * If we already send data, signal a broken backend connection
422          * upwards in the chain.
423          */
424         if (data_sent) {
425             ap_proxy_backend_broke(r, output_brigade);
426             /* Return DONE to avoid error messages being added to the stream */
427             rv = DONE;
428         } else
429             rv = HTTP_SERVICE_UNAVAILABLE;
430     }
431
432     /*
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.
436      */
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);
440     }
441
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);
445
446     apr_brigade_destroy(output_brigade);
447
448     if (rv)
449         return rv;
450
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);
457         return OK;
458     }
459
460     ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
461                  "proxy: got bad response (%d) from %pI (%s)",
462                  result,
463                  conn->worker->cp->addr,
464                  conn->worker->hostname);
465
466     /* We had a failure: Close connection to backend */
467     conn->close++;
468     return HTTP_SERVICE_UNAVAILABLE;
469 }
470
471 /*
472  * This handles ajp:// URLs
473  */
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)
478 {
479     int status;
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,
485                                                  &proxy_module);
486
487     /*
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.
495      *
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.
500      */
501     apr_pool_t *p = r->connection->pool;
502     apr_uri_t *uri = apr_palloc(r->connection->pool, sizeof(*uri));
503
504
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);
508         return DECLINED;
509     }
510     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
511                  "proxy: AJP: serving URL %s", url);
512
513     /* create space for state information */
514     if (!backend) {
515         status = ap_proxy_acquire_connection(scheme, &backend, worker,
516                                              r->server);
517         if (status != OK) {
518             if (backend) {
519                 backend->close_on_recycle = 1;
520                 ap_proxy_release_connection(scheme, backend, r->server);
521             }
522             return status;
523         }
524     }
525
526     backend->is_ssl = 0;
527     backend->close_on_recycle = 0;
528
529     /* Step One: Determine Who To Connect To */
530     status = ap_proxy_determine_connection(p, r, conf, worker, backend,
531                                            uri, &url, proxyname, proxyport,
532                                            server_portstr,
533                                            sizeof(server_portstr));
534
535     if (status != OK)
536         goto cleanup;
537
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",
542                      backend->hostname);
543         status = HTTP_SERVICE_UNAVAILABLE;
544         goto cleanup;
545     }
546
547     /* Step Three: Process the Request */
548     status = ap_proxy_ajp_request(p, r, backend, origin, dconf, uri, url,
549                                   server_portstr);
550
551 cleanup:
552     /* Do not close the socket */
553     ap_proxy_release_connection(scheme, backend, r->server);
554     return status;
555 }
556
557 static void ap_proxy_http_register_hook(apr_pool_t *p)
558 {
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);
561 }
562
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 */
571 };
572