fix severe bug causing deadlocks

master
nixo 2020-10-28 23:45:24 +01:00
parent 4e26e066ad
commit 7b6b1f32d5
2 changed files with 42 additions and 45 deletions

View File

@ -62,6 +62,6 @@ macro app(def)
end end
end end
function serve(h::App, context, host = ip"127.0.0.1", port = 1965; kws...) function serve(h::App, host = ip"127.0.0.1", port = 1965; kws...)
@async listen((req) -> h.warez(req), context, host, port; kws...) @async listen((req) -> h.warez(req), host, port; kws...)
end end

View File

@ -1,5 +1,5 @@
function listen(f, function listen(f,
context::SSLContext, init_context,
host::Union{IPAddr, String} = Sockets.localhost, host::Union{IPAddr, String} = Sockets.localhost,
port::Integer = 1965 port::Integer = 1965
; ;
@ -9,35 +9,36 @@ function listen(f,
server = Sockets.listen(Sockets.InetAddr(host, port)) server = Sockets.listen(Sockets.InetAddr(host, port))
verbose && @info "Listening on: $host:$port" verbose && @info "Listening on: $host:$port"
return listenloop(f, server, init_context, connection_count, readtimeout, verbose)
return listenloop(f, server, context, connection_count, readtimeout, verbose)
end end
"""" """"
Main server loop. Main server loop.
Accepts new tcp connections and spawns async tasks to handle them." Accepts new tcp connections and spawns async tasks to handle them."
""" """
function listenloop(f, server, context, connection_count, readtimeout, verbose) function listenloop(f, server, init_context, connection_count, readtimeout, verbose)
count = 1 count = 1
while isopen(server) while isopen(server)
try try
# @info "Ready to accept new connection" verbose && @info "Ready to accept new connection"
io = accept(server) io = accept(server)
if io === nothing if isnothing(io)
verbose && @warn "unable to accept new connection" verbose && @warn "unable to accept new connection"
continue continue
end end
connection_count[] += 1 connection_count[] += 1
verbose && @info "$(connection_count[]) total connections"
@async try @async try
handle_connection(f, server, context, io, verbose) handle_connection(f, server, init_context, io, verbose)
catch e catch e
if e isa Base.IOError && e.code == -54 if e isa Base.IOError && e.code in (-54, -104)
verbose && @warn "connection reset by peer (ECONNRESET)" verbose && @warn "connection reset by peer (ECONNRESET)"
else else
@error exception=(e, stacktrace(catch_backtrace())) @warn exception=(e, stacktrace(catch_backtrace()))
end end
finally finally
connection_count[] -= 1 connection_count[] -= 1
verbose && @info "$(connection_count[]) total connections"
# handle_connection is in charge of closing the underlying io # handle_connection is in charge of closing the underlying io
end end
catch e catch e
@ -50,47 +51,43 @@ function listenloop(f, server, context, connection_count, readtimeout, verbose)
end end
end end
count += 1 count += 1
verbose && @info "Responded to $(count) clients in total"
end end
return return
end end
function handle_connection(f, server, context, io, verbose) function handle_connection(f, server, init_context, io, verbose)
client = SSLClient(context, io) client = SSLClient(init_context(), io)
while isopen(server) && isopen(io) try
try while isopen(server) && isopen(io)
while true if isreadable(io) && length(client.write_buf) == 0
if isreadable(io) && length(client.write_buf) == 0 if OpenSSL.do_sock_read(client) == -1
# 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(client.context.data) < 2 ||
client.context.data[end-1:end] != UInt8['\r', '\n'])
# println("HERE")
OpenSSL.do_sock_read(client)
end
f(GeminiRequest(Connection(server, client),
Request(String(client.context.data)),
Dict()))
break break
end end
end end
catch e if iswritable(io) && length(client.write_buf) > 0
rethrow(e) verbose && println("do_write")
finally if OpenSSL.do_sock_write(client) == -1
close(client) break
end
end
if OpenSSL.ssl_init_finished(client)
verbose && println("init_finished")
# TODO: add a timeout!
while isopen(server) && isopen(io) &&
(length(client.context.data) < 2 ||
client.context.data[end-1:end] != UInt8['\r', '\n'])
OpenSSL.do_sock_read(client)
end
f(GeminiRequest(Connection(server, client),
Request(String(client.context.data)),
Dict()))
break
end
end end
catch e
rethrow(e)
finally
close(client)
end end
end end