]> granicus.if.org Git - apache/blob - modules/proxy/mod_proxy_ajp.c
Added cping/cpong support for the AJP protocol.
[apache] / modules / proxy / mod_proxy_ajp.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  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: AJP Auto Flushing
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
109 /*
110  * process the request and write the response.
111  */
112 static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r,
113                                 proxy_conn_rec *conn,
114                                 conn_rec *origin,
115                                 proxy_dir_conf *conf,
116                                 apr_uri_t *uri,
117                                 char *url, char *server_portstr)
118 {
119     apr_status_t status;
120     int result;
121     apr_bucket *e;
122     apr_bucket_brigade *input_brigade;
123     apr_bucket_brigade *output_brigade;
124     ajp_msg_t *msg;
125     apr_size_t bufsiz;
126     char *buff;
127     apr_uint16_t size;
128     const char *tenc;
129     int havebody = 1;
130     int isok = 1;
131     apr_off_t bb_len;
132     int data_sent = 0;
133     int rv = 0;
134     apr_int32_t conn_poll_fd;
135     apr_pollfd_t *conn_poll;
136
137     /*
138      * Send the AJP request to the remote server
139      */
140
141     /* send request headers */
142     status = ajp_send_header(conn->sock, r, uri);
143     if (status != APR_SUCCESS) {
144         conn->close++;
145         ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
146                      "proxy: AJP: request failed to %pI (%s)",
147                      conn->worker->cp->addr,
148                      conn->worker->hostname);
149         if (status == AJP_EOVERFLOW)
150             return HTTP_BAD_REQUEST;
151         else
152             return HTTP_SERVICE_UNAVAILABLE;
153     }
154
155     /* allocate an AJP message to store the data of the buckets */
156     status = ajp_alloc_data_msg(r->pool, &buff, &bufsiz, &msg);
157     if (status != APR_SUCCESS) {
158         /* We had a failure: Close connection to backend */
159         conn->close++;
160         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
161                      "proxy: ajp_alloc_data_msg failed");
162         return HTTP_INTERNAL_SERVER_ERROR;
163     }
164
165     /* read the first bloc of data */
166     input_brigade = apr_brigade_create(p, r->connection->bucket_alloc);
167     tenc = apr_table_get(r->headers_in, "Transfer-Encoding");
168     if (tenc && (strcasecmp(tenc, "chunked") == 0)) {
169         /* The AJP protocol does not want body data yet */
170         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
171                      "proxy: request is chunked");
172     } else {
173         status = ap_get_brigade(r->input_filters, input_brigade,
174                                 AP_MODE_READBYTES, APR_BLOCK_READ,
175                                 AJP13_MAX_SEND_BODY_SZ);
176
177         if (status != APR_SUCCESS) {
178             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
179                          "proxy: ap_get_brigade failed");
180             apr_brigade_destroy(input_brigade);
181             return HTTP_INTERNAL_SERVER_ERROR;
182         }
183
184         /* have something */
185         if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
186             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
187                          "proxy: APR_BUCKET_IS_EOS");
188         }
189
190         /* Try to send something */
191         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
192                      "proxy: data to read (max %" APR_SIZE_T_FMT
193                      " at %" APR_SIZE_T_FMT ")", bufsiz, msg->pos);
194
195         status = apr_brigade_flatten(input_brigade, buff, &bufsiz);
196         if (status != APR_SUCCESS) {
197             /* We had a failure: Close connection to backend */
198             conn->close++;
199             apr_brigade_destroy(input_brigade);
200             ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
201                          "proxy: apr_brigade_flatten");
202             return HTTP_INTERNAL_SERVER_ERROR;
203         }
204         apr_brigade_cleanup(input_brigade);
205
206         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
207                      "proxy: got %" APR_SIZE_T_FMT " bytes of data", bufsiz);
208         if (bufsiz > 0) {
209             status = ajp_send_data_msg(conn->sock, msg, bufsiz);
210             if (status != APR_SUCCESS) {
211                 /* We had a failure: Close connection to backend */
212                 conn->close++;
213                 apr_brigade_destroy(input_brigade);
214                 ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
215                              "proxy: send failed to %pI (%s)",
216                              conn->worker->cp->addr,
217                              conn->worker->hostname);
218                 return HTTP_SERVICE_UNAVAILABLE;
219             }
220             conn->worker->s->transferred += bufsiz;
221         }
222     }
223
224     /* read the response */
225     conn->data = NULL;
226     status = ajp_read_header(conn->sock, r,
227                              (ajp_msg_t **)&(conn->data));
228     if (status != APR_SUCCESS) {
229         /* We had a failure: Close connection to backend */
230         conn->close++;
231         apr_brigade_destroy(input_brigade);
232         ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
233                      "proxy: read response failed from %pI (%s)",
234                      conn->worker->cp->addr,
235                      conn->worker->hostname);
236         return HTTP_SERVICE_UNAVAILABLE;
237     }
238     /* parse the reponse */
239     result = ajp_parse_type(r, conn->data);
240     output_brigade = apr_brigade_create(p, r->connection->bucket_alloc);
241
242     /*
243      * Prepare apr_pollfd_t struct for possible later check if there is currently
244      * data available from the backend (do not flush response to client)
245      * or not (flush response to client)
246      */
247     conn_poll = apr_pcalloc(p, sizeof(apr_pollfd_t));
248     conn_poll->reqevents = APR_POLLIN;
249     conn_poll->desc_type = APR_POLL_SOCKET;
250     conn_poll->desc.s = conn->sock;
251
252     bufsiz = AJP13_MAX_SEND_BODY_SZ;
253     while (isok) {
254         switch (result) {
255             case CMD_AJP13_GET_BODY_CHUNK:
256                 if (havebody) {
257                     if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
258                         /* This is the end */
259                         bufsiz = 0;
260                         havebody = 0;
261                         ap_log_error(APLOG_MARK, APLOG_DEBUG, status, r->server,
262                                      "proxy: APR_BUCKET_IS_EOS");
263                     } else {
264                         status = ap_get_brigade(r->input_filters, input_brigade,
265                                                 AP_MODE_READBYTES,
266                                                 APR_BLOCK_READ,
267                                                 AJP13_MAX_SEND_BODY_SZ);
268                         if (status != APR_SUCCESS) {
269                             ap_log_error(APLOG_MARK, APLOG_DEBUG, status,
270                                          r->server,
271                                          "ap_get_brigade failed");
272                             break;
273                         }
274                         bufsiz = AJP13_MAX_SEND_BODY_SZ;
275                         status = apr_brigade_flatten(input_brigade, buff,
276                                                      &bufsiz);
277                         apr_brigade_cleanup(input_brigade);
278                         if (status != APR_SUCCESS) {
279                             ap_log_error(APLOG_MARK, APLOG_DEBUG, status,
280                                          r->server,
281                                          "apr_brigade_flatten failed");
282                             break;
283                         }
284                     }
285
286                     ajp_msg_reset(msg);
287                     /* will go in ajp_send_data_msg */
288                     status = ajp_send_data_msg(conn->sock, msg, bufsiz);
289                     if (status != APR_SUCCESS) {
290                         ap_log_error(APLOG_MARK, APLOG_DEBUG, status, r->server,
291                                      "ajp_send_data_msg failed");
292                         break;
293                     }
294                     conn->worker->s->transferred += bufsiz;
295                 } else {
296                     /*
297                      * something is wrong TC asks for more body but we are
298                      * already at the end of the body data
299                      */
300                     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
301                                  "ap_proxy_ajp_request error read after end");
302                     isok = 0;
303                 }
304                 break;
305             case CMD_AJP13_SEND_HEADERS:
306                 /* AJP13_SEND_HEADERS: process them */
307                 status = ajp_parse_header(r, conf, conn->data);
308                 if (status != APR_SUCCESS) {
309                     isok = 0;
310                 }
311                 break;
312             case CMD_AJP13_SEND_BODY_CHUNK:
313                 /* AJP13_SEND_BODY_CHUNK: piece of data */
314                 status = ajp_parse_data(r, conn->data, &size, &buff);
315                 if (status == APR_SUCCESS) {
316                     e = apr_bucket_transient_create(buff, size,
317                                                     r->connection->bucket_alloc);
318                     APR_BRIGADE_INSERT_TAIL(output_brigade, e);
319
320                     if ( (conn->worker->flush_packets == flush_on) ||
321                          ( (conn->worker->flush_packets == flush_auto) &&
322                            (apr_poll(conn_poll, 1, &conn_poll_fd,
323                                      conn->worker->flush_wait)
324                              == APR_TIMEUP) ) ) {
325                         e = apr_bucket_flush_create(r->connection->bucket_alloc);
326                         APR_BRIGADE_INSERT_TAIL(output_brigade, e);
327                     }
328                     apr_brigade_length(output_brigade, 0, &bb_len);
329                     if (bb_len != -1)
330                         conn->worker->s->read += bb_len;
331                     if (ap_pass_brigade(r->output_filters,
332                                         output_brigade) != APR_SUCCESS) {
333                         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
334                                       "proxy: error processing body");
335                         isok = 0;
336                     }
337                     data_sent = 1;
338                     apr_brigade_cleanup(output_brigade);
339                 }
340                 else {
341                     isok = 0;
342                 }
343                 break;
344             case CMD_AJP13_END_RESPONSE:
345                 e = apr_bucket_eos_create(r->connection->bucket_alloc);
346                 APR_BRIGADE_INSERT_TAIL(output_brigade, e);
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                 /* XXX: what about flush here? See mod_jk */
354                 data_sent = 1;
355                 break;
356             default:
357                 isok = 0;
358                 break;
359         }
360
361         /*
362          * If connection has been aborted by client: Stop working.
363          * Nevertheless, we regard our operation so far as a success:
364          * So do not set isok to 0 and set result to CMD_AJP13_END_RESPONSE
365          * But: Close this connection to the backend.
366          */
367         if (r->connection->aborted) {
368             conn->close++;
369             result = CMD_AJP13_END_RESPONSE;
370             break;
371         }
372
373         if (!isok)
374             break;
375
376         if (result == CMD_AJP13_END_RESPONSE)
377             break;
378
379         /* read the response */
380         status = ajp_read_header(conn->sock, r,
381                                  (ajp_msg_t **)&(conn->data));
382         if (status != APR_SUCCESS) {
383             isok = 0;
384             ap_log_error(APLOG_MARK, APLOG_DEBUG, status, r->server,
385                          "ajp_read_header failed");
386             break;
387         }
388         result = ajp_parse_type(r, conn->data);
389     }
390     apr_brigade_destroy(input_brigade);
391
392     /*
393      * Clear output_brigade to remove possible buckets that remained there
394      * after an error.
395      */
396     apr_brigade_cleanup(output_brigade);
397
398     if (status != APR_SUCCESS) {
399         /* We had a failure: Close connection to backend */
400         conn->close++;
401         ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
402                      "proxy: send body failed to %pI (%s)",
403                      conn->worker->cp->addr,
404                      conn->worker->hostname);
405         /*
406          * If we already send data, signal a broken backend connection
407          * upwards in the chain.
408          */
409         if (data_sent) {
410             ap_proxy_backend_broke(r, output_brigade);
411             /* Return DONE to avoid error messages being added to the stream */
412             rv = DONE;
413         } else
414             rv = HTTP_SERVICE_UNAVAILABLE;
415     }
416
417     /*
418      * Ensure that we sent an EOS bucket thru the filter chain, if we already
419      * have sent some data. Maybe ap_proxy_backend_broke was called and added
420      * one to the brigade already (no longer making it empty). So we should
421      * not do this in this case.
422      */
423     if (data_sent && !r->eos_sent && APR_BRIGADE_EMPTY(output_brigade)) {
424         e = apr_bucket_eos_create(r->connection->bucket_alloc);
425         APR_BRIGADE_INSERT_TAIL(output_brigade, e);
426     }
427
428     /* If we have added something to the brigade above, sent it */
429     if (!APR_BRIGADE_EMPTY(output_brigade))
430         ap_pass_brigade(r->output_filters, output_brigade);
431
432     apr_brigade_destroy(output_brigade);
433
434     if (rv)
435         return rv;
436
437     /* Nice we have answer to send to the client */
438     if (result == CMD_AJP13_END_RESPONSE && isok) {
439         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
440                      "proxy: got response from %pI (%s)",
441                      conn->worker->cp->addr,
442                      conn->worker->hostname);
443         return OK;
444     }
445
446     ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
447                  "proxy: got bad response (%d) from %pI (%s)",
448                  result,
449                  conn->worker->cp->addr,
450                  conn->worker->hostname);
451
452     /* We had a failure: Close connection to backend */
453     conn->close++;
454     return HTTP_SERVICE_UNAVAILABLE;
455 }
456
457 /*
458  * This handles ajp:// URLs
459  */
460 static int proxy_ajp_handler(request_rec *r, proxy_worker *worker,
461                              proxy_server_conf *conf,
462                              char *url, const char *proxyname,
463                              apr_port_t proxyport)
464 {
465     int status;
466     char server_portstr[32];
467     conn_rec *origin = NULL;
468     proxy_conn_rec *backend = NULL;
469     const char *scheme = "AJP";
470     proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config,
471                                                  &proxy_module);
472
473     /*
474      * Note: Memory pool allocation.
475      * A downstream keepalive connection is always connected to the existence
476      * (or not) of an upstream keepalive connection. If this is not done then
477      * load balancing against multiple backend servers breaks (one backend
478      * server ends up taking 100% of the load), and the risk is run of
479      * downstream keepalive connections being kept open unnecessarily. This
480      * keeps webservers busy and ties up resources.
481      *
482      * As a result, we allocate all sockets out of the upstream connection
483      * pool, and when we want to reuse a socket, we check first whether the
484      * connection ID of the current upstream connection is the same as that
485      * of the connection when the socket was opened.
486      */
487     apr_pool_t *p = r->connection->pool;
488     apr_uri_t *uri = apr_palloc(r->connection->pool, sizeof(*uri));
489
490
491     if (strncasecmp(url, "ajp:", 4) != 0) {
492         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
493                      "proxy: AJP: declining URL %s", url);
494         return DECLINED;
495     }
496     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
497                  "proxy: AJP: serving URL %s", url);
498
499     /* create space for state information */
500     if (!backend) {
501         status = ap_proxy_acquire_connection(scheme, &backend, worker,
502                                              r->server);
503         if (status != OK) {
504             if (backend) {
505                 backend->close_on_recycle = 1;
506                 ap_proxy_release_connection(scheme, backend, r->server);
507             }
508             return status;
509         }
510     }
511
512     backend->is_ssl = 0;
513     backend->close_on_recycle = 0;
514
515     /* Step One: Determine Who To Connect To */
516     status = ap_proxy_determine_connection(p, r, conf, worker, backend,
517                                            uri, &url, proxyname, proxyport,
518                                            server_portstr,
519                                            sizeof(server_portstr));
520
521     if (status != OK)
522         goto cleanup;
523
524     /* Step Two: Make the Connection */
525     if (ap_proxy_connect_backend(scheme, backend, worker, r->server)) {
526         ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
527                      "proxy: AJP: failed to make connection to backend: %s",
528                      backend->hostname);
529         status = HTTP_SERVICE_UNAVAILABLE;
530         goto cleanup;
531     }
532
533     /* Handle CPING/CPONG */
534     if (worker->ping_timeout_set) {
535         status = ajp_handle_cping_cpong(backend->sock, r,
536                                         worker->ping_timeout);
537         if (status != APR_SUCCESS) {
538             backend->close++;
539             ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
540                          "proxy: AJP: cping/cpong failed to %pI (%s)",
541                          worker->cp->addr,
542                          worker->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