From eafde74019ab2bc5e72b01cd03debc4255694f69 Mon Sep 17 00:00:00 2001 From: John Holdsworth Date: Sat, 2 Sep 2017 21:16:48 +0100 Subject: [PATCH] NSLocalizedDescription for http errors --- .../URL.subproj/CFURLSessionInterface.c | 7 ++++++ .../URL.subproj/CFURLSessionInterface.h | 4 ++++ Foundation/NSString.swift | 2 +- Foundation/URLSession/http/EasyHandle.swift | 10 ++++---- .../URLSession/http/HTTPURLProtocol.swift | 24 ++++++++++++------- Foundation/URLSession/http/MultiHandle.swift | 16 +++++++++---- 6 files changed, 45 insertions(+), 18 deletions(-) diff --git a/CoreFoundation/URL.subproj/CFURLSessionInterface.c b/CoreFoundation/URL.subproj/CFURLSessionInterface.c index f44f44d59d..887b4f263d 100644 --- a/CoreFoundation/URL.subproj/CFURLSessionInterface.c +++ b/CoreFoundation/URL.subproj/CFURLSessionInterface.c @@ -19,6 +19,7 @@ //===----------------------------------------------------------------------===// #include "CFURLSessionInterface.h" +#include #include FILE* aa = NULL; @@ -31,6 +32,11 @@ static CFURLSessionMultiCode MakeMultiCode(CURLMcode value) { return (CFURLSessionMultiCode) { value }; } +CFStringRef CFURLSessionCreateErrorDescription(int value) { + const char *description = curl_easy_strerror(value); + return CFStringCreateWithBytes(kCFAllocatorSystemDefault, + (const uint8_t *)description, strlen(description), kCFStringEncodingUTF8, NO); +} CFURLSessionEasyHandle _Nonnull CFURLSessionEasyHandleInit() { return curl_easy_init(); @@ -135,6 +141,7 @@ CFURLSessionEasyCode CFURLSessionInit(void) { return MakeEasyCode(curl_global_init(CURL_GLOBAL_SSL)); } +int const CFURLSessionEasyErrorSize = { CURL_ERROR_SIZE + 1 }; CFURLSessionEasyCode const CFURLSessionEasyCodeOK = { CURLE_OK }; CFURLSessionEasyCode const CFURLSessionEasyCodeUNSUPPORTED_PROTOCOL = { CURLE_UNSUPPORTED_PROTOCOL }; diff --git a/CoreFoundation/URL.subproj/CFURLSessionInterface.h b/CoreFoundation/URL.subproj/CFURLSessionInterface.h index 6dc29bcf66..131b576ac7 100644 --- a/CoreFoundation/URL.subproj/CFURLSessionInterface.h +++ b/CoreFoundation/URL.subproj/CFURLSessionInterface.h @@ -48,6 +48,10 @@ typedef struct CFURLSessionEasyCode { int value; } CFURLSessionEasyCode; +CF_EXPORT CFStringRef _Nonnull CFURLSessionCreateErrorDescription(int value); + +CF_EXPORT int const CFURLSessionEasyErrorSize; + /// CURLcode CF_EXPORT CFURLSessionEasyCode const CFURLSessionEasyCodeOK; // CURLE_OK CF_EXPORT CFURLSessionEasyCode const CFURLSessionEasyCodeUNSUPPORTED_PROTOCOL; // CURLE_UNSUPPORTED_PROTOCOL diff --git a/Foundation/NSString.swift b/Foundation/NSString.swift index f17d469f6c..79fcaa5a90 100644 --- a/Foundation/NSString.swift +++ b/Foundation/NSString.swift @@ -20,7 +20,7 @@ extension unichar : ExpressibleByUnicodeScalarLiteral { } } -// Placeholder for a future implementation +/// Returns a localized string, using the main bundle if one is not specified. public func NSLocalizedString(_ key: String, tableName: String? = nil, diff --git a/Foundation/URLSession/http/EasyHandle.swift b/Foundation/URLSession/http/EasyHandle.swift index ea0356d9d7..b7b7550612 100644 --- a/Foundation/URLSession/http/EasyHandle.swift +++ b/Foundation/URLSession/http/EasyHandle.swift @@ -56,6 +56,7 @@ internal final class _EasyHandle { fileprivate var headerList: _CurlStringList? fileprivate var pauseState: _PauseState = [] internal var timeoutTimer: _TimeoutSource! + internal lazy var errorBuffer = [UInt8](repeating: 0, count: Int(CFURLSessionEasyErrorSize)) #if os(Android) static fileprivate var _CAInfoFile: UnsafeMutablePointer? #endif @@ -89,8 +90,8 @@ extension _EasyHandle { } internal extension _EasyHandle { - func completedTransfer(withErrorCode errorCode: Int?) { - delegate?.transferCompleted(withErrorCode: errorCode) + func completedTransfer(withError error: NSError?) { + delegate?.transferCompleted(withError: error) } } internal protocol _EasyHandleDelegate: class { @@ -107,7 +108,7 @@ internal protocol _EasyHandleDelegate: class { func fill(writeBuffer buffer: UnsafeMutableBufferPointer) -> _EasyHandle._WriteBufferResult /// The transfer for this handle completed. /// - parameter errorCode: An NSURLError code, or `nil` if no error occured. - func transferCompleted(withErrorCode errorCode: Int?) + func transferCompleted(withError error: NSError?) /// Seek the input stream to the given position func seekInputStream(to position: UInt64) throws /// Gets called during the transfer to update progress. @@ -146,7 +147,8 @@ extension _EasyHandle { /// Set error buffer for error messages /// - SeeAlso: https://curl.haxx.se/libcurl/c/CURLOPT_ERRORBUFFER.html func set(errorBuffer buffer: UnsafeMutableBufferPointer?) { - try! CFURLSession_easy_setopt_ptr(rawHandle, CFURLSessionOptionERRORBUFFER, buffer?.baseAddress ?? nil).asError() + let buffer = buffer ?? errorBuffer.withUnsafeMutableBufferPointer { $0 } + try! CFURLSession_easy_setopt_ptr(rawHandle, CFURLSessionOptionERRORBUFFER, buffer.baseAddress).asError() } /// Request failure on HTTP response >= 400 func set(failOnHTTPErrorCode flag: Bool) { diff --git a/Foundation/URLSession/http/HTTPURLProtocol.swift b/Foundation/URLSession/http/HTTPURLProtocol.swift index 78aff84b68..7371889614 100644 --- a/Foundation/URLSession/http/HTTPURLProtocol.swift +++ b/Foundation/URLSession/http/HTTPURLProtocol.swift @@ -134,7 +134,9 @@ fileprivate extension _HTTPURLProtocol { // NSURLErrorNoPermissionsToReadFile // NSURLErrorFileDoesNotExist self.internalState = .transferFailed - failWith(errorCode: errorCode(fileSystemError: e), request: request) + let error = NSError(domain: NSURLErrorDomain, code: errorCode(fileSystemError: e), + userInfo: [NSLocalizedDescriptionKey: "File system error"]) + failWith(error: error, request: request) return } @@ -328,17 +330,19 @@ internal extension _HTTPURLProtocol { case stream(InputStream) } - func failWith(errorCode: Int, request: URLRequest) { + func failWith(error: NSError, request: URLRequest) { //TODO: Error handling let userInfo: [String : Any]? = request.url.map { [ + NSUnderlyingErrorKey: error, NSURLErrorFailingURLErrorKey: $0, NSURLErrorFailingURLStringErrorKey: $0.absoluteString, + NSLocalizedDescriptionKey: NSLocalizedString(error.localizedDescription, comment: "N/A") ] } - let error = URLError(_nsError: NSError(domain: NSURLErrorDomain, code: errorCode, userInfo: userInfo)) - completeTask(withError: error) - self.client?.urlProtocol(self, didFailWithError: error) + let urlError = URLError(_nsError: NSError(domain: NSURLErrorDomain, code: error.code, userInfo: userInfo)) + completeTask(withError: urlError) + self.client?.urlProtocol(self, didFailWithError: urlError) } } @@ -528,16 +532,16 @@ extension _HTTPURLProtocol: _EasyHandleDelegate { } } - func transferCompleted(withErrorCode errorCode: Int?) { + func transferCompleted(withError error: NSError?) { // At this point the transfer is complete and we can decide what to do. // If everything went well, we will simply forward the resulting data // to the delegate. But in case of redirects etc. we might send another // request. guard case .transferInProgress(let ts) = internalState else { fatalError("Transfer completed, but it wasn't in progress.") } guard let request = task?.currentRequest else { fatalError("Transfer completed, but there's no current request.") } - guard errorCode == nil else { + guard error == nil else { internalState = .transferFailed - failWith(errorCode: errorCode!, request: request) + failWith(error: error!, request: request) return } @@ -555,7 +559,9 @@ extension _HTTPURLProtocol: _EasyHandleDelegate { completeTask() case .failWithError(let errorCode): internalState = .transferFailed - failWith(errorCode: errorCode, request: request) + let error = NSError(domain: NSURLErrorDomain, code: errorCode, + userInfo: [NSLocalizedDescriptionKey: "Completion failure"]) + failWith(error: error, request: request) case .redirectWithRequest(let newRequest): redirectFor(request: newRequest) } diff --git a/Foundation/URLSession/http/MultiHandle.swift b/Foundation/URLSession/http/MultiHandle.swift index 506c9a9f23..4998899bc3 100644 --- a/Foundation/URLSession/http/MultiHandle.swift +++ b/Foundation/URLSession/http/MultiHandle.swift @@ -202,12 +202,20 @@ fileprivate extension URLSession._MultiHandle { } let easyHandle = easyHandles[idx] // Find the NSURLError code - let errorCode = easyHandle.urlErrorCode(for: easyCode) - completedTransfer(forEasyHandle: easyHandle, errorCode: errorCode) + var error: NSError? + if let errorCode = easyHandle.urlErrorCode(for: easyCode) { + let errorDescription = easyHandle.errorBuffer[0] != 0 ? + String(cString: easyHandle.errorBuffer) : + CFURLSessionCreateErrorDescription(easyCode.value)._swiftObject + error = NSError(domain: NSURLErrorDomain, code: errorCode, userInfo: [ + NSLocalizedDescriptionKey: errorDescription + ]) + } + completedTransfer(forEasyHandle: easyHandle, error: error) } /// Transfer completed. - func completedTransfer(forEasyHandle handle: _EasyHandle, errorCode: Int?) { - handle.completedTransfer(withErrorCode: errorCode) + func completedTransfer(forEasyHandle handle: _EasyHandle, error: NSError?) { + handle.completedTransfer(withError: error) } }