@@ -13,6 +13,7 @@ export listen, listen!, Server, forceclose, port
1313
1414using Sockets, Logging, LoggingExtras, MbedTLS, Dates
1515using MbedTLS: SSLContext, SSLConfig
16+ using ConcurrentUtilities: Lockable, lock
1617using .. IOExtras, .. Streams, .. Messages, .. Parsers, .. Connections, .. Exceptions
1718import .. access_threaded, .. SOCKET_TYPE_TLS, .. @logfmt_str
1819
@@ -83,10 +84,19 @@ accept(s::Listener{SSLConfig}) = getsslcontext(Sockets.accept(s.server), s.ssl)
8384
8485function getsslcontext (tcp, sslconfig)
8586 try
87+ handshake_done = Ref {Bool} (false )
8688 ssl = MbedTLS. SSLContext ()
8789 MbedTLS. setup! (ssl, sslconfig)
8890 MbedTLS. associate! (ssl, tcp)
89- MbedTLS. handshake! (ssl)
91+ handshake_task = @async begin
92+ MbedTLS. handshake! (ssl)
93+ handshake_done[] = true
94+ end
95+ timedwait (5.0 ) do
96+ handshake_done[] || istaskdone (handshake_task)
97+ end
98+ ! istaskdone (handshake_task) && wait (handshake_task)
99+ handshake_done[] || throw (Base. IOError (" SSL handshake timed out" , Base. ETIMEDOUT))
90100 return ssl
91101 catch e
92102 @try Base. IOError close (tcp)
@@ -363,31 +373,46 @@ Accepts new tcp connections and spawns async tasks to handle them."
363373function listenloop (f, listener, conns, tcpisvalid,
364374 max_connections, readtimeout, access_log, ready_to_accept, verbose)
365375 sem = Base. Semaphore (max_connections)
376+ ssl = Lockable (listener. ssl)
377+ connections = Lockable (conns)
366378 verbose >= 0 && @infov 1 " Listening on: $(listener. hostname) :$(listener. hostport) , thread id: $(Threads. threadid ()) "
367379 notify (ready_to_accept)
368380 while isopen (listener)
369381 try
370382 Base. acquire (sem)
371- io = accept (listener)
372- if io === nothing
373- @warnv 1 " unable to accept new connection"
374- continue
375- elseif ! tcpisvalid (io)
376- @warnv 1 " !tcpisvalid: $io "
377- close (io)
378- continue
379- end
380- conn = Connection (io)
381- conn. state = IDLE
382- push! (conns, conn)
383- conn. host, conn. port = listener. hostname, listener. hostport
384- @async try
385- handle_connection (f, conn, listener, readtimeout, access_log)
386- finally
387- # handle_connection is in charge of closing the underlying io
388- delete! (conns, conn)
389- Base. release (sem)
390- end
383+ io = Sockets. accept (listener. server)
384+ Threads. @spawn begin
385+ local conn = nothing
386+ isssl = ! isnothing (listener. ssl)
387+ try
388+ if io === nothing
389+ @warnv 1 " unable to accept new connection"
390+ return
391+ end
392+ if isssl
393+ io = lock (ssl) do ssl
394+ return getsslcontext (io, ssl)
395+ end
396+ end
397+ if ! tcpisvalid (io)
398+ close (io)
399+ return
400+ end
401+ conn = Connection (io)
402+ conn. state = IDLE
403+ lock (connections) do conns
404+ push! (conns, conn)
405+ end
406+ conn. host, conn. port = listener. hostname, listener. hostport
407+ handle_connection (f, conn, listener, readtimeout, access_log)
408+ finally
409+ # handle_connection is in charge of closing the underlying io, but it may not get there
410+ ! isnothing (conn) && lock (connections) do conns
411+ delete! (conns, conn)
412+ end
413+ Base. release (sem)
414+ end
415+ end # Task.@spawn
391416 catch e
392417 if e isa Base. IOError && e. code == Base. UV_ECONNABORTED
393418 verbose >= 0 && @infov 1 " Server on $(listener. hostname) :$(listener. hostport) closing"
@@ -442,7 +467,7 @@ function handle_connection(f, c::Connection, listener, readtimeout, access_log)
442467 request. response. status = 200
443468
444469 try
445- # invokelatest becuase the perf is negligible, but this makes live-editing handlers more Revise friendly
470+ # invokelatest because the perf is negligible, but this makes live-editing handlers more Revise friendly
446471 @debugv 1 " invoking handler"
447472 Base. invokelatest (f, http)
448473 # If `startwrite()` was never called, throw an error so we send a 500 and log this
0 commit comments