wininet: Fixed gzip decoding on chunked stream.
diff --git a/dlls/wininet/http.c b/dlls/wininet/http.c
index 0f59810..8f9f404 100644
--- a/dlls/wininet/http.c
+++ b/dlls/wininet/http.c
@@ -165,14 +165,16 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
}
}
-/* 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 @@
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 @@
/* 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 @@
WARN("WriteFile failed: %u\n", GetLastError());
}
- if(!bytes_read && (req->dwContentRead == req->dwContentLength))
+ if(finished_reading)
HTTP_FinishedReading(req);
return ret;