URLSessionUploadTask автоматически отменяется мгновенно

У меня есть эта странная проблема, в которой недавно созданный URLSessionUploadTask отменяется мгновенно. Я не уверен, является ли это ошибкой в ​​текущей бета-версии Xcode 8.

Я подозреваю, что это может быть ошибка, потому что код, который я собираюсь опубликовать, работал нормально только один раз. После этого в него не было внесено никаких изменений, и он просто перестал работать. Да, он буквально побежал один раз, а потом перестал работать. Я опубликую ошибку ближе к концу.

Я опубликую код ниже, но сначала я подведу итог, как работает логика здесь.

Мой тест или пользовательский API (IE для использования на игровых площадках или непосредственно в приложениях) вызывает authorize метод. это authorize метод в свою очередь вызовет buildPOSTTask, который создаст действительный URL и вернет URLSessionUploadTask быть использованным authorize метод.

С учетом сказанного, код ниже:

Сессия:

internal let urlSession = URLSession(configuration: .default)

Функция для создания задачи загрузки:

internal func buildPOSTTask(onURLSession urlSession: URLSession, appendingPath path: String, withPostParameters postParams: [String : String]?, getParameters getParams: [String : String]?, httpHeaders: [String : String]?, completionHandler completion: URLSessionUploadTaskCompletionHandler) -> URLSessionUploadTask {
    let fullURL: URL
    if let gets = getParams {
        fullURL = buildURL(appendingPath: path, withGetParameters: gets)
    } else {
        fullURL = URL(string: path, relativeTo: baseURL)!
    }

    var request = URLRequest(url: fullURL)
    request.httpMethod = "POST"

    var postParameters: Data? = nil

    if let posts = postParams {
        do {
            postParameters = try JSONSerialization.data(withJSONObject: posts, options: [])
        } catch let error as NSError {
            fatalError("[\(#function) \(#line)]: Could not build POST task: \(error.localizedDescription)")
        }
    }

    let postTask = urlSession.uploadTask(with: request, from: postParameters, completionHandler: completion)
    return postTask
}

Функция аутентификации, которая использует задачу, созданную вышеуказанной функцией:

    public func authorize(withCode code: String?, completion: AccessTokenExchangeCompletionHandler) {

// I have removed a lot of irrelevant code here, such as the dictionary building code, to make this snippet shorter.

        let obtainTokenTask = buildPOSTTask(onURLSession: self.urlSession, appendingPath: "auth/access_token", withPostParameters: nil, getParameters: body, httpHeaders: nil) { (data, response, error) in
            if let err = error {
                completion(error: err)
            } else {
                print("Response is \(response)")
                completion(error: nil)
            }
        }

        obtainTokenTask.resume()
    }

Я поймал эту ошибку в тесте:

let testUser = Anilist(grantType: grant, name: "Test Session")

let exp = expectation(withDescription: "Waiting for authorization")

testUser.authorize(withCode: "a valid code") { (error) in
    if let er = error {
        XCTFail("Authentication error: \(er.localizedDescription)")
    }
    exp.fulfill()
}
self.waitForExpectations(withTimeout: 5) { (err) in
    if let error = err {
        XCTFail(error.localizedDescription)
    }
}

Это всегда терпит неудачу немедленно с этой ошибкой:

Домен ошибки =NSURLErrorDomain Code=-999 "отменен" NSErrorFailingURLStringKey = https://anilist.co/api/auth/access_token?client_secret=REMOVED&grant_type=authorization_code&redirect_uri=genericwebsitethatshouldntexist.bo&client_id=ibanez-hod6w&code=REMOVED

Вот несколько вещей, которые нужно иметь в виду:

  • URL, используемый сеансом, действителен.
  • Все учетные данные действительны.
  • Он мгновенно завершается с ошибкой "отменено", которой раньше просто не было. Я нигде не отменяю задачу, поэтому она отменяется системой.
  • Также происходит сбой на игровых площадках с включенным неопределенным исполнением. Это не ограничивается моими тестами.

Вот список вещей, которые я пробовал:

  • Поскольку я подозреваю, что это ошибка, я сначала попытался очистить свой проект, удалить производные данные и сбросить все симуляторы. Никто из них не работал.
  • Даже дошел до перезагрузки моего Mac...
  • Под небольшим подозрением, что задача загрузки была освобождена из-за отсутствия сильных указателей, и, в свою очередь, вызывает cancel Я тоже переписал authorize вернуть задачу, созданную buildPOSTTask и назначил его переменной в моем тесте. Задача все еще отменялась.

Вещи, которые мне еще предстоит попробовать (но я приму любые другие идеи, когда буду работать над этим):

  • Запустите его на физическом устройстве. В настоящее время загружается iOS 10 на iPad, так как это проект iOS 10. РЕДАКТИРОВАТЬ: Я только что попытался, и это невозможно сделать.

У меня нет идей, что попробовать. Сгенерированные журналы, похоже, не содержат никакой полезной информации.

РЕДАКТИРОВАТЬ:

Я решил просто опубликовать весь проект здесь. Когда все будет готово, все будет с открытым исходным кодом, а полученные мной учетные данные API предназначены для тестового приложения.

ALCKit

4 ответа

Решение

После того, как я безостановочно боролся с этим в течение 6 дней, и после того, как безостановочно погуглил над решением, я действительно счастлив сказать, что наконец-то понял это.

Оказывается, что по какой-то таинственной причине from: параметр в uploadTask(with:from:completionHandler) не может быть ноль. Несмотря на то, что параметр помечен как необязательный Data, он отменяется мгновенно, когда он отсутствует. Вероятно, это ошибка со стороны Apple, и я открыл ошибку, когда не смог заставить ее работать, поэтому я обновлю свой отчет об ошибке этой новой информацией.

С учетом сказанного все, что мне нужно было сделать, это обновить мой buildPOSTTask метод для учета возможности пропущенного словаря быть нулем. Теперь все работает нормально:

internal func buildPOSTTask(onURLSession urlSession: URLSession, appendingPath path: String, withPostParameters postParams: [String : String]?, getParameters getParams: [String : String]?, httpHeaders: [String : String]?, completionHandler completion: URLSessionUploadTaskCompletionHandler) -> URLSessionUploadTask {
    let fullURL: URL
    if let gets = getParams {
        fullURL = buildURL(appendingPath: path, withGetParameters: gets)
    } else {
        fullURL = URL(string: path, relativeTo: baseURL)!
    }

    var request = URLRequest(url: fullURL)
    request.httpMethod = "POST"

    var postParameters: Data

    if let posts = postParams {
        do {
            postParameters = try JSONSerialization.data(withJSONObject: posts, options: [])
        } catch let error as NSError {
            fatalError("[\(#function) \(#line)]: Could not build POST task: \(error.localizedDescription)")
        }
    } else {
        postParameters = Data()
    }

    let postTask = urlSession.uploadTask(with: request, from: postParameters, completionHandler: completion)
    return postTask
}

Вы случайно не используете стороннюю библиотеку, такую ​​как Ensighten? У меня была точно такая же проблема в бета-версии XCode 8 (прекрасно работает в XCode 7), и все мои блоки с параметрами nil вызывали сбои. Оказывается, это была библиотека, выполняющая некоторую кодировку, вызывающую проблему.

Ваш сервер сломан..

tcp_connection_cancel 1
nw_socket_handle_socket_event Event mask: 0x4
nw_socket_handle_socket_event Socket received WRITE_CLOSE event
nw_endpoint_handler_cancel [1 anilist.co:443 ready resolver (satisfied)]
nw_endpoint_handler_cancel [1.1 104.28.1.44:443 ready socket-flow (satisfied)]
__nw_socket_service_writes_block_invoke sendmsg(fd 9, 31 bytes): socket has been closed
nw_endpoint_flow_protocol_error [1.1
104.28.1.44:443 cancelled socket-flow (null)] Socket protocol sent error: [32] Broken pipe
nw_endpoint_flow_protocol_disconnected [1.1 104.28.1.44:443 cancelled socket-flow (null)] Output protocol disconnected
nw_endpoint_handler_cancel [1.2 104.28.0.44:443 initial path (null)] 
nw_resolver_cancel_on_queue 0x60800010da40
[NWConcrete_tcp_connection dealloc] 1
[User Defaults] CFPrefsPlistSource<0x6180000f8700> (Domain: XIO.PrivateAPITest, User: kCFPreferencesCurrentUser, ByHost: No, Container: (null)) is waiting for writes to complete so it can determine if new data is available

Он ждет бесконечно "пишет", чтобы завершить..

Он отправляет запрос.. выполняет рукопожатие SSL и не получает ответа. Это время ожидания и считает это неработающим запросом..

class WTF : NSObject, URLSessionDelegate {

    var urlSession: URLSession!

    override init() {
        super.init()

        urlSession = URLSession(configuration: .default, delegate: self, delegateQueue: nil)

        var request = URLRequest(url: URL(string: "https://anilist.co/api/auth/access_token?client_secret=REMOVED&grant_type=authorization_code&redirect_uri=genericwebsitethatshouldntexist.bo&client_id=ibanez-hod6w&code=REMOVED")!)
        request.httpMethod = "POST"

        let data = try! JSONSerialization.data(withJSONObject: ["Test":"Test"], options: [])

        urlSession.uploadTask(with: request, from: data).resume()
    }

    func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {

        completionHandler(.performDefaultHandling, nil)

    }

    func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {

    }

    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: NSError?) {

    }

    func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: (URLRequest?) -> Void) {

        completionHandler(request)

    }

    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: (URLSession.ResponseDisposition) -> Void) {

        completionHandler(.allow)

    }

    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {

    }
}

Для меня это была слабая ссылка, вызывающая проблему, поэтому я изменил

      completion: { [weak self] (response: Result<ResponseType, Error>)

к

      completion: { [self] (response: Result<ResponseType, Error>)
Другие вопросы по тегам