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);
}