Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

asynchttpserver "Bad file descriptor" crashes #12526

Closed
zedeus opened this issue Oct 26, 2019 · 8 comments
Closed

asynchttpserver "Bad file descriptor" crashes #12526

zedeus opened this issue Oct 26, 2019 · 8 comments
Labels
Async Everything related to Nim's async Severe Standard Library

Comments

@zedeus
Copy link
Contributor

zedeus commented Oct 26, 2019

asynchttpserver crashes due to cancelled(?) requests:

/home/zed/src/dev/nim/nitter/src/nitter.nim(30) nitter
/home/zed/.nimble/pkgs/jester-0.4.3/jester.nim(499) serve
/home/zed/.choosenim/toolchains/nim-1.0.0/lib/pure/asyncdispatch.nim(1874) runForever
/home/zed/.choosenim/toolchains/nim-1.0.0/lib/pure/asyncdispatch.nim(1569) poll
/home/zed/.choosenim/toolchains/nim-1.0.0/lib/pure/asyncdispatch.nim(1335) runOnce
/home/zed/.choosenim/toolchains/nim-1.0.0/lib/pure/asyncdispatch.nim(210) processPendingCallbacks
/home/zed/.choosenim/toolchains/nim-1.0.0/lib/pure/asyncmacro.nim(34) sendNimAsyncContinue
/home/zed/.choosenim/toolchains/nim-1.0.0/lib/pure/asyncnet.nim(446) sendIter
/home/zed/.choosenim/toolchains/nim-1.0.0/lib/pure/asyncfutures.nim(383) read
[[reraised from:
/home/zed/src/dev/nim/nitter/src/nitter.nim(30) nitter
/home/zed/.nimble/pkgs/jester-0.4.3/jester.nim(499) serve
/home/zed/.choosenim/toolchains/nim-1.0.0/lib/pure/asyncdispatch.nim(1874) runForever
/home/zed/.choosenim/toolchains/nim-1.0.0/lib/pure/asyncdispatch.nim(1569) poll
/home/zed/.choosenim/toolchains/nim-1.0.0/lib/pure/asyncdispatch.nim(1335) runOnce
/home/zed/.choosenim/toolchains/nim-1.0.0/lib/pure/asyncdispatch.nim(210) processPendingCallbacks
/home/zed/.choosenim/toolchains/nim-1.0.0/lib/pure/asyncfutures.nim(431) asyncCheckCallback
]]
Error: unhandled exception: Bad file descriptor [OSError]

There's a few ways to trigger this. My use case is a website, where navigating around quickly can cause cancelled requests which triggers the error. Another way is scrubbing around a video stream to parts not buffered yet (causing previous buffering to get cancelled), example repo here for reproduction.

It gets weird though, when run behind nginx every request triggers the error. I haven't had time to investigate this, but it occurred on multiple machines with different nginx configurations, which certainly shouldn't cause all requests to get cancelled.

Related: #12325

@zedeus zedeus changed the title asynchttpserver crashes asynchttpserver "Bad file descriptor" crashes Oct 26, 2019
@dom96 dom96 added Async Everything related to Nim's async Severe Standard Library labels Oct 26, 2019
@zedeus
Copy link
Contributor Author

zedeus commented Nov 10, 2019

I did some investigation and it may be an issue with Jester, but I'm not sure.
Using this simple reproducible example with nginx.

nginx config:

http {
   server {
      listen 80;
      listen [::]:80;
   
      server_name localhost;
   
      location / {
         proxy_pass http://localhost:5000/;
      }
   }
}

Server:

import jester

routes:
  get "/":
    resp Http200

Run:

sudo nginx # -s reload if already running
nim c -d:useStdLib -r server.nim
curl http://localhost

This immediately results in the bad file descriptor exception, so I did some digging, and it appears to happen because the socket is closed before the response gets sent, due to Jester calling asyncCheck from its send proc here.

Likewise, if we replace the server code with the following, where asyncCheck is used inside the request callback, the same exception happens:

import asynchttpserver, asyncdispatch

var server = newAsyncHttpServer()
proc cb(req: Request) {.async.} =
  asyncCheck req.respond(Http200, "Hello World")

waitFor server.serve(Port(5000), cb)

Jester's send proc is not async (neither is most of the call stack above it) so I'm not sure how to go about fixing this

@dom96
Copy link
Contributor

dom96 commented Nov 10, 2019

So there is at least two issues here:

  • asynchttpserver's respond proc probably shouldn't crash when the socket disconnects (we have a flag called SafeDisconn which should prevent this, not sure why it's not in this case, maybe it's not enabled for send?)

  • anything that runs asyncCheck will crash the process when the future fails, this probably isn't ideal and we may wish to enable users to set a global async exception handling callback

@disruptek
Copy link
Contributor

I don't want to leave my server loop to handle an exception, as that is disruptive to all other clients and whatever state I'm working with in the server loop. Hence #12325.

@zedeus
Copy link
Contributor Author

zedeus commented Jan 22, 2020

To mitigate this so I can use Jester with asynchttpserver, I've hacked together a fork that removes support for httpbeast and awaits the requests properly. There might be a way to keep httpbeast support and await asynchttpserver's requests, but this works for now.
https://github.com/zedeus/jester/tree/asyncify

@dom96
Copy link
Contributor

dom96 commented Jan 27, 2020

There certainly is a way to keep httpbeast: httpbeast is enabled statically so there is absolutely no reason you would need to remove httpbeast support for this. I'm curious what the actual fix is and would like to merge it into Jester.

@zedeus
Copy link
Contributor Author

zedeus commented Jan 27, 2020

It's not so much supporting httpbeast, it's the critical path (the send procs etc) that require {.async.} with asynchttpserver which afaik httpbeast doesn't. As described in my post with the nginx example, the fix is just changing it to call things async properly instead of asyncCheck'ing the requests. The two issues you mentioned seem orthogonal to me.

Oh also, the leak/race condition I mentioned on IRC went away when I switched to asynchttpserver. Unfortunately I don't have an easy way to reproduce it apart from running httpbeast on nitter.net with its large amount of traffic that makes it easier to trigger. A more analytical approach might be better, it seems to be a file descriptor leak but that's just a guess

@disruptek
Copy link
Contributor

If the repository Zed created doesn't help, and #12325 doesn't help, and Zed's
new branch doesn't help, hit me up in person this weekend and we can walk through
it. I feel like something must be getting lost in translation via irc/GH...

@zedeus
Copy link
Contributor Author

zedeus commented Jan 28, 2020

Turns out supporting both with async was very easy, opened dom96/jester#236

@zedeus zedeus closed this as completed Nov 15, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Async Everything related to Nim's async Severe Standard Library
Projects
None yet
Development

No branches or pull requests

3 participants