TLSConfiguration sensible defaults are not very sensible

it is way too easy to create a TLSConfiguration that lacks the necessary applicationProtocols.

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.

1 Like

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:

  1. 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.

  2. 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.

There is no spec that requires listeners to offer an ALPN on port 443. If they don't, they only offer HTTP/1.1.