Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What kind of URL is not conforming to RFC 3986 but is conforming to RFC 1808, RFC 1738, and RFC 2732?

The doc of URLComponents.init(url:resolvingAgainstBaseURL:) says:

Returns the initialized URL components object, or nil if the URL could not be parsed.

Knowing that:

  • Swift URL/NSURL is for URLs based on RFC 1808, RFC 1738, and RFC 2732: https://developer.apple.com/documentation/foundation/nsurl
  • Swift URLComponents/NSURLComponents is for URLs based on RFC 3986: https://developer.apple.com/documentation/foundation/nsurlcomponents

I assume that the initialization of URLComponents will fail when the URL is conforming to RFC 1808/1738/2732 but not RFC 3986. What kind of URL is that? Any example?

The only hint I have so far as a difference may be related to different reserved characters?

like image 520
Cœur Avatar asked Apr 10 '19 09:04

Cœur


1 Answers

Let's explore it from its source code, as Swift Foundation is open-source.

  1. The URLComponents initializer is implemented in apple/swift – URLComponents.swift and apple/swift-corelibs-foundation – URLComponents.swift and simply calls the initializer of NSURLComponents.

  2. The NSURLComponents initializer is implemented in apple/swift-corelibs-foundation – NSURL.swift and simply calls _CFURLComponentsCreateWithURL.

  3. _CFURLComponentsCreateWithURL is implemented in apple/swift-corelibs-foundation – CFURLComponents.c and does:

    • a failable copy with CFURLCopyAbsoluteURL
    • a failable creation with _CFURLComponentsCreateWithString which calls:
      • _CFURIParserParseURIReference + a failable _CFURIParserURLStringIsValid
  4. CFURLCopyAbsoluteURL is implemented in apple/swift-corelibs-foundation – CFURL.c and only fails for:

    #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
        if ( base && CFURLIsFileReferenceURL(base) && !CFURLHasDirectoryPath(base) ) {
            // 16695827 - If the base URL is a file reference URL which doesn't end with a slash, we have to convert it to a file path URL before we can make it absolute.
            base = CFURLCreateFilePathURL(alloc, base, NULL);
            if ( !base ) {
                // could not convert file reference URL to file path URL -- fail will NULL
                return NULL;
            }
        }
    #endif
    

    The implementation of CFURLCreateFilePathURL is in opensource.apple.com/source/CF – CFURL.c, and my understanding is that it will only fail if there is no scheme or no path, which shouldn't be possible as we previously tested for a file scheme or file existence with CFURLIsFileReferenceURL.

  5. _CFURIParserParseURIReference is implemented in apple/swift-corelibs-foundation – CFURLComponents_URIParser.c and will only fail if the URL length is more than 2 GB, which I believe is unrelated to RFC specifications.

  6. _CFURIParserURLStringIsValid will essentially call _CFURIParserValidateComponent for each component and fail for invalid characters or escape sequences. This is possibly the most relevant part.

Now, with a bit of experiments, we know we need a scheme (for instance, https:// or simply a://) and we play with the reserved characters to come up with examples such as:

// OK
let url = URL(string: "a://@@")!
// CRASH
let components = URLComponents(url: url, resolvingAgainstBaseURL: true)!

Trying the alternative initializer of URLComponents will also fail, so don't try to think it's different:

// CRASH
let components = URLComponents(string: url.absoluteString)!

Conclusion

"a://@@" is an example of valid NSURL but invalid RFC 3986.


On a side note, some Swift people seem to wish for the future to unify the support of URL and URLComponents (no more RFC differences) as seen in URL.swift:

// Future implementation note:
// NSURL (really CFURL, which provides its implementation) has quite a few quirks in its processing of some more esoteric (and some not so esoteric) strings. We would like to move much of this over to the more modern NSURLComponents, but binary compat concerns have made this difficult.
// Hopefully soon, we can replace some of the below delegation to NSURL with delegation to NSURLComponents instead. It cannot be done piecemeal, because otherwise we will get inconsistent results from the API.

I'm not sure how they plan to do this, as it would mean that either URL(string: "a://@@") would fail or URLComponents(string: "a://@@") would succeed.

like image 73
Cœur Avatar answered Sep 26 '22 15:09

Cœur