in buckets/deflate_buckets.c [225:413]
static apr_status_t serf_deflate_refill(serf_bucket_t *bucket)
{
deflate_context_t *ctx = bucket->data;
apr_status_t status;
int zRC;
int flush_v = Z_NO_FLUSH;
/* 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) {
const char *private_data;
apr_size_t private_len;
/* 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)) {
status = ctx->stream_status;
ctx->stream_status = APR_SUCCESS;
return status;
}
if (APR_STATUS_IS_EOF(ctx->stream_status))
flush_v = Z_FINISH;
/* 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;
if (ctx->memLevel >= 0)
ctx->crc = crc32(ctx->crc, (const Bytef *)private_data,
private_len);
} else {
ctx->zstream.next_in = Z_NULL;
ctx->zstream.avail_in = 0;
}
}
while (1) {
if (ctx->memLevel < 0) {
zRC = inflate(&ctx->zstream, flush_v);
if (zRC == Z_BUF_ERROR && APR_STATUS_IS_EOF(ctx->stream_status) &&
ctx->zstream.avail_out > 0) {
/* Zlib can't continue, although there's still space in the
output buffer. This can happen either if the stream is
truncated or corrupted. As we don't know for sure,
return a generic error. */
return SERF_ERROR_DECOMPRESSION_FAILED;
}
}
else {
zRC = deflate(&ctx->zstream, flush_v);
}
if (zRC == Z_BUF_ERROR || ctx->zstream.avail_out == 0) {
/* We're full or zlib requires more space. Either case, clear
out our buffer, reset, and return. */
apr_size_t private_len;
serf_bucket_t *tmp;
ctx->zstream.next_out = ctx->buffer;
private_len = ctx->bufferSize - ctx->zstream.avail_out;
if (ctx->memLevel < 0)
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;
zRC = Z_OK;
break;
}
if (zRC == Z_STREAM_END) {
apr_size_t private_len;
serf_bucket_t *tmp;
private_len = ctx->bufferSize - ctx->zstream.avail_out;
if (ctx->memLevel < 0)
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;
if (ctx->zstream.avail_in) {
/* 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:
if (ctx->memLevel >= 0) {
char *verify_header = serf_bucket_mem_alloc(
bucket->allocator,
DEFLATE_VERIFY_SIZE);
verify_header[0] = ctx->crc & 0xFF;
verify_header[1] = (ctx->crc >> 8) & 0xFF;
verify_header[2] = (ctx->crc >> 16) & 0xFF;
verify_header[3] = (ctx->crc >> 24) & 0xFF;
verify_header[4] = ctx->zstream.total_in & 0xFF;
verify_header[5] = (ctx->zstream.total_in >> 8) & 0xFF;
verify_header[6] = (ctx->zstream.total_in >> 16) & 0xFF;
verify_header[7] = (ctx->zstream.total_in >> 24) & 0xFF;
serf_bucket_aggregate_append(
ctx->inflate_stream,
serf_bucket_simple_own_create(verify_header,
DEFLATE_VERIFY_SIZE,
bucket->allocator));
ctx->state = STATE_COMPRESS_FINISH;
}
else {
ctx->stream_left = ctx->stream_size =
DEFLATE_VERIFY_SIZE;
ctx->state++;
}
break;
case SERF_DEFLATE_DEFLATE:
/* Deflate does not have a verify footer. */
if (ctx->memLevel >= 0)
ctx->state = STATE_COMPRESS_FINISH;
else
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. */
}
if (zRC != Z_OK && zRC != Z_STREAM_END)
return SERF_ERROR_DECOMPRESSION_FAILED;
else
return APR_SUCCESS;
}