UPDATEURLSessionDataDelegate не вызывается с помощью http2 соединения
Я обнаружил, что если я запускаю сервер, а затем приложение 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 (в readme есть инструкции по его запуску).
Это в основном установка, чтобы принять SUB
запрос на /sub
и отправить обратно 200 OK немедленно, а затем каждую секунду он отправляет "a"
в виде немного данных.
Проблема, с которой я сталкиваюсь, заключается в том, что, насколько это касается сервера Go, соединение выполняется нормально. Однако URLSessionDelegate
вызывается с ожидаемым URLAuthenticationChallenge
(сервер разрешает только зашифрованные соединения), но методы URLSessionDataDelegate
, вызываемые при получении ответа и при получении данных, никогда не вызываются.
Вы можете убедиться, что сервер работает, как ожидалось, запустив его, а затем, используя следующую локон команды:
curl --http2 -k -v -X SUB https://localhost:10443/sub
(возможно, потребуется загрузить последнюю версию локона - см здесь для информации : https://simonecarletti.com/blog/2016/01/http2-curl-macosx/)
Я также подтвердил, что данные фактически принимаются соединением, выполненным в приложении macOS (с использованием Wireshark), но делегат никогда не вызывается.
Кто-нибудь знает, почему это может произойти? Будут ли данные где-то буферизованы? Поддерживается ли поддержка HTTP/2 не полностью в URLSession
?
вы уверены, что completionHandler называется? –
(я случайно удалил вызов 'completeHandler (.allow)' в функции делегата 'didReceive response').Он никогда не называется, хотя, что похоже на суть проблемы. – hamchapman
Не существует ли известной проблемы с попыткой использования NSURLSession с 'localhost'? – matt