static int validate_client_certificate(int preverify_ok, X509_STORE_CTX *ctx) { serf__log(TEST_VERBOSE, __FILE__, "validate_client_certificate called, " "preverify code: %d.\n", preverify_ok); return preverify_ok; }
static void log_error(int verbose_flag, const char *filename, serf__spnego_context_t *ctx, OM_uint32 err_maj_stat, OM_uint32 err_min_stat, const char *msg) { OM_uint32 maj_stat, min_stat; gss_buffer_desc stat_buff; OM_uint32 msg_ctx = 0; if (verbose_flag) { maj_stat = gss_display_status(&min_stat, err_maj_stat, GSS_C_GSS_CODE, ctx->gss_mech, &msg_ctx, &stat_buff); if (maj_stat == GSS_S_COMPLETE || maj_stat == GSS_S_FAILURE) { maj_stat = gss_display_status(&min_stat, err_min_stat, GSS_C_MECH_CODE, ctx->gss_mech, &msg_ctx, &stat_buff); } serf__log(verbose_flag, filename, "%s (%x,%d): %s\n", msg, err_maj_stat, err_min_stat, stat_buff.value); } }
static apr_status_t ssl_reset(serv_ctx_t *serv_ctx) { ssl_context_t *ssl_ctx = serv_ctx->ssl_ctx; serf__log(TEST_VERBOSE, __FILE__, "Reset ssl context.\n"); ssl_ctx->handshake_done = 0; if (ssl_ctx) SSL_clear(ssl_ctx->ssl); init_ssl(serv_ctx); return APR_SUCCESS; }
static apr_status_t ssl_socket_write(serv_ctx_t *serv_ctx, const char *data, apr_size_t *len) { ssl_context_t *ssl_ctx = serv_ctx->ssl_ctx; int result = SSL_write(ssl_ctx->ssl, data, *len); if (result > 0) { *len = result; return APR_SUCCESS; } if (result == 0) return APR_EAGAIN; serf__log(TEST_VERBOSE, __FILE__, "ssl_socket_write: ssl error?\n"); return SERF_ERROR_ISSUE_IN_TESTSUITE; }
static apr_status_t ssl_socket_read(serv_ctx_t *serv_ctx, char *data, apr_size_t *len) { ssl_context_t *ssl_ctx = serv_ctx->ssl_ctx; int result = SSL_read(ssl_ctx->ssl, data, *len); if (result > 0) { *len = result; return APR_SUCCESS; } else { int ssl_err; ssl_err = SSL_get_error(ssl_ctx->ssl, result); switch (ssl_err) { case SSL_ERROR_SYSCALL: /* error in bio_bucket_read, probably APR_EAGAIN or APR_EOF */ *len = 0; return serv_ctx->bio_read_status; case SSL_ERROR_WANT_READ: *len = 0; return APR_EAGAIN; case SSL_ERROR_SSL: default: *len = 0; serf__log(TEST_VERBOSE, __FILE__, "ssl_socket_read SSL Error %d: ", ssl_err); ERR_print_errors_fp(stderr); serf__log_nopref(TEST_VERBOSE, "\n"); return SERF_ERROR_ISSUE_IN_TESTSUITE; } } /* not reachable */ return SERF_ERROR_ISSUE_IN_TESTSUITE; }
/* Exchange data between proxy and server */ static apr_status_t proxy_replay(serv_ctx_t *servctx, apr_int16_t rtnevents, apr_pool_t *pool) { apr_status_t status; if (rtnevents & APR_POLLIN) { apr_size_t len; char buf[BUFSIZE]; serf_bucket_t *tmp; serf__log(TEST_VERBOSE, __FILE__, "proxy_replay: POLLIN\n"); /* Read all incoming data from the server to forward it to the client later. */ do { len = BUFSIZE; status = apr_socket_recv(servctx->proxy_client_sock, buf, &len); if (SERF_BUCKET_READ_ERROR(status)) return status; serf__log(TEST_VERBOSE, __FILE__, "proxy: reading %d bytes %.*s from server.\n", len, len, buf); tmp = serf_bucket_simple_copy_create(buf, len, servctx->allocator); serf_bucket_aggregate_append(servctx->clientstream, tmp); } while (!status); } if (rtnevents & APR_POLLOUT) { apr_size_t len; char *buf; serf__log(TEST_VERBOSE, __FILE__, "proxy_replay: POLLOUT\n"); /* Send all data received from the client to the server. */ do { apr_size_t readlen; readlen = BUFSIZE; if (!servctx->servstream) servctx->servstream = serf__bucket_stream_create( servctx->allocator, detect_eof,servctx); status = serf_bucket_read(servctx->servstream, BUFSIZE, &buf, &readlen); if (SERF_BUCKET_READ_ERROR(status)) return status; if (!readlen) break; len = readlen; serf__log(TEST_VERBOSE, __FILE__, "proxy: sending %d bytes %.*s to server.\n", len, len, buf); status = apr_socket_send(servctx->proxy_client_sock, buf, &len); if (status != APR_SUCCESS) { return status; } if (len != readlen) /* abort for now */ return APR_EGENERAL; } while (!status); } else if (rtnevents & APR_POLLIN) { /* ignore */ } else { printf("Unknown rtnevents: %d\n", rtnevents); abort(); } return status; }
/* Verify received requests and take the necessary actions (return a response, kill the connection ...) */ static apr_status_t replay(serv_ctx_t *servctx, apr_int16_t rtnevents, apr_pool_t *pool) { apr_status_t status = APR_SUCCESS; test_server_action_t *action; if (rtnevents & APR_POLLIN) { if (servctx->message_list == NULL) { /* we're not expecting any requests to reach this server! */ serf__log(TEST_VERBOSE, __FILE__, "Received request where none was expected.\n"); return SERF_ERROR_ISSUE_IN_TESTSUITE; } if (servctx->cur_action >= servctx->action_count) { char buf[128]; apr_size_t len = sizeof(buf); status = servctx->read(servctx, buf, &len); if (! APR_STATUS_IS_EAGAIN(status)) { /* we're out of actions! */ serf__log(TEST_VERBOSE, __FILE__, "Received more requests than expected.\n"); return SERF_ERROR_ISSUE_IN_TESTSUITE; } return status; } action = &servctx->action_list[servctx->cur_action]; serf__log(TEST_VERBOSE, __FILE__, "POLLIN while replaying action %d, kind: %d.\n", servctx->cur_action, action->kind); /* Read the remaining data from the client and kill the socket. */ if (action->kind == SERVER_IGNORE_AND_KILL_CONNECTION) { char buf[128]; apr_size_t len = sizeof(buf); status = servctx->read(servctx, buf, &len); if (status == APR_EOF) { serf__log(TEST_VERBOSE, __FILE__, "Killing this connection.\n"); apr_socket_close(servctx->client_sock); servctx->client_sock = NULL; next_action(servctx); return APR_SUCCESS; } return status; } else if (action->kind == SERVER_RECV || (action->kind == SERVER_RESPOND && servctx->outstanding_responses == 0)) { apr_size_t msg_len, len; char buf[128]; test_server_message_t *message; message = &servctx->message_list[servctx->cur_message]; msg_len = strlen(message->text); do { len = msg_len - servctx->message_buf_pos; if (len > sizeof(buf)) len = sizeof(buf); status = servctx->read(servctx, buf, &len); if (SERF_BUCKET_READ_ERROR(status)) return status; if (status == APR_EOF) { serf__log(TEST_VERBOSE, __FILE__, "Server: Client hung up the connection.\n"); break; } if (servctx->options & TEST_SERVER_DUMP) fwrite(buf, len, 1, stdout); if (strncmp(buf, message->text + servctx->message_buf_pos, len) != 0) { /* ## TODO: Better diagnostics. */ printf("Expected: (\n"); fwrite(message->text + servctx->message_buf_pos, len, 1, stdout); printf(")\n"); printf("Actual: (\n"); fwrite(buf, len, 1, stdout); printf(")\n"); return SERF_ERROR_ISSUE_IN_TESTSUITE; } servctx->message_buf_pos += len; if (servctx->message_buf_pos >= msg_len) { next_message(servctx); servctx->message_buf_pos -= msg_len; if (action->kind == SERVER_RESPOND) servctx->outstanding_responses++; if (action->kind == SERVER_RECV) next_action(servctx); break; } } while (!status); } else if (action->kind == PROXY_FORWARD) { apr_size_t len; char buf[BUFSIZE]; serf_bucket_t *tmp; /* Read all incoming data from the client to forward it to the server later. */ do { len = BUFSIZE; status = servctx->read(servctx, buf, &len); if (SERF_BUCKET_READ_ERROR(status)) return status; serf__log(TEST_VERBOSE, __FILE__, "proxy: reading %d bytes %.*s from client with " "status %d.\n", len, len, buf, status); if (status == APR_EOF) { serf__log(TEST_VERBOSE, __FILE__, "Proxy: client hung up the connection. Reset the " "connection to the server.\n"); /* We have to stop forwarding, if a new connection opens the CONNECT request should not be forwarded to the server. */ next_action(servctx); } if (!servctx->servstream) servctx->servstream = serf__bucket_stream_create( servctx->allocator, detect_eof,servctx); if (len) { tmp = serf_bucket_simple_copy_create(buf, len, servctx->allocator); serf_bucket_aggregate_append(servctx->servstream, tmp); } } while (!status); } } if (rtnevents & APR_POLLOUT) { action = &servctx->action_list[servctx->cur_action]; serf__log(TEST_VERBOSE, __FILE__, "POLLOUT when replaying action %d, kind: %d.\n", servctx->cur_action, action->kind); if (action->kind == SERVER_RESPOND && servctx->outstanding_responses) { apr_size_t msg_len; apr_size_t len; msg_len = strlen(action->text); len = msg_len - servctx->action_buf_pos; status = servctx->send(servctx, action->text + servctx->action_buf_pos, &len); if (status != APR_SUCCESS) return status; if (servctx->options & TEST_SERVER_DUMP) fwrite(action->text + servctx->action_buf_pos, len, 1, stdout); servctx->action_buf_pos += len; if (servctx->action_buf_pos >= msg_len) { next_action(servctx); servctx->outstanding_responses--; } } else if (action->kind == SERVER_KILL_CONNECTION || action->kind == SERVER_IGNORE_AND_KILL_CONNECTION) { serf__log(TEST_VERBOSE, __FILE__, "Killing this connection.\n"); apr_socket_close(servctx->client_sock); servctx->client_sock = NULL; next_action(servctx); } else if (action->kind == PROXY_FORWARD) { apr_size_t len; char *buf; if (!servctx->proxy_client_sock) { serf__log(TEST_VERBOSE, __FILE__, "Proxy: setting up connection " "to server.\n"); status = create_client_socket(&servctx->proxy_client_sock, servctx, action->text); if (!servctx->clientstream) servctx->clientstream = serf__bucket_stream_create( servctx->allocator, detect_eof,servctx); } /* Send all data received from the server to the client. */ do { apr_size_t readlen; readlen = BUFSIZE; status = serf_bucket_read(servctx->clientstream, readlen, &buf, &readlen); if (SERF_BUCKET_READ_ERROR(status)) return status; if (!readlen) break; len = readlen; serf__log(TEST_VERBOSE, __FILE__, "proxy: sending %d bytes to client.\n", len); status = servctx->send(servctx, buf, &len); if (status != APR_SUCCESS) { return status; } if (len != readlen) /* abort for now, return buf to aggregate if not everything could be sent. */ return APR_EGENERAL; } while (!status); } } else if (rtnevents & APR_POLLIN) { /* ignore */ } else { printf("Unknown rtnevents: %d\n", rtnevents); abort(); } return status; }
static apr_status_t ssl_handshake(serv_ctx_t *serv_ctx) { ssl_context_t *ssl_ctx = serv_ctx->ssl_ctx; int result; if (ssl_ctx->handshake_done) return APR_SUCCESS; /* SSL handshake */ result = SSL_accept(ssl_ctx->ssl); if (result == 1) { X509 *peer; serf__log(TEST_VERBOSE, __FILE__, "Handshake successful.\n"); /* Check client certificate */ peer = SSL_get_peer_certificate(ssl_ctx->ssl); if (peer) { serf__log(TEST_VERBOSE, __FILE__, "Peer cert received.\n"); if (SSL_get_verify_result(ssl_ctx->ssl) == X509_V_OK) { /* The client sent a certificate which verified OK */ char buf[1024]; int ret; X509_NAME *subject = X509_get_subject_name(peer); ret = X509_NAME_get_text_by_NID(subject, NID_commonName, buf, 1024); if (ret != -1 && strcmp(serv_ctx->client_cn, buf) != 0) { serf__log(TEST_VERBOSE, __FILE__, "Client cert common name " "\"%s\" doesn't match expected \"%s\".\n", buf, serv_ctx->client_cn); return SERF_ERROR_ISSUE_IN_TESTSUITE; } } } else { if (serv_ctx->client_cn) { serf__log(TEST_VERBOSE, __FILE__, "Client cert expected but not" " received.\n"); return SERF_ERROR_ISSUE_IN_TESTSUITE; } } ssl_ctx->handshake_done = 1; } else { int ssl_err; ssl_err = SSL_get_error(ssl_ctx->ssl, result); switch (ssl_err) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: return APR_EAGAIN; case SSL_ERROR_SYSCALL: return serv_ctx->bio_read_status; /* Usually APR_EAGAIN */ default: serf__log(TEST_VERBOSE, __FILE__, "SSL Error %d: ", ssl_err); ERR_print_errors_fp(stderr); serf__log_nopref(TEST_VERBOSE, "\n"); return SERF_ERROR_ISSUE_IN_TESTSUITE; } } return APR_EAGAIN; }
apr_status_t serf__spnego_init_sec_context(serf__spnego_context_t *ctx, const char *service, const char *hostname, serf__spnego_buffer_t *input_buf, serf__spnego_buffer_t *output_buf, apr_pool_t *result_pool, apr_pool_t *scratch_pool ) { gss_buffer_desc gss_input_buf = GSS_C_EMPTY_BUFFER; gss_buffer_desc *gss_output_buf_p; OM_uint32 gss_min_stat, gss_maj_stat; gss_name_t host_gss_name; gss_buffer_desc bufdesc; gss_OID dummy; /* unused */ /* Get the name for the HTTP service at the target host. */ /* TODO: should be shared between multiple requests. */ bufdesc.value = apr_pstrcat(scratch_pool, service, "@", hostname, NULL); bufdesc.length = strlen(bufdesc.value); serf__log(AUTH_VERBOSE, __FILE__, "Get principal for %s\n", bufdesc.value); gss_maj_stat = gss_import_name (&gss_min_stat, &bufdesc, GSS_C_NT_HOSTBASED_SERVICE, &host_gss_name); if(GSS_ERROR(gss_maj_stat)) { log_error(AUTH_VERBOSE, __FILE__, ctx, gss_maj_stat, gss_min_stat, "Error converting principal name to GSS internal format "); return SERF_ERROR_AUTHN_FAILED; } /* If the server sent us a token, pass it to gss_init_sec_token for validation. */ gss_input_buf.value = input_buf->value; gss_input_buf.length = input_buf->length; gss_output_buf_p = apr_pcalloc(result_pool, sizeof(*gss_output_buf_p)); /* Establish a security context to the server. */ gss_maj_stat = gss_init_sec_context (&gss_min_stat, /* minor_status */ GSS_C_NO_CREDENTIAL, /* XXXXX claimant_cred_handle */ &ctx->gss_ctx, /* gssapi context handle */ host_gss_name, /* HTTP@server name */ ctx->gss_mech, /* mech_type (SPNEGO) */ GSS_C_MUTUAL_FLAG, /* ensure the peer authenticates itself */ 0, /* default validity period */ GSS_C_NO_CHANNEL_BINDINGS, /* do not use channel bindings */ &gss_input_buf, /* server token, initially empty */ &dummy, /* actual mech type */ gss_output_buf_p, /* output_token */ NULL, /* ret_flags */ NULL /* not interested in remaining validity */ ); apr_pool_cleanup_register(result_pool, gss_output_buf_p, cleanup_sec_buffer, apr_pool_cleanup_null); output_buf->value = gss_output_buf_p->value; output_buf->length = gss_output_buf_p->length; switch(gss_maj_stat) { case GSS_S_COMPLETE: return APR_SUCCESS; case GSS_S_CONTINUE_NEEDED: return APR_EAGAIN; default: log_error(AUTH_VERBOSE, __FILE__, ctx, gss_maj_stat, gss_min_stat, "Error during Kerberos handshake"); return SERF_ERROR_AUTHN_FAILED; } }
static apr_status_t serf_deflate_read(serf_bucket_t *bucket, apr_size_t requested, const char **data, apr_size_t *len) { deflate_context_t *ctx = bucket->data; apr_status_t status; const char *private_data; apr_size_t private_len; int zRC; while (1) { switch (ctx->state) { case STATE_READING_HEADER: case STATE_READING_VERIFY: status = serf_bucket_read(ctx->stream, ctx->stream_left, &private_data, &private_len); if (SERF_BUCKET_READ_ERROR(status)) { return status; } /* The C99 standard (7.21.1/2) requires valid data pointer * even for zero length array for all functions unless explicitly * stated otherwise. So don't copy data even most mempy() * implementations have special handling for zero length copy. */ if (private_len) { memcpy(ctx->hdr_buffer + (ctx->stream_size - ctx->stream_left), private_data, private_len); ctx->stream_left -= private_len; } if (ctx->stream_left == 0) { ctx->state++; if (APR_STATUS_IS_EAGAIN(status)) { *len = 0; return status; } } else if (status) { *len = 0; return status; } break; case STATE_HEADER: if (ctx->hdr_buffer[0] != deflate_magic[0] || ctx->hdr_buffer[1] != deflate_magic[1]) { serf__log(LOGLVL_ERROR, LOGCOMP_COMPR, __FILE__, ctx->config, "Incorrect magic number. Actual:%hhx%hhx.\n", ctx->hdr_buffer[0], ctx->hdr_buffer[1]); return SERF_ERROR_DECOMPRESSION_FAILED; } if (ctx->hdr_buffer[3] != 0) { serf__log(LOGLVL_ERROR, LOGCOMP_COMPR, __FILE__, ctx->config, "Incorrect magic number (at offset 3). Actual: " "%x\n", ctx->hdr_buffer[3]); return SERF_ERROR_DECOMPRESSION_FAILED; } ctx->state++; break; case STATE_VERIFY: { unsigned long compCRC, compLen, actualLen; /* Do the checksum computation. */ compCRC = getLong((unsigned char*)ctx->hdr_buffer); if (ctx->crc != compCRC) { serf__log(LOGLVL_ERROR, LOGCOMP_COMPR, __FILE__, ctx->config, "Incorrect crc. Expected: %ld, Actual:%ld\n", compCRC, ctx->crc); return SERF_ERROR_DECOMPRESSION_FAILED; } compLen = getLong((unsigned char*)ctx->hdr_buffer + 4); /* The length in the trailer is module 2^32, so do the same for the actual length. */ actualLen = ctx->zstream.total_out; actualLen &= 0xFFFFFFFF; if (actualLen != compLen) { serf__log(LOGLVL_ERROR, LOGCOMP_COMPR, __FILE__, ctx->config, "Incorrect length. Expected: %ld, Actual:%ld\n", compLen, ctx->zstream.total_out); return SERF_ERROR_DECOMPRESSION_FAILED; } ctx->state++; break; } case STATE_INIT: zRC = inflateInit2(&ctx->zstream, ctx->windowSize); if (zRC != Z_OK) { serf__log(LOGLVL_ERROR, LOGCOMP_COMPR, __FILE__, ctx->config, "inflateInit2 error %d - %s\n", zRC, ctx->zstream.msg); return SERF_ERROR_DECOMPRESSION_FAILED; } ctx->zstream.next_out = ctx->buffer; ctx->zstream.avail_out = ctx->bufferSize; ctx->state++; break; case STATE_FINISH: inflateEnd(&ctx->zstream); serf_bucket_aggregate_prepend(ctx->stream, ctx->inflate_stream); ctx->inflate_stream = 0; ctx->state++; break; case STATE_INFLATE: /* Do we have anything already uncompressed to read? */ status = serf_bucket_read(ctx->inflate_stream, requested, data, len); if (SERF_BUCKET_READ_ERROR(status)) { return status; } /* Hide EOF. */ if (APR_STATUS_IS_EOF(status)) { status = ctx->stream_status; if (APR_STATUS_IS_EOF(status)) { /* We've read all of the data from our stream, but we * need to continue to iterate until we flush * out the zlib buffer. */ status = APR_SUCCESS; } } if (*len != 0) { return status; } /* We tried; but we have nothing buffered. Fetch more. */ /* It is possible that we maxed out avail_out before * exhausting avail_in; therefore, continue using the * previous buffer. Otherwise, fetch more data from * our stream bucket. */ if (ctx->zstream.avail_in == 0) { /* When we empty our inflated stream, we'll return this * status - this allow us to eventually pass up EAGAINs. */ ctx->stream_status = serf_bucket_read(ctx->stream, ctx->bufferSize, &private_data, &private_len); if (SERF_BUCKET_READ_ERROR(ctx->stream_status)) { return ctx->stream_status; } if (!private_len && APR_STATUS_IS_EAGAIN(ctx->stream_status)) { *len = 0; status = ctx->stream_status; ctx->stream_status = APR_SUCCESS; return status; } /* Make valgrind happy and explictly initialize next_in to specific * value for empty buffer. */ if (private_len) { ctx->zstream.next_in = (unsigned char*)private_data; ctx->zstream.avail_in = private_len; } else { ctx->zstream.next_in = Z_NULL; ctx->zstream.avail_in = 0; } } while (1) { zRC = inflate(&ctx->zstream, Z_NO_FLUSH); /* We're full or zlib requires more space. Either case, clear out our buffer, reset, and return. */ if (zRC == Z_BUF_ERROR || ctx->zstream.avail_out == 0) { serf_bucket_t *tmp; ctx->zstream.next_out = ctx->buffer; private_len = ctx->bufferSize - ctx->zstream.avail_out; ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, private_len); /* FIXME: There probably needs to be a free func. */ tmp = SERF_BUCKET_SIMPLE_STRING_LEN((char *)ctx->buffer, private_len, bucket->allocator); serf_bucket_aggregate_append(ctx->inflate_stream, tmp); ctx->zstream.avail_out = ctx->bufferSize; break; } if (zRC == Z_STREAM_END) { serf_bucket_t *tmp; private_len = ctx->bufferSize - ctx->zstream.avail_out; ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, private_len); /* FIXME: There probably needs to be a free func. */ tmp = SERF_BUCKET_SIMPLE_STRING_LEN((char *)ctx->buffer, private_len, bucket->allocator); serf_bucket_aggregate_append(ctx->inflate_stream, tmp); ctx->zstream.avail_out = ctx->bufferSize; /* Push back the remaining data to be read. */ tmp = serf_bucket_aggregate_create(bucket->allocator); serf_bucket_set_config(tmp, ctx->config); serf_bucket_aggregate_prepend(tmp, ctx->stream); ctx->stream = tmp; /* We now need to take the remaining avail_in and * throw it in ctx->stream so our next read picks it up. */ tmp = SERF_BUCKET_SIMPLE_STRING_LEN( (const char*)ctx->zstream.next_in, ctx->zstream.avail_in, bucket->allocator); serf_bucket_aggregate_prepend(ctx->stream, tmp); switch (ctx->format) { case SERF_DEFLATE_GZIP: ctx->stream_left = ctx->stream_size = DEFLATE_VERIFY_SIZE; ctx->state++; break; case SERF_DEFLATE_DEFLATE: /* Deflate does not have a verify footer. */ ctx->state = STATE_FINISH; break; default: /* Not reachable */ return APR_EGENERAL; } break; } /* Any other error? */ if (zRC != Z_OK) { serf__log(LOGLVL_ERROR, LOGCOMP_COMPR, __FILE__, ctx->config, "inflate error %d - %s\n", zRC, ctx->zstream.msg); return SERF_ERROR_DECOMPRESSION_FAILED; } /* As long as zRC == Z_OK, just keep looping. */ } /* Okay, we've inflated. Try to read. */ status = serf_bucket_read(ctx->inflate_stream, requested, data, len); /* Hide EOF. */ if (APR_STATUS_IS_EOF(status)) { status = ctx->stream_status; /* If the inflation wasn't finished, return APR_SUCCESS. */ if (zRC != Z_STREAM_END) return APR_SUCCESS; /* If our stream is finished too and all data was inflated, * return SUCCESS so we'll iterate one more time. */ if (APR_STATUS_IS_EOF(status)) { /* No more data to read from the stream, and everything inflated. If all data was received correctly, state should have been advanced to STATE_READING_VERIFY or STATE_FINISH. If not, then the data was incomplete and we have an error. */ if (ctx->state != STATE_INFLATE) return APR_SUCCESS; else { serf__log(LOGLVL_ERROR, LOGCOMP_COMPR, __FILE__, ctx->config, "Unexpected EOF on input stream\n"); return SERF_ERROR_DECOMPRESSION_FAILED; } } } return status; case STATE_DONE: /* We're done inflating. Use our finished buffer. */ return serf_bucket_read(ctx->stream, requested, data, len); default: /* Not reachable */ return APR_EGENERAL; } } /* NOTREACHED */ }