@@ -13,6 +13,7 @@ export listen, listen!, Server, forceclose, port
13
13
14
14
using Sockets, Logging, LoggingExtras, MbedTLS, Dates
15
15
using MbedTLS: SSLContext, SSLConfig
16
+ using ConcurrentUtilities: Lockable, lock
16
17
using .. IOExtras, .. Streams, .. Messages, .. Parsers, .. Connections, .. Exceptions
17
18
import .. access_threaded, .. SOCKET_TYPE_TLS, .. @logfmt_str
18
19
@@ -83,10 +84,19 @@ accept(s::Listener{SSLConfig}) = getsslcontext(Sockets.accept(s.server), s.ssl)
83
84
84
85
function getsslcontext (tcp, sslconfig)
85
86
try
87
+ handshake_done = Ref {Bool} (false )
86
88
ssl = MbedTLS. SSLContext ()
87
89
MbedTLS. setup! (ssl, sslconfig)
88
90
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))
90
100
return ssl
91
101
catch e
92
102
@try Base. IOError close (tcp)
@@ -363,31 +373,55 @@ Accepts new tcp connections and spawns async tasks to handle them."
363
373
function listenloop (f, listener, conns, tcpisvalid,
364
374
max_connections, readtimeout, access_log, ready_to_accept, verbose)
365
375
sem = Base. Semaphore (max_connections)
376
+ ssl = Lockable (listener. ssl)
377
+ connections = Lockable (conns)
366
378
verbose >= 0 && @infov 1 " Listening on: $(listener. hostname) :$(listener. hostport) , thread id: $(Threads. threadid ()) "
367
379
notify (ready_to_accept)
368
380
while isopen (listener)
369
381
try
370
382
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
+ catch e
409
+ if e isa Base. IOError && e. code == Base. UV_ECONNABORTED
410
+ verbose >= 0 && @infov 1 " Server on $(listener. hostname) :$(listener. hostport) closing"
411
+ else
412
+ @errorv 2 " Server on $(listener. hostname) :$(listener. hostport) errored" exception= (e, catch_backtrace ())
413
+ # quick little sleep in case there's a temporary
414
+ # local error accepting and this might help avoid quickly re-erroring
415
+ sleep (0.05 + rand () * 0.05 )
416
+ end
417
+ # handle_connection is in charge of closing the underlying io, but it may not get there
418
+ finally
419
+ ! isnothing (conn) && lock (connections) do conns
420
+ delete! (conns, conn)
421
+ end
422
+ Base. release (sem)
423
+ end
424
+ end # Task.@spawn
391
425
catch e
392
426
if e isa Base. IOError && e. code == Base. UV_ECONNABORTED
393
427
verbose >= 0 && @infov 1 " Server on $(listener. hostname) :$(listener. hostport) closing"
@@ -442,7 +476,7 @@ function handle_connection(f, c::Connection, listener, readtimeout, access_log)
442
476
request. response. status = 200
443
477
444
478
try
445
- # invokelatest becuase the perf is negligible, but this makes live-editing handlers more Revise friendly
479
+ # invokelatest because the perf is negligible, but this makes live-editing handlers more Revise friendly
446
480
@debugv 1 " invoking handler"
447
481
Base. invokelatest (f, http)
448
482
# If `startwrite()` was never called, throw an error so we send a 500 and log this
0 commit comments