fix chunked encoding and add tests

This commit is contained in:
Benoit Chesneau 2010-01-20 16:21:20 +01:00
parent d4ae13cde0
commit bfeb4f9416
15 changed files with 111 additions and 84 deletions

View File

@ -131,33 +131,34 @@ class HttpParser(object):
def body_eof(self):
"""do we have all the body ?"""
if self.is_chunked and self._chunk_eof:
return True
if self._content_len == 0:
return True
import sys
if self.is_chunked:
if self._chunk_eof:
return True
elif self._content_len == 0:
return True
return False
def read_chunk(self, data):
dlen = len(data)
if not self.start_offset:
i = data.find("\n")
i = data.find("\r\n")
if i != -1:
chunk = data[:i].strip().split(";", 1)
chunk_size = int(line.pop(0), 16)
self.start_offset = i+1
chunk_size = int(chunk.pop(0), 16)
self.start_offset = i+2
self.chunk_size = chunk_size
else:
buf = self.data[self.start_offset:]
end_offset = chunk_size + 2
# we wait CRLF else return None
if len(buf) == end_offset:
if chunk_size <= 0:
if self.chunk_size == 0:
self._chunk_eof = True
# we put data
return '', data[:end_offset]
return '', data[:self.start_offset]
else:
buf = data[self.start_offset:self.start_offset+self.chunk_size]
end_offset = self.start_offset + self.chunk_size + 2
# we wait CRLF else return None
if len(data) >= end_offset:
ret = buf, data[end_offset:]
self.chunk_size = 0
return buf[chunk_size:], data[:end_offset]
return ret
return '', data
def trailing_header(self, data):
@ -173,7 +174,9 @@ class HttpParser(object):
dlen = len(data)
chunk = ''
if self.is_chunked:
chunk, data = self.read_chunk(data)
if not chunk:
return '', data
else:

View File

@ -156,10 +156,33 @@ def test_010(buf, p):
t.eq(p.path, "/post_chunked_all_your_base")
t.eq(p.headers, [('Transfer-Encoding', 'chunked')])
t.eq(p.is_chunked, True)
t.eq(p._chunk_eof, False)
t.ne(p.body_eof(), True)
body = ""
buf = buf[i:]
print buf
while not p.body_eof():
chunk, buf = p.filter_body(buf)
body += chunk
print chunk
if chunk:
body += chunk
t.eq(body, "all your base are belong to us")
@t.request("011.http")
def test_011(buf, p):
headers = []
i = p.filter_headers(headers, buf)
t.ne(i, -1)
t.eq(p.method, "POST")
t.eq(p.version, (1, 1))
t.eq(p.path, "/two_chunks_mult_zero_end")
t.eq(p.headers, [('Transfer-Encoding', 'chunked')])
t.eq(p.is_chunked, True)
t.eq(p._chunk_eof, False)
t.ne(p.body_eof(), True)
body = ""
buf = buf[i:]
while not p.body_eof():
chunk, buf = p.filter_body(buf)
if chunk:
body += chunk
t.eq(body, "hello world")

View File

@ -1,6 +1,6 @@
PUT /stuff/here?foo=bar HTTP/1.0
Server: http://127.0.0.1:5984
Content-Type: application/json
Content-Length: 14
PUT /stuff/here?foo=bar HTTP/1.0\r\n
Server: http://127.0.0.1:5984\r\n
Content-Type: application/json\r\n
Content-Length: 14\r\n
\r\n
{"nom": "nom"}

View File

@ -1,5 +1,5 @@
GET /test HTTP/1.1
User-Agent: curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1
Host: 0.0.0.0=5000
Accept: */*
GET /test HTTP/1.1\r\n
User-Agent: curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1\r\n
Host: 0.0.0.0=5000\r\n
Accept: */*\r\n
\r\n

View File

@ -1,10 +1,10 @@
GET /favicon.ico HTTP/1.1
Host: 0.0.0.0=5000
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
GET /favicon.ico HTTP/1.1\r\n
Host: 0.0.0.0=5000\r\n
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n
Accept-Language: en-us,en;q=0.5\r\n
Accept-Encoding: gzip,deflate\r\n
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n
Keep-Alive: 300\r\n
Connection: keep-alive\r\n
\r\n

View File

@ -1,3 +1,3 @@
GET /dumbfuck HTTP/1.1
aaaaaaaaaaaaa:++++++++++
GET /dumbfuck HTTP/1.1\r\n
aaaaaaaaaaaaa:++++++++++\r\n
\r\n

View File

@ -1,2 +1,2 @@
GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1
GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n
\r\n

View File

@ -1,2 +1,2 @@
GET /get_no_headers_no_body/world HTTP/1.1
GET /get_no_headers_no_body/world HTTP/1.1\r\n
\r\n

View File

@ -1,3 +1,3 @@
GET /get_one_header_no_body HTTP/1.1
Accept: */*
GET /get_one_header_no_body HTTP/1.1\r\n
Accept: */*\r\n
\r\n

View File

@ -1,4 +1,4 @@
GET /get_funky_content_length_body_hello HTTP/1.0
conTENT-Length: 5
GET /get_funky_content_length_body_hello HTTP/1.0\r\n
conTENT-Length: 5\r\n
\r\n
HELLO

View File

@ -1,6 +1,6 @@
POST /post_identity_body_world?q=search#hey HTTP/1.1
Accept: */*
Transfer-Encoding: identity
Content-Length: 5
POST /post_identity_body_world?q=search#hey HTTP/1.1\r\n
Accept: */*\r\n
Transfer-Encoding: identity\r\n
Content-Length: 5\r\n
\r\n
World

View File

@ -1,6 +1,7 @@
POST /post_chunked_all_your_base HTTP/1.1
Transfer-Encoding: chunked
POST /post_chunked_all_your_base HTTP/1.1\r\n
Transfer-Encoding: chunked\r\n
\r\n
1e\r\n
all your base are belong to us\r\n
0\r\n
1e
all your base are belong to us
0

View File

@ -1,8 +1,9 @@
POST /two_chunks_mult_zero_end HTTP/1.1
Transfer-Encoding: chunked
5
hello
6
world
000
POST /two_chunks_mult_zero_end HTTP/1.1\r\n
Transfer-Encoding: chunked\r\n
\r\n
5\r\n
hello\r\n
6\r\n
world\r\n
000\r\n
\r\n

View File

@ -1,10 +1,11 @@
POST /chunked_w_trailing_headers HTTP/1.1
Transfer-Encoding: chunked
5
hello
6
world
0
Vary: *
Content-Type: text/plain
POST /chunked_w_trailing_headers HTTP/1.1\r\n
Transfer-Encoding: chunked\r\n
\r\n
5\r\n
hello\r\n
6\r\n
world\r\n
0\r\n
Vary: *\r\n
Content-Type: text/plain\r\n
\r\n

View File

@ -16,10 +16,8 @@ def data_source(fname, eol):
with open(fname) as handle:
lines = []
for line in handle:
next = line.rstrip("\r\n") + eol
if next == "\r\n":
eol = ""
lines.append(next)
line = line.rstrip("\n").replace("\\r\\n", "\r\n")
lines.append(line)
return "".join(lines)
class request(object):