Commit a76db219 authored by Jacek Caban's avatar Jacek Caban Committed by Alexandre Julliard

wininet: Fixed gzip decoding on chunked stream.

parent 4540d74f
...@@ -165,14 +165,16 @@ struct HttpAuthInfo ...@@ -165,14 +165,16 @@ struct HttpAuthInfo
BOOL finished; /* finished authenticating */ BOOL finished; /* finished authenticating */
}; };
#ifdef HAVE_ZLIB
struct gzip_stream_t { struct gzip_stream_t {
#ifdef HAVE_ZLIB
z_stream zstream; z_stream zstream;
BYTE buf[4096];
};
#endif #endif
BYTE buf[8192];
DWORD buf_size;
DWORD buf_pos;
BOOL end_of_data;
};
static BOOL HTTP_OpenConnection(LPWININETHTTPREQW lpwhr); static BOOL HTTP_OpenConnection(LPWININETHTTPREQW lpwhr);
static BOOL HTTP_GetResponseHeaders(LPWININETHTTPREQW lpwhr, BOOL clear); static BOOL HTTP_GetResponseHeaders(LPWININETHTTPREQW lpwhr, BOOL clear);
...@@ -223,6 +225,11 @@ static void init_gzip_stream(WININETHTTPREQW *req) ...@@ -223,6 +225,11 @@ static void init_gzip_stream(WININETHTTPREQW *req)
gzip_stream->zstream.opaque = NULL; gzip_stream->zstream.opaque = NULL;
gzip_stream->zstream.next_in = NULL; gzip_stream->zstream.next_in = NULL;
gzip_stream->zstream.avail_in = 0; 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); zres = inflateInit2(&gzip_stream->zstream, 0x1f);
if(zres != Z_OK) { if(zres != Z_OK) {
...@@ -232,14 +239,6 @@ static void init_gzip_stream(WININETHTTPREQW *req) ...@@ -232,14 +239,6 @@ static void init_gzip_stream(WININETHTTPREQW *req)
} }
req->gzip_stream = gzip_stream; 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 #else
...@@ -1750,61 +1749,6 @@ static DWORD HTTPREQ_SetOption(WININETHANDLEHEADER *hdr, DWORD option, void *buf ...@@ -1750,61 +1749,6 @@ static DWORD HTTPREQ_SetOption(WININETHANDLEHEADER *hdr, DWORD option, void *buf
return ERROR_INTERNET_INVALID_OPTION; 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) */ /* read some more data into the read buffer (the read section must be held) */
static BOOL read_more_data( WININETHTTPREQW *req, int maxlen ) static BOOL read_more_data( WININETHTTPREQW *req, int maxlen )
{ {
...@@ -1820,14 +1764,9 @@ 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 (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, if(!NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
maxlen - req->read_size, 0, &len )) maxlen - req->read_size, 0, &len ))
return FALSE; return FALSE;
}
req->read_size += len; req->read_size += len;
return TRUE; return TRUE;
...@@ -1939,17 +1878,10 @@ static BOOL start_next_chunk( WININETHTTPREQW *req ) ...@@ -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) */ /* 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 ) 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->read_chunked) return (req->dwContentLength == 0);
if (req->dwContentLength == ~0u) return FALSE; if (req->dwContentLength == ~0u) return FALSE;
return (req->dwContentLength == req->dwContentRead); return (req->dwContentLength == req->dwContentRead);
...@@ -1973,6 +1905,76 @@ static BOOL refill_buffer( WININETHTTPREQW *req ) ...@@ -1973,6 +1905,76 @@ static BOOL refill_buffer( WININETHTTPREQW *req )
return TRUE; 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) static void HTTP_ReceiveRequestData(WININETHTTPREQW *req, BOOL first_notif)
{ {
INTERNET_ASYNC_RESULT iar; INTERNET_ASYNC_RESULT iar;
...@@ -1996,37 +1998,51 @@ static void HTTP_ReceiveRequestData(WININETHTTPREQW *req, BOOL first_notif) ...@@ -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) */ /* 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) static DWORD HTTPREQ_Read(WININETHTTPREQW *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
{ {
BOOL finished_reading = FALSE;
int len, bytes_read = 0; int len, bytes_read = 0;
DWORD ret = ERROR_SUCCESS; DWORD ret = ERROR_SUCCESS;
EnterCriticalSection( &req->read_section ); EnterCriticalSection( &req->read_section );
if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead)) if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
{ {
if (!start_next_chunk( req )) goto done; if (!start_next_chunk( req )) goto done;
} }
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) {
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->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
if (req->read_size) if (req->read_size) {
{
bytes_read = min( req->read_size, size ); bytes_read = min( req->read_size, size );
memcpy( buffer, req->read_buf + req->read_pos, bytes_read ); memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
remove_data( req, bytes_read ); remove_data( req, bytes_read );
} }
if (size > bytes_read) if (size > bytes_read && (!bytes_read || sync)) {
{
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 (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read, if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
sync ? MSG_WAITALL : 0, &len)) sync ? MSG_WAITALL : 0, &len))
bytes_read += len; bytes_read += len;
/* always return success, even if the network layer returns an error */ /* always return success, even if the network layer returns an error */
} }
finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
} }
done: done:
req->dwContentRead += bytes_read; req->dwContentRead += bytes_read;
...@@ -2044,7 +2060,7 @@ done: ...@@ -2044,7 +2060,7 @@ done:
WARN("WriteFile failed: %u\n", GetLastError()); WARN("WriteFile failed: %u\n", GetLastError());
} }
if(!bytes_read && (req->dwContentRead == req->dwContentLength)) if(finished_reading)
HTTP_FinishedReading(req); HTTP_FinishedReading(req);
return ret; return ret;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment