wininet: Added support for decompressing gzip encoded content.
diff --git a/configure b/configure
index 413c590..52a8130 100755
--- a/configure
+++ b/configure
Binary files differ
diff --git a/configure.ac b/configure.ac
index 879e358..beccbbd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -377,7 +377,8 @@
unistd.h \
utime.h \
valgrind/memcheck.h \
- valgrind/valgrind.h
+ valgrind/valgrind.h \
+ zlib.h
)
AC_HEADER_STAT()
@@ -1172,6 +1173,13 @@
pthread_get_stacksize_np,
[$LIBPTHREAD])
+dnl **** Check for zlib ****
+if test "$ac_cv_header_zlib_h" = "yes"
+then
+ AC_CHECK_LIB(z,inflate,[AC_DEFINE(HAVE_ZLIB,1,[Define to 1 if you have the `z' library (-lz).])
+ AC_SUBST(ZLIB,"-lz")])
+fi
+
dnl **** Check for EsounD ****
if test "x$with_esd" != xno
then
diff --git a/dlls/wininet/Makefile.in b/dlls/wininet/Makefile.in
index 116c9fd..c7cc1e0 100644
--- a/dlls/wininet/Makefile.in
+++ b/dlls/wininet/Makefile.in
@@ -7,7 +7,7 @@
IMPORTLIB = wininet
IMPORTS = mpr shlwapi shell32 user32 advapi32 kernel32 ntdll
DELAYIMPORTS = secur32 crypt32
-EXTRALIBS = @SOCKETLIBS@
+EXTRALIBS = @SOCKETLIBS@ @ZLIB@
C_SRCS = \
cookie.c \
diff --git a/dlls/wininet/http.c b/dlls/wininet/http.c
index 8db3e97..b11ce21 100644
--- a/dlls/wininet/http.c
+++ b/dlls/wininet/http.c
@@ -48,6 +48,9 @@
#endif
#include <time.h>
#include <assert.h>
+#ifdef HAVE_ZLIB
+# include <zlib.h>
+#endif
#include "windef.h"
#include "winbase.h"
@@ -162,6 +165,15 @@
BOOL finished; /* finished authenticating */
};
+#ifdef HAVE_ZLIB
+
+struct gzip_stream_t {
+ z_stream zstream;
+ BYTE buf[4096];
+};
+
+#endif
+
static BOOL HTTP_OpenConnection(LPWININETHTTPREQW lpwhr);
static BOOL HTTP_GetResponseHeaders(LPWININETHTTPREQW lpwhr, BOOL clear);
static BOOL HTTP_ProcessHeader(LPWININETHTTPREQW lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
@@ -188,6 +200,57 @@
return &req->pCustHeaders[HeaderIndex];
}
+#ifdef HAVE_ZLIB
+
+static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
+{
+ return HeapAlloc(GetProcessHeap(), 0, items*size);
+}
+
+static void wininet_zfree(voidpf opaque, voidpf address)
+{
+ HeapFree(GetProcessHeap(), 0, address);
+}
+
+static void init_gzip_stream(WININETHTTPREQW *req)
+{
+ gzip_stream_t *gzip_stream;
+ int zres;
+
+ gzip_stream = HeapAlloc(GetProcessHeap(), 0, sizeof(gzip_stream_t));
+ gzip_stream->zstream.zalloc = wininet_zalloc;
+ gzip_stream->zstream.zfree = wininet_zfree;
+ gzip_stream->zstream.opaque = NULL;
+ gzip_stream->zstream.next_in = NULL;
+ gzip_stream->zstream.avail_in = 0;
+
+ zres = inflateInit2(&gzip_stream->zstream, 0x1f);
+ if(zres != Z_OK) {
+ ERR("inflateInit failed: %d\n", zres);
+ HeapFree(GetProcessHeap(), 0, gzip_stream);
+ return;
+ }
+
+ 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
+
+static void init_gzip_stream(WININETHTTPREQW *req)
+{
+ ERR("gzip stream not supported, missing zlib.\n");
+}
+
+#endif
+
/* set the request content length based on the headers */
static DWORD set_content_length( LPWININETHTTPREQW lpwhr )
{
@@ -208,6 +271,16 @@
lpwhr->read_chunked = TRUE;
}
+ if(lpwhr->decoding) {
+ int encoding_idx;
+
+ static const WCHAR gzipW[] = {'g','z','i','p',0};
+
+ encoding_idx = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Encoding, 0, FALSE);
+ if(encoding_idx != -1 && !strcmpiW(lpwhr->pCustHeaders[encoding_idx].lpszValue, gzipW))
+ init_gzip_stream(lpwhr);
+ }
+
return lpwhr->dwContentLength;
}
@@ -1476,6 +1549,14 @@
TRACE("%p\n",lpwhr);
+#ifdef HAVE_ZLIB
+ if(lpwhr->gzip_stream) {
+ inflateEnd(&lpwhr->gzip_stream->zstream);
+ HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
+ lpwhr->gzip_stream = NULL;
+ }
+#endif
+
if (!NETCON_connected(&lpwhr->netConnection))
return;
@@ -1659,26 +1740,95 @@
HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
if (!(req->lpHttpSession->lpszPassword = WININET_strdupW(buffer))) return ERROR_OUTOFMEMORY;
return ERROR_SUCCESS;
+ case INTERNET_OPTION_HTTP_DECODING:
+ if(size != sizeof(BOOL))
+ return ERROR_INVALID_PARAMETER;
+ req->decoding = *(BOOL*)buffer;
+ return ERROR_SUCCESS;
}
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 )
{
int len;
- if (req->read_size && req->read_pos)
+ if (req->read_pos)
{
/* move existing data to the start of the buffer */
- memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
+ if(req->read_size)
+ memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
req->read_pos = 0;
}
if (maxlen == -1) maxlen = sizeof(req->read_buf);
- if (!NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
- maxlen - req->read_size, 0, &len )) return FALSE;
+
+ 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;
+ }
+
req->read_size += len;
return TRUE;
}
@@ -1847,6 +1997,7 @@
static DWORD HTTPREQ_Read(WININETHTTPREQW *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
{
int len, bytes_read = 0;
+ DWORD ret = ERROR_SUCCESS;
EnterCriticalSection( &req->read_section );
if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
@@ -1862,12 +2013,20 @@
remove_data( req, bytes_read );
}
- if (size > bytes_read && (!bytes_read || sync))
+ if (size > bytes_read)
{
- 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 */
+ 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,
+ sync ? MSG_WAITALL : 0, &len))
+ bytes_read += len;
+ /* always return success, even if the network layer returns an error */
+ }
}
done:
req->dwContentRead += bytes_read;
@@ -1876,7 +2035,7 @@
TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
LeaveCriticalSection( &req->read_section );
- if(req->lpszCacheFile) {
+ if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
BOOL res;
DWORD dwBytesWritten;
@@ -1888,7 +2047,7 @@
if(!bytes_read && (req->dwContentRead == req->dwContentLength))
HTTP_FinishedReading(req);
- return ERROR_SUCCESS;
+ return ret;
}
@@ -2101,7 +2260,7 @@
}
done:
- if (*available == sizeof(req->read_buf)) /* check if we have even more pending in the socket */
+ if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
{
DWORD extra;
if (NETCON_query_data_available(&req->netConnection, &extra))
@@ -2396,6 +2555,16 @@
index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
break;
+ case HTTP_QUERY_CONTENT_LENGTH:
+ if(lpwhr->gzip_stream) {
+ INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND);
+ return FALSE;
+ }
+
+ index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
+ requested_index,request_only);
+ break;
+
case HTTP_QUERY_RAW_HEADERS_CRLF:
{
LPWSTR headers;
@@ -2504,6 +2673,10 @@
return TRUE;
}
break;
+ case HTTP_QUERY_CONTENT_ENCODING:
+ index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
+ requested_index,request_only);
+ break;
default:
assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
diff --git a/dlls/wininet/internet.h b/dlls/wininet/internet.h
index 95632bc..1d49eaa 100644
--- a/dlls/wininet/internet.h
+++ b/dlls/wininet/internet.h
@@ -183,6 +183,8 @@
struct HttpAuthInfo;
+typedef struct gzip_stream_t gzip_stream_t;
+
typedef struct
{
WININETHANDLEHEADER hdr;
@@ -209,6 +211,9 @@
DWORD read_pos; /* current read position in read_buf */
DWORD read_size; /* valid data size in read_buf */
BYTE read_buf[4096]; /* buffer for already read but not returned data */
+
+ BOOL decoding;
+ gzip_stream_t *gzip_stream;
} WININETHTTPREQW, *LPWININETHTTPREQW;
diff --git a/include/config.h.in b/include/config.h.in
index 12ce108..c22d1f7 100644
--- a/include/config.h.in
+++ b/include/config.h.in
@@ -1041,6 +1041,12 @@
/* Define if Xrender has the XRenderSetPictureTransform function */
#undef HAVE_XRENDERSETPICTURETRANSFORM
+/* Define to 1 if you have the `z' library (-lz). */
+#undef HAVE_ZLIB
+
+/* Define to 1 if you have the <zlib.h> header file. */
+#undef HAVE_ZLIB_H
+
/* Define to 1 if you have the `_pclose' function. */
#undef HAVE__PCLOSE