From b3f32988c57900cb8a0fb861f1f7fce9308bc6cb Mon Sep 17 00:00:00 2001 From: Arturas Slajus Date: Wed, 26 Apr 2023 09:18:14 +0300 Subject: [PATCH 1/2] Add `TLSSocket.applicationProtocolOption` and better docs for `TLSSocket.applicationProtocol` --- .../fs2/io/net/tls/TLSSocketPlatform.scala | 18 +++++++++++++----- .../main/scala/fs2/io/net/tls/TLSEngine.scala | 13 +++++++++++-- .../fs2/io/net/tls/TLSSocketPlatform.scala | 8 ++++++++ .../scala/fs2/io/net/tls/S2nConnection.scala | 9 +++++++-- .../fs2/io/net/tls/TLSSocketPlatform.scala | 7 ++++++- .../main/scala/fs2/io/net/tls/TLSSocket.scala | 10 ++++++++++ 6 files changed, 55 insertions(+), 10 deletions(-) diff --git a/io/js/src/main/scala/fs2/io/net/tls/TLSSocketPlatform.scala b/io/js/src/main/scala/fs2/io/net/tls/TLSSocketPlatform.scala index eebb3c8982..957a8547fb 100644 --- a/io/js/src/main/scala/fs2/io/net/tls/TLSSocketPlatform.scala +++ b/io/js/src/main/scala/fs2/io/net/tls/TLSSocketPlatform.scala @@ -71,10 +71,10 @@ private[tls] trait TLSSocketCompanionPlatform { self: TLSSocket.type => tlsSock, readStream, sessionRef.discrete.unNone.head.compile.lastOrError, - F.delay[Any](tlsSock.alpnProtocol).flatMap { - case false => "".pure // mimicking JVM - case protocol: String => protocol.pure - case _ => F.raiseError(new NoSuchElementException) + F.delay[Any](tlsSock.alpnProtocol).map { + case false => Some("") // mimicking JVM + case protocol: String => Some(protocol) + case _ => None } ) @@ -82,7 +82,15 @@ private[tls] trait TLSSocketCompanionPlatform { self: TLSSocket.type => sock: facade.tls.TLSSocket, readStream: SuspendedStream[F, Byte], val session: F[SSLSession], - val applicationProtocol: F[String] + val applicationProtocolOption: F[Option[String]] ) extends Socket.AsyncSocket[F](sock, readStream) with UnsealedTLSSocket[F] + { + override def applicationProtocol: F[String] = applicationProtocolOption.flatMap { + case None => Async[F].raiseError(new NoSuchElementException( + "`tlsSock.alpnProtocol` returned neither false, nor a String" + )) + case Some(protocol) => Async[F].pure(protocol) + } + } } diff --git a/io/jvm/src/main/scala/fs2/io/net/tls/TLSEngine.scala b/io/jvm/src/main/scala/fs2/io/net/tls/TLSEngine.scala index 9be231c4ba..62dd9e842d 100644 --- a/io/jvm/src/main/scala/fs2/io/net/tls/TLSEngine.scala +++ b/io/jvm/src/main/scala/fs2/io/net/tls/TLSEngine.scala @@ -37,7 +37,16 @@ import cats.syntax.all._ */ private[tls] trait TLSEngine[F[_]] { def beginHandshake: F[Unit] - def applicationProtocol: F[String] + + /** + * Returns [[None]] if it has not yet been determined if application protocols might be used for this connection, + * [[Some]] with an empty String if application protocols values will not be used, or a non-empty application + * protocol String if a value was successfully negotiated. + * + * @see https://docs.oracle.com/javase/8/docs/api/javax/net/ssl/SSLEngine.html#getApplicationProtocol-- + */ + def applicationProtocol: F[Option[String]] + def session: F[SSLSession] def stopWrap: F[Unit] def stopUnwrap: F[Unit] @@ -80,7 +89,7 @@ private[tls] object TLSEngine { def beginHandshake = Sync[F].delay(engine.beginHandshake()) def session = Sync[F].delay(engine.getSession()) - def applicationProtocol = Sync[F].delay(Option(engine.getApplicationProtocol()).get) + def applicationProtocol = Sync[F].delay(Option(engine.getApplicationProtocol())) def stopWrap = Sync[F].delay(engine.closeOutbound()) def stopUnwrap = Sync[F].delay(engine.closeInbound()).attempt.void diff --git a/io/jvm/src/main/scala/fs2/io/net/tls/TLSSocketPlatform.scala b/io/jvm/src/main/scala/fs2/io/net/tls/TLSSocketPlatform.scala index d541f927c4..0d00a053d7 100644 --- a/io/jvm/src/main/scala/fs2/io/net/tls/TLSSocketPlatform.scala +++ b/io/jvm/src/main/scala/fs2/io/net/tls/TLSSocketPlatform.scala @@ -103,6 +103,14 @@ private[tls] trait TLSSocketCompanionPlatform { self: TLSSocket.type => engine.session def applicationProtocol: F[String] = + engine.applicationProtocol.flatMap { + case Some(protocol) => Applicative[F].pure(protocol) + case None => Async[F].raiseError(new NoSuchElementException( + "It has not yet been determined if application protocols might be used for this connection." + )) + } + + def applicationProtocolOption: F[Option[String]] = engine.applicationProtocol def isOpen: F[Boolean] = socket.isOpen 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 03868cf04b..9609b9099a 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 @@ -51,7 +51,12 @@ private[tls] trait S2nConnection[F[_]] { def shutdown: F[Unit] - def applicationProtocol: F[String] + /** + * Returns [[None]] if there is no protocol being negotiated. + * + * @see [[https://aws.github.io/s2n-tls/doxygen/s2n_8h.html#ae53faa26669e258afff875d45140f14e]] + */ + def applicationProtocol: F[Option[String]] def session: F[SSLSession] @@ -199,7 +204,7 @@ private[tls] object S2nConnection { .void def applicationProtocol = - F.delay(guard(s2n_get_application_protocol(conn))).map(fromCString(_)) + F.delay(Option(s2n_get_application_protocol(conn)).map(fromCString(_))) def session = F.delay { val len = guard(s2n_connection_get_session_length(conn)) diff --git a/io/native/src/main/scala/fs2/io/net/tls/TLSSocketPlatform.scala b/io/native/src/main/scala/fs2/io/net/tls/TLSSocketPlatform.scala index 2aa0be334d..9fd8f48329 100644 --- a/io/native/src/main/scala/fs2/io/net/tls/TLSSocketPlatform.scala +++ b/io/native/src/main/scala/fs2/io/net/tls/TLSSocketPlatform.scala @@ -95,7 +95,12 @@ private[tls] trait TLSSocketCompanionPlatform { self: TLSSocket.type => def session: F[SSLSession] = connection.session - def applicationProtocol: F[String] = connection.applicationProtocol + def applicationProtocol: F[String] = connection.applicationProtocol.flatMap { + case Some(protocol) => F.pure(protocol) + case None => F.raiseError(new NoSuchElementException("No application protocol was negotiated")) + } + + override def applicationProtocolOption: F[Option[String]] = connection.applicationProtocol def isOpen: F[Boolean] = socket.isOpen } diff --git a/io/shared/src/main/scala/fs2/io/net/tls/TLSSocket.scala b/io/shared/src/main/scala/fs2/io/net/tls/TLSSocket.scala index 22aaf888a8..39c8db6e07 100644 --- a/io/shared/src/main/scala/fs2/io/net/tls/TLSSocket.scala +++ b/io/shared/src/main/scala/fs2/io/net/tls/TLSSocket.scala @@ -38,9 +38,19 @@ sealed trait TLSSocket[F[_]] extends Socket[F] with TLSSocketPlatform[F] { def session: F[SSLSession] /** Provides access to the current application protocol that has been negotiated. + * + * Raises a [[NoSuchElementException]] if it has not yet been determined if application protocols might + * be used for this connection. See [[applicationProtocolOption]] for a safer option. + * + * @see https://discord.com/channels/632277896739946517/632310980449402880/1100649913223745597 */ def applicationProtocol: F[String] + /** Provides access to the current application protocol that has been negotiated. + * + * Returns [[None]] if it has not yet been determined if application protocols might be used for this connection. + */ + def applicationProtocolOption: F[Option[String]] } object TLSSocket extends TLSSocketCompanionPlatform { From eb496d96ae091a86848552d1edca2a7c9c503e77 Mon Sep 17 00:00:00 2001 From: Arturas Slajus Date: Wed, 26 Apr 2023 09:24:01 +0300 Subject: [PATCH 2/2] Launched scalafmt. --- .../scala/fs2/io/net/tls/TLSSocketPlatform.scala | 12 +++++++----- io/jvm/src/main/scala/fs2/io/net/tls/TLSEngine.scala | 7 +++---- .../scala/fs2/io/net/tls/TLSSocketPlatform.scala | 9 ++++++--- .../main/scala/fs2/io/net/tls/S2nConnection.scala | 9 ++++----- .../scala/fs2/io/net/tls/TLSSocketPlatform.scala | 3 ++- .../src/main/scala/fs2/io/net/tls/TLSSocket.scala | 8 ++++---- 6 files changed, 26 insertions(+), 22 deletions(-) diff --git a/io/js/src/main/scala/fs2/io/net/tls/TLSSocketPlatform.scala b/io/js/src/main/scala/fs2/io/net/tls/TLSSocketPlatform.scala index 957a8547fb..869f39c4c6 100644 --- a/io/js/src/main/scala/fs2/io/net/tls/TLSSocketPlatform.scala +++ b/io/js/src/main/scala/fs2/io/net/tls/TLSSocketPlatform.scala @@ -84,12 +84,14 @@ private[tls] trait TLSSocketCompanionPlatform { self: TLSSocket.type => val session: F[SSLSession], val applicationProtocolOption: F[Option[String]] ) extends Socket.AsyncSocket[F](sock, readStream) - with UnsealedTLSSocket[F] - { + with UnsealedTLSSocket[F] { override def applicationProtocol: F[String] = applicationProtocolOption.flatMap { - case None => Async[F].raiseError(new NoSuchElementException( - "`tlsSock.alpnProtocol` returned neither false, nor a String" - )) + case None => + Async[F].raiseError( + new NoSuchElementException( + "`tlsSock.alpnProtocol` returned neither false, nor a String" + ) + ) case Some(protocol) => Async[F].pure(protocol) } } diff --git a/io/jvm/src/main/scala/fs2/io/net/tls/TLSEngine.scala b/io/jvm/src/main/scala/fs2/io/net/tls/TLSEngine.scala index 62dd9e842d..ccdabb6bfa 100644 --- a/io/jvm/src/main/scala/fs2/io/net/tls/TLSEngine.scala +++ b/io/jvm/src/main/scala/fs2/io/net/tls/TLSEngine.scala @@ -38,11 +38,10 @@ import cats.syntax.all._ private[tls] trait TLSEngine[F[_]] { def beginHandshake: F[Unit] - /** - * Returns [[None]] if it has not yet been determined if application protocols might be used for this connection, - * [[Some]] with an empty String if application protocols values will not be used, or a non-empty application + /** Returns [[None]] if it has not yet been determined if application protocols might be used for this connection, + * [[Some]] with an empty String if application protocols values will not be used, or a non-empty application * protocol String if a value was successfully negotiated. - * + * * @see https://docs.oracle.com/javase/8/docs/api/javax/net/ssl/SSLEngine.html#getApplicationProtocol-- */ def applicationProtocol: F[Option[String]] diff --git a/io/jvm/src/main/scala/fs2/io/net/tls/TLSSocketPlatform.scala b/io/jvm/src/main/scala/fs2/io/net/tls/TLSSocketPlatform.scala index 0d00a053d7..6f8b61a572 100644 --- a/io/jvm/src/main/scala/fs2/io/net/tls/TLSSocketPlatform.scala +++ b/io/jvm/src/main/scala/fs2/io/net/tls/TLSSocketPlatform.scala @@ -105,9 +105,12 @@ private[tls] trait TLSSocketCompanionPlatform { self: TLSSocket.type => def applicationProtocol: F[String] = engine.applicationProtocol.flatMap { case Some(protocol) => Applicative[F].pure(protocol) - case None => Async[F].raiseError(new NoSuchElementException( - "It has not yet been determined if application protocols might be used for this connection." - )) + case None => + Async[F].raiseError( + new NoSuchElementException( + "It has not yet been determined if application protocols might be used for this connection." + ) + ) } def applicationProtocolOption: F[Option[String]] = 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 9609b9099a..37df08ecad 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 @@ -51,11 +51,10 @@ private[tls] trait S2nConnection[F[_]] { def shutdown: F[Unit] - /** - * Returns [[None]] if there is no protocol being negotiated. - * - * @see [[https://aws.github.io/s2n-tls/doxygen/s2n_8h.html#ae53faa26669e258afff875d45140f14e]] - */ + /** Returns [[None]] if there is no protocol being negotiated. + * + * @see [[https://aws.github.io/s2n-tls/doxygen/s2n_8h.html#ae53faa26669e258afff875d45140f14e]] + */ def applicationProtocol: F[Option[String]] def session: F[SSLSession] diff --git a/io/native/src/main/scala/fs2/io/net/tls/TLSSocketPlatform.scala b/io/native/src/main/scala/fs2/io/net/tls/TLSSocketPlatform.scala index 9fd8f48329..f715ca36f2 100644 --- a/io/native/src/main/scala/fs2/io/net/tls/TLSSocketPlatform.scala +++ b/io/native/src/main/scala/fs2/io/net/tls/TLSSocketPlatform.scala @@ -97,7 +97,8 @@ private[tls] trait TLSSocketCompanionPlatform { self: TLSSocket.type => def applicationProtocol: F[String] = connection.applicationProtocol.flatMap { case Some(protocol) => F.pure(protocol) - case None => F.raiseError(new NoSuchElementException("No application protocol was negotiated")) + case None => + F.raiseError(new NoSuchElementException("No application protocol was negotiated")) } override def applicationProtocolOption: F[Option[String]] = connection.applicationProtocol diff --git a/io/shared/src/main/scala/fs2/io/net/tls/TLSSocket.scala b/io/shared/src/main/scala/fs2/io/net/tls/TLSSocket.scala index 39c8db6e07..18cef52160 100644 --- a/io/shared/src/main/scala/fs2/io/net/tls/TLSSocket.scala +++ b/io/shared/src/main/scala/fs2/io/net/tls/TLSSocket.scala @@ -38,16 +38,16 @@ sealed trait TLSSocket[F[_]] extends Socket[F] with TLSSocketPlatform[F] { def session: F[SSLSession] /** Provides access to the current application protocol that has been negotiated. - * + * * Raises a [[NoSuchElementException]] if it has not yet been determined if application protocols might * be used for this connection. See [[applicationProtocolOption]] for a safer option. - * + * * @see https://discord.com/channels/632277896739946517/632310980449402880/1100649913223745597 */ def applicationProtocol: F[String] - /** Provides access to the current application protocol that has been negotiated. - * + /** Provides access to the current application protocol that has been negotiated. + * * Returns [[None]] if it has not yet been determined if application protocols might be used for this connection. */ def applicationProtocolOption: F[Option[String]]