2

I'm trying to craft a very specific HTTP request to a server (ie. defining the exact set of HTTP headers), but NSURLSession keeps "helpfully" inserting a bunch of HTTP headers like Accept, Accept-Language and Accept-Encoding.

Consider the following playground (Swift 2.x) which sends a request to a service that just echos the HTTP headers that were sent:

import Foundation
import XCPlayground

XCPlaygroundPage.currentPage.needsIndefiniteExecution = true

let url = NSURL(string: "http://httpbin.org/headers")!
let request = NSMutableURLRequest(URL: url, cachePolicy: .ReloadIgnoringLocalCacheData, timeoutInterval: 30000)
let configuration = NSURLSessionConfiguration.ephemeralSessionConfiguration()
let session = NSURLSession(configuration: configuration)

let task = session.dataTaskWithRequest(request) { (data: NSData?, response: NSURLResponse?, error: NSError?) in
    print(NSString(data: data!, encoding: NSUTF8StringEncoding))
    XCPlaygroundPage.currentPage.finishExecution()
}
task.resume()

You can see that there are three Accept headers being sent. How can I prevent that?

I've tried setting the header using request.setValue(nil, forHTTPHeaderField: "Accept-Language") but that gets ignored. Tried setting it to "", but no good. I've also tried manipulating the HTTPAdditionalHeaders property on NSURLSessionConfiguration, but no love.

How do I get NSURLSession to not be quite so helpful?

Craig Edwards
  • 1,344
  • 13
  • 19

1 Answers1

3

I doubt what you're asking for is possible. NSURLSession (and NSURLConnection) automatically provide a number of headers, and that's one of them.

There's also no valid reason to remove them. All three of those headers have been part of the spec since the original HTTP/0.9 spec (https://www.w3.org/Protocols/HTTP/HTRQ_Headers.html). There's absolutely no excuse for any server not either handling those correctly or ignoring them outright.

With that said, if you provide the wrong value for those fields (and the default may be wrong), the server may refuse to give you results. To solve that problem, first figure out what type of data the server is actually going to provide, and specify that value in the Accept header instead of the default.

For example, you might set Accept to "application/json" or one of the other variants (What is the correct JSON content type?) if you're expecting JSON data.

That said, if you really must avoid having those headers sent, you can always open a socket, construct the request manually, and send it. Assuming the server doesn't require chunked encoding, it is pretty easy to do. (Chunked encoding, however, is a horror of Herculean proportions, so if your server sends that back, you'll pretty much have to resort to adding libcurl into your project and using that to make requests instead.)

Community
  • 1
  • 1
dgatwood
  • 9,519
  • 1
  • 24
  • 48
  • Thanks for your thoughts. My case is a little bit special as my app (serveupapp.com) is one that accepts incoming requests from a remote source (likely a mobile app that is under development) and either returns a mocked response or forwards it through to the real server. I really want to faithfully forward the exact set of headers I receive... as you say, it is almost certain that I will actually receive these headers, however I'm just trying to think about how I will handle the case where I don't. – Craig Edwards Sep 24 '16 at 21:31
  • Looking at the Swift open source code (https://github.com/apple/swift-corelibs-foundation/blob/db54d310bf348de6834d1ec9f83721d403100ccf/Foundation/NSURLSession/NSURLSessionTask.swift#L594), it looks like everything gets delegated to libcurl. Unfortunately, the way that `NSURLSession` and its friends are structured appears to make overriding those methods a little tricky without a bunch of brittle magic that could change between releases. I may just have to live with it. – Craig Edwards Sep 24 '16 at 21:36
  • What you see there is not the actual Foundation networking source code. That's strictly a compatibility layer that Apple provided so that the most critical parts of NSURLSession could be used on non-Mac platforms. As a result, lots of stuff is completely missing—for example, background sessions. The real NSURLSession implementation is written in Objective-C, and is IIRC a fairly thin layer on top of CFNetwork, which is written in C/C++. Unless, of course, you're using this API on Linux or some other OS, that is. – dgatwood Sep 24 '16 at 23:27
  • Sorry for the delay in replying. I ended up raising a support ticket with Apple to see what they suggested. In a nutshell, `NSURLSession` basically relies on some of those headers being set (it does actually send some that it doesn't technically need too though). Anyway, if I don't want that behaviour, I need to go lower. `NSURLConnection` is no good because it uses `NSURLSession` under the covers. `CFHTTPStream` is likely the best bet and I could use`CFHTTPMessage` to do HTTP parsing. Alternatively, I could look at other 3rd-party libraries. Thanks for your assistance and counsel. – Craig Edwards Oct 10 '16 at 09:42