blob: 986a66ff436cbbad8b17ba539fb16e6990bbd8e6 [file] [log] [blame]
// Copyright 2019 Google Inc. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "tools/windows/converter_exe/wininet_client.h"
#include <assert.h>
#include <stdlib.h>
#include <windows.h>
#include <wininet.h>
namespace crash {
namespace internal {
// This class implements HttpClient based on WinInet APIs.
class WinInetClient : public HttpClient {
public:
virtual ~WinInetClient() {}
virtual bool CrackUrl(const TCHAR* url,
DWORD flags,
TCHAR* scheme,
size_t scheme_buffer_length,
TCHAR* host,
size_t host_buffer_length,
TCHAR* uri,
size_t uri_buffer_length,
int* port) const;
virtual bool Open(const TCHAR* user_agent,
DWORD access_type,
const TCHAR* proxy_name,
const TCHAR* proxy_bypass,
HttpHandle* session_handle) const;
virtual bool Connect(HttpHandle session_handle,
const TCHAR* server,
int port,
HttpHandle* connection_handle) const;
virtual bool OpenRequest(HttpHandle connection_handle,
const TCHAR* verb,
const TCHAR* uri,
const TCHAR* version,
const TCHAR* referrer,
bool is_secure,
HttpHandle* request_handle) const;
virtual bool SendRequest(HttpHandle request_handle,
const TCHAR* headers,
DWORD headers_length) const;
virtual bool ReceiveResponse(HttpHandle request_handle) const;
virtual bool GetHttpStatusCode(HttpHandle request_handle,
int* status_code) const;
virtual bool GetContentLength(HttpHandle request_handle,
DWORD* content_length) const;
virtual bool ReadData(HttpHandle request_handle,
void* buffer,
DWORD buffer_length,
DWORD* bytes_read) const;
virtual bool Close(HttpHandle handle) const;
private:
static DWORD MapAccessType(DWORD access_type);
static HINTERNET ToHINTERNET(HttpHandle handle);
static HttpHandle FromHINTERNET(HINTERNET handle);
};
bool WinInetClient::CrackUrl(const TCHAR* url,
DWORD flags,
TCHAR* scheme,
size_t scheme_buffer_length,
TCHAR* host,
size_t host_buffer_length,
TCHAR* uri,
size_t uri_buffer_length,
int* port) const {
assert(url);
assert(scheme);
assert(host);
assert(uri);
assert(port);
URL_COMPONENTS url_comp = {0};
url_comp.dwStructSize = sizeof(url_comp);
url_comp.lpszScheme = scheme;
url_comp.dwSchemeLength = static_cast<DWORD>(scheme_buffer_length);
url_comp.lpszHostName = host;
url_comp.dwHostNameLength = static_cast<DWORD>(host_buffer_length);
url_comp.lpszUrlPath = uri;
url_comp.dwUrlPathLength = static_cast<DWORD>(uri_buffer_length);
bool result = !!::InternetCrackUrl(url, 0, flags, &url_comp);
if (result) {
*port = static_cast<int>(url_comp.nPort);
}
return result;
}
bool WinInetClient::Open(const TCHAR* user_agent,
DWORD access_type,
const TCHAR* proxy_name,
const TCHAR* proxy_bypass,
HttpHandle* session_handle) const {
*session_handle = FromHINTERNET(::InternetOpen(user_agent,
MapAccessType(access_type),
proxy_name,
proxy_bypass,
0));
return !!(*session_handle);
}
bool WinInetClient::Connect(HttpHandle session_handle,
const TCHAR* server,
int port,
HttpHandle* connection_handle) const {
assert(server);
// Uses NULL user name and password to connect. Always uses http service.
*connection_handle = FromHINTERNET(::InternetConnect(
ToHINTERNET(session_handle),
server,
static_cast<INTERNET_PORT>(port),
NULL,
NULL,
INTERNET_SERVICE_HTTP,
0,
0));
return !!(*connection_handle);
}
bool WinInetClient::OpenRequest(HttpHandle connection_handle,
const TCHAR* verb,
const TCHAR* uri,
const TCHAR* version,
const TCHAR* referrer,
bool is_secure,
HttpHandle* request_handle) const {
assert(connection_handle);
assert(verb);
assert(uri);
*request_handle = FromHINTERNET(::HttpOpenRequest(
ToHINTERNET(connection_handle),
verb,
uri,
version,
referrer,
NULL,
is_secure ? INTERNET_FLAG_SECURE : 0,
NULL));
return !!(*request_handle);
}
bool WinInetClient::SendRequest(HttpHandle request_handle,
const TCHAR* headers,
DWORD headers_length) const {
assert(request_handle);
return !!::HttpSendRequest(ToHINTERNET(request_handle),
headers,
headers_length,
NULL,
0);
}
bool WinInetClient::ReceiveResponse(HttpHandle) const {
return true;
}
bool WinInetClient::GetHttpStatusCode(HttpHandle request_handle,
int* status_code) const {
assert(request_handle);
TCHAR http_status_string[4] = {0};
DWORD http_status_string_size = sizeof(http_status_string);
if (!::HttpQueryInfo(ToHINTERNET(request_handle),
HTTP_QUERY_STATUS_CODE,
static_cast<void*>(&http_status_string),
&http_status_string_size,
0)) {
return false;
}
*status_code = _tcstol(http_status_string, NULL, 10);
return true;
}
bool WinInetClient::GetContentLength(HttpHandle request_handle,
DWORD* content_length) const {
assert(request_handle);
assert(content_length);
TCHAR content_length_string[11];
DWORD content_length_string_size = sizeof(content_length_string);
if (!::HttpQueryInfo(ToHINTERNET(request_handle),
HTTP_QUERY_CONTENT_LENGTH,
static_cast<void*>(&content_length_string),
&content_length_string_size,
0)) {
*content_length = kUnknownContentLength;
} else {
*content_length = wcstol(content_length_string, NULL, 10);
}
return true;
}
bool WinInetClient::ReadData(HttpHandle request_handle,
void* buffer,
DWORD buffer_length,
DWORD* bytes_read) const {
assert(request_handle);
assert(buffer);
assert(bytes_read);
DWORD bytes_read_local = 0;
if (!::InternetReadFile(ToHINTERNET(request_handle),
buffer,
buffer_length,
&bytes_read_local)) {
return false;
}
*bytes_read = bytes_read_local;
return true;
}
bool WinInetClient::Close(HttpHandle handle) const {
assert(handle);
return !!::InternetCloseHandle(ToHINTERNET(handle));
}
DWORD WinInetClient::MapAccessType(DWORD access_type) {
switch (static_cast<AccessType>(access_type)) {
case ACCESS_TYPE_PRECONFIG:
default:
return INTERNET_OPEN_TYPE_PRECONFIG;
case ACCESS_TYPE_DIRECT:
return INTERNET_OPEN_TYPE_DIRECT;
case ACCESS_TYPE_PROXY:
return INTERNET_OPEN_TYPE_PROXY;
}
}
HINTERNET WinInetClient::ToHINTERNET(HttpHandle handle) {
return static_cast<HINTERNET>(handle);
}
HttpHandle WinInetClient::FromHINTERNET(HINTERNET handle) {
return static_cast<HttpHandle>(handle);
}
} // namespace internal
HttpClient* CreateWinInetClient(const TCHAR*) {
return new internal::WinInetClient();
}
} // namespace crash