Merge pull request #1198 from johnno1962e/NSLocalizedDescription

diff --git a/CoreFoundation/URL.subproj/CFURLSessionInterface.c b/CoreFoundation/URL.subproj/CFURLSessionInterface.c
index f44f44d..887b4f2 100644
--- a/CoreFoundation/URL.subproj/CFURLSessionInterface.c
+++ b/CoreFoundation/URL.subproj/CFURLSessionInterface.c
@@ -19,6 +19,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "CFURLSessionInterface.h"
+#include <CoreFoundation/CFString.h>
 #include <curl/curl.h>
 
 FILE* aa = NULL;
@@ -31,6 +32,11 @@
     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 @@
     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 6dc29bc..131b576 100644
--- a/CoreFoundation/URL.subproj/CFURLSessionInterface.h
+++ b/CoreFoundation/URL.subproj/CFURLSessionInterface.h
@@ -48,6 +48,10 @@
     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 f17d469..79fcaa5 100644
--- a/Foundation/NSString.swift
+++ b/Foundation/NSString.swift
@@ -20,7 +20,7 @@
     }
 }
 
-// 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 ea0356d..b7b7550 100644
--- a/Foundation/URLSession/http/EasyHandle.swift
+++ b/Foundation/URLSession/http/EasyHandle.swift
@@ -56,6 +56,7 @@
     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<Int8>?
     #endif
@@ -89,8 +90,8 @@
 }
 
 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 @@
     func fill(writeBuffer buffer: UnsafeMutableBufferPointer<Int8>) -> _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 @@
     /// Set error buffer for error messages
     /// - SeeAlso: https://curl.haxx.se/libcurl/c/CURLOPT_ERRORBUFFER.html
     func set(errorBuffer buffer: UnsafeMutableBufferPointer<UInt8>?) {
-        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 78aff84..7371889 100644
--- a/Foundation/URLSession/http/HTTPURLProtocol.swift
+++ b/Foundation/URLSession/http/HTTPURLProtocol.swift
@@ -134,7 +134,9 @@
             //     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 @@
         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 @@
         }
     }
 
-    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 @@
             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 506c9a9..4998899 100644
--- a/Foundation/URLSession/http/MultiHandle.swift
+++ b/Foundation/URLSession/http/MultiHandle.swift
@@ -202,12 +202,20 @@
         }
         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)
     }
 }