hgomez      01/03/27 14:42:04

  Modified:    src/native/mod_jk/common jk_ajp13_worker.c
  Log:
  The long awaited :) correction to tomcat restart problem.
  No need to restart apache now
  Will need a little adjustement, ie instead of 3
  fixed loop try to connect 2 * times the numbers of
  workers matching the requested URL (Dan ?)
  
  Revision  Changes    Path
  1.6       +224 -111  jakarta-tomcat/src/native/mod_jk/common/jk_ajp13_worker.c
  
  Index: jk_ajp13_worker.c
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/native/mod_jk/common/jk_ajp13_worker.c,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- jk_ajp13_worker.c 2001/03/06 20:22:22     1.5
  +++ jk_ajp13_worker.c 2001/03/27 22:42:04     1.6
  @@ -54,10 +54,11 @@
    */
   
   /***************************************************************************
  - * Description: Bi-directional protocol.                       *
  + * Description: Bi-directional protocol.                                   *
  + * Author:      Henri Gomez <[EMAIL PROTECTED]>                               *
    * Author:      Costin <[EMAIL PROTECTED]>                              *
    * Author:      Gal Shachor <[EMAIL PROTECTED]>                           *
  - * Version:     $Revision: 1.5 $                                           *
  + * Version:     $Revision: 1.6 $                                           *
    ***************************************************************************/
   
   #include "jk_pool.h"
  @@ -75,6 +76,9 @@
   #define JK_INTERNAL_ERROR       (-2)
   #define MAX_SEND_BODY_SZ        (DEF_BUFFER_SZ - 6)
   
  +struct ajp13_operation;
  +typedef struct ajp13_operation ajp13_operation_t;
  +
   struct ajp13_endpoint;
   typedef struct ajp13_endpoint ajp13_endpoint_t;
   
  @@ -111,6 +115,18 @@
       unsigned left_bytes_to_send;
   };
   
  +/* 
  + * little struct to avoid multiples ptr passing
  + * this struct is ready to hold upload file fd
  + * to add upload persistant storage
  + */
  +struct ajp13_operation {
  +     jk_msg_buf_t    *request;       /* original request storage */
  +     jk_msg_buf_t    *reply;         /* reply storage (chuncked by ajp13 */
  +     int             uploadfd;       /* future persistant storage id */
  +     int             recoverable;    /* if exchange could be conducted on another 
TC */
  +};
  +
   static void reset_endpoint(ajp13_endpoint_t *ep)
   {
       ep->reuse = JK_FALSE; 
  @@ -343,6 +359,7 @@
                    len = 0;
                }
   
  +             /* the right place to add file storage for upload */
                if(read_into_msg_buff(ep, r, msg, l, len)) {
                    r->content_read += len;
                    return JK_AJP13_HAS_RESPONSE;
  @@ -529,123 +546,219 @@
              "In jk_endpoint_t::done, NULL parameters\n");
       return JK_FALSE;
   }
  -
  -static int JK_METHOD service(jk_endpoint_t *e, 
  -                             jk_ws_service_t *s,
  -                             jk_logger_t *l,
  -                             int *is_recoverable_error)
  -{
  -    jk_log(l, 
  -           JK_LOG_DEBUG, 
  -           "Into jk_endpoint_t::service\n");
   
  -    if(e && e->endpoint_private && s && is_recoverable_error) {
  -        ajp13_endpoint_t *p = e->endpoint_private;
  -        jk_msg_buf_t *msg = jk_b_new(&(p->pool));
  +/*
  + * send request to Tomcat via Ajp13
  + * - first try to find reuseable socket
  + * - if no one available, try to connect
  + * - send request, but send must be see as asynchronous,
  + *   since send() call will return noerror about 95% of time
  + *   Hopefully we'll get more information on next read.
  + * 
  + * nb: reqmsg is the original request msg buffer
  + *     repmsg is the reply msg buffer which could be scratched
  + */
  +static int send_request(jk_endpoint_t *e,
  +                                             jk_ws_service_t *s,
  +                                             jk_logger_t *l,
  +                                             ajp13_endpoint_t *p,
  +                                             ajp13_operation_t *op)
  +{
  +     /*
  +      * First try to reuse open connections...
  +     */
  +     while((p->sd > 0) && !connection_tcp_send_message(p, op->request, l)) {
  +             jk_log(l, JK_LOG_ERROR, "Error sending request try another pooled 
connection\n");
  +             jk_close_socket(p->sd);
  +             p->sd = -1;
  +             reuse_connection(p, l);
  +     }
  +
  +     /*
  +      * If we failed to reuse a connection, try to reconnect.
  +      */
  +     if(p->sd < 0) {
  +             connect_to_tomcat(p, l);
  +             if(p->sd >= 0) {
  +             /*
  +              * After we are connected, each error that we are going to
  +              * have is probably unrecoverable
  +              */
  +             op->recoverable = JK_FALSE;
  +             if(!connection_tcp_send_message(p, op->request, l)) {
  +                     jk_log(l, JK_LOG_ERROR, "Error sending request on a fresh 
connection\n");
  +                     return JK_FALSE;
  +             }
  +             } else {
  +                     jk_log(l, JK_LOG_ERROR, "Error connecting to the Tomcat 
process.\n");
  +                     return JK_FALSE;
  +             }
  +     }
   
  -        jk_b_set_buffer_size( msg, DEF_BUFFER_SZ); 
  -         jk_b_reset(msg);
  -        
  -        p->left_bytes_to_send = s->content_length;
  -        p->reuse = JK_FALSE;
  -        *is_recoverable_error = JK_TRUE;
  -
  -        if(!ajp13_marshal_into_msgb(msg, s, l)) {
  -            *is_recoverable_error = JK_FALSE;                
  -            return JK_FALSE;
  -        }
  +     /*
  +      * From now on an error means that we have an internal server error
  +      * or Tomcat crashed. In any case we cannot recover this.
  +      */
  +     op->recoverable = JK_FALSE;
  +
  +     if(p->left_bytes_to_send > 0) {
  +             unsigned len = p->left_bytes_to_send;
  +             if(len > MAX_SEND_BODY_SZ) 
  +                     len = MAX_SEND_BODY_SZ;
  +             if(!read_into_msg_buff(p, s, op->reply, l, len)) 
  +                     return JK_FALSE;
  +             s->content_read = len;
  +             if(!connection_tcp_send_message(p, op->reply, l)) {
  +                     jk_log(l, JK_LOG_ERROR, "Error sending request body\n");
  +                     return JK_FALSE;
  +             }  
  +     }
  +
  +     return (JK_TRUE);
  +}
  +
  +/*
  + * get replies from Tomcat via Ajp13
  + * We will know only at read time if the remote host closed
  + * the connection (half-closed state - FIN-WAIT2). In that case
  + * we must close our side of the socket and abort emission.
  + * We will need another connection to send the request
  + * There is need of refactoring here since we mix 
  + * reply reception (tomcat -> apache) and request send (apache -> tomcat)
  + * and everything using the same buffer (repmsg)
  + * ajp13 is async but handling read/send this way prevent nice recovery
  + * In fact if tomcat link is broken during upload (browser -> apache -> tomcat)
  + * we'll loose data and we'll have to abort the whole request.
  + */
  +static int get_reply(jk_endpoint_t *e,
  +                        jk_ws_service_t *s,
  +                        jk_logger_t *l,
  +                     ajp13_endpoint_t *p,
  +                     ajp13_operation_t *op)
  +{
  +     /* Start read all reply message */
  +     while(1) {
  +             int rc = 0;
  +
  +             if(!connection_tcp_get_message(p, op->reply, l)) {
  +                     jk_log(l, JK_LOG_ERROR, "Error reading reply\n");
  +                     /* we just can't recover, unset recover flag */
  +                     return JK_FALSE;
  +             }
   
  -        /*
  -         * First try to reuse open connections...
  -         */
  -        while((p->sd > 0) && !connection_tcp_send_message(p, msg, l)) {
  -         jk_log(l, JK_LOG_ERROR,
  -                            "Error sending request try another pooled 
connection\n");
  -                 jk_close_socket(p->sd);
  -            p->sd = -1;
  -            reuse_connection(p, l);
  -        } 
  -
  -        /*
  -         * If we failed to reuse a connection, try to reconnect.
  -         */
  -        if(p->sd < 0) {
  -            connect_to_tomcat(p, l);
  -            if(p->sd >= 0) {
  -                /*
  -                 * After we are connected, each error that we are going to
  -                 * have is probably unrecoverable
  -                 */            
  -                *is_recoverable_error = JK_FALSE;
  -                if(!connection_tcp_send_message(p, msg, l)) {
  -                 jk_log(l, JK_LOG_ERROR,
  -                                    "Error sending request on a fresh 
connection\n");
  -                         return JK_FALSE;
  -                } 
  -            } else {
  -                jk_log(l, 
  -                       JK_LOG_ERROR,
  -                                "Error connecting to the Tomcat process.\n");
  -                     return JK_FALSE;
  -            }
  -        } 
  +             rc = ajp13_process_callback(op->reply, p, s, l);
   
  -        /* 
  -         * From now on an error means that we have an internal server error 
  -         * or Tomcat crashed. In any case we cannot recover this.
  -         */
  -        *is_recoverable_error = JK_FALSE;
  -        
  +             /* no more data to be sent, fine we have finish here */
  +                     if(JK_AJP13_END_RESPONSE == rc)
  +                     return JK_TRUE;
  +             else if(JK_AJP13_HAS_RESPONSE == rc) {
  +             /* 
  +              * in upload-mode there is no second chance since
  +              * we may have allready send part of uploaded data 
  +              * to Tomcat.
  +              * In this case if Tomcat connection is broken we must 
  +              * abort request and indicate error.
  +              * A possible work-around could be to store the uploaded
  +              * data to file and replay for it
  +              */
  +                     op->recoverable = JK_FALSE; 
  +                     rc = connection_tcp_send_message(p, op->reply, l);
  +                     if (rc < 0) {
  +                             jk_log(l, JK_LOG_ERROR, "Error sending request data 
%d\n", rc);
  +                                     return JK_FALSE;
  +                     }
  +             } else if(rc < 0) {
  +                     return (JK_FALSE); /* XXX error */
  +             }
  +     }
  +}
   
  -        if(p->left_bytes_to_send > 0) {
  -            unsigned len = p->left_bytes_to_send;
  -            if(len > MAX_SEND_BODY_SZ) {
  -                len = MAX_SEND_BODY_SZ;
  -            }
  -            if(!read_into_msg_buff(p, s, msg, l, len)) {
  -                return JK_FALSE;
  -            }                  
  -            s->content_read = len;
  -            if(!connection_tcp_send_message(p, msg, l)) {
  -             jk_log(l, JK_LOG_ERROR,
  -                                "Error sending request body\n");
  -                     return JK_FALSE;
  -            }   
  -        }
  +#define      JK_RETRIES 3
   
  -        /*
  -         * Enter the message pump.
  -         */
  -         while(1) {
  -            int rc = 0;
  -            
  -                 if(!connection_tcp_get_message(p, msg, l)) {
  -                     jk_log(l, JK_LOG_ERROR,
  -                                    "Error reading request\n");
  -                     return JK_FALSE;
  -                 }
  +/*
  + * service is now splitted in send_request and get_reply
  + * much more easier to do errors recovery
  + */
  +static int JK_METHOD service(jk_endpoint_t *e, 
  +                             jk_ws_service_t *s,
  +                             jk_logger_t *l,
  +                             int *is_recoverable_error)
  +{
  +     int i;
  +     ajp13_operation_t       oper;
  +     ajp13_operation_t       *op = &oper;
  +
  +     jk_log(l, JK_LOG_DEBUG, "Into jk_endpoint_t::service\n");
  +
  +     if(e && e->endpoint_private && s && is_recoverable_error) {
  +             ajp13_endpoint_t *p = e->endpoint_private;
  +                     op->request = jk_b_new(&(p->pool));
  +             jk_b_set_buffer_size(op->request, DEF_BUFFER_SZ); 
  +             jk_b_reset(op->request);
  +       
  +             op->reply = jk_b_new(&(p->pool));
  +             jk_b_set_buffer_size(op->reply, DEF_BUFFER_SZ);
  +             jk_b_reset(op->reply); 
  +             
  +             op->recoverable = JK_TRUE;
  +             op->uploadfd     = -1;          /* not yet used, later ;) */
  +
  +             p->left_bytes_to_send = s->content_length;
  +             p->reuse = JK_FALSE;
  +             *is_recoverable_error = JK_TRUE;
  +
  +             /* 
  +              * We get here initial request (in reqmsg)
  +              */
  +             if(!ajp13_marshal_into_msgb(op->request, s, l)) {
  +                     *is_recoverable_error = JK_FALSE;                
  +                     return JK_FALSE;
  +             }
   
  -            rc = ajp13_process_callback(msg, p, s, l);
  -            if(JK_AJP13_END_RESPONSE == rc) {
  -                return JK_TRUE;
  -            } else if(JK_AJP13_HAS_RESPONSE == rc) {
  -                     rc = connection_tcp_send_message(p, msg, l);
  -                     if(rc < 0) {
  -                             jk_log(l, JK_LOG_DEBUG,
  -                                        "Error reading response1 %d\n", rc);
  -                             return JK_FALSE;
  -                     }
  -            } else if(rc < 0) {
  -                break; /* XXX error */
  -            }
  -         }        
  -    } else {
  -        jk_log(l, 
  -               JK_LOG_ERROR, 
  -               "In jk_endpoint_t::service, NULL parameters\n");
  -    }
  +             /* 
  +              * JK_RETRIES could be replaced by the number of workers in
  +              * a load-balancing configuration 
  +              */
  +             for (i = 0; i < JK_RETRIES; i++)
  +             {
  +                     /*
  +                      * We're using reqmsg which hold initial request
  +                      * if Tomcat is stopped or restarted, we will pass reqmsg
  +                      * to next valid tomcat. 
  +                      */
  +                     if (! send_request(e, s, l, p, op)) {
  +                             jk_log(l, JK_LOG_ERROR, "In jk_endpoint_t::service, 
send_request failed in send loop %d\n", i);
  +                             continue;
  +                     }
  +
  +                     /* Up to there we can recover */
  +                     *is_recoverable_error = JK_TRUE;
  +                     op->recoverable = JK_TRUE;
  +
  +                     if (get_reply(e, s, l, p, op))
  +                             return (JK_TRUE);
  +
  +                     /* if we can't get reply, check if no recover flag was set 
  +                      * if is_recoverable_error is cleared, we have started 
received 
  +                      * upload data and we must consider that operation is no more 
recoverable
  +                      */
  +                     if (! op->recoverable) {
  +                             *is_recoverable_error = JK_FALSE;
  +                             jk_log(l, JK_LOG_ERROR, "In jk_endpoint_t::service, 
get_reply failed without recovery in send loop %d\n", i);
  +                             return JK_FALSE;
  +                     }
  +                             
  +                     jk_log(l, JK_LOG_ERROR, "In jk_endpoint_t::service, get_reply 
failed in send loop %d\n", i);
  +
  +                     jk_close_socket(p->sd);
  +                     p->sd = -1;
  +                     reuse_connection(p, l);
  +             }
  +     } else {
  +             jk_log(l, JK_LOG_ERROR, "In jk_endpoint_t::service, NULL 
parameters\n");
  +     }
   
  -    return JK_FALSE;
  +     return JK_FALSE;
   }
   
   static int JK_METHOD get_endpoint(jk_worker_t *pThis,
  
  
  

Reply via email to