URLRequest error in iOS 13

My iOS app obtains streamed events from an endpoint. I use the following code successfully to initiate streaming and worked beautifully before iOS 13. Now the following code will generate an error:


func startStreaming() {

precondition( !self.isStreaming )

//

// create a request and task to start streaming

//

if let request = SensorInventory.setupHTTPRequestForURL(SensorProject.projectBaseUrlString + "/devices:stream") {

let task = self.session.uploadTask(withStreamedRequest: request)

self.streamingTask = task

task.resume()

}

}


The method setupHTTPRequestForURL simply creates a request object with a base 64 encoded string of key and password and sets the value for the httpHeaderField:

request.httpMethod = "GET"

request.setValue("Basic \(base64LoginString)", forHTTPHeaderField: "Authorization")



No error is thrown in iOS 12 - streamed data is returned however in iOS 13:


2019-09-22 18:00:27.728325-0700 DTSensors[9880:1176642] GET method must not have a body

2019-09-22 18:00:27.728938-0700 DTSensors[9880:1173067] Task <4D2AF35D-EA2F-4CEA-BFEE-5AF936AC3BAF>.<1600> finished with error [-1103] Error Domain=NSURLErrorDomain Code=-1103 "resource exceeds maximum size" UserInfo={NSLocalizedDescription=resource exceeds maximum size, NSErrorFailingURLStringKey=https://api.disruptive-technologies.com/v2/projects/bhm5ru43iktucae701rg/devices:stream, NSErrorFailingURLKey=https://api.disruptive-technologies.com/v2/projects/bhm5ru43iktucae701rg/devices:stream, _NSURLErrorRelatedURLSessionTaskErrorKey=(

"LocalUploadTask <4D2AF35D-EA2F-4CEA-BFEE-5AF936AC3BAF>.<1600>"

), _NSURLErrorFailingURLSessionTaskErrorKey=LocalUploadTask <4D2AF35D-EA2F-4CEA-BFEE-5AF936AC3BAF>.<1600>, NSUnderlyingError=0x600001509950 {Error Domain=kCFErrorDomainCFNetwork Code=-1103 "(null)"}}



When I examine the request structure I see (sensitive data is removed at ***):


https://api.YYYY.com/v2/projects/XXXXXXXXX/devices:stream

▿ url : Optional<URL>

▿ some : https://api.YYYY.com/v2/projects/XXXXXXXXX/devices:stream

- _url : https://api.YYYY.com/v2/projects/XXXXXXXXX/devices:stream

- cachePolicy : 0

- timeoutInterval : 60.0

- mainDocumentURL : nil

- networkServiceType : __C.NSURLRequestNetworkServiceType

- allowsCellularAccess : true

▿ httpMethod : Optional<String>

- some : "GET"

▿ allHTTPHeaderFields : Optional<Dictionary<String, String>>

▿ some : 1 element

▿ 0 : 2 elements

- key : "Authorization"

- value : "Basic Ymhxb2hlajI0dGUwMDBiMjR2ajA6NjQ0YWQ2MTgxN2E0NDA3MWI4OTEyM2EwNzI2ZGE2MTY="

- httpBody : nil

- httpBodyStream : nil

- httpShouldHandleCookies : true

- httpShouldUsePipelining : false


- Note that the httpBody is nil in contrast to what the output above describes


Anyone have a suggestion about how to address this problem? NB The same basic authorization works just fine if I am asking the endpoint for data, it just does not work if I attempt to initiate streaming.

Thanks!

Replies

The problem here is that the HTTP standard specifically outlaws a GET request having a body. Historically

URLSession
would allow this, resulting in some mighty weird on-the-wire behaviour. In iOS 13 and friends we’ve specifically outlawed this, and such requests will fail immediately.

This change was called out in the iOS 13 Release Notes; search the doc for “46025234” to find the specific entry.

The correct solution here is to switch from using GET to some other HTTP method. Most folks use POST for this sort of thing. If the server requires you to use GET, you should talk to the server folks to get that fixed.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thank you Quinn. I tried using POST and while the error is gone my app does not receive any streamed data so I have a feeling that the streaming endpoint is expecting a GET method.


I am unclear about what would be in the httpBody of the request though I suspect it would be an input stream. But I never set it explicitly. I suspect it happens either in the uploadTask(withStreamedRequest:) method or in the completion handler of the urlSession(session:task:completionHandler:) delegate method. Right?


I have contacted the server people and await their reply.


Once again, much obliged!

I thought that you’re expecting streamed data back from the server? If so, using an upload task is a bit weird. Have you tried using a data task?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

I just finshed fixing this defect and there were a number of bone-head mistakes in my implementation. Yes, Quinn you pointed out one of them. The fix was relatively simple - first, this was a data task and not an upload task as you correctly point out. Why this ever worked is a mystery. Second, when using URLRequest a get will end up getting a body if the withStreamedRequest: is called, which now causes an error. No need for the URLRequest. The simpliest solution turned out to be simply create a data task on the Session specifying the streaming endpoint.