diff --git a/io/native/src/main/scala/fs2/io/net/tls/S2nConnection.scala b/io/native/src/main/scala/fs2/io/net/tls/S2nConnection.scala index 3a26097f7d..d5dea005fe 100644 --- a/io/native/src/main/scala/fs2/io/net/tls/S2nConnection.scala +++ b/io/native/src/main/scala/fs2/io/net/tls/S2nConnection.scala @@ -199,7 +199,10 @@ private[tls] object S2nConnection { .void def applicationProtocol = - F.delay(guard(s2n_get_application_protocol(conn))).map(fromCString(_)) + F.delay(s2n_get_application_protocol(conn)).map { + case null => "" + case ptr => fromCString(ptr) + } def session = F.delay { val len = guard(s2n_connection_get_session_length(conn)) diff --git a/io/native/src/test/scala/fs2/io/net/tls/TLSSocketSuite.scala b/io/native/src/test/scala/fs2/io/net/tls/TLSSocketSuite.scala index 07883b8404..b58de41ae7 100644 --- a/io/native/src/test/scala/fs2/io/net/tls/TLSSocketSuite.scala +++ b/io/native/src/test/scala/fs2/io/net/tls/TLSSocketSuite.scala @@ -354,4 +354,77 @@ class TLSSocketSuite extends TLSSuite { checkStarvation.both(checkBlinding).void } } + + test("present application protocol") { + val setup = for { + tlsContext <- testTlsContext + serverSocket <- Network[IO].bind(SocketAddress(ip"127.0.0.1", Port.Wildcard)) + client = Network[IO] + .connect(serverSocket.address) + .flatMap( + tlsContext + .clientBuilder(_) + .withParameters( + TLSParameters(serverName = Some("Unknown"), protocolPreferences = Some(List("h2"))) + ) + .build + ) + } yield serverSocket.accept.flatMap(s => + Stream.resource( + tlsContext + .serverBuilder(s) + .withParameters(TLSParameters(protocolPreferences = Some(List("h2")))) + .build + ) + ) -> client + + Stream + .resource(setup) + .flatMap { case (server, clientSocket) => + val echoServer = server.map { socket => + socket.reads.chunks.foreach(socket.write(_)) + }.parJoinUnbounded + + val client = Stream.resource(clientSocket).evalMap { clientSocket => + clientSocket.applicationProtocol + } + + client.concurrently(echoServer) + } + .compile + .lastOrError + .map(assertEquals(_, "h2")) + } + + test("missing application protocol") { + val setup = for { + tlsContext <- testTlsContext + serverSocket <- Network[IO].bind(SocketAddress(ip"127.0.0.1", Port.Wildcard)) + client = Network[IO] + .connect(serverSocket.address) + .flatMap( + tlsContext + .clientBuilder(_) + .withParameters(TLSParameters(serverName = Some("Unknown"))) + .build + ) + } yield serverSocket.accept.flatMap(s => Stream.resource(tlsContext.server(s))) -> client + + Stream + .resource(setup) + .flatMap { case (server, clientSocket) => + val echoServer = server.map { socket => + socket.reads.chunks.foreach(socket.write(_)) + }.parJoinUnbounded + + val client = Stream.resource(clientSocket).evalMap { clientSocket => + clientSocket.applicationProtocol + } + + client.concurrently(echoServer) + } + .compile + .lastOrError + .map(assertEquals(_, "")) + } }