]> granicus.if.org Git - apache/blob - modules/proxy/mod_proxy_ajp.c
4beae30b273ff967e9039b303d3ff6b1329a1557
[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     proxy_server_conf *psf =
137     ap_get_module_config(r->server->module_config, &proxy_module);
138     apr_size_t maxsize = AJP_MSG_BUFFER_SZ;
139
140     if (psf->io_buffer_size_set)
141        maxsize = psf->io_buffer_size;
142     if (maxsize > AJP_MAX_BUFFER_SZ)
143        maxsize = AJP_MAX_BUFFER_SZ;
144     else if (maxsize < AJP_MSG_BUFFER_SZ)
145        maxsize = AJP_MSG_BUFFER_SZ;
146     maxsize = APR_ALIGN(maxsize, 1024);
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, maxsize, 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     bufsiz = maxsize;
168     status = ajp_alloc_data_msg(r->pool, &buff, &bufsiz, &msg);
169     if (status != APR_SUCCESS) {
170         /* We had a failure: Close connection to backend */
171         conn->close++;
172         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
173                      "proxy: ajp_alloc_data_msg failed");
174         return HTTP_INTERNAL_SERVER_ERROR;
175     }
176
177     /* read the first bloc of data */
178     input_brigade = apr_brigade_create(p, r->connection->bucket_alloc);
179     tenc = apr_table_get(r->headers_in, "Transfer-Encoding");
180     if (tenc && (strcasecmp(tenc, "chunked") == 0)) {
181         /* The AJP protocol does not want body data yet */
182         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
183                      "proxy: request is chunked");
184     } else {
185         status = ap_get_brigade(r->input_filters, input_brigade,
186                                 AP_MODE_READBYTES, APR_BLOCK_READ,
187                                 maxsize - AJP_HEADER_SZ);
188
189         if (status != APR_SUCCESS) {
190             /* We had a failure: Close connection to backend */
191             conn->close++;
192             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
193                          "proxy: ap_get_brigade failed");
194             apr_brigade_destroy(input_brigade);
195             return HTTP_INTERNAL_SERVER_ERROR;
196         }
197
198         /* have something */
199         if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
200             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
201                          "proxy: APR_BUCKET_IS_EOS");
202         }
203
204         /* Try to send something */
205         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
206                      "proxy: data to read (max %" APR_SIZE_T_FMT
207                      " at %" APR_SIZE_T_FMT ")", bufsiz, msg->pos);
208
209         status = apr_brigade_flatten(input_brigade, buff, &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: apr_brigade_flatten");
216             return HTTP_INTERNAL_SERVER_ERROR;
217         }
218         apr_brigade_cleanup(input_brigade);
219
220         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
221                      "proxy: got %" APR_SIZE_T_FMT " bytes of data", bufsiz);
222         if (bufsiz > 0) {
223             status = ajp_send_data_msg(conn->sock, msg, bufsiz);
224             if (status != APR_SUCCESS) {
225                 /* We had a failure: Close connection to backend */
226                 conn->close++;
227                 apr_brigade_destroy(input_brigade);
228                 ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
229                              "proxy: send failed to %pI (%s)",
230                              conn->worker->cp->addr,
231                              conn->worker->hostname);
232                 return HTTP_SERVICE_UNAVAILABLE;
233             }
234             conn->worker->s->transferred += bufsiz;
235         }
236     }
237
238     /* read the response */
239     conn->data = NULL;
240     status = ajp_read_header(conn->sock, r, maxsize,
241                              (ajp_msg_t **)&(conn->data));
242     if (status != APR_SUCCESS) {
243         /* We had a failure: Close connection to backend */
244         conn->close++;
245         apr_brigade_destroy(input_brigade);
246         ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
247                      "proxy: read response failed from %pI (%s)",
248                      conn->worker->cp->addr,
249                      conn->worker->hostname);
250         return HTTP_SERVICE_UNAVAILABLE;
251     }
252     /* parse the reponse */
253     result = ajp_parse_type(r, conn->data);
254     output_brigade = apr_brigade_create(p, r->connection->bucket_alloc);
255
256     /*
257      * Prepare apr_pollfd_t struct for possible later check if there is currently
258      * data available from the backend (do not flush response to client)
259      * or not (flush response to client)
260      */
261     conn_poll = apr_pcalloc(p, sizeof(apr_pollfd_t));
262     conn_poll->reqevents = APR_POLLIN;
263     conn_poll->desc_type = APR_POLL_SOCKET;
264     conn_poll->desc.s = conn->sock;
265
266     bufsiz = maxsize;
267     while (isok) {
268         switch (result) {
269             case CMD_AJP13_GET_BODY_CHUNK:
270                 if (havebody) {
271                     if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
272                         /* This is the end */
273                         bufsiz = 0;
274                         havebody = 0;
275                         ap_log_error(APLOG_MARK, APLOG_DEBUG, status, r->server,
276                                      "proxy: APR_BUCKET_IS_EOS");
277                     } else {
278                         status = ap_get_brigade(r->input_filters, input_brigade,
279                                                 AP_MODE_READBYTES,
280                                                 APR_BLOCK_READ,
281                                                 maxsize - AJP_HEADER_SZ);
282                         if (status != APR_SUCCESS) {
283                             ap_log_error(APLOG_MARK, APLOG_DEBUG, status,
284                                          r->server,
285                                          "ap_get_brigade failed");
286                             break;
287                         }
288                         bufsiz = maxsize;
289                         status = apr_brigade_flatten(input_brigade, buff,
290                                                      &bufsiz);
291                         apr_brigade_cleanup(input_brigade);
292                         if (status != APR_SUCCESS) {
293                             ap_log_error(APLOG_MARK, APLOG_DEBUG, status,
294                                          r->server,
295                                          "apr_brigade_flatten failed");
296                             break;
297                         }
298                     }
299
300                     ajp_msg_reset(msg);
301                     /* will go in ajp_send_data_msg */
302                     status = ajp_send_data_msg(conn->sock, msg, bufsiz);
303                     if (status != APR_SUCCESS) {
304                         ap_log_error(APLOG_MARK, APLOG_DEBUG, status, r->server,
305                                      "ajp_send_data_msg failed");
306                         break;
307                     }
308                     conn->worker->s->transferred += bufsiz;
309                 } else {
310                     /*
311                      * something is wrong TC asks for more body but we are
312                      * already at the end of the body data
313                      */
314                     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
315                                  "ap_proxy_ajp_request error read after end");
316                     isok = 0;
317                 }
318                 break;
319             case CMD_AJP13_SEND_HEADERS:
320                 /* AJP13_SEND_HEADERS: process them */
321                 status = ajp_parse_header(r, conf, conn->data);
322                 if (status != APR_SUCCESS) {
323                     isok = 0;
324                 }
325                 break;
326             case CMD_AJP13_SEND_BODY_CHUNK:
327                 /* AJP13_SEND_BODY_CHUNK: piece of data */
328                 status = ajp_parse_data(r, conn->data, &size, &buff);
329                 if (status == APR_SUCCESS) {
330                     if (size == 0) {
331                         /* AJP13_SEND_BODY_CHUNK with zero length
332                          * is explicit flush message
333                          */
334                         e = apr_bucket_flush_create(r->connection->bucket_alloc);
335                         APR_BRIGADE_INSERT_TAIL(output_brigade, e);
336                     }
337                     else {
338                         e = apr_bucket_transient_create(buff, size,
339                                                     r->connection->bucket_alloc);
340                         APR_BRIGADE_INSERT_TAIL(output_brigade, e);
341
342                         if ((conn->worker->flush_packets == flush_on) ||
343                             ((conn->worker->flush_packets == flush_auto) &&
344                             (apr_poll(conn_poll, 1, &conn_poll_fd,
345                                       conn->worker->flush_wait)
346                                         == APR_TIMEUP) ) ) {
347                             e = apr_bucket_flush_create(r->connection->bucket_alloc);
348                             APR_BRIGADE_INSERT_TAIL(output_brigade, e);
349                         }
350                         apr_brigade_length(output_brigade, 0, &bb_len);
351                         if (bb_len != -1)
352                             conn->worker->s->read += bb_len;
353                     }
354                     if (ap_pass_brigade(r->output_filters,
355                                         output_brigade) != APR_SUCCESS) {
356                         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
357                                       "proxy: error processing body");
358                         isok = 0;
359                     }
360                     data_sent = 1;
361                     apr_brigade_cleanup(output_brigade);
362                 }
363                 else {
364                     isok = 0;
365                 }
366                 break;
367             case CMD_AJP13_END_RESPONSE:
368                 e = apr_bucket_eos_create(r->connection->bucket_alloc);
369                 APR_BRIGADE_INSERT_TAIL(output_brigade, e);
370                 if (ap_pass_brigade(r->output_filters,
371                                     output_brigade) != APR_SUCCESS) {
372                     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
373                                   "proxy: error processing body");
374                     isok = 0;
375                 }
376                 /* XXX: what about flush here? See mod_jk */
377                 data_sent = 1;
378                 break;
379             default:
380                 isok = 0;
381                 break;
382         }
383
384         /*
385          * If connection has been aborted by client: Stop working.
386          * Nevertheless, we regard our operation so far as a success:
387          * So do not set isok to 0 and set result to CMD_AJP13_END_RESPONSE
388          * But: Close this connection to the backend.
389          */
390         if (r->connection->aborted) {
391             conn->close++;
392             result = CMD_AJP13_END_RESPONSE;
393             break;
394         }
395
396         if (!isok)
397             break;
398
399         if (result == CMD_AJP13_END_RESPONSE)
400             break;
401
402         /* read the response */
403         status = ajp_read_header(conn->sock, r, maxsize,
404                                  (ajp_msg_t **)&(conn->data));
405         if (status != APR_SUCCESS) {
406             isok = 0;
407             ap_log_error(APLOG_MARK, APLOG_DEBUG, status, r->server,
408                          "ajp_read_header failed");
409             break;
410         }
411         result = ajp_parse_type(r, conn->data);
412     }
413     apr_brigade_destroy(input_brigade);
414
415     /*
416      * Clear output_brigade to remove possible buckets that remained there
417      * after an error.
418      */
419     apr_brigade_cleanup(output_brigade);
420
421     if (status != APR_SUCCESS) {
422         /* We had a failure: Close connection to backend */
423         conn->close++;
424         ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
425                      "proxy: send body failed to %pI (%s)",
426                      conn->worker->cp->addr,
427                      conn->worker->hostname);
428         /*
429          * If we already send data, signal a broken backend connection
430          * upwards in the chain.
431          */
432         if (data_sent) {
433             ap_proxy_backend_broke(r, output_brigade);
434             /* Return DONE to avoid error messages being added to the stream */
435             rv = DONE;
436         } else
437             rv = HTTP_SERVICE_UNAVAILABLE;
438     }
439
440     /*
441      * Ensure that we sent an EOS bucket thru the filter chain, if we already
442      * have sent some data. Maybe ap_proxy_backend_broke was called and added
443      * one to the brigade already (no longer making it empty). So we should
444      * not do this in this case.
445      */
446     if (data_sent && !r->eos_sent && APR_BRIGADE_EMPTY(output_brigade)) {
447         e = apr_bucket_eos_create(r->connection->bucket_alloc);
448         APR_BRIGADE_INSERT_TAIL(output_brigade, e);
449     }
450
451     /* If we have added something to the brigade above, sent it */
452     if (!APR_BRIGADE_EMPTY(output_brigade))
453         ap_pass_brigade(r->output_filters, output_brigade);
454
455     apr_brigade_destroy(output_brigade);
456
457     if (rv)
458         return rv;
459
460     /* Nice we have answer to send to the client */
461     if (result == CMD_AJP13_END_RESPONSE && isok) {
462         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
463                      "proxy: got response from %pI (%s)",
464                      conn->worker->cp->addr,
465                      conn->worker->hostname);
466         return OK;
467     }
468
469     ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
470                  "proxy: got bad response (%d) from %pI (%s)",
471                  result,
472                  conn->worker->cp->addr,
473                  conn->worker->hostname);
474
475     /* We had a failure: Close connection to backend */
476     conn->close++;
477     return HTTP_SERVICE_UNAVAILABLE;
478 }
479
480 /*
481  * This handles ajp:// URLs
482  */
483 static int proxy_ajp_handler(request_rec *r, proxy_worker *worker,
484                              proxy_server_conf *conf,
485                              char *url, const char *proxyname,
486                              apr_port_t proxyport)
487 {
488     int status;
489     char server_portstr[32];
490     conn_rec *origin = NULL;
491     proxy_conn_rec *backend = NULL;
492     const char *scheme = "AJP";
493     proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config,
494                                                  &proxy_module);
495
496     /*
497      * Note: Memory pool allocation.
498      * A downstream keepalive connection is always connected to the existence
499      * (or not) of an upstream keepalive connection. If this is not done then
500      * load balancing against multiple backend servers breaks (one backend
501      * server ends up taking 100% of the load), and the risk is run of
502      * downstream keepalive connections being kept open unnecessarily. This
503      * keeps webservers busy and ties up resources.
504      *
505      * As a result, we allocate all sockets out of the upstream connection
506      * pool, and when we want to reuse a socket, we check first whether the
507      * connection ID of the current upstream connection is the same as that
508      * of the connection when the socket was opened.
509      */
510     apr_pool_t *p = r->connection->pool;
511     apr_uri_t *uri = apr_palloc(r->connection->pool, sizeof(*uri));
512
513
514     if (strncasecmp(url, "ajp:", 4) != 0) {
515         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
516                      "proxy: AJP: declining URL %s", url);
517         return DECLINED;
518     }
519     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
520                  "proxy: AJP: serving URL %s", url);
521
522     /* create space for state information */
523     if (!backend) {
524         status = ap_proxy_acquire_connection(scheme, &backend, worker,
525                                              r->server);
526         if (status != OK) {
527             if (backend) {
528                 backend->close = 1;
529                 ap_proxy_release_connection(scheme, backend, r->server);
530             }
531             return status;
532         }
533     }
534
535     backend->is_ssl = 0;
536     backend->close = 0;
537
538     /* Step One: Determine Who To Connect To */
539     status = ap_proxy_determine_connection(p, r, conf, worker, backend,
540                                            uri, &url, proxyname, proxyport,
541                                            server_portstr,
542                                            sizeof(server_portstr));
543
544     if (status != OK)
545         goto cleanup;
546
547     /* Step Two: Make the Connection */
548     if (ap_proxy_connect_backend(scheme, backend, worker, r->server)) {
549         ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
550                      "proxy: AJP: failed to make connection to backend: %s",
551                      backend->hostname);
552         status = HTTP_SERVICE_UNAVAILABLE;
553         goto cleanup;
554     }
555
556     /* Handle CPING/CPONG */
557     if (worker->ping_timeout_set) {
558         status = ajp_handle_cping_cpong(backend->sock, r,
559                                         worker->ping_timeout);
560         if (status != APR_SUCCESS) {
561             backend->close++;
562             ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
563                          "proxy: AJP: cping/cpong failed to %pI (%s)",
564                          worker->cp->addr,
565                          worker->hostname);
566             status = HTTP_SERVICE_UNAVAILABLE;
567             goto cleanup;
568         }
569     }
570     /* Step Three: Process the Request */
571     status = ap_proxy_ajp_request(p, r, backend, origin, dconf, uri, url,
572                                   server_portstr);
573
574 cleanup:
575     /* Do not close the socket */
576     ap_proxy_release_connection(scheme, backend, r->server);
577     return status;
578 }
579
580 static void ap_proxy_http_register_hook(apr_pool_t *p)
581 {
582     proxy_hook_scheme_handler(proxy_ajp_handler, NULL, NULL, APR_HOOK_FIRST);
583     proxy_hook_canon_handler(proxy_ajp_canon, NULL, NULL, APR_HOOK_FIRST);
584 }
585
586 module AP_MODULE_DECLARE_DATA proxy_ajp_module = {
587     STANDARD20_MODULE_STUFF,
588     NULL,                       /* create per-directory config structure */
589     NULL,                       /* merge per-directory config structures */
590     NULL,                       /* create per-server config structure */
591     NULL,                       /* merge per-server config structures */
592     NULL,                       /* command apr_table_t */
593     ap_proxy_http_register_hook /* register hooks */
594 };
595