function listen(f, context::SSLContext, host::Union{IPAddr, String} = Sockets.localhost, port::Integer = 1965 ; connection_count::Ref{Int} = Ref(0), readtimeout::Int = 0, verbose::Bool = false) server = Sockets.listen(Sockets.InetAddr(host, port)) verbose && @info "Listening on: $host:$port" return listenloop(f, server, context, connection_count, readtimeout, verbose) end """" Main server loop. Accepts new tcp connections and spawns async tasks to handle them." """ function listenloop(f, server, context, connection_count, readtimeout, verbose) count = 1 while isopen(server) try # @info "Ready to accept new connection" io = accept(server) if io === nothing verbose && @warn "unable to accept new connection" continue end connection_count[] += 1 @async try handle_connection(f, server, context, io, verbose) catch e if e isa Base.IOError && e.code == -54 verbose && @warn "connection reset by peer (ECONNRESET)" else @error exception=(e, stacktrace(catch_backtrace())) end finally connection_count[] -= 1 # handle_connection is in charge of closing the underlying io end catch e close(server) if e isa InterruptException @warn "Interrupted: listen($server)" break else rethrow(e) end end count += 1 end return end function handle_connection(f, server, context, io, verbose) client = SSLClient(context, io) while isopen(server) && isopen(io) try content = UInt8[] client.io_on_read = (x) -> append!(content, x) (ip, client_port) = Sockets.getpeername(io) while true if isreadable(io) && length(client.write_buf) == 0 # verbose && println("do_read") if OpenSSL.do_sock_read(client) == -1 break end end if iswritable(io) && length(client.write_buf) > 0 # verbose && println("do_write") if OpenSSL.do_sock_write(client) == -1 break end end # verbose && println("end loop") if OpenSSL.ssl_init_finished(client) # verbose && println("init_finished") # TODO: add a timeout! while isopen(server) && isopen(io) && (length(content) == 0 || bytesavailable(client.sock) > 0) # println("HERE") OpenSSL.do_sock_read(client) end f(Connection(server, client), Request(String(content))) break end end catch e rethrow(e) finally close(client) end end end