Fix handling of ZSTREAM_END result from zlib.

Avoid getting stuck when ZSTREAM_END occurs with no more bytes written out.
This commit is contained in:
James Turner 2013-02-07 12:59:12 +01:00
parent f983194d7e
commit 45ae3978f6

View File

@ -298,25 +298,51 @@ public:
zlib.next_out = zlibOutputBuffer; zlib.next_out = zlibOutputBuffer;
zlib.avail_out = ZLIB_DECOMPRESS_BUFFER_SIZE; zlib.avail_out = ZLIB_DECOMPRESS_BUFFER_SIZE;
if (contentGZip) { if (contentGZip && !handleGZipHeader()) {
return;
}
int writtenSize = 0;
do {
int result = inflate(&zlib, Z_NO_FLUSH);
if (result == Z_OK || result == Z_STREAM_END) {
// nothing to do
} else {
SG_LOG(SG_IO, SG_WARN, "got Zlib error:" << result);
return;
}
writtenSize = ZLIB_DECOMPRESS_BUFFER_SIZE - zlib.avail_out;
if (result == Z_STREAM_END) {
break;
}
} while ((writtenSize == 0) && (zlib.avail_in > 0));
if (writtenSize > 0) {
activeRequest->processBodyBytes((const char*) zlibOutputBuffer, writtenSize);
}
}
bool handleGZipHeader()
{
// we clear this down to contentDeflate once the GZip header has been seen // we clear this down to contentDeflate once the GZip header has been seen
if (reqSize < GZIP_HEADER_SIZE) { if (zlib.avail_in < GZIP_HEADER_SIZE) {
return; // need more header bytes return false; // need more header bytes
} }
if ((zlibInflateBuffer[0] != GZIP_HEADER_ID1) || if ((zlibInflateBuffer[0] != GZIP_HEADER_ID1) ||
(zlibInflateBuffer[1] != GZIP_HEADER_ID2) || (zlibInflateBuffer[1] != GZIP_HEADER_ID2) ||
(zlibInflateBuffer[2] != GZIP_HEADER_METHOD_DEFLATE)) (zlibInflateBuffer[2] != GZIP_HEADER_METHOD_DEFLATE))
{ {
return; // invalid GZip header return false; // invalid GZip header
} }
char flags = zlibInflateBuffer[3]; char flags = zlibInflateBuffer[3];
int gzipHeaderSize = GZIP_HEADER_SIZE; int gzipHeaderSize = GZIP_HEADER_SIZE;
if (flags & GZIP_HEADER_FEXTRA) { if (flags & GZIP_HEADER_FEXTRA) {
gzipHeaderSize += 2; gzipHeaderSize += 2;
if (reqSize < gzipHeaderSize) { if (zlib.avail_in < gzipHeaderSize) {
return; // need more header bytes return false; // need more header bytes
} }
unsigned short extraHeaderBytes = *(reinterpret_cast<unsigned short*>(zlibInflateBuffer + GZIP_HEADER_FEXTRA)); unsigned short extraHeaderBytes = *(reinterpret_cast<unsigned short*>(zlibInflateBuffer + GZIP_HEADER_FEXTRA));
@ -325,14 +351,14 @@ public:
} }
gzipHeaderSize += extraHeaderBytes; gzipHeaderSize += extraHeaderBytes;
if (reqSize < gzipHeaderSize) { if (zlib.avail_in < gzipHeaderSize) {
return; // need more header bytes return false; // need more header bytes
} }
} }
if (flags & GZIP_HEADER_FNAME) { if (flags & GZIP_HEADER_FNAME) {
gzipHeaderSize++; gzipHeaderSize++;
while (gzipHeaderSize <= reqSize) { while (gzipHeaderSize <= zlib.avail_in) {
if (zlibInflateBuffer[gzipHeaderSize-1] == 0) { if (zlibInflateBuffer[gzipHeaderSize-1] == 0) {
break; // found terminating NULL character break; // found terminating NULL character
} }
@ -341,7 +367,7 @@ public:
if (flags & GZIP_HEADER_COMMENT) { if (flags & GZIP_HEADER_COMMENT) {
gzipHeaderSize++; gzipHeaderSize++;
while (gzipHeaderSize <= reqSize) { while (gzipHeaderSize <= zlib.avail_in) {
if (zlibInflateBuffer[gzipHeaderSize-1] == 0) { if (zlibInflateBuffer[gzipHeaderSize-1] == 0) {
break; // found terminating NULL character break; // found terminating NULL character
} }
@ -352,33 +378,16 @@ public:
gzipHeaderSize += 2; gzipHeaderSize += 2;
} }
if (reqSize < gzipHeaderSize) { if (zlib.avail_in < gzipHeaderSize) {
return; // need more header bytes return false; // need more header bytes
} }
zlib.next_in += gzipHeaderSize; zlib.next_in += gzipHeaderSize;
zlib.avail_in = reqSize - gzipHeaderSize; zlib.avail_in -= gzipHeaderSize;
// now we've processed the GZip header, can decode as deflate // now we've processed the GZip header, can decode as deflate
contentGZip = false; contentGZip = false;
contentDeflate = true; contentDeflate = true;
} return true;
int writtenSize = 0;
do {
int result = inflate(&zlib, Z_NO_FLUSH);
if (result == Z_OK || result == Z_STREAM_END) {
} else {
SG_LOG(SG_IO, SG_WARN, "got Zlib error:" << result);
return;
}
writtenSize = ZLIB_DECOMPRESS_BUFFER_SIZE - zlib.avail_out;
} while ((writtenSize == 0) && (zlib.avail_in > 0));
if (writtenSize > 0) {
activeRequest->processBodyBytes((const char*) zlibOutputBuffer, writtenSize);
}
} }
virtual void foundTerminator(void) virtual void foundTerminator(void)
@ -405,6 +414,7 @@ public:
case STATE_GETTING_CHUNKED_BYTES: case STATE_GETTING_CHUNKED_BYTES:
setTerminator("\r\n"); setTerminator("\r\n");
state = STATE_GETTING_CHUNKED; state = STATE_GETTING_CHUNKED;
buffer.clear();
break; break;