Is there a way to make Pulsar read the whole stream sent by the client before closing its socket? Because this causes bugs and failures in case of multiple clients.
Hi,Thank you for your answer.The problem is that when pulsar raises an HTTP error, he resets the connection, causing the client to receive RST packets.
Here is an extract of the RFC 7230 explaining the problem and solution:
To avoid the TCP reset problem, servers typically close a connectionin stages. First, the server performs a half-close by closing onlythe write side of the read/write connection. The server thencontinues to read from the connection until it receives acorresponding close by the client, or until the server is reasonablycertain that its own TCP stack has received the client'sacknowledgement of the packet(s) containing the server's lastresponse. Finally, the server fully closes the connection."
Is there a workaround in pulsar or is there a way to configure pulsar to never close the socket in case of error and wait for client close?
The http client is a standerd brownser.It is actually not about pipelining this happens with a single request-response exchange.
imagine a client making a PUT request to upload a huge file.The client sends its header and then start sending the packects of the file.As soon as pulsar gets the header of the client's request and sees he is not authenticated, he sends backs an HTTP error 401.So far so good.But he also closes his socket immediatly sending a RST packet whereas the normal HTTP way would be to send a FIN packet, receive FINACK, and wait faor the client's FIN packet to close the socket.It is actually not about waiting for the whole body of the request, which could be not very optimal if it's a really big one, but about closing the socket after the client said he had been told about it.We could imagine a simple pulsar app sending back 401 error to any request received. If you would use curl to send a big request you would see that it is cut in the middle of it in an inappropriate way.
Run the following server code:
from pulsar.apps import wsgi
from pulsar.utils.exceptions import HttpException
def hello(environ, start_response=None):
raise HttpException(status=401)
if __name__ == '__main__':
wsgi.WSGIServer(callable=hello).start()
$ python3 server.py --bind X.X.X.X
Note that you have to use an IP address corresponding to a real ethernet card, not the loopback interface (The MTU needs to be small enough, lo usually has a pretty big MTU, 65k on my system vs 1500 for an ethernet card).
Create a big file to upload (we have to use a pretty big file because the RTT between the client and the server is really low in this configuration, in real life, we have experienced the problem with 5 Mo files).
$ dd if=/dev/urandom of=bigfile.bin bs=1K count=…
Upload the file to the server, which most of the time makes the bug appear:
$ curl –v --upload-file …
curl output:
* Send failure: Connection reset by peer
* Closing connection 0
curl: (55) Send failure: Connection reset by peer
As described before, this bug is due to the server immediately closing the connection on error, instead of first performing a half close and then waiting for the close from the client (“TCP reset problem” described here: http://tools.ietf.org/html/rfc7230#section-6.6).
Instead of closing the socket directly with socket.close(), the framework should follow those steps:
· Call socket.shutdown(socket.SHUT_WR), to indicate that we want to close the connection
· Continue reading what the client is sending
· Once the client has closed the connection, call socket.close().
As described before, this bug is due to the server immediately closing the connection on error, instead of first performing a half close and then waiting for the close from the client (“TCP reset problem” described here: http://tools.ietf.org/html/rfc7230#section-6.6).
Instead of closing the socket directly with socket.close(), the framework should follow those steps:
· Call socket.shutdown(socket.SHUT_WR), to indicate that we want to close the connection
· Continue reading what the client is sending
· Once the client has closed the connection, call socket.close().
OK, can you check if this commit in master fixes your problem?I cannot replicate your issue on my local machine, even with a 10GB file and the card ethernet address.
I have not yet being able to consistently reproduce the issue, but the RST packet is definitely there early. I'll look into other WSGI implementations to see how they are dealing with it.