OxygenFramework / Oxygen.jl

💨 A breath of fresh air for programming web apps in Julia
https://oxygenframework.github.io/Oxygen.jl/
MIT License
383 stars 25 forks source link

Websocket disconnect errors instead of handling gracefully #201

Open bryaan opened 3 weeks ago

bryaan commented 3 weeks ago

When my websocket client disconnects, the server throws an error:

 Info: 2024-06-04 21:41:58 AppWebSockerServer - WS Client Disconnected
┌ Error: 2024-06-04 21:41:59 handle_connection handler error.
│
│ ===========================
│ HTTP Error message:
│
│ ERROR: TaskFailedException
│ Stacktrace:
│  [1] wait
│    @ ./task.jl:352 [inlined]
│  [2] (::Oxygen.Core.var"#17#20"{Oxygen.Core.var"#15#16"{Oxygen.Core.var"#29#31"{Oxygen.Core.var"#40#44"{Oxygen.Core.AutoDoc.var"#2#4"{Oxygen.Core.var"#34#37"{HTTP.Handlers.Router{typeof(HTTP.Handlers.default404), typeof(HTTP.Handlers.default405), Nothing}, Bool, Bool}, HTTP.Handlers.Router{typeof(HTTP.Handlers.default404), typeof(HTTP.Handlers.default405), Nothing}, Vector{typeof(Main.LunaStox.ServerHandlers.CorsMiddleware)}, Dict{String, Tuple}}, Oxygen.Core.AppContext.Service, Bool}, HTTP.Handlers.Router{typeof(HTTP.Handlers.default404), typeof(HTTP.Handlers.default405), Nothing}, String}}})(stream::HTTP.Streams.Stream{HTTP.Messages.Request, HTTP.Connections.Connection{Sockets.TCPSocket}})
│    @ Oxygen.Core ~/.julia/packages/Oxygen/LWjgB/src/core.jl:225
│  [3] #invokelatest#2
│    @ ./essentials.jl:887 [inlined]
│  [4] invokelatest
│    @ ./essentials.jl:884 [inlined]
│  [5] handle_connection(f::Function, c::HTTP.Connections.Connection{Sockets.TCPSocket}, listener::HTTP.Servers.Listener{Nothing, Sockets.TCPServer}, readtimeout::Int64, access_log::Function)
│    @ HTTP.Servers ~/.julia/packages/HTTP/sJD5V/src/Servers.jl:469
│  [6] (::HTTP.Servers.var"#16#17"{Oxygen.Core.var"#17#20"{Oxygen.Core.var"#15#16"{Oxygen.Core.var"#29#31"{Oxygen.Core.var"#40#44"{Oxygen.Core.AutoDoc.var"#2#4"{Oxygen.Core.var"#34#37"{HTTP.Handlers.Router{typeof(HTTP.Handlers.default404), typeof(HTTP.Handlers.default405), Nothing}, Bool, Bool}, HTTP.Handlers.Router{typeof(HTTP.Handlers.default404), typeof(HTTP.Handlers.default405), Nothing}, Vector{typeof(Main.LunaStox.ServerHandlers.CorsMiddleware)}, Dict{String, Tuple}}, Oxygen.Core.AppContext.Service, Bool}, HTTP.Handlers.Router{typeof(HTTP.Handlers.default404), typeof(HTTP.Handlers.default405), Nothing}, String}}}, HTTP.Servers.Listener{Nothing, Sockets.TCPServer}, Set{HTTP.Connections.Connection}, Int64, Oxygen.Core.var"#25#26", ReentrantLock, Base.Semaphore, HTTP.Connections.Connection{Sockets.TCPSocket}})()
│    @ HTTP.Servers ~/.julia/packages/HTTP/sJD5V/src/Servers.jl:401
│
│     nested task error: TaskFailedException
│     Stacktrace:
│      [1] wait
│        @ ./task.jl:352 [inlined]
│      [2] (::Oxygen.Core.var"#18#21"{HTTP.Streams.Stream{HTTP.Messages.Request, HTTP.Connections.Connection{Sockets.TCPSocket}}, Oxygen.Core.var"#15#16"{Oxygen.Core.var"#29#31"{Oxygen.Core.var"#40#44"{Oxygen.Core.AutoDoc.var"#2#4"{Oxygen.Core.var"#34#37"{HTTP.Handlers.Router{typeof(HTTP.Handlers.default404), typeof(HTTP.Handlers.default405), Nothing}, Bool, Bool}, HTTP.Handlers.Router{typeof(HTTP.Handlers.default404), typeof(HTTP.Handlers.default405), Nothing}, Vector{typeof(Main.LunaStox.ServerHandlers.CorsMiddleware)}, Dict{String, Tuple}}, Oxygen.Core.AppContext.Service, Bool}, HTTP.Handlers.Router{typeof(HTTP.Handlers.default404), typeof(HTTP.Handlers.default405), Nothing}, String}}})()
│        @ Oxygen.Core ~/.julia/packages/Oxygen/LWjgB/src/core.jl:223
│
│         nested task error: IOError: stream is closed or unusable
│         Stacktrace:
│           [1] check_open
│             @ ./stream.jl:388 [inlined]
│           [2] uv_write_async(s::Sockets.TCPSocket, p::Ptr{UInt8}, n::UInt64)
│             @ Base ./stream.jl:1074
│           [3] uv_write(s::Sockets.TCPSocket, p::Ptr{UInt8}, n::UInt64)
│             @ Base ./stream.jl:1037
│           [4] unsafe_write(s::Sockets.TCPSocket, p::Ptr{UInt8}, n::UInt64)
│             @ Base ./stream.jl:1120
│           [5] unsafe_write
│             @ ~/.julia/packages/HTTP/sJD5V/src/Connections.jl:132 [inlined]
│           [6] unsafe_write
│             @ ./io.jl:698 [inlined]
│           [7] write(io::IO, x1::Any, xs::Vararg{Any})
│             @ Base ./io.jl:721 [inlined]
│           [8] writeheaders(io::HTTP.Connections.Connection{Sockets.TCPSocket}, m::HTTP.Messages.Response, buf::IOBuffer)
│             @ HTTP.Messages ~/.julia/packages/HTTP/sJD5V/src/Messages.jl:503
│           [9] writeheaders(io::IO, m::HTTP.Messages.Message, buf::IOBuffer)
│             @ HTTP.Messages ~/.julia/packages/HTTP/sJD5V/src/Messages.jl:494 [inlined]
│          [10] startwrite(http::HTTP.Streams.Stream{HTTP.Messages.Request, HTTP.Connections.Connection{Sockets.TCPSocket}})
│             @ HTTP.Streams ~/.julia/packages/HTTP/sJD5V/src/Streams.jl:79
│          [11] (::HTTP.Handlers.var"#1#2"{Oxygen.Core.var"#12#14"{Oxygen.Core.var"#29#31"{Oxygen.Core.var"#40#44"{Oxygen.Core.AutoDoc.var"#2#4"{Oxygen.Core.var"#34#37"{HTTP.Handlers.Router{typeof(HTTP.Handlers.default404), typeof(HTTP.Handlers.default405), Nothing}, Bool, Bool}, HTTP.Handlers.Router{typeof(HTTP.Handlers.default404), typeof(HTTP.Handlers.default405), Nothing}, Vector{typeof(Main.LunaStox.ServerHandlers.CorsMiddleware)}, Dict{String, Tuple}}, Oxygen.Core.AppContext.Service, Bool}, HTTP.Handlers.Router{typeof(HTTP.Handlers.default404), typeof(HTTP.Handlers.default405), Nothing}, String}, Sockets.IPv4, HTTP.Streams.Stream{HTTP.Messages.Request, HTTP.Connections.Connection{Sockets.TCPSocket}}}})(stream::HTTP.Streams.Stream{HTTP.Messages.Request, HTTP.Connections.Connection{Sockets.TCPSocket}})
│             @ HTTP.Handlers ~/.julia/packages/HTTP/sJD5V/src/Handlers.jl:60
│          [12] #15
│             @ Oxygen.Core ~/.julia/packages/Oxygen/LWjgB/src/core.jl:208 [inlined]
│          [13] (::Oxygen.Core.var"#19#22"{HTTP.Streams.Stream{HTTP.Messages.Request, HTTP.Connections.Connection{Sockets.TCPSocket}}, Oxygen.Core.var"#15#16"{Oxygen.Core.var"#29#31"{Oxygen.Core.var"#40#44"{Oxygen.Core.AutoDoc.var"#2#4"{Oxygen.Core.var"#34#37"{HTTP.Handlers.Router{typeof(HTTP.Handlers.default404), typeof(HTTP.Handlers.default405), Nothing}, Bool, Bool}, HTTP.Handlers.Router{typeof(HTTP.Handlers.default404), typeof(HTTP.Handlers.default405), Nothing}, Vector{typeof(Main.LunaStox.ServerHandlers.CorsMiddleware)}, Dict{String, Tuple}}, Oxygen.Core.AppContext.Service, Bool}, HTTP.Handlers.Router{typeof(HTTP.Handlers.default404), typeof(HTTP.Handlers.default405), Nothing}, String}}})()
│             @ Oxygen.Core ~/.julia/packages/Oxygen/LWjgB/src/core.jl:222
│   request =
│    HTTP.Messages.Request:
│    """
│    GET /ws/scanner HTTP/1.1
│    Host: 127.0.0.1:7820
│    User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:124.0) Gecko/20100101 Firefox/124.0
│    Accept: */*
│    Accept-Language: en-US,en;q=0.5
│    Accept-Encoding: gzip, deflate, br
│    Sec-WebSocket-Version: 13
│    Origin: http://localhost:5173
│    Sec-WebSocket-Extensions: permessage-deflate
│    Sec-WebSocket-Key: 7LG0jbS9Ud1oP9uTOFKCrA==
│    DNT: 1
│    Connection: keep-alive, Upgrade
│    Sec-Fetch-Dest: empty
│    Sec-Fetch-Mode: websocket
│    Sec-Fetch-Site: cross-site
│    Pragma: no-cache
│    Cache-Control: no-cache
│    Upgrade: websocket
│
│    """
└ @ HTTP.Servers ~/.julia/packages/HTTP/sJD5V/src/Servers.jl:483
ndortega commented 3 weeks ago

Hi @bryaan,

What kind of behavior would expect to get when a client forcefully disconnects? It should be simple enough to explicitly catch and hide this error since it's so common

bryaan commented 3 weeks ago

Yes, that sounds like a good default.

bryaan commented 1 week ago

Any progress on this? really ugly to see these common messages in my error log.

bryaan commented 1 week ago

I've tried to fix this like so, but the error still bubbles up.

function _try_send(ws, message)
    try
        send(ws, message)
    catch e
    end
end