Skip to content

Commit e5134ee

Browse files
authored
Merge pull request #3600 from typelevel/topic/backport-3599
Backport #3599
2 parents 6c8fd4a + f5f8e66 commit e5134ee

File tree

2 files changed

+56
-3
lines changed

2 files changed

+56
-3
lines changed

io/jvm/src/main/scala/fs2/io/net/tls/TLSEngine.scala

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,9 +215,7 @@ private[tls] object TLSEngine {
215215
else
216216
binding.read(engine.getSession.getPacketBufferSize).flatMap {
217217
case Some(c) => unwrapBuffer.input(c) >> unwrapHandshake
218-
case None =>
219-
unwrapBuffer.inputRemains
220-
.flatMap(x => if (x > 0) Applicative[F].unit else stopUnwrap)
218+
case None => stopUnwrap
221219
}
222220
}
223221
case SSLEngineResult.HandshakeStatus.NEED_UNWRAP_AGAIN =>

io/jvm/src/test/scala/fs2/io/net/tls/TLSSocketSuite.scala

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,5 +219,60 @@ class TLSSocketSuite extends TLSSuite {
219219
.to(Chunk)
220220
.assertEquals(msg)
221221
}
222+
223+
test("endOfOutput during handshake results in termination") {
224+
val msg = Chunk.array(("Hello, world! " * 20000).getBytes)
225+
226+
def limitWrites(raw: Socket[IO], limit: Int): Socket[IO] = new Socket[IO] {
227+
def endOfInput = raw.endOfInput
228+
def endOfOutput = raw.endOfOutput
229+
def isOpen = raw.isOpen
230+
def localAddress = raw.localAddress
231+
def read(maxBytes: Int) = raw.read(maxBytes)
232+
def readN(numBytes: Int) = raw.readN(numBytes)
233+
def reads = raw.reads
234+
def remoteAddress = raw.remoteAddress
235+
def writes = raw.writes
236+
237+
private var totalWritten: Int = 0
238+
def write(bytes: Chunk[Byte]) =
239+
if (totalWritten >= limit) endOfOutput
240+
else {
241+
val b = bytes.take(limit - totalWritten)
242+
raw.write(b) >> IO(totalWritten += b.size) >> IO(totalWritten >= limit)
243+
.ifM(endOfOutput, IO.unit)
244+
}
245+
}
246+
247+
// Setup an HTTPS echo server & a client that starts a TLS handshake but only sends the first few bytes and then signals no more output
248+
// Doing so should not cause the server to peg a CPU
249+
val setup = for {
250+
tlsContext <- Resource.eval(testTlsContext)
251+
addressAndConnections <- Network[IO].serverResource(Some(ip"127.0.0.1"))
252+
(serverAddress, server) = addressAndConnections
253+
echoServer =
254+
server
255+
.flatMap(s => Stream.resource(tlsContext.serverBuilder(s).withLogger(logger).build))
256+
.map { socket =>
257+
socket.reads.chunks.foreach(socket.write)
258+
}
259+
.parJoinUnbounded
260+
client <- Network[IO].client(serverAddress).flatMap { rawClient =>
261+
tlsContext.client(limitWrites(rawClient, 10))
262+
}
263+
} yield echoServer -> client
264+
265+
Stream
266+
.resource(setup)
267+
.flatMap { case (echoServer, clientSocket) =>
268+
val client =
269+
Stream.exec(clientSocket.write(msg)).onFinalize(clientSocket.endOfOutput) ++
270+
clientSocket.reads.take(msg.size.toLong)
271+
272+
client.concurrently(echoServer)
273+
}
274+
.compile
275+
.drain
276+
}
222277
}
223278
}

0 commit comments

Comments
 (0)