Skip to content

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

Merged
merged 1 commit into from
Oct 13, 2017

Conversation

johnno1962
Copy link
Contributor

@johnno1962 johnno1962 commented Sep 2, 2017

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.

@@ -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 {
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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 ?

Copy link
Contributor Author

@johnno1962 johnno1962 Sep 5, 2017

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.

Copy link
Contributor Author

@johnno1962 johnno1962 Sep 5, 2017

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?
Copy link
Contributor

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.

Copy link
Contributor Author

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) {
Copy link
Contributor

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?

Copy link
Contributor Author

@johnno1962 johnno1962 Sep 5, 2017

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);
}
Copy link
Contributor

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?

Copy link
Contributor Author

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)
}
Copy link
Contributor

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.

Copy link
Contributor Author

@johnno1962 johnno1962 Sep 5, 2017

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.

Copy link
Contributor

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)
Copy link
Contributor

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.

Copy link
Contributor Author

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);
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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"
Copy link
Contributor

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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CFURLSessionCreateErrorDescription

Copy link
Contributor Author

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;
Copy link
Contributor

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.

Copy link
Contributor Author

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)
}
Copy link
Contributor

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.

@johnno1962
Copy link
Contributor Author

johnno1962 commented Sep 6, 2017

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'))

@johnno1962 johnno1962 force-pushed the NSLocalizedDescription branch from 1214e1d to 6318490 Compare September 7, 2017 08:54
@johnno1962
Copy link
Contributor Author

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))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tyyypo ;)

@@ -20,6 +20,11 @@ extension unichar : ExpressibleByUnicodeScalarLiteral {
}
}

// Placeholder for a future implementation
public func NSLocalizedString(_ key: String, _ comment: String?) -> String {
Copy link
Contributor

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.

Copy link
Contributor

@parkera parkera Sep 7, 2017

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)
}

Copy link
Contributor Author

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 };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: spaces around +

parkera
parkera previously requested changes Sep 7, 2017
@@ -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"])
Copy link
Contributor

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).

Copy link
Contributor Author

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:….

@johnno1962 johnno1962 force-pushed the NSLocalizedDescription branch 2 times, most recently from 56bc8e9 to 79fa1d3 Compare September 7, 2017 20:05
@pushkarnk
Copy link
Member

fyi @saiHemak

@johnno1962
Copy link
Contributor Author

johnno1962 commented Sep 14, 2017

Hi @parkera, anything else you need from me to merge this? I can then rebase and we could focus on landing #1113 and I’ll be out of your hair for another year! The Android toolchain is essentially complete.

@alblue
Copy link
Contributor

alblue commented Sep 15, 2017

@swift-ci please test

@pushkarnk
Copy link
Member

@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 ?

@pushkarnk
Copy link
Member

@swift-ci please test

@pushkarnk
Copy link
Member

@johnno1962 That the Android toolchain is complete is fantastic news indeed! Thank you :)

@alblue
Copy link
Contributor

alblue commented Sep 18, 2017

@parkera has the above met your requirements now? If so, can you complete the review?

bundle: Bundle = Bundle.main,
value: String = "",
comment: String) -> String {
return bundle.localizedString(forKey: key, value:value, table:tableName)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: spaces after colons.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Ian. Amended.

@johnno1962 johnno1962 force-pushed the NSLocalizedDescription branch from 5cf17a7 to d809b59 Compare September 19, 2017 10:28
@ianpartridge
Copy link
Contributor

@swift-ci please test

@alblue
Copy link
Contributor

alblue commented Oct 5, 2017

@parkera can you revisit this please; in particular the signature changes in d809b59 which may or may not be desirable?

@johnno1962
Copy link
Contributor Author

Signature changes were to bring it into line with the documented Darwin signature. It was me that made it optional.

@alblue
Copy link
Contributor

alblue commented Oct 13, 2017

Can we split this into two PRs?

The first can add the NSLocalizedDescription with the right syntax. I think that's the main one that @parkera wanted to check. Once we've go that agreed and merged, we can rebase the remainder of this change onto that one. Sound good?

@johnno1962
Copy link
Contributor Author

johnno1962 commented Oct 13, 2017

OK, I’ve created #1263 but my main concern as it has always been is to have this PR merged before #1216 as it will break in way that I don’t have the time to fix/test. Hopefully we can agree the signature of NSLocalizedDescription quickly!

@johnno1962 johnno1962 force-pushed the NSLocalizedDescription branch 2 times, most recently from 7b7c423 to 21d2b72 Compare October 13, 2017 13:14
@johnno1962
Copy link
Contributor Author

Right, rebased and merged. You know I’ll never never get that hour of my life back 😀!

@johnno1962 johnno1962 force-pushed the NSLocalizedDescription branch from 21d2b72 to eafde74 Compare October 13, 2017 14:43
@alblue
Copy link
Contributor

alblue commented Oct 13, 2017

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.

@alblue
Copy link
Contributor

alblue commented Oct 13, 2017

@swift-ci please test

@alblue
Copy link
Contributor

alblue commented Oct 13, 2017

@swift-ci please test and merge

@swift-ci swift-ci merged commit a0f8df7 into swiftlang:master Oct 13, 2017
@johnno1962
Copy link
Contributor Author

fireworks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants