wsgi: chunked transfer requests
This commit is contained in:
@@ -52,7 +52,8 @@ def format_date_time(timestamp):
|
|||||||
|
|
||||||
|
|
||||||
class Input(object):
|
class Input(object):
|
||||||
def __init__(self, rfile, content_length, wfile=None, wfile_line=None):
|
def __init__(self, rfile, content_length, wfile=None, wfile_line=None,
|
||||||
|
chunked_input=False):
|
||||||
self.rfile = rfile
|
self.rfile = rfile
|
||||||
if content_length is not None:
|
if content_length is not None:
|
||||||
content_length = int(content_length)
|
content_length = int(content_length)
|
||||||
@@ -62,6 +63,8 @@ class Input(object):
|
|||||||
self.wfile_line = wfile_line
|
self.wfile_line = wfile_line
|
||||||
|
|
||||||
self.position = 0
|
self.position = 0
|
||||||
|
self.chunked_input = chunked_input
|
||||||
|
self.chunk_length = -1
|
||||||
|
|
||||||
def _do_read(self, reader, length=None):
|
def _do_read(self, reader, length=None):
|
||||||
if self.wfile is not None:
|
if self.wfile is not None:
|
||||||
@@ -80,7 +83,38 @@ class Input(object):
|
|||||||
self.position += len(read)
|
self.position += len(read)
|
||||||
return read
|
return read
|
||||||
|
|
||||||
|
def _chunked_read(self, rfile, length=None):
|
||||||
|
if self.wfile is not None:
|
||||||
|
## 100 Continue
|
||||||
|
self.wfile.write(self.wfile_line)
|
||||||
|
self.wfile = None
|
||||||
|
self.wfile_line = None
|
||||||
|
|
||||||
|
response = []
|
||||||
|
if length is None:
|
||||||
|
if self.chunk_length > self.position:
|
||||||
|
response.append(rfile.read(self.chunk_length - self.position))
|
||||||
|
while self.chunk_length != 0:
|
||||||
|
self.chunk_length = int(rfile.readline(), 16)
|
||||||
|
response.append(rfile.read(self.chunk_length))
|
||||||
|
rfile.readline()
|
||||||
|
else:
|
||||||
|
while length > 0 and self.chunk_length != 0:
|
||||||
|
if self.chunk_length > self.position:
|
||||||
|
response.append(rfile.read(
|
||||||
|
min(self.chunk_length - self.position, length)))
|
||||||
|
length -= len(response[-1])
|
||||||
|
self.position += len(response[-1])
|
||||||
|
if self.chunk_length == self.position:
|
||||||
|
rfile.readline()
|
||||||
|
else:
|
||||||
|
self.chunk_length = int(rfile.readline(), 16)
|
||||||
|
self.position = 0
|
||||||
|
return ''.join(response)
|
||||||
|
|
||||||
def read(self, length=None):
|
def read(self, length=None):
|
||||||
|
if self.chunked_input:
|
||||||
|
return self._chunked_read(self.rfile, length)
|
||||||
return self._do_read(self.rfile.read, length)
|
return self._do_read(self.rfile.read, length)
|
||||||
|
|
||||||
def readline(self):
|
def readline(self):
|
||||||
@@ -317,8 +351,10 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
|
|||||||
else:
|
else:
|
||||||
wfile = None
|
wfile = None
|
||||||
wfile_line = None
|
wfile_line = None
|
||||||
|
chunked = env.get('HTTP_TRANSFER_ENCODING', '').lower() == 'chunked'
|
||||||
env['wsgi.input'] = env['eventlet.input'] = Input(
|
env['wsgi.input'] = env['eventlet.input'] = Input(
|
||||||
self.rfile, length, wfile=wfile, wfile_line=wfile_line)
|
self.rfile, length, wfile=wfile, wfile_line=wfile_line,
|
||||||
|
chunked_input=chunked)
|
||||||
|
|
||||||
return env
|
return env
|
||||||
|
|
||||||
|
@@ -57,6 +57,14 @@ def big_chunks(env, start_response):
|
|||||||
for x in range(10):
|
for x in range(10):
|
||||||
yield line
|
yield line
|
||||||
|
|
||||||
|
def chunked_post(env, start_response):
|
||||||
|
start_response('200 OK', [('Content-type', 'text/plain')])
|
||||||
|
if env['PATH_INFO'] == '/a':
|
||||||
|
return [env['wsgi.input'].read()]
|
||||||
|
elif env['PATH_INFO'] == '/b':
|
||||||
|
return [x for x in iter(lambda: env['wsgi.input'].read(4096), '')]
|
||||||
|
elif env['PATH_INFO'] == '/c':
|
||||||
|
return [x for x in iter(lambda: env['wsgi.input'].read(1), '')]
|
||||||
|
|
||||||
class Site(object):
|
class Site(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -291,6 +299,34 @@ class TestHttpd(TestCase):
|
|||||||
res = httpc.get("https://localhost:4202/foo")
|
res = httpc.get("https://localhost:4202/foo")
|
||||||
self.assertEquals(res, '')
|
self.assertEquals(res, '')
|
||||||
|
|
||||||
|
def test_014_chunked_post(self):
|
||||||
|
self.site.application = chunked_post
|
||||||
|
sock = api.connect_tcp(('127.0.0.1', 12346))
|
||||||
|
fd = sock.makeGreenFile()
|
||||||
|
fd.write('PUT /a HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n'
|
||||||
|
'Transfer-Encoding: chunked\r\n\r\n'
|
||||||
|
'2\r\noh\r\n4\r\n hai\r\n0\r\n\r\n')
|
||||||
|
fd.readuntil('\r\n\r\n')
|
||||||
|
response = fd.read()
|
||||||
|
self.assert_(response == 'oh hai', 'invalid response %s' % response)
|
||||||
|
|
||||||
|
sock = api.connect_tcp(('127.0.0.1', 12346))
|
||||||
|
fd = sock.makeGreenFile()
|
||||||
|
fd.write('PUT /b HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n'
|
||||||
|
'Transfer-Encoding: chunked\r\n\r\n'
|
||||||
|
'2\r\noh\r\n4\r\n hai\r\n0\r\n\r\n')
|
||||||
|
fd.readuntil('\r\n\r\n')
|
||||||
|
response = fd.read()
|
||||||
|
self.assert_(response == 'oh hai', 'invalid response %s' % response)
|
||||||
|
|
||||||
|
sock = api.connect_tcp(('127.0.0.1', 12346))
|
||||||
|
fd = sock.makeGreenFile()
|
||||||
|
fd.write('PUT /c HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n'
|
||||||
|
'Transfer-Encoding: chunked\r\n\r\n'
|
||||||
|
'2\r\noh\r\n4\r\n hai\r\n0\r\n\r\n')
|
||||||
|
fd.readuntil('\r\n\r\n')
|
||||||
|
response = fd.read(8192)
|
||||||
|
self.assert_(response == 'oh hai', 'invalid response %s' % response)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
Reference in New Issue
Block a user