Get grpc trailer fields through NSURLSession

I'm trying to implement support for grpc http/2 streams using NSURLSession. Almost everything works fine, data streaming is flowing from the server and from the client and responses are coming through my NSURLSessionTaskDelegate. I'm getting the responses and streamed data through the appropriate handlers (didReceiveData, didReceiveResponse).

However, I cannot seem to find an API to access the trailers expected by grpc. Specifically, the expected trailer "grpc-status: 0" is in the response, but after the data. Is there no way to gain access to trailers in the NSURLSession Framework?

Answered by DTS Engineer in 863110022

URLSession doesn’t support HTTP trailers (r. 26845013). That leaves you with a couple of options:

  • You can rework your on-the-wire protocol to avoid the need for them.
  • You can write or acquire your own HTTP stack that includes this support.

If you do the latter, make sure to use Network framework as your underlying transport, because that ensures that your app will do all the usual things expected on Apple platforms (proxy support, VPN On Demand, and so on). TN3151 Choosing the right networking API talks more about this; see the HTTP alternatives and BSD Sockets best practices sections.

Note For gRPC specifically, there’s gRPC Swift and you should be able to combine that with SwiftNIO Transport Services.

Normally at this point I’d suggest that you file an enhancement request against URLSession for trailer support, but I don’t think that’d be productive. URLSession is a high-level API and this is a pretty low-level feature. I think you’re more likely to see trailer support land in a low-level framework, with Network framework being the obvious candidate. However, Network framework doesn’t currently support HTTP [1], so there’s nothing to file an ER against.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Although if you rummage around in the debugger, you’ll see that URLSession delegates the HTTP heavy lifting to Network framework (assuming that you clear the usesClassicLoadingMode property).

Accepted Answer

URLSession doesn’t support HTTP trailers (r. 26845013). That leaves you with a couple of options:

  • You can rework your on-the-wire protocol to avoid the need for them.
  • You can write or acquire your own HTTP stack that includes this support.

If you do the latter, make sure to use Network framework as your underlying transport, because that ensures that your app will do all the usual things expected on Apple platforms (proxy support, VPN On Demand, and so on). TN3151 Choosing the right networking API talks more about this; see the HTTP alternatives and BSD Sockets best practices sections.

Note For gRPC specifically, there’s gRPC Swift and you should be able to combine that with SwiftNIO Transport Services.

Normally at this point I’d suggest that you file an enhancement request against URLSession for trailer support, but I don’t think that’d be productive. URLSession is a high-level API and this is a pretty low-level feature. I think you’re more likely to see trailer support land in a low-level framework, with Network framework being the obvious candidate. However, Network framework doesn’t currently support HTTP [1], so there’s nothing to file an ER against.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Although if you rummage around in the debugger, you’ll see that URLSession delegates the HTTP heavy lifting to Network framework (assuming that you clear the usesClassicLoadingMode property).

Thanks for the reply @DTS Engineer.

For my project, using Swift is not really an option for now. My stack is more C++/Objective-C based, and so gRPC-Swift and SwiftNIO are not ideal.

From what I gathered, there isn't an equivalent or other alternative that would enable this for Objective-C. There is grpc-ios, but, without going into details, it doesn't really make sense to use that either, I'm looking for a lower level framework.

I see two options for me:

  1. Use the Network framework and take care of the whole TLS handshakes, HTTP headers, etc and obtain my trailer support that way.
  2. Use libcurl, which would do a lot of that for me and get trailers from there, as it supports it.

Option #1 is a huge lift and would mean a lot of maintainability problems in the future.

Option #2 is a bit safer, but I'm afraid of it contradicting your statement: "make sure to use Network framework as your underlying transport, because that ensures that your app will do all the usual things expected on Apple platforms". Does this mean there could be certification problems for my app if I was to use libcurl? Are there any specific guidelines I can look at to confirm this wouldn't be a problem?

If libcurl isn't an option, are there known objective-c alternatives that do not force us to implement the whole HTTP/2 stack ourselves?

Thanks again!

Does this mean there could be certification problems for my app if I was to use libcurl?

I’m not sure what you mean by “certification problems”. If you’re asking about whether it could run into problems during App Review, that’s hard to answer. The problems I’m talking about tend to show up in specific network environments, and App Review doesn’t document their network environment at that level of detail.

The real concern is what happens when your app hits a wide range of users. If, for example, the user has VPN On Demand enabled, or relies on a network relay, or is on a network that only provides access to the Internet via an HTTP proxy, or whatever. Lots of these weird environments exist, and our recommended APIs, Network framework and URLSession, support them automatically. If you use other APIs you can easily run into problems. Some of those problems just require you to write more code, but some are much less tractable.

I’m presuming that libcurl uses BSD Sockets under the covers. If so, that presents some challenges. We talk about these in TN3151 Choosing the right networking API, and specifically in the BSD Sockets best practices, DNS best practices, and TLS best practices sections. I can’t comment on whether a given open source library follows those best practices. It’s something you’d have to ask via the library’s support channel.

Honestly, I suspect that you’d be better off figuring out how to integrate Swift code into your build process, because:

  • This code is already specific to Apple platforms, as you’re using URLSession, so you don’t have to worry about cross-platform Swift [1].
  • Apple is shipping an increasing number of Swift-only frameworks [2], so the work you put in today will pay off in the future.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Swift’s cross-platform support is pretty darned good these days, but there’s no doubt that it complicates things.

[2] Historically I’ve referred folks to this post, but nowadays I think my more recent post is a better fit.

Get grpc trailer fields through NSURLSession
 
 
Q