jk_ajp_common.c

来自「Tomcat 4.1与WebServer集成组件的源代码包.」· C语言 代码 · 共 1,483 行 · 第 1/4 页

C
1,483
字号
static int ajp_read_fully_from_server(jk_ws_service_t *s,                                      unsigned char   *buf,                                      unsigned         len){    unsigned rdlen = 0;    unsigned padded_len = len;    if (s->is_chunked && s->no_more_chunks) {    return 0;    }    if (s->is_chunked) {        /* Corner case: buf must be large enough to hold next         * chunk size (if we're on or near a chunk border).         * Pad the length to a reasonable value, otherwise the         * read fails and the remaining chunks are tossed.         */        padded_len = (len < CHUNK_BUFFER_PAD) ?                      len : len - CHUNK_BUFFER_PAD;    }    while(rdlen < padded_len) {        unsigned this_time = 0;        if(!s->read(s, buf + rdlen, len - rdlen, &this_time)) {            return -1;        }        if(0 == this_time) {        if (s->is_chunked) {        s->no_more_chunks = 1; /* read no more */        }            break;        }        rdlen += this_time;    }    return (int)rdlen;}/* * Read data from AJP13/AJP14 protocol * Returns -1 on error, else number of bytes read */static int ajp_read_into_msg_buff(ajp_endpoint_t  *ae,                                  jk_ws_service_t *r,                                  jk_msg_buf_t    *msg,                                  int         len,                                  jk_logger_t     *l){    unsigned char *read_buf = jk_b_get_buff(msg);    jk_b_reset(msg);    read_buf += AJP_HEADER_LEN;    /* leave some space for the buffer headers */    read_buf += AJP_HEADER_SZ_LEN; /* leave some space for the read length */    /* Pick the max size since we don't know the content_length */    if (r->is_chunked && len == 0) {    len = AJP13_MAX_SEND_BODY_SZ;    }    if ((len = ajp_read_fully_from_server(r, read_buf, len)) < 0) {        jk_log(l, JK_LOG_ERROR, "ERROR: receiving data from server failed, the client aborted the connection or network errors.\n");        return -1;    }    if (!r->is_chunked) {	ae->left_bytes_to_send -= len;    }    if (len > 0) {    /* Recipient recognizes empty packet as end of stream, not       an empty body packet */        if(0 != jk_b_append_int(msg, (unsigned short)len)) {            jk_log(l, JK_LOG_ERROR,                    "read_into_msg_buff: Error - jk_b_append_int failed\n");            return -1;    }    }    jk_b_set_len(msg, jk_b_get_len(msg) + len);    return len;}/* * 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 ajp_send_request(jk_endpoint_t *e,                            jk_ws_service_t *s,                            jk_logger_t *l,                            ajp_endpoint_t *ae,                            ajp_operation_t *op){    /* Up to now, we can recover */    op->recoverable = JK_TRUE;    /*     * First try to reuse open connections...    */    while ((ae->sd > 0) && ! ajp_connection_tcp_send_message(ae, op->request, l)) {        jk_log(l, JK_LOG_INFO, "Error sending request try another pooled connection\n");        jk_close_socket(ae->sd);        ae->sd = -1;        ajp_reuse_connection(ae, l);    }    /*     * If we failed to reuse a connection, try to reconnect.     */    if (ae->sd < 0) {        if (ajp_connect_to_endpoint(ae, l) == JK_TRUE) {        /*         * After we are connected, each error that we are going to         * have is probably unrecoverable         */        if (!ajp_connection_tcp_send_message(ae, op->request, l)) {            jk_log(l, JK_LOG_INFO, "Error sending request on a fresh connection\n");            return JK_FALSE;        }        } else {            jk_log(l, JK_LOG_INFO, "Error connecting to the Tomcat process.\n");            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.     */    jk_log(l, JK_LOG_DEBUG, "ajp_send_request 2: request body to send %d - request body to resend %d\n",         ae->left_bytes_to_send, jk_b_get_len(op->reply) - AJP_HEADER_LEN);    /*     * POST recovery job is done here.     * It's not very fine to have posted data in reply but that's the only easy     * way to do that for now. Sharing the reply is really a bad solution but     * it will works for POST DATA less than 8k.     * We send here the first part of data which was sent previously to the     * remote Tomcat     */    if (jk_b_get_len(op->post) > AJP_HEADER_LEN) {        if(!ajp_connection_tcp_send_message(ae, op->post, l)) {            jk_log(l, JK_LOG_ERROR, "Error resending request body\n");            return JK_FALSE;        }    }    else    {        /* We never sent any POST data and we check it we have to send at         * least of block of data (max 8k). These data will be kept in reply         * for resend if the remote Tomcat is down, a fact we will learn only         * doing a read (not yet)          */	/* || s->is_chunked - this can't be done here. The original protocol sends the first	   chunk of post data ( based on Content-Length ), and that's what the java side expects.	   Sending this data for chunked would break other ajp13 serers.	   Note that chunking will continue to work - using the normal read.	*/        if (ae->left_bytes_to_send > 0) {            int len = ae->left_bytes_to_send;            if (len > AJP13_MAX_SEND_BODY_SZ)                 len = AJP13_MAX_SEND_BODY_SZ;                    if ((len = ajp_read_into_msg_buff(ae, s, op->post, len, l)) < 0) {                /* the browser stop sending data, no need to recover */                op->recoverable = JK_FALSE;                return JK_FALSE;            }            s->content_read = len;            if (!ajp_connection_tcp_send_message(ae, op->post, l)) {                jk_log(l, JK_LOG_ERROR, "Error sending request body\n");                return JK_FALSE;            }          }    }    return (JK_TRUE);}/* * What to do with incoming data (dispatcher) */static int ajp_process_callback(jk_msg_buf_t *msg,                                 jk_msg_buf_t *pmsg,                                ajp_endpoint_t *ae,                                jk_ws_service_t *r,                                 jk_logger_t *l) {    int code = (int)jk_b_get_byte(msg);    switch(code) {        case JK_AJP13_SEND_HEADERS:            {                jk_res_data_t res;                if (!ajp_unmarshal_response(msg, &res, ae, l)) {                    jk_log(l, JK_LOG_ERROR, "Error ajp_process_callback - ajp_unmarshal_response failed\n");                    return JK_AJP13_ERROR;                }                if (!r->start_response(r,                                        res.status,                                        res.msg,                                        (const char * const *)res.header_names,                                       (const char * const *)res.header_values,                                       res.num_headers)) {                    jk_log(l, JK_LOG_ERROR, "Error ajp_process_callback - start_response failed\n");                    return JK_CLIENT_ERROR;                }            }        break;        case JK_AJP13_SEND_BODY_CHUNK:            {                unsigned len = (unsigned)jk_b_get_int(msg);                if(!r->write(r, jk_b_get_buff(msg) + jk_b_get_pos(msg), len)) {                    jk_log(l, JK_LOG_ERROR, "ERROR sending data to client. Connection aborted or network problems\n");                    return JK_CLIENT_ERROR;                }            }        break;        case JK_AJP13_GET_BODY_CHUNK:            {        int len = (int)jk_b_get_int(msg);                if(len > AJP13_MAX_SEND_BODY_SZ) {                    len = AJP13_MAX_SEND_BODY_SZ;                }                if(len > ae->left_bytes_to_send) {                    len = ae->left_bytes_to_send;                }        if(len < 0) {            len = 0;        }        /* the right place to add file storage for upload */        if ((len = ajp_read_into_msg_buff(ae, r, pmsg, len, l)) >= 0) {            r->content_read += len;            return JK_AJP13_HAS_RESPONSE;        }                          jk_log(l, JK_LOG_ERROR, "ERROR reading POST data from client. Connection aborted or network problems\n");        return JK_INTERNAL_ERROR;                   }        break;        case JK_AJP13_END_RESPONSE:            {                ae->reuse = (int)jk_b_get_byte(msg);                if( ! ae->reuse ) {                    /*                     * Strange protocol error.                     */                    jk_log(l, JK_LOG_DEBUG, "Reuse: %d\n", ae->reuse );                    ae->reuse = JK_FALSE;                }                /* Reuse in all cases */                ae->reuse = JK_TRUE;            }            return JK_AJP13_END_RESPONSE;        break;        default:            jk_log(l, JK_LOG_ERROR, "Error ajp_process_callback - Invalid code: %d\n", code);            return JK_AJP13_ERROR;    }        return JK_AJP13_NO_RESPONSE;}/* * get replies from Tomcat via Ajp13/Ajp14 * 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/ajp14 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 ajp_get_reply(jk_endpoint_t *e,                         jk_ws_service_t *s,                         jk_logger_t *l,                         ajp_endpoint_t *p,                         ajp_operation_t *op){    /* Start read all reply message */    while(1) {        int rc = 0;        if(!ajp_connection_tcp_get_message(p, op->reply, l)) {            jk_log(l, JK_LOG_ERROR, "Error reading reply from tomcat. Tomcat is down or network problems.\n");            /* we just can't recover, unset recover flag */            return JK_FALSE;        }        rc = ajp_process_callback(op->reply, op->post, p, s, l);        /* 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 = ajp_connection_tcp_send_message(p, op->post, l);                if (rc < 0) {                jk_log(l, JK_LOG_ERROR, "Error sending request data %d. Tomcat is down or network problems.\n", rc);                        return JK_FALSE;            }        } else if(JK_FATAL_ERROR == rc) {          /*           * we won't be able to gracefully recover from this so           * set recoverable to false and get out.           */            op->recoverable = JK_FALSE;            return JK_FALSE;        } else if(JK_CLIENT_ERROR == rc) {                  /*                   * Client has stop talking to us, so get out.                   * We assume this isn't our fault, so just a normal exit.                   * In most (all?)  cases, the ajp13_endpoint::reuse will still be                   * false here, so this will be functionally the same as an                   * un-recoverable error.  We just won't log it as such.                   */                return JK_TRUE;        } else if(rc < 0) {            return (JK_FALSE); /* XXX error */        }    }}#define JK_RETRIES 3/* * service is now splitted in ajp_send_request and ajp_get_reply * much more easier to do errors recovery * * We serve here the request, using AJP13/AJP14 (e->proto) * */int JK_METHOD ajp_service(jk_endpoint_t   *e,                 jk_ws_service_t *s,                jk_logger_t     *l,                int             *is_recoverable_error){    int i;    ajp_operation_t oper;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?