urlmon: Move HttpProtocol::Continue implementation to generic Protocol object.
This commit is contained in:
parent
4c129514b5
commit
c2ffe97779
|
@ -80,8 +80,93 @@ typedef struct {
|
|||
static const WCHAR wszHeaders[] = {'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',
|
||||
':',' ','g','z','i','p',',',' ','d','e','f','l','a','t','e',0};
|
||||
|
||||
static LPWSTR query_http_info(HttpProtocol *This, DWORD option)
|
||||
{
|
||||
LPWSTR ret = NULL;
|
||||
DWORD len = 0;
|
||||
BOOL res;
|
||||
|
||||
res = HttpQueryInfoW(This->base.request, option, NULL, &len, NULL);
|
||||
if (!res && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
|
||||
ret = heap_alloc(len);
|
||||
res = HttpQueryInfoW(This->base.request, option, ret, &len, NULL);
|
||||
}
|
||||
if(!res) {
|
||||
TRACE("HttpQueryInfoW(%d) failed: %08x\n", option, GetLastError());
|
||||
heap_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define ASYNCPROTOCOL_THIS(iface) DEFINE_THIS2(HttpProtocol, base, iface)
|
||||
|
||||
static HRESULT HttpProtocol_start_downloading(Protocol *prot)
|
||||
{
|
||||
HttpProtocol *This = ASYNCPROTOCOL_THIS(prot);
|
||||
LPWSTR content_type = 0, content_length = 0;
|
||||
DWORD len = sizeof(DWORD);
|
||||
DWORD status_code;
|
||||
BOOL res;
|
||||
HRESULT hres;
|
||||
|
||||
static const WCHAR wszDefaultContentType[] =
|
||||
{'t','e','x','t','/','h','t','m','l',0};
|
||||
|
||||
if(!This->http_negotiate) {
|
||||
WARN("Expected IHttpNegotiate pointer to be non-NULL\n");
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
res = HttpQueryInfoW(This->base.request, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
|
||||
&status_code, &len, NULL);
|
||||
if(res) {
|
||||
LPWSTR response_headers = query_http_info(This, HTTP_QUERY_RAW_HEADERS_CRLF);
|
||||
if(response_headers) {
|
||||
hres = IHttpNegotiate_OnResponse(This->http_negotiate, status_code, response_headers,
|
||||
NULL, NULL);
|
||||
heap_free(response_headers);
|
||||
if (hres != S_OK) {
|
||||
WARN("IHttpNegotiate_OnResponse failed: %08x\n", hres);
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
}else {
|
||||
WARN("HttpQueryInfo failed: %d\n", GetLastError());
|
||||
}
|
||||
|
||||
if(This->https)
|
||||
IInternetProtocolSink_ReportProgress(This->base.protocol_sink, BINDSTATUS_ACCEPTRANGES, NULL);
|
||||
|
||||
content_type = query_http_info(This, HTTP_QUERY_CONTENT_TYPE);
|
||||
if(content_type) {
|
||||
/* remove the charset, if present */
|
||||
LPWSTR p = strchrW(content_type, ';');
|
||||
if (p) *p = '\0';
|
||||
|
||||
IInternetProtocolSink_ReportProgress(This->base.protocol_sink,
|
||||
(This->base.bindf & BINDF_FROMURLMON)
|
||||
? BINDSTATUS_MIMETYPEAVAILABLE : BINDSTATUS_RAWMIMETYPE,
|
||||
content_type);
|
||||
heap_free(content_type);
|
||||
}else {
|
||||
WARN("HttpQueryInfo failed: %d\n", GetLastError());
|
||||
IInternetProtocolSink_ReportProgress(This->base.protocol_sink,
|
||||
(This->base.bindf & BINDF_FROMURLMON)
|
||||
? BINDSTATUS_MIMETYPEAVAILABLE : BINDSTATUS_RAWMIMETYPE,
|
||||
wszDefaultContentType);
|
||||
}
|
||||
|
||||
content_length = query_http_info(This, HTTP_QUERY_CONTENT_LENGTH);
|
||||
if(content_length) {
|
||||
This->base.content_length = atoiW(content_length);
|
||||
heap_free(content_length);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static void HttpProtocol_close_connection(Protocol *prot)
|
||||
{
|
||||
HttpProtocol *This = ASYNCPROTOCOL_THIS(prot);
|
||||
|
@ -104,46 +189,10 @@ static void HttpProtocol_close_connection(Protocol *prot)
|
|||
#undef ASYNCPROTOCOL_THIS
|
||||
|
||||
static const ProtocolVtbl AsyncProtocolVtbl = {
|
||||
HttpProtocol_start_downloading,
|
||||
HttpProtocol_close_connection
|
||||
};
|
||||
|
||||
static void HTTPPROTOCOL_ReportResult(HttpProtocol *This, HRESULT hres)
|
||||
{
|
||||
if (!(This->base.flags & FLAG_RESULT_REPORTED) &&
|
||||
This->base.protocol_sink)
|
||||
{
|
||||
This->base.flags |= FLAG_RESULT_REPORTED;
|
||||
IInternetProtocolSink_ReportResult(This->base.protocol_sink, hres, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void HTTPPROTOCOL_ReportData(HttpProtocol *This)
|
||||
{
|
||||
DWORD bscf;
|
||||
if (!(This->base.flags & FLAG_LAST_DATA_REPORTED) &&
|
||||
This->base.protocol_sink)
|
||||
{
|
||||
if (This->base.flags & FLAG_FIRST_DATA_REPORTED)
|
||||
{
|
||||
bscf = BSCF_INTERMEDIATEDATANOTIFICATION;
|
||||
}
|
||||
else
|
||||
{
|
||||
This->base.flags |= FLAG_FIRST_DATA_REPORTED;
|
||||
bscf = BSCF_FIRSTDATANOTIFICATION;
|
||||
}
|
||||
if (This->base.flags & FLAG_ALL_DATA_READ &&
|
||||
!(This->base.flags & FLAG_LAST_DATA_REPORTED))
|
||||
{
|
||||
This->base.flags |= FLAG_LAST_DATA_REPORTED;
|
||||
bscf |= BSCF_LASTDATANOTIFICATION;
|
||||
}
|
||||
IInternetProtocolSink_ReportData(This->base.protocol_sink, bscf,
|
||||
This->base.current_position+This->base.available_bytes,
|
||||
This->base.content_length);
|
||||
}
|
||||
}
|
||||
|
||||
static void CALLBACK HTTPPROTOCOL_InternetStatusCallback(
|
||||
HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus,
|
||||
LPVOID lpvStatusInformation, DWORD dwStatusInformationLength)
|
||||
|
@ -539,156 +588,10 @@ done:
|
|||
static HRESULT WINAPI HttpProtocol_Continue(IInternetProtocol *iface, PROTOCOLDATA *pProtocolData)
|
||||
{
|
||||
HttpProtocol *This = PROTOCOL_THIS(iface);
|
||||
DWORD len = sizeof(DWORD), status_code;
|
||||
LPWSTR response_headers = 0, content_type = 0, content_length = 0;
|
||||
|
||||
static const WCHAR wszDefaultContentType[] =
|
||||
{'t','e','x','t','/','h','t','m','l',0};
|
||||
|
||||
TRACE("(%p)->(%p)\n", This, pProtocolData);
|
||||
|
||||
if (!pProtocolData)
|
||||
{
|
||||
WARN("Expected pProtocolData to be non-NULL\n");
|
||||
return S_OK;
|
||||
}
|
||||
else if (!This->base.request)
|
||||
{
|
||||
WARN("Expected request to be non-NULL\n");
|
||||
return S_OK;
|
||||
}
|
||||
else if (!This->http_negotiate)
|
||||
{
|
||||
WARN("Expected IHttpNegotiate pointer to be non-NULL\n");
|
||||
return S_OK;
|
||||
}
|
||||
else if (!This->base.protocol_sink)
|
||||
{
|
||||
WARN("Expected IInternetProtocolSink pointer to be non-NULL\n");
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
if (pProtocolData->pData == (LPVOID)BINDSTATUS_DOWNLOADINGDATA)
|
||||
{
|
||||
if (!HttpQueryInfoW(This->base.request, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
|
||||
&status_code, &len, NULL))
|
||||
{
|
||||
WARN("HttpQueryInfo failed: %d\n", GetLastError());
|
||||
}
|
||||
else
|
||||
{
|
||||
len = 0;
|
||||
if ((!HttpQueryInfoW(This->base.request, HTTP_QUERY_RAW_HEADERS_CRLF, response_headers, &len,
|
||||
NULL) &&
|
||||
GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
|
||||
!(response_headers = heap_alloc(len)) ||
|
||||
!HttpQueryInfoW(This->base.request, HTTP_QUERY_RAW_HEADERS_CRLF, response_headers, &len,
|
||||
NULL))
|
||||
{
|
||||
WARN("HttpQueryInfo failed: %d\n", GetLastError());
|
||||
}
|
||||
else
|
||||
{
|
||||
HRESULT hres = IHttpNegotiate_OnResponse(This->http_negotiate, status_code,
|
||||
response_headers, NULL, NULL);
|
||||
if (hres != S_OK)
|
||||
{
|
||||
WARN("IHttpNegotiate_OnResponse failed: %08x\n", hres);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(This->https)
|
||||
IInternetProtocolSink_ReportProgress(This->base.protocol_sink, BINDSTATUS_ACCEPTRANGES, NULL);
|
||||
|
||||
len = 0;
|
||||
if ((!HttpQueryInfoW(This->base.request, HTTP_QUERY_CONTENT_TYPE, content_type, &len, NULL) &&
|
||||
GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
|
||||
!(content_type = heap_alloc(len)) ||
|
||||
!HttpQueryInfoW(This->base.request, HTTP_QUERY_CONTENT_TYPE, content_type, &len, NULL))
|
||||
{
|
||||
WARN("HttpQueryInfo failed: %d\n", GetLastError());
|
||||
IInternetProtocolSink_ReportProgress(This->base.protocol_sink,
|
||||
(This->base.bindf & BINDF_FROMURLMON) ?
|
||||
BINDSTATUS_MIMETYPEAVAILABLE :
|
||||
BINDSTATUS_RAWMIMETYPE,
|
||||
wszDefaultContentType);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* remove the charset, if present */
|
||||
LPWSTR p = strchrW(content_type, ';');
|
||||
if (p) *p = '\0';
|
||||
|
||||
IInternetProtocolSink_ReportProgress(This->base.protocol_sink,
|
||||
(This->base.bindf & BINDF_FROMURLMON) ?
|
||||
BINDSTATUS_MIMETYPEAVAILABLE :
|
||||
BINDSTATUS_RAWMIMETYPE,
|
||||
content_type);
|
||||
}
|
||||
|
||||
len = 0;
|
||||
if ((!HttpQueryInfoW(This->base.request, HTTP_QUERY_CONTENT_LENGTH, content_length, &len, NULL) &&
|
||||
GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
|
||||
!(content_length = heap_alloc(len)) ||
|
||||
!HttpQueryInfoW(This->base.request, HTTP_QUERY_CONTENT_LENGTH, content_length, &len, NULL))
|
||||
{
|
||||
WARN("HttpQueryInfo failed: %d\n", GetLastError());
|
||||
This->base.content_length = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
This->base.content_length = atoiW(content_length);
|
||||
}
|
||||
|
||||
if(This->base.bindf & BINDF_NEEDFILE) {
|
||||
WCHAR cache_file[MAX_PATH];
|
||||
DWORD buflen = sizeof(cache_file);
|
||||
|
||||
if(InternetQueryOptionW(This->base.request, INTERNET_OPTION_DATAFILE_NAME,
|
||||
cache_file, &buflen))
|
||||
{
|
||||
IInternetProtocolSink_ReportProgress(This->base.protocol_sink,
|
||||
BINDSTATUS_CACHEFILENAMEAVAILABLE,
|
||||
cache_file);
|
||||
}else {
|
||||
FIXME("Could not get cache file\n");
|
||||
}
|
||||
}
|
||||
|
||||
This->base.flags |= FLAG_FIRST_CONTINUE_COMPLETE;
|
||||
}
|
||||
|
||||
if (pProtocolData->pData >= (LPVOID)BINDSTATUS_DOWNLOADINGDATA)
|
||||
{
|
||||
/* InternetQueryDataAvailable may immediately fork and perform its asynchronous
|
||||
* read, so clear the flag _before_ calling so it does not incorrectly get cleared
|
||||
* after the status callback is called */
|
||||
This->base.flags &= ~FLAG_REQUEST_COMPLETE;
|
||||
if (!InternetQueryDataAvailable(This->base.request, &This->base.available_bytes, 0, 0))
|
||||
{
|
||||
if (GetLastError() != ERROR_IO_PENDING)
|
||||
{
|
||||
This->base.flags |= FLAG_REQUEST_COMPLETE;
|
||||
WARN("InternetQueryDataAvailable failed: %d\n", GetLastError());
|
||||
HTTPPROTOCOL_ReportResult(This, INET_E_DATA_NOT_AVAILABLE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
This->base.flags |= FLAG_REQUEST_COMPLETE;
|
||||
HTTPPROTOCOL_ReportData(This);
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
heap_free(response_headers);
|
||||
heap_free(content_type);
|
||||
heap_free(content_length);
|
||||
|
||||
/* Returns S_OK on native */
|
||||
return S_OK;
|
||||
return protocol_continue(&This->base, pProtocolData);
|
||||
}
|
||||
|
||||
static HRESULT WINAPI HttpProtocol_Abort(IInternetProtocol *iface, HRESULT hrReason,
|
||||
|
|
|
@ -53,6 +53,11 @@ WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
|
|||
#define FLAG_LAST_DATA_REPORTED 0x0010
|
||||
#define FLAG_RESULT_REPORTED 0x0020
|
||||
|
||||
static inline HRESULT report_progress(Protocol *protocol, ULONG status_code, LPCWSTR status_text)
|
||||
{
|
||||
return IInternetProtocolSink_ReportProgress(protocol->protocol_sink, status_code, status_text);
|
||||
}
|
||||
|
||||
static inline HRESULT report_result(Protocol *protocol, HRESULT hres)
|
||||
{
|
||||
if (!(protocol->flags & FLAG_RESULT_REPORTED) && protocol->protocol_sink) {
|
||||
|
@ -95,6 +100,69 @@ static void all_data_read(Protocol *protocol)
|
|||
report_result(protocol, S_OK);
|
||||
}
|
||||
|
||||
HRESULT protocol_continue(Protocol *protocol, PROTOCOLDATA *data)
|
||||
{
|
||||
HRESULT hres;
|
||||
|
||||
if (!data) {
|
||||
WARN("Expected pProtocolData to be non-NULL\n");
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
if(!protocol->request) {
|
||||
WARN("Expected request to be non-NULL\n");
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
if(!protocol->protocol_sink) {
|
||||
WARN("Expected IInternetProtocolSink pointer to be non-NULL\n");
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
if(data->pData == (LPVOID)BINDSTATUS_DOWNLOADINGDATA) {
|
||||
hres = protocol->vtbl->start_downloading(protocol);
|
||||
if(FAILED(hres)) {
|
||||
protocol_close_connection(protocol);
|
||||
report_result(protocol, hres);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
if(protocol->bindf & BINDF_NEEDFILE) {
|
||||
WCHAR cache_file[MAX_PATH];
|
||||
DWORD buflen = sizeof(cache_file);
|
||||
|
||||
if(InternetQueryOptionW(protocol->request, INTERNET_OPTION_DATAFILE_NAME,
|
||||
cache_file, &buflen)) {
|
||||
report_progress(protocol, BINDSTATUS_CACHEFILENAMEAVAILABLE, cache_file);
|
||||
}else {
|
||||
FIXME("Could not get cache file\n");
|
||||
}
|
||||
}
|
||||
|
||||
protocol->flags |= FLAG_FIRST_CONTINUE_COMPLETE;
|
||||
}
|
||||
|
||||
if(data->pData >= (LPVOID)BINDSTATUS_DOWNLOADINGDATA) {
|
||||
BOOL res;
|
||||
|
||||
/* InternetQueryDataAvailable may immediately fork and perform its asynchronous
|
||||
* read, so clear the flag _before_ calling so it does not incorrectly get cleared
|
||||
* after the status callback is called */
|
||||
protocol->flags &= ~FLAG_REQUEST_COMPLETE;
|
||||
res = InternetQueryDataAvailable(protocol->request, &protocol->available_bytes, 0, 0);
|
||||
if(res) {
|
||||
protocol->flags |= FLAG_REQUEST_COMPLETE;
|
||||
report_data(protocol);
|
||||
}else if(GetLastError() != ERROR_IO_PENDING) {
|
||||
protocol->flags |= FLAG_REQUEST_COMPLETE;
|
||||
WARN("InternetQueryDataAvailable failed: %d\n", GetLastError());
|
||||
report_result(protocol, INET_E_DATA_NOT_AVAILABLE);
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT protocol_read(Protocol *protocol, void *buf, ULONG size, ULONG *read_ret)
|
||||
{
|
||||
ULONG read = 0;
|
||||
|
|
|
@ -103,9 +103,11 @@ typedef struct {
|
|||
} Protocol;
|
||||
|
||||
struct ProtocolVtbl {
|
||||
HRESULT (*start_downloading)(Protocol*);
|
||||
void (*close_connection)(Protocol*);
|
||||
};
|
||||
|
||||
HRESULT protocol_continue(Protocol*,PROTOCOLDATA*);
|
||||
HRESULT protocol_read(Protocol*,void*,ULONG,ULONG*);
|
||||
HRESULT protocol_lock_request(Protocol*);
|
||||
HRESULT protocol_unlock_request(Protocol*);
|
||||
|
|
Loading…
Reference in New Issue