【问题标题】:URLSessionDataDelegate not being called using http2 connection未使用 http2 连接调用 URLSessionDataDelegate
【发布时间】:2017-03-17 06:30:54
【问题描述】:

更新

我发现,如果我运行服务器,然后运行 ​​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?

【问题讨论】:

  • 你确定调用了completionHandler?
  • (我在didReceive response 委托函数中无意中删除了对completionHandler(.allow) 的调用)。它从来没有被调用过,感觉就像问题的症结所在
  • 在尝试将 NSURLSession 与 localhost 一起使用时是否存在已知问题?
  • 我不知道,但这似乎完全有可能 - 有链接吗?
  • 我添加了关于我现在看到的内容的更新。似乎localhost 不是问题,但正在进行某种缓冲。

标签: swift nsurlsession http2 nsurlsessiondatatask


【解决方案1】:

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

【讨论】:

    猜你喜欢
    • 2016-08-23
    • 2019-03-11
    • 2020-07-11
    • 2018-03-06
    • 2016-10-27
    • 2020-08-02
    • 1970-01-01
    • 2019-11-25
    • 1970-01-01
    相关资源
    最近更新 更多