some APIs like GitHub’s don’t require it, but many others will hang up instantly if the TLS layer doesn’t offer a valid protocol, like h2. when this happens, it’s not very obvious at all that the problem was the TLS layer was misconfigured.
it’s doesn’t help that the introductory example just suggests calling createClientConfiguration.
let configuration = TLSConfiguration.makeClientConfiguration()
let sslContext = try NIOSSLContext(configuration: configuration)
let client = ClientBootstrap(group: group)
.channelInitializer { channel in
// important: The handler must be initialized _inside_ the `channelInitializer`
let handler = try NIOSSLClientHandler(context: sslContext)
[...]
channel.pipeline.addHandler(handler)
[...]
}
the doccomment for that method says:
/// Creates a TLS configuration for use with client-side contexts.
///
/// This provides sensible defaults, and can be used without customisation. For server-side
/// contexts, you should use ``TLSConfiguration/makeServerConfiguration(certificateChain:privateKey:)`` instead.
///
/// For customising fields, modify the returned TLSConfiguration object.
but an empty array of application protocols isn’t very sensible.
Sure it is: for almost everything but HTTP there are no application protocols to set. IANA has the full set of standardised ALPN identifiers. Here's the list:
Protocol
Identifier
HTTP/0.9
http/0.9
HTTP/1.0
http/1.0
HTTP/1.1
http/1.1
SPDY/1
spdy/1
SPDY/2
spdy/2
SPDY/3
spdy/3
Traversal Using Relays around NAT (TURN)
stun.turn
NAT discovery using Session Traversal Utilities for NAT (STUN)
stun.nat-discovery
HTTP/2 over TLS
h2
HTTP/2 over TCP
h2c
WebRTC Media and Data
webrtc
Confidential WebRTC Media and Data
c-webrtc
FTP
ftp
IMAP
imap
POP3
pop3
ManageSieve
managesieve
CoAP
coap
XMPP jabber:client namespace
xmpp-client
XMPP jabber:server namespace
xmpp-server
acme-tls/1
acme-tls/1
OASIS Message Queuing Telemetry Transport (MQTT)
mqtt
DNS-over-TLS
dot
Network Time Security Key Establishment, version 1
ntske/1
SunRPC
sunrpc
HTTP/3
h3
SMB2
smb
IRC
irc
NNTP (reading)
nntp
NNTP (transit)
nnsp
DoQ
doq
SIP
sip/2
TDS/8.0
tds/8.0
DICOM
dicom
Note that many of these don't really need an ALPN because they don't conflict on ports. The only reason to require ALPN is when you're trying to offer multiple services that listen on the same port. In the 99% case, that's just the HTTP protocols. And in all of these cases, there exists a default that is used when no ALPN is available.
To that end, not only is an empty array of application protocols sensible, it's also quite common.
i think there are at least two counterarguments to that:
HTTP is way more common than many of these other protocols. sure, they exist, but enough people are using HTTP that if the API isn’t working well for HTTP use cases, it’s worth revisiting the API.
not all services enforce the ALPN requirement. this means you can write a client that works with a lot of services, and then try to use that client to interact with a different service that does enforce the requirement. and it’s not going to be obvious at all that the service is refusing to respond because you did not fulfill the ALPN requirement. so not forcing developers to think about ALPN in the beginning can cause a lot of wasted time down the road.
Services must enforce the ALPN requirement unless they can fall into the "prior knowledge" carveout.
If you are offering a listening service on 443 and no ALPN is offered, then you can only assume that you're going to get HTTP/1.1. That's what it means to offer such a service.
i’m not offering a listening service in this instance, i’m interacting with other listening services that don’t enforce it, as a client. are they not following the spec? probably. but then you might try interacting with a service that does follow the spec, and it’s going to be really hard to figure out why the same client works with service A and not service B, because TLSConfiguration didn’t ask you for any applicationProtocols when you were writing the client for service A.