Skip to content

Raise EOFError when the connection closes while a write waits - #42

Open
andrewfader wants to merge 1 commit into
socketry:mainfrom
andrewfader:fix-write-after-close
Open

andrewfader wants to merge 1 commit into
socketry:mainfrom
andrewfader:fix-write-after-close

Conversation

@andrewfader

Copy link
Copy Markdown

write_frame and write_frames read @framer again after taking the write lock, and write_frame never checks it at all. Under async-http the lock is a semaphore and the write inside it can block on the socket, so the reading task can close the connection (setting @framer = nil) while a writer waits. The writer then raises NoMethodError on nil instead of the EOFError a closed connection gives, and async-http doesn't retry NoMethodError.

We hit both traces in production with Falcon and async-http talking HTTP/2 to a CDN-fronted origin: undefined method 'flush' for nil from #41's shape (peer hangs up while a request waits on the lock), and undefined method 'write_frame' for nil from socketry/async-http#254 (a window update after a graceful GOAWAY closed the connection).

The fix checks the framer at each use, inside the lock and again before the flush, raising EOFError, "Connection closed!" as write_frames already does for a connection that was closed up front.

The new tests close the connection from inside synchronize, which is the state the race leaves behind. Without the change they fail with NoMethodError: undefined method 'write_frame' for nil and undefined method 'flush' for nil; with it both raise EOFError. The reproduction script in #41 now ends with EOFError from the GET. bundle exec sus (248 passed) and bundle exec rubocop are clean.

This turns #254 into an EOFError but doesn't make the buffered body readable; that part is in async-http, where a closed stream shouldn't request a window update at all.

Fixes #41.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Connection#write_frames and #write_frame raise NoMethodError when the connection closes while a write waits

1 participant