wininet: Added support for decompressing gzip encoded content.

This commit is contained in:
Jacek Caban 2009-05-29 23:35:13 +02:00 committed by Alexandre Julliard
parent 26bbf072aa
commit 11ca05f6ae
6 changed files with 289 additions and 15 deletions

84
configure vendored
View File

@ -669,6 +669,7 @@ ALSALIBS
ESDLIBS
ESDINCL
ESDCONFIG
ZLIB
FREETYPEINCL
FREETYPELIBS
ft_devel
@ -5776,6 +5777,7 @@ done
for ac_header in \
@ -5899,7 +5901,8 @@ for ac_header in \
unistd.h \
utime.h \
valgrind/memcheck.h \
valgrind/valgrind.h
valgrind/valgrind.h \
zlib.h
do
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
@ -15051,6 +15054,85 @@ done
LIBS="$ac_wine_check_funcs_save_LIBS"
if test "$ac_cv_header_zlib_h" = "yes"
then
{ $as_echo "$as_me:$LINENO: checking for inflate in -lz" >&5
$as_echo_n "checking for inflate in -lz... " >&6; }
if test "${ac_cv_lib_z_inflate+set}" = set; then
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lz $LIBS"
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char inflate ();
int
main ()
{
return inflate ();
;
return 0;
}
_ACEOF
rm -f conftest.$ac_objext conftest$ac_exeext
if { (ac_try="$ac_link"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
$as_echo "$ac_try_echo") >&5
(eval "$ac_link") 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
$as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } && {
test -z "$ac_c_werror_flag" ||
test ! -s conftest.err
} && test -s conftest$ac_exeext && {
test "$cross_compiling" = yes ||
$as_test_x conftest$ac_exeext
}; then
ac_cv_lib_z_inflate=yes
else
$as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_cv_lib_z_inflate=no
fi
rm -rf conftest.dSYM
rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_z_inflate" >&5
$as_echo "$ac_cv_lib_z_inflate" >&6; }
if test "x$ac_cv_lib_z_inflate" = x""yes; then
cat >>confdefs.h <<\_ACEOF
#define HAVE_ZLIB 1
_ACEOF
ZLIB="-lz"
fi
fi
if test "x$with_esd" != xno
then
save_CFLAGS="$CFLAGS"

View File

@ -377,7 +377,8 @@ AC_CHECK_HEADERS(\
unistd.h \
utime.h \
valgrind/memcheck.h \
valgrind/valgrind.h
valgrind/valgrind.h \
zlib.h
)
AC_HEADER_STAT()
@ -1172,6 +1173,13 @@ WINE_CHECK_LIB_FUNCS(\
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

View File

@ -7,7 +7,7 @@ MODULE = wininet.dll
IMPORTLIB = wininet
IMPORTS = mpr shlwapi shell32 user32 advapi32 kernel32 ntdll
DELAYIMPORTS = secur32 crypt32
EXTRALIBS = @SOCKETLIBS@
EXTRALIBS = @SOCKETLIBS@ @ZLIB@
C_SRCS = \
cookie.c \

View File

@ -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 @@ struct HttpAuthInfo
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 @@ static LPHTTPHEADERW HTTP_GetHeader(LPWININETHTTPREQW req, LPCWSTR head)
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 @@ static DWORD set_content_length( LPWININETHTTPREQW lpwhr )
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 @@ static void HTTPREQ_CloseConnection(WININETHANDLEHEADER *hdr)
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 @@ static DWORD HTTPREQ_SetOption(WININETHANDLEHEADER *hdr, DWORD option, void *buf
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 */
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 (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;
maxlen - req->read_size, 0, &len ))
return FALSE;
}
req->read_size += len;
return TRUE;
}
@ -1847,6 +1997,7 @@ static void HTTP_ReceiveRequestData(WININETHTTPREQW *req, BOOL first_notif)
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,13 +2013,21 @@ static DWORD HTTPREQ_Read(WININETHTTPREQW *req, void *buffer, DWORD size, DWORD
remove_data( req, bytes_read );
}
if (size > bytes_read && (!bytes_read || sync))
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 (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;
*read = bytes_read;
@ -1876,7 +2035,7 @@ done:
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 @@ done:
if(!bytes_read && (req->dwContentRead == req->dwContentLength))
HTTP_FinishedReading(req);
return ERROR_SUCCESS;
return ret;
}
@ -2101,7 +2260,7 @@ static DWORD HTTPREQ_QueryDataAvailable(WININETHANDLEHEADER *hdr, DWORD *availab
}
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 @@ static BOOL HTTP_HttpQueryInfoW( LPWININETHTTPREQW lpwhr, DWORD dwInfoLevel,
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 @@ static BOOL HTTP_HttpQueryInfoW( LPWININETHTTPREQW lpwhr, DWORD dwInfoLevel,
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));

View File

@ -183,6 +183,8 @@ typedef struct
struct HttpAuthInfo;
typedef struct gzip_stream_t gzip_stream_t;
typedef struct
{
WININETHANDLEHEADER hdr;
@ -209,6 +211,9 @@ typedef struct
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;

View File

@ -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