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

Posted

技术标签:

【中文标题】什么样的 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? 【发布时间】:2019-08-31 16:52:49 【问题描述】:

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

返回已初始化的 URL 组件对象,如果无法解析 URL,则返回 nil。

知道:

Swift URL/NSURL 用于基于 RFC 1808、RFC 1738 和 RFC 2732 的 URL:https://developer.apple.com/documentation/foundation/nsurl Swift URLComponents/NSURLComponents 用于基于 RFC 3986 的 URL:https://developer.apple.com/documentation/foundation/nsurlcomponents

我假设当 URL 符合 RFC 1808/1738/2732 但不符合 RFC 3986 时,URLComponents 的初始化将失败。那是什么类型的 URL?有什么例子吗?

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

【问题讨论】:

【参考方案1】:

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

    URLComponents 初始化器在 apple/swift – URLComponents.swift 和 apple/swift-corelibs-foundation – URLComponents.swift 中实现,并简单地调用 NSURLComponents 的初始化器。

    NSURLComponents 初始化器在 apple/swift-corelibs-foundation – NSURL.swift 中实现,并简单地调用 _CFURLComponentsCreateWithURL

    _CFURLComponentsCreateWithURL 在apple/swift-corelibs-foundation – CFURLComponents.c 中实现,并且:

    带有CFURLCopyAbsoluteURL 的失败副本 _CFURLComponentsCreateWithString 的失败创建,它调用: _CFURIParserParseURIReference + 一个失败的_CFURIParserURLStringIsValid

    CFURLCopyAbsoluteURL 在apple/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或者没有路径的情况下才会失败,这应该是不可能的,因为我们之前测试过文件scheme或者文件存在CFURLIsFileReferenceURL

    _CFURIParserParseURIReference 在apple/swift-corelibs-foundation – CFURLComponents_URIParser.c 中实现,只有在 URL 长度超过 2 GB 时才会失败,我认为这与 RFC 规范无关。

    _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 和 URLComponents 的支持(不再有 RFC 差异)as seen in URL.swift:

// 未来实现说明: // NSURL(实际上是 CFURL,它提供了它的实现)在处理一些更深奥(和一些不那么深奥)的字符串时有很多怪癖。我们希望将其中的大部分内容转移到更现代的 NSURLComponents,但二进制兼容性问题让这变得困难。 // 希望很快,我们可以将下面的一些对 NSURL 的委托替换为对 NSURLComponents 的委托。不能零碎完成,否则我们会从 API 中得到不一致的结果。

我不确定他们打算如何做到这一点,因为这意味着URL(string: "a://@@") 将失败或URLComponents(string: "a://@@") 将成功。

【讨论】:

这真的很有趣,谢谢!还帮助我摆脱了在我的代码覆盖率报告中困扰我的一行红色 - 即使是这样一个边缘情况! 聪明的香肠 :-)

以上是关于什么样的 URL 不符合 RFC 3986 但符合 RFC 1808、RFC 1738 和 RFC 2732?的主要内容,如果未能解决你的问题,请参考以下文章

Java URL 类 getPath()、getQuery() 和 getFile() 与 RFC3986 URI 语法不一致

URL解析测试套件

RFC 3986 中关于非英文字符的“不区分大小写”是啥意思?

Rails 6- 符合 RFC5322 的电子邮件验证

RFC 3986 中的 HTTP 请求主机值语法

不在 URL 中编码括号有啥危险?