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
This commit is contained in:
parent
8cde5c5152
commit
1335417f9f
7 changed files with 224 additions and 54 deletions
|
@ -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,
|
||||
|
|
|
@ -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:
|
||||
|
@ -448,19 +454,39 @@ NSString *Breakpad::NextCrashReportToUpload() {
|
|||
return [NSString stringWithFormat:@"%@/%@", directory, config];
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -794,18 +820,65 @@ void BreakpadUploadNextReport(BreakpadRef ref) {
|
|||
BreakpadUploadNextReportWithParameters(ref, nil);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -204,6 +204,11 @@ NSDictionary *readConfigurationData(const char *configFile) {
|
|||
return self;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
+ (NSDictionary *)readConfigurationDataFromFile:(NSString *)configFile {
|
||||
return readConfigurationData([configFile fileSystemRepresentation]);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (void)translateConfigurationData:(NSDictionary *)config {
|
||||
parameters_ = [[NSMutableDictionary alloc] init];
|
||||
|
@ -486,6 +491,46 @@ NSDictionary *readConfigurationData(const char *configFile) {
|
|||
[extraServerVars_ setObject:value forKey:key];
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (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]];
|
||||
|
@ -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_) {
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue