Reputation: 309
I have a simple http server in python that implement PUT using 100 continue:
class TestHandler(SimpleHTTPRequestHandler):
def do_PUT(self):
length = int(self.headers.get('Content-Length'))
self.send_response_only(100)
self.end_headers()
data = self.rfile.read(length)
res = manipulate(data)
new_length = len(res)
self.send_response(200)
self.send_header("Content-Length", new_length)
self.end_headers()
self.wfile.write(res)
server = HTTPServer(("localhost", 8080), TestHandler)
server.serve_forever()
I try to connect to the server using this client:
def send_put(data):
c = HTTPConnection('localhost', 8080)
c.request('PUT', 'http://localhost:8080/', headers={'Content-Length': len(data), 'Expect': '100-continue'})
r = c.getresponse()
if 100 != r.status:
return
c.request('PUT', 'http://localhost:8080/', body=data)
r = c.getresponse()
print(r.read())
but the code always get stuck on the first 'getresponse' even though I can see the 100-continue response on wireshark, what am I doing wrong here? Is python http even support 100-continue?
EDIT: after looking at some of python http code I found why the getresponse is stuck; python's http just ignores the 100-continue and waits for the next response that never comes(from python3.4/http/client.py):
# read until we get a non-100 response
while True:
version, status, reason = self._read_status()
if status != CONTINUE:
break
# skip the header from the 100 response
while True:
skip = self.fp.readline(_MAXLINE + 1)
if len(skip) > _MAXLINE:
raise LineTooLong("header line")
skip = skip.strip()
if not skip:
break
if self.debuglevel > 0:
print("header:", skip)
Upvotes: 3
Views: 3450
Reputation: 181998
I ran into this as well; it's a nine year old Python issue. I came up with the following rather gross "just get it to run" workaround, which seems to work in my case (Python 3.5, HTTPS only):
class ContinueHTTPResponse(http.client.HTTPResponse):
def _read_status(self, *args, **kwargs):
version, status, reason = super()._read_status(*args, **kwargs)
if status == 100:
status = 199
return version, status, reason
def begin(self, *args, **kwargs):
super().begin(*args, **kwargs)
if self.status == 199:
self.status = 100
def _check_close(self, *args, **kwargs):
return super()._check_close(*args, **kwargs) and self.status != 100
class ContinueHTTPSConnection(http.client.HTTPSConnection):
response_class = ContinueHTTPResponse
def getresponse(self, *args, **kwargs):
logging.debug('running getresponse')
response = super().getresponse(*args, **kwargs)
if response.status == 100:
setattr(self, '_HTTPConnection__state', http.client._CS_REQ_SENT)
setattr(self, '_HTTPConnection__response', None)
return response
I'm using it somewhat like this:
conn = ContinueHTTPSConnection(host)
conn.request(...)
resp = conn.getresponse()
if resp.status == http.client.CONTINUE:
resp.read()
conn.send(body)
resp = conn.getresponse()
# do something with resp if you want...
Caveat: super hacky. Probably full of bugs. Use at your own risk.
Upvotes: 2