xref: /aosp_15_r20/external/google-breakpad/src/common/mac/HTTPRequest.m (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
1*9712c20fSFrederick Mayle// Copyright 2020 Google LLC
2*9712c20fSFrederick Mayle//
3*9712c20fSFrederick Mayle// Redistribution and use in source and binary forms, with or without
4*9712c20fSFrederick Mayle// modification, are permitted provided that the following conditions are
5*9712c20fSFrederick Mayle// met:
6*9712c20fSFrederick Mayle//
7*9712c20fSFrederick Mayle//     * Redistributions of source code must retain the above copyright
8*9712c20fSFrederick Mayle// notice, this list of conditions and the following disclaimer.
9*9712c20fSFrederick Mayle//     * Redistributions in binary form must reproduce the above
10*9712c20fSFrederick Mayle// copyright notice, this list of conditions and the following disclaimer
11*9712c20fSFrederick Mayle// in the documentation and/or other materials provided with the
12*9712c20fSFrederick Mayle// distribution.
13*9712c20fSFrederick Mayle//     * Neither the name of Google LLC nor the names of its
14*9712c20fSFrederick Mayle// contributors may be used to endorse or promote products derived from
15*9712c20fSFrederick Mayle// this software without specific prior written permission.
16*9712c20fSFrederick Mayle//
17*9712c20fSFrederick Mayle// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18*9712c20fSFrederick Mayle// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19*9712c20fSFrederick Mayle// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20*9712c20fSFrederick Mayle// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21*9712c20fSFrederick Mayle// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22*9712c20fSFrederick Mayle// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23*9712c20fSFrederick Mayle// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24*9712c20fSFrederick Mayle// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25*9712c20fSFrederick Mayle// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26*9712c20fSFrederick Mayle// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27*9712c20fSFrederick Mayle// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*9712c20fSFrederick Mayle
29*9712c20fSFrederick Mayle#import "HTTPRequest.h"
30*9712c20fSFrederick Mayle
31*9712c20fSFrederick Mayle#include <Availability.h>
32*9712c20fSFrederick Mayle#include <AvailabilityMacros.h>
33*9712c20fSFrederick Mayle
34*9712c20fSFrederick Mayle#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && defined(__IPHONE_7_0) && \
35*9712c20fSFrederick Mayle     __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_7_0)
36*9712c20fSFrederick Mayle#import <UIKit/UIKit.h>
37*9712c20fSFrederick Mayle#define HAS_BACKGROUND_TASK_API 1
38*9712c20fSFrederick Mayle#else
39*9712c20fSFrederick Mayle#define HAS_BACKGROUND_TASK_API 0
40*9712c20fSFrederick Mayle#endif
41*9712c20fSFrederick Mayle
42*9712c20fSFrederick Mayle#import "encoding_util.h"
43*9712c20fSFrederick Mayle
44*9712c20fSFrederick Mayle#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && defined(__IPHONE_7_0) && \
45*9712c20fSFrederick Mayle     __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_7_0) ||                  \
46*9712c20fSFrederick Mayle    (defined(MAC_OS_X_VERSION_MIN_REQUIRED) &&                             \
47*9712c20fSFrederick Mayle     defined(MAC_OS_X_VERSION_10_11) &&                                    \
48*9712c20fSFrederick Mayle     MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11)
49*9712c20fSFrederick Mayle#define USE_NSURLSESSION 1
50*9712c20fSFrederick Mayle#else
51*9712c20fSFrederick Mayle#define USE_NSURLSESSION 0
52*9712c20fSFrederick Mayle#endif
53*9712c20fSFrederick Mayle
54*9712c20fSFrederick Mayle// As -[NSURLConnection sendSynchronousRequest:returningResponse:error:] has
55*9712c20fSFrederick Mayle// been deprecated with iOS 9.0 / OS X 10.11 SDKs, this function re-implements
56*9712c20fSFrederick Mayle// it using -[NSURLSession dataTaskWithRequest:completionHandler:] which is
57*9712c20fSFrederick Mayle// available on iOS 7+.
58*9712c20fSFrederick Maylestatic NSData* SendSynchronousNSURLRequest(NSURLRequest* req,
59*9712c20fSFrederick Mayle                                           NSURLResponse** outResponse,
60*9712c20fSFrederick Mayle                                           NSError** outError) {
61*9712c20fSFrederick Mayle#if USE_NSURLSESSION
62*9712c20fSFrederick Mayle  __block NSData* result = nil;
63*9712c20fSFrederick Mayle  __block NSError* error = nil;
64*9712c20fSFrederick Mayle  __block NSURLResponse* response = nil;
65*9712c20fSFrederick Mayle  dispatch_semaphore_t waitSemaphone = dispatch_semaphore_create(0);
66*9712c20fSFrederick Mayle
67*9712c20fSFrederick Mayle  NSURLSessionConfiguration* config =
68*9712c20fSFrederick Mayle      [NSURLSessionConfiguration defaultSessionConfiguration];
69*9712c20fSFrederick Mayle  [config setTimeoutIntervalForRequest:240.0];
70*9712c20fSFrederick Mayle  NSURLSession* session = [NSURLSession sessionWithConfiguration:config];
71*9712c20fSFrederick Mayle  NSURLSessionDataTask *task = [session
72*9712c20fSFrederick Mayle      dataTaskWithRequest:req
73*9712c20fSFrederick Mayle        completionHandler:^(NSData* data, NSURLResponse* resp, NSError* err) {
74*9712c20fSFrederick Mayle          if (outError)
75*9712c20fSFrederick Mayle            error = [err retain];
76*9712c20fSFrederick Mayle          if (outResponse)
77*9712c20fSFrederick Mayle            response = [resp retain];
78*9712c20fSFrederick Mayle          if (err == nil)
79*9712c20fSFrederick Mayle            result = [data retain];
80*9712c20fSFrederick Mayle          dispatch_semaphore_signal(waitSemaphone);
81*9712c20fSFrederick Mayle        }];
82*9712c20fSFrederick Mayle  [task resume];
83*9712c20fSFrederick Mayle
84*9712c20fSFrederick Mayle#if HAS_BACKGROUND_TASK_API
85*9712c20fSFrederick Mayle  // Used to guard against ending the background task twice, which UIKit
86*9712c20fSFrederick Mayle  // considers to be an error.
87*9712c20fSFrederick Mayle  __block BOOL isBackgroundTaskActive = YES;
88*9712c20fSFrederick Mayle  __block UIBackgroundTaskIdentifier backgroundTaskIdentifier =
89*9712c20fSFrederick Mayle      UIBackgroundTaskInvalid;
90*9712c20fSFrederick Mayle  backgroundTaskIdentifier = [UIApplication.sharedApplication
91*9712c20fSFrederick Mayle      beginBackgroundTaskWithName:@"Breakpad Upload"
92*9712c20fSFrederick Mayle                expirationHandler:^{
93*9712c20fSFrederick Mayle                  if (!isBackgroundTaskActive) {
94*9712c20fSFrederick Mayle                    return;
95*9712c20fSFrederick Mayle                  }
96*9712c20fSFrederick Mayle                  isBackgroundTaskActive = NO;
97*9712c20fSFrederick Mayle
98*9712c20fSFrederick Mayle                  [task cancel];
99*9712c20fSFrederick Mayle                  [UIApplication.sharedApplication
100*9712c20fSFrederick Mayle                      endBackgroundTask:backgroundTaskIdentifier];
101*9712c20fSFrederick Mayle                }];
102*9712c20fSFrederick Mayle#endif  // HAS_BACKGROUND_TASK_API
103*9712c20fSFrederick Mayle
104*9712c20fSFrederick Mayle  dispatch_semaphore_wait(waitSemaphone, DISPATCH_TIME_FOREVER);
105*9712c20fSFrederick Mayle  dispatch_release(waitSemaphone);
106*9712c20fSFrederick Mayle
107*9712c20fSFrederick Mayle#if HAS_BACKGROUND_TASK_API
108*9712c20fSFrederick Mayle  if (backgroundTaskIdentifier != UIBackgroundTaskInvalid) {
109*9712c20fSFrederick Mayle    // Dispatch to main queue in order to synchronize access to
110*9712c20fSFrederick Mayle    // `isBackgroundTaskActive` with the background task expiration handler,
111*9712c20fSFrederick Mayle    // which is always run on the main thread.
112*9712c20fSFrederick Mayle    dispatch_async(dispatch_get_main_queue(), ^{
113*9712c20fSFrederick Mayle      if (!isBackgroundTaskActive) {
114*9712c20fSFrederick Mayle        return;
115*9712c20fSFrederick Mayle      }
116*9712c20fSFrederick Mayle      isBackgroundTaskActive = NO;
117*9712c20fSFrederick Mayle
118*9712c20fSFrederick Mayle      [UIApplication.sharedApplication
119*9712c20fSFrederick Mayle          endBackgroundTask:backgroundTaskIdentifier];
120*9712c20fSFrederick Mayle    });
121*9712c20fSFrederick Mayle  }
122*9712c20fSFrederick Mayle#endif  // HAS_BACKGROUND_TASK_API
123*9712c20fSFrederick Mayle
124*9712c20fSFrederick Mayle  if (outError)
125*9712c20fSFrederick Mayle    *outError = [error autorelease];
126*9712c20fSFrederick Mayle  if (outResponse)
127*9712c20fSFrederick Mayle    *outResponse = [response autorelease];
128*9712c20fSFrederick Mayle  return [result autorelease];
129*9712c20fSFrederick Mayle#else  // USE_NSURLSESSION
130*9712c20fSFrederick Mayle  return [NSURLConnection sendSynchronousRequest:req
131*9712c20fSFrederick Mayle                               returningResponse:outResponse
132*9712c20fSFrederick Mayle                                           error:outError];
133*9712c20fSFrederick Mayle#endif  // USE_NSURLSESSION
134*9712c20fSFrederick Mayle}
135*9712c20fSFrederick Mayle
136*9712c20fSFrederick Mayle@implementation HTTPRequest
137*9712c20fSFrederick Mayle
138*9712c20fSFrederick Mayle//=============================================================================
139*9712c20fSFrederick Mayle- (id)initWithURL:(NSURL*)URL {
140*9712c20fSFrederick Mayle  if ((self = [super init])) {
141*9712c20fSFrederick Mayle    URL_ = [URL copy];
142*9712c20fSFrederick Mayle  }
143*9712c20fSFrederick Mayle
144*9712c20fSFrederick Mayle  return self;
145*9712c20fSFrederick Mayle}
146*9712c20fSFrederick Mayle
147*9712c20fSFrederick Mayle//=============================================================================
148*9712c20fSFrederick Mayle- (void)dealloc {
149*9712c20fSFrederick Mayle  [URL_ release];
150*9712c20fSFrederick Mayle  [response_ release];
151*9712c20fSFrederick Mayle
152*9712c20fSFrederick Mayle  [super dealloc];
153*9712c20fSFrederick Mayle}
154*9712c20fSFrederick Mayle
155*9712c20fSFrederick Mayle//=============================================================================
156*9712c20fSFrederick Mayle- (NSURL*)URL {
157*9712c20fSFrederick Mayle  return URL_;
158*9712c20fSFrederick Mayle}
159*9712c20fSFrederick Mayle
160*9712c20fSFrederick Mayle//=============================================================================
161*9712c20fSFrederick Mayle- (NSHTTPURLResponse*)response {
162*9712c20fSFrederick Mayle  return response_;
163*9712c20fSFrederick Mayle}
164*9712c20fSFrederick Mayle
165*9712c20fSFrederick Mayle//=============================================================================
166*9712c20fSFrederick Mayle- (NSString*)HTTPMethod {
167*9712c20fSFrederick Mayle  @throw [NSException
168*9712c20fSFrederick Mayle      exceptionWithName:NSInternalInconsistencyException
169*9712c20fSFrederick Mayle                 reason:[NSString stringWithFormat:@"You must"
170*9712c20fSFrederick Mayle                                                    "override %@ in a subclass",
171*9712c20fSFrederick Mayle                                                   NSStringFromSelector(_cmd)]
172*9712c20fSFrederick Mayle               userInfo:nil];
173*9712c20fSFrederick Mayle}
174*9712c20fSFrederick Mayle
175*9712c20fSFrederick Mayle//=============================================================================
176*9712c20fSFrederick Mayle- (NSString*)contentType {
177*9712c20fSFrederick Mayle  return nil;
178*9712c20fSFrederick Mayle}
179*9712c20fSFrederick Mayle
180*9712c20fSFrederick Mayle//=============================================================================
181*9712c20fSFrederick Mayle- (NSData*)bodyData {
182*9712c20fSFrederick Mayle  return nil;
183*9712c20fSFrederick Mayle}
184*9712c20fSFrederick Mayle
185*9712c20fSFrederick Mayle//=============================================================================
186*9712c20fSFrederick Mayle- (NSData*)send:(NSError**)withError {
187*9712c20fSFrederick Mayle  NSMutableURLRequest* req = [[NSMutableURLRequest alloc]
188*9712c20fSFrederick Mayle          initWithURL:URL_
189*9712c20fSFrederick Mayle          cachePolicy:NSURLRequestUseProtocolCachePolicy
190*9712c20fSFrederick Mayle      timeoutInterval:60.0];
191*9712c20fSFrederick Mayle
192*9712c20fSFrederick Mayle  NSString* contentType = [self contentType];
193*9712c20fSFrederick Mayle  if ([contentType length] > 0) {
194*9712c20fSFrederick Mayle    [req setValue:contentType forHTTPHeaderField:@"Content-type"];
195*9712c20fSFrederick Mayle  }
196*9712c20fSFrederick Mayle
197*9712c20fSFrederick Mayle  NSData* bodyData = [self bodyData];
198*9712c20fSFrederick Mayle  if ([bodyData length] > 0) {
199*9712c20fSFrederick Mayle    [req setHTTPBody:bodyData];
200*9712c20fSFrederick Mayle  }
201*9712c20fSFrederick Mayle
202*9712c20fSFrederick Mayle  [req setHTTPMethod:[self HTTPMethod]];
203*9712c20fSFrederick Mayle
204*9712c20fSFrederick Mayle  [response_ release];
205*9712c20fSFrederick Mayle  response_ = nil;
206*9712c20fSFrederick Mayle
207*9712c20fSFrederick Mayle  NSData* data = nil;
208*9712c20fSFrederick Mayle  if ([[req URL] isFileURL]) {
209*9712c20fSFrederick Mayle    [[req HTTPBody] writeToURL:[req URL] options:0 error:withError];
210*9712c20fSFrederick Mayle  } else {
211*9712c20fSFrederick Mayle    NSURLResponse* response = nil;
212*9712c20fSFrederick Mayle    data = SendSynchronousNSURLRequest(req, &response, withError);
213*9712c20fSFrederick Mayle    response_ = (NSHTTPURLResponse*)[response retain];
214*9712c20fSFrederick Mayle  }
215*9712c20fSFrederick Mayle  [req release];
216*9712c20fSFrederick Mayle
217*9712c20fSFrederick Mayle  return data;
218*9712c20fSFrederick Mayle}
219*9712c20fSFrederick Mayle
220*9712c20fSFrederick Mayle//=============================================================================
221*9712c20fSFrederick Mayle+ (NSData*)formDataForFileContents:(NSData*)contents withName:(NSString*)name {
222*9712c20fSFrederick Mayle  NSMutableData* data = [NSMutableData data];
223*9712c20fSFrederick Mayle  NSString* escaped = PercentEncodeNSString(name);
224*9712c20fSFrederick Mayle  NSString* fmt = @"Content-Disposition: form-data; name=\"%@\"; "
225*9712c20fSFrederick Mayle                   "filename=\"minidump.dmp\"\r\nContent-Type: "
226*9712c20fSFrederick Mayle                   "application/octet-stream\r\n\r\n";
227*9712c20fSFrederick Mayle  NSString* pre = [NSString stringWithFormat:fmt, escaped];
228*9712c20fSFrederick Mayle
229*9712c20fSFrederick Mayle  [data appendData:[pre dataUsingEncoding:NSUTF8StringEncoding]];
230*9712c20fSFrederick Mayle  [data appendData:contents];
231*9712c20fSFrederick Mayle
232*9712c20fSFrederick Mayle  return data;
233*9712c20fSFrederick Mayle}
234*9712c20fSFrederick Mayle
235*9712c20fSFrederick Mayle//=============================================================================
236*9712c20fSFrederick Mayle+ (NSData*)formDataForFile:(NSString*)file withName:(NSString*)name {
237*9712c20fSFrederick Mayle  NSData* contents = [NSData dataWithContentsOfFile:file];
238*9712c20fSFrederick Mayle
239*9712c20fSFrederick Mayle  return [HTTPRequest formDataForFileContents:contents withName:name];
240*9712c20fSFrederick Mayle}
241*9712c20fSFrederick Mayle
242*9712c20fSFrederick Mayle//=============================================================================
243*9712c20fSFrederick Mayle+ (NSData*)formDataForKey:(NSString*)key value:(NSString*)value {
244*9712c20fSFrederick Mayle  NSString* escaped = PercentEncodeNSString(key);
245*9712c20fSFrederick Mayle  NSString* fmt = @"Content-Disposition: form-data; name=\"%@\"\r\n\r\n%@\r\n";
246*9712c20fSFrederick Mayle  NSString* form = [NSString stringWithFormat:fmt, escaped, value];
247*9712c20fSFrederick Mayle
248*9712c20fSFrederick Mayle  return [form dataUsingEncoding:NSUTF8StringEncoding];
249*9712c20fSFrederick Mayle}
250*9712c20fSFrederick Mayle
251*9712c20fSFrederick Mayle//=============================================================================
252*9712c20fSFrederick Mayle+ (void)appendFileToBodyData:(NSMutableData*)data
253*9712c20fSFrederick Mayle                    withName:(NSString*)name
254*9712c20fSFrederick Mayle              withFileOrData:(id)fileOrData {
255*9712c20fSFrederick Mayle  NSData* fileData;
256*9712c20fSFrederick Mayle
257*9712c20fSFrederick Mayle  // The object can be either the path to a file (NSString) or the contents
258*9712c20fSFrederick Mayle  // of the file (NSData).
259*9712c20fSFrederick Mayle  if ([fileOrData isKindOfClass:[NSData class]])
260*9712c20fSFrederick Mayle    fileData = [self formDataForFileContents:fileOrData withName:name];
261*9712c20fSFrederick Mayle  else
262*9712c20fSFrederick Mayle    fileData = [HTTPRequest formDataForFile:fileOrData withName:name];
263*9712c20fSFrederick Mayle
264*9712c20fSFrederick Mayle  [data appendData:fileData];
265*9712c20fSFrederick Mayle}
266*9712c20fSFrederick Mayle
267*9712c20fSFrederick Mayle@end
268