Source code
Revision control
Copy as Markdown
Other Tools
// 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 <assert.h>↩
#include <stdio.h>↩
#include <Windows.h>↩
#include <WinInet.h>↩
↩
#include <vector>↩
↩
#include "tools/windows/converter_exe/http_download.h"↩
#include "tools/windows/converter_exe/winhttp_client.h"↩
#include "tools/windows/converter_exe/wininet_client.h"↩
↩
namespace crash {↩
static const std::vector<char>::size_type kVectorChunkSize = 4096; // 4 KB↩
↩
using std::vector;↩
↩
// Class that atuo closes the contained HttpHandle when the object↩
// goes out of scope.↩
class AutoHttpHandle {↩
public:↩
AutoHttpHandle() : handle_(NULL) {}↩
explicit AutoHttpHandle(HttpHandle handle) : handle_(handle) {}↩
~AutoHttpHandle() {↩
if (handle_) {↩
InternetCloseHandle(handle_);↩
}↩
}↩
↩
HttpHandle get() { return handle_; }↩
HttpHandle* get_handle_addr () { return &handle_; }↩
↩
private:↩
HttpHandle handle_;↩
};↩
↩
// Template class for auto releasing the contained pointer when↩
// the object goes out of scope.↩
template<typename T>↩
class AutoPtr {↩
public:↩
explicit AutoPtr(T* ptr) : ptr_(ptr) {}↩
~AutoPtr() {↩
if (ptr_) {↩
delete ptr_;↩
}↩
}↩
↩
T* get() { return ptr_; }↩
T* operator -> () { return ptr_; }↩
↩
private:↩
T* ptr_;↩
};↩
↩
// CheckParameters ensures that the parameters in |parameters| are safe for↩
// use in an HTTP URL. Returns true if they are, false if unsafe characters↩
// are present.↩
static bool CheckParameters(const map<wstring, wstring> *parameters) {↩
for (map<wstring, wstring>::const_iterator iterator = parameters->begin();↩
iterator != parameters->end();↩
++iterator) {↩
const wstring &key = iterator->first;↩
if (key.empty()) {↩
// Disallow empty parameter names.↩
return false;↩
}↩
for (unsigned int i = 0; i < key.size(); ++i) {↩
wchar_t c = key[i];↩
if (c < 32 || c == '"' || c == '?' || c == '&' || c > 127) {↩
return false;↩
}↩
}↩
↩
const wstring &value = iterator->second;↩
for (unsigned int i = 0; i < value.size(); ++i) {↩
wchar_t c = value[i];↩
if (c < 32 || c == '"' || c == '?' || c == '&' || c > 127) {↩
return false;↩
}↩
}↩
}↩
↩
return true;↩
}↩
↩
HttpClient* HTTPDownload::CreateHttpClient(const wchar_t* url) {↩
const TCHAR* kHttpApiPolicyEnvironmentVariable = TEXT("USE_WINHTTP");↩
TCHAR buffer[2] = {0};↩
HttpClient* http_client = NULL;↩
↩
if (::GetEnvironmentVariable(kHttpApiPolicyEnvironmentVariable,↩
buffer,↩
sizeof(buffer)/sizeof(buffer[0])) > 0) {↩
fprintf(stdout,↩
"Environment variable [%ws] is set, use WinHttp\n",↩
kHttpApiPolicyEnvironmentVariable);↩
http_client = CreateWinHttpClient(url);↩
if (http_client == NULL) {↩
fprintf(stderr, "WinHttpClient not created, Is the protocol HTTPS? "↩
"Fall back to WinInet API.\n");↩
}↩
} else {↩
fprintf(stderr,↩
"Environment variable [%ws] is NOT set, use WinInet API\n",↩
kHttpApiPolicyEnvironmentVariable);↩
}↩
↩
if (http_client == NULL) {↩
return CreateWinInetClient(url);↩
}↩
↩
return http_client;↩
}↩
↩
// static↩
bool HTTPDownload::Download(const wstring &url,↩
const map<wstring, wstring> *parameters,↩
string *content, int *status_code) {↩
assert(content);↩
AutoPtr<HttpClient> http_client(CreateHttpClient(url.c_str()));↩
↩
if (!http_client.get()) {↩
fprintf(stderr, "Failed to create any http client.\n");↩
return false;↩
}↩
↩
if (status_code) {↩
*status_code = 0;↩
}↩
↩
wchar_t scheme[16] = {0};↩
wchar_t host[256] = {0};↩
wchar_t path[256] = {0};↩
int port = 0;↩
if (!http_client->CrackUrl(url.c_str(),↩
0,↩
scheme,↩
sizeof(scheme)/sizeof(scheme[0]),↩
host,↩
sizeof(host)/sizeof(host[0]),↩
path,↩
sizeof(path)/sizeof(path[0]),↩
&port)) {↩
fprintf(stderr,↩
"HTTPDownload::Download: InternetCrackUrl: error %lu for %ws\n",↩
GetLastError(), url.c_str());↩
return false;↩
}↩
↩
bool secure = false;↩
if (_wcsicmp(scheme, L"https") == 0) {↩
secure = true;↩
} else if (wcscmp(scheme, L"http") != 0) {↩
fprintf(stderr,↩
"HTTPDownload::Download: scheme must be http or https for %ws\n",↩
url.c_str());↩
return false;↩
}↩
↩
AutoHttpHandle internet;↩
if (!http_client->Open(NULL, // user agent↩
HttpClient::ACCESS_TYPE_PRECONFIG,↩
NULL, // proxy name↩
NULL, // proxy bypass↩
internet.get_handle_addr())) {↩
fprintf(stderr,↩
"HTTPDownload::Download: Open: error %lu for %ws\n",↩
GetLastError(), url.c_str());↩
return false;↩
}↩
↩
AutoHttpHandle connection;↩
if (!http_client->Connect(internet.get(),↩
host,↩
port,↩
connection.get_handle_addr())) {↩
fprintf(stderr,↩
"HTTPDownload::Download: InternetConnect: error %lu for %ws\n",↩
GetLastError(), url.c_str());↩
return false;↩
}↩
↩
wstring request_string = path;↩
if (parameters) {↩
// TODO(mmentovai): escape bad characters in parameters instead of↩
// forbidding them.↩
if (!CheckParameters(parameters)) {↩
fprintf(stderr,↩
"HTTPDownload::Download: invalid characters in parameters\n");↩
return false;↩
}↩
↩
bool added_parameter = false;↩
for (map<wstring, wstring>::const_iterator iterator = parameters->begin();↩
iterator != parameters->end();↩
++iterator) {↩
request_string.append(added_parameter ? L"&" : L"?");↩
request_string.append(iterator->first);↩
request_string.append(L"=");↩
request_string.append(iterator->second);↩
added_parameter = true;↩
}↩
}↩
↩
AutoHttpHandle request;↩
if (!http_client->OpenRequest(connection.get(),↩
L"GET",↩
request_string.c_str(),↩
NULL, // version↩
NULL, // referer↩
secure,↩
request.get_handle_addr())) {↩
fprintf(stderr,↩
"HttpClient::OpenRequest: error %lu for %ws, request: %ws\n",↩
GetLastError(), url.c_str(), request_string.c_str());↩
return false;↩
}↩
↩
if (!http_client->SendRequest(request.get(), NULL, 0)) {↩
fprintf(stderr,↩
"HttpClient::SendRequest: error %lu for %ws\n",↩
GetLastError(), url.c_str());↩
return false;↩
}↩
↩
if (!http_client->ReceiveResponse(request.get())) {↩
fprintf(stderr,↩
"HttpClient::ReceiveResponse: error %lu for %ws\n",↩
GetLastError(), url.c_str());↩
return false;↩
}↩
↩
int http_status = 0;↩
if (!http_client->GetHttpStatusCode(request.get(), &http_status)) {↩
fprintf(stderr,↩
"HttpClient::GetHttpStatusCode: error %lu for %ws\n",↩
GetLastError(), url.c_str());↩
return false;↩
}↩
if (http_status != 200) {↩
fprintf(stderr,↩
"HTTPDownload::Download: HTTP status code %d for %ws\n",↩
http_status, url.c_str());↩
return false;↩
}↩
↩
DWORD content_length = 0;↩
vector<char>::size_type buffer_size = 0;↩
http_client->GetContentLength(request.get(), &content_length);↩
if (content_length == HttpClient::kUnknownContentLength) {↩
buffer_size = kVectorChunkSize;↩
} else {↩
buffer_size = content_length;↩
}↩
↩
if (content_length != 0) {↩
vector<char> response_buffer = vector<char>(buffer_size+1);↩
DWORD size_read;↩
DWORD total_read = 0;↩
bool read_result;↩
do {↩
if (content_length == HttpClient::kUnknownContentLength↩
&& buffer_size == total_read) {↩
// The content length wasn't specified in the response header, so we↩
// have to keep growing the buffer until we're done reading.↩
buffer_size += kVectorChunkSize;↩
response_buffer.resize(buffer_size);↩
}↩
read_result = !!http_client->ReadData(↩
request.get(),↩
&response_buffer[total_read],↩
static_cast<DWORD>(buffer_size) - total_read,↩
&size_read);↩
total_read += size_read;↩
} while (read_result && (size_read != 0));↩
↩
if (!read_result) {↩
fprintf(stderr,↩
"HttpClient::ReadData: error %lu for %ws\n",↩
GetLastError(),↩
url.c_str());↩
return false;↩
} else if (size_read != 0) {↩
fprintf(stderr,↩
"HttpClient::ReadData: error %lu/%lu for %ws\n",↩
total_read,↩
content_length,↩
url.c_str());↩
return false;↩
}↩
content->assign(&response_buffer[0], total_read);↩
} else {↩
content->clear();↩
}↩
return true;↩
}↩
↩
} // namespace crash↩