不使用 http2 连接调用 URLSessionDataDelegate

URLSessionDataDelegate not being called using http2 connection

更新

我发现如果我 运行 服务器,然后是 macOS 应用程序并保持 40 秒(因此服务器发送了 40 "a" 个字符,每秒一个)然后最终didReceive response 委托被调用,然后 didReceive data 委托开始被每个新数据位调用。这导致在 macOS 应用程序的控制台中记录如下:

URLAuthenticationChallenge
Got response: <NSHTTPURLResponse: 0x6080000385c0> { URL: https://localhost:10443/sub } { status code: 200, headers {
    "Content-Type" = "text/plain; charset=utf-8";
    Date = "Thu, 03 Nov 2016 16:51:28 GMT";
    Vary = "Accept-Encoding";
} }
Received data: Optional("{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n{\"Data\":\"a\"}\n")
Received data: Optional("{\"Data\":\"a\"}\n")
Received data: Optional("{\"Data\":\"a\"}\n")
Received data: Optional("{\"Data\":\"a\"}\n")
Received data: Optional("{\"Data\":\"a\"}\n")
Received data: Optional("{\"Data\":\"a\"}\n")
Received data: Optional("{\"Data\":\"a\"}\n")
...

这表明某处正在进行缓冲。


我一直在测试 URLSession 如何与 HTTP/2 连接一起工作,我 运行 遇到了一些问题。

我这里有一个非常简单的 macOS 应用程序:https://github.com/hamchapman/http2-barebones-mac-app 虽然它的整个代码基本上就是这样:

class ViewController: NSViewController, URLSessionDelegate, URLSessionDataDelegate {
    override func viewDidLoad() {
        var urlComponents = URLComponents()
        urlComponents.scheme = "https"
        urlComponents.host = "localhost"
        urlComponents.port = 10443

        guard let url = urlComponents.url else {
            print("Bad URL, try again")
            return
        }

        var request = URLRequest(url: url.appendingPathComponent("/sub"))
        request.httpMethod = "SUB"
        request.timeoutInterval = REALLY_LONG_TIME

        let sessionConfiguration = URLSessionConfiguration.ephemeral
        sessionConfiguration.timeoutIntervalForResource = REALLY_LONG_TIME
        sessionConfiguration.timeoutIntervalForRequest = REALLY_LONG_TIME

        let session = Foundation.URLSession(configuration: sessionConfiguration, delegate: self, delegateQueue: nil)

        let task: URLSessionDataTask = session.dataTask(with: request)
        task.resume()
    }

    public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
        print("Got response: \(response)")
        completionHandler(.allow)
    }

    public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
        let dataString = String(data: data, encoding: .utf8)
        print("Received data: \(dataString)")
    }

    public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        print("Error: \(error)")
    }

    // So it works with self-signed certs (we don't care about TLS etc in this example)
    public func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        guard challenge.previousFailureCount == 0 else {
            challenge.sender?.cancel(challenge)
            completionHandler(.cancelAuthenticationChallenge, nil)
            return
        }

        let allowAllCredential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
        completionHandler(.useCredential, allowAllCredential)
    }
}

您可以看到正在使用的 http 方法是 SUB。如果您想订阅给定的资源,这被设计为您使用的一种方法,在我的简单示例中,该资源位于路径 /sub。理论上,当服务器有新数据要发送时,这应该能够利用 HTTP/2 流将新数据发送到 macOS 应用程序的连接。

这是我一直用作服务器的非常基本的 (Go) 应用程序:https://github.com/hamchapman/http2-barebones-server(自述文件中有关于如何 运行 它的说明)。

它基本上设置为在 /sub 接受 SUB 请求并立即发回 200 OK,然后每秒发送 "a" 作为一点数据。

我面临的问题是,就 Go 服务器而言,连接正常。但是,URLSessionDelegate 被调用时使用了预期的 URLAuthenticationChallenge(服务器只允许加密连接),但是 URLSessionDataDelegate 方法在接收到响应和接收到数据时被调用是永远不会叫。

您可以通过 运行 连接服务器然后使用以下 curl 命令来验证服务器是否按预期工作:

curl --http2 -k -v -X SUB https://localhost:10443/sub

(您可能需要下载最新版本的 curl - 请参阅此处了解信息:https://simonecarletti.com/blog/2016/01/http2-curl-macosx/

我还验证了在 macOS 应用程序中建立的连接(使用 Wireshark)实际上正在接收数据,但从未调用委托。

有谁知道为什么会这样?数据是否在某处缓冲?在 URLSession 中 HTTP/2 支持不完全吗?

因为缓冲了前512个字节:https://forums.developer.apple.com/thread/64875