📜 ⬆️ ⬇️

How I debugged the python httplib and httplib2

It took me once in my project to implement work with file storage using the HTTP REST API. The project is being developed in python, and the http client has already been implemented using the httplib2 library, so it was decided to extend the http client functionality and work with the file storage through the same library. The problem occurred when uploading files to the server. The first PUT request is executed, then all subsequent requests refuse to be executed - 500 Internal Server Error .

I look at Wireshark and it turns out that after the first request the server sends connection: headers in the response : connection: keep alive and closes the connection after 5 seconds. It's simple - this keep-alive timeout is set on the server.


')
And this is how it looks on the client:

I include debug logs for httplib2:

httplib2.debuglevel = 4 

Perform a PUT request on the client:

 res = rq.request(url, 'PUT', mediafile, h) connect: (system.restfs.test, 9990) ************ send: 'PUT /domain/p.city/sounds/test_folder/12.wav HTTP/1.1\r\nHost: system.restfs.test:9990\r\nContent-Length: 13864\r\ncontent-type: audio/x-wav\r\naccept-encoding: gzip, deflate\r\nuser-agent: Python-httplib2/0.8 (gzip)\r\n\r\n' send: <open file '/home/mixo/\xd0\x97\xd0\xb0\xd0\xb3\xd1\x80\xd1\x83\xd0\xb7\xd0\xba\xd0\xb8/12.wav', mode 'r' at 0x7fca638c4db0> sendIng a read()able reply: 'HTTP/1.1 201 Created\r\n' header: connection: keep-alive header: server: Cowboy header: date: Fri, 11 Dec 2015 05:14:09 GMT header: content-length: 0 header: content-type: audio/x-wav 

Repeated PUT request:

 res = rq.request(url, 'PUT', mediafile, h) send: 'PUT /domain/p.city/sounds/test_folder/12.wav HTTP/1.1\r\nHost: system.restfs.test:9990\r\nContent-Length: 13864\r\ncontent-type: audio/x-wav\r\naccept-encoding: gzip, deflate\r\nuser-agent: Python-httplib2/0.8 (gzip)\r\n\r\n' send: <open file '/home/mixo/\xd0\x97\xd0\xb0\xd0\xb3\xd1\x80\xd1\x83\xd0\xb7\xd0\xba\xd0\xb8/12.wav', mode 'r' at 0x7f167a933030> sendIng a read()able reply: '' connect: (system.restfs.test, 9990) ************ send: 'PUT /domain/p.city/sounds/test_folder/12.wav HTTP/1.1\r\nHost: system.restfs.test:9990\r\nContent-Length: 13864\r\ncontent-type: audio/x-wav\r\naccept-encoding: gzip, deflate\r\nuser-agent: Python-httplib2/0.8 (gzip)\r\n\r\n' send: <open file '/home/mixo/\xd0\x97\xd0\xb0\xd0\xb3\xd1\x80\xd1\x83\xd0\xb7\xd0\xba\xd0\xb8/12.wav', mode 'r' at 0x7f167a933030> sendIng a read()able reply: 'HTTP/1.1 500 Internal Server Error\r\n' header: connection: keep-alive header: server: Cowboy header: date: Fri, 11 Dec 2015 05:26:27 GMT header: content-length: 0 header: content-type: audio/x-wav 

Here we see that httplib2 honestly, as prescribed by the server, does not re-establish the connection and sends a new request to the same socket without receiving a response, the connection is re-established and sends a second request. But this repeated request is no longer processed by the server, but an error 500 is returned.



Moreover, if you compare the Wireshark logs for the two requests, it is clear that after reinstalling the connection, the file is not sent and the request is terminated and is not sent completely.

Immediately, as a temporary solution, it was chosen to put in the response on the server the header connection: close . This option turned out to be a worker: it's nice to feel that you are on the right track, and the decision is close.

But not as close as I thought then. After examining the source code for httplib (which is expanding httplib2), a simpler solution was chosen and a pull-request for httplib was created:



During the detailed review of the pull-request together with the support guys, it turned out that the problem is at the junction of the httplib library and the httplib2 library:


In the case of re-sending the request, you need to start reading the file again. It remains to choose the culprit and execute to choose a measure of restraint. In the httplib library, when sending a file, it is assumed that if the object has a read () method, then it can be read and transmitted. Is it also possible to assume that the same object has the tell () and seek () methods and to return the cursor to the beginning of the file. And if so, then this logic must still be rendered in httplib2. As a result, a pull-request for httplib2 was created. At the moment, the problem found does not have a final solution, but there is a great desire to bring it to a victorious final.

I hope this post will be useful. Thanks for attention.

Source: https://habr.com/ru/post/273051/


All Articles