migrate HTTPUtils to swift

This commit is contained in:
Ehren Kret 2024-03-01 10:32:34 -06:00 committed by GitHub
parent 54581676b7
commit c76f7dfbdf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 107 additions and 152 deletions

View File

@ -2307,7 +2307,6 @@
F9C5CDA4289453B400548EEE /* LegacyMessageJobFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5CACE289453B200548EEE /* LegacyMessageJobFinder.swift */; settings = {COMPILER_FLAGS = "-fcxx-modules"; }; };
F9C5CDA5289453B400548EEE /* IncomingGroupsV2MessageJob+SDS.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5CACF289453B200548EEE /* IncomingGroupsV2MessageJob+SDS.swift */; settings = {COMPILER_FLAGS = "-fcxx-modules"; }; };
F9C5CDA7289453B400548EEE /* IncomingGroupsV2MessageJob.h in Headers */ = {isa = PBXBuildFile; fileRef = F9C5CAD1289453B200548EEE /* IncomingGroupsV2MessageJob.h */; settings = {ATTRIBUTES = (Public, ); }; };
F9C5CDAA289453B400548EEE /* HTTPUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = F9C5CAD5289453B200548EEE /* HTTPUtils.h */; settings = {ATTRIBUTES = (Public, ); }; };
F9C5CDAF289453B400548EEE /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5CADA289453B200548EEE /* NetworkManager.swift */; settings = {COMPILER_FLAGS = "-fcxx-modules"; }; };
F9C5CDB0289453B400548EEE /* GiphyDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5CADC289453B200548EEE /* GiphyDownloader.swift */; settings = {COMPILER_FLAGS = "-fcxx-modules"; }; };
F9C5CDB1289453B400548EEE /* GiphyAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5CADD289453B200548EEE /* GiphyAPI.swift */; settings = {COMPILER_FLAGS = "-fcxx-modules"; }; };
@ -2319,7 +2318,6 @@
F9C5CDB9289453B400548EEE /* OWSRequestFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C5CAE6289453B200548EEE /* OWSRequestFactory.m */; settings = {COMPILER_FLAGS = "-fcxx-modules"; }; };
F9C5CDBA289453B400548EEE /* OWSDevicesService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5CAE7289453B200548EEE /* OWSDevicesService.swift */; settings = {COMPILER_FLAGS = "-fcxx-modules"; }; };
F9C5CDBB289453B400548EEE /* SignalServiceProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5CAE8289453B200548EEE /* SignalServiceProfile.swift */; settings = {COMPILER_FLAGS = "-fcxx-modules"; }; };
F9C5CDBC289453B400548EEE /* HTTPUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C5CAE9289453B200548EEE /* HTTPUtils.m */; settings = {COMPILER_FLAGS = "-fcxx-modules"; }; };
F9C5CDBE289453B400548EEE /* RESTNetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5CAEB289453B200548EEE /* RESTNetworkManager.swift */; settings = {COMPILER_FLAGS = "-fcxx-modules"; }; };
F9C5CDC2289453B400548EEE /* HTTPEntities.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5CAEF289453B200548EEE /* HTTPEntities.swift */; settings = {COMPILER_FLAGS = "-fcxx-modules"; }; };
F9C5CDC3289453B400548EEE /* DeviceProvisioningService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5CAF0289453B200548EEE /* DeviceProvisioningService.swift */; settings = {COMPILER_FLAGS = "-fcxx-modules"; }; };
@ -5121,7 +5119,6 @@
F9C5CACE289453B200548EEE /* LegacyMessageJobFinder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyMessageJobFinder.swift; sourceTree = "<group>"; };
F9C5CACF289453B200548EEE /* IncomingGroupsV2MessageJob+SDS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "IncomingGroupsV2MessageJob+SDS.swift"; sourceTree = "<group>"; };
F9C5CAD1289453B200548EEE /* IncomingGroupsV2MessageJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IncomingGroupsV2MessageJob.h; sourceTree = "<group>"; };
F9C5CAD5289453B200548EEE /* HTTPUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTTPUtils.h; sourceTree = "<group>"; };
F9C5CADA289453B200548EEE /* NetworkManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkManager.swift; sourceTree = "<group>"; };
F9C5CADC289453B200548EEE /* GiphyDownloader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GiphyDownloader.swift; sourceTree = "<group>"; };
F9C5CADD289453B200548EEE /* GiphyAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GiphyAPI.swift; sourceTree = "<group>"; };
@ -5133,7 +5130,6 @@
F9C5CAE6289453B200548EEE /* OWSRequestFactory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSRequestFactory.m; sourceTree = "<group>"; };
F9C5CAE7289453B200548EEE /* OWSDevicesService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSDevicesService.swift; sourceTree = "<group>"; };
F9C5CAE8289453B200548EEE /* SignalServiceProfile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalServiceProfile.swift; sourceTree = "<group>"; };
F9C5CAE9289453B200548EEE /* HTTPUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTTPUtils.m; sourceTree = "<group>"; };
F9C5CAEB289453B200548EEE /* RESTNetworkManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RESTNetworkManager.swift; sourceTree = "<group>"; };
F9C5CAEF289453B200548EEE /* HTTPEntities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPEntities.swift; sourceTree = "<group>"; };
F9C5CAF0289453B200548EEE /* DeviceProvisioningService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceProvisioningService.swift; sourceTree = "<group>"; };
@ -10450,8 +10446,6 @@
505C2ED529971D4E00C23FB2 /* DeviceLimitExceededError.swift */,
F9C5CAF0289453B200548EEE /* DeviceProvisioningService.swift */,
F9C5CAEF289453B200548EEE /* HTTPEntities.swift */,
F9C5CAD5289453B200548EEE /* HTTPUtils.h */,
F9C5CAE9289453B200548EEE /* HTTPUtils.m */,
F9C5CAE0289453B200548EEE /* HTTPUtils.swift */,
F9C5CADA289453B200548EEE /* NetworkManager.swift */,
F9C5CAE7289453B200548EEE /* OWSDevicesService.swift */,
@ -10783,7 +10777,6 @@
F9C5CE27289453B400548EEE /* DarwinNotificationCenter.h in Headers */,
F9C5CE2E289453B400548EEE /* DataSource.h in Headers */,
F9C5CE3F289453B400548EEE /* FunctionalUtil.h in Headers */,
F9C5CDAA289453B400548EEE /* HTTPUtils.h in Headers */,
F9C5CDA7289453B400548EEE /* IncomingGroupsV2MessageJob.h in Headers */,
F9C5CC0A289453B300548EEE /* InstalledSticker.h in Headers */,
F9C5CC16289453B300548EEE /* KnownStickerPack.h in Headers */,
@ -13232,7 +13225,6 @@
667AF9E22B4DC5EE008AEE5D /* GroupUpdateSource.swift in Sources */,
F9C5CD94289453B300548EEE /* HTMLMetadata.swift in Sources */,
F9C5CDC2289453B400548EEE /* HTTPEntities.swift in Sources */,
F9C5CDBC289453B400548EEE /* HTTPUtils.m in Sources */,
F9C5CDB4289453B400548EEE /* HTTPUtils.swift in Sources */,
66FC637C29DF8FF200F00DAC /* HydratedMessageBody.swift in Sources */,
F9C5CDDF289453B400548EEE /* ImageQuality.swift in Sources */,

View File

@ -11,7 +11,6 @@
#import <SignalMessaging/SignalMessaging-Swift.h>
#import <SignalServiceKit/AppContext.h>
#import <SignalServiceKit/AppReadiness.h>
#import <SignalServiceKit/HTTPUtils.h>
#import <SignalServiceKit/MIMETypeUtil.h>
#import <SignalServiceKit/NSData+Image.h>
#import <SignalServiceKit/OWSFileSystem.h>
@ -388,7 +387,13 @@ NSString *const kNSNotificationKey_UserProfileWriter = @"kNSNotificationKey_User
{
[self reuploadLocalProfileWithSneakyTransactionWithAuthedAccount:authedAccount]
.done(^(id value) { OWSLogInfo(@"Done."); })
.catch(^(NSError *error) { OWSFailDebugUnlessNetworkFailure(error); });
.catch(^(NSError *error) {
if (error.isNetworkFailureOrTimeout) {
OWSLogWarn(@"Error: %@", error);
} else {
OWSFailDebug(@"Error: %@", error);
}
});
}
#pragma mark - Profile Key Rotation

View File

@ -21,7 +21,6 @@ FOUNDATION_EXPORT const unsigned char SignalServiceKitVersionString[];
#import <SignalServiceKit/DarwinNotificationCenter.h>
#import <SignalServiceKit/DataSource.h>
#import <SignalServiceKit/FunctionalUtil.h>
#import <SignalServiceKit/HTTPUtils.h>
#import <SignalServiceKit/IncomingGroupsV2MessageJob.h>
#import <SignalServiceKit/InstalledSticker.h>
#import <SignalServiceKit/KnownStickerPack.h>

View File

@ -1,28 +0,0 @@
//
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
NS_ASSUME_NONNULL_BEGIN
dispatch_queue_t NetworkManagerQueue(void);
#define OWSFailDebugUnlessNetworkFailure(error) \
if (error.isNetworkFailureOrTimeout) { \
OWSLogWarn(@"Error: %@", error); \
} else { \
OWSFailDebug(@"Error: %@", error); \
}
#pragma mark -
@interface HTTPUtils : NSObject
#if TESTABLE_BUILD
+ (void)logCurlForTask:(NSURLSessionTask *)task;
+ (void)logCurlForURLRequest:(NSURLRequest *)originalRequest;
#endif
@end
NS_ASSUME_NONNULL_END

View File

@ -1,101 +0,0 @@
//
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
#import "HTTPUtils.h"
#import "MIMETypeUtil.h"
#import <SignalServiceKit/SignalServiceKit-Swift.h>
NS_ASSUME_NONNULL_BEGIN
dispatch_queue_t NetworkManagerQueue(void)
{
static dispatch_queue_t serialQueue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
serialQueue = dispatch_queue_create("org.signal.network-manager", DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL);
});
return serialQueue;
}
#pragma mark -
@implementation HTTPUtils
#if TESTABLE_BUILD
// TODO: Port to Swift
+ (void)logCurlForTask:(NSURLSessionTask *)task
{
[self logCurlForURLRequest:task.originalRequest];
}
// TODO: Port to Swift
+ (void)logCurlForURLRequest:(NSURLRequest *)originalRequest
{
NSMutableArray<NSString *> *curlComponents = [NSMutableArray new];
[curlComponents addObject:@"curl"];
// Verbose
[curlComponents addObject:@"-v"];
// Insecure
[curlComponents addObject:@"-k"];
// Method, e.g. GET
[curlComponents addObject:@"-X"];
[curlComponents addObject:originalRequest.HTTPMethod];
// Headers
for (NSString *header in originalRequest.allHTTPHeaderFields) {
NSString *headerValue = originalRequest.allHTTPHeaderFields[header];
// We don't yet support escaping header values.
// If these asserts trip, we'll need to add that.
OWSAssertDebug([header rangeOfString:@"'"].location == NSNotFound);
OWSAssertDebug([headerValue rangeOfString:@"'"].location == NSNotFound);
[curlComponents addObject:@"-H"];
[curlComponents addObject:[NSString stringWithFormat:@"'%@: %@'", header, headerValue]];
}
// Body/parameters (e.g. JSON payload)
if (originalRequest.HTTPBody.length > 0) {
NSString *_Nullable contentType = originalRequest.allHTTPHeaderFields[@"Content-Type"];
BOOL isJson = [contentType isEqualToString:OWSMimeTypeJson];
BOOL isProtobuf = [contentType isEqualToString:@"application/x-protobuf"];
BOOL isFormData = [contentType isEqualToString:@"application/x-www-form-urlencoded"];
BOOL isSenderKeyMessage = [contentType isEqualToString:@"application/vnd.signal-messenger.mrm"];
if (isJson) {
NSString *jsonBody = [[NSString alloc] initWithData:originalRequest.HTTPBody encoding:NSUTF8StringEncoding];
// We don't yet support escaping JSON.
// If these asserts trip, we'll need to add that.
OWSAssertDebug([jsonBody rangeOfString:@"'"].location == NSNotFound);
[curlComponents addObject:@"--data-ascii"];
[curlComponents addObject:[NSString stringWithFormat:@"'%@'", jsonBody]];
} else if (isProtobuf || isFormData || isSenderKeyMessage) {
NSData *bodyData = originalRequest.HTTPBody;
NSString *filename = [NSString stringWithFormat:@"%@.tmp", NSUUID.UUID.UUIDString];
uint8_t bodyBytes[bodyData.length];
[bodyData getBytes:bodyBytes length:bodyData.length];
NSMutableArray<NSString *> *echoBytes = [NSMutableArray new];
for (NSUInteger i = 0; i < bodyData.length; i++) {
uint8_t bodyByte = bodyBytes[i];
[echoBytes addObject:[NSString stringWithFormat:@"\\\\x%02X", bodyByte]];
}
NSString *echoCommand =
[NSString stringWithFormat:@"echo -n -e %@ > %@", [echoBytes componentsJoinedByString:@""], filename];
OWSLogVerbose(@"curl for request: %@", echoCommand);
[curlComponents addObject:@"--data-binary"];
[curlComponents addObject:[NSString stringWithFormat:@"@%@", filename]];
} else {
OWSFailDebug(@"Unknown content type: %@", contentType);
}
}
// TODO: Add support for cookies.
// Double-quote the URL.
[curlComponents addObject:[NSString stringWithFormat:@"\"%@\"", originalRequest.URL.absoluteString]];
NSString *curlCommand = [curlComponents componentsJoinedByString:@" "];
OWSLogVerbose(@"curl for request: %@", curlCommand);
}
#endif
@end
NS_ASSUME_NONNULL_END

View File

@ -7,7 +7,95 @@ import Foundation
import SignalCoreKit
import CFNetwork
extension HTTPUtils {
/// This extension sacrifices Dictionary performance in order to ignore http
/// header case and should not be generally used. Since the number of http
/// headers is generally small, this is an acceptable tradeoff for this use case
/// but may not be for other use cases.
private extension Dictionary where Key == String {
subscript(header header: String) -> Value? {
get {
if let key = keys.first(where: { $0.caseInsensitiveCompare(header) == .orderedSame }) {
return self[key]
}
return nil
}
set {
if let key = keys.first(where: { $0.caseInsensitiveCompare(header) == .orderedSame }) {
self[key] = newValue
} else {
self[header] = newValue
}
}
}
}
class HTTPUtils: Dependencies {
#if TESTABLE_BUILD
public static func logCurl(for task: URLSessionTask) {
guard let request = task.originalRequest else {
Logger.debug("attempted to log curl on a task with no original request")
return
}
logCurl(for: request)
}
public static func logCurl(for request: URLRequest) {
guard let httpMethod = request.httpMethod else {
Logger.debug("attempted to log curl on a request with no http method")
return
}
var curlComponents = ["curl", "-v", "-k", "-X", httpMethod]
for (header, headerValue) in request.allHTTPHeaderFields ?? [:] {
// We don't yet support escaping header values.
// If these asserts trip, we'll need to add that.
owsAssertDebug(!header.contains("'"))
owsAssertDebug(!headerValue.contains("'"))
curlComponents.append("-H")
curlComponents.append("'\(header): \(headerValue)'")
}
if let httpBody = request.httpBody,
!httpBody.isEmpty {
let contentType = request.allHTTPHeaderFields?[header: "Content-Type"]
switch contentType {
case OWSMimeTypeJson:
guard let jsonBody = String(data: httpBody, encoding: .utf8) else {
Logger.debug("data attached to request as json was not utf8 encoded")
return
}
// We don't yet support escaping JSON.
// If these asserts trip, we'll need to add that.
owsAssertDebug(!jsonBody.contains("'"))
curlComponents.append("--data-ascii")
curlComponents.append("'\(jsonBody)'")
case OWSMimeTypeProtobuf, "application/x-www-form-urlencoded", "application/vnd.signal-messenger.mrm":
let filename = "\(UUID().uuidString).tmp"
var echoBytes = ""
for byte in httpBody {
echoBytes.append(String(format: "\\\\x%02X", byte))
}
let echoCommand = "echo -n -e \(echoBytes) > \(filename)"
Logger.verbose("curl for request: \(echoCommand)")
curlComponents.append("--data-binary")
curlComponents.append("@\(filename)")
default:
owsFailDebug("Unknown content type: \(contentType ?? "<nil>")")
}
}
// TODO: Add support for cookies.
guard let url = request.url else {
Logger.debug("attempted to log curl on a request with no url")
return
}
curlComponents.append("\"\(url.absoluteString)\"")
let curlCommand = curlComponents.joined(separator: " ")
Logger.verbose("curl for request: \(curlCommand)")
}
#endif
// This DRYs up handling of main service errors
// so that REST and websocket errors are handled

View File

@ -6,6 +6,10 @@
import Foundation
import SignalCoreKit
private let networkManagerQueue = DispatchQueue(
label: "org.signal.network-manager",
autoreleaseFrequency: .workItem)
class RESTNetworkManager {
fileprivate typealias Success = (HTTPResponse) -> Void
fileprivate typealias Failure = (OWSHTTPErrorWrapper) -> Void
@ -23,7 +27,6 @@ class RESTNetworkManager {
success: @escaping Success,
failure: @escaping Failure
) {
let networkManagerQueue = NetworkManagerQueue()
networkManagerQueue.async {
self.makeRequestSync(request, completionQueue: completionQueue, success: success, failure: failure)
}
@ -52,7 +55,6 @@ class RESTNetworkManager {
}
#endif
let networkManagerQueue = NetworkManagerQueue()
networkManagerQueue.async {
sessionManagerPool.returnToPool(sessionManager)
}
@ -63,7 +65,6 @@ class RESTNetworkManager {
}
}
let failure = { (errorWrapper: OWSHTTPErrorWrapper) in
let networkManagerQueue = NetworkManagerQueue()
networkManagerQueue.async {
sessionManagerPool.returnToPool(sessionManager)
}
@ -96,14 +97,14 @@ private class RESTSessionManager {
public let createdDate = Date()
init() {
assertOnQueue(NetworkManagerQueue())
assertOnQueue(networkManagerQueue)
urlSession = SSKEnvironment.shared.signalService.urlSessionForMainSignalService()
}
public func performRequest(_ request: TSRequest,
success: @escaping RESTNetworkManager.Success,
failure: @escaping RESTNetworkManager.Failure) {
assertOnQueue(NetworkManagerQueue())
assertOnQueue(networkManagerQueue)
owsAssertDebug(!FeatureFlags.deprecateREST)
// We should only use the RESTSessionManager for requests to the Signal main service.
@ -127,7 +128,7 @@ private class RESTSessionManager {
let wrappedError = OWSHTTPErrorWrapper(error: httpError)
if httpError.httpStatusCode == 401, request.shouldCheckDeregisteredOn401 {
NetworkManagerQueue().async {
networkManagerQueue.async {
self.makeIsDeregisteredRequest(
originalRequestFailureHandler: failure,
originalRequestFailure: wrappedError
@ -187,10 +188,10 @@ private class RESTSessionManager {
private class OWSSessionManagerPool {
private let maxSessionManagerAge = 5 * kMinuteInterval
// must only be accessed from the NetworkManagerQueue for thread-safety
// must only be accessed from the networkManagerQueue for thread-safety
private var pool: [RESTSessionManager] = []
// accessed from both NetworkManagerQueue and the main thread so needs a lock
// accessed from both networkManagerQueue and the main thread so needs a lock
@Atomic private var lastDiscardDate: Date?
init() {
@ -219,7 +220,7 @@ private class OWSSessionManagerPool {
}
func get() -> RESTSessionManager {
assertOnQueue(NetworkManagerQueue())
assertOnQueue(networkManagerQueue)
// Iterate over the pool, discarding expired session managers
// until we find an unexpired session manager in the pool or
@ -236,7 +237,7 @@ private class OWSSessionManagerPool {
}
func returnToPool(_ sessionManager: RESTSessionManager) {
assertOnQueue(NetworkManagerQueue())
assertOnQueue(networkManagerQueue)
let maxPoolSize = CurrentAppContext().isNSE ? 5 : 32
guard pool.count < maxPoolSize && !shouldDiscardSessionManager(sessionManager) else {

View File

@ -5,7 +5,6 @@
#import "OWS2FAManager.h"
#import "AppReadiness.h"
#import "HTTPUtils.h"
#import <SignalServiceKit/SignalServiceKit-Swift.h>
NS_ASSUME_NONNULL_BEGIN