@@ -219,6 +219,69 @@ 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+ @ deprecated(" " , " " )
230+ def isOpen = raw.isOpen
231+ @ deprecated(" " , " " )
232+ def localAddress = raw.localAddress
233+ def peerAddress = raw.peerAddress
234+ def read (maxBytes : Int ) = raw.read(maxBytes)
235+ def readN (numBytes : Int ) = raw.readN(numBytes)
236+ def reads = raw.reads
237+ @ deprecated(" " , " " )
238+ def remoteAddress = raw.remoteAddress
239+ def writes = raw.writes
240+
241+ def address = raw.address
242+ def getOption [A ](key : SocketOption .Key [A ]) = raw.getOption(key)
243+ def setOption [A ](key : SocketOption .Key [A ], value : A ) = raw.setOption(key, value)
244+ def supportedOptions = raw.supportedOptions
245+
246+ private var totalWritten : Int = 0
247+ def write (bytes : Chunk [Byte ]) =
248+ if (totalWritten >= limit) endOfOutput
249+ else {
250+ val b = bytes.take(limit - totalWritten)
251+ raw.write(b) >> IO (totalWritten += b.size) >> IO (totalWritten >= limit)
252+ .ifM(endOfOutput, IO .unit)
253+ }
254+ }
255+
256+ // 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
257+ // Doing so should not cause the server to peg a CPU
258+ val setup = for {
259+ tlsContext <- Resource .eval(testTlsContext)
260+ serverSocket <- Network [IO ].bind(SocketAddress (ip " 127.0.0.1 " , Port .Wildcard ))
261+ echoServer =
262+ serverSocket.accept
263+ .flatMap(s => Stream .resource(tlsContext.serverBuilder(s).withLogger(logger).build))
264+ .map { socket =>
265+ socket.reads.chunks.foreach(socket.write)
266+ }
267+ .parJoinUnbounded
268+ client <- Network [IO ].connect(serverSocket.address).flatMap { rawClient =>
269+ tlsContext.client(limitWrites(rawClient, 10 ))
270+ }
271+ } yield echoServer -> client
272+
273+ Stream
274+ .resource(setup)
275+ .flatMap { case (echoServer, clientSocket) =>
276+ val client =
277+ Stream .exec(clientSocket.write(msg)).onFinalize(clientSocket.endOfOutput) ++
278+ clientSocket.reads.take(msg.size.toLong)
279+
280+ client.concurrently(echoServer)
281+ }
282+ .compile
283+ .drain
284+ }
222285 }
223286
224287 group(" TLSContextBuilder" ) {
0 commit comments