2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 /***************************************************************************
19 * Description: common stuff for bi-directional protocols ajp13/ajp14. *
20 * Author: Gal Shachor <shachor@il.ibm.com> *
21 * Author: Henri Gomez <hgomez@apache.org> *
22 * Version: $Revision: 1137200 $ *
23 ***************************************************************************/
26 #include "jk_global.h"
30 #include "jk_ajp_common.h"
31 #include "jk_connect.h"
32 #if defined(AS400) && !defined(AS400_UTF8)
33 #include "util_ebcdic.h"
35 #if defined(NETWARE) && defined(__NOVELL_LIBC__)
39 const char *response_trans_headers[] = {
53 static const char *long_res_header_for_sc(int sc)
55 const char *rc = NULL;
57 if (sc <= SC_RES_HEADERS_NUM && sc > 0) {
58 rc = response_trans_headers[sc - 1];
64 static const char *ajp_state_type[] = {
65 JK_AJP_STATE_TEXT_IDLE,
67 JK_AJP_STATE_TEXT_ERROR,
68 JK_AJP_STATE_TEXT_PROBE,
73 #define UNKNOWN_METHOD (-1)
75 static int sc_for_req_method(const char *method, size_t len)
77 /* Note: the following code was generated by the "shilka" tool from
78 the "cocom" parsing/compilation toolkit. It is an optimized lookup
79 based on analysis of the input keywords. Postprocessing was done
80 on the shilka output, but the basic structure and analysis is
81 from there. Should new HTTP methods be added, then manual insertion
82 into this code is fine, or simply re-running the shilka tool on
83 the appropriate input. */
85 /* Note: it is also quite reasonable to just use our method_registry,
86 but I'm assuming (probably incorrectly) we want more speed here
87 (based on the optimizations the previous code was doing). */
95 return (method[1] == 'C'
97 ? SC_M_ACL : UNKNOWN_METHOD);
99 return (method[1] == 'U'
101 ? SC_M_PUT : UNKNOWN_METHOD);
103 return (method[1] == 'E'
105 ? SC_M_GET : UNKNOWN_METHOD);
107 return UNKNOWN_METHOD;
114 return (method[1] == 'E'
117 ? SC_M_HEAD : UNKNOWN_METHOD);
119 return (method[1] == 'O'
122 ? SC_M_POST : UNKNOWN_METHOD);
124 return (method[1] == 'O'
127 ? SC_M_MOVE : UNKNOWN_METHOD);
129 return (method[1] == 'O'
132 ? SC_M_LOCK : UNKNOWN_METHOD);
134 return (method[1] == 'O'
137 ? SC_M_COPY : UNKNOWN_METHOD);
139 return UNKNOWN_METHOD;
146 return (memcmp(method, "MERGE", 5) == 0
147 ? SC_M_MERGE : UNKNOWN_METHOD);
149 return (memcmp(method, "MKCOL", 5) == 0
150 ? SC_M_MKCOL : UNKNOWN_METHOD);
152 return (memcmp(method, "LABEL", 5) == 0
153 ? SC_M_LABEL : UNKNOWN_METHOD);
155 return (memcmp(method, "TRACE", 5) == 0
156 ? SC_M_TRACE : UNKNOWN_METHOD);
158 return UNKNOWN_METHOD;
168 return (memcmp(method, "UNLOCK", 6) == 0
169 ? SC_M_UNLOCK : UNKNOWN_METHOD);
171 return (memcmp(method, "UPDATE", 6) == 0
172 ? SC_M_UPDATE : UNKNOWN_METHOD);
174 return UNKNOWN_METHOD;
177 return (memcmp(method, "REPORT", 6) == 0
178 ? SC_M_REPORT : UNKNOWN_METHOD);
180 return (memcmp(method, "SEARCH", 6) == 0
181 ? SC_M_SEARCH : UNKNOWN_METHOD);
183 return (memcmp(method, "DELETE", 6) == 0
184 ? SC_M_DELETE : UNKNOWN_METHOD);
186 return UNKNOWN_METHOD;
193 return (memcmp(method, "OPTIONS", 7) == 0
194 ? SC_M_OPTIONS : UNKNOWN_METHOD);
196 return (memcmp(method, "CHECKIN", 7) == 0
197 ? SC_M_CHECKIN : UNKNOWN_METHOD);
199 return UNKNOWN_METHOD;
206 return (memcmp(method, "PROPFIND", 8) == 0
207 ? SC_M_PROPFIND : UNKNOWN_METHOD);
209 return (memcmp(method, "CHECKOUT", 8) == 0
210 ? SC_M_CHECKOUT : UNKNOWN_METHOD);
212 return UNKNOWN_METHOD;
216 return (memcmp(method, "PROPPATCH", 9) == 0
217 ? SC_M_PROPPATCH : UNKNOWN_METHOD);
223 return (memcmp(method, "UNCHECKOUT", 10) == 0
224 ? SC_M_UNCHECKOUT : UNKNOWN_METHOD);
226 return (memcmp(method, "MKACTIVITY", 10) == 0
227 ? SC_M_MKACTIVITY : UNKNOWN_METHOD);
229 return UNKNOWN_METHOD;
233 return (memcmp(method, "MKWORKSPACE", 11) == 0
234 ? SC_M_MKWORKSPACE : UNKNOWN_METHOD);
237 return (memcmp(method, "VERSION-CONTROL", 15) == 0
238 ? SC_M_VERSION_CONTROL : UNKNOWN_METHOD);
241 return (memcmp(method, "BASELINE-CONTROL", 16) == 0
242 ? SC_M_BASELINE_CONTROL : UNKNOWN_METHOD);
245 return UNKNOWN_METHOD;
251 static int sc_for_req_header(const char *header_name)
254 size_t len = strlen(header_name);
255 const char *p = header_name;
258 /* ACCEPT-LANGUAGE is the longest header
259 * that is of interest.
261 if (len < 4 || len > 15)
262 return UNKNOWN_METHOD;
265 header[i++] = toupper((unsigned char)*p);
272 /* Always do memcmp including the final \0-termination character.
276 if (memcmp(p, "CCEPT", 6) == 0) {
279 else if (header[6] == '-') {
281 if (memcmp(p, "CHARSET", 8) == 0)
282 return SC_ACCEPT_CHARSET;
283 else if (memcmp(p, "ENCODING", 9) == 0)
284 return SC_ACCEPT_ENCODING;
285 else if (memcmp(p, "LANGUAGE", 9) == 0)
286 return SC_ACCEPT_LANGUAGE;
288 return UNKNOWN_METHOD;
291 return UNKNOWN_METHOD;
293 else if (memcmp(p, "UTHORIZATION", 13) == 0)
294 return SC_AUTHORIZATION;
296 return UNKNOWN_METHOD;
299 if(memcmp(p, "OOKIE2", 7) == 0)
301 else if (memcmp(p, "OOKIE", 6) == 0)
303 else if(memcmp(p, "ONNECTION", 10) == 0)
304 return SC_CONNECTION;
305 else if(memcmp(p, "ONTENT-TYPE", 12) == 0)
306 return SC_CONTENT_TYPE;
307 else if(memcmp(p, "ONTENT-LENGTH", 14) == 0)
308 return SC_CONTENT_LENGTH;
310 return UNKNOWN_METHOD;
313 if(memcmp(p, "OST", 4) == 0)
316 return UNKNOWN_METHOD;
319 if(memcmp(p, "RAGMA", 6) == 0)
322 return UNKNOWN_METHOD;
325 if(memcmp(p, "EFERER", 7) == 0)
328 return UNKNOWN_METHOD;
331 if(memcmp(p, "SER-AGENT", 10) == 0)
332 return SC_USER_AGENT;
334 return UNKNOWN_METHOD;
337 return UNKNOWN_METHOD;
342 /* Return the string representation of the worker state */
343 const char *jk_ajp_get_state(ajp_worker_t *aw, jk_logger_t *l)
345 return ajp_state_type[aw->s->state];
348 /* Return the int representation of the worker state */
349 int jk_ajp_get_state_code(const char *v)
352 return JK_AJP_STATE_DEF;
353 else if (*v == 'i' || *v == 'I' || *v == 'n' || *v == 'N' || *v == '0')
354 return JK_AJP_STATE_IDLE;
355 else if (*v == 'o' || *v == 'O' || *v == '1')
356 return JK_AJP_STATE_OK;
357 else if (*v == 'e' || *v == 'E' || *v == '4')
358 return JK_AJP_STATE_ERROR;
359 else if (*v == 'p' || *v == 'P' || *v == '6')
360 return JK_AJP_STATE_PROBE;
362 return JK_AJP_STATE_DEF;
365 int jk_ajp_get_cping_mode(const char *m, int def)
371 if (*m == 'C' || *m == 'c')
372 mv |= AJP_CPING_CONNECT;
373 else if (*m == 'P' || *m == 'p')
374 mv |= AJP_CPING_PREPOST;
375 else if (*m == 'I' || *m == 'i')
376 mv |= AJP_CPING_INTERVAL;
377 else if (*m == 'A' || *m == 'a') {
378 mv = AJP_CPING_CONNECT | AJP_CPING_PREPOST | AJP_CPING_INTERVAL;
390 AJPV13_REQUEST/AJPV14_REQUEST=
391 request_prefix (1) (byte)
401 num_headers*(req_header_name header_value)
403 ?context (byte)(string)
404 ?servlet_path (byte)(string)
405 ?remote_user (byte)(string)
406 ?auth_type (byte)(string)
407 ?query_string (byte)(string)
408 ?route (byte)(string)
409 ?ssl_cert (byte)(string)
410 ?ssl_cipher (byte)(string)
411 ?ssl_session (byte)(string)
412 ?ssl_key_size (byte)(int) via JkOptions +ForwardKeySize
413 request_terminator (byte)
414 ?body content_length*(var binary)
418 static int ajp_marshal_into_msgb(jk_msg_buf_t *msg,
420 jk_logger_t *l, ajp_endpoint_t * ae)
427 if ((method = sc_for_req_method(s->method,
428 strlen(s->method))) == UNKNOWN_METHOD)
429 method = SC_M_JK_STORED;
431 if (jk_b_append_byte(msg, JK_AJP13_FORWARD_REQUEST) ||
432 jk_b_append_byte(msg, (unsigned char)method) ||
433 jk_b_append_string(msg, s->protocol) ||
434 jk_b_append_string(msg, s->req_uri) ||
435 jk_b_append_string(msg, s->remote_addr) ||
436 jk_b_append_string(msg, s->remote_host) ||
437 jk_b_append_string(msg, s->server_name) ||
438 jk_b_append_int(msg, (unsigned short)s->server_port) ||
439 jk_b_append_byte(msg, (unsigned char)(s->is_ssl)) ||
440 jk_b_append_int(msg, (unsigned short)(s->num_headers))) {
442 jk_log(l, JK_LOG_ERROR,
443 "failed appending the message begining");
448 for (i = 0; i < s->num_headers; i++) {
451 if ((sc = sc_for_req_header(s->headers_names[i])) != UNKNOWN_METHOD) {
452 if (jk_b_append_int(msg, (unsigned short)sc)) {
453 jk_log(l, JK_LOG_ERROR,
454 "failed appending the header name");
460 if (jk_b_append_string(msg, s->headers_names[i])) {
461 jk_log(l, JK_LOG_ERROR,
462 "failed appending the header name");
468 if (jk_b_append_string(msg, s->headers_values[i])) {
469 jk_log(l, JK_LOG_ERROR,
470 "failed appending the header value");
477 if (jk_b_append_byte(msg, SC_A_SECRET) ||
478 jk_b_append_string(msg, s->secret)) {
479 jk_log(l, JK_LOG_ERROR,
480 "failed appending secret");
486 if (s->remote_user) {
487 if (jk_b_append_byte(msg, SC_A_REMOTE_USER) ||
488 jk_b_append_string(msg, s->remote_user)) {
489 jk_log(l, JK_LOG_ERROR,
490 "failed appending the remote user");
496 if (jk_b_append_byte(msg, SC_A_AUTH_TYPE) ||
497 jk_b_append_string(msg, s->auth_type)) {
498 jk_log(l, JK_LOG_ERROR,
499 "failed appending the auth type");
504 if (s->query_string) {
505 if (jk_b_append_byte(msg, SC_A_QUERY_STRING) ||
506 #if defined(AS400) && !defined(AS400_UTF8)
507 jk_b_append_asciistring(msg, s->query_string)) {
509 jk_b_append_string(msg, s->query_string)) {
511 jk_log(l, JK_LOG_ERROR,
512 "failed appending the query string");
518 if (jk_b_append_byte(msg, SC_A_ROUTE) ||
519 jk_b_append_string(msg, s->route)) {
520 jk_log(l, JK_LOG_ERROR,
521 "failed appending the route");
526 if (s->ssl_cert_len) {
527 if (jk_b_append_byte(msg, SC_A_SSL_CERT) ||
528 jk_b_append_string(msg, s->ssl_cert)) {
529 jk_log(l, JK_LOG_ERROR,
530 "failed appending the SSL certificates");
537 if (jk_b_append_byte(msg, SC_A_SSL_CIPHER) ||
538 jk_b_append_string(msg, s->ssl_cipher)) {
539 jk_log(l, JK_LOG_ERROR,
540 "failed appending the SSL ciphers");
545 if (s->ssl_session) {
546 if (jk_b_append_byte(msg, SC_A_SSL_SESSION) ||
547 jk_b_append_string(msg, s->ssl_session)) {
548 jk_log(l, JK_LOG_ERROR,
549 "failed appending the SSL session");
556 * ssl_key_size is required by Servlet 2.3 API
557 * added support only in ajp14 mode
558 * JFC removed: ae->proto == AJP14_PROTO
560 if (s->ssl_key_size != -1) {
561 if (jk_b_append_byte(msg, SC_A_SSL_KEY_SIZE) ||
562 jk_b_append_int(msg, (unsigned short)s->ssl_key_size)) {
563 jk_log(l, JK_LOG_ERROR,
564 "failed appending the SSL key size");
570 /* If the method was unrecognized, encode it as an attribute */
571 if (method == SC_M_JK_STORED) {
572 if (JK_IS_DEBUG_LEVEL(l))
573 jk_log(l, JK_LOG_DEBUG, "unknown method %s", s->method);
574 if (jk_b_append_byte(msg, SC_A_STORED_METHOD) ||
575 jk_b_append_string(msg, s->method)) {
576 jk_log(l, JK_LOG_ERROR,
577 "failed appending the request method");
583 /* Forward the remote port information, which was forgotten
584 * from the builtin data of the AJP 13 protocol.
585 * Since the servlet spec allows to retrieve it via getRemotePort(),
586 * we provide the port to the Tomcat connector as a request
587 * attribute. Modern Tomcat versions know how to retrieve
588 * the remote port from this attribute.
591 if (jk_b_append_byte(msg, SC_A_REQ_ATTRIBUTE) ||
592 jk_b_append_string(msg, SC_A_REQ_REMOTE_PORT) ||
593 jk_b_append_string(msg, s->remote_port)) {
594 jk_log(l, JK_LOG_ERROR,
595 "failed appending the remote port %s",
602 /* Forward activation information from the load balancer.
603 * It can be used by the backend to deny access by requests,
604 * which come with a session id but for an invalid session.
605 * Such requests get forwarded to backends even if they
606 * are disabled" in the load balancer, because the balancer
607 * does not know, which sessions are valid.
608 * If the backend can check, that is was "disabled" it can
609 * delete the session cookie and respond with a self-referential
610 * redirect. The new request will then be balanced to some
611 * other node that is not disabled.
614 if (jk_b_append_byte(msg, SC_A_REQ_ATTRIBUTE) ||
615 jk_b_append_string(msg, SC_A_JK_LB_ACTIVATION) ||
616 jk_b_append_string(msg, s->activation)) {
617 jk_log(l, JK_LOG_ERROR,
618 "failed appending the activation state %s",
625 if (s->num_attributes > 0) {
626 for (i = 0; i < s->num_attributes; i++) {
627 if (jk_b_append_byte(msg, SC_A_REQ_ATTRIBUTE) ||
628 jk_b_append_string(msg, s->attributes_names[i]) ||
629 jk_b_append_string(msg, s->attributes_values[i])) {
630 jk_log(l, JK_LOG_ERROR,
631 "failed appending attribute %s=%s",
632 s->attributes_names[i], s->attributes_values[i]);
639 if (jk_b_append_byte(msg, SC_A_ARE_DONE)) {
640 jk_log(l, JK_LOG_ERROR,
641 "failed appending the message end");
646 if (JK_IS_DEBUG_LEVEL(l))
647 jk_log(l, JK_LOG_DEBUG, "ajp marshaling done");
653 AJPV13_RESPONSE/AJPV14_RESPONSE:=
658 num_headers*(res_header_name header_value)
660 terminator boolean <! -- recycle connection or not -->
663 sc_req_header_name | (string)
666 sc_res_header_name | (string)
673 body length*(var binary)
678 static int ajp_unmarshal_response(jk_msg_buf_t *msg,
680 ajp_endpoint_t * ae, jk_logger_t *l)
682 jk_pool_t *p = &ae->pool;
686 d->status = jk_b_get_int(msg);
688 jk_log(l, JK_LOG_ERROR,
694 d->msg = (char *)jk_b_get_string(msg);
696 #if (defined(AS400) && !defined(AS400_UTF8)) || defined(_OSD_POSIX)
697 jk_xlate_from_ascii(d->msg, strlen(d->msg));
701 if (JK_IS_DEBUG_LEVEL(l))
702 jk_log(l, JK_LOG_DEBUG,
703 "status = %d", d->status);
705 d->num_headers = jk_b_get_int(msg);
706 d->header_names = d->header_values = NULL;
708 if (JK_IS_DEBUG_LEVEL(l))
709 jk_log(l, JK_LOG_DEBUG,
710 "Number of headers is = %d",
713 if (d->num_headers) {
714 d->header_names = jk_pool_alloc(p, sizeof(char *) * d->num_headers);
715 d->header_values = jk_pool_alloc(p, sizeof(char *) * d->num_headers);
717 if (d->header_names && d->header_values) {
719 for (i = 0; i < d->num_headers; i++) {
720 unsigned short name = jk_b_pget_int(msg, msg->pos);
722 if ((name & 0XFF00) == 0XA000) {
724 name = name & 0X00FF;
725 if (name <= SC_RES_HEADERS_NUM) {
727 (char *)long_res_header_for_sc(name);
730 jk_log(l, JK_LOG_ERROR,
731 "No such sc (%d)", name);
737 d->header_names[i] = (char *)jk_b_get_string(msg);
738 if (!d->header_names[i]) {
739 jk_log(l, JK_LOG_ERROR,
744 #if (defined(AS400) && !defined(AS400_UTF8)) || defined(_OSD_POSIX)
745 jk_xlate_from_ascii(d->header_names[i],
746 strlen(d->header_names[i]));
751 d->header_values[i] = (char *)jk_b_get_string(msg);
752 if (!d->header_values[i]) {
753 jk_log(l, JK_LOG_ERROR,
754 "NULL header value");
759 #if (defined(AS400) && !defined(AS400_UTF8)) || defined(_OSD_POSIX)
760 jk_xlate_from_ascii(d->header_values[i],
761 strlen(d->header_values[i]));
764 if (JK_IS_DEBUG_LEVEL(l))
765 jk_log(l, JK_LOG_DEBUG,
766 "Header[%d] [%s] = [%s]",
767 i, d->header_names[i], d->header_values[i]);
779 static void ajp_abort_endpoint(ajp_endpoint_t * ae, int shutdown, jk_logger_t *l)
782 if (shutdown == JK_TRUE && IS_VALID_SOCKET(ae->sd)) {
783 if (ae->hard_close) {
784 /* Force unclean connection close to communicate client write errors
785 * back to Tomcat by aborting AJP response writes.
787 jk_close_socket(ae->sd, l);
790 jk_shutdown_socket(ae->sd, l);
793 ae->worker->s->connected--;
794 ae->sd = JK_INVALID_SOCKET;
795 ae->last_op = JK_AJP13_END_RESPONSE;
800 * Reset the endpoint (clean buf and close socket)
802 static void ajp_reset_endpoint(ajp_endpoint_t * ae, jk_logger_t *l)
806 if (JK_IS_DEBUG_LEVEL(l))
807 jk_log(l, JK_LOG_DEBUG,
808 "(%s) resetting endpoint with socket %d%s",
809 ae->worker->name, ae->sd, ae->reuse? "" : " (socket shutdown)");
811 ajp_abort_endpoint(ae, JK_TRUE, l);
813 jk_reset_pool(&(ae->pool));
818 * Close the endpoint (close pool and close socket)
820 void ajp_close_endpoint(ajp_endpoint_t * ae, jk_logger_t *l)
824 if (JK_IS_DEBUG_LEVEL(l))
825 jk_log(l, JK_LOG_DEBUG,
826 "(%s) closing endpoint with socket %d%s",
827 ae->worker->name, ae->sd, ae->reuse ? "" : " (socket shutdown)");
828 if (IS_VALID_SOCKET(ae->sd)) {
829 jk_shutdown_socket(ae->sd, l);
831 ae->sd = JK_INVALID_SOCKET;
832 jk_close_pool(&(ae->pool));
838 /** Steal a connection from an idle cache endpoint
839 * @param ae endpoint that needs a new connection
841 * @return JK_FALSE: failure
843 * @remark Always closes old socket endpoint
845 static int ajp_next_connection(ajp_endpoint_t *ae, jk_logger_t *l)
849 ajp_worker_t *aw = ae->worker;
853 /* Close previous socket */
854 if (IS_VALID_SOCKET(ae->sd))
855 jk_shutdown_socket(ae->sd, l);
856 /* Mark existing endpoint socket as closed */
857 ae->sd = JK_INVALID_SOCKET;
858 JK_ENTER_CS(&aw->cs, rc);
861 for (i = 0; i < aw->ep_cache_sz; i++) {
862 /* Find cache slot with usable socket */
863 if (aw->ep_cache[i] && IS_VALID_SOCKET(aw->ep_cache[i]->sd)) {
864 ae->sd = aw->ep_cache[i]->sd;
865 aw->ep_cache[i]->sd = JK_INVALID_SOCKET;
869 JK_LEAVE_CS(&aw->cs, rc);
870 if (IS_VALID_SOCKET(ae->sd)) {
872 if (JK_IS_DEBUG_LEVEL(l))
873 jk_log(l, JK_LOG_DEBUG,
874 "(%s) Will try pooled connection socket %d from slot %d",
875 ae->worker->name, ae->sd, i);
882 /** Handle the cping/cpong query
884 * @param timeout wait timeout in milliseconds
886 * @return JK_FALSE: failure
888 * @remark Always closes socket in case of
891 static int ajp_handle_cping_cpong(ajp_endpoint_t * ae, int timeout, jk_logger_t *l)
900 msg = jk_b_new(&ae->pool);
902 jk_log(l, JK_LOG_ERROR,
903 "Failed allocating AJP message");
907 if (jk_b_set_buffer_size(msg, 16)) {
908 jk_log(l, JK_LOG_ERROR,
909 "Failed allocating AJP message buffer");
914 jk_b_append_byte(msg, AJP13_CPING_REQUEST);
916 /* Send CPing query */
917 if (ajp_connection_tcp_send_message(ae, msg, l) != JK_TRUE) {
918 jk_log(l, JK_LOG_INFO,
919 "can't send cping query");
924 for (i = 0; i < 2; i++) {
925 /* wait for Pong reply for timeout milliseconds
927 if (jk_is_input_event(ae->sd, timeout, l) == JK_FALSE) {
928 ae->last_errno = errno;
929 jk_log(l, JK_LOG_INFO, "timeout in reply cpong");
930 /* We can't trust this connection any more. */
931 ajp_abort_endpoint(ae, JK_TRUE, l);
936 /* Read and check for Pong reply
938 if (ajp_connection_tcp_get_message(ae, msg, l) != JK_TRUE) {
939 jk_log(l, JK_LOG_INFO,
940 "awaited reply cpong, not received");
945 if ((cmd = jk_b_get_byte(msg)) != AJP13_CPONG_REPLY) {
946 /* If the respose was not CPONG it means that
947 * the previous response was not consumed by the
948 * client but the AJP messages was already in
949 * the network buffer.
950 * silently drop this single extra packet instead
951 * recycling the connection
953 if (i || ae->last_op == JK_AJP13_END_RESPONSE ||
954 cmd < JK_AJP13_SEND_BODY_CHUNK ||
955 cmd > AJP13_CPONG_REPLY) {
956 jk_log(l, JK_LOG_WARNING,
957 "awaited reply cpong, received %d instead. "
958 "Closing connection",
960 /* We can't trust this connection any more. */
961 ajp_abort_endpoint(ae, JK_TRUE, l);
966 jk_log(l, JK_LOG_INFO,
967 "awaited reply cpong, received %d instead. "
968 "Retrying next packet",
974 ae->last_op = AJP13_CPONG_REPLY;
975 /* We have received Pong reply */
983 /** Connect an endpoint to a backend
986 * @return JK_FALSE: failure
988 * @remark Always closes socket in case of
990 * @remark Cares about ae->last_errno
992 int ajp_connect_to_endpoint(ajp_endpoint_t * ae, jk_logger_t *l)
1000 ae->sd = jk_open_socket(&ae->worker->worker_inet_addr,
1001 ae->worker->keepalive,
1002 ae->worker->socket_timeout,
1003 ae->worker->socket_connect_timeout,
1004 ae->worker->socket_buf, l);
1006 if (!IS_VALID_SOCKET(ae->sd)) {
1007 ae->last_errno = errno;
1008 jk_log(l, JK_LOG_INFO,
1009 "Failed opening socket to (%s) (errno=%d)",
1010 jk_dump_hinfo(&ae->worker->worker_inet_addr, buf), ae->last_errno);
1014 ae->worker->s->connected++;
1015 /* set last_access only if needed */
1016 if (ae->worker->cache_timeout > 0)
1017 ae->last_access = time(NULL);
1018 /* Check if we must execute a logon after the physical connect */
1019 /* XXX: Not sure, if we really should do logon before cping/cpong */
1020 /* XXX: and if no cping/cpong is allowed before or after logon. */
1021 if (ae->worker->logon != NULL) {
1022 rc = ae->worker->logon(ae, l);
1023 if (rc == JK_FALSE) {
1024 jk_log(l, JK_LOG_ERROR,
1025 "(%s) ajp14 worker logon to the backend server failed",
1027 /* Close the socket if unable to logon */
1028 ajp_abort_endpoint(ae, JK_TRUE, l);
1031 /* XXX: Should we send a cping also after logon to validate the connection? */
1032 else if (ae->worker->connect_timeout > 0) {
1033 rc = ajp_handle_cping_cpong(ae, ae->worker->connect_timeout, l);
1035 jk_log(l, JK_LOG_ERROR,
1036 "(%s) cping/cpong after connecting to the backend server failed (errno=%d)",
1037 ae->worker->name, ae->last_errno);
1043 /* Syncing config values from shm */
1044 void jk_ajp_pull(ajp_worker_t * aw, int locked, jk_logger_t *l)
1046 int address_change = JK_FALSE;
1048 char host[JK_SHM_STR_SIZ+1];
1049 struct sockaddr_in inet_addr;
1052 if (JK_IS_DEBUG_LEVEL(l))
1053 jk_log(l, JK_LOG_DEBUG,
1054 "syncing mem for ajp worker '%s' from shm (%u -> %u) [%u->%u]",
1055 aw->name, aw->sequence, aw->s->h.sequence, aw->addr_sequence, aw->s->addr_sequence);
1056 if (locked == JK_FALSE)
1059 aw->cache_timeout = aw->s->cache_timeout;
1060 aw->connect_timeout = aw->s->connect_timeout;
1061 aw->ping_timeout = aw->s->ping_timeout;
1062 aw->reply_timeout = aw->s->reply_timeout;
1063 aw->prepost_timeout = aw->s->prepost_timeout;
1064 aw->recovery_opts = aw->s->recovery_opts;
1065 aw->retries = aw->s->retries;
1066 aw->retry_interval = aw->s->retry_interval;
1067 aw->max_packet_size = aw->s->max_packet_size;
1068 aw->sequence = aw->s->h.sequence;
1069 if (aw->addr_sequence != aw->s->addr_sequence) {
1070 address_change = JK_TRUE;
1071 aw->addr_sequence = aw->s->addr_sequence;
1072 strncpy(host, aw->s->host, JK_SHM_STR_SIZ);
1075 if (locked == JK_FALSE)
1078 if (address_change == JK_TRUE) {
1079 if (!jk_resolve(host, port, &inet_addr,
1080 aw->worker.we->pool, l)) {
1081 jk_log(l, JK_LOG_ERROR,
1082 "Failed resolving address '%s:%d' for worker '%s'.",
1083 host, port, aw->name);
1087 JK_ENTER_CS(&aw->cs, rc);
1090 for (i = 0; i < aw->ep_cache_sz; i++) {
1091 /* Close all connections in the cache */
1092 if (aw->ep_cache[i] && IS_VALID_SOCKET(aw->ep_cache[i]->sd)) {
1093 int sd = aw->ep_cache[i]->sd;
1094 aw->ep_cache[i]->sd = JK_INVALID_SOCKET;
1095 aw->ep_cache[i]->addr_sequence = aw->addr_sequence;
1096 jk_shutdown_socket(sd, l);
1102 strncpy(aw->host, host, JK_SHM_STR_SIZ);
1103 memcpy(&(aw->worker_inet_addr), &inet_addr, sizeof(inet_addr));
1105 JK_LEAVE_CS(&aw->cs, rc);
1107 jk_log(l, JK_LOG_ERROR,
1108 "locking thread (errno=%d)", errno);
1116 /* Syncing config values to shm */
1117 void jk_ajp_push(ajp_worker_t * aw, int locked, jk_logger_t *l)
1119 int address_change = JK_FALSE;
1123 if (JK_IS_DEBUG_LEVEL(l))
1124 jk_log(l, JK_LOG_DEBUG,
1125 "syncing shm for ajp worker '%s' from mem (%u -> %u) [%u->%u]",
1126 aw->name, aw->s->h.sequence, aw->sequence, aw->s->addr_sequence, aw->addr_sequence);
1127 if (locked == JK_FALSE)
1130 aw->s->cache_timeout = aw->cache_timeout;
1131 aw->s->connect_timeout = aw->connect_timeout;
1132 aw->s->ping_timeout = aw->ping_timeout;
1133 aw->s->reply_timeout = aw->reply_timeout;
1134 aw->s->prepost_timeout = aw->prepost_timeout;
1135 aw->s->recovery_opts = aw->recovery_opts;
1136 aw->s->retries = aw->retries;
1137 aw->s->retry_interval = aw->retry_interval;
1138 aw->s->max_packet_size = aw->max_packet_size;
1139 aw->s->h.sequence = aw->sequence;
1140 if (aw->s->addr_sequence != aw->addr_sequence) {
1141 address_change = JK_TRUE;
1142 strncpy(aw->s->host, aw->host, JK_SHM_STR_SIZ);
1143 aw->s->port = aw->port;
1144 aw->s->addr_sequence = aw->addr_sequence;
1146 if (locked == JK_FALSE)
1149 if (address_change == JK_TRUE) {
1151 JK_ENTER_CS(&aw->cs, rc);
1154 for (i = 0; i < aw->ep_cache_sz; i++) {
1155 /* Close all connections in the cache */
1156 if (aw->ep_cache[i] && IS_VALID_SOCKET(aw->ep_cache[i]->sd)) {
1157 int sd = aw->ep_cache[i]->sd;
1158 aw->ep_cache[i]->sd = JK_INVALID_SOCKET;
1159 aw->ep_cache[i]->addr_sequence = aw->addr_sequence;
1160 jk_shutdown_socket(sd, l);
1164 JK_LEAVE_CS(&aw->cs, rc);
1166 jk_log(l, JK_LOG_ERROR,
1167 "locking thread (errno=%d)", errno);
1173 /** Send a message to an endpoint, using corresponding PROTO HEADER
1174 * @param ae endpoint
1175 * @param msg message to send
1177 * @return JK_FATAL_ERROR: endpoint contains unknown protocol
1178 * JK_FALSE: other failure
1180 * @remark Always closes socket in case of
1181 * a socket error, or JK_FATAL_ERROR
1182 * @remark Cares about ae->last_errno
1184 int ajp_connection_tcp_send_message(ajp_endpoint_t * ae,
1185 jk_msg_buf_t *msg, jk_logger_t *l)
1192 if (ae->proto == AJP13_PROTO) {
1193 jk_b_end(msg, AJP13_WS_HEADER);
1194 if (JK_IS_DEBUG_LEVEL(l))
1195 jk_dump_buff(l, JK_LOG_DEBUG, "sending to ajp13", msg);
1197 else if (ae->proto == AJP14_PROTO) {
1198 jk_b_end(msg, AJP14_WS_HEADER);
1199 if (JK_IS_DEBUG_LEVEL(l))
1200 jk_dump_buff(l, JK_LOG_DEBUG, "sending to ajp14", msg);
1203 jk_log(l, JK_LOG_ERROR,
1204 "(%s) unknown protocol %d, supported are AJP13/AJP14",
1205 ae->worker->name, ae->proto);
1206 /* We've got a protocol error. */
1207 /* We can't trust this connection any more, */
1208 /* because we might have send already parts of the request. */
1209 ajp_abort_endpoint(ae, JK_TRUE, l);
1211 return JK_FATAL_ERROR;
1214 /* This is the only place in this function where we use the socket. */
1215 /* If sendfull gets an error, it implicitely closes the socket. */
1216 /* So any socket error inside ajp_connection_tcp_send_message */
1217 /* results in a socket close and invalidated endpoint connection. */
1218 if ((rc = jk_tcp_socket_sendfull(ae->sd, msg->buf,
1219 msg->len, l)) > 0) {
1220 ae->endpoint.wr += (jk_uint64_t)rc;
1224 ae->last_errno = errno;
1225 jk_log(l, JK_LOG_INFO,
1226 "sendfull for socket %d returned %d (errno=%d)",
1227 ae->sd, rc, ae->last_errno);
1228 ajp_abort_endpoint(ae, JK_FALSE, l);
1233 /** Receive a message from an endpoint, checking PROTO HEADER
1234 * @param ae endpoint
1235 * @param msg message to send
1237 * @return JK_TRUE: success
1238 * JK_FALSE: could not read the AJP packet header
1239 * JK_AJP_PROTOCOL_ERROR: failure after reading
1240 * the AJP packet header
1241 * @remark Always closes socket in case of
1243 * @remark Cares about ae->last_errno
1245 int ajp_connection_tcp_get_message(ajp_endpoint_t * ae,
1246 jk_msg_buf_t *msg, jk_logger_t *l)
1248 unsigned char head[AJP_HEADER_LEN];
1251 unsigned int header;
1257 /* If recvfull gets an error, it implicitely closes the socket. */
1258 /* We will invalidate the endpoint connection. */
1259 rc = jk_tcp_socket_recvfull(ae->sd, head, AJP_HEADER_LEN, l);
1261 /* If the return code is not negative */
1262 /* then we always get back the correct number of bytes. */
1264 if (rc == JK_SOCKET_EOF) {
1265 ae->last_errno = EPIPE;
1266 jk_log(l, JK_LOG_INFO,
1267 "(%s) can't receive the response header message from tomcat, "
1268 "tomcat (%s) has forced a connection close for socket %d",
1269 ae->worker->name, jk_dump_hinfo(&ae->worker->worker_inet_addr, buf),
1273 ae->last_errno = -rc;
1274 jk_log(l, JK_LOG_INFO,
1275 "(%s) can't receive the response header message from tomcat, "
1276 "network problems or tomcat (%s) is down (errno=%d)",
1277 ae->worker->name, jk_dump_hinfo(&ae->worker->worker_inet_addr, buf),
1280 ajp_abort_endpoint(ae, JK_FALSE, l);
1284 ae->endpoint.rd += (jk_uint64_t)rc;
1285 header = ((unsigned int)head[0] << 8) | head[1];
1287 if (ae->proto == AJP13_PROTO) {
1288 if (header != AJP13_SW_HEADER) {
1290 if (header == AJP14_SW_HEADER) {
1291 jk_log(l, JK_LOG_ERROR,
1292 "received AJP14 reply on an AJP13 connection from %s",
1293 jk_dump_hinfo(&ae->worker->worker_inet_addr, buf));
1296 jk_log(l, JK_LOG_ERROR,
1297 "wrong message format 0x%04x from %s",
1298 header, jk_dump_hinfo(&ae->worker->worker_inet_addr,
1301 /* We've got a protocol error. */
1302 /* We can't trust this connection any more. */
1303 ajp_abort_endpoint(ae, JK_TRUE, l);
1305 return JK_AJP_PROTOCOL_ERROR;
1308 else if (ae->proto == AJP14_PROTO) {
1309 if (header != AJP14_SW_HEADER) {
1311 if (header == AJP13_SW_HEADER) {
1312 jk_log(l, JK_LOG_ERROR,
1313 "received AJP13 reply on an AJP14 connection from %s",
1314 jk_dump_hinfo(&ae->worker->worker_inet_addr, buf));
1317 jk_log(l, JK_LOG_ERROR,
1318 "wrong message format 0x%04x from %s",
1319 header, jk_dump_hinfo(&ae->worker->worker_inet_addr,
1322 /* We've got a protocol error. */
1323 /* We can't trust this connection any more. */
1324 ajp_abort_endpoint(ae, JK_TRUE, l);
1326 return JK_AJP_PROTOCOL_ERROR;
1330 msglen = ((head[2] & 0xff) << 8);
1331 msglen += (head[3] & 0xFF);
1333 if (msglen > msg->maxlen) {
1334 jk_log(l, JK_LOG_ERROR,
1335 "wrong message size %d %d from %s",
1336 msglen, msg->maxlen,
1337 jk_dump_hinfo(&ae->worker->worker_inet_addr, buf));
1338 /* We've got a protocol error. */
1339 /* We can't trust this connection any more. */
1340 ajp_abort_endpoint(ae, JK_TRUE, l);
1342 return JK_AJP_PROTOCOL_ERROR;
1348 /* If recvfull gets an error, it implicitely closes the socket. */
1349 /* We will invalidate the endpoint connection. */
1350 rc = jk_tcp_socket_recvfull(ae->sd, msg->buf, msglen, l);
1351 /* If the return code is not negative */
1352 /* then we always get back the correct number of bytes. */
1354 if (rc == JK_SOCKET_EOF) {
1355 ae->last_errno = EPIPE;
1356 jk_log(l, JK_LOG_ERROR,
1357 "(%s) can't receive the response body message from tomcat, "
1358 "tomcat (%s) has forced a connection close for socket %d",
1359 ae->worker->name, jk_dump_hinfo(&ae->worker->worker_inet_addr, buf),
1363 ae->last_errno = -rc;
1364 jk_log(l, JK_LOG_ERROR,
1365 "(%s) can't receive the response body message from tomcat, "
1366 "network problems or tomcat (%s) is down (errno=%d)",
1367 ae->worker->name, jk_dump_hinfo(&ae->worker->worker_inet_addr, buf),
1370 ajp_abort_endpoint(ae, JK_FALSE, l);
1372 /* Although we have a connection, this is effectively a protocol error.
1373 * We received the AJP header packet, but not the packet payload
1375 return JK_AJP_PROTOCOL_ERROR;
1377 ae->endpoint.rd += (jk_uint64_t)rc;
1379 if (JK_IS_DEBUG_LEVEL(l)) {
1380 if (ae->proto == AJP13_PROTO)
1381 jk_dump_buff(l, JK_LOG_DEBUG, "received from ajp13", msg);
1382 else if (ae->proto == AJP14_PROTO)
1383 jk_dump_buff(l, JK_LOG_DEBUG, "received from ajp14", msg);
1390 * Read all the data from the socket.
1392 * Socket API doesn't guaranty that all the data will be kept in a
1393 * single read, so we must loop until all awaited data is received
1396 static int ajp_read_fully_from_server(jk_ws_service_t *s, jk_logger_t *l,
1397 unsigned char *buf, unsigned int len)
1399 unsigned int rdlen = 0;
1400 unsigned int padded_len = len;
1404 if (s->is_chunked && s->no_more_chunks) {
1408 if (s->is_chunked) {
1409 /* Corner case: buf must be large enough to hold next
1410 * chunk size (if we're on or near a chunk border).
1411 * Pad the length to a reasonable value, otherwise the
1412 * read fails and the remaining chunks are tossed.
1414 padded_len = (len < CHUNK_BUFFER_PAD) ? len : len - CHUNK_BUFFER_PAD;
1417 while (rdlen < padded_len) {
1418 unsigned int this_time = 0;
1419 if (!s->read(s, buf + rdlen, len - rdlen, &this_time)) {
1420 /* Remote Client read failed. */
1422 return JK_CLIENT_RD_ERROR;
1425 if (0 == this_time) {
1426 if (s->is_chunked) {
1427 s->no_more_chunks = 1; /* read no more */
1440 * Read data from AJP13/AJP14 protocol
1441 * Returns -1 on error, else number of bytes read
1444 static int ajp_read_into_msg_buff(ajp_endpoint_t * ae,
1446 jk_msg_buf_t *msg, int len, jk_logger_t *l)
1448 unsigned char *read_buf = msg->buf;
1454 read_buf += AJP_HEADER_LEN; /* leave some space for the buffer headers */
1455 read_buf += AJP_HEADER_SZ_LEN; /* leave some space for the read length */
1457 /* Pick the max size since we don't know the content_length */
1458 if (r->is_chunked && len == 0) {
1459 len = AJP13_MAX_SEND_BODY_SZ;
1462 if ((len = ajp_read_fully_from_server(r, l, read_buf, len)) < 0) {
1463 jk_log(l, JK_LOG_INFO,
1464 "(%s) receiving data from client failed. "
1465 "Connection aborted or network problems",
1468 return JK_CLIENT_RD_ERROR;
1471 if (!r->is_chunked) {
1472 ae->left_bytes_to_send -= len;
1476 /* Recipient recognizes empty packet as end of stream, not
1477 an empty body packet */
1478 if (0 != jk_b_append_int(msg, (unsigned short)len)) {
1479 jk_log(l, JK_LOG_INFO,
1480 "Failed appending message length");
1482 return JK_CLIENT_RD_ERROR;
1494 * send request to Tomcat via Ajp13
1495 * - first try to find reuseable socket
1496 * - if no such available, try to connect
1497 * - send request, but send must be seen as asynchronous,
1498 * since send() call will return noerror about 95% of time
1499 * Hopefully we'll get more information on next read.
1501 * nb: op->request is the original request msg buffer
1502 * op->reply is the reply msg buffer which could be scratched
1504 * Return values of ajp_send_request() function:
1505 * return value op->recoverable reason
1506 * JK_FATAL_ERROR JK_FALSE ajp_connection_tcp_send_message() returns JK_FATAL_ERROR
1507 * Endpoint belongs to unknown protocol.
1508 * JK_FATAL_ERROR JK_TRUE ajp_connection_tcp_send_message() returns JK_FALSE
1509 * Sending request or request body in jk_tcp_socket_sendfull() returns with error.
1510 * JK_FATAL_ERROR JK_TRUE Could not connect to backend
1511 * JK_CLIENT_RD_ERROR JK_FALSE Error during reading parts of POST body from client
1512 * JK_TRUE JK_TRUE All other cases (OK)
1514 static int ajp_send_request(jk_endpoint_t *e,
1517 ajp_endpoint_t * ae, ajp_operation_t * op)
1528 /* Up to now, we can recover */
1529 op->recoverable = JK_TRUE;
1531 /* Check if the previous request really ended
1533 if (ae->last_op != JK_AJP13_END_RESPONSE &&
1534 ae->last_op != AJP13_CPONG_REPLY) {
1535 jk_log(l, JK_LOG_INFO,
1536 "(%s) did not receive END_RESPONSE, "
1537 "closing socket %d",
1538 ae->worker->name, ae->sd);
1539 ajp_abort_endpoint(ae, JK_TRUE, l);
1542 * First try to check open connections...
1544 while (IS_VALID_SOCKET(ae->sd)) {
1546 if (jk_is_socket_connected(ae->sd, l) == JK_FALSE) {
1547 ae->last_errno = errno;
1548 jk_log(l, JK_LOG_DEBUG,
1549 "(%s) failed sending request, "
1550 "socket %d is not connected any more (errno=%d)",
1551 ae->worker->name, ae->sd, ae->last_errno);
1552 ajp_abort_endpoint(ae, JK_FALSE, l);
1556 if (ae->worker->prepost_timeout > 0 && !err) {
1557 /* handle cping/cpong if prepost_timeout is set
1558 * If the socket is disconnected no need to handle
1561 if (ajp_handle_cping_cpong(ae,
1562 ae->worker->prepost_timeout, l) == JK_FALSE) {
1563 jk_log(l, JK_LOG_INFO,
1564 "(%s) failed sending request, "
1565 "socket %d prepost cping/cpong failure (errno=%d)",
1566 ae->worker->name, ae->sd, ae->last_errno);
1567 /* XXX: Is there any reason to try other
1568 * connections to the node if one of them fails
1569 * the cping/cpong heartbeat?
1570 * Tomcat can be either too busy or simply dead, so
1571 * there is a chance that all other connections would
1579 /* We've got a connected socket and the optional
1580 * cping/cpong worked, so let's send the request now.
1582 if (err == JK_FALSE) {
1583 rc = ajp_connection_tcp_send_message(ae, op->request, l);
1584 /* If this worked, we can break out of the loop
1585 * and proceed with the request.
1587 if (rc == JK_TRUE) {
1588 ae->last_op = JK_AJP13_FORWARD_REQUEST;
1591 /* Error during sending the request.
1594 if (rc == JK_FATAL_ERROR)
1595 op->recoverable = JK_FALSE;
1596 jk_log(l, JK_LOG_INFO,
1597 "(%s) failed sending request (%srecoverable) "
1600 op->recoverable ? "" : "un",
1603 return JK_FATAL_ERROR;
1605 /* If we got an error or can't send data, then try to steal another pooled
1606 * connection and try again. If we are not successful, break out of this
1607 * loop and try to open a new connection after the loop.
1609 if (ajp_next_connection(ae, l) == JK_FALSE)
1614 * If we failed to reuse a connection, try to reconnect.
1616 if (!IS_VALID_SOCKET(ae->sd)) {
1617 /* Could not steal any connection from an endpoint - backend is disconnected */
1618 if (err_conn + err_cping + err_send > 0)
1619 jk_log(l, JK_LOG_INFO,
1620 "(%s) all endpoints are disconnected, "
1621 "detected by connect check (%d), cping (%d), send (%d)",
1622 ae->worker->name, err_conn, err_cping, err_send);
1623 else if (JK_IS_DEBUG_LEVEL(l))
1624 jk_log(l, JK_LOG_DEBUG,
1625 "(%s) all endpoints are disconnected.",
1627 /* Connect to the backend.
1629 if (ajp_connect_to_endpoint(ae, l) != JK_TRUE) {
1630 jk_log(l, JK_LOG_ERROR,
1631 "(%s) connecting to backend failed. Tomcat is probably not started "
1632 "or is listening on the wrong port (errno=%d)",
1633 ae->worker->name, ae->last_errno);
1635 return JK_FATAL_ERROR;
1637 if (ae->worker->connect_timeout <= 0 &&
1638 ae->worker->prepost_timeout > 0) {
1639 /* handle cping/cpong if prepost_timeout is set
1640 * and we didn't already do a connect cping/cpong.
1642 if (ajp_handle_cping_cpong(ae,
1643 ae->worker->prepost_timeout, l) == JK_FALSE) {
1644 jk_log(l, JK_LOG_INFO,
1645 "(%s) failed sending request, "
1646 "socket %d prepost cping/cpong failure (errno=%d)",
1647 ae->worker->name, ae->sd, ae->last_errno);
1649 return JK_FATAL_ERROR;
1653 /* We've got a connected socket and the optional
1654 * cping/cpong worked, so let's send the request now.
1656 rc = ajp_connection_tcp_send_message(ae, op->request, l);
1657 /* Error during sending the request.
1659 if (rc != JK_TRUE) {
1660 if (rc == JK_FATAL_ERROR)
1661 op->recoverable = JK_FALSE;
1662 jk_log(l, JK_LOG_ERROR,
1663 "(%s) failed sending request on a fresh connection (%srecoverable), "
1664 "socket %d (errno=%d)",
1665 ae->worker->name, op->recoverable ? "" : "un",
1666 ae->sd, ae->last_errno);
1668 return JK_FATAL_ERROR;
1670 ae->last_op = JK_AJP13_FORWARD_REQUEST;
1672 else if (JK_IS_DEBUG_LEVEL(l))
1673 jk_log(l, JK_LOG_DEBUG,
1674 "(%s) Statistics about invalid connections: "
1675 "connect check (%d), cping (%d), send (%d)",
1676 ae->worker->name, err_conn, err_cping, err_send);
1679 * From now on an error means that we have an internal server error
1680 * or Tomcat crashed.
1683 if (JK_IS_DEBUG_LEVEL(l))
1684 jk_log(l, JK_LOG_DEBUG,
1685 "(%s) request body to send %" JK_UINT64_T_FMT " - request body to resend %d",
1686 ae->worker->name, ae->left_bytes_to_send,
1687 op->reply->len - AJP_HEADER_LEN);
1690 * POST recovery job is done here and will work when data to
1691 * POST are less than 8k, since it's the maximum size of op-post buffer.
1692 * We send here the first part of data which was sent previously to the
1696 /* Did we have something to resend (ie the op-post has been feeded previously */
1698 postlen = op->post->len;
1699 if (postlen > AJP_HEADER_LEN) {
1700 rc = ajp_connection_tcp_send_message(ae, op->post, l);
1701 /* Error during sending the request body.
1703 if (rc != JK_TRUE) {
1704 if (rc == JK_FATAL_ERROR)
1705 op->recoverable = JK_FALSE;
1706 jk_log(l, JK_LOG_ERROR,
1707 "(%s) failed sending request body of size %d (%srecoverable), "
1708 "socket %d (errno=%d)",
1709 ae->worker->name, postlen, op->recoverable ? "" : "un",
1710 ae->sd, ae->last_errno);
1712 return JK_FATAL_ERROR;
1715 if (JK_IS_DEBUG_LEVEL(l))
1716 jk_log(l, JK_LOG_DEBUG, "Resent the request body (%d)",
1720 else if (s->reco_status == RECO_FILLED) {
1721 /* Recovery in LB MODE */
1722 postlen = s->reco_buf->len;
1724 if (postlen > AJP_HEADER_LEN) {
1725 rc = ajp_connection_tcp_send_message(ae, s->reco_buf, l);
1726 /* Error during sending the request body.
1728 if (rc != JK_TRUE) {
1729 if (rc == JK_FATAL_ERROR)
1730 op->recoverable = JK_FALSE;
1731 jk_log(l, JK_LOG_ERROR,
1732 "(%s) failed sending request body of size %d (lb mode) (%srecoverable), "
1733 "socket %d (errno=%d)",
1734 ae->worker->name, postlen, op->recoverable ? "" : "un",
1735 ae->sd, ae->last_errno);
1737 return JK_FATAL_ERROR;
1741 if (JK_IS_DEBUG_LEVEL(l))
1742 jk_log(l, JK_LOG_DEBUG,
1743 "Resent the request body (lb mode) (%d)", postlen);
1747 /* We never sent any POST data and we check if we have to send at
1748 * least one block of data (max 8k). These data will be kept in reply
1749 * for resend if the remote Tomcat is down, a fact we will learn only
1750 * doing a read (not yet)
1752 /* || s->is_chunked - this can't be done here. The original protocol
1753 sends the first chunk of post data ( based on Content-Length ),
1754 and that's what the java side expects.
1755 Sending this data for chunked would break other ajp13 servers.
1757 Note that chunking will continue to work - using the normal read.
1760 if (ae->left_bytes_to_send > 0) {
1761 int len = AJP13_MAX_SEND_BODY_SZ;
1762 if (ae->left_bytes_to_send < (jk_uint64_t)AJP13_MAX_SEND_BODY_SZ) {
1763 len = (int)ae->left_bytes_to_send;
1765 if ((len = ajp_read_into_msg_buff(ae, s, op->post, len, l)) <= 0) {
1766 if (JK_IS_DEBUG_LEVEL(l))
1767 jk_log(l, JK_LOG_DEBUG,
1768 "(%s) browser stop sending data, no need to recover",
1770 op->recoverable = JK_FALSE;
1771 /* Send an empty POST message since per AJP protocol
1772 * spec whenever we have content length the message
1773 * packet must be followed with initial POST packet.
1774 * Size zero will be handled as error in container.
1776 jk_b_reset(op->post);
1777 jk_b_append_int(op->post, 0);
1778 ajp_connection_tcp_send_message(ae, op->post, l);
1780 return JK_CLIENT_RD_ERROR;
1783 /* If a RECOVERY buffer is available in LB mode, fill it */
1784 if (s->reco_status == RECO_INITED) {
1785 jk_b_copy(op->post, s->reco_buf);
1786 s->reco_status = RECO_FILLED;
1788 if (JK_IS_DEBUG_LEVEL(l))
1789 jk_log(l, JK_LOG_DEBUG,
1790 "(%s) sending %d bytes of request body",
1791 ae->worker->name, len);
1793 s->content_read = (jk_uint64_t)len;
1794 rc = ajp_connection_tcp_send_message(ae, op->post, l);
1795 /* Error during sending the request body.
1797 if (rc != JK_TRUE) {
1798 if (rc == JK_FATAL_ERROR)
1799 op->recoverable = JK_FALSE;
1800 jk_log(l, JK_LOG_ERROR,
1801 "(%s) failed sending request body of size %d (%srecoverable), "
1802 "socket %d (errno=%d)",
1803 ae->worker->name, len, op->recoverable ? "" : "un",
1804 ae->sd, ae->last_errno);
1806 return JK_FATAL_ERROR;
1816 * What to do with incoming data (dispatcher)
1819 static int ajp_process_callback(jk_msg_buf_t *msg,
1821 ajp_endpoint_t * ae,
1822 jk_ws_service_t *r, jk_logger_t *l)
1824 int code = (int)jk_b_get_byte(msg);
1829 case JK_AJP13_SEND_HEADERS:
1833 if (ae->last_op == JK_AJP13_SEND_HEADERS) {
1834 /* Do not send anything to the client.
1835 * Backend already send us the headers.
1837 if (JK_IS_DEBUG_LEVEL(l)) {
1838 jk_log(l, JK_LOG_DEBUG,
1839 "Already received AJP13_SEND HEADERS");
1842 return JK_AJP13_ERROR;
1844 if (!ajp_unmarshal_response(msg, &res, ae, l)) {
1845 jk_log(l, JK_LOG_ERROR,
1846 "ajp_unmarshal_response failed");
1848 return JK_AJP13_ERROR;
1850 r->http_response_status = res.status;
1851 if (r->extension.fail_on_status_size > 0)
1852 rc = is_http_status_fail(r->extension.fail_on_status_size,
1853 r->extension.fail_on_status, res.status);
1855 rc = is_http_status_fail(ae->worker->http_status_fail_num,
1856 ae->worker->http_status_fail, res.status);
1859 return JK_STATUS_FATAL_ERROR;
1863 return JK_STATUS_ERROR;
1866 if (r->extension.use_server_error_pages &&
1867 r->http_response_status >= r->extension.use_server_error_pages)
1868 r->response_blocked = JK_TRUE;
1871 * Call even if response is blocked, since it also handles
1872 * forwarding some headers for special http status codes
1873 * even if the server uses an own error page.
1874 * Example: The WWW-Authenticate header in case of
1875 * HTTP_UNAUTHORIZED (401).
1877 r->start_response(r, res.status, res.msg,
1878 (const char *const *)res.header_names,
1879 (const char *const *)res.header_values,
1882 if (!r->response_blocked) {
1883 if (r->flush && r->flush_header)
1887 return JK_AJP13_SEND_HEADERS;
1889 case JK_AJP13_SEND_BODY_CHUNK:
1890 if (ae->last_op == JK_AJP13_FORWARD_REQUEST) {
1891 /* AJP13_SEND_BODY_CHUNK with length 0 is
1892 * explicit flush packet message.
1893 * Ignore those if they are left over from previous responses.
1894 * Reportedly some versions of JBoss suffer from that problem.
1896 if (jk_b_get_int(msg) == 0) {
1897 jk_log(l, JK_LOG_DEBUG,
1898 "Ignoring flush message received while sending the request");
1901 /* We have just send a request but received something
1902 * that probably originates from buffered response.
1904 if (JK_IS_DEBUG_LEVEL(l)) {
1905 jk_log(l, JK_LOG_DEBUG,
1906 "Unexpected AJP13_SEND_BODY_CHUNK");
1909 return JK_AJP13_ERROR;
1911 if (!r->response_blocked) {
1912 unsigned int len = (unsigned int)jk_b_get_int(msg);
1914 * Do a sanity check on len to prevent write reading beyond buffer
1915 * boundaries and thus revealing possible sensitive memory
1916 * contents to the client.
1917 * len cannot be larger than msg->len - 3 because the ajp message
1918 * contains the magic byte for JK_AJP13_SEND_BODY_CHUNK (1 byte)
1919 * and the length of the chunk (2 bytes). The remaining part of
1920 * the message is the chunk.
1922 if (len > (unsigned int)(msg->len - 3)) {
1923 jk_log(l, JK_LOG_ERROR,
1924 "Chunk length too large. Length of AJP message is %i,"
1925 " chunk length is %i.", msg->len, len);
1927 return JK_INTERNAL_ERROR;
1930 /* AJP13_SEND_BODY_CHUNK with length 0 is
1931 * explicit flush packet message.
1933 if (r->response_started) {
1939 jk_log(l, JK_LOG_DEBUG,
1940 "Ignoring flush message received before headers");
1944 if (!r->write(r, msg->buf + msg->pos, len)) {
1945 jk_log(l, JK_LOG_INFO,
1946 "Writing to client aborted or client network problems");
1948 return JK_CLIENT_WR_ERROR;
1950 if (r->flush && r->flush_packets)
1956 case JK_AJP13_GET_BODY_CHUNK:
1958 int len = (int)jk_b_get_int(msg);
1963 if (len > AJP13_MAX_SEND_BODY_SZ) {
1964 len = AJP13_MAX_SEND_BODY_SZ;
1966 if ((jk_uint64_t)len > ae->left_bytes_to_send) {
1967 len = (int)ae->left_bytes_to_send;
1970 /* the right place to add file storage for upload */
1971 if ((len = ajp_read_into_msg_buff(ae, r, pmsg, len, l)) >= 0) {
1972 r->content_read += (jk_uint64_t)len;
1974 return JK_AJP13_HAS_RESPONSE;
1977 jk_log(l, JK_LOG_INFO,
1978 "Reading from client aborted or client network problems");
1981 return JK_CLIENT_RD_ERROR;
1985 case JK_AJP13_END_RESPONSE:
1986 ae->reuse = (int)jk_b_get_byte(msg);
1989 * AJP13 protocol reuse flag set to false.
1990 * Tomcat will close its side of the connection.
1992 jk_log(l, JK_LOG_WARNING, "AJP13 protocol: Reuse is set to false");
1994 else if (r->disable_reuse) {
1995 if (JK_IS_DEBUG_LEVEL(l)) {
1996 jk_log(l, JK_LOG_DEBUG, "AJP13 protocol: Reuse is disabled");
1998 ae->reuse = JK_FALSE;
2001 /* Reuse in all cases */
2002 if (JK_IS_DEBUG_LEVEL(l)) {
2003 jk_log(l, JK_LOG_DEBUG, "AJP13 protocol: Reuse is OK");
2005 ae->reuse = JK_TRUE;
2007 if (!r->response_blocked) {
2009 /* Done with response */
2012 else if (r->flush && !r->flush_packets) {
2013 /* Flush after the last write */
2018 return JK_AJP13_END_RESPONSE;
2022 jk_log(l, JK_LOG_ERROR,
2023 "Unknown AJP protocol code: %02X", code);
2025 return JK_AJP13_ERROR;
2029 return JK_AJP13_NO_RESPONSE;
2033 * get replies from Tomcat via Ajp13/Ajp14
2034 * ajp13/ajp14 is async but handling read/send this way prevent nice recovery
2035 * In fact if tomcat link is broken during upload (browser -> apache -> tomcat)
2036 * we'll loose data and we'll have to abort the whole request.
2038 * Return values of ajp_get_reply() function:
2039 * return value op->recoverable reason
2040 * JK_REPLY_TIMEOUT ?recovery_options Reply timeout while waiting for response packet
2041 * JK_FALSE ?recovery_options Error during ajp_connection_tcp_get_message()
2042 * Could not read the AJP packet header
2043 * JK_AJP_PROTOCOL_ERROR: ?recovery_options Error during ajp_connection_tcp_get_message()
2044 * Failure after reading the AJP packet header
2045 * JK_STATUS_ERROR mostly JK_TRUE ajp_process_callback() returns JK_STATUS_ERROR
2046 * Recoverable, if callback didn't return with a JK_HAS_RESPONSE before.
2047 * JK_HAS_RESPONSE: parts of the post buffer are consumed.
2048 * JK_STATUS_FATAL_ERROR mostly JK_TRUE ajp_process_callback() returns JK_STATUS_FATAL_ERROR
2049 * Recoverable, if callback didn't return with a JK_HAS_RESPONSE before.
2050 * JK_HAS_RESPONSE: parts of the post buffer are consumed.
2051 * JK_FATAL_ERROR ? ajp_process_callback() returns JK_AJP13_ERROR
2052 * JK_AJP13_ERROR: protocol error, or JK_INTERNAL_ERROR: chunk size to large
2053 * JK_CLIENT_RD_ERROR ? ajp_process_callback() returns JK_CLIENT_RD_ERROR
2054 * JK_CLIENT_RD_ERROR: could not read post from client.
2055 * JK_CLIENT_WR_ERROR ? ajp_process_callback() returns JK_CLIENT_WR_ERROR
2056 * JK_CLIENT_WR_ERROR: could not write back result to client
2057 * JK_TRUE ? ajp_process_callback() returns JK_AJP13_END_RESPONSE
2058 * JK_FALSE ? Other unhandled cases (unknown return codes)
2060 static int ajp_get_reply(jk_endpoint_t *e,
2063 ajp_endpoint_t * p, ajp_operation_t * op)
2065 /* Don't get header from tomcat yet */
2066 int headeratclient = JK_FALSE;
2071 /* Start read all reply message */
2074 /* Allow to overwrite reply_timeout on a per URL basis via service struct */
2075 int reply_timeout = s->extension.reply_timeout;
2076 if (reply_timeout < 0)
2077 reply_timeout = p->worker->reply_timeout;
2078 /* If we set a reply timeout, check if something is available */
2079 if (reply_timeout > 0) {
2080 if (jk_is_input_event(p->sd, reply_timeout, l) ==
2082 p->last_errno = errno;
2083 jk_log(l, JK_LOG_ERROR,
2084 "(%s) Timeout with waiting reply from tomcat. "
2085 "Tomcat is down, stopped or network problems (errno=%d)",
2086 p->worker->name, p->last_errno);
2087 /* We can't trust this connection any more. */
2088 ajp_abort_endpoint(p, JK_TRUE, l);
2089 if (headeratclient == JK_FALSE) {
2090 if (p->worker->recovery_opts & RECOVER_ABORT_IF_TCGETREQUEST)
2091 op->recoverable = JK_FALSE;
2093 * We revert back to recoverable, if recovery_opts allow it for GET or HEAD
2095 if (op->recoverable == JK_FALSE) {
2096 if (p->worker->recovery_opts & RECOVER_ALWAYS_HTTP_HEAD) {
2097 if (!strcmp(s->method, "HEAD"))
2098 op->recoverable = JK_TRUE;
2100 else if (p->worker->recovery_opts & RECOVER_ALWAYS_HTTP_GET) {
2101 if (!strcmp(s->method, "GET"))
2102 op->recoverable = JK_TRUE;
2107 if (p->worker->recovery_opts & RECOVER_ABORT_IF_TCSENDHEADER)
2108 op->recoverable = JK_FALSE;
2112 return JK_REPLY_TIMEOUT;
2116 if ((rc = ajp_connection_tcp_get_message(p, op->reply, l)) != JK_TRUE) {
2117 if (headeratclient == JK_FALSE) {
2118 jk_log(l, JK_LOG_ERROR,
2119 "(%s) Tomcat is down or refused connection. "
2120 "No response has been sent to the client (yet)",
2123 * communication with tomcat has been interrupted BEFORE
2124 * headers have been sent to the client.
2128 * We mark it unrecoverable if recovery_opts set to RECOVER_ABORT_IF_TCGETREQUEST
2130 if (p->worker->recovery_opts & RECOVER_ABORT_IF_TCGETREQUEST)
2131 op->recoverable = JK_FALSE;
2133 * We revert back to recoverable, if recovery_opts allow it for GET or HEAD
2135 if (op->recoverable == JK_FALSE) {
2136 if (p->worker->recovery_opts & RECOVER_ALWAYS_HTTP_HEAD) {
2137 if (!strcmp(s->method, "HEAD"))
2138 op->recoverable = JK_TRUE;
2140 else if (p->worker->recovery_opts & RECOVER_ALWAYS_HTTP_GET) {
2141 if (!strcmp(s->method, "GET"))
2142 op->recoverable = JK_TRUE;
2148 jk_log(l, JK_LOG_ERROR,
2149 "(%s) Tomcat is down or network problems. "
2150 "Part of the response has already been sent to the client",
2153 /* communication with tomcat has been interrupted AFTER
2154 * headers have been sent to the client.
2155 * headers (and maybe parts of the body) have already been
2156 * sent, therefore the response is "complete" in a sense
2157 * that nobody should append any data, especially no 500 error
2158 * page of the webserver!
2162 * We mark it unrecoverable if recovery_opts set to RECOVER_ABORT_IF_TCSENDHEADER
2164 if (p->worker->recovery_opts & RECOVER_ABORT_IF_TCSENDHEADER)
2165 op->recoverable = JK_FALSE;
2173 rc = ajp_process_callback(op->reply, op->post, p, s, l);
2175 /* no more data to be sent, fine we have finish here */
2176 if (JK_AJP13_END_RESPONSE == rc) {
2180 else if (JK_AJP13_SEND_HEADERS == rc) {
2181 if (headeratclient == JK_FALSE)
2182 headeratclient = JK_TRUE;
2184 /* Backend send headers twice?
2185 * This is protocol violation
2187 jk_log(l, JK_LOG_ERROR,
2188 "(%s) Tomcat already send headers",
2190 op->recoverable = JK_FALSE;
2195 else if (JK_STATUS_ERROR == rc || JK_STATUS_FATAL_ERROR == rc) {
2196 jk_log(l, JK_LOG_INFO,
2197 "(%s) request failed%s, "
2198 "because of response status %d, ",
2200 rc == JK_STATUS_FATAL_ERROR ? "" : " (soft)",
2201 s->http_response_status);
2205 else if (JK_AJP13_HAS_RESPONSE == rc) {
2207 * in upload-mode there is no second chance since
2208 * we may have already sent part of the uploaded data
2210 * In this case if Tomcat connection is broken we must
2211 * abort request and indicate error.
2212 * A possible work-around could be to store the uploaded
2213 * data to file and replay for it
2215 op->recoverable = JK_FALSE;
2216 rc = ajp_connection_tcp_send_message(p, op->post, l);
2218 jk_log(l, JK_LOG_ERROR,
2219 "(%s) Tomcat is down or network problems",
2225 else if (JK_AJP13_ERROR == rc) {
2227 * Tomcat has send invalid AJP message.
2228 * Loadbalancer if present will decide if
2229 * failover is possible.
2232 return JK_FATAL_ERROR;
2234 else if (JK_CLIENT_RD_ERROR == rc) {
2236 * Client has stop sending to us, so get out.
2237 * We assume this isn't our fault, so just a normal exit.
2240 return JK_CLIENT_RD_ERROR;
2242 else if (JK_CLIENT_WR_ERROR == rc) {
2244 * Client has stop receiving to us, so get out.
2245 * We assume this isn't our fault, so just a normal exit.
2248 return JK_CLIENT_WR_ERROR;
2250 else if (JK_INTERNAL_ERROR == rc) {
2252 * Internal error, like memory allocation or invalid packet lengths.
2255 return JK_FATAL_ERROR;
2257 else if (JK_AJP13_NO_RESPONSE == rc) {
2259 * This is fine, loop again, more data to send.
2264 op->recoverable = JK_FALSE;
2265 jk_log(l, JK_LOG_ERROR,
2266 "(%s) Callback returns with unknown value %d",
2267 p->worker->name, rc);
2272 /* XXX: Not reached? */
2277 static void ajp_update_stats(jk_endpoint_t *e, ajp_worker_t *aw, int rc, jk_logger_t *l)
2279 aw->s->readed += e->rd;
2280 aw->s->transferred += e->wr;
2283 if (rc == JK_TRUE) {
2284 aw->s->state = JK_AJP_STATE_OK;
2286 else if (rc == JK_CLIENT_ERROR) {
2287 aw->s->state = JK_AJP_STATE_OK;
2288 aw->s->client_errors++;
2291 aw->s->state = JK_AJP_STATE_ERROR;
2293 aw->s->error_time = time(NULL);
2298 * service is now splitted in ajp_send_request and ajp_get_reply
2299 * much more easier to do errors recovery
2301 * We serve here the request, using AJP13/AJP14 (e->proto)
2303 * Return values of service() method for ajp13/ajp14 worker:
2304 * return value is_error e->recoverable reason
2305 * JK_FALSE JK_HTTP_SERVER_ERROR TRUE Invalid Parameters (null values)
2306 * JK_SERVER_ERROR JK_HTTP_SERVER_ERROR TRUE Error during initializing empty request, response or post body objects
2307 * JK_CLIENT_ERROR JK_HTTP_REQUEST_TOO_LARGE JK_TRUE Request doesn't fit into buffer (error during ajp_marshal_into_msgb())
2308 * JK_CLIENT_ERROR JK_HTTP_BAD_REQUEST JK_FALSE Error during reading parts of POST body from client
2309 * JK_FATAL_ERROR JK_HTTP_SERVER_ERROR JK_FALSE If ajp_send_request() returns JK_FATAL_ERROR and !op->recoverable.
2310 * JK_FATAL_ERROR JK_HTTP_SERVER_ERROR JK_FALSE If ajp_send_request() returns JK_TRUE but !op->recoverable.
2311 * This should never happen.
2312 * JK_CLIENT_ERROR JK_HTTP_BAD_REQUEST ? ajp_get_reply() returns JK_CLIENT_RD_ERROR
2313 * JK_CLIENT_ERROR JK_HTTP_OK ? ajp_get_reply() returns JK_CLIENT_WR_ERROR
2314 * JK_FATAL_ERROR JK_HTTP_SERVER_ERROR JK_TRUE ajp_get_reply() returns JK_FATAL_ERROR
2315 * JK_FATAL_ERROR: protocol error or internal error
2316 * JK_FATAL_ERROR JK_HTTP_SERVER_ERROR JK_FALSE ajp_get_reply() returns JK_FATAL_ERROR
2317 * JK_FATAL_ERROR: protocol error or internal error
2318 * JK_STATUS_ERROR JK_HTTP_SERVER_BUSY JK_TRUE ajp_get_reply() returns JK_STATUS_ERROR
2319 * Only if op->recoverable and no more ajp13/ajp14 direct retries
2320 * JK_STATUS_ERROR JK_HTTP_SERVER_BUSY JK_FALSE ajp_get_reply() returns JK_STATUS_ERROR
2321 * Only if !op->recoverable
2322 * JK_STATUS_FATAL_ERROR JK_HTTP_SERVER_BUSY JK_TRUE ajp_get_reply() returns JK_STATUS_ERROR
2323 * Only if op->recoverable and no more ajp13/ajp14 direct retries
2324 * JK_STATUS_FATAL_ERROR JK_HTTP_SERVER_BUSY JK_FALSE ajp_get_reply() returns JK_STATUS_FATAL_ERROR
2325 * Only if !op->recoverable
2326 * JK_REPLY_TIMEOUT JK_HTTP_GATEWAY_TIME_OUT JK_TRUE ajp_get_reply() returns JK_REPLY_TIMEOUT
2327 * JK_AJP_PROTOCOL_ERROR JK_HTTP_GATEWAY_TIME_OUT ? ajp_get_reply() returns JK_AJP_PROTOCOL_ERROR
2328 * ??? JK_FATAL_ERROR JK_HTTP_GATEWAY_TIME_OUT JK_FALSE ajp_get_reply() returns something else
2329 * Only if !op->recoverable
2330 * ??? JK_FALSE JK_HTTP_SERVER_BUSY JK_TRUE ajp_get_reply() returns JK_FALSE
2331 * Only if op->recoverable and no more ajp13/ajp14 direct retries
2332 * JK_TRUE JK_HTTP_OK ? OK
2334 static int JK_METHOD ajp_service(jk_endpoint_t *e,
2336 jk_logger_t *l, int *is_error)
2340 ajp_operation_t oper;
2341 ajp_operation_t *op = &oper;
2351 if (!e || !e->endpoint_private || !s || !is_error) {
2352 JK_LOG_NULL_PARAMS(l);
2354 *is_error = JK_HTTP_SERVER_ERROR;
2359 p = e->endpoint_private;
2362 if (aw->sequence != aw->s->h.sequence)
2363 jk_ajp_pull(aw, JK_FALSE, l);
2367 /* Set returned error to SERVER ERROR */
2368 *is_error = JK_HTTP_SERVER_ERROR;
2370 op->request = jk_b_new(&(p->pool));
2372 jk_log(l, JK_LOG_ERROR,
2373 "Failed allocating AJP message");
2375 return JK_SERVER_ERROR;
2377 if (jk_b_set_buffer_size(op->request, aw->max_packet_size)) {
2378 jk_log(l, JK_LOG_ERROR,
2379 "Failed allocating AJP message buffer");
2381 return JK_SERVER_ERROR;
2383 jk_b_reset(op->request);
2385 op->reply = jk_b_new(&(p->pool));
2387 jk_log(l, JK_LOG_ERROR,
2388 "Failed allocating AJP message");
2390 return JK_SERVER_ERROR;
2392 if (jk_b_set_buffer_size(op->reply, aw->max_packet_size)) {
2393 jk_log(l, JK_LOG_ERROR,
2394 "Failed allocating AJP message buffer");
2396 return JK_SERVER_ERROR;
2399 op->post = jk_b_new(&(p->pool));
2401 jk_log(l, JK_LOG_ERROR,
2402 "Failed allocating AJP message");
2404 return JK_SERVER_ERROR;
2406 if (jk_b_set_buffer_size(op->post, aw->max_packet_size)) {
2407 jk_log(l, JK_LOG_ERROR,
2408 "Failed allocating AJP message buffer");
2410 return JK_SERVER_ERROR;
2412 jk_b_reset(op->post);
2414 /* Set returned error to OK */
2415 *is_error = JK_HTTP_OK;
2417 op->recoverable = JK_TRUE;
2418 op->uploadfd = -1; /* not yet used, later ;) */
2420 p->left_bytes_to_send = s->content_length;
2421 p->reuse = JK_FALSE;
2422 p->hard_close = JK_FALSE;
2424 s->secret = aw->secret;
2427 * We get here initial request (in op->request)
2429 if (!ajp_marshal_into_msgb(op->request, s, l, p)) {
2430 *is_error = JK_HTTP_REQUEST_TOO_LARGE;
2431 jk_log(l, JK_LOG_INFO,
2432 "Creating AJP message failed, "
2433 "without recovery");
2434 aw->s->client_errors++;
2436 return JK_CLIENT_ERROR;
2439 if (JK_IS_DEBUG_LEVEL(l)) {
2440 jk_log(l, JK_LOG_DEBUG, "processing %s with %d retries",
2441 aw->name, aw->retries);
2444 if (aw->s->state == JK_AJP_STATE_ERROR)
2445 aw->s->state = JK_AJP_STATE_PROBE;
2446 if (aw->s->busy > aw->s->max_busy)
2447 aw->s->max_busy = aw->s->busy;
2448 retry_interval = p->worker->retry_interval;
2449 for (i = 0; i < aw->retries; i++) {
2450 /* Reset reply message buffer for each retry */
2451 jk_b_reset(op->reply);
2454 * ajp_send_request() already locally handles
2455 * reconnecting and broken connection detection.
2456 * So if we already failed in it, wait a bit before
2457 * retrying the same backend.
2459 if (i > 0 && retry_interval >= 0) {
2460 if (JK_IS_DEBUG_LEVEL(l))
2461 jk_log(l, JK_LOG_DEBUG,
2462 "retry %d, sleeping for %d ms before retrying",
2464 jk_sleep(retry_interval);
2465 /* Pull shared memory if something changed during sleep */
2466 if (aw->sequence != aw->s->h.sequence)
2467 jk_ajp_pull(aw, JK_FALSE, l);
2470 * We're using op->request which hold initial request
2471 * if Tomcat is stopped or restarted, we will pass op->request
2472 * to next valid tomcat.
2474 log_error = JK_TRUE;
2477 err = ajp_send_request(e, s, l, p, op);
2478 e->recoverable = op->recoverable;
2479 if (err == JK_CLIENT_RD_ERROR) {
2480 *is_error = JK_HTTP_BAD_REQUEST;
2481 msg = "because of client read error";
2482 aw->s->client_errors++;
2483 rc = JK_CLIENT_ERROR;
2484 log_error = JK_FALSE;
2485 e->recoverable = JK_FALSE;
2486 /* Ajp message set reuse to TRUE in END_REQUEST message
2487 * However due to client bad request if the recovery
2488 * RECOVER_ABORT_IF_CLIENTERROR is set the physical connection
2489 * will be closed and application in Tomcat can catch that
2490 * generated exception, knowing the client aborted the
2491 * connection. This AJP protocol limitation, where we
2492 * should actually send some packet informing the backend
2493 * that client broke the connection in a middle of
2494 * request/response cycle.
2496 if (aw->recovery_opts & RECOVER_ABORT_IF_CLIENTERROR) {
2497 /* Mark the endpoint for shutdown */
2498 p->reuse = JK_FALSE;
2499 p->hard_close = JK_TRUE;
2502 else if (err == JK_FATAL_ERROR) {
2503 *is_error = JK_HTTP_SERVER_BUSY;
2504 msg = "because of error during request sending";
2506 if (!op->recoverable) {
2507 *is_error = JK_HTTP_SERVER_ERROR;
2508 msg = "because of protocol error during request sending";
2511 else if (err == JK_TRUE && op->recoverable) {
2512 /* Up to there we can recover */
2514 err = ajp_get_reply(e, s, l, p, op);
2515 e->recoverable = op->recoverable;
2516 if (err == JK_TRUE) {
2517 *is_error = JK_HTTP_OK;
2518 /* Done with the request */
2519 ajp_update_stats(e, aw, JK_TRUE, l);
2524 if (err == JK_CLIENT_RD_ERROR) {
2525 *is_error = JK_HTTP_BAD_REQUEST;
2526 msg = "because of client read error";
2527 aw->s->client_errors++;
2528 rc = JK_CLIENT_ERROR;
2529 log_error = JK_FALSE;
2530 e->recoverable = JK_FALSE;
2531 op->recoverable = JK_FALSE;
2532 if (aw->recovery_opts & RECOVER_ABORT_IF_CLIENTERROR) {
2533 /* Mark the endpoint for shutdown */
2534 p->reuse = JK_FALSE;
2535 p->hard_close = JK_TRUE;
2538 else if (err == JK_CLIENT_WR_ERROR) {
2539 /* XXX: Is this correct to log this as 200? */
2540 *is_error = JK_HTTP_OK;
2541 msg = "because of client write error";
2542 aw->s->client_errors++;
2543 rc = JK_CLIENT_ERROR;
2544 log_error = JK_FALSE;
2545 e->recoverable = JK_FALSE;
2546 op->recoverable = JK_FALSE;
2547 if (aw->recovery_opts & RECOVER_ABORT_IF_CLIENTERROR) {
2548 /* Mark the endpoint for shutdown */
2549 p->reuse = JK_FALSE;
2550 p->hard_close = JK_TRUE;
2553 else if (err == JK_FATAL_ERROR) {
2554 *is_error = JK_HTTP_SERVER_ERROR;
2555 msg = "because of server error";
2558 else if (err == JK_REPLY_TIMEOUT) {
2559 *is_error = JK_HTTP_GATEWAY_TIME_OUT;
2560 msg = "because of reply timeout";
2561 aw->s->reply_timeouts++;
2564 else if (err == JK_STATUS_ERROR || err == JK_STATUS_FATAL_ERROR) {
2565 *is_error = JK_HTTP_SERVER_BUSY;
2566 msg = "because of response status";
2569 else if (err == JK_AJP_PROTOCOL_ERROR) {
2570 *is_error = JK_HTTP_BAD_GATEWAY;
2571 msg = "because of protocol error";
2574 /* This should only be the cases err == JK_FALSE */
2576 /* if we can't get reply, check if unrecoverable flag was set
2577 * if is_recoverable_error is cleared, we have started
2578 * receiving upload data and we must consider that
2579 * operation is no more recoverable
2581 *is_error = JK_HTTP_BAD_GATEWAY;
2587 /* XXX: this should never happen:
2588 * ajp_send_request() never returns JK_TRUE if !op->recoverable.
2589 * and all other return values have already been handled.
2591 e->recoverable = JK_FALSE;
2592 *is_error = JK_HTTP_SERVER_ERROR;
2593 msg = "because of an unknown reason";
2594 rc = JK_FATAL_ERROR;
2595 jk_log(l, JK_LOG_ERROR,
2596 "(%s) unexpected condition err=%d recoverable=%d",
2597 aw->name, err, op->recoverable);
2599 if (!op->recoverable && log_error == JK_TRUE) {
2600 jk_log(l, JK_LOG_ERROR,
2601 "(%s) sending request to tomcat failed (unrecoverable), "
2604 aw->name, msg, i + 1);
2607 jk_log(l, JK_LOG_INFO,
2608 "(%s) sending request to tomcat failed (%srecoverable), "
2612 op->recoverable ? "" : "un",
2615 if (!op->recoverable) {
2616 ajp_update_stats(e, aw, rc, l);
2620 /* Get another connection from the pool and try again.
2621 * Note: All sockets are probably closed already.
2623 ajp_next_connection(p, l);
2625 /* Log the error only once per failed request. */
2626 jk_log(l, JK_LOG_ERROR,
2627 "(%s) connecting to tomcat failed.",
2630 ajp_update_stats(e, aw, rc, l);
2636 * Validate the worker (ajp13/ajp14)
2639 int ajp_validate(jk_worker_t *pThis,
2641 jk_worker_env_t *we, jk_logger_t *l, int proto)
2648 if (proto == AJP13_PROTO) {
2649 port = AJP13_DEF_PORT;
2650 host = AJP13_DEF_HOST;
2652 else if (proto == AJP14_PROTO) {
2653 port = AJP14_DEF_PORT;
2654 host = AJP14_DEF_HOST;
2657 jk_log(l, JK_LOG_ERROR,
2658 "unknown protocol %d", proto);
2663 if (pThis && pThis->worker_private) {
2664 ajp_worker_t *p = pThis->worker_private;
2665 p->port = jk_get_worker_port(props, p->name, port);
2669 strncpy(p->host, jk_get_worker_host(props, p->name, host), JK_SHM_STR_SIZ);
2671 if (JK_IS_DEBUG_LEVEL(l))
2672 jk_log(l, JK_LOG_DEBUG,
2673 "worker %s contact is '%s:%d'",
2674 p->name, p->host, p->port);
2675 /* Copy the contact to shm */
2676 strncpy(p->s->host, p->host, JK_SHM_STR_SIZ);
2677 p->s->port = p->port;
2678 p->s->addr_sequence = p->addr_sequence = 0;
2679 /* Resolve if port > 0.
2682 if (jk_resolve(p->host, p->port, &p->worker_inet_addr, we->pool, l)) {
2686 jk_log(l, JK_LOG_ERROR,
2687 "worker %s can't resolve tomcat address %s",
2689 p->s->port = p->port = 0;
2690 if (JK_IS_DEBUG_LEVEL(l))
2691 jk_log(l, JK_LOG_DEBUG,
2692 "worker %s contact is disabled",
2698 p->s->port = p->port = 0;
2699 if (JK_IS_DEBUG_LEVEL(l))
2700 jk_log(l, JK_LOG_DEBUG,
2701 "worker %s contact is disabled",
2708 JK_LOG_NULL_PARAMS(l);
2715 static int ajp_create_endpoint_cache(ajp_worker_t *p, int proto, jk_logger_t *l)
2718 time_t now = time(NULL);
2722 p->ep_cache = (ajp_endpoint_t **)calloc(1, sizeof(ajp_endpoint_t *) *
2728 if (JK_IS_DEBUG_LEVEL(l))
2729 jk_log(l, JK_LOG_DEBUG,
2730 "setting connection pool size to %u with min %u and acquire timeout %d",
2731 p->ep_cache_sz, p->ep_mincache_sz, p->cache_acquire_timeout);
2732 for (i = 0; i < p->ep_cache_sz; i++) {
2733 p->ep_cache[i] = (ajp_endpoint_t *)calloc(1, sizeof(ajp_endpoint_t));
2734 if (!p->ep_cache[i]) {
2735 jk_log(l, JK_LOG_ERROR,
2736 "allocating endpoint slot %d (errno=%d)",
2741 p->ep_cache[i]->sd = JK_INVALID_SOCKET;
2742 p->ep_cache[i]->reuse = JK_FALSE;
2743 p->ep_cache[i]->hard_close = JK_FALSE;
2744 p->ep_cache[i]->last_access = now;
2745 jk_open_pool(&(p->ep_cache[i]->pool), p->ep_cache[i]->buf,
2746 sizeof(p->ep_cache[i]->buf));
2747 p->ep_cache[i]->worker = p;
2748 p->ep_cache[i]->endpoint.endpoint_private = p->ep_cache[i];
2749 p->ep_cache[i]->proto = proto;
2750 p->ep_cache[i]->endpoint.service = ajp_service;
2751 p->ep_cache[i]->endpoint.done = ajp_done;
2752 p->ep_cache[i]->last_op = JK_AJP13_END_RESPONSE;
2753 p->ep_cache[i]->addr_sequence = 0;
2760 int ajp_init(jk_worker_t *pThis,
2761 jk_map_t *props, jk_worker_env_t *we, jk_logger_t *l, int proto)
2766 * start the connection cache
2770 cache = jk_get_worker_def_cache_size(proto);
2772 if (pThis && pThis->worker_private) {
2773 ajp_worker_t *p = pThis->worker_private;
2775 p->ep_cache_sz = jk_get_worker_cache_size(props, p->name, cache);
2776 p->ep_mincache_sz = jk_get_worker_cache_size_min(props, p->name,
2777 (p->ep_cache_sz+1) / 2);
2779 jk_get_worker_socket_timeout(props, p->name, AJP_DEF_SOCKET_TIMEOUT);
2781 p->socket_connect_timeout =
2782 jk_get_worker_socket_connect_timeout(props, p->name,
2783 p->socket_timeout * 1000);
2786 jk_get_worker_socket_keepalive(props, p->name, JK_FALSE);
2789 jk_get_worker_cache_timeout(props, p->name,
2790 AJP_DEF_CACHE_TIMEOUT);
2793 jk_get_worker_ping_timeout(props, p->name,
2794 AJP_DEF_PING_TIMEOUT);
2796 jk_get_worker_ping_mode(props, p->name,
2799 p->connect_timeout =
2800 jk_get_worker_connect_timeout(props, p->name,
2801 AJP_DEF_CONNECT_TIMEOUT);
2803 p->prepost_timeout =
2804 jk_get_worker_prepost_timeout(props, p->name,
2805 AJP_DEF_PREPOST_TIMEOUT);
2807 if ((p->ping_mode & AJP_CPING_CONNECT) &&
2808 p->connect_timeout == AJP_DEF_CONNECT_TIMEOUT)
2809 p->connect_timeout = p->ping_timeout;
2811 if ((p->ping_mode & AJP_CPING_PREPOST) &&
2812 p->prepost_timeout == AJP_DEF_PREPOST_TIMEOUT)
2813 p->prepost_timeout = p->ping_timeout;
2815 p->conn_ping_interval =
2816 jk_get_worker_conn_ping_interval(props, p->name, 0);
2817 if ((p->ping_mode & AJP_CPING_INTERVAL) &&
2818 p->conn_ping_interval == 0) {
2819 /* XXX: Ping timeout is in miliseconds
2820 * and ping_interval is in seconds.
2821 * Use 10 times larger value for ping interval
2822 * (ping_timeout / 1000) * 10
2824 p->conn_ping_interval = p->ping_timeout / 100;
2827 jk_get_worker_reply_timeout(props, p->name,
2828 AJP_DEF_REPLY_TIMEOUT);
2831 jk_get_worker_recovery_opts(props, p->name,
2832 AJP_DEF_RECOVERY_OPTS);
2835 jk_get_worker_retries(props, p->name,
2838 p->max_packet_size =
2839 jk_get_max_packet_size(props, p->name);
2842 jk_get_worker_socket_buffer(props, p->name, p->max_packet_size);
2845 jk_get_worker_retry_interval(props, p->name,
2847 p->cache_acquire_timeout = jk_get_worker_cache_acquire_timeout(props,
2848 p->name, p->retries * p->retry_interval);
2849 p->http_status_fail_num = jk_get_worker_fail_on_status(props, p->name,
2850 &p->http_status_fail[0],
2851 JK_MAX_HTTP_STATUS_FAILS);
2853 if (p->retries < 1) {
2854 jk_log(l, JK_LOG_INFO,
2855 "number of retries must be greater then 1. Setting to default=%d",
2857 p->retries = JK_RETRIES;
2860 p->maintain_time = jk_get_worker_maintain_time(props);
2861 if(p->maintain_time < 0)
2862 p->maintain_time = 0;
2863 p->s->last_maintain_time = time(NULL);
2864 p->s->last_reset = p->s->last_maintain_time;
2866 if (JK_IS_DEBUG_LEVEL(l)) {
2868 jk_log(l, JK_LOG_DEBUG,
2869 "setting endpoint options:",
2871 jk_log(l, JK_LOG_DEBUG,
2875 jk_log(l, JK_LOG_DEBUG,
2876 "socket timeout: %d",
2879 jk_log(l, JK_LOG_DEBUG,
2880 "socket connect timeout: %d",
2881 p->socket_connect_timeout);
2883 jk_log(l, JK_LOG_DEBUG,
2887 jk_log(l, JK_LOG_DEBUG,
2891 jk_log(l, JK_LOG_DEBUG,
2895 jk_log(l, JK_LOG_DEBUG,
2896 "connect timeout: %d",
2897 p->connect_timeout);
2899 jk_log(l, JK_LOG_DEBUG,
2900 "reply timeout: %d",
2903 jk_log(l, JK_LOG_DEBUG,
2904 "prepost timeout: %d",
2905 p->prepost_timeout);
2907 jk_log(l, JK_LOG_DEBUG,
2908 "recovery options: %d",
2911 jk_log(l, JK_LOG_DEBUG,
2915 jk_log(l, JK_LOG_DEBUG,
2916 "max packet size: %d",
2917 p->max_packet_size);
2919 jk_log(l, JK_LOG_DEBUG,
2920 "retry interval: %d",
2924 * Need to initialize secret here since we could return from inside
2925 * of the following loop
2928 p->secret = jk_get_worker_secret(props, p->name);
2929 /* Initialize cache slots */
2930 JK_INIT_CS(&(p->cs), rc);
2932 jk_log(l, JK_LOG_ERROR,
2933 "creating thread lock (errno=%d)",
2938 if (!ajp_create_endpoint_cache(p, proto, l)) {
2939 jk_log(l, JK_LOG_ERROR,
2940 "allocating connection pool of size %u",
2948 JK_LOG_NULL_PARAMS(l);
2955 int JK_METHOD ajp_worker_factory(jk_worker_t **w,
2956 const char *name, jk_logger_t *l)
2961 if (name == NULL || w == NULL) {
2962 JK_LOG_NULL_PARAMS(l);
2967 aw = (ajp_worker_t *) calloc(1, sizeof(ajp_worker_t));
2969 jk_log(l, JK_LOG_ERROR,
2970 "malloc of private_data failed");
2975 jk_open_pool(&aw->p,
2977 sizeof(jk_pool_atom_t) * TINY_POOL_SIZE);
2979 strncpy(aw->name, name, JK_SHM_STR_SIZ);
2982 aw->ep_cache_sz = 0;
2983 aw->ep_cache = NULL;
2984 aw->connect_retry_attempts = AJP_DEF_RETRY_ATTEMPTS;
2985 aw->worker.worker_private = aw;
2987 aw->worker.maintain = ajp_maintain;
2993 aw->s = jk_shm_alloc_ajp_worker(&aw->p);
2995 jk_close_pool(&aw->p);
2997 jk_log(l, JK_LOG_ERROR,
2998 "allocating ajp worker record from shared memory");
3006 int ajp_destroy(jk_worker_t **pThis, jk_logger_t *l, int proto)
3010 if (pThis && *pThis && (*pThis)->worker_private) {
3012 ajp_worker_t *aw = (*pThis)->worker_private;
3014 if (JK_IS_DEBUG_LEVEL(l))
3015 jk_log(l, JK_LOG_DEBUG,
3016 "up to %u endpoints to close",
3019 for (i = 0; i < aw->ep_cache_sz; i++) {
3020 if (aw->ep_cache[i])
3021 ajp_close_endpoint(aw->ep_cache[i], l);
3024 JK_DELETE_CS(&(aw->cs), i);
3027 /* take care of removing previously allocated data */
3028 if (aw->login->servlet_engine_name)
3029 free(aw->login->servlet_engine_name);
3035 jk_close_pool(&aw->p);
3041 JK_LOG_NULL_PARAMS(l);
3046 int JK_METHOD ajp_done(jk_endpoint_t **e, jk_logger_t *l)
3050 if (e && *e && (*e)->endpoint_private) {
3051 ajp_endpoint_t *p = (*e)->endpoint_private;
3053 ajp_worker_t *w = p->worker;
3055 /* set last_access only if needed */
3056 if (w->cache_timeout > 0)
3057 p->last_access = time(NULL);
3058 if (w->s->addr_sequence != p->addr_sequence) {
3059 p->reuse = JK_FALSE;
3060 p->addr_sequence = w->s->addr_sequence;
3062 ajp_reset_endpoint(p, l);
3064 JK_ENTER_CS(&w->cs, rc);
3068 for (i = w->ep_cache_sz - 1; i >= 0; i--) {
3069 if (w->ep_cache[i] == NULL) {
3074 JK_LEAVE_CS(&w->cs, rc);
3077 if (JK_IS_DEBUG_LEVEL(l))
3078 jk_log(l, JK_LOG_DEBUG,
3079 "recycling connection pool slot=%u for worker %s",
3080 i, p->worker->name);
3084 /* This should never hapen because
3085 * there is always free empty cache slot
3087 jk_log(l, JK_LOG_ERROR,
3088 "could not find empty connection pool slot from %u for worker %s",
3089 w->ep_cache_sz, w->name);
3094 jk_log(l, JK_LOG_ERROR,
3095 "locking thread (errno=%d)", errno);
3100 JK_LOG_NULL_PARAMS(l);
3105 int ajp_get_endpoint(jk_worker_t *pThis,
3106 jk_endpoint_t **je, jk_logger_t *l, int proto)
3110 if (pThis && pThis->worker_private && je) {
3111 ajp_worker_t *aw = pThis->worker_private;
3112 ajp_endpoint_t *ae = NULL;
3117 /* Loop until cache_acquire_timeout interval elapses */
3118 while ((retry * JK_SLEEP_DEF) < aw->cache_acquire_timeout) {
3120 JK_ENTER_CS(&aw->cs, rc);
3123 /* Try to find connected socket cache entry */
3124 for (slot = 0; slot < aw->ep_cache_sz; slot++) {
3125 if (aw->ep_cache[slot] &&
3126 IS_VALID_SOCKET(aw->ep_cache[slot]->sd)) {
3127 ae = aw->ep_cache[slot];
3129 aw->ep_cache[slot] = NULL;
3133 /* XXX: We shouldn't have non reusable
3134 * opened socket in the cache
3136 ajp_reset_endpoint(ae, l);
3138 jk_log(l, JK_LOG_WARNING,
3139 "closing non reusable pool slot=%d", slot);
3144 /* No connected cache entry found.
3145 * Use the first free one.
3147 for (slot = 0; slot < aw->ep_cache_sz; slot++) {
3148 if (aw->ep_cache[slot]) {
3149 ae = aw->ep_cache[slot];
3150 aw->ep_cache[slot] = NULL;
3155 JK_LEAVE_CS(&aw->cs, rc);
3157 if (aw->cache_timeout > 0)
3158 ae->last_access = time(NULL);
3159 *je = &ae->endpoint;
3160 if (JK_IS_DEBUG_LEVEL(l))
3161 jk_log(l, JK_LOG_DEBUG,
3162 "acquired connection pool slot=%u after %d retries",
3169 if (JK_IS_DEBUG_LEVEL(l))
3170 jk_log(l, JK_LOG_DEBUG,
3171 "could not get free endpoint for worker %s"
3172 " (retry %d, sleeping for %d ms)",
3173 aw->name, retry, JK_SLEEP_DEF);
3174 jk_sleep(JK_SLEEP_DEF);
3178 jk_log(l, JK_LOG_ERROR,
3179 "locking thread (errno=%d)", errno);
3185 jk_log(l, JK_LOG_WARNING,
3186 "Unable to get the free endpoint for worker %s from %u slots",
3187 aw->name, aw->ep_cache_sz);
3190 JK_LOG_NULL_PARAMS(l);
3197 int JK_METHOD ajp_maintain(jk_worker_t *pThis, time_t mstarted, jk_logger_t *l)
3201 if (pThis && pThis->worker_private) {
3202 ajp_worker_t *aw = pThis->worker_private;
3203 time_t now = mstarted;
3209 /* Now we check for global maintenance (once for all processes).
3210 * Checking workers for idleness.
3211 * Therefore we globally sync and we use a global timestamp.
3212 * Since it's possible that we come here a few milliseconds
3213 * before the interval has passed, we allow a little tolerance.
3215 delta = (long)difftime(mstarted, aw->s->last_maintain_time) + JK_AJP_MAINTAIN_TOLERANCE;
3216 if (delta >= aw->maintain_time) {
3217 aw->s->last_maintain_time = mstarted;
3218 if (aw->s->state == JK_AJP_STATE_OK &&
3219 aw->s->used == aw->s->used_snapshot)
3220 aw->s->state = JK_AJP_STATE_IDLE;
3221 aw->s->used_snapshot = aw->s->used;
3226 /* Do connection pool maintenance only if timeouts or keepalives are set */
3227 if (aw->cache_timeout <= 0 &&
3228 aw->conn_ping_interval <= 0) {
3229 /* Nothing to do. */
3234 JK_ENTER_CS(&aw->cs, rc);
3236 unsigned int n = 0, k = 0, cnt = 0;
3238 unsigned int m, m_count = 0;
3240 /* Count open slots */
3241 for (i = (int)aw->ep_cache_sz - 1; i >= 0; i--) {
3242 if (aw->ep_cache[i] && IS_VALID_SOCKET(aw->ep_cache[i]->sd))
3245 m_sock = (jk_sock_t *)malloc((cnt + 1) * sizeof(jk_sock_t));
3246 /* Handle worker cache timeouts */
3247 if (aw->cache_timeout > 0) {
3248 for (i = (int)aw->ep_cache_sz - 1;
3250 /* Skip the closed sockets */
3251 if (aw->ep_cache[i] && IS_VALID_SOCKET(aw->ep_cache[i]->sd)) {
3252 int elapsed = (int)difftime(mstarted, aw->ep_cache[i]->last_access);
3253 if (elapsed > aw->cache_timeout) {
3256 if (JK_IS_DEBUG_LEVEL(l))
3258 aw->ep_cache[i]->reuse = JK_FALSE;
3259 m_sock[m_count++] = aw->ep_cache[i]->sd;
3260 aw->ep_cache[i]->sd = JK_INVALID_SOCKET;
3261 ajp_reset_endpoint(aw->ep_cache[i], l);
3262 if (JK_IS_DEBUG_LEVEL(l))
3263 jk_log(l, JK_LOG_DEBUG,
3264 "cleaning pool slot=%d elapsed %d in %d",
3265 i, elapsed, (int)(difftime(time(NULL), rt)));
3268 if (cnt <= aw->ep_mincache_sz + n) {
3269 if (JK_IS_DEBUG_LEVEL(l)) {
3270 jk_log(l, JK_LOG_DEBUG,
3271 "reached pool min size %u from %u cache slots",
3272 aw->ep_mincache_sz, aw->ep_cache_sz);
3278 /* Handle worker connection keepalive */
3279 if (aw->conn_ping_interval > 0 && aw->ping_timeout > 0) {
3280 for (i = (int)aw->ep_cache_sz - 1; i >= 0; i--) {
3281 /* Skip the closed sockets */
3282 if (aw->ep_cache[i] && IS_VALID_SOCKET(aw->ep_cache[i]->sd)) {
3283 int elapsed = (int)difftime(now, aw->ep_cache[i]->last_access);
3284 if (elapsed > aw->conn_ping_interval) {
3286 /* handle cping/cpong.
3288 if (ajp_handle_cping_cpong(aw->ep_cache[i],
3289 aw->ping_timeout, l) == JK_FALSE) {
3290 jk_log(l, JK_LOG_INFO,
3291 "(%s) failed sending request, "
3292 "socket %d keepalive cping/cpong "
3293 "failure (errno=%d)",
3295 aw->ep_cache[i]->sd,
3296 aw->ep_cache[i]->last_errno);
3297 aw->ep_cache[i]->reuse = JK_FALSE;
3298 m_sock[m_count++] = aw->ep_cache[i]->sd;
3299 aw->ep_cache[i]->sd = JK_INVALID_SOCKET;
3300 ajp_reset_endpoint(aw->ep_cache[i], l);
3304 aw->ep_cache[i]->last_access = now;
3310 JK_LEAVE_CS(&aw->cs, rc);
3311 /* Shutdown sockets outside of the lock.
3312 * This has benefits only if maintain was
3313 * called from the watchdog thread.
3315 for (m = 0; m < m_count; m++) {
3316 jk_shutdown_socket(m_sock[m], l);
3319 if (n && JK_IS_DEBUG_LEVEL(l))
3320 jk_log(l, JK_LOG_DEBUG,
3321 "recycled %u sockets in %d seconds from %u pool slots",
3322 n, (int)(difftime(time(NULL), mstarted)),
3324 if (k && JK_IS_DEBUG_LEVEL(l))
3325 jk_log(l, JK_LOG_DEBUG,
3326 "pinged %u sockets in %d seconds from %u pool slots",
3327 k, (int)(difftime(time(NULL), mstarted)),
3333 jk_log(l, JK_LOG_ERROR,
3334 "locking thread (errno=%d)",
3342 JK_LOG_NULL_PARAMS(l);
3349 int ajp_has_endpoint(jk_worker_t *pThis,
3354 if (pThis && pThis->worker_private) {
3355 ajp_worker_t *aw = pThis->worker_private;
3358 JK_ENTER_CS(&aw->cs, rc);
3361 /* Try to find connected socket cache entry */
3362 for (slot = 0; slot < aw->ep_cache_sz; slot++) {
3363 if (aw->ep_cache[slot]) {
3364 JK_LEAVE_CS(&aw->cs, rc);
3368 JK_LEAVE_CS(&aw->cs, rc);
3371 jk_log(l, JK_LOG_ERROR,
3372 "locking thread (errno=%d)", errno);
3378 JK_LOG_NULL_PARAMS(l);