-
-
Notifications
You must be signed in to change notification settings - Fork 345
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
Add a method to trio socket objects that checks whether the OS thinks the socket is readable #760
Comments
@wgwz You mentioned in gitter that you had some questions about this? |
This is slightly larger than we usually use for "good first issues", but if you're ambitious then it could work. Basically what's needed is:
|
I'll have a look at this |
add a new is_readable method to SocketType (fix #760)
It sounds a little strange to hear "could" receive data. Does that mean it is a readable object or does it mean there is data on the socket that may be read by the next receive_some() command? I am looking to find a way to put all my trio.nursery()'s in to one nursery group but when I do a receive_some() on a socket it locks up and freezes the app. I know the socket is readable I just need to know if it is a good time to read from it or not. I other words, if the socket is going to lock up then I can skip and check the next socket and see if there is data there to be read. And just have one loop if the socket had an attribute "read_waiting" == True when there is data that will be read or the socket will be closed as usual if the read is emtpy. |
Say you're writing an HTTP/1.1 client library, that supports connection pooling. When you take an idle connection out of your pool, it might be fine to use... OR, the server might have gotten tired of waiting for that connection to do something while it was sitting idle in the pool, and have already closed it. Because of infelicities in HTTP/1.1's design, there is no 100% reliable way to detect this, because there's a race condition: you might send a new request down the connection just as the server is closing it, and then you have no idea what happened and whether the connection dropped before or after the server started processing your request.
So, we would like to minimize how often this happens. But, because there's actually no way to detect it at the protocol level, there really is no good way to do this using regular high-level APIs. The best approach is: when you take an idle connection out of the pool, before you start using it again, do a quick check whether the underlying socket is readable. You don't want to actually read from it; there's nothing to learn by doing so. If an HTTP/1.1 server sent anything on an idle connection, it's either a 408 Request Timeout or just a preemptory close. You especially don't want to block waiting to receive data, since that would defeat the whole purpose – we just want to check whether you could receive data. And you want to do this directly on the raw socket, ignoring things like any TLS encryption layer, because TLS does not really provide any way to do a pure check for whether unencrypted bytes are available – to figure that out you first have to read the encrypted bytes and then check if they make a complete frame, and we don't want to read any bytes.
So... for this specific, weird, and incredibly important use case, you don't want to use Trio's high-level
Stream
layer, you want to peek through all the abstraction layers, ignore Trio's socket I/O API entirely, and callselect
orpoll
directly on the underlying socket just to ask the OS whether it is readable.So far, so good; Trio lets you do all that using supported, public APIs.
But! Suppose that for testing, you want to use a fake virtual networking layer. That's great: we have first-class support for that in Trio's socket I/O API (#170). Except... asks or urllib3 or whatever, for this one operation, currently has to do an end-run around Trio's socket I/O API – it has to directly call the OS's
select
orpoll
operation, which means that it needs a real OS socket, not a fake virtual socket. And that means that currently, our network faking API cannot handle HTTP clients, which is kind of an important use case.Solution: we should add a method on Trio socket objects that does this readability check. It's pretty straightforward, though there are a few complications. (Mostly, you need to use
select
on windows andpoll
everywhere else.) And the whole point of this is that libraries like asks or urllib3 will call this method instead of doing their own call toselect
/poll
. And it will do exactly the same thing they would have done by hand. Except..... since it's a method on the socket object, our fake virtual sockets will be able to override it to do a fake virtualselect
/poll
. And that will solve the problem.The text was updated successfully, but these errors were encountered: