2022-09-06 19:14:07 -04:00
|
|
|
|
// Copyright 2014 The Crashpad Authors
|
2014-10-31 12:17:32 -04:00
|
|
|
|
//
|
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
|
//
|
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
//
|
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
|
|
#include "util/net/http_transport.h"
|
|
|
|
|
|
|
|
|
|
#import <Foundation/Foundation.h>
|
2017-02-28 22:02:34 -05:00
|
|
|
|
#include <sys/utsname.h>
|
2014-10-31 12:17:32 -04:00
|
|
|
|
|
2023-06-30 11:39:44 -04:00
|
|
|
|
#include "base/apple/bridging.h"
|
2023-08-17 11:10:53 -04:00
|
|
|
|
#include "base/apple/foundation_util.h"
|
2014-10-31 12:17:32 -04:00
|
|
|
|
#include "base/strings/stringprintf.h"
|
|
|
|
|
#include "base/strings/sys_string_conversions.h"
|
2017-02-28 22:02:34 -05:00
|
|
|
|
#include "build/build_config.h"
|
|
|
|
|
#include "package.h"
|
2015-10-22 16:14:18 -07:00
|
|
|
|
#include "util/file/file_io.h"
|
2015-09-14 14:51:05 -07:00
|
|
|
|
#include "util/misc/implicit_cast.h"
|
2022-07-14 11:00:29 -04:00
|
|
|
|
#include "util/misc/metrics.h"
|
2014-10-31 12:17:32 -04:00
|
|
|
|
#include "util/net/http_body.h"
|
|
|
|
|
|
2021-11-10 15:56:37 -05:00
|
|
|
|
// An implementation of NSInputStream that reads from a
|
|
|
|
|
// crashpad::HTTPBodyStream.
|
|
|
|
|
@interface CrashpadHTTPBodyStreamTransport : NSInputStream {
|
|
|
|
|
@private
|
|
|
|
|
NSStreamStatus _streamStatus;
|
2023-06-30 11:39:44 -04:00
|
|
|
|
id<NSStreamDelegate> __strong _delegate;
|
2021-11-10 15:56:37 -05:00
|
|
|
|
crashpad::HTTPBodyStream* _bodyStream; // weak
|
|
|
|
|
}
|
|
|
|
|
- (instancetype)initWithBodyStream:(crashpad::HTTPBodyStream*)bodyStream;
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation CrashpadHTTPBodyStreamTransport
|
|
|
|
|
|
|
|
|
|
- (instancetype)initWithBodyStream:(crashpad::HTTPBodyStream*)bodyStream {
|
|
|
|
|
if ((self = [super init])) {
|
|
|
|
|
_streamStatus = NSStreamStatusNotOpen;
|
|
|
|
|
_bodyStream = bodyStream;
|
|
|
|
|
}
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NSInputStream:
|
|
|
|
|
|
|
|
|
|
- (BOOL)hasBytesAvailable {
|
|
|
|
|
// Per Apple's documentation: "May also return YES if a read must be attempted
|
|
|
|
|
// in order to determine the availability of bytes."
|
|
|
|
|
switch (_streamStatus) {
|
|
|
|
|
case NSStreamStatusAtEnd:
|
|
|
|
|
case NSStreamStatusClosed:
|
|
|
|
|
case NSStreamStatusError:
|
|
|
|
|
return NO;
|
|
|
|
|
default:
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSInteger)read:(uint8_t*)buffer maxLength:(NSUInteger)maxLen {
|
|
|
|
|
_streamStatus = NSStreamStatusReading;
|
|
|
|
|
|
|
|
|
|
crashpad::FileOperationResult rv =
|
|
|
|
|
_bodyStream->GetBytesBuffer(buffer, maxLen);
|
|
|
|
|
|
|
|
|
|
if (rv == 0)
|
|
|
|
|
_streamStatus = NSStreamStatusAtEnd;
|
|
|
|
|
else if (rv < 0)
|
|
|
|
|
_streamStatus = NSStreamStatusError;
|
|
|
|
|
else
|
|
|
|
|
_streamStatus = NSStreamStatusOpen;
|
|
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL)getBuffer:(uint8_t**)buffer length:(NSUInteger*)length {
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NSStream:
|
|
|
|
|
|
|
|
|
|
- (void)scheduleInRunLoop:(NSRunLoop*)runLoop forMode:(NSString*)mode {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)removeFromRunLoop:(NSRunLoop*)runLoop forMode:(NSString*)mode {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)open {
|
|
|
|
|
_streamStatus = NSStreamStatusOpen;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)close {
|
|
|
|
|
_streamStatus = NSStreamStatusClosed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSStreamStatus)streamStatus {
|
|
|
|
|
return _streamStatus;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (id<NSStreamDelegate>)delegate {
|
|
|
|
|
return _delegate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)setDelegate:(id)delegate {
|
|
|
|
|
_delegate = delegate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (id)propertyForKey:(NSStreamPropertyKey)key {
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL)setProperty:(id)property forKey:(NSStreamPropertyKey)key {
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
2015-03-11 16:59:59 -04:00
|
|
|
|
namespace crashpad {
|
2014-10-31 12:17:32 -04:00
|
|
|
|
|
2015-03-11 16:59:59 -04:00
|
|
|
|
namespace {
|
2014-10-31 12:17:32 -04:00
|
|
|
|
|
2017-02-28 22:02:34 -05:00
|
|
|
|
NSString* AppendEscapedFormat(NSString* base,
|
|
|
|
|
NSString* format,
|
|
|
|
|
NSString* data) {
|
|
|
|
|
return [base stringByAppendingFormat:
|
|
|
|
|
format,
|
|
|
|
|
[data stringByAddingPercentEncodingWithAllowedCharacters:
|
|
|
|
|
[[NSCharacterSet
|
|
|
|
|
characterSetWithCharactersInString:
|
|
|
|
|
@"()<>@,;:\\\"/[]?={} \t"] invertedSet]]];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This builds the same User-Agent string that CFNetwork would build internally,
|
|
|
|
|
// but it uses PACKAGE_NAME and PACKAGE_VERSION in place of values obtained from
|
|
|
|
|
// the main bundle’s Info.plist.
|
|
|
|
|
NSString* UserAgentString() {
|
|
|
|
|
NSString* user_agent = [NSString string];
|
|
|
|
|
|
|
|
|
|
// CFNetwork would use the main bundle’s CFBundleName, or the main
|
|
|
|
|
// executable’s filename if none.
|
2021-05-18 14:34:34 -04:00
|
|
|
|
user_agent = AppendEscapedFormat(user_agent, @"%@", @PACKAGE_NAME);
|
2017-02-28 22:02:34 -05:00
|
|
|
|
|
|
|
|
|
// CFNetwork would use the main bundle’s CFBundleVersion, or the string
|
|
|
|
|
// “(unknown version)” if none.
|
2021-05-18 14:34:34 -04:00
|
|
|
|
user_agent = AppendEscapedFormat(user_agent, @"/%@", @PACKAGE_VERSION);
|
2017-02-28 22:02:34 -05:00
|
|
|
|
|
|
|
|
|
// Expected to be CFNetwork.
|
|
|
|
|
NSBundle* nsurl_bundle = [NSBundle bundleForClass:[NSURLRequest class]];
|
2023-08-17 11:10:53 -04:00
|
|
|
|
NSString* bundle_name = base::apple::ObjCCast<NSString>([nsurl_bundle
|
2023-06-30 11:39:44 -04:00
|
|
|
|
objectForInfoDictionaryKey:base::apple::CFToNSPtrCast(kCFBundleNameKey)]);
|
2017-02-28 22:02:34 -05:00
|
|
|
|
if (bundle_name) {
|
|
|
|
|
user_agent = AppendEscapedFormat(user_agent, @" %@", bundle_name);
|
|
|
|
|
|
2023-08-17 11:10:53 -04:00
|
|
|
|
NSString* bundle_version = base::apple::ObjCCast<NSString>(
|
2023-06-30 11:39:44 -04:00
|
|
|
|
[nsurl_bundle objectForInfoDictionaryKey:base::apple::CFToNSPtrCast(
|
|
|
|
|
kCFBundleVersionKey)]);
|
2017-02-28 22:02:34 -05:00
|
|
|
|
if (bundle_version) {
|
|
|
|
|
user_agent = AppendEscapedFormat(user_agent, @"/%@", bundle_version);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
utsname os;
|
|
|
|
|
if (uname(&os) != 0) {
|
|
|
|
|
PLOG(WARNING) << "uname";
|
|
|
|
|
} else {
|
2021-05-18 14:34:34 -04:00
|
|
|
|
user_agent = AppendEscapedFormat(user_agent, @" %@", @(os.sysname));
|
|
|
|
|
user_agent = AppendEscapedFormat(user_agent, @"/%@", @(os.release));
|
2017-02-28 22:02:34 -05:00
|
|
|
|
|
|
|
|
|
// CFNetwork just uses the equivalent of os.machine to obtain the native
|
|
|
|
|
// (kernel) architecture. Here, give the process’ architecture as well as
|
|
|
|
|
// the native architecture. Use the same strings that the kernel would, so
|
|
|
|
|
// that they can be de-duplicated.
|
|
|
|
|
#if defined(ARCH_CPU_X86)
|
|
|
|
|
NSString* arch = @"i386";
|
|
|
|
|
#elif defined(ARCH_CPU_X86_64)
|
|
|
|
|
NSString* arch = @"x86_64";
|
2020-07-07 23:43:11 -04:00
|
|
|
|
#elif defined(ARCH_CPU_ARM64)
|
|
|
|
|
NSString* arch = @"arm64";
|
2017-02-28 22:02:34 -05:00
|
|
|
|
#else
|
|
|
|
|
#error Port
|
|
|
|
|
#endif
|
|
|
|
|
user_agent = AppendEscapedFormat(user_agent, @" (%@", arch);
|
|
|
|
|
|
2021-05-18 14:34:34 -04:00
|
|
|
|
NSString* machine = @(os.machine);
|
2017-02-28 22:02:34 -05:00
|
|
|
|
if (![machine isEqualToString:arch]) {
|
|
|
|
|
user_agent = AppendEscapedFormat(user_agent, @"; %@", machine);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
user_agent = [user_agent stringByAppendingString:@")"];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return user_agent;
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-31 12:17:32 -04:00
|
|
|
|
class HTTPTransportMac final : public HTTPTransport {
|
|
|
|
|
public:
|
|
|
|
|
HTTPTransportMac();
|
2021-09-20 12:55:12 -07:00
|
|
|
|
|
|
|
|
|
HTTPTransportMac(const HTTPTransportMac&) = delete;
|
|
|
|
|
HTTPTransportMac& operator=(const HTTPTransportMac&) = delete;
|
|
|
|
|
|
2014-10-31 12:17:32 -04:00
|
|
|
|
~HTTPTransportMac() override;
|
|
|
|
|
|
2015-02-05 18:05:40 -05:00
|
|
|
|
bool ExecuteSynchronously(std::string* response_body) override;
|
2014-10-31 12:17:32 -04:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
HTTPTransportMac::HTTPTransportMac() : HTTPTransport() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HTTPTransportMac::~HTTPTransportMac() {
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-05 18:05:40 -05:00
|
|
|
|
bool HTTPTransportMac::ExecuteSynchronously(std::string* response_body) {
|
2014-10-31 12:17:32 -04:00
|
|
|
|
DCHECK(body_stream());
|
|
|
|
|
|
|
|
|
|
@autoreleasepool {
|
|
|
|
|
NSString* url_ns_string = base::SysUTF8ToNSString(url());
|
|
|
|
|
NSURL* url = [NSURL URLWithString:url_ns_string];
|
|
|
|
|
NSMutableURLRequest* request =
|
|
|
|
|
[NSMutableURLRequest requestWithURL:url
|
|
|
|
|
cachePolicy:NSURLRequestUseProtocolCachePolicy
|
|
|
|
|
timeoutInterval:timeout()];
|
|
|
|
|
[request setHTTPMethod:base::SysUTF8ToNSString(method())];
|
|
|
|
|
|
2017-02-28 22:02:34 -05:00
|
|
|
|
// If left to its own devices, CFNetwork would build a user-agent string
|
|
|
|
|
// based on keys in the main bundle’s Info.plist, giving ugly results if
|
|
|
|
|
// there is no Info.plist. Provide a User-Agent string similar to the one
|
|
|
|
|
// that CFNetwork would use, but with appropriate values in place of the
|
|
|
|
|
// Info.plist-derived strings.
|
|
|
|
|
[request setValue:UserAgentString() forHTTPHeaderField:@"User-Agent"];
|
|
|
|
|
|
2014-10-31 12:17:32 -04:00
|
|
|
|
for (const auto& pair : headers()) {
|
|
|
|
|
[request setValue:base::SysUTF8ToNSString(pair.second)
|
|
|
|
|
forHTTPHeaderField:base::SysUTF8ToNSString(pair.first)];
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-30 11:39:44 -04:00
|
|
|
|
NSInputStream* input_stream = [[CrashpadHTTPBodyStreamTransport alloc]
|
|
|
|
|
initWithBodyStream:body_stream()];
|
|
|
|
|
[request setHTTPBodyStream:input_stream];
|
2014-10-31 12:17:32 -04:00
|
|
|
|
|
|
|
|
|
NSURLResponse* response = nil;
|
|
|
|
|
NSError* error = nil;
|
2015-10-07 16:16:53 -04:00
|
|
|
|
#pragma clang diagnostic push
|
|
|
|
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
|
|
|
// Deprecated in OS X 10.11. The suggested replacement, NSURLSession, is
|
|
|
|
|
// only available on 10.9 and later, and this needs to run on earlier
|
|
|
|
|
// releases.
|
2015-02-05 18:05:40 -05:00
|
|
|
|
NSData* body = [NSURLConnection sendSynchronousRequest:request
|
|
|
|
|
returningResponse:&response
|
|
|
|
|
error:&error];
|
2015-10-07 16:16:53 -04:00
|
|
|
|
#pragma clang diagnostic pop
|
2014-10-31 12:17:32 -04:00
|
|
|
|
|
|
|
|
|
if (error) {
|
2022-07-14 11:00:29 -04:00
|
|
|
|
Metrics::CrashUploadErrorCode(error.code);
|
2014-10-31 12:17:32 -04:00
|
|
|
|
LOG(ERROR) << [[error localizedDescription] UTF8String] << " ("
|
|
|
|
|
<< [[error domain] UTF8String] << " " << [error code] << ")";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!response) {
|
|
|
|
|
LOG(ERROR) << "no response";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
NSHTTPURLResponse* http_response =
|
2023-08-17 11:10:53 -04:00
|
|
|
|
base::apple::ObjCCast<NSHTTPURLResponse>(response);
|
2014-10-31 12:17:32 -04:00
|
|
|
|
if (!http_response) {
|
|
|
|
|
LOG(ERROR) << "no http_response";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
NSInteger http_status = [http_response statusCode];
|
2018-08-09 16:41:44 -07:00
|
|
|
|
if (http_status < 200 || http_status > 203) {
|
2014-10-31 12:17:32 -04:00
|
|
|
|
LOG(ERROR) << base::StringPrintf("HTTP status %ld",
|
2014-11-06 16:44:38 -05:00
|
|
|
|
implicit_cast<long>(http_status));
|
2014-10-31 12:17:32 -04:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-05 18:05:40 -05:00
|
|
|
|
if (response_body) {
|
|
|
|
|
response_body->assign(static_cast<const char*>([body bytes]),
|
|
|
|
|
[body length]);
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-31 12:17:32 -04:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
// static
|
2016-04-25 12:13:07 -07:00
|
|
|
|
std::unique_ptr<HTTPTransport> HTTPTransport::Create() {
|
|
|
|
|
return std::unique_ptr<HTTPTransport>(new HTTPTransportMac());
|
2014-10-31 12:17:32 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace crashpad
|