wininet: Fixed gzip decoding on chunked stream.

This commit is contained in:
Jacek Caban 2009-06-07 21:19:06 +02:00 committed by Alexandre Julliard
parent 4540d74f49
commit a76db21978

View File

@ -165,14 +165,16 @@ struct HttpAuthInfo
BOOL finished; /* finished authenticating */
};
#ifdef HAVE_ZLIB
struct gzip_stream_t {
#ifdef HAVE_ZLIB
z_stream zstream;
BYTE buf[4096];
};
#endif
BYTE buf[8192];
DWORD buf_size;
DWORD buf_pos;
BOOL end_of_data;
};
static BOOL HTTP_OpenConnection(LPWININETHTTPREQW lpwhr);
static BOOL HTTP_GetResponseHeaders(LPWININETHTTPREQW lpwhr, BOOL clear);
@ -223,6 +225,11 @@ static void init_gzip_stream(WININETHTTPREQW *req)
gzip_stream->zstream.opaque = NULL;
gzip_stream->zstream.next_in = NULL;
gzip_stream->zstream.avail_in = 0;
gzip_stream->zstream.next_out = NULL;
gzip_stream->zstream.avail_out = 0;
gzip_stream->buf_pos = 0;
gzip_stream->buf_size = 0;
gzip_stream->end_of_data = FALSE;
zres = inflateInit2(&gzip_stream->zstream, 0x1f);
if(zres != Z_OK) {
@ -232,14 +239,6 @@ static void init_gzip_stream(WININETHTTPREQW *req)
}
req->gzip_stream = gzip_stream;
req->dwContentLength = ~0u;
if(req->read_size) {
memcpy(gzip_stream->buf, req->read_buf + req->read_pos, req->read_size);
gzip_stream->zstream.next_in = gzip_stream->buf;
gzip_stream->zstream.avail_in = req->read_size;
req->read_size = 0;
}
}
#else
@ -1750,61 +1749,6 @@ static DWORD HTTPREQ_SetOption(WININETHANDLEHEADER *hdr, DWORD option, void *buf
return ERROR_INTERNET_INVALID_OPTION;
}
static inline BOOL is_gzip_buf_empty(gzip_stream_t *gzip_stream)
{
#ifdef HAVE_ZLIB
return gzip_stream->zstream.avail_in == 0;
#else
return TRUE;
#endif
}
static DWORD read_gzip_data(WININETHTTPREQW *req, BYTE *buf, int size, int flags, int *read_ret)
{
DWORD ret = ERROR_SUCCESS;
int read = 0;
#ifdef HAVE_ZLIB
int res, len, zres;
z_stream *zstream;
zstream = &req->gzip_stream->zstream;
while(read < size) {
if(is_gzip_buf_empty(req->gzip_stream)) {
res = NETCON_recv( &req->netConnection, req->gzip_stream->buf,
sizeof(req->gzip_stream->buf), flags, &len);
if(!res) {
if(!read)
ret = INTERNET_GetLastError();
break;
}
zstream->next_in = req->gzip_stream->buf;
zstream->avail_in = len;
}
zstream->next_out = buf+read;
zstream->avail_out = size-read;
zres = inflate(zstream, Z_FULL_FLUSH);
read = size - zstream->avail_out;
if(zres == Z_STREAM_END) {
TRACE("end of data\n");
req->dwContentLength = req->dwContentRead + req->read_size + read;
break;
}else if(zres != Z_OK) {
WARN("inflate failed %d\n", zres);
if(!read)
ret = ERROR_INTERNET_DECODING_FAILED;
break;
}
}
#endif
*read_ret = read;
return ret;
}
/* read some more data into the read buffer (the read section must be held) */
static BOOL read_more_data( WININETHTTPREQW *req, int maxlen )
{
@ -1820,14 +1764,9 @@ static BOOL read_more_data( WININETHTTPREQW *req, int maxlen )
if (maxlen == -1) maxlen = sizeof(req->read_buf);
if (req->gzip_stream) {
if(read_gzip_data(req, req->read_buf + req->read_size, maxlen - req->read_size, 0, &len) != ERROR_SUCCESS)
return FALSE;
}else {
if(!NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
maxlen - req->read_size, 0, &len ))
return FALSE;
}
if(!NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
maxlen - req->read_size, 0, &len ))
return FALSE;
req->read_size += len;
return TRUE;
@ -1939,17 +1878,10 @@ static BOOL start_next_chunk( WININETHTTPREQW *req )
}
}
/* return the size of data available to be read immediately (the read section must be held) */
static DWORD get_avail_data( WININETHTTPREQW *req )
{
if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
return 0;
return min( req->read_size, req->dwContentLength - req->dwContentRead );
}
/* check if we have reached the end of the data to read (the read section must be held) */
static BOOL end_of_read_data( WININETHTTPREQW *req )
{
if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
if (req->read_chunked) return (req->dwContentLength == 0);
if (req->dwContentLength == ~0u) return FALSE;
return (req->dwContentLength == req->dwContentRead);
@ -1973,6 +1905,76 @@ static BOOL refill_buffer( WININETHTTPREQW *req )
return TRUE;
}
static DWORD read_gzip_data(WININETHTTPREQW *req, BYTE *buf, int size, BOOL sync, int *read_ret)
{
DWORD ret = ERROR_SUCCESS;
int read = 0;
#ifdef HAVE_ZLIB
z_stream *zstream = &req->gzip_stream->zstream;
int zres;
while(read < size && !req->gzip_stream->end_of_data) {
if(!req->read_size) {
if(!sync || !refill_buffer(req))
break;
}
zstream->next_in = req->read_buf+req->read_pos;
zstream->avail_in = req->read_size;
zstream->next_out = buf+read;
zstream->avail_out = size-read;
zres = inflate(zstream, Z_FULL_FLUSH);
read = size - zstream->avail_out;
remove_data(req, req->read_size-zstream->avail_in);
if(zres == Z_STREAM_END) {
TRACE("end of data\n");
req->gzip_stream->end_of_data = TRUE;
}else if(zres != Z_OK) {
WARN("inflate failed %d\n", zres);
if(!read)
ret = ERROR_INTERNET_DECODING_FAILED;
break;
}
}
#endif
*read_ret = read;
return ret;
}
static void refill_gzip_buffer(WININETHTTPREQW *req)
{
DWORD res;
int len;
if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
return;
if(req->gzip_stream->buf_pos) {
if(req->gzip_stream->buf_size)
memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
req->gzip_stream->buf_pos = 0;
}
res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
if(res == ERROR_SUCCESS)
req->gzip_stream->buf_size += len;
}
/* return the size of data available to be read immediately (the read section must be held) */
static DWORD get_avail_data( WININETHTTPREQW *req )
{
if (req->gzip_stream) {
refill_gzip_buffer(req);
return req->gzip_stream->buf_size;
}
if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
return 0;
return min( req->read_size, req->dwContentLength - req->dwContentRead );
}
static void HTTP_ReceiveRequestData(WININETHTTPREQW *req, BOOL first_notif)
{
INTERNET_ASYNC_RESULT iar;
@ -1996,37 +1998,51 @@ static void HTTP_ReceiveRequestData(WININETHTTPREQW *req, BOOL first_notif)
/* read data from the http connection (the read section must be held) */
static DWORD HTTPREQ_Read(WININETHTTPREQW *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
{
BOOL finished_reading = FALSE;
int len, bytes_read = 0;
DWORD ret = ERROR_SUCCESS;
EnterCriticalSection( &req->read_section );
if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
{
if (!start_next_chunk( req )) goto done;
}
if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
if (req->read_size)
{
bytes_read = min( req->read_size, size );
memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
remove_data( req, bytes_read );
}
if(req->gzip_stream) {
if(req->gzip_stream->buf_size) {
bytes_read = min(req->gzip_stream->buf_size, size);
memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
req->gzip_stream->buf_pos += bytes_read;
req->gzip_stream->buf_size -= bytes_read;
}else if(!req->read_size && !req->gzip_stream->end_of_data) {
refill_buffer(req);
}
if (size > bytes_read)
{
if (req->gzip_stream) {
if(is_gzip_buf_empty(req->gzip_stream) || !bytes_read || sync) {
ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size - bytes_read, sync ? MSG_WAITALL : 0, &len);
if(ret == ERROR_SUCCESS)
bytes_read += len;
}
}else if (!bytes_read || sync) {
if(size > bytes_read) {
ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
if(ret == ERROR_SUCCESS)
bytes_read += len;
}
finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
}else {
if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
if (req->read_size) {
bytes_read = min( req->read_size, size );
memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
remove_data( req, bytes_read );
}
if (size > bytes_read && (!bytes_read || sync)) {
if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
sync ? MSG_WAITALL : 0, &len))
bytes_read += len;
/* always return success, even if the network layer returns an error */
}
finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
}
done:
req->dwContentRead += bytes_read;
@ -2044,7 +2060,7 @@ done:
WARN("WriteFile failed: %u\n", GetLastError());
}
if(!bytes_read && (req->dwContentRead == req->dwContentLength))
if(finished_reading)
HTTP_FinishedReading(req);
return ret;