def test_read_line_to_piece(self): ''' Make sure _handle_data() reads pieces after lines ''' client = http_clnt.HttpClient() context = http_clnt.ClientContext({}, None, None) stream = FakeStream(context) self.pieces = [] # Start over self.lines = [] # Start over context.handle_piece = self.handle_piece context.handle_line = self.handle_line bytez = six.b('').join([ six.b('HTTP/1.1 200 Ok\r\n'), six.b('Content-Type: text/plain\r\n'), six.b('Server: Neubot/0.0.1.0\r\n'), six.b('\r\n'), six.b('A') * 64 ]) client._handle_data(stream, bytez) self.assertEqual(len(self.lines), 4) self.assertEqual(self.lines[0], six.b('HTTP/1.1 200 Ok\r\n')) self.assertEqual(self.lines[1], six.b('Content-Type: text/plain\r\n')) self.assertEqual(self.lines[2], six.b('Server: Neubot/0.0.1.0\r\n')) self.assertEqual(self.lines[3], six.b('\r\n')) context.left = 64 client._handle_data(stream, bytez) self.assertEqual(context.left, 0) self.assertEqual(len(self.pieces), 1) self.assertEqual(self.pieces[0], six.b('A') * 64)
def test_append_file(self): ''' Make sure append_file() works ''' client = http_clnt.HttpClient() context = http_clnt.ClientContext({}, None, None) stream = FakeStream(context) client.append_file(stream, '1234') # Whathever works self.assertEqual(context.outfp, '1234')
def test_protocol_version(self): ''' Make sure _handle_firstline() requires HTTP/1.{0,1} protocol ''' client = http_clnt.HttpClient() context = http_clnt.ClientContext({}, None, None) stream = FakeStream(context) self.assertRaises(RuntimeError, client._handle_firstline, stream, six.b('HTTP/1.2 200 Ok\r\n'))
def test_append_end_of_headers(self): ''' Make sure append_end_of_headers() works ''' client = http_clnt.HttpClient() context = http_clnt.ClientContext({}, None, None) stream = FakeStream(context) client.append_end_of_headers(stream) self.assertEqual(context.outq[0], http_clnt.CRLF) self.assertEqual(len(context.outq), 1)
def test_eoh_empty(self): ''' Make sure _handle_header_ex() recognizes EOH w/ empty string ''' client = http_clnt.HttpClient() context = http_clnt.ClientContext({}, None, None) stream = FakeStream(context) self.handle_done_cnt = 0 # Start over client._handle_header_ex(stream, six.b(''), self.handle_done) self.assertEqual(self.handle_done_cnt, 1)
def test_readline_too_long(self): ''' Make sure _handle_data() fails when reading too-long lines ''' client = http_clnt.HttpClient() context = http_clnt.ClientContext({}, None, None) stream = FakeStream(context) bytez = six.b('A') * http_clnt.MAXLINE # Note: failure because no LF at line[MAXLINE -1] self.assertRaises(RuntimeError, client._handle_data, stream, bytez)
def test_header_badfmt(self): ''' Make sure _handle_header_ex() errs out on bad header format ''' client = http_clnt.HttpClient() context = http_clnt.ClientContext({}, None, None) stream = FakeStream(context) self.assertRaises(RuntimeError, client._handle_header_ex, stream, six.b('Content-Type text/plain\r\n'), self.handle_done)
def test_append_bytes(self): ''' Make sure append_bytes() works ''' client = http_clnt.HttpClient() context = http_clnt.ClientContext({}, None, None) stream = FakeStream(context) client.append_bytes(stream, six.b('A') * 512) self.assertEqual(context.outq[0], six.b('A') * 512) self.assertEqual(len(context.outq), 1)
def test_no_data_open(self): ''' Make sure _handle_data() works for no data and open stream ''' client = http_clnt.HttpClient() context = http_clnt.ClientContext({}, None, None) stream = FakeStream(context) client._handle_data(stream, six.b('')) # Make sure the code schedules the next recv self.assertEqual(stream.count, http_clnt.MAXRECEIVE) self.assertEqual(stream.func, client._handle_data)
def test_fp_body(self): ''' Make sure send_message() works with filep body ''' client = http_clnt.HttpClient() context = http_clnt.ClientContext({}, None, None) stream = FakeStream(context) client.append_request(stream, 'GET', '/', 'HTTP/1.0') client.append_header(stream, 'Accept', 'text/plain') client.append_header(stream, 'User-Agent', 'Neubot/0.0.1.0') client.append_header(stream, 'Content-Length', str(3 * http_clnt.MAXREAD - 4)) client.append_header(stream, 'Content-Type', 'text/plain') client.append_end_of_headers(stream) stringio = six.BytesIO(six.b('A') * (3 * http_clnt.MAXREAD - 4)) client.append_file(stream, stringio) # Make sure send_complete is called just once and at the end self.send_complete_cnt = 0 client.handle_send_complete = self.handle_send_complete # First send() sends just the request headers and should not # invoke the send_complete() hook client.send_message(stream) self.assertEqual( stream.outs, six.b(''.join([ 'GET / HTTP/1.0\r\n', 'Accept: text/plain\r\n', 'User-Agent: Neubot/0.0.1.0\r\n', 'Content-Length: %d\r\n' % (3 * http_clnt.MAXREAD - 4), 'Content-Type: text/plain\r\n', '\r\n' ]))) self.assertEqual(self.send_complete_cnt, 0) # Second send() sends the first MAXREAD bytes and should not # invoke the send_complete() hook client._handle_send_complete(stream) self.assertEqual(stream.outs, six.b('A') * http_clnt.MAXREAD) self.assertEqual(self.send_complete_cnt, 0) # Third send() sends the second MAXREAD bytes and should not # invoke the send_complete() hook client._handle_send_complete(stream) self.assertEqual(stream.outs, six.b('A') * http_clnt.MAXREAD) self.assertEqual(self.send_complete_cnt, 0) # Fourth send() sends the third MAXREAD bytes and should not # invoke the send_complete() hook client._handle_send_complete(stream) self.assertEqual(stream.outs, six.b('A') * (http_clnt.MAXREAD - 4)) self.assertEqual(self.send_complete_cnt, 0) # Fifth send() should cleanup things, should invoke the # send_complete() hook, and clear the output file client._handle_send_complete(stream) self.assertEqual(context.outfp, None) self.assertEqual(self.send_complete_cnt, 1)
def test_append_chunk(self): ''' Make sure append_chunk() works ''' client = http_clnt.HttpClient() context = http_clnt.ClientContext({}, None, None) stream = FakeStream(context) client.append_chunk(stream, six.b('A') * 513) self.assertEqual(context.outq[0], six.b('201\r\n')) self.assertEqual(context.outq[1], six.b('A') * 513) self.assertEqual(context.outq[2], http_clnt.CRLF) self.assertEqual(len(context.outq), 3)
def test_no_data_closed(self): ''' Make sure _handle_data() works for no data and closed stream ''' client = http_clnt.HttpClient() context = http_clnt.ClientContext({}, None, None) stream = FakeStream(context) stream.isclosed = 1 # Pretend the stream is closed client._handle_data(stream, six.b('')) # Make sure we don't schedule the next recv self.assertEqual(stream.count, 0) self.assertEqual(stream.func, None)
def test_append_header(self): ''' Make sure append_header() works ''' client = http_clnt.HttpClient() context = http_clnt.ClientContext({}, None, None) stream = FakeStream(context) client.append_header(stream, 'Content-Type', 'text/plain') self.assertEqual(context.outq[0], six.b('Content-Type')) self.assertEqual(context.outq[1], http_clnt.COLON) self.assertEqual(context.outq[2], http_clnt.SPACE) self.assertEqual(context.outq[3], six.b('text/plain')) self.assertEqual(context.outq[4], http_clnt.CRLF) self.assertEqual(len(context.outq), 5)
def test_numtokens(self): ''' Make sure _handle_firstline() requires 3+ tokens ''' client = http_clnt.HttpClient() context = http_clnt.ClientContext({}, None, None) stream = FakeStream(context) self.assertRaises(RuntimeError, client._handle_firstline, stream, six.b('')) self.assertRaises(RuntimeError, client._handle_firstline, stream, six.b('\r\n')) self.assertRaises(RuntimeError, client._handle_firstline, stream, six.b('HTTP/1.0\r\n')) self.assertRaises(RuntimeError, client._handle_firstline, stream, six.b('HTTP/1.0 200\r\n'))
def test_success(self): ''' Make sure _handle_firstline() works as expected ''' client = http_clnt.HttpClient() context = http_clnt.ClientContext({}, None, None) stream = FakeStream(context) client._handle_firstline(stream, six.b('HTTP/1.1 404 Not Found\r\n')) self.assertEqual(context.protocol, six.b('HTTP/1.1')) self.assertEqual(context.code, six.b('404')) self.assertEqual(context.reason, six.b('Not Found')) # Make sure state is OK self.assertEqual(context.last_hdr, six.b('')) self.assertEqual(context.headers, {}) self.assertEqual(context.handle_line, client._handle_header)
def test_readpiece_small(self): ''' Make sure _handle_data() works for reading small pieces ''' client = http_clnt.HttpClient() context = http_clnt.ClientContext({}, None, None) stream = FakeStream(context) bytez = six.b('A') * 7 context.left = 7 self.pieces = [] # Start over context.handle_piece = self.handle_piece client._handle_data(stream, bytez) self.assertEqual(context.left, 0) self.assertEqual(len(self.pieces), 1) self.assertEqual(self.pieces[0], six.b('A') * 7)
def test_header_smpl(self): ''' Make sure _handle_header_ex() correctly parses simple headers ''' client = http_clnt.HttpClient() context = http_clnt.ClientContext({}, None, None) stream = FakeStream(context) self.handle_done_cnt = 0 # Start over client._handle_header_ex(stream, six.b('Content-Type: text/plain\r\n'), self.handle_done) self.assertEqual(self.handle_done_cnt, 0) self.assertTrue(six.b('content-type') in context.headers) self.assertFalse(six.b('Content-Type') in context.headers) self.assertEqual(context.headers[six.b('content-type')], six.b('text/plain'))
def test_append_request(self): ''' Make sure append_request() works ''' client = http_clnt.HttpClient() context = http_clnt.ClientContext({}, None, None) stream = FakeStream(context) client.append_request(stream, 'GET', '/', 'HTTP/1.0') self.assertEqual(context.method, six.b('GET')) self.assertEqual(context.outq[0], six.b('GET')) self.assertEqual(context.outq[1], http_clnt.SPACE) self.assertEqual(context.outq[2], six.b('/')) self.assertEqual(context.outq[3], http_clnt.SPACE) self.assertEqual(context.outq[4], six.b('HTTP/1.0')) self.assertEqual(context.outq[5], http_clnt.CRLF) self.assertEqual(len(context.outq), 6)
def test_readline_smpl(self): ''' Make sure _handle_data() works for reading simple lines ''' client = http_clnt.HttpClient() context = http_clnt.ClientContext({}, None, None) stream = FakeStream(context) bytez = six.b('GET / HTTP/1.0\r\nAccept: */*\r\n\r\n') self.lines = [] # Start over context.handle_line = self.handle_line client._handle_data(stream, bytez) # Make sure we have read the three lines self.assertEqual(len(self.lines), 3) self.assertEqual(self.lines[0], six.b('GET / HTTP/1.0\r\n')) self.assertEqual(self.lines[1], six.b('Accept: */*\r\n')) self.assertEqual(self.lines[2], six.b('\r\n'))
def test_folding_tab(self): ''' Make sure _handle_header_ex() folds line starting with tab ''' client = http_clnt.HttpClient() context = http_clnt.ClientContext({}, None, None) stream = FakeStream(context) self.handle_done_cnt = 0 # Start over client._handle_header_ex(stream, six.b('Content-Type: \r\n'), self.handle_done) client._handle_header_ex(stream, six.b('\ttext/plain\r\n'), self.handle_done) self.assertEqual(self.handle_done_cnt, 0) self.assertTrue(six.b('content-type') in context.headers) self.assertFalse(six.b('Content-Type') in context.headers) self.assertEqual(context.headers[six.b('content-type')], six.b('text/plain'))
def test_no_body(self): ''' Make sure send_message() works without body ''' client = http_clnt.HttpClient() context = http_clnt.ClientContext({}, None, None) stream = FakeStream(context) client.append_request(stream, 'GET', '/', 'HTTP/1.0') client.append_header(stream, 'Accept', 'text/plain') client.append_header(stream, 'User-Agent', 'Neubot/0.0.1.0') client.append_end_of_headers(stream) client.send_message(stream) self.assertEqual( stream.outs, six.b(''.join([ 'GET / HTTP/1.0\r\n', 'Accept: text/plain\r\n', 'User-Agent: Neubot/0.0.1.0\r\n', '\r\n' ])))
def test_nofolding_first_hdr(self): ''' Make sure _handle_header_ex() allows first header to start with a space or tab ''' # This is a "feature" of Neubot's HTTP client = http_clnt.HttpClient() context = http_clnt.ClientContext({}, None, None) stream = FakeStream(context) self.handle_done_cnt = 0 # Start over client._handle_header_ex(stream, six.b(' Content-Type: text/plain\r\n'), self.handle_done) self.assertEqual(self.handle_done_cnt, 0) self.assertTrue(six.b('content-type') in context.headers) self.assertFalse(six.b('Content-Type') in context.headers) self.assertEqual(context.headers[six.b('content-type')], six.b('text/plain'))
def test_blanks(self): ''' Make sure _handle_firstline() works as expected w/ extra blanks ''' client = http_clnt.HttpClient() context = http_clnt.ClientContext({}, None, None) stream = FakeStream(context) client._handle_firstline(stream, six.b(' HTTP/1.1 404 Not Found \r\n')) self.assertEqual(context.protocol, six.b('HTTP/1.1')) self.assertEqual(context.code, six.b('404')) self.assertEqual(context.reason, six.b('Not Found')) client._handle_firstline(stream, six.b('\tHTTP/1.1\t404\tNot Found\t\r\n')) self.assertEqual(context.protocol, six.b('HTTP/1.1')) self.assertEqual(context.code, six.b('404')) self.assertEqual(context.reason, six.b('Not Found'))
def test_folding_multi(self): ''' Make sure _handle_header_ex() folds multiple lines ''' client = http_clnt.HttpClient() context = http_clnt.ClientContext({}, None, None) stream = FakeStream(context) self.handle_done_cnt = 0 # Start over client._handle_header_ex(stream, six.b('Accept: \r\n'), self.handle_done) client._handle_header_ex(stream, six.b(' application/json,\r\n'), self.handle_done) client._handle_header_ex(stream, six.b(' text/plain\r\n'), self.handle_done) self.assertEqual(self.handle_done_cnt, 0) self.assertTrue(six.b('accept') in context.headers) self.assertFalse(six.b('Accept') in context.headers) self.assertEqual(context.headers[six.b('accept')], six.b('application/json, text/plain'))
def test_colon_in_folded_line(self): ''' Make sure _handle_header_ex() correctly handles colon in folded line ''' client = http_clnt.HttpClient() context = http_clnt.ClientContext({}, None, None) stream = FakeStream(context) self.handle_done_cnt = 0 # Start over client._handle_header_ex(stream, six.b('Accept: \r\n'), self.handle_done) client._handle_header_ex(stream, six.b(' application:json,\r\n'), self.handle_done) client._handle_header_ex(stream, six.b(' text:plain\r\n'), self.handle_done) self.assertEqual(self.handle_done_cnt, 0) self.assertTrue(six.b('accept') in context.headers) self.assertFalse(six.b('Accept') in context.headers) self.assertEqual(context.headers[six.b('accept')], six.b('application:json, text:plain'))
def test_header_multiple_spaces(self): ''' Make sure _handle_header_ex() correctly parses multiple headers with headrs with equal name and spaces ''' client = http_clnt.HttpClient() context = http_clnt.ClientContext({}, None, None) stream = FakeStream(context) self.handle_done_cnt = 0 # Start over client._handle_header_ex( stream, six.b('Content-Type : text/plain \r\n'), self.handle_done) client._handle_header_ex(stream, six.b('Content-Type\t\t: \ttext/plain\t\r\n'), self.handle_done) self.assertEqual(self.handle_done_cnt, 0) self.assertTrue(six.b('content-type') in context.headers) self.assertFalse(six.b('Content-Type') in context.headers) self.assertEqual(context.headers[six.b('content-type')], six.b('text/plain, text/plain'))
def test_readline_partial(self): ''' Make sure _handle_data() works for reading partial lines ''' client = http_clnt.HttpClient() context = http_clnt.ClientContext({}, None, None) stream = FakeStream(context) self.lines = [] # Start over context.handle_line = self.handle_line # Here we have a partial line so nothing will happen bytez = six.b('GET / HTTP/1') client._handle_data(stream, bytez) self.assertEqual(len(self.lines), 0) # Here we resume and split the three input lines bytez = six.b('.0\r\nAccept: */*\r\n\r\n') client._handle_data(stream, bytez) self.assertEqual(len(self.lines), 3) self.assertEqual(self.lines[0], six.b('GET / HTTP/1.0\r\n')) self.assertEqual(self.lines[1], six.b('Accept: */*\r\n')) self.assertEqual(self.lines[2], six.b('\r\n'))
def test_readpiece_large(self): ''' Make sure _handle_data() works for reading large pieces ''' client = http_clnt.HttpClient() context = http_clnt.ClientContext({}, None, None) stream = FakeStream(context) self.pieces = [] # Start over context.left = http_clnt.MAXRECEIVE + 8 context.handle_piece = self.handle_piece bytez = six.b('A') * (http_clnt.MAXRECEIVE + 4) client._handle_data(stream, bytez) self.assertEqual(context.left, 8) self.assertEqual(len(self.pieces), 1) self.assertEqual(self.pieces[0], six.b('A') * http_clnt.MAXRECEIVE) bytez = six.b('A') * 4 client._handle_data(stream, bytez) self.assertEqual(context.left, 0) self.assertEqual(len(self.pieces), 2) self.assertEqual(self.pieces[0], six.b('A') * http_clnt.MAXRECEIVE) self.assertEqual(self.pieces[1], six.b('A') * 8)