Bug 1048091 - Support for multiple upload files in Breakpad. r=ted
authorDavid Major <dmajor@mozilla.com>
Sat, 30 Aug 2014 15:49:33 +1200
changeset 202559 1be81104d7d9f46212bf621bf33754f073ea3362
parent 202558 1f81a6902e968383ea5fb80d9da4c088a7aef8af
child 202560 991b62ff5461b89bb49ad4e152286834eeebdc6f
push id48444
push userdmajor@mozilla.com
push dateSat, 30 Aug 2014 03:50:34 +0000
treeherdermozilla-inbound@1be81104d7d9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersted
bugs1048091
milestone34.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1048091 - Support for multiple upload files in Breakpad. r=ted
toolkit/crashreporter/client/crashreporter_gtk_common.cpp
toolkit/crashreporter/client/crashreporter_win.cpp
toolkit/crashreporter/google-breakpad/src/client/windows/sender/crash_report_sender.cc
toolkit/crashreporter/google-breakpad/src/client/windows/sender/crash_report_sender.h
toolkit/crashreporter/google-breakpad/src/common/linux/http_upload.cc
toolkit/crashreporter/google-breakpad/src/common/linux/http_upload.h
toolkit/crashreporter/google-breakpad/src/common/windows/http_upload.cc
toolkit/crashreporter/google-breakpad/src/common/windows/http_upload.h
--- a/toolkit/crashreporter/client/crashreporter_gtk_common.cpp
+++ b/toolkit/crashreporter/client/crashreporter_gtk_common.cpp
@@ -188,21 +188,23 @@ void LoadProxyinfo()
 }
 #endif
 
 gpointer SendThread(gpointer args)
 {
   string response, error;
   long response_code;
 
+  std::map<string, string> files;
+  files["upload_file_minidump"] = gDumpFile;
+
   bool success = google_breakpad::HTTPUpload::SendRequest
     (gSendURL,
      gQueryParameters,
-     gDumpFile,
-     "upload_file_minidump",
+     files,
      gHttpProxy, gAuth,
      gCACertificateFile,
      &response,
      &response_code,
      &error);
   if (success) {
     LogMessage("Crash report submitted successfully");
   }
--- a/toolkit/crashreporter/client/crashreporter_win.cpp
+++ b/toolkit/crashreporter/client/crashreporter_win.cpp
@@ -392,19 +392,22 @@ static DWORD WINAPI SendThreadProc(LPVOI
   bool finishedOk;
   SendThreadData* td = (SendThreadData*)param;
 
   if (td->sendURL.empty()) {
     finishedOk = false;
     LogMessage("No server URL, not sending report");
   } else {
     google_breakpad::CrashReportSender sender(L"");
+    std::map<wstring, wstring> files;
+    files[L"upload_file_minidump"] = td->dumpFile;
+
     finishedOk = (sender.SendCrashReport(td->sendURL,
                                          td->queryParameters,
-                                         td->dumpFile,
+                                         files,
                                          &td->serverResponse)
                   == google_breakpad::RESULT_SUCCEEDED);
     if (finishedOk) {
       LogMessage("Crash report submitted successfully");
     }
     else {
       // get an error string and print it to the log
       //XXX: would be nice to get the HTTP status code here, filed:
--- a/toolkit/crashreporter/google-breakpad/src/client/windows/sender/crash_report_sender.cc
+++ b/toolkit/crashreporter/google-breakpad/src/client/windows/sender/crash_report_sender.cc
@@ -54,27 +54,27 @@ CrashReportSender::CrashReportSender(con
   if (OpenCheckpointFile(L"r", &fd) == 0) {
     ReadCheckpoint(fd);
     fclose(fd);
   }
 }
 
 ReportResult CrashReportSender::SendCrashReport(
     const wstring &url, const map<wstring, wstring> &parameters,
-    const wstring &dump_file_name, wstring *report_code) {
+    const map<wstring, wstring> &files, wstring *report_code) {
   int today = GetCurrentDate();
   if (today == last_sent_date_ &&
       max_reports_per_day_ != -1 &&
       reports_sent_ >= max_reports_per_day_) {
     return RESULT_THROTTLED;
   }
 
   int http_response = 0;
   bool result = HTTPUpload::SendRequest(
-    url, parameters, dump_file_name, L"upload_file_minidump", NULL, report_code,
+    url, parameters, files, NULL, report_code,
     &http_response);
 
   if (result) {
     ReportSent(today);
     return RESULT_SUCCEEDED;
   } else if (http_response >= 400 && http_response < 500) {
     return RESULT_REJECTED;
   } else {
--- a/toolkit/crashreporter/google-breakpad/src/client/windows/sender/crash_report_sender.h
+++ b/toolkit/crashreporter/google-breakpad/src/client/windows/sender/crash_report_sender.h
@@ -72,29 +72,29 @@ class CrashReportSender {
   // period.  This uses the state persisted to the checkpoint file.
   // The default value of -1 means that there is no limit on reports sent.
   void set_max_reports_per_day(int reports) {
     max_reports_per_day_ = reports;
   }
 
   int max_reports_per_day() const { return max_reports_per_day_; }
 
-  // Sends the specified minidump file, along with the map of
+  // Sends the specified files, along with the map of
   // name value pairs, as a multipart POST request to the given URL.
   // Parameter names must contain only printable ASCII characters,
   // and may not contain a quote (") character.
   // Only HTTP(S) URLs are currently supported.  The return value indicates
   // the result of the operation (see above for possible results).
   // If report_code is non-NULL and the report is sent successfully (that is,
   // the return value is RESULT_SUCCEEDED), a code uniquely identifying the
   // report will be returned in report_code.
   // (Otherwise, report_code will be unchanged.)
   ReportResult SendCrashReport(const wstring &url,
                                const map<wstring, wstring> &parameters,
-                               const wstring &dump_file_name,
+                               const map<wstring, wstring> &files,
                                wstring *report_code);
 
  private:
   // Reads persistent state from a checkpoint file.
   void ReadCheckpoint(FILE *fd);
 
   // Called when a new report has been sent, to update the checkpoint state.
   void ReportSent(int today);
--- a/toolkit/crashreporter/google-breakpad/src/common/linux/http_upload.cc
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/http_upload.cc
@@ -51,18 +51,17 @@ static size_t WriteCallback(void *ptr, s
 
 namespace google_breakpad {
 
 static const char kUserAgent[] = "Breakpad/1.0 (Linux)";
 
 // static
 bool HTTPUpload::SendRequest(const string &url,
                              const map<string, string> &parameters,
-                             const string &upload_file,
-                             const string &file_part_name,
+                             const map<string, string> &files,
                              const string &proxy,
                              const string &proxy_user_pwd,
                              const string &ca_certificate_file,
                              string *response_body,
                              long *response_code,
                              string *error_description) {
   if (response_code != NULL)
     *response_code = 0;
@@ -120,21 +119,23 @@ bool HTTPUpload::SendRequest(const strin
   *(void**) (&curl_formadd) = dlsym(curl_lib, "curl_formadd");
   map<string, string>::const_iterator iter = parameters.begin();
   for (; iter != parameters.end(); ++iter)
     (*curl_formadd)(&formpost, &lastptr,
                  CURLFORM_COPYNAME, iter->first.c_str(),
                  CURLFORM_COPYCONTENTS, iter->second.c_str(),
                  CURLFORM_END);
 
-  // Add form file.
-  (*curl_formadd)(&formpost, &lastptr,
-               CURLFORM_COPYNAME, file_part_name.c_str(),
-               CURLFORM_FILE, upload_file.c_str(),
-               CURLFORM_END);
+  // Add form files.
+  for (iter = files.begin(); iter != files.end(); ++iter) {
+    (*curl_formadd)(&formpost, &lastptr,
+                 CURLFORM_COPYNAME, iter->first.c_str(),
+                 CURLFORM_FILE, iter->second.c_str(),
+                 CURLFORM_END);
+  }
 
   (*curl_easy_setopt)(curl, CURLOPT_HTTPPOST, formpost);
 
   // Disable 100-continue header.
   struct curl_slist *headerlist = NULL;
   char buf[] = "Expect:";
   struct curl_slist* (*curl_slist_append)(struct curl_slist *, const char *);
   *(void**) (&curl_slist_append) = dlsym(curl_lib, "curl_slist_append");
--- a/toolkit/crashreporter/google-breakpad/src/common/linux/http_upload.h
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/http_upload.h
@@ -40,33 +40,32 @@
 #include "common/using_std_string.h"
 
 namespace google_breakpad {
 
 using std::map;
 
 class HTTPUpload {
  public:
-  // Sends the given set of parameters, along with the contents of
-  // upload_file, as a multipart POST request to the given URL.
-  // file_part_name contains the name of the file part of the request
+  // 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
   // (i.e. it corresponds to the name= attribute on an <input type="file">.
   // Parameter names must contain only printable ASCII characters,
   // and may not contain a quote (") character.
   // 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).
   // If the send fails, a description of the error will be
   // returned in error_description.
   static bool SendRequest(const string &url,
                           const map<string, string> &parameters,
-                          const string &upload_file,
-                          const string &file_part_name,
+                          const map<string, string> &files,
                           const string &proxy,
                           const string &proxy_user_pwd,
                           const string &ca_certificate_file,
                           string *response_body,
                           long *response_code,
                           string *error_description);
 
  private:
--- a/toolkit/crashreporter/google-breakpad/src/common/windows/http_upload.cc
+++ b/toolkit/crashreporter/google-breakpad/src/common/windows/http_upload.cc
@@ -59,18 +59,17 @@ class HTTPUpload::AutoInternetHandle {
 
  private:
   HINTERNET handle_;
 };
 
 // static
 bool HTTPUpload::SendRequest(const wstring &url,
                              const map<wstring, wstring> &parameters,
-                             const wstring &upload_file,
-                             const wstring &file_part_name,
+                             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
@@ -138,18 +137,17 @@ bool HTTPUpload::SendRequest(const wstri
   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, upload_file,
-                           file_part_name, boundary, &request_body)) {
+  if (!GenerateRequestBody(parameters, files, boundary, &request_body)) {
     return false;
   }
 
   if (timeout) {
     if (!InternetSetOption(request.get(),
                            INTERNET_OPTION_SEND_TIMEOUT,
                            timeout,
                            sizeof(*timeout))) {
@@ -264,63 +262,65 @@ wstring HTTPUpload::GenerateMultipartBou
 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> &parameters,
-                                     const wstring &upload_file,
-                                     const wstring &file_part_name,
+                                     const map<wstring, wstring> &files,
                                      const wstring &boundary,
                                      string *request_body) {
-  vector<char> contents;
-  if (!GetFileContents(upload_file, &contents)) {
-    return false;
-  }
-
   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 the upload file as a binary (octet-stream) part
-  string filename_utf8 = WideToUTF8(upload_file);
-  if (filename_utf8.empty()) {
-    return false;
-  }
+  for (map<wstring, wstring>::const_iterator pos = files.begin();
+       pos != files.end(); ++pos) {
+    vector<char> contents;
+    if (!GetFileContents(pos->second, &contents)) {
+      return false;
+    }
 
-  string file_part_name_utf8 = WideToUTF8(file_part_name);
-  if (file_part_name_utf8.empty()) {
-    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;
+    }
 
-  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");
+    string file_part_name_utf8 = WideToUTF8(pos->first);
+    if (file_part_name_utf8.empty()) {
+      return false;
+    }
 
-  if (!contents.empty()) {
+    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("\r\n");
   request_body->append("--" + boundary_str + "--\r\n");
   return true;
 }
 
 // static
 bool HTTPUpload::GetFileContents(const wstring &filename,
                                  vector<char> *contents) {
   // The "open" method on pre-MSVC8 ifstream implementations doesn't accept a
--- a/toolkit/crashreporter/google-breakpad/src/common/windows/http_upload.h
+++ b/toolkit/crashreporter/google-breakpad/src/common/windows/http_upload.h
@@ -49,31 +49,30 @@ namespace google_breakpad {
 
 using std::string;
 using std::wstring;
 using std::map;
 using std::vector;
 
 class HTTPUpload {
  public:
-  // Sends the given set of parameters, along with the contents of
-  // upload_file, as a multipart POST request to the given URL.
-  // file_part_name contains the name of the file part of the request
+  // 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
   // (i.e. it corresponds to the name= attribute on an <input type="file">.
   // Parameter names must contain only printable ASCII characters,
   // and may not contain a quote (") character.
   // 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 SendRequest(const wstring &url,
                           const map<wstring, wstring> &parameters,
-                          const wstring &upload_file,
-                          const wstring &file_part_name,
+                          const map<wstring, wstring> &files,
                           int *timeout,
                           wstring *response_body,
                           int *response_code);
 
  private:
   class AutoInternetHandle;
 
   // Retrieves the HTTP response.  If NULL is passed in for response,
@@ -83,22 +82,21 @@ class HTTPUpload {
   static bool HTTPUpload::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, an upload filename, and a file part name,
+  // 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> &parameters,
-                                  const wstring &upload_file,
-                                  const wstring &file_part_name,
+                                  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);