97 lines
3.2 KiB
Julia
97 lines
3.2 KiB
Julia
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
|