def copy_range(src_file, start, size, dest): total_sent = 0 src_file.seek(start) while size > 0: data = eintr_retry_call(src_file.read, min(size, DEFAULT_BUFFER_SIZE)) if len(data) == 0: break # EOF dest.write(data) size -= len(data) total_sent += len(data) del data return total_sent
def do_http_basic(self): from calibre.srv.errors import HTTPNotFound, HTTPRedirect body = 'Requested resource not found' def handler(data): raise HTTPNotFound(body) def raw_send(conn, raw): conn.send(raw) conn._HTTPConnection__state = http_client._CS_REQ_SENT return conn.getresponse() base_timeout = 0.5 if is_ci else 0.1 with TestServer(handler, timeout=base_timeout, max_header_line_size=100. / 1024, max_request_body_size=100. / (1024 * 1024)) as server: conn = server.connect() r = raw_send(conn, b'hello\n') self.ae(r.status, http_client.BAD_REQUEST) self.ae(r.read(), b'HTTP requires CRLF line terminators') r = raw_send(conn, b'\r\nGET /index.html HTTP/1.1\r\n\r\n') self.ae(r.status, http_client.NOT_FOUND), self.ae( r.read(), b'Requested resource not found') r = raw_send(conn, b'\r\n\r\nGET /index.html HTTP/1.1\r\n\r\n') self.ae(r.status, http_client.BAD_REQUEST) self.ae(r.read(), b'Multiple leading empty lines not allowed') r = raw_send(conn, b'hello world\r\n') self.ae(r.status, http_client.BAD_REQUEST) self.ae(r.read(), b'Malformed Request-Line') r = raw_send(conn, b'x' * 200) self.ae(r.status, http_client.BAD_REQUEST) self.ae(r.read(), b'') r = raw_send(conn, b'XXX /index.html HTTP/1.1\r\n\r\n') self.ae(r.status, http_client.BAD_REQUEST), self.ae(r.read(), b'Unknown HTTP method') # Test 404 conn.request('HEAD', '/moose') r = conn.getresponse() self.ae(r.status, http_client.NOT_FOUND) self.assertIsNotNone(r.getheader('Date', None)) self.ae(r.getheader('Content-Length'), unicode_type(len(body))) self.ae(r.getheader('Content-Type'), 'text/plain; charset=UTF-8') self.ae(len(r.getheaders()), 3) self.ae(r.read(), b'') conn.request('GET', '/choose') r = conn.getresponse() self.ae(r.status, http_client.NOT_FOUND) self.ae(r.read(), b'Requested resource not found') # Test 500 server.change_handler(lambda data: 1 / 0) conn = server.connect() conn.request('GET', '/test/') r = conn.getresponse() self.ae(r.status, http_client.INTERNAL_SERVER_ERROR) # Test 301 def handler(data): raise HTTPRedirect('/somewhere-else') server.change_handler(handler) conn = server.connect() conn.request('GET', '/') r = conn.getresponse() self.ae(r.status, http_client.MOVED_PERMANENTLY) self.ae(r.getheader('Location'), '/somewhere-else') self.ae(b'', r.read()) server.change_handler( lambda data: data.path[0] + data.read().decode('ascii')) conn = server.connect(timeout=base_timeout * 5) # Test simple GET conn.request('GET', '/test/') r = conn.getresponse() self.ae(r.status, http_client.OK) self.ae(r.read(), b'test') # Test TRACE lines = [ 'TRACE /xxx HTTP/1.1', 'Test: value', 'Xyz: abc, def', '', '' ] r = raw_send(conn, ('\r\n'.join(lines)).encode('ascii')) self.ae(r.status, http_client.OK) self.ae(r.read().decode('utf-8'), '\n'.join(lines[:-2])) # Test POST with simple body conn.request('POST', '/test', 'body') r = conn.getresponse() self.ae(r.status, http_client.OK) self.ae(r.read(), b'testbody') # Test POST with chunked transfer encoding conn.request('POST', '/test', headers={'Transfer-Encoding': 'chunked'}) conn.send(b'4\r\nbody\r\na\r\n1234567890\r\n0\r\n\r\n') r = conn.getresponse() self.ae(r.status, http_client.OK) self.ae(r.read(), b'testbody1234567890') conn.request('GET', '/test' + ('a' * 200)) r = conn.getresponse() self.ae(r.status, http_client.BAD_REQUEST) conn = server.connect() conn.request('GET', '/test', ('a' * 200)) r = conn.getresponse() self.ae(r.status, http_client.REQUEST_ENTITY_TOO_LARGE) conn = server.connect() conn.request('POST', '/test', headers={'Transfer-Encoding': 'chunked'}) conn.send(b'x\r\nbody\r\n0\r\n\r\n') r = conn.getresponse() self.ae(r.status, http_client.BAD_REQUEST) self.assertIn(b'not a valid chunk size', r.read()) conn.request('POST', '/test', headers={'Transfer-Encoding': 'chunked'}) conn.send(b'4\r\nbody\r\n200\r\n\r\n') r = conn.getresponse() self.ae(r.status, http_client.REQUEST_ENTITY_TOO_LARGE) conn.request('POST', '/test', body='a' * 200) r = conn.getresponse() self.ae(r.status, http_client.REQUEST_ENTITY_TOO_LARGE) conn = server.connect() conn.request('POST', '/test', headers={'Transfer-Encoding': 'chunked'}) conn.send(b'3\r\nbody\r\n0\r\n\r\n') r = conn.getresponse() self.ae(r.status, http_client.BAD_REQUEST), self.ae( r.read(), b'Chunk does not have trailing CRLF') conn = server.connect(timeout=base_timeout * 5) conn.request('POST', '/test', headers={'Transfer-Encoding': 'chunked'}) conn.send(b'30\r\nbody\r\n0\r\n\r\n') r = conn.getresponse() self.ae(r.status, http_client.REQUEST_TIMEOUT) self.assertIn(b'', r.read()) conn = server.connect() # Test closing server.loop.opts.timeout = 10 # ensure socket is not closed because of timeout conn.request('GET', '/close', headers={'Connection': 'close'}) r = conn.getresponse() self.ae(r.status, 200), self.ae(r.read(), b'close') server.loop.wakeup() num = 10 while num and server.loop.num_active_connections != 0: time.sleep(0.01) num -= 1 self.ae(server.loop.num_active_connections, 0) self.assertIsNone(conn.sock) # Test timeout server.loop.opts.timeout = 0.1 conn = server.connect(timeout=1) conn.request('GET', '/something') r = conn.getresponse() self.ae(r.status, 200), self.ae(r.read(), b'something') self.assertIn(b'Request Timeout', eintr_retry_call(conn.sock.recv, 500))
def test_http_basic(self): # {{{ 'Test basic HTTP protocol conformance' from calibre.srv.errors import HTTPNotFound, HTTPRedirect body = 'Requested resource not found' def handler(data): raise HTTPNotFound(body) def raw_send(conn, raw): conn.send(raw) conn._HTTPConnection__state = httplib._CS_REQ_SENT return conn.getresponse() base_timeout = 0.5 if is_ci else 0.1 with TestServer(handler, timeout=base_timeout, max_header_line_size=100./1024, max_request_body_size=100./(1024*1024)) as server: conn = server.connect() r = raw_send(conn, b'hello\n') self.ae(r.status, httplib.BAD_REQUEST) self.ae(r.read(), b'HTTP requires CRLF line terminators') r = raw_send(conn, b'\r\nGET /index.html HTTP/1.1\r\n\r\n') self.ae(r.status, httplib.NOT_FOUND), self.ae(r.read(), b'Requested resource not found') r = raw_send(conn, b'\r\n\r\nGET /index.html HTTP/1.1\r\n\r\n') self.ae(r.status, httplib.BAD_REQUEST) self.ae(r.read(), b'Multiple leading empty lines not allowed') r = raw_send(conn, b'hello world\r\n') self.ae(r.status, httplib.BAD_REQUEST) self.ae(r.read(), b'Malformed Request-Line') r = raw_send(conn, b'x' * 200) self.ae(r.status, httplib.BAD_REQUEST) self.ae(r.read(), b'') r = raw_send(conn, b'XXX /index.html HTTP/1.1\r\n\r\n') self.ae(r.status, httplib.BAD_REQUEST), self.ae(r.read(), b'Unknown HTTP method') # Test 404 conn.request('HEAD', '/moose') r = conn.getresponse() self.ae(r.status, httplib.NOT_FOUND) self.assertIsNotNone(r.getheader('Date', None)) self.ae(r.getheader('Content-Length'), str(len(body))) self.ae(r.getheader('Content-Type'), 'text/plain; charset=UTF-8') self.ae(len(r.getheaders()), 3) self.ae(r.read(), '') conn.request('GET', '/choose') r = conn.getresponse() self.ae(r.status, httplib.NOT_FOUND) self.ae(r.read(), b'Requested resource not found') # Test 500 orig = server.loop.log.filter_level server.loop.log.filter_level = server.loop.log.ERROR + 10 server.change_handler(lambda data:1/0) conn = server.connect() conn.request('GET', '/test/') r = conn.getresponse() self.ae(r.status, httplib.INTERNAL_SERVER_ERROR) server.loop.log.filter_level = orig # Test 301 def handler(data): raise HTTPRedirect('/somewhere-else') server.change_handler(handler) conn = server.connect() conn.request('GET', '/') r = conn.getresponse() self.ae(r.status, httplib.MOVED_PERMANENTLY) self.ae(r.getheader('Location'), '/somewhere-else') self.ae('', r.read()) server.change_handler(lambda data:data.path[0] + data.read().decode('ascii')) conn = server.connect(timeout=base_timeout * 5) # Test simple GET conn.request('GET', '/test/') r = conn.getresponse() self.ae(r.status, httplib.OK) self.ae(r.read(), b'test') # Test TRACE lines = ['TRACE /xxx HTTP/1.1', 'Test: value', 'Xyz: abc, def', '', ''] r = raw_send(conn, ('\r\n'.join(lines)).encode('ascii')) self.ae(r.status, httplib.OK) self.ae(r.read().decode('utf-8'), '\n'.join(lines[:-2])) # Test POST with simple body conn.request('POST', '/test', 'body') r = conn.getresponse() self.ae(r.status, httplib.OK) self.ae(r.read(), b'testbody') # Test POST with chunked transfer encoding conn.request('POST', '/test', headers={'Transfer-Encoding': 'chunked'}) conn.send(b'4\r\nbody\r\na\r\n1234567890\r\n0\r\n\r\n') r = conn.getresponse() self.ae(r.status, httplib.OK) self.ae(r.read(), b'testbody1234567890') # Test various incorrect input orig_level, server.log.filter_level = server.log.filter_level, server.log.ERROR conn.request('GET', '/test' + ('a' * 200)) r = conn.getresponse() self.ae(r.status, httplib.BAD_REQUEST) conn = server.connect() conn.request('GET', '/test', ('a' * 200)) r = conn.getresponse() self.ae(r.status, httplib.REQUEST_ENTITY_TOO_LARGE) conn = server.connect() conn.request('POST', '/test', headers={'Transfer-Encoding': 'chunked'}) conn.send(b'x\r\nbody\r\n0\r\n\r\n') r = conn.getresponse() self.ae(r.status, httplib.BAD_REQUEST) self.assertIn(b'not a valid chunk size', r.read()) conn.request('POST', '/test', headers={'Transfer-Encoding': 'chunked'}) conn.send(b'4\r\nbody\r\n200\r\n\r\n') r = conn.getresponse() self.ae(r.status, httplib.REQUEST_ENTITY_TOO_LARGE) conn.request('POST', '/test', body='a'*200) r = conn.getresponse() self.ae(r.status, httplib.REQUEST_ENTITY_TOO_LARGE) conn = server.connect() conn.request('POST', '/test', headers={'Transfer-Encoding': 'chunked'}) conn.send(b'3\r\nbody\r\n0\r\n\r\n') r = conn.getresponse() self.ae(r.status, httplib.BAD_REQUEST), self.ae(r.read(), b'Chunk does not have trailing CRLF') conn = server.connect(timeout=base_timeout * 5) conn.request('POST', '/test', headers={'Transfer-Encoding': 'chunked'}) conn.send(b'30\r\nbody\r\n0\r\n\r\n') r = conn.getresponse() self.ae(r.status, httplib.REQUEST_TIMEOUT) self.assertIn(b'', r.read()) server.log.filter_level = orig_level conn = server.connect() # Test pipelining responses = [] for i in xrange(10): conn._HTTPConnection__state = httplib._CS_IDLE conn.request('GET', '/%d'%i) responses.append(conn.response_class(conn.sock, strict=conn.strict, method=conn._method)) for i in xrange(10): r = responses[i] r.begin() self.ae(r.read(), ('%d' % i).encode('ascii')) conn._HTTPConnection__state = httplib._CS_IDLE # Test closing server.loop.opts.timeout = 10 # ensure socket is not closed because of timeout conn.request('GET', '/close', headers={'Connection':'close'}) r = conn.getresponse() self.ae(r.status, 200), self.ae(r.read(), 'close') server.loop.wakeup() num = 10 while num and server.loop.num_active_connections != 0: time.sleep(0.01) num -= 1 self.ae(server.loop.num_active_connections, 0) self.assertIsNone(conn.sock) # Test timeout server.loop.opts.timeout = 0.1 conn = server.connect(timeout=1) conn.request('GET', '/something') r = conn.getresponse() self.ae(r.status, 200), self.ae(r.read(), 'something') self.assertIn('Request Timeout', eintr_retry_call(conn.sock.recv, 500))