How to keep an inactive connection open with PycURL? - python

Pseudo-code to better explain question:
#!/usr/bin/env python2.7
import pycurl, threading
def threaded_work():
conn = pycurl.Curl()
conn.setopt(pycurl.TIMEOUT, 10)
# Make a request to host #1 just to open the connection to it.
conn.setopt(pycurl.URL, 'https://host1.example.com/')
conn.perform_rs()
while not condition_that_may_take_very_long:
conn.setopt(pycurl.URL, 'https://host2.example.com/')
print 'Response from host #2: ' + conn.perform_rs()
# Now, after what may be a very long time, we must request host #1 again with a (hopefully) already established connection.
conn.setopt(pycurl.URL, 'https://host1.example.com/')
print 'Response from host #1, hopefully with an already established connection from above: ' + conn.perform_rs()
conn.close()
for _ in xrange(30):
# Multiple threads must work with host #1 and host #2 individually.
threading.Thread(target = threaded_work).start()
I am omitting extra, only unnecessary details for brevity so that the main problem has focus.
As you can see, I have multiple threads that must work with two different hosts, host #1 and host #2. Mostly, the threads will be working with host #2 until a certain condition is met. That condition may take hours or even longer to be met, and will be met at different times in different threads. Once the condition (condition_that_may_take_very_long in the example) is met, I would like host #1 to be requested as fast as possible with the connection that I have already established at the start of the threaded_work method. Is there any efficient way to efficiently accomplish this (open to the suggestion of using two PycURL handles, too)?

Pycurl uses libcurl. libcurl keeps connections alive by default after use, so as long as you keep the handle alive and use that for the subsequent transfer, it will keep the connection alive and ready for reuse.
However, due to modern networks and network equipment (NATs, firewalls, web servers), connections without traffic are often killed off relatively soon so having an idle connection and expecting it to actually work after "hours", is a very slim chance and rare occurance. Typically, libcurl will then discover that the connection has been killed in the mean time and create a new one to use at the next use.
Additionally, and in line with what I've described above, since libcurl 7.65.0 it now defaults to not reusing connections anymore that are older than 118 seconds. Changeable with the CURLOPT_MAXAGE_CONN option. The reason is that they barely ever work so by avoiding having to keep them around, detect them to be dead and reissue the request, this is an optimization.

Related

MicroPython usockets not timing out

For various reasons, I am trying to have my ESP32 device with MicroPython poll all 256 options of 192.168.1.*:79 to find a 'host' PC. In doing so, the ESP32 attempts to create a socket and connect it to each possible address, i.e.:
while not connected:
try:
addr = generate_next_address()
s = usocket.socket()
s.connect(addr)
except OSError:
s.close()
continue
print("Found a connection!")
connected = True
When attempting to send a connection to a device that refuses the connect(), it is very quick to throw the exception and move onward. However, the problem is when it starts encountering devices that either don't respond or don't exist, it waits for a significant time before timing out.
Now, I've tried every variation of using usocket.settimeout(), usocket.setblocking(), uselect.poll(), and time.delay(), but I was unable to get anything to change the timeout period.
By setting blocking to false, the script immediately attempts all 256 addresses and then breaks out of the while loop, disallowing an opportunity to connect properly. Having blocking on completely ignores any timeout setting I attempt, continuing to take 15-20 seconds to timeout, as opposed to 1.
Is there something I'm not understanding about how this works? Is there a solution that is obvious but I have missed?

socket.timeout; Explanation?

I am building a port scanning program ((irrelevant to the question, just explaining the background)), and I know the IP of the host, but not what ports are open. Hence, the scan.
It is in the early stages of development, so the error handling is bad, but not bad enough to make why Python does this explainable.
It tries to connect to, say, 123.456.7.8, 1. Obviously it's a ridiculous port to be open, so it throws an error. The error is No Route to Host or the such, right? Wrong! It is instead Operation Timed Out!.
Okay, let's increase the timeout in case my calculations were incorrect.
.
..
...
....All that did was rinse and repeat!
About 20 minutes later, the timeout is at 20 seconds, and it still is timing out. Really? Why does python raise a timed out error though, instead of No route to host! or similar?
I need to distinguish between time outs and connection failures, because there is a difference between late and nowhere. This prevents me from doing so, creating an infinite loop of hurry up and wait.
Whatever shall I do? Wherever shall I go?
Python socket module is a thin wrapper around your platform's socket API. The issue is unrelated to Python.
It is not necessary that you get No Route to Host error. Moreover it is common that a firewall just drops received packets (for a filtered port) that may manifest as a timeout error in your code. See Drop vs. Reject (ignore the conclusion but read the explanation of what is happening).
To workaround, make multiple concurrent connections and set a fixed timeout or use raw-sockets and send the packets yourself (you could use scapy, to investigate the behavior).

Python using try to reduce timeout wait

I am using exscripts module which has a call conn.connect('IP address').
It tries to open a telnet session to that IP.
It will generate an error after connection times out.
The timeout exception is set somewhere in the code of the module or it would be what the default for telnet is. (not sure)
This timeout time is too long and slowing down the script if 1 device is not reachable. Is there something we can do with the try except here ? Like
Try for 3 secs:
then process the code
except:
print " timed out"
We changed the API. Mike Pennington only recently introduced the new connect_timeout parameter for that specific use case.
New solution (current master, latest release on pypi 2.1.451):
conn = Telnet(connect_timeout=3)
We changed the API because you usually don't want to wait for unreachable devices, but want to wait for commands to finish (some take a little longer).
I think you can use
conn = Telnet(timeout=3)
I dont know whether timeout in seconds. If microseconds, try 3000

Timeout for idle connection

I am using asyncore and asynchat modules to build a SMTP server (I used code from smtpd lib to build the SMTP server) but I have a problem with connection timeouts. When I open telnet connection to the SMTP server and leave it so, the connection is established althought no data exchange happens. I want to set timeout e.g 30 seconds and to close the idle connection by server if nothing comes from the client (else there could be an easy DOS vulnerability). I googled for a solution, read source codes and documentation but didn't found anything usable.
Thanks
According to asyncore documentation, asyncore.loop() has a timeout parameter, which defaults to 30 seconds. So apparently default already should be 30 seconds, you can try and play with it to suit your own needs.
The timeout argument sets the timeout parameter for the appropriate
select() or poll() call, measured in seconds; the default is 30
seconds.
Ok, the above actually refers to poll() or select() timeout and not the idle timeout.
As per this page, you can hack asyncore to support timeouts like this:
Add the following block to your own copy of asyncore.poll just after the for fd in e: block...
#handle timeouts
rw = set(r) + set(w)
now = time.time()
for f in (i for i in rw if i in map):
map[f].lastdata = now
for j in (map[i] for i in map if i not in rw):
if j.timeout+j.lastdata now:
#timeout!
j.handle_close()
You ARE going to need to initialize .timeout and .lastdata members for
every instance, but that shouldn't be so bad (for a socket that
doesn't time out, I would actually suggest a 1 hour or 1 day timeout).

Python 3.2.3: Socket takes longer to timeout than it should?

I'm using Python 3.2.3 on Windows 7, and one piece of code I have connects to a server with a blocking socket, with a user-specified timeout value. The code is simply:
testconn = socket.create_connection((host, port), timeout)
The code works fine, apart from the odd fact that timing out seems to take longer than it should on invalid requests. I tried connecting to www.google.com:59855 deliberately (random port should mean it should try connecting until it reaches the timeout), with a timeout of 5 seconds, but it seemed to take 15 seconds at least to timeout.
Are there any possible reasons for this, and/or any fixes? (It's not a huge problem if it's not fixable, but a solution would be appreciated nevertheless.) Thanks in advance.
This isn't an issue specific to Python 3 or Windows. Take a look at the docs for create_connection(): http://docs.python.org/library/socket.html#socket.create_connection
The important snippet is:
if host is a non-numeric hostname, it will try to resolve it for both
AF_INET and AF_INET6, and then try to connect to all possible
addresses in turn until a connection succeeds.
It resolves the name using socket.getaddrinfo. If you run
socket.getaddrinfo('google.com', 59855, 0, socket.SOCK_STREAM)
You'll probably get a few results returned. When you call socket.create_connection, it will iterate over all of those results, each waiting for timeout seconds until it fails. Because it waits timeout seconds for EACH result, the total time is obviously going to be greater than timeout.
If you call create_connection with an IP address rather than host name, e.g.
testconn = socket.create_connection(('74.125.226.201', 59855), timeout=5)
you should get your 5 second timeout.
And if you're really curious, take a look at the source for create_connection. It's pretty simple and you can see the loop that is causing your problems:
https://github.com/python/cpython/blob/3.2/Lib/socket.py#L408

Categories