static void read_response()

in support/ab.c [1948:2192]


static void read_response(struct connection * c)
{
    struct worker *worker = c->worker;
    apr_size_t r;
    apr_status_t status;
    char *part;
    char respcode[4];       /* 3 digits and null */

read_more:
    r = sizeof(worker->buffer);
    if (c->length && r > c->length - c->bread) {
        r = c->length - c->bread;
    }
#ifdef USE_SSL
    if (c->ssl) {
        status = SSL_read(c->ssl, worker->buffer, r);
        if (status <= 0) {
            int scode = SSL_get_error(c->ssl, status);
            switch (scode) {
            case SSL_ERROR_WANT_READ:
                set_conn_state(c, STATE_READ, APR_POLLIN);
                break;

            case SSL_ERROR_WANT_WRITE:
                set_conn_state(c, STATE_READ, APR_POLLOUT);
                break;

            case SSL_ERROR_SYSCALL:
                if (status == 0 && c->keptalive) {
            case SSL_ERROR_ZERO_RETURN:
                    /* connection closed cleanly or aborted during keepalive:
                     * let the length check determine whether it's an error
                     */
                    shutdown_connection(c);
                    break;
                }
            default:
                /* some fatal error: */
                BIO_printf(bio_err, "SSL read failed (%d) - closing connection\n", scode);
                ERR_print_errors(bio_err);
                abort_connection(c);
                break;
            }
            return;
        }
        r = status;
    }
    else
#endif
    {
        status = apr_socket_recv(c->aprsock, worker->buffer, &r);
        if (APR_STATUS_IS_EAGAIN(status)) {
            set_conn_state(c, STATE_READ, APR_POLLIN);
            return;
        }
        if (status != APR_SUCCESS && !r) {
            if (APR_STATUS_IS_EOF(status) || c->keptalive) {
                /* connection closed cleanly or aborted during keepalive:
                 * let the length check determine whether it's an error
                 */
                shutdown_connection(c);
            }
            else {
                worker->metrics.err_recv++;
                if (recverrok) {
                    if (verbosity >= 1) {
                        char buf[120];
                        fprintf(stderr,"%s: %s (%d)\n", "apr_socket_recv",
                                apr_strerror(status, buf, sizeof buf), status);
                    }
                }
                else {
                    graceful_strerror("apr_socket_recv", status);
                }
                abort_connection(c);
            }
            return;
        }
    }

    worker->metrics.totalread += r;
    if (c->read == 0) {
        c->beginread = apr_time_now();
    }
    c->read += r;

    if (!c->gotheader) {
        char *s;
        int l = 4;
        apr_size_t space = CBUFFSIZE - c->cbx - 1; /* -1 allows for \0 term */
        int tocopy = (space < r) ? space : r;
#ifdef NOT_ASCII
        apr_size_t inbytes_left = space, outbytes_left = space;

        status = apr_xlate_conv_buffer(from_ascii, worker->buffer, &inbytes_left,
                           c->cbuff + c->cbx, &outbytes_left);
        if (status || inbytes_left || outbytes_left) {
            fprintf(stderr, "only simple translation is supported (%d/%" APR_SIZE_T_FMT
                            "/%" APR_SIZE_T_FMT ")\n", status, inbytes_left, outbytes_left);
            exit(1);
        }
#else
        memcpy(c->cbuff + c->cbx, worker->buffer, space);
#endif              /* NOT_ASCII */
        c->cbx += tocopy;
        space -= tocopy;
        c->cbuff[c->cbx] = 0;   /* terminate for benefit of strstr */
        if (verbosity >= 2) {
            printf("LOG: header received:\n%s\n", c->cbuff);
        }
        s = strstr(c->cbuff, "\r\n\r\n");
        /*
         * this next line is so that we talk to NCSA 1.5 which blatantly
         * breaks the http specifaction
         */
        if (!s) {
            s = strstr(c->cbuff, "\n\n");
            l = 2;
        }

        if (!s) {
            /* read rest next time */
            if (space) {
                set_conn_state(c, STATE_READ, APR_POLLIN);
            }
            else {
                /* header is in invalid or too big - close connection */
                if (++worker->metrics.err_response > 10) {
                    fprintf(stderr,
                            "\nTest aborted after 10 failures\n\n");
                    graceful_error("Response header too long\n");
                }
                abort_connection(c);
            }
            return;
        }
        {
            /* have full header */
            s[l / 2] = '\0';     /* terminate at end of header */
            c->gotheader = 1;

            /* account for the body we may have read already */
            c->bread += c->cbx - (s + l - c->cbuff) + r - tocopy;
            worker->metrics.totalbread += c->bread;

            /*
             * XXX: this parsing isn't even remotely HTTP compliant... but in
             * the interest of speed it doesn't totally have to be, it just
             * needs to be extended to handle whatever servers folks want to
             * test against. -djg
             */

            /* check response code */
            part = strstr(c->cbuff, "HTTP");    /* really HTTP/1.x_ */
            if (part && strlen(part) > strlen("HTTP/1.x_")) {
                strncpy(respcode, (part + strlen("HTTP/1.x_")), 3);
                respcode[3] = '\0';
            }
            else {
                strcpy(respcode, "500");
            }

            if (respcode[0] != '2') {
                worker->metrics.err_response++;
                if (verbosity >= 2)
                    printf("WARNING: Response code not 2xx (%s)\n", respcode);
            }
            else if (verbosity >= 3) {
                printf("LOG: Response code = %s\n", respcode);
            }

            c->keepalive = (keepalive && xstrcasestr(c->cbuff, "Keep-Alive"));
            if (c->keepalive) {
                const char *cl = xstrcasestr(c->cbuff, "Content-Length:");
                if (cl && method != HEAD) {
                    /* response to HEAD doesn't have entity body */
                    c->length = atoi(cl + 16);
                }
                else {
                    c->length = 0;
                }
            }

            /* We have received the header, so we know this destination socket
             * address is working, so schedule all remaining connections. */
            if (!worker->succeeded_once) {
                int i;
                apr_time_t now = apr_time_now();
                for (i = 1; i < worker->concurrency; i++) {
                    worker->conns[i].delay = now + (i * ramp);
                    APR_RING_INSERT_TAIL(&worker->delayed_ring, &worker->conns[i],
                                         connection, delay_list);
                }
                worker->succeeded_once = 1;

                /*
                 * first time, extract some interesting info
                 */
                if (worker->slot == 0) {
                    char *p, *q;
                    size_t len = 0;
                    p = xstrcasestr(c->cbuff, "Server:");
                    q = servername;
                    if (p) {
                        p += 8;
                        /* -1 to not overwrite last '\0' byte */
                        while (*p > 32 && len++ < sizeof(servername) - 1)
                            *q++ = *p++;
                    }
                    *q = 0;
                }

#if APR_HAS_THREADS
                if (num_workers > 1 && worker->slot == 0) {
                    apr_status_t rv;
                    apr_thread_mutex_lock(workers_mutex);
                    rv = apr_thread_cond_signal(workers_can_start);
                    if (rv != APR_SUCCESS) {
                        graceful_strerror("apr_thread_cond_wait()", rv);
                        close_connection(c);
                        return;
                    }
                    workers_can_start = NULL; /* one shot */
                    apr_thread_mutex_unlock(workers_mutex);
                }
#endif
            }
        }
    }
    else {
        /* outside header, everything we have read is entity body */
        c->bread += r;
        worker->metrics.totalbread += r;
    }

    /* read incomplete or connection terminated by close, continue
     * reading until we get everything or EOF/EAGAIN.
     */
    if (c->bread < c->length || (!c->length && method != HEAD)) {
        goto read_more;
    }

    /* read complete, reuse/close depending on keepalive */
    finalize_connection(c, c->keepalive != 0);
}