aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorblundell@chromium.org <blundell@chromium.org>2014-09-01 11:02:57 +0000
committerblundell@chromium.org <blundell@chromium.org>2014-09-01 11:02:57 +0000
commit1335417f9feefd20e5b0e4b1e18e9010edb87043 (patch)
tree0296d7d860021ec4d7f6dae5d5a58c259038c4b5
parentSupport for multiple upload files in CrashReportSender/HTTPUpload (diff)
downloadbreakpad-1335417f9feefd20e5b0e4b1e18e9010edb87043.tar.xz
Adding possibility for client to upload the file
This CL adds three features that will allow the client to upload the report file. Three main modifications are made : - Allow upload url to have a file:// scheme, and write the HTTP request to file in that case - Split the request in two parts in case of a file:// scheme, the request time and the response time. A new API [handleNetworkResponse] is added. - Give the opportunity to the client to get the configuration NSDictionary to be able to recreate the breakpad context at response time. Patch by Olivier Robin <olivierrobin@chromium.org> Review URL: https://breakpad.appspot.com/2764002/ git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1368 4c0a9323-5329-0410-9bdc-e9ce6186880e
-rw-r--r--src/client/ios/Breakpad.h22
-rw-r--r--src/client/ios/Breakpad.mm103
-rw-r--r--src/client/ios/BreakpadController.h14
-rw-r--r--src/client/ios/BreakpadController.mm31
-rw-r--r--src/client/mac/sender/uploader.h8
-rw-r--r--src/client/mac/sender/uploader.mm84
-rw-r--r--src/common/mac/HTTPMultipartUpload.m16
7 files changed, 224 insertions, 54 deletions
diff --git a/src/client/ios/Breakpad.h b/src/client/ios/Breakpad.h
index 9d212f70..c099ad07 100644
--- a/src/client/ios/Breakpad.h
+++ b/src/client/ios/Breakpad.h
@@ -199,6 +199,9 @@ void BreakpadRemoveUploadParameter(BreakpadRef ref, NSString *key);
// Returns the number of crash reports waiting to send to the server.
int BreakpadGetCrashReportCount(BreakpadRef ref);
+// Returns the next upload configuration. The report file is deleted.
+NSDictionary *BreakpadGetNextReportConfiguration(BreakpadRef ref);
+
// Upload next report to the server.
void BreakpadUploadNextReport(BreakpadRef ref);
@@ -207,6 +210,25 @@ void BreakpadUploadNextReport(BreakpadRef ref);
void BreakpadUploadNextReportWithParameters(BreakpadRef ref,
NSDictionary *server_parameters);
+// Upload a report to the server.
+// |server_parameters| is additional server parameters to send.
+// |configuration| is the configuration of the breakpad report to send.
+void BreakpadUploadReportWithParametersAndConfiguration(
+ BreakpadRef ref,
+ NSDictionary *server_parameters,
+ NSDictionary *configuration);
+
+// Handles the network response of a breakpad upload. This function is needed if
+// the actual upload is done by the Breakpad client.
+// |configuration| is the configuration of the upload. It must contain the same
+// fields as the configuration passed to
+// BreakpadUploadReportWithParametersAndConfiguration.
+// |data| and |error| contain the network response.
+void BreakpadHandleNetworkResponse(BreakpadRef ref,
+ NSDictionary *configuration,
+ NSData *data,
+ NSError *error);
+
// Upload a file to the server. |data| is the content of the file to sent.
// |server_parameters| is additional server parameters to send.
void BreakpadUploadData(BreakpadRef ref, NSData *data, NSString *name,
diff --git a/src/client/ios/Breakpad.mm b/src/client/ios/Breakpad.mm
index 5c4043c4..ca856f8f 100644
--- a/src/client/ios/Breakpad.mm
+++ b/src/client/ios/Breakpad.mm
@@ -152,9 +152,15 @@ class Breakpad {
void RemoveKeyValue(NSString *key);
NSArray *CrashReportsToUpload();
NSString *NextCrashReportToUpload();
+ NSDictionary *NextCrashReportConfiguration();
void UploadNextReport(NSDictionary *server_parameters);
+ void UploadReportWithConfiguration(NSDictionary *configuration,
+ NSDictionary *server_parameters);
void UploadData(NSData *data, NSString *name,
NSDictionary *server_parameters);
+ void HandleNetworkResponse(NSDictionary *configuration,
+ NSData *data,
+ NSError *error);
NSDictionary *GenerateReport(NSDictionary *server_parameters);
private:
@@ -449,18 +455,38 @@ NSString *Breakpad::NextCrashReportToUpload() {
}
//=============================================================================
+NSDictionary *Breakpad::NextCrashReportConfiguration() {
+ return [Uploader readConfigurationDataFromFile:NextCrashReportToUpload()];
+}
+
+//=============================================================================
+void Breakpad::HandleNetworkResponse(NSDictionary *configuration,
+ NSData *data,
+ NSError *error) {
+ Uploader *uploader = [[[Uploader alloc]
+ initWithConfig:configuration] autorelease];
+ [uploader handleNetworkResponse:data withError:error];
+}
+
+//=============================================================================
+void Breakpad::UploadReportWithConfiguration(NSDictionary *configuration,
+ NSDictionary *server_parameters) {
+ Uploader *uploader = [[[Uploader alloc]
+ initWithConfig:configuration] autorelease];
+ if (!uploader)
+ return;
+ for (NSString *key in server_parameters) {
+ [uploader addServerParameter:[server_parameters objectForKey:key]
+ forKey:key];
+ }
+ [uploader report];
+}
+
+//=============================================================================
void Breakpad::UploadNextReport(NSDictionary *server_parameters) {
- NSString *configFile = NextCrashReportToUpload();
- if (configFile) {
- Uploader *uploader = [[[Uploader alloc]
- initWithConfigFile:[configFile UTF8String]] autorelease];
- if (uploader) {
- for (NSString *key in server_parameters) {
- [uploader addServerParameter:[server_parameters objectForKey:key]
- forKey:key];
- }
- [uploader report];
- }
+ NSDictionary *configuration = NextCrashReportConfiguration();
+ if (configuration) {
+ return UploadReportWithConfiguration(configuration, server_parameters);
}
}
@@ -795,17 +821,64 @@ void BreakpadUploadNextReport(BreakpadRef ref) {
}
//=============================================================================
+NSDictionary *BreakpadGetNextReportConfiguration(BreakpadRef ref) {
+ try {
+ Breakpad *breakpad = (Breakpad *)ref;
+ if (breakpad)
+ return breakpad->NextCrashReportConfiguration();
+ } catch(...) { // don't let exceptions leave this C API
+ fprintf(stderr, "BreakpadGetNextReportConfiguration() : error\n");
+ }
+ return nil;
+}
+
+//=============================================================================
+void BreakpadUploadReportWithParametersAndConfiguration(
+ BreakpadRef ref,
+ NSDictionary *server_parameters,
+ NSDictionary *configuration) {
+ try {
+ Breakpad *breakpad = (Breakpad *)ref;
+ if (!breakpad || !configuration)
+ return;
+ breakpad->UploadReportWithConfiguration(configuration, server_parameters);
+ } catch(...) { // don't let exceptions leave this C API
+ fprintf(stderr,
+ "BreakpadUploadReportWithParametersAndConfiguration() : error\n");
+ }
+
+}
+
+//=============================================================================
void BreakpadUploadNextReportWithParameters(BreakpadRef ref,
NSDictionary *server_parameters) {
try {
+ Breakpad *breakpad = (Breakpad *)ref;
+ if (!breakpad)
+ return;
+ NSDictionary *configuration = breakpad->NextCrashReportConfiguration();
+ if (!configuration)
+ return;
+ return BreakpadUploadReportWithParametersAndConfiguration(ref,
+ server_parameters,
+ configuration);
+ } catch(...) { // don't let exceptions leave this C API
+ fprintf(stderr, "BreakpadUploadNextReportWithParameters() : error\n");
+ }
+}
+
+void BreakpadHandleNetworkResponse(BreakpadRef ref,
+ NSDictionary *configuration,
+ NSData *data,
+ NSError *error) {
+ try {
// Not called at exception time
Breakpad *breakpad = (Breakpad *)ref;
+ if (breakpad && configuration)
+ breakpad->HandleNetworkResponse(configuration,data, error);
- if (breakpad) {
- breakpad->UploadNextReport(server_parameters);
- }
} catch(...) { // don't let exceptions leave this C API
- fprintf(stderr, "BreakpadUploadNextReport() : error\n");
+ fprintf(stderr, "BreakpadHandleNetworkResponse() : error\n");
}
}
diff --git a/src/client/ios/BreakpadController.h b/src/client/ios/BreakpadController.h
index cf1fa774..13609cb8 100644
--- a/src/client/ios/BreakpadController.h
+++ b/src/client/ios/BreakpadController.h
@@ -122,6 +122,20 @@
// Get the number of crash reports waiting to upload.
- (void)getCrashReportCount:(void(^)(int))callback;
+// Get the next report to upload.
+// - If upload is disabled, callback will be called with (nil, -1).
+// - If a delay is to be waited before sending, callback will be called with
+// (nil, n), with n (> 0) being the number of seconds to wait.
+// - if no delay is needed, callback will be called with (0, configuration),
+// configuration being next report to upload, or nil if none is pending.
+- (void)getNextReportConfigurationOrSendDelay:
+ (void(^)(NSDictionary*, int))callback;
+
+// Sends synchronously the report specified by |configuration|. This method is
+// NOT thread safe and must be called from the breakpad thread.
+- (void)threadUnsafeSendReportWithConfiguration:(NSDictionary*)configuration
+ withBreakpadRef:(BreakpadRef)ref;
+
@end
#endif // CLIENT_IOS_HANDLER_IOS_BREAKPAD_CONTROLLER_H_
diff --git a/src/client/ios/BreakpadController.mm b/src/client/ios/BreakpadController.mm
index a85dd68e..dd71cff6 100644
--- a/src/client/ios/BreakpadController.mm
+++ b/src/client/ios/BreakpadController.mm
@@ -155,6 +155,18 @@ NSString* GetPlatform() {
});
}
+// This method must be called from the breakpad queue.
+- (void)threadUnsafeSendReportWithConfiguration:(NSDictionary*)configuration
+ withBreakpadRef:(BreakpadRef)ref {
+ NSAssert(started_, @"The controller must be started before "
+ "threadUnsafeSendReportWithConfiguration is called");
+ if (breakpadRef_) {
+ BreakpadUploadReportWithParametersAndConfiguration(breakpadRef_,
+ uploadTimeParameters_,
+ configuration);
+ }
+}
+
- (void)setUploadingEnabled:(BOOL)enabled {
NSAssert(started_,
@"The controller must be started before setUploadingEnabled is called");
@@ -260,6 +272,25 @@ NSString* GetPlatform() {
});
}
+- (void)getNextReportConfigurationOrSendDelay:
+ (void(^)(NSDictionary*, int))callback {
+ NSAssert(started_, @"The controller must be started before "
+ "getNextReportConfigurationOrSendDelay is called");
+ dispatch_async(queue_, ^{
+ if (!breakpadRef_) {
+ callback(nil, -1);
+ return;
+ }
+ int delay = [self sendDelay];
+ if (delay != 0) {
+ callback(nil, delay);
+ return;
+ }
+ [self reportWillBeSent];
+ callback(BreakpadGetNextReportConfiguration(breakpadRef_), 0);
+ });
+}
+
#pragma mark -
- (int)sendDelay {
diff --git a/src/client/mac/sender/uploader.h b/src/client/mac/sender/uploader.h
index 318165c9..5f6aa464 100644
--- a/src/client/mac/sender/uploader.h
+++ b/src/client/mac/sender/uploader.h
@@ -67,6 +67,10 @@ extern NSString *const kDefaultServerType;
- (id)initWithConfig:(NSDictionary *)config;
+// Reads the file |configFile| and returns the corresponding NSDictionary.
+// |configFile| will be deleted after reading.
++ (NSDictionary *)readConfigurationDataFromFile:(NSString *)configFile;
+
- (NSMutableDictionary *)parameters;
- (void)report;
@@ -78,4 +82,8 @@ extern NSString *const kDefaultServerType;
// will be uploaded to the crash server.
- (void)addServerParameter:(id)value forKey:(NSString *)key;
+// This method process the HTTP response and renames the minidump file with the
+// new ID.
+- (void)handleNetworkResponse:(NSData *)data withError:(NSError *)error;
+
@end
diff --git a/src/client/mac/sender/uploader.mm b/src/client/mac/sender/uploader.mm
index b6da8214..42a43bfc 100644
--- a/src/client/mac/sender/uploader.mm
+++ b/src/client/mac/sender/uploader.mm
@@ -205,6 +205,11 @@ NSDictionary *readConfigurationData(const char *configFile) {
}
//=============================================================================
++ (NSDictionary *)readConfigurationDataFromFile:(NSString *)configFile {
+ return readConfigurationData([configFile fileSystemRepresentation]);
+}
+
+//=============================================================================
- (void)translateConfigurationData:(NSDictionary *)config {
parameters_ = [[NSMutableDictionary alloc] init];
@@ -487,6 +492,46 @@ NSDictionary *readConfigurationData(const char *configFile) {
}
//=============================================================================
+- (void)handleNetworkResponse:(NSData *)data withError:(NSError *)error {
+ NSString *result = [[NSString alloc] initWithData:data
+ encoding:NSUTF8StringEncoding];
+ const char *reportID = "ERR";
+ if (error) {
+ fprintf(stderr, "Breakpad Uploader: Send Error: %s\n",
+ [[error description] UTF8String]);
+ } else {
+ NSCharacterSet *trimSet =
+ [NSCharacterSet whitespaceAndNewlineCharacterSet];
+ reportID = [[result stringByTrimmingCharactersInSet:trimSet] UTF8String];
+ [self logUploadWithID:reportID];
+ }
+
+ // rename the minidump file according to the id returned from the server
+ NSString *minidumpDir =
+ [parameters_ objectForKey:@kReporterMinidumpDirectoryKey];
+ NSString *minidumpID = [parameters_ objectForKey:@kReporterMinidumpIDKey];
+
+ NSString *srcString = [NSString stringWithFormat:@"%@/%@.dmp",
+ minidumpDir, minidumpID];
+ NSString *destString = [NSString stringWithFormat:@"%@/%s.dmp",
+ minidumpDir, reportID];
+
+ const char *src = [srcString fileSystemRepresentation];
+ const char *dest = [destString fileSystemRepresentation];
+
+ if (rename(src, dest) == 0) {
+ GTMLoggerInfo(@"Breakpad Uploader: Renamed %s to %s after successful " \
+ "upload",src, dest);
+ }
+ else {
+ // can't rename - don't worry - it's not important for users
+ GTMLoggerDebug(@"Breakpad Uploader: successful upload report ID = %s\n",
+ reportID );
+ }
+ [result release];
+}
+
+//=============================================================================
- (void)report {
NSURL *url = [NSURL URLWithString:[parameters_ objectForKey:@BREAKPAD_URL]];
HTTPMultipartUpload *upload = [[HTTPMultipartUpload alloc] initWithURL:url];
@@ -511,43 +556,16 @@ NSDictionary *readConfigurationData(const char *configFile) {
// Send it
NSError *error = nil;
NSData *data = [upload send:&error];
- NSString *result = [[NSString alloc] initWithData:data
- encoding:NSUTF8StringEncoding];
- const char *reportID = "ERR";
- if (error) {
- fprintf(stderr, "Breakpad Uploader: Send Error: %s\n",
- [[error description] UTF8String]);
+ if (![url isFileURL]) {
+ [self handleNetworkResponse:data withError:error];
} else {
- NSCharacterSet *trimSet =
- [NSCharacterSet whitespaceAndNewlineCharacterSet];
- reportID = [[result stringByTrimmingCharactersInSet:trimSet] UTF8String];
- [self logUploadWithID:reportID];
+ if (error) {
+ fprintf(stderr, "Breakpad Uploader: Error writing request file: %s\n",
+ [[error description] UTF8String]);
+ }
}
- // rename the minidump file according to the id returned from the server
- NSString *minidumpDir =
- [parameters_ objectForKey:@kReporterMinidumpDirectoryKey];
- NSString *minidumpID = [parameters_ objectForKey:@kReporterMinidumpIDKey];
-
- NSString *srcString = [NSString stringWithFormat:@"%@/%@.dmp",
- minidumpDir, minidumpID];
- NSString *destString = [NSString stringWithFormat:@"%@/%s.dmp",
- minidumpDir, reportID];
-
- const char *src = [srcString fileSystemRepresentation];
- const char *dest = [destString fileSystemRepresentation];
-
- if (rename(src, dest) == 0) {
- GTMLoggerInfo(@"Breakpad Uploader: Renamed %s to %s after successful " \
- "upload",src, dest);
- }
- else {
- // can't rename - don't worry - it's not important for users
- GTMLoggerDebug(@"Breakpad Uploader: successful upload report ID = %s\n",
- reportID );
- }
- [result release];
} else {
// Minidump is missing -- upload just the log file.
if (logFileData_) {
diff --git a/src/common/mac/HTTPMultipartUpload.m b/src/common/mac/HTTPMultipartUpload.m
index 76f38f8a..bd88cffa 100644
--- a/src/common/mac/HTTPMultipartUpload.m
+++ b/src/common/mac/HTTPMultipartUpload.m
@@ -143,7 +143,7 @@
//=============================================================================
- (NSData *)send:(NSError **)error {
- NSMutableURLRequest *req =
+ NSMutableURLRequest *req =
[[NSMutableURLRequest alloc]
initWithURL:url_ cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:10.0 ];
@@ -190,12 +190,16 @@
[response_ release];
response_ = nil;
-
- NSData *data = [NSURLConnection sendSynchronousRequest:req
- returningResponse:&response_
- error:error];
- [response_ retain];
+ NSData *data;
+ if ([[req URL] isFileURL]) {
+ [[req HTTPBody] writeToURL:[req URL] options:0 error:error];
+ } else {
+ data = [NSURLConnection sendSynchronousRequest:req
+ returningResponse:&response_
+ error:error];
+ [response_ retain];
+ }
[req release];
return data;