changeset 7aaca41bca97 in /home/hg/repos/gajim
details:http://hg.gajim.org/gajim?cmd=changeset;node=7aaca41bca97
description: Try to handle incomplete HTTP. See #5401. Please test.
Approach: Keep filling the receive buffer until we have found enough
data to extract the first HTTP header and body
diffstat:
src/common/xmpp/transports_nb.py | 89 +++++++++++++++--------------
test/integration/test_xmpp_transports_nb.py | 42 +++++++-------
2 files changed, 66 insertions(+), 65 deletions(-)
diffs (173 lines):
diff -r 7d6e9b014c78 -r 7aaca41bca97 src/common/xmpp/transports_nb.py
--- a/src/common/xmpp/transports_nb.py Wed Nov 11 23:38:17 2009 +0100
+++ b/src/common/xmpp/transports_nb.py Thu Nov 12 21:23:10 2009 +0100
@@ -645,44 +645,39 @@
if self.get_state() == PROXY_CONNECTING:
NonBlockingTCP._on_receive(self, data)
return
- if not self.recvbuff:
- # recvbuff empty - fresh HTTP message was received
- try:
- statusline, headers, self.recvbuff =
self.parse_http_message(data)
- except ValueError:
- self.disconnect()
- return
- if statusline[1] != '200':
- log.error('HTTP Error: %s %s' % (statusline[1],
statusline[2]))
- self.disconnect()
- return
- self.expected_length = int(headers['Content-Length'])
- if 'Connection' in headers and
headers['Connection'].strip()=='close':
- self.close_current_connection = True
- else:
- #sth in recvbuff - append currently received data to
HTTP msg in buffer
- self.recvbuff = '%s%s' % (self.recvbuff, data)
- if self.expected_length > len(self.recvbuff):
+ # append currently received data to HTTP msg in buffer
+ self.recvbuff = '%s%s' % (self.recvbuff or '', data)
+ statusline, headers, httpbody, self.recvbuff =
self.parse_http_message(self.recvbuff)
+
+ if not (statusline and headers and httpbody):
+ log.debug('Received incomplete HTTP response')
+ return
+
+ if statusline[1] != '200':
+ log.error('HTTP Error: %s %s' % (statusline[1],
statusline[2]))
+ self.disconnect()
+ return
+ self.expected_length = int(headers['Content-Length'])
+ if 'Connection' in headers and
headers['Connection'].strip()=='close':
+ self.close_current_connection = True
+
+ if self.expected_length > len(httpbody):
# If we haven't received the whole HTTP mess yet, let's
end the thread.
# It will be finnished from one of following recvs on
plugged socket.
log.info('not enough bytes in HTTP response - %d
expected, %d got' %
(self.expected_length, len(self.recvbuff)))
- return
+ else:
+ # everything was received
+ self.expected_length = 0
- # everything was received
- httpbody = self.recvbuff
-
- self.recvbuff = ''
- self.expected_length = 0
-
- if not self.http_persistent or self.close_current_connection:
- # not-persistent connections disconnect after response
- self.disconnect(do_callback=False)
- self.close_current_connection = False
- self.last_recv_time = time.time()
- self.on_receive(data=httpbody, socket=self)
- self.on_http_request_possible()
+ if not self.http_persistent or
self.close_current_connection:
+ # not-persistent connections disconnect after
response
+ self.disconnect(do_callback=False)
+ self.close_current_connection = False
+ self.last_recv_time = time.time()
+ self.on_receive(data=httpbody, socket=self)
+ self.on_http_request_possible()
def build_http_message(self, httpbody, method='POST'):
'''
@@ -718,18 +713,24 @@
httpbody - string with http body)
'''
message = message.replace('\r','')
- # Remove latest \n
- if message.endswith('\n'):
- message = message[:-1]
- (header, httpbody) = message.split('\n\n', 1)
- header = header.split('\n')
- statusline = header[0].split(' ', 2)
- header = header[1:]
- headers = {}
- for dummy in header:
- row = dummy.split(' ', 1)
- headers[row[0][:-1]] = row[1]
- return (statusline, headers, httpbody)
+ splitted = message.split('\n\n')
+ if len(splitted) < 2:
+ # no complete http message. Keep filling the buffer
until we find one
+ buffer_rest = message
+ return ('', '', '', buffer_rest)
+ else:
+ (header, httpbody) = splitted[:2]
+ if httpbody.endswith('\n'):
+ httpbody = httpbody[:-1]
+ buffer_rest = "\n\n".join(splitted[2:])
+ header = header.split('\n')
+ statusline = header[0].split(' ', 2)
+ header = header[1:]
+ headers = {}
+ for dummy in header:
+ row = dummy.split(' ', 1)
+ headers[row[0][:-1]] = row[1]
+ return (statusline, headers, httpbody, buffer_rest)
class NonBlockingHTTPBOSH(NonBlockingHTTP):
diff -r 7d6e9b014c78 -r 7aaca41bca97 test/integration/test_xmpp_transports_nb.py
--- a/test/integration/test_xmpp_transports_nb.py Wed Nov 11 23:38:17
2009 +0100
+++ b/test/integration/test_xmpp_transports_nb.py Thu Nov 12 21:23:10
2009 +0100
@@ -227,9 +227,10 @@
data = "<test>Please don't fail!</test>"
http_message = transport.build_http_message(data)
- statusline, headers, http_body = transport.parse_http_message(
+ statusline, headers, http_body, buffer_rest =
transport.parse_http_message(
http_message)
+ self.assertFalse(bool(buffer_rest))
self.assertTrue(statusline and isinstance(statusline, list))
self.assertTrue(headers and isinstance(headers, dict))
self.assertEqual(data, http_body, msg='Input and output are
different')
@@ -250,26 +251,25 @@
transport._on_receive(message)
self.assertTrue(self.have_received_expected(), msg='Failed: In
one go')
-# FIXME: Not yet implemented.
-# def test_receive_http_message_in_chunks(self):
-# ''' Let _on_receive handle some chunked http messages '''
-# transport = self._get_transport(self.bosh_http_dict)
-#
-# header = ("HTTP/1.1 200 OK\r\nContent-Type: text/xml;
charset=utf-8\r\n" +
-# "Content-Length: 88\r\n\r\n")
-# payload = "<test>Please don't fail!</test>"
-# body = "<body
xmlns='http://jabber.org/protocol/httpbind'>%s</body>" \
-# % payload
-# message = "%s%s" % (header, body)
-#
-# chunk1, chunk2, chunk3 = message[:20], message[20:73],
message[73:]
-# nextmessage_chunk = "\r\n\r\nHTTP/1.1 200 OK\r\nContent-Type:
text/x"
-# chunks = (chunk1, chunk2, chunk3, nextmessage_chunk)
-#
-# transport.onreceive(self.expect_receive(body, msg='Failed: In
chunks'))
-# for chunk in chunks:
-# transport._on_receive(chunk)
-# self.assertTrue(self.have_received_expected(), msg='Failed: In
chunks')
+ def test_receive_http_message_in_chunks(self):
+ ''' Let _on_receive handle some chunked http messages '''
+ transport = self._get_transport(self.bosh_http_dict)
+
+ header = ("HTTP/1.1 200 OK\r\nContent-Type: text/xml;
charset=utf-8\r\n" +
+ "Content-Length: 88\r\n\r\n")
+ payload = "<test>Please don't fail!</test>"
+ body = "<body
xmlns='http://jabber.org/protocol/httpbind'>%s</body>" \
+ % payload
+ message = "%s%s" % (header, body)
+
+ chunk1, chunk2, chunk3 = message[:20], message[20:73],
message[73:]
+ nextmessage_chunk = "\r\n\r\nHTTP/1.1 200 OK\r\nContent-Type:
text/x"
+ chunks = (chunk1, chunk2, chunk3, nextmessage_chunk)
+
+ transport.onreceive(self.expect_receive(body, msg='Failed: In
chunks'))
+ for chunk in chunks:
+ transport._on_receive(chunk)
+ self.assertTrue(self.have_received_expected(), msg='Failed: In
chunks')
if __name__ == '__main__':
unittest.main()
_______________________________________________
Commits mailing list
[email protected]
http://lists.gajim.org/cgi-bin/listinfo/commits