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

Server now closes socket when connection is dropped by client ( Fix #19 ) #20

Closed
wants to merge 2 commits into from

Conversation

innuos-ccarvalho
Copy link
Contributor

@innuos-ccarvalho innuos-ccarvalho commented Aug 26, 2022

Server now closes socket when connection is dropped by client to avoid sockets hanging in CLOSE_WAIT state.

This aims at solving issue #19 as discussed. I changed the solution a bit from what was proposed at first to follow the pattern already existent in Client. I'm not sure however about the best reason and message for the disconnection.

I'm not sure about the testing rules for this branch, I tested this case has best I could manually. If I should do something else, like updating automatic tests, please do say.

Also took the liberty to fix a couple of typos in comments, hope you don't mind.

Thank you

@codecov-commenter
Copy link

Codecov Report

Merging #20 (633dbab) into main (271f8fc) will not change coverage.
The diff coverage is 100.00%.

@@            Coverage Diff            @@
##              main       #20   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            2         2           
  Lines          195       200    +5     
  Branches        41        42    +1     
=========================================
+ Hits           195       200    +5     
Impacted Files Coverage Δ
src/simple_websocket/ws.py 100.00% <100.00%> (ø)

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

@@ -163,11 +163,15 @@ def _thread(self):
if len(in_data) == 0:
raise OSError()
except (OSError, ConnectionResetError): # pragma: no cover
self.close(reason=CloseReason.GOING_AWAY,
message='Connection Error')
Copy link
Owner

@miguelgrinberg miguelgrinberg Aug 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe there is some corner case that I'm missing, but isn't this exception handler being invoked when the other side goes away? So what would be the point to send a close packet to that other side that is already gone?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left it there because I couldn't be sure that an OSError was caused only by the client going away, it could be something on our side (system issues), for example problems in the recv(). Replacing it with a self.sock.close() would work, however we may incur in protocol violation if it's not true that the other side went away, I reckon.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reanimating this: Should I change this to self.sock.close()? @miguelgrinberg

@@ -348,6 +352,10 @@ def choose_subprotocol(self, request):
return subprotocol
return None

def close(self, reason=None, message=None):
super().close(reason=reason, message=message)
self.sock.close()
Copy link
Owner

@miguelgrinberg miguelgrinberg Aug 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the correct way to handle a graceful connection end? It seems the side that is going away sends a close packet, but does not give the other side to acknowledge and respond, since it immediately closes the socket as well. I think the correct closing handshake is for the first party to send the close packet, then wait for the other side to response with their own close packet, and only then both close their sockets.

And if we assume this is correct, why is only the server implementation closing the socket, but not the client?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're right, we shouldn't force the socket to close right away, I overlooked that part of the websocket protocol. However, the Client, as far as I can understand, is in fact closing the socket after super().close() (line 440), actually it was bit of "cargo cult" from my part using that pattern on Server.

I can definitely remove this override of close() if you agree it's the best way and test again. However I'm not comfortable changing Client.close() as I'm not confident in my ability to test it fully.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reanimating this: Do you agree on removing this on close() of Server?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, as long as the close call happens when the server receives the close confirmation packet from the client. I think that should be already in place, but should probably be verified.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, I'll change it, analyse the code to check for the close on the Server and retest the original problematic case again

…socket disconnection after send a close packet.
@miguelgrinberg
Copy link
Owner

This is now merged. I have simplified the closing logic a bit, and also added a missing close for the selector, which is used when the optional pings are enabled. Thanks!

@innuos-ccarvalho
Copy link
Contributor Author

Thanks for your work!

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.

3 participants