Port new symbol upload API to Windows symupload tool.
- CL for Linux change, including new documentation for API, at: https://chromium-review.googlesource.com/c/breakpad/breakpad/+/1422400/3 Change-Id: I579744fec74c64757b8bc31de63d7a07ef9a0f1f Reviewed-on: https://chromium-review.googlesource.com/c/breakpad/breakpad/+/1487982 Reviewed-by: Ivan Penkov <ivanpe@chromium.org>
This commit is contained in:
parent
756daa536a
commit
548ca6e382
6 changed files with 888 additions and 420 deletions
|
@ -84,6 +84,8 @@
|
|||
'pdb_source_line_writer.h',
|
||||
'string_utils.cc',
|
||||
'string_utils-inl.h',
|
||||
'symbol_collector_client.cc',
|
||||
'symbol_collector_client.h',
|
||||
],
|
||||
'dependencies': [
|
||||
'dia_sdk',
|
||||
|
|
|
@ -33,20 +33,24 @@
|
|||
#pragma warning(disable:4530)
|
||||
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
#include "common/windows/string_utils-inl.h"
|
||||
|
||||
#include "common/windows/http_upload.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
namespace {
|
||||
using std::string;
|
||||
using std::wstring;
|
||||
using std::map;
|
||||
using std::vector;
|
||||
using std::ifstream;
|
||||
using std::ios;
|
||||
|
||||
using std::ifstream;
|
||||
using std::ios;
|
||||
const wchar_t kUserAgent[] = L"Breakpad/1.0 (Windows)";
|
||||
|
||||
static const wchar_t kUserAgent[] = L"Breakpad/1.0 (Windows)";
|
||||
|
||||
// Helper class which closes an internet handle when it goes away
|
||||
class HTTPUpload::AutoInternetHandle {
|
||||
// Helper class which closes an internet handle when it goes away
|
||||
class AutoInternetHandle {
|
||||
public:
|
||||
explicit AutoInternetHandle(HINTERNET handle) : handle_(handle) {}
|
||||
~AutoInternetHandle() {
|
||||
|
@ -59,269 +63,9 @@ class HTTPUpload::AutoInternetHandle {
|
|||
|
||||
private:
|
||||
HINTERNET handle_;
|
||||
};
|
||||
};
|
||||
|
||||
// static
|
||||
bool HTTPUpload::SendRequest(const wstring &url,
|
||||
const map<wstring, wstring> ¶meters,
|
||||
const map<wstring, wstring> &files,
|
||||
int *timeout,
|
||||
wstring *response_body,
|
||||
int *response_code) {
|
||||
if (response_code) {
|
||||
*response_code = 0;
|
||||
}
|
||||
|
||||
// TODO(bryner): support non-ASCII parameter names
|
||||
if (!CheckParameters(parameters)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Break up the URL and make sure we can handle it
|
||||
wchar_t scheme[16], host[256], path[256];
|
||||
URL_COMPONENTS components;
|
||||
memset(&components, 0, sizeof(components));
|
||||
components.dwStructSize = sizeof(components);
|
||||
components.lpszScheme = scheme;
|
||||
components.dwSchemeLength = sizeof(scheme) / sizeof(scheme[0]);
|
||||
components.lpszHostName = host;
|
||||
components.dwHostNameLength = sizeof(host) / sizeof(host[0]);
|
||||
components.lpszUrlPath = path;
|
||||
components.dwUrlPathLength = sizeof(path) / sizeof(path[0]);
|
||||
if (!InternetCrackUrl(url.c_str(), static_cast<DWORD>(url.size()),
|
||||
0, &components)) {
|
||||
return false;
|
||||
}
|
||||
bool secure = false;
|
||||
if (wcscmp(scheme, L"https") == 0) {
|
||||
secure = true;
|
||||
} else if (wcscmp(scheme, L"http") != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AutoInternetHandle internet(InternetOpen(kUserAgent,
|
||||
INTERNET_OPEN_TYPE_PRECONFIG,
|
||||
NULL, // proxy name
|
||||
NULL, // proxy bypass
|
||||
0)); // flags
|
||||
if (!internet.get()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AutoInternetHandle connection(InternetConnect(internet.get(),
|
||||
host,
|
||||
components.nPort,
|
||||
NULL, // user name
|
||||
NULL, // password
|
||||
INTERNET_SERVICE_HTTP,
|
||||
0, // flags
|
||||
NULL)); // context
|
||||
if (!connection.get()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD http_open_flags = secure ? INTERNET_FLAG_SECURE : 0;
|
||||
http_open_flags |= INTERNET_FLAG_NO_COOKIES;
|
||||
AutoInternetHandle request(HttpOpenRequest(connection.get(),
|
||||
L"POST",
|
||||
path,
|
||||
NULL, // version
|
||||
NULL, // referer
|
||||
NULL, // agent type
|
||||
http_open_flags,
|
||||
NULL)); // context
|
||||
if (!request.get()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
wstring boundary = GenerateMultipartBoundary();
|
||||
wstring content_type_header = GenerateRequestHeader(boundary);
|
||||
HttpAddRequestHeaders(request.get(),
|
||||
content_type_header.c_str(),
|
||||
static_cast<DWORD>(-1),
|
||||
HTTP_ADDREQ_FLAG_ADD);
|
||||
|
||||
string request_body;
|
||||
if (!GenerateRequestBody(parameters, files, boundary, &request_body)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (timeout) {
|
||||
if (!InternetSetOption(request.get(),
|
||||
INTERNET_OPTION_SEND_TIMEOUT,
|
||||
timeout,
|
||||
sizeof(*timeout))) {
|
||||
fwprintf(stderr, L"Could not unset send timeout, continuing...\n");
|
||||
}
|
||||
|
||||
if (!InternetSetOption(request.get(),
|
||||
INTERNET_OPTION_RECEIVE_TIMEOUT,
|
||||
timeout,
|
||||
sizeof(*timeout))) {
|
||||
fwprintf(stderr, L"Could not unset receive timeout, continuing...\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (!HttpSendRequest(request.get(), NULL, 0,
|
||||
const_cast<char *>(request_body.data()),
|
||||
static_cast<DWORD>(request_body.size()))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The server indicates a successful upload with HTTP status 200.
|
||||
wchar_t http_status[4];
|
||||
DWORD http_status_size = sizeof(http_status);
|
||||
if (!HttpQueryInfo(request.get(), HTTP_QUERY_STATUS_CODE,
|
||||
static_cast<LPVOID>(&http_status), &http_status_size,
|
||||
0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int http_response = wcstol(http_status, NULL, 10);
|
||||
if (response_code) {
|
||||
*response_code = http_response;
|
||||
}
|
||||
|
||||
bool result = (http_response == 200);
|
||||
|
||||
if (result) {
|
||||
result = ReadResponse(request.get(), response_body);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// static
|
||||
bool HTTPUpload::ReadResponse(HINTERNET request, wstring *response) {
|
||||
bool has_content_length_header = false;
|
||||
wchar_t content_length[32];
|
||||
DWORD content_length_size = sizeof(content_length);
|
||||
DWORD claimed_size = 0;
|
||||
string response_body;
|
||||
|
||||
if (HttpQueryInfo(request, HTTP_QUERY_CONTENT_LENGTH,
|
||||
static_cast<LPVOID>(&content_length),
|
||||
&content_length_size, 0)) {
|
||||
has_content_length_header = true;
|
||||
claimed_size = wcstol(content_length, NULL, 10);
|
||||
response_body.reserve(claimed_size);
|
||||
}
|
||||
|
||||
|
||||
DWORD bytes_available;
|
||||
DWORD total_read = 0;
|
||||
BOOL return_code;
|
||||
|
||||
while (((return_code = InternetQueryDataAvailable(request, &bytes_available,
|
||||
0, 0)) != 0) && bytes_available > 0) {
|
||||
vector<char> response_buffer(bytes_available);
|
||||
DWORD size_read;
|
||||
|
||||
return_code = InternetReadFile(request,
|
||||
&response_buffer[0],
|
||||
bytes_available, &size_read);
|
||||
|
||||
if (return_code && size_read > 0) {
|
||||
total_read += size_read;
|
||||
response_body.append(&response_buffer[0], size_read);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool succeeded = return_code && (!has_content_length_header ||
|
||||
(total_read == claimed_size));
|
||||
if (succeeded && response) {
|
||||
*response = UTF8ToWide(response_body);
|
||||
}
|
||||
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
// static
|
||||
wstring HTTPUpload::GenerateMultipartBoundary() {
|
||||
// The boundary has 27 '-' characters followed by 16 hex digits
|
||||
static const wchar_t kBoundaryPrefix[] = L"---------------------------";
|
||||
static const int kBoundaryLength = 27 + 16 + 1;
|
||||
|
||||
// Generate some random numbers to fill out the boundary
|
||||
int r0 = rand();
|
||||
int r1 = rand();
|
||||
|
||||
wchar_t temp[kBoundaryLength];
|
||||
swprintf(temp, kBoundaryLength, L"%s%08X%08X", kBoundaryPrefix, r0, r1);
|
||||
|
||||
// remove when VC++7.1 is no longer supported
|
||||
temp[kBoundaryLength - 1] = L'\0';
|
||||
|
||||
return wstring(temp);
|
||||
}
|
||||
|
||||
// static
|
||||
wstring HTTPUpload::GenerateRequestHeader(const wstring &boundary) {
|
||||
wstring header = L"Content-Type: multipart/form-data; boundary=";
|
||||
header += boundary;
|
||||
return header;
|
||||
}
|
||||
|
||||
// static
|
||||
bool HTTPUpload::GenerateRequestBody(const map<wstring, wstring> ¶meters,
|
||||
const map<wstring, wstring> &files,
|
||||
const wstring &boundary,
|
||||
string *request_body) {
|
||||
string boundary_str = WideToUTF8(boundary);
|
||||
if (boundary_str.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
request_body->clear();
|
||||
|
||||
// Append each of the parameter pairs as a form-data part
|
||||
for (map<wstring, wstring>::const_iterator pos = parameters.begin();
|
||||
pos != parameters.end(); ++pos) {
|
||||
request_body->append("--" + boundary_str + "\r\n");
|
||||
request_body->append("Content-Disposition: form-data; name=\"" +
|
||||
WideToUTF8(pos->first) + "\"\r\n\r\n" +
|
||||
WideToUTF8(pos->second) + "\r\n");
|
||||
}
|
||||
|
||||
for (map<wstring, wstring>::const_iterator pos = files.begin();
|
||||
pos != files.end(); ++pos) {
|
||||
vector<char> contents;
|
||||
if (!GetFileContents(pos->second, &contents)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now append the upload files as a binary (octet-stream) part
|
||||
string filename_utf8 = WideToUTF8(pos->second);
|
||||
if (filename_utf8.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string file_part_name_utf8 = WideToUTF8(pos->first);
|
||||
if (file_part_name_utf8.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
request_body->append("--" + boundary_str + "\r\n");
|
||||
request_body->append("Content-Disposition: form-data; "
|
||||
"name=\"" + file_part_name_utf8 + "\"; "
|
||||
"filename=\"" + filename_utf8 + "\"\r\n");
|
||||
request_body->append("Content-Type: application/octet-stream\r\n");
|
||||
request_body->append("\r\n");
|
||||
|
||||
if (!contents.empty()) {
|
||||
request_body->append(&(contents[0]), contents.size());
|
||||
}
|
||||
request_body->append("\r\n");
|
||||
}
|
||||
request_body->append("--" + boundary_str + "--\r\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool HTTPUpload::GetFileContents(const wstring &filename,
|
||||
vector<char> *contents) {
|
||||
bool GetFileContents(const wstring &filename, vector<char> *contents) {
|
||||
bool rv = false;
|
||||
// The "open" method on pre-MSVC8 ifstream implementations doesn't accept a
|
||||
// wchar_t* filename, so use _wfopen directly in that case. For VC8 and
|
||||
|
@ -353,10 +97,9 @@ bool HTTPUpload::GetFileContents(const wstring &filename,
|
|||
file.close();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
wstring HTTPUpload::UTF8ToWide(const string &utf8) {
|
||||
wstring UTF8ToWide(const string &utf8) {
|
||||
if (utf8.length() == 0) {
|
||||
return wstring();
|
||||
}
|
||||
|
@ -374,10 +117,9 @@ wstring HTTPUpload::UTF8ToWide(const string &utf8) {
|
|||
wstring result(buf);
|
||||
delete[] buf;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
string HTTPUpload::WideToMBCP(const wstring &wide, unsigned int cp) {
|
||||
string WideToMBCP(const wstring &wide, unsigned int cp) {
|
||||
if (wide.length() == 0) {
|
||||
return string();
|
||||
}
|
||||
|
@ -397,10 +139,9 @@ string HTTPUpload::WideToMBCP(const wstring &wide, unsigned int cp) {
|
|||
string result(buf);
|
||||
delete[] buf;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
bool HTTPUpload::CheckParameters(const map<wstring, wstring> ¶meters) {
|
||||
bool CheckParameters(const map<wstring, wstring> ¶meters) {
|
||||
for (map<wstring, wstring>::const_iterator pos = parameters.begin();
|
||||
pos != parameters.end(); ++pos) {
|
||||
const wstring &str = pos->first;
|
||||
|
@ -415,6 +156,355 @@ bool HTTPUpload::CheckParameters(const map<wstring, wstring> ¶meters) {
|
|||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Converts a UTF16 string to UTF8.
|
||||
string WideToUTF8(const wstring &wide) {
|
||||
return WideToMBCP(wide, CP_UTF8);
|
||||
}
|
||||
|
||||
bool ReadResponse(HINTERNET request, wstring *response) {
|
||||
bool has_content_length_header = false;
|
||||
wchar_t content_length[32];
|
||||
DWORD content_length_size = sizeof(content_length);
|
||||
DWORD claimed_size = 0;
|
||||
string response_body;
|
||||
|
||||
if (HttpQueryInfo(request, HTTP_QUERY_CONTENT_LENGTH,
|
||||
static_cast<LPVOID>(&content_length),
|
||||
&content_length_size, 0)) {
|
||||
has_content_length_header = true;
|
||||
claimed_size = wcstol(content_length, NULL, 10);
|
||||
response_body.reserve(claimed_size);
|
||||
}
|
||||
|
||||
DWORD bytes_available;
|
||||
DWORD total_read = 0;
|
||||
BOOL return_code;
|
||||
|
||||
while (((return_code = InternetQueryDataAvailable(request, &bytes_available,
|
||||
0, 0)) != 0) && bytes_available > 0) {
|
||||
vector<char> response_buffer(bytes_available);
|
||||
DWORD size_read;
|
||||
|
||||
return_code = InternetReadFile(request,
|
||||
&response_buffer[0],
|
||||
bytes_available, &size_read);
|
||||
|
||||
if (return_code && size_read > 0) {
|
||||
total_read += size_read;
|
||||
response_body.append(&response_buffer[0], size_read);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool succeeded = return_code && (!has_content_length_header ||
|
||||
(total_read == claimed_size));
|
||||
if (succeeded && response) {
|
||||
*response = UTF8ToWide(response_body);
|
||||
}
|
||||
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
bool SendRequestInner(
|
||||
const wstring& url,
|
||||
const wstring& http_method,
|
||||
const wstring& content_type_header,
|
||||
const string& request_body,
|
||||
int* timeout_ms,
|
||||
wstring* response_body,
|
||||
int* response_code) {
|
||||
if (response_code) {
|
||||
*response_code = 0;
|
||||
}
|
||||
|
||||
// Break up the URL and make sure we can handle it
|
||||
wchar_t scheme[16], host[256], path[1024];
|
||||
URL_COMPONENTS components;
|
||||
memset(&components, 0, sizeof(components));
|
||||
components.dwStructSize = sizeof(components);
|
||||
components.lpszScheme = scheme;
|
||||
components.dwSchemeLength = sizeof(scheme) / sizeof(scheme[0]);
|
||||
components.lpszHostName = host;
|
||||
components.dwHostNameLength = sizeof(host) / sizeof(host[0]);
|
||||
components.lpszUrlPath = path;
|
||||
components.dwUrlPathLength = sizeof(path) / sizeof(path[0]);
|
||||
if (!InternetCrackUrl(url.c_str(), static_cast<DWORD>(url.size()),
|
||||
0, &components)) {
|
||||
DWORD err = GetLastError();
|
||||
wprintf(L"%d\n", err);
|
||||
return false;
|
||||
}
|
||||
bool secure = false;
|
||||
if (wcscmp(scheme, L"https") == 0) {
|
||||
secure = true;
|
||||
}
|
||||
else if (wcscmp(scheme, L"http") != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AutoInternetHandle internet(InternetOpen(kUserAgent,
|
||||
INTERNET_OPEN_TYPE_PRECONFIG,
|
||||
NULL, // proxy name
|
||||
NULL, // proxy bypass
|
||||
0)); // flags
|
||||
if (!internet.get()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AutoInternetHandle connection(InternetConnect(internet.get(),
|
||||
host,
|
||||
components.nPort,
|
||||
NULL, // user name
|
||||
NULL, // password
|
||||
INTERNET_SERVICE_HTTP,
|
||||
0, // flags
|
||||
NULL)); // context
|
||||
if (!connection.get()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD http_open_flags = secure ? INTERNET_FLAG_SECURE : 0;
|
||||
http_open_flags |= INTERNET_FLAG_NO_COOKIES;
|
||||
AutoInternetHandle request(HttpOpenRequest(connection.get(),
|
||||
http_method.c_str(),
|
||||
path,
|
||||
NULL, // version
|
||||
NULL, // referer
|
||||
NULL, // agent type
|
||||
http_open_flags,
|
||||
NULL)); // context
|
||||
if (!request.get()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!content_type_header.empty()) {
|
||||
HttpAddRequestHeaders(request.get(),
|
||||
content_type_header.c_str(),
|
||||
static_cast<DWORD>(-1),
|
||||
HTTP_ADDREQ_FLAG_ADD);
|
||||
}
|
||||
|
||||
if (timeout_ms) {
|
||||
if (!InternetSetOption(request.get(),
|
||||
INTERNET_OPTION_SEND_TIMEOUT,
|
||||
timeout_ms,
|
||||
sizeof(*timeout_ms))) {
|
||||
fwprintf(stderr, L"Could not unset send timeout, continuing...\n");
|
||||
}
|
||||
|
||||
if (!InternetSetOption(request.get(),
|
||||
INTERNET_OPTION_RECEIVE_TIMEOUT,
|
||||
timeout_ms,
|
||||
sizeof(*timeout_ms))) {
|
||||
fwprintf(stderr, L"Could not unset receive timeout, continuing...\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (!HttpSendRequest(request.get(), NULL, 0,
|
||||
const_cast<char *>(request_body.data()),
|
||||
static_cast<DWORD>(request_body.size()))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The server indicates a successful upload with HTTP status 200.
|
||||
wchar_t http_status[4];
|
||||
DWORD http_status_size = sizeof(http_status);
|
||||
if (!HttpQueryInfo(request.get(), HTTP_QUERY_STATUS_CODE,
|
||||
static_cast<LPVOID>(&http_status), &http_status_size,
|
||||
0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int http_response = wcstol(http_status, NULL, 10);
|
||||
if (response_code) {
|
||||
*response_code = http_response;
|
||||
}
|
||||
|
||||
bool result = (http_response == 200);
|
||||
|
||||
if (result) {
|
||||
result = ReadResponse(request.get(), response_body);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
wstring GenerateMultipartBoundary() {
|
||||
// The boundary has 27 '-' characters followed by 16 hex digits
|
||||
static const wchar_t kBoundaryPrefix[] = L"---------------------------";
|
||||
static const int kBoundaryLength = 27 + 16 + 1;
|
||||
|
||||
// Generate some random numbers to fill out the boundary
|
||||
int r0 = rand();
|
||||
int r1 = rand();
|
||||
|
||||
wchar_t temp[kBoundaryLength];
|
||||
swprintf(temp, kBoundaryLength, L"%s%08X%08X", kBoundaryPrefix, r0, r1);
|
||||
|
||||
// remove when VC++7.1 is no longer supported
|
||||
temp[kBoundaryLength - 1] = L'\0';
|
||||
|
||||
return wstring(temp);
|
||||
}
|
||||
|
||||
wstring GenerateMultipartPostRequestHeader(const wstring &boundary) {
|
||||
wstring header = L"Content-Type: multipart/form-data; boundary=";
|
||||
header += boundary;
|
||||
return header;
|
||||
}
|
||||
|
||||
bool AppendFileToRequestBody(
|
||||
const wstring& file_part_name,
|
||||
const wstring& filename,
|
||||
string* request_body) {
|
||||
string file_part_name_utf8 = WideToUTF8(file_part_name);
|
||||
if (file_part_name_utf8.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string filename_utf8 = WideToUTF8(filename);
|
||||
if (filename_utf8.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
request_body->append("Content-Disposition: form-data; "
|
||||
"name=\"" + file_part_name_utf8 + "\"; "
|
||||
"filename=\"" + filename_utf8 + "\"\r\n");
|
||||
request_body->append("Content-Type: application/octet-stream\r\n");
|
||||
request_body->append("\r\n");
|
||||
|
||||
vector<char> contents;
|
||||
if (!GetFileContents(filename, &contents)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!contents.empty()) {
|
||||
request_body->append(&(contents[0]), contents.size());
|
||||
}
|
||||
request_body->append("\r\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GenerateRequestBody(const map<wstring, wstring> ¶meters,
|
||||
const map<wstring, wstring> &files,
|
||||
const wstring &boundary,
|
||||
string *request_body) {
|
||||
string boundary_str = WideToUTF8(boundary);
|
||||
if (boundary_str.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
request_body->clear();
|
||||
|
||||
// Append each of the parameter pairs as a form-data part
|
||||
for (map<wstring, wstring>::const_iterator pos = parameters.begin();
|
||||
pos != parameters.end(); ++pos) {
|
||||
request_body->append("--" + boundary_str + "\r\n");
|
||||
request_body->append("Content-Disposition: form-data; name=\"" +
|
||||
WideToUTF8(pos->first) + "\"\r\n\r\n" +
|
||||
WideToUTF8(pos->second) + "\r\n");
|
||||
}
|
||||
|
||||
// Now append each upload file as a binary (octet-stream) part
|
||||
for (map<wstring, wstring>::const_iterator pos = files.begin();
|
||||
pos != files.end(); ++pos) {
|
||||
request_body->append("--" + boundary_str + "\r\n");
|
||||
|
||||
if (!AppendFileToRequestBody(pos->first, pos->second, request_body)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
request_body->append("--" + boundary_str + "--\r\n");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
namespace google_breakpad {
|
||||
bool HTTPUpload::SendPutRequest(
|
||||
const wstring& url,
|
||||
const wstring& path,
|
||||
int* timeout_ms,
|
||||
wstring* response_body,
|
||||
int* response_code) {
|
||||
string request_body;
|
||||
if (!AppendFileToRequestBody(L"symbol_file", path, &request_body)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return SendRequestInner(
|
||||
url,
|
||||
L"PUT",
|
||||
L"",
|
||||
request_body,
|
||||
timeout_ms,
|
||||
response_body,
|
||||
response_code);
|
||||
}
|
||||
|
||||
bool HTTPUpload::SendGetRequest(
|
||||
const wstring& url,
|
||||
int* timeout_ms,
|
||||
wstring* response_body,
|
||||
int* response_code) {
|
||||
return SendRequestInner(
|
||||
url,
|
||||
L"GET",
|
||||
L"",
|
||||
"",
|
||||
timeout_ms,
|
||||
response_body,
|
||||
response_code);
|
||||
}
|
||||
|
||||
bool HTTPUpload::SendMultipartPostRequest(
|
||||
const wstring& url,
|
||||
const map<wstring, wstring>& parameters,
|
||||
const map<wstring, wstring>& files,
|
||||
int* timeout_ms,
|
||||
wstring* response_body,
|
||||
int* response_code) {
|
||||
// TODO(bryner): support non-ASCII parameter names
|
||||
if (!CheckParameters(parameters)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
wstring boundary = GenerateMultipartBoundary();
|
||||
wstring content_type_header = GenerateMultipartPostRequestHeader(boundary);
|
||||
|
||||
string request_body;
|
||||
if (!GenerateRequestBody(parameters, files, boundary, &request_body)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return SendRequestInner(
|
||||
url,
|
||||
L"POST",
|
||||
content_type_header,
|
||||
request_body,
|
||||
timeout_ms,
|
||||
response_body,
|
||||
response_code);
|
||||
}
|
||||
|
||||
bool HTTPUpload::SendSimplePostRequest(
|
||||
const wstring& url,
|
||||
const wstring& body,
|
||||
const wstring& content_type,
|
||||
int *timeout_ms,
|
||||
wstring *response_body,
|
||||
int *response_code) {
|
||||
return SendRequestInner(
|
||||
url,
|
||||
L"POST",
|
||||
content_type,
|
||||
WideToUTF8(body),
|
||||
timeout_ms,
|
||||
response_body,
|
||||
response_code);
|
||||
}
|
||||
} // namespace google_breakpad
|
||||
|
|
|
@ -42,18 +42,41 @@
|
|||
#include <wininet.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::string;
|
||||
using std::wstring;
|
||||
using std::map;
|
||||
using std::vector;
|
||||
|
||||
class HTTPUpload {
|
||||
public:
|
||||
// Sends a PUT request containing the data in |path| to the given
|
||||
// URL.
|
||||
// Only HTTP(S) URLs are currently supported. Returns true on success.
|
||||
// If the request is successful and response_body is non-NULL,
|
||||
// the response body will be returned in response_body.
|
||||
// If response_code is non-NULL, it will be set to the HTTP response code
|
||||
// received (or 0 if the request failed before getting an HTTP response).
|
||||
static bool SendPutRequest(
|
||||
const wstring& url,
|
||||
const wstring& path,
|
||||
int* timeout_ms,
|
||||
wstring* response_body,
|
||||
int* response_code);
|
||||
|
||||
// Sends a GET request to the given URL.
|
||||
// Only HTTP(S) URLs are currently supported. Returns true on success.
|
||||
// If the request is successful and response_body is non-NULL,
|
||||
// the response body will be returned in response_body.
|
||||
// If response_code is non-NULL, it will be set to the HTTP response code
|
||||
// received (or 0 if the request failed before getting an HTTP response).
|
||||
static bool SendGetRequest(
|
||||
const wstring& url,
|
||||
int* timeout_ms,
|
||||
wstring* response_body,
|
||||
int* response_code);
|
||||
|
||||
// Sends the given sets of parameters and files as a multipart POST
|
||||
// request to the given URL.
|
||||
// Each key in |files| is the name of the file part of the request
|
||||
|
@ -65,55 +88,29 @@ class HTTPUpload {
|
|||
// the response body will be returned in response_body.
|
||||
// If response_code is non-NULL, it will be set to the HTTP response code
|
||||
// received (or 0 if the request failed before getting an HTTP response).
|
||||
static bool SendRequest(const wstring &url,
|
||||
const map<wstring, wstring> ¶meters,
|
||||
const map<wstring, wstring> &files,
|
||||
int *timeout,
|
||||
static bool SendMultipartPostRequest(
|
||||
const wstring& url,
|
||||
const map<wstring, wstring>& parameters,
|
||||
const map<wstring, wstring>& files,
|
||||
int *timeout_ms,
|
||||
wstring *response_body,
|
||||
int *response_code);
|
||||
|
||||
// Sends a POST request, with the body set to |body|, to the given URL.
|
||||
// Only HTTP(S) URLs are currently supported. Returns true on success.
|
||||
// If the request is successful and response_body is non-NULL,
|
||||
// the response body will be returned in response_body.
|
||||
// If response_code is non-NULL, it will be set to the HTTP response code
|
||||
// received (or 0 if the request failed before getting an HTTP response).
|
||||
static bool SendSimplePostRequest(
|
||||
const wstring& url,
|
||||
const wstring& body,
|
||||
const wstring& content_type,
|
||||
int *timeout_ms,
|
||||
wstring *response_body,
|
||||
int *response_code);
|
||||
|
||||
private:
|
||||
class AutoInternetHandle;
|
||||
|
||||
// Retrieves the HTTP response. If NULL is passed in for response,
|
||||
// this merely checks (via the return value) that we were successfully
|
||||
// able to retrieve exactly as many bytes of content in the response as
|
||||
// were specified in the Content-Length header.
|
||||
static bool ReadResponse(HINTERNET request, wstring* response);
|
||||
|
||||
// Generates a new multipart boundary for a POST request
|
||||
static wstring GenerateMultipartBoundary();
|
||||
|
||||
// Generates a HTTP request header for a multipart form submit.
|
||||
static wstring GenerateRequestHeader(const wstring &boundary);
|
||||
|
||||
// Given a set of parameters, a set of upload files, and a file part name,
|
||||
// generates a multipart request body string with these parameters
|
||||
// and minidump contents. Returns true on success.
|
||||
static bool GenerateRequestBody(const map<wstring, wstring> ¶meters,
|
||||
const map<wstring, wstring> &files,
|
||||
const wstring &boundary,
|
||||
string *request_body);
|
||||
|
||||
// Fills the supplied vector with the contents of filename.
|
||||
static bool GetFileContents(const wstring &filename, vector<char> *contents);
|
||||
|
||||
// Converts a UTF8 string to UTF16.
|
||||
static wstring UTF8ToWide(const string &utf8);
|
||||
|
||||
// Converts a UTF16 string to UTF8.
|
||||
static string WideToUTF8(const wstring &wide) {
|
||||
return WideToMBCP(wide, CP_UTF8);
|
||||
}
|
||||
|
||||
// Converts a UTF16 string to specified code page.
|
||||
static string WideToMBCP(const wstring &wide, unsigned int cp);
|
||||
|
||||
// Checks that the given list of parameters has only printable
|
||||
// ASCII characters in the parameter name, and does not contain
|
||||
// any quote (") characters. Returns true if so.
|
||||
static bool CheckParameters(const map<wstring, wstring> ¶meters);
|
||||
|
||||
// No instances of this class should be created.
|
||||
// Disallow all constructors, destructors, and operator=.
|
||||
HTTPUpload();
|
||||
|
|
155
src/common/windows/symbol_collector_client.cc
Normal file
155
src/common/windows/symbol_collector_client.cc
Normal file
|
@ -0,0 +1,155 @@
|
|||
#include "common/windows/symbol_collector_client.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <regex>
|
||||
|
||||
#include "common/windows/http_upload.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// static
|
||||
bool SymbolCollectorClient::CreateUploadUrl(
|
||||
wstring& api_url,
|
||||
wstring& api_key,
|
||||
UploadUrlResponse *uploadUrlResponse) {
|
||||
wstring url = api_url +
|
||||
L"/v1/uploads:create"
|
||||
L"?key=" + api_key;
|
||||
wstring response;
|
||||
int response_code;
|
||||
|
||||
if (!HTTPUpload::SendSimplePostRequest(
|
||||
url,
|
||||
L"",
|
||||
L"",
|
||||
NULL,
|
||||
&response,
|
||||
&response_code)) {
|
||||
wprintf(L"Failed to create upload url.\n");
|
||||
wprintf(L"Response code: %ld\n", response_code);
|
||||
wprintf(L"Response:\n");
|
||||
wprintf(L"%s\n", response.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Note camel-case rather than underscores.
|
||||
std::wregex upload_url_regex(L"\"uploadUrl\": \"([^\"]+)\"");
|
||||
std::wregex upload_key_regex(L"\"uploadKey\": \"([^\"]+)\"");
|
||||
|
||||
std::wsmatch upload_url_match;
|
||||
if (!std::regex_search(response, upload_url_match, upload_url_regex) ||
|
||||
upload_url_match.size() != 2) {
|
||||
wprintf(L"Failed to parse create url response.");
|
||||
wprintf(L"Response:\n");
|
||||
wprintf(L"%s\n", response.c_str());
|
||||
return false;
|
||||
}
|
||||
wstring upload_url = upload_url_match[1].str();
|
||||
|
||||
std::wsmatch upload_key_match;
|
||||
if (!std::regex_search(response, upload_key_match, upload_key_regex) ||
|
||||
upload_key_match.size() != 2) {
|
||||
wprintf(L"Failed to parse create url response.");
|
||||
wprintf(L"Response:\n");
|
||||
wprintf(L"%s\n", response.c_str());
|
||||
return false;
|
||||
}
|
||||
wstring upload_key = upload_key_match[1].str();
|
||||
|
||||
uploadUrlResponse->upload_url = upload_url;
|
||||
uploadUrlResponse->upload_key = upload_key;
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
CompleteUploadResult SymbolCollectorClient::CompleteUpload(
|
||||
wstring& api_url,
|
||||
wstring& api_key,
|
||||
const wstring& upload_key,
|
||||
const wstring& debug_file,
|
||||
const wstring& debug_id) {
|
||||
wstring url = api_url +
|
||||
L"/v1/uploads/" + upload_key + L":complete"
|
||||
L"?key=" + api_key;
|
||||
wstring body =
|
||||
L"{ symbol_id: {"
|
||||
L"debug_file: \"" + debug_file + L"\", "
|
||||
L"debug_id: \"" + debug_id + L"\" "
|
||||
L"} }";
|
||||
wstring response;
|
||||
int response_code;
|
||||
|
||||
if (!HTTPUpload::SendSimplePostRequest(
|
||||
url,
|
||||
body,
|
||||
L"application/json",
|
||||
NULL,
|
||||
&response,
|
||||
&response_code)) {
|
||||
wprintf(L"Failed to complete upload.\n");
|
||||
wprintf(L"Response code: %ld\n", response_code);
|
||||
wprintf(L"Response:\n");
|
||||
wprintf(L"%s\n", response.c_str());
|
||||
return CompleteUploadResult::Error;
|
||||
}
|
||||
|
||||
std::wregex result_regex(L"\"result\": \"([^\"]+)\"");
|
||||
std::wsmatch result_match;
|
||||
if (!std::regex_search(response, result_match, result_regex) ||
|
||||
result_match.size() != 2) {
|
||||
wprintf(L"Failed to parse complete upload response.");
|
||||
wprintf(L"Response:\n");
|
||||
wprintf(L"%s\n", response.c_str());
|
||||
return CompleteUploadResult::Error;
|
||||
}
|
||||
wstring result = result_match[1].str();
|
||||
|
||||
if (result.compare(L"DUPLICATE_DATA") == 0) {
|
||||
return CompleteUploadResult::DuplicateData;
|
||||
}
|
||||
|
||||
return CompleteUploadResult::Ok;
|
||||
}
|
||||
|
||||
// static
|
||||
SymbolStatus SymbolCollectorClient::CheckSymbolStatus(
|
||||
wstring& api_url,
|
||||
wstring& api_key,
|
||||
const wstring& debug_file,
|
||||
const wstring& debug_id) {
|
||||
wstring response;
|
||||
int response_code;
|
||||
wstring url = api_url +
|
||||
L"/v1/symbols/" + debug_file + L"/" + debug_id + L":checkStatus"
|
||||
L"?key=" + api_key;
|
||||
|
||||
if (!HTTPUpload::SendGetRequest(
|
||||
url,
|
||||
NULL,
|
||||
&response,
|
||||
&response_code)) {
|
||||
wprintf(L"Failed to check symbol status.\n");
|
||||
wprintf(L"Response code: %ld\n", response_code);
|
||||
wprintf(L"Response:\n");
|
||||
wprintf(L"%s\n", response.c_str());
|
||||
return SymbolStatus::Unknown;
|
||||
}
|
||||
|
||||
std::wregex status_regex(L"\"status\": \"([^\"]+)\"");
|
||||
std::wsmatch status_match;
|
||||
if (!std::regex_search(response, status_match, status_regex) ||
|
||||
status_match.size() != 2) {
|
||||
wprintf(L"Failed to parse check symbol status response.");
|
||||
wprintf(L"Response:\n");
|
||||
wprintf(L"%s\n", response.c_str());
|
||||
return SymbolStatus::Unknown;
|
||||
}
|
||||
wstring status = status_match[1].str();
|
||||
|
||||
return (status.compare(L"FOUND") == 0) ?
|
||||
SymbolStatus::Found :
|
||||
SymbolStatus::Missing;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
89
src/common/windows/symbol_collector_client.h
Normal file
89
src/common/windows/symbol_collector_client.h
Normal file
|
@ -0,0 +1,89 @@
|
|||
// Copyright (c) 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.
|
||||
|
||||
#ifndef COMMON_WINDOWS_SYMBOL_COLLECTOR_CLIENT_H_
|
||||
#define COMMON_WINDOWS_SYMBOL_COLLECTOR_CLIENT_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::wstring;
|
||||
|
||||
struct UploadUrlResponse {
|
||||
// URL at which to HTTP PUT symbol file.
|
||||
wstring upload_url;
|
||||
// Unique key used to complete upload of symbol file.
|
||||
wstring upload_key;
|
||||
};
|
||||
|
||||
enum SymbolStatus {
|
||||
Found,
|
||||
Missing,
|
||||
Unknown
|
||||
};
|
||||
|
||||
enum CompleteUploadResult {
|
||||
Ok,
|
||||
DuplicateData,
|
||||
Error
|
||||
};
|
||||
|
||||
// Client to interact with sym-upload-v2 API server via HTTP/REST.
|
||||
class SymbolCollectorClient {
|
||||
public:
|
||||
// Returns a URL at which a symbol file can be HTTP PUT without
|
||||
// authentication, along with an upload key that can be used to
|
||||
// complete the upload process with CompleteUpload.
|
||||
static bool CreateUploadUrl(
|
||||
wstring& api_url,
|
||||
wstring& api_key,
|
||||
UploadUrlResponse *uploadUrlResponse);
|
||||
|
||||
// Notify the API that symbol file upload is finished and its contents
|
||||
// are ready to be read and/or used for further processing.
|
||||
static CompleteUploadResult CompleteUpload(
|
||||
wstring& api_url,
|
||||
wstring& api_key,
|
||||
const wstring& upload_key,
|
||||
const wstring& debug_file,
|
||||
const wstring& debug_id);
|
||||
|
||||
// Returns whether or not a symbol file corresponding to the debug_file/
|
||||
// debug_id pair is already present in symbol storage.
|
||||
static SymbolStatus CheckSymbolStatus(
|
||||
wstring& api_url,
|
||||
wstring& api_key,
|
||||
const wstring& debug_file,
|
||||
const wstring& debug_id);
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // COMMON_WINDOWS_SYMBOL_COLLECTOR_CLIENT_H_
|
|
@ -56,12 +56,17 @@
|
|||
|
||||
#include "common/windows/http_upload.h"
|
||||
#include "common/windows/pdb_source_line_writer.h"
|
||||
#include "common/windows/symbol_collector_client.h"
|
||||
|
||||
using std::string;
|
||||
using std::wstring;
|
||||
using std::vector;
|
||||
using std::map;
|
||||
using google_breakpad::HTTPUpload;
|
||||
using google_breakpad::SymbolCollectorClient;
|
||||
using google_breakpad::SymbolStatus;
|
||||
using google_breakpad::UploadUrlResponse;
|
||||
using google_breakpad::CompleteUploadResult;
|
||||
using google_breakpad::PDBModuleInfo;
|
||||
using google_breakpad::PDBSourceLineWriter;
|
||||
using google_breakpad::WindowsStringUtils;
|
||||
|
@ -154,6 +159,92 @@ static bool DumpSymbolsToTempFile(const wchar_t *file,
|
|||
return writer.GetModuleInfo(pdb_info);
|
||||
}
|
||||
|
||||
static bool DoSymUploadV2(
|
||||
const wchar_t* api_url,
|
||||
const wchar_t* api_key,
|
||||
const wstring& debug_file,
|
||||
const wstring& debug_id,
|
||||
const wstring& symbol_file,
|
||||
bool force) {
|
||||
wstring url(api_url);
|
||||
wstring key(api_key);
|
||||
|
||||
if (!force) {
|
||||
SymbolStatus symbolStatus = SymbolCollectorClient::CheckSymbolStatus(
|
||||
url,
|
||||
key,
|
||||
debug_file,
|
||||
debug_id);
|
||||
if (symbolStatus == SymbolStatus::Found) {
|
||||
wprintf(L"Symbol file already exists, upload aborted."
|
||||
L" Use \"-f\" to overwrite.\n");
|
||||
return true;
|
||||
}
|
||||
else if (symbolStatus == SymbolStatus::Unknown) {
|
||||
wprintf(L"Failed to get check for existing symbol.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
UploadUrlResponse uploadUrlResponse;
|
||||
if (!SymbolCollectorClient::CreateUploadUrl(
|
||||
url,
|
||||
key,
|
||||
&uploadUrlResponse)) {
|
||||
wprintf(L"Failed to create upload URL.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
wstring signed_url = uploadUrlResponse.upload_url;
|
||||
wstring upload_key = uploadUrlResponse.upload_key;
|
||||
wstring response;
|
||||
int response_code;
|
||||
bool success = HTTPUpload::SendPutRequest(
|
||||
signed_url,
|
||||
symbol_file,
|
||||
/* timeout = */ NULL,
|
||||
&response,
|
||||
&response_code);
|
||||
if (!success) {
|
||||
wprintf(L"Failed to send symbol file.\n");
|
||||
wprintf(L"Response code: %ld\n", response_code);
|
||||
wprintf(L"Response:\n");
|
||||
wprintf(L"%s\n", response.c_str());
|
||||
return false;
|
||||
}
|
||||
else if (response_code == 0) {
|
||||
wprintf(L"Failed to send symbol file: No response code\n");
|
||||
return false;
|
||||
}
|
||||
else if (response_code != 200) {
|
||||
wprintf(L"Failed to send symbol file: Response code %ld\n", response_code);
|
||||
wprintf(L"Response:\n");
|
||||
wprintf(L"%s\n", response.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
CompleteUploadResult completeUploadResult =
|
||||
SymbolCollectorClient::CompleteUpload(
|
||||
url,
|
||||
key,
|
||||
upload_key,
|
||||
debug_file,
|
||||
debug_id);
|
||||
if (completeUploadResult == CompleteUploadResult::Error) {
|
||||
wprintf(L"Failed to complete upload.\n");
|
||||
return false;
|
||||
}
|
||||
else if (completeUploadResult == CompleteUploadResult::DuplicateData) {
|
||||
wprintf(L"Uploaded file checksum matched existing file checksum,"
|
||||
L" no change necessary.\n");
|
||||
}
|
||||
else {
|
||||
wprintf(L"Successfully sent the symbol file.\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
__declspec(noreturn) void printUsageAndExit() {
|
||||
wprintf(L"Usage:\n\n"
|
||||
L" symupload [--timeout NN] [--product product_name] ^\n"
|
||||
|
@ -166,13 +257,28 @@ __declspec(noreturn) void printUsageAndExit() {
|
|||
wprintf(L"Example:\n\n"
|
||||
L" symupload.exe --timeout 0 --product Chrome ^\n"
|
||||
L" chrome.dll http://no.free.symbol.server.for.you\n");
|
||||
wprintf(L"\n");
|
||||
wprintf(L"sym-upload-v2 usage:\n"
|
||||
L" symupload -p [-f] <file.exe|file.dll> <API-URL> <API-key>\n");
|
||||
wprintf(L"\n");
|
||||
wprintf(L"sym_upload_v2 Options:\n");
|
||||
wprintf(L" <API-URL> is the sym_upload_v2 API URL.\n");
|
||||
wprintf(L" <API-key> is a secret used to authenticate with the API.\n");
|
||||
wprintf(L" -p:\t Use sym_upload_v2 protocol.\n");
|
||||
wprintf(L" -f:\t Force symbol upload if already exists.\n");
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int wmain(int argc, wchar_t *argv[]) {
|
||||
const wchar_t *module;
|
||||
const wchar_t *product = nullptr;
|
||||
int timeout = -1;
|
||||
int currentarg = 1;
|
||||
bool use_sym_upload_v2 = false;
|
||||
bool force = false;
|
||||
const wchar_t* api_url = nullptr;
|
||||
const wchar_t* api_key = nullptr;
|
||||
while (argc > currentarg + 1) {
|
||||
if (!wcscmp(L"--timeout", argv[currentarg])) {
|
||||
timeout = _wtoi(argv[currentarg + 1]);
|
||||
|
@ -184,6 +290,16 @@ int wmain(int argc, wchar_t *argv[]) {
|
|||
currentarg += 2;
|
||||
continue;
|
||||
}
|
||||
if (!wcscmp(L"-p", argv[currentarg])) {
|
||||
use_sym_upload_v2 = true;
|
||||
++currentarg;
|
||||
continue;
|
||||
}
|
||||
if (!wcscmp(L"-f", argv[currentarg])) {
|
||||
force = true;
|
||||
++currentarg;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -200,7 +316,31 @@ int wmain(int argc, wchar_t *argv[]) {
|
|||
}
|
||||
|
||||
wstring code_file = WindowsStringUtils::GetBaseName(wstring(module));
|
||||
wstring file_version;
|
||||
// Don't make a missing version a hard error. Issue a warning, and let the
|
||||
// server decide whether to reject files without versions.
|
||||
if (!GetFileVersionString(module, &file_version)) {
|
||||
fwprintf(stderr, L"Warning: Could not get file version for %s\n", module);
|
||||
}
|
||||
|
||||
bool success = true;
|
||||
|
||||
if (use_sym_upload_v2) {
|
||||
if (argc >= currentarg + 2) {
|
||||
api_url = argv[currentarg++];
|
||||
api_key = argv[currentarg++];
|
||||
|
||||
success = DoSymUploadV2(
|
||||
api_url,
|
||||
api_key,
|
||||
pdb_info.debug_file,
|
||||
pdb_info.debug_identifier,
|
||||
symbol_file,
|
||||
force);
|
||||
} else {
|
||||
printUsageAndExit();
|
||||
}
|
||||
} else {
|
||||
map<wstring, wstring> parameters;
|
||||
parameters[L"code_file"] = code_file;
|
||||
parameters[L"debug_file"] = pdb_info.debug_file;
|
||||
|
@ -208,34 +348,28 @@ int wmain(int argc, wchar_t *argv[]) {
|
|||
parameters[L"os"] = L"windows"; // This version of symupload is Windows-only
|
||||
parameters[L"cpu"] = pdb_info.cpu;
|
||||
|
||||
map<wstring, wstring> files;
|
||||
files[L"symbol_file"] = symbol_file;
|
||||
|
||||
if (!file_version.empty()) {
|
||||
parameters[L"version"] = file_version;
|
||||
}
|
||||
|
||||
// Don't make a missing product name a hard error. Issue a warning and let
|
||||
// the server decide whether to reject files without product name.
|
||||
if (product) {
|
||||
parameters[L"product"] = product;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
fwprintf(
|
||||
stderr,
|
||||
L"Warning: No product name (flag --product) was specified for %s\n",
|
||||
module);
|
||||
}
|
||||
|
||||
// Don't make a missing version a hard error. Issue a warning, and let the
|
||||
// server decide whether to reject files without versions.
|
||||
wstring file_version;
|
||||
if (GetFileVersionString(module, &file_version)) {
|
||||
parameters[L"version"] = file_version;
|
||||
} else {
|
||||
fwprintf(stderr, L"Warning: Could not get file version for %s\n", module);
|
||||
}
|
||||
|
||||
map<wstring, wstring> files;
|
||||
files[L"symbol_file"] = symbol_file;
|
||||
|
||||
bool success = true;
|
||||
|
||||
while (currentarg < argc) {
|
||||
int response_code;
|
||||
if (!HTTPUpload::SendRequest(argv[currentarg], parameters, files,
|
||||
if (!HTTPUpload::SendMultipartPostRequest(argv[currentarg], parameters, files,
|
||||
timeout == -1 ? NULL : &timeout,
|
||||
nullptr, &response_code)) {
|
||||
success = false;
|
||||
|
@ -245,6 +379,7 @@ int wmain(int argc, wchar_t *argv[]) {
|
|||
}
|
||||
currentarg++;
|
||||
}
|
||||
}
|
||||
|
||||
_wunlink(symbol_file.c_str());
|
||||
|
||||
|
|
Loading…
Reference in a new issue