2016-11-03 2 views
0

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?

+0

вы уверены, что completionHandler называется? –

+0

(я случайно удалил вызов 'completeHandler (.allow)' в функции делегата 'didReceive response').Он никогда не называется, хотя, что похоже на суть проблемы. – hamchapman

+0

Не существует ли известной проблемы с попыткой использования NSURLSession с 'localhost'? – matt

ответ

Смежные вопросы