什么样的 URL 不符合 RFC 3986 但符合 RFC 1808、RFC 1738 和 RFC 2732?

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

URLComponents.init(url:resolvingAgainstBaseURL:) 的文档说:

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

知道:

我认为当 URL 符合 RFC 1808/1738/2732 但不符合 RFC 3986 时 URLComponents 的初始化将失败。什么样的 URL 是吗?有例子吗?

到目前为止,我唯一的提示可能与不同的保留字符有关?

让我们从它的源代码来探索它,因为 Swift Foundation 是开源的。

  1. URLComponents初始化器在apple/swift – URLComponents.swift and apple/swift-corelibs-foundation – URLComponents.swift中实现,只是调用了NSURLComponents.

  2. 的初始化器
  3. NSURLComponents 初始化器在 apple/swift-corelibs-foundation – NSURL.swift 中实现,只需调用 _CFURLComponentsCreateWithURL.

  4. _CFURLComponentsCreateWithURLapple/swift-corelibs-foundation – CFURLComponents.c 中实现并执行:

    • 具有 CFURLCopyAbsoluteURL
    • 的失败副本
    • 带有 _CFURLComponentsCreateWithString 的失败创建调用:
      • _CFURIParserParseURIReference + 一个失败的 _CFURIParserURLStringIsValid
  5. CFURLCopyAbsoluteURLapple/swift-corelibs-foundation – CFURL.c 中实现并且仅在以下情况下失败:

    #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
    

    CFURLCreateFilePathURL的实现在opensource.apple.com/source/CF – CFURL.c,我的理解是没有scheme或者没有路径才会失败,我们之前测试过应该不会CFURLIsFileReferenceURL.

  6. 的文件方案或文件存在
  7. _CFURIParserParseURIReferenceapple/swift-corelibs-foundation – CFURLComponents_URIParser.c 中实现,只有当 URL 长度超过 2 GB 时才会失败,我认为这与 RFC 规范无关。

  8. _CFURIParserURLStringIsValid 本质上会为每个组件调用 _CFURIParserValidateComponent 并且会因无效字符或转义序列而失败。 这可能是最相关的部分。

现在,通过一些实验,我们知道我们需要一个方案(例如,https:// 或简单地 a://)并且我们使用保留字符来得出示例,例如:

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

尝试 URLComponents 的替代初始化程序也会失败,所以不要试图认为它是不同的:

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

结论

"a://@@" 是有效 NSURL 但无效 RFC 3986 的示例。


附带一提,一些 Swift 人似乎希望未来统一对 URL 和 URL 组件的支持(不再有 RFC 差异)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.

我不确定他们打算如何执行此操作,因为这意味着要么 URL(string: "a://@@") 会失败,要么 URLComponents(string: "a://@@") 会成功。