-
Notifications
You must be signed in to change notification settings - Fork 1.2k
NSLocalizedDescription for http errors taken from libcurl #1198
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
dd12300
to
f9b2c18
Compare
@@ -170,6 +171,9 @@ extension _EasyHandle { | |||
let protocols = (CFURLSessionProtocolHTTP | CFURLSessionProtocolHTTPS) | |||
try! CFURLSession_easy_setopt_long(rawHandle, CFURLSessionOptionPROTOCOLS, protocols).asError() | |||
try! CFURLSession_easy_setopt_long(rawHandle, CFURLSessionOptionREDIR_PROTOCOLS, protocols).asError() | |||
errorBuffer.withUnsafeMutableBufferPointer { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
setAllowedProtocolsToHTTPAndHTTPS()
doesn't seem to be an accurate name for this function any more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’m just using that function for some other initialisation as a convenience rather than create a new one. I think the name if probably good as it is as it relates to it’s primary purpose.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could move this extra code into another of the setup functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think @pushkarnk ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see there is a function set(errorBuffer:) which is called from _HTTPURLProtocol.configureEasyHandle(forRequest:) I could set the buffer there but it wouldn’t guarantee the buffer still exists when you call curl_easy_strerror() on the handle.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’ve pushed a new revision introducing a default error buffer so the messages are always as descriptive as possible. See what you think.
@@ -575,6 +576,10 @@ extension _HTTPURLProtocol: _EasyHandleDelegate { | |||
} | |||
} | |||
|
|||
internal struct URLErrorInfo { | |||
let code: Int, description: String? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Put these on separate lines.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough ;) Just being lazy.
@@ -330,12 +330,13 @@ internal extension _HTTPURLProtocol { | |||
case stream(InputStream) | |||
} | |||
|
|||
func failWith(errorCode: Int, request: URLRequest) { | |||
func failWith(errorCode: Int, errorDescription: String?, request: URLRequest) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be better if this function took a URLErrorInfo
instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn’t realy pan out as you would be having to create a URLErrrorInfo at the last minute in two out of three of the places where this function is called. Better to leave it as an extra argument. URLErrorInfo is mostly to convey the info through the various callbacks in place of just the code.
@@ -31,6 +31,10 @@ static CFURLSessionMultiCode MakeMultiCode(CURLMcode value) { | |||
return (CFURLSessionMultiCode) { value }; | |||
} | |||
|
|||
const char *CFURLSessionErrorDescription(int value) { | |||
return curl_easy_strerror(value); | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the memory management here? Does the result need to be freed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
buffer is either static string or buffer set/owned by EasyHandle.swift. It doesn’t need to be freed.
func completedTransfer(forEasyHandle handle: _EasyHandle, errorCode: Int?) { | ||
handle.completedTransfer(withErrorCode: errorCode) | ||
func completedTransfer(forEasyHandle handle: _EasyHandle, errorInfo: URLErrorInfo?) { | ||
handle.completedTransfer(withErrorInfo: errorInfo) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should all use Foundation's existing abstraction for errors: NSError
, instead of a custom struct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can’t construct an NSError before we pass it around as the code doesn’t initially know the URL that is needed for the userInfo.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That can be set later by creating another NSError
, or using the concept of underlying error.
It's important that we don't create several different kinds of abstractions for the same concept in one source base.
@@ -557,7 +558,7 @@ extension _HTTPURLProtocol: _EasyHandleDelegate { | |||
completeTask() | |||
case .failWithError(let errorCode): | |||
internalState = .transferFailed | |||
failWith(errorCode: errorCode, request: request) | |||
failWith(errorCode: errorCode, errorDescription: "Completion failure", request: request) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're in a somewhat unfortunate place here, because localization doesn't quite work yet, but we still should find a way to make this use the usual localized string lookup API (on bundle) instead of hard coding English strings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’ve created a placeholder for a future NSLocalizedString(_, _) implementation and used it.
@@ -48,6 +48,10 @@ typedef struct CFURLSessionEasyCode { | |||
int value; | |||
} CFURLSessionEasyCode; | |||
|
|||
CF_EXPORT const char * _Nonnull CFURLSessionErrorDescription(int value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CF API really should use a CFStringRef
as a result instead of C strings. That resolves the memory management question of the result for callers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to CFString. Unsure of the allocator I should be using if you want to check it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CF (almost) always uses kCFAllocatorSystemDefault
internally.
@@ -19,6 +19,7 @@ | |||
//===----------------------------------------------------------------------===// | |||
|
|||
#include "CFURLSessionInterface.h" | |||
#include "CFString.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#include <CoreFoundation/CFString.h>
@@ -31,6 +32,10 @@ static CFURLSessionMultiCode MakeMultiCode(CURLMcode value) { | |||
return (CFURLSessionMultiCode) { value }; | |||
} | |||
|
|||
CFStringRef CFURLSessionErrorDescription(int value) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CFURLSessionCreateErrorDescription
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New version with this change and your other suggestions.
@@ -48,6 +48,10 @@ typedef struct CFURLSessionEasyCode { | |||
int value; | |||
} CFURLSessionEasyCode; | |||
|
|||
CF_EXPORT CFStringRef _Nonnull CFURLSessionErrorDescription(int value); | |||
|
|||
CF_EXPORT int const CFURLSessionEasyErrorSize; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels like an abstraction leak, but I'm not sure what to do about it exactly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, the buffer thing is a bit untidy.
func completedTransfer(forEasyHandle handle: _EasyHandle, errorCode: Int?) { | ||
handle.completedTransfer(withErrorCode: errorCode) | ||
func completedTransfer(forEasyHandle handle: _EasyHandle, errorInfo: URLErrorInfo?) { | ||
handle.completedTransfer(withErrorInfo: errorInfo) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That can be set later by creating another NSError
, or using the concept of underlying error.
It's important that we don't create several different kinds of abstractions for the same concept in one source base.
I was using the errorBuffer incorrectly. In the event of an error it can be written to with a more specific text which you have to check for or fall back to curl_easy_strerror() as shown in the example further down https://curl.haxx.se/libcurl/c/CURLOPT_ERRORBUFFER.html. I’m now seeing more complete error messages like: Optional(Foundation.URLError(_nsError: Couldn't resolve host 'wsssww.bbc.co.uk')) |
1214e1d
to
6318490
Compare
This has stabilized and I’ve squashed it unless you have any further comments. |
let error = URLError(_nsError: NSError(domain: NSURLErrorDomain, code: errorCode, userInfo: userInfo)) | ||
completeTask(withError: error) | ||
self.client?.urlProtocol(self, didFailWithError: error) | ||
let urlErrror = URLError(_nsError: NSError(domain: NSURLErrorDomain, code: error.code, userInfo: userInfo)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tyyypo ;)
Foundation/NSString.swift
Outdated
@@ -20,6 +20,11 @@ extension unichar : ExpressibleByUnicodeScalarLiteral { | |||
} | |||
} | |||
|
|||
// Placeholder for a future implementation | |||
public func NSLocalizedString(_ key: String, _ comment: String?) -> String { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is, I believe, not the correct signature for NSLocalizedString in Swift. For one, “comment” should be an argument label (i.e., comment:
and not _ comment:
), but there are also other parameters with default values that are not reflected here. I understand it is a placeholder, but the signature should match Darwin Foundation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, if we're going to add it we may as well put in the placeholder as it's done in Darwin:
public
func NSLocalizedString(_ key: String,
tableName: String? = nil,
bundle: Bundle = Bundle.main,
value: String = "",
comment: String) -> String {
return bundle.localizedString(forKey: key, value:value, table:tableName)
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Seems to work
@@ -135,6 +141,7 @@ CFURLSessionEasyCode CFURLSessionInit(void) { | |||
return MakeEasyCode(curl_global_init(CURL_GLOBAL_SSL)); | |||
} | |||
|
|||
int const CFURLSessionEasyErrorSize = { CURL_ERROR_SIZE+1 }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: spaces around +
@@ -557,7 +561,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"]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should be calling that new NSLocalizedString
function here (once its signature is fixed).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Signature fixed. NSLocalizedString() is called indirectly in the creation of the final URLError struct in failWith(error:….
56bc8e9
to
79fa1d3
Compare
fyi @saiHemak |
@swift-ci please test |
@johnno1962 Sorry for the delay in reviewing. I went through the review comments and the changes, and I think we should go ahead and merge this. Does this look good to you @parkera ? |
@swift-ci please test |
@johnno1962 That the Android toolchain is complete is fantastic news indeed! Thank you :) |
@parkera has the above met your requirements now? If so, can you complete the review? |
Foundation/NSString.swift
Outdated
bundle: Bundle = Bundle.main, | ||
value: String = "", | ||
comment: String) -> String { | ||
return bundle.localizedString(forKey: key, value:value, table:tableName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: spaces after colons.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Ian. Amended.
5cf17a7
to
d809b59
Compare
@swift-ci please test |
Signature changes were to bring it into line with the documented Darwin signature. It was me that made it optional. |
Can we split this into two PRs? The first can add the |
7b7c423
to
21d2b72
Compare
Right, rebased and merged. You know I’ll never never get that hour of my life back 😀! |
21d2b72
to
eafde74
Compare
OK, I've gone through all of @parkera @xwu and @ianpartridge comments and I'm happy that they've been addressed. I'm going to kick off a test run, and provided that passes and there's no objections from @parkera et al will do a test'n'merge later on tonight. |
@swift-ci please test |
@swift-ci please test and merge |
Hi Apple,
At the moment any network error using a URLSession contains only the default "The operation could not be completed” as its NSLocalizedDescription. This PR conveys the value of curl_easy_strerror through the implementation so something more specific is reported such as "Foundation.URLError(_nsError: Peer certificate cannot be authenticated with given CA certificates)"
Thanks.