Код состояния 400: NSHTTPURLResponse на вызовы, сделанные с помощью URLSession.shared.dataTask

Это проблема, с которой я столкнулся после того, как обновил iOS 11 (15A5304i) и Xcode 9 (9M137d) до последней бета-версии.

Когда я возвращаюсь к предыдущей бета-версии, она исчезает.

Выполнение любых вызовов GET https на веб-сайт приводит к ответу 400 от серверов.

networkError(FoodAPI.NetworkError.responseStatusError(status: 400, message: "<NSHTTPURLResponse: 0x1c4239700> { URL: https://stackoverflow.com/ } { status code: 400, headers {\n    Date = \"Thu, 22 Jun 2017 17:41:03 GMT\";\n} }"))

Вот как я звоню

func callTask(_ request: URLRequest, completion: @escaping (ResultType<Data, NetworkError>) -> Void) {
  let task = URLSession.shared.dataTask(with: request) { data, response, error in

    guard let response = response as? HTTPURLResponse else {
      completion(.failure(.responseInvalidFormat))
      return
    }

    if response.statusCode >= 400 {
      let error = response.description
      completion(.failure(.responseStatusError(status: response.statusCode, message: error)))
      return
    }

    guard let data = data else {
      completion(.failure(.responseNoDataError))
      return
    }

    completion(.success(data))
  }
  task.resume()
}

и вот как формируется запрос:

  private func generateRequest(urlString: String,
                               method: HttpMethod = .get,
                               contentType: ContentType = .json,
                               headers: [String: String] = ["":""],
                               body: [String: Any]? = nil) throws -> URLRequest {


    guard let url = URL(string: urlString) else {
      throw NetworkError.requestGenerationFail(url: urlString, httpMethod: method)
    }
    var request = URLRequest(url: url)
    request.httpMethod = method.rawValue
    request.setValue("***** by Reza: info: [email protected]", forHTTPHeaderField: "User-Agent")

    request.allHTTPHeaderFields = headers
    if contentType == .urlEncoded && headers["content-type"] ?? "" != ContentType.urlEncoded.rawValue {
      request.addValue(ContentType.urlEncoded.rawValue, forHTTPHeaderField: "content-type")
    }

    if let body = body {
      request.httpBody = try self.generateBody(body: body, contentType: contentType)
    }

    return request
  }

Опять же, это отлично работает в предыдущей бета-версии iOS 11, однако новая приводит к ответу 400 на вызовы https к веб-серверам с небольшим количеством данных или без них.

Стоит отметить, что сначала я обращаюсь к API конечной точки Yelp, который размещен на https://api.yelp.com/. . Это соединение удалось без каких-либо проблем. Любые дальнейшие вызовы на другой хост (https://stackoverflow.com, https://www.yelp.com) возвращает 400.

Когда я запускаю код в симуляторе, используя старую бета-версию, он работает нормально, когда я развертываю его на своем iPhone 6, который использует новую iOS, это приводит к 400 с серверов.

Я не проверял это на принадлежащем мне сервере, поэтому я могу просматривать журналы, но что-то по пути ломается и заставляет серверы возвращаться с неверным ответом на запрос. Эти же вызовы прекрасно работают с curl, postman, браузерами и предыдущими сборками iOS.

Я надеялся, что кто-то, кто сталкивался с той же проблемой, может пролить свет.


person Reza Shirazian    schedule 22.06.2017    source источник
comment
Вы сравнивали необработанные запросы (например, с Чарльзом или просто сбросом данных) с использованием обеих бета-версий? Это может дать вам представление о том, в чем проблема (параметр enconding? И т. д.)   -  person nathan    schedule 23.06.2017
comment
@nts, все сообщения зашифрованы, поэтому то, что я вижу на Чарльзе, нечитаемо для обеих бета-версий. Интересно, что вызовы конечных точек http завершаются успешно.   -  person Reza Shirazian    schedule 23.06.2017
comment
Пользовательское шифрование или просто HTTPS?   -  person nathan    schedule 23.06.2017
comment
Просто HTTPS.....   -  person Reza Shirazian    schedule 23.06.2017
comment
Чарльз поддерживает декодирование SSL, установив собственный профиль. Подробнее здесь charlesproxy.com/documentation/proxying/ssl-proxying   -  person nathan    schedule 23.06.2017
comment
Я посмотрю на это, я новичок в Чарльзе, и мне нужно кое-что прочитать об этом. Спасибо за ссылки!   -  person Reza Shirazian    schedule 23.06.2017
comment
Ага. Найдите раздел либо для симулятора iOS, либо для реального устройства. Или вы можете просто использовать библиотеку ведения журнала/отладки (например, Dotzu, netfox)   -  person nathan    schedule 23.06.2017


Ответы (1)


Я определил проблему. Хотя причина до сих пор неясна.

В функции generateRequest я передаю словарь [String:String] со значениями по умолчанию ["":""] для заголовков запросов.

Похоже, что request.allHTTPHeaderFields = ["":""] нарушит ваш запрос.

В предыдущих сборках это не вызывало никаких проблем, но это приведет к неправильному запросу в текущей бета-версии.

Обновить

Я сообщил об ошибке Apple, когда впервые столкнулся с этой проблемой, и это был их ответ:

Проблема заключалась в том, что в CFNetwork-856.4.7 (часть seed 1) мы допустили ошибку и удалили пустые заголовки. В Seed 2 (CFNetwork-861.1) мы ограничили удаляемые заголовки четырьмя «специальными» заголовками (Content-Type, User-Agent, Accept-Language, Accept-Encoding). Вот почему вы заметили изменение в поведении.

macOS El Capitan также отправляет заголовок «=», который приводит к коду состояния HTTP 400. Итак, теперь это работает правильно в Seed 2. Seed 1 был аномалией и был неправильным.

Сообщите нам, решена ли проблема для вас, обновив отчет об ошибке.

person Reza Shirazian    schedule 24.06.2017
comment
Пустой словарь — это [:], а не [:]. Последнее означает, что вы добавляете две пустые строки в качестве ключ-значение. - person nathan; 26.06.2017