I noticed a strange behavior for a long time that cause my server do extra work. For Safari browser. Whenever you touch the address bar and start to edit the existing URL, the browser sends the same get request to the server and close the connection before the server return the response. When you finish editing the address and hit enter it will send the new request.
This behavior can happen multiple times while you edit your URL in the address bar.
This cause the server to fully process the response and when it return the result it through Broken pipe.
This happen on both cases for DEBUG = True/False. So I can see it on local debug server and I can see a request happening on my NGINX production server.
Is there a way to identify this request so to not serve results and save the server processing power?
Thanks
Related
I have an application in production for couple of years in Google App Engine (GAP). In a given entry point of my application I get data from a 3rd party API using requests as such:
#app.route('/example')
def example():
response = requests.get(url, params=PARAMS, headers=HEADERS)
Studently (2 days ago), this peace of code stopped working. Basically, what happens is that the request is hanging, and after 30sec, the worker dies with the error message:
upstream prematurely closed connection while reading response header from upstream, client...
Nonetheless, the API responds immediately when executing the code in my local computer. The same exact behavior is detected when using curl in shell.
Any idea what might causing this? Or to debug it? Can this be related with some DNS problem? I don't even know if the problem is related with the API or is GAE that somehow is blocking the request.
Thanks in advance!
I have some theories:
(1) Rate Limiting -- If you are doing this API call a lot, they may be blocking your IP address.
(2) 1st Gen GAE headers -- 1st gen GAE sends special headers with every request. This makes it obvious that a computer is initiating the request and not a person. It is possible that the third party blocks requests with these headers.
(3) Blocking Cloud IPs -- Google publishes a full list of its IP addresses and the third party may be blocking all of them.
Given that it works on your own computer with curl, I suspect (1) is the answer. If they would do (2) or (3) then they would likely also block curl.
The real question is if Google App Engine guarantees it would complete a HTTP request even if the connection is no longer existed (such as terminated, lost Internet connection).
Says we have a python script running on Google App Engine:
db.put(status = "Outputting")
print very_very_very_long_string_like_1GB
db.put(status = "done")
If the client decides to close the connection in the middle (too much data coming...), will status = "done" be executed? Or will the instance be killed and all following code be ignored?
If the client breaks the connect, the request will continue to execute. Unless it reaches the deadline of 60 seconds.
GAE uses Pending Queue to queue up requests. If client drops connection and request is already in the queue or being executed, then it will not be aborted. Afaik all other http servres behave the same way.
This will be a real problem when you make requests that change state (PUT, POST, DELETE) on mobile networks. On Edge networks we see about 1% of large requests (uploads, ~500kb) dropped in the middle of request executing (exec takes about 1s): e.g. server gets the data and processes it, but client does not receive response, triggering it to retry. This could produce duplicate data in the DB, breaking integrity of this data.
To alleviate this you will need to make your web methods idempotent: repeating the same method with same arguments does not change state. The easiest way to achieve this would be one of:
Hash relevant data and compare to existing hashes. In you case it would be the string you are trying to save (very_very_very_long_string_like_1GB). You can do this server side.
Client provides unique request-scoped ID, and sever checks if this ID was already used.
I have a piece of custom built web server code. It was written using the evnet module.
It seems to the cut the length of the message when requested from a remote client. But when I use it on the same machine, it seems to deliver the full message. I can't figure out what the problem could be or how to diagnose it. I tested it using a web browser, curl and nc. It never delivered the full length message when requesting from remote clients.
Here's a simplified version of my webserver that still exhibits the problem. I am doing this on Ubuntu 11.04 with Python 2.7.1
You are closing the socket immediately after calling send() to send a bunch of data. If there is data still buffered, it will be thrown away when you close the socket.
Instead, you should call shutdown(SHUT_WR) on the socket to tell the remote end that you are finished sending. This is called a TCP "half-close". In response, the other end will close its side, and you will get a notification that the socket is no longer readable. Then, and only then, should you close the socket handle.
I am running a django application on twisted using the django-on-twisted scripts from this site.
All requests are served by an nginx server which reverse proxies relevant requests to twisted. I have a url setup for an API, which basically just receives get requests and does some processing on the get parameters before sending a response. However, when a specific client is hitting the api, the twisted server just shuts down. Pasted below is the Nginx log:
the.ip.of.client - - [21/Apr/2012:11:30:36 -0400] "GET /api/url/?get=params&more=params HTTP/1.1" 499 0 "-" "Java/1.6.0_24"
The twisted logs show nothing but twisted stops working at this point. By the error code 499, i am assuming that the client closed the connection unexpectedly, which I have no problem with. Whether the client receives the response or not is not important to me. Here is the relevant django view:
def api_url(request):
if request.GET:
get_param = request.GET.get('get', [''])[0]
more_param = request.GET.get('more', [''])[0]
#some processing here based on the get params
return HttpResponse('OK')
else:
raise Http404
The request from the client is a valid request and does not affect the processing in an adverse way. I have tested it from the shell. When I tried it on the django development server, it crashed in the same way too without leaving any traces of receiving the request. Everything works perfectly well when testing it from the browser. Also, the twisted server works well for all the regular use cases. This is the first time I am facing an issue with it. Any help or pointers will be appreciated.
There is no 499 http code in rfc. Nginx defines 499 code itself.
When a client sent a request, and closed the connection without waiting for
the response, a 499 code occurs. If there're a lot of 499s in your
access_log, it's mostly caused by the slow back-ends (too slow for your
users to wait). You may have to optimize your website performance.
http://forum.nginx.org/read.php?2,213789,213794#msg-213794
you say the problem is from a client hitting a particular url (reproducible?)
since it works for you with gunicorn but not django-on-twisted, either the script is not working properly or twisted.web2 is the issue.
please try $ sh init.sh yourdjangoproject stand.
you can also try to modify run.py to catch SystemExit:
import pdb
try:
# __main__ stuff here.
except (KeyboardInterrupt, SystemExit):
pdb.set_trace()
We're developing a Python web service and a client web site in parallel. When we make an HTTP request from the client to the service, one call consistently raises a socket.error in socket.py, in read:
(104, 'Connection reset by peer')
When I listen in with wireshark, the "good" and "bad" responses look very similar:
Because of the size of the OAuth header, the request is split into two packets. The service responds to both with ACK
The service sends the response, one packet per header (HTTP/1.0 200 OK, then the Date header, etc.). The client responds to each with ACK.
(Good request) the server sends a FIN, ACK. The client responds with a FIN, ACK. The server responds ACK.
(Bad request) the server sends a RST, ACK, the client doesn't send a TCP response, the socket.error is raised on the client side.
Both the web service and the client are running on a Gentoo Linux x86-64 box running glibc-2.6.1. We're using Python 2.5.2 inside the same virtual_env.
The client is a Django 1.0.2 app that is calling httplib2 0.4.0 to make requests. We're signing requests with the OAuth signing algorithm, with the OAuth token always set to an empty string.
The service is running Werkzeug 0.3.1, which is using Python's wsgiref.simple_server. I ran the WSGI app through wsgiref.validator with no issues.
It seems like this should be easy to debug, but when I trace through a good request on the service side, it looks just like the bad request, in the socket._socketobject.close() function, turning delegate methods into dummy methods. When the send or sendto (can't remember which) method is switched off, the FIN or RST is sent, and the client starts processing.
"Connection reset by peer" seems to place blame on the service, but I don't trust httplib2 either. Can the client be at fault?
** Further debugging - Looks like server on Linux **
I have a MacBook, so I tried running the service on one and the client website on the other. The Linux client calls the OS X server without the bug (FIN ACK). The OS X client calls the Linux service with the bug (RST ACK, and a (54, 'Connection reset by peer')). So, it looks like it's the service running on Linux. Is it x86_64? A bad glibc? wsgiref? Still looking...
** Further testing - wsgiref looks flaky **
We've gone to production with Apache and mod_wsgi, and the connection resets have gone away. See my answer below, but my advice is to log the connection reset and retry. This will let your server run OK in development mode, and solidly in production.
I've had this problem. See The Python "Connection Reset By Peer" Problem.
You have (most likely) run afoul of small timing issues based on the Python Global Interpreter Lock.
You can (sometimes) correct this with a time.sleep(0.01) placed strategically.
"Where?" you ask. Beats me. The idea is to provide some better thread concurrency in and around the client requests. Try putting it just before you make the request so that the GIL is reset and the Python interpreter can clear out any pending threads.
Don't use wsgiref for production. Use Apache and mod_wsgi, or something else.
We continue to see these connection resets, sometimes frequently, with wsgiref (the backend used by the werkzeug test server, and possibly others like the Django test server). Our solution was to log the error, retry the call in a loop, and give up after ten failures. httplib2 tries twice, but we needed a few more. They seem to come in bunches as well - adding a 1 second sleep might clear the issue.
We've never seen a connection reset when running through Apache and mod_wsgi. I don't know what they do differently, (maybe they just mask them), but they don't appear.
When we asked the local dev community for help, someone confirmed that they see a lot of connection resets with wsgiref that go away on the production server. There's a bug there, but it is going to be hard to find it.
Normally, you'd get an RST if you do a close which doesn't linger (i.e. in which data can be discarded by the stack if it hasn't been sent and ACK'd) and a normal FIN if you allow the close to linger (i.e. the close waits for the data in transit to be ACK'd).
Perhaps all you need to do is set your socket to linger so that you remove the race condition between a non lingering close done on the socket and the ACKs arriving?
I had the same issue however with doing an upload of a very large file using a python-requests client posting to a nginx+uwsgi backend.
What ended up being the cause was the the backend had a cap on the max file size for uploads lower than what the client was trying to send.
The error never showed up in our uwsgi logs since this limit was actually one imposed by nginx.
Upping the limit in nginx removed the error.