def test_http_response(self): # {{{ 'Test HTTP protocol responses' from calibre.srv.http_response import parse_multipart_byterange def handler(conn): return conn.generate_static_output('test', lambda : ''.join(conn.path)) with NamedTemporaryFile(suffix='test.epub') as f, open(P('localization/locales.zip'), 'rb') as lf, \ TestServer(handler, timeout=0.2, compress_min_size=0) as server: fdata = string.ascii_letters * 100 f.write(fdata), f.seek(0) # Test ETag conn = server.connect() conn.request('GET', '/an_etagged_path') r = conn.getresponse() self.ae(r.status, httplib.OK), self.ae(r.read(), b'an_etagged_path') etag = r.getheader('ETag') self.ae(etag, '"%s"' % hashlib.sha1('an_etagged_path').hexdigest()) conn.request('GET', '/an_etagged_path', headers={'If-None-Match':etag}) r = conn.getresponse() self.ae(r.status, httplib.NOT_MODIFIED) self.ae(r.read(), b'') # Test gzip conn.request('GET', '/an_etagged_path', headers={'Accept-Encoding':'gzip'}) r = conn.getresponse() self.ae(r.status, httplib.OK), self.ae(zlib.decompress(r.read(), 16+zlib.MAX_WBITS), b'an_etagged_path') # Test getting a filesystem file for use_sendfile in (True, False): server.change_handler(lambda conn: f) server.loop.opts.use_sendfile = use_sendfile conn = server.connect() conn.request('GET', '/test') r = conn.getresponse() etag = type('')(r.getheader('ETag')) self.assertTrue(etag) self.ae(r.getheader('Content-Type'), guess_type(f.name)[0]) self.ae(type('')(r.getheader('Accept-Ranges')), 'bytes') self.ae(int(r.getheader('Content-Length')), len(fdata)) self.ae(r.status, httplib.OK), self.ae(r.read(), fdata) conn.request('GET', '/test', headers={'Range':'bytes=2-25'}) r = conn.getresponse() self.ae(r.status, httplib.PARTIAL_CONTENT) self.ae(type('')(r.getheader('Accept-Ranges')), 'bytes') self.ae(type('')(r.getheader('Content-Range')), 'bytes 2-25/%d' % len(fdata)) self.ae(int(r.getheader('Content-Length')), 24) self.ae(r.read(), fdata[2:26]) conn.request('GET', '/test', headers={'Range':'bytes=100000-'}) r = conn.getresponse() self.ae(r.status, httplib.REQUESTED_RANGE_NOT_SATISFIABLE) self.ae(type('')(r.getheader('Content-Range')), 'bytes */%d' % len(fdata)) conn.request('GET', '/test', headers={'Range':'bytes=25-50', 'If-Range':etag}) r = conn.getresponse() self.ae(r.status, httplib.PARTIAL_CONTENT), self.ae(r.read(), fdata[25:51]) self.ae(int(r.getheader('Content-Length')), 26) conn.request('GET', '/test', headers={'Range':'bytes=0-1000000'}) r = conn.getresponse() self.ae(r.status, httplib.PARTIAL_CONTENT), self.ae(r.read(), fdata) conn.request('GET', '/test', headers={'Range':'bytes=25-50', 'If-Range':'"nomatch"'}) r = conn.getresponse() self.ae(r.status, httplib.OK), self.ae(r.read(), fdata) self.assertFalse(r.getheader('Content-Range')) self.ae(int(r.getheader('Content-Length')), len(fdata)) conn.request('GET', '/test', headers={'Range':'bytes=0-25,26-50'}) r = conn.getresponse() self.ae(r.status, httplib.PARTIAL_CONTENT) clen = int(r.getheader('Content-Length')) data = r.read() self.ae(clen, len(data)) buf = BytesIO(data) self.ae(parse_multipart_byterange(buf, r.getheader('Content-Type')), [(0, fdata[:26]), (26, fdata[26:51])]) # Test sending of larger file start_time = monotonic() lf.seek(0) data = lf.read() server.change_handler(lambda conn: lf) conn = server.connect() conn.request('GET', '/test') r = conn.getresponse() self.ae(r.status, httplib.OK) rdata = r.read() self.ae(len(data), len(rdata)) self.ae(hashlib.sha1(data).hexdigest(), hashlib.sha1(rdata).hexdigest()) self.ae(data, rdata) time_taken = monotonic() - start_time self.assertLess(time_taken, 1, 'Large file transfer took too long')
def test_http_response(self): # {{{ 'Test HTTP protocol responses' from calibre.srv.http_response import parse_multipart_byterange def handler(conn): return conn.generate_static_output('test', lambda: ''.join(conn.path)) with NamedTemporaryFile(suffix='test.epub') as f, open(P('localization/locales.zip'), 'rb') as lf, \ TestServer(handler, timeout=1, compress_min_size=0) as server: fdata = (string.ascii_letters * 100).encode('ascii') f.write(fdata), f.seek(0) # Test ETag conn = server.connect() conn.request('GET', '/an_etagged_path') r = conn.getresponse() self.ae(r.status, http_client.OK), self.ae(r.read(), b'an_etagged_path') etag = r.getheader('ETag') self.ae(etag, '"%s"' % hashlib.sha1(b'an_etagged_path').hexdigest()) conn.request('GET', '/an_etagged_path', headers={'If-None-Match': etag}) r = conn.getresponse() self.ae(r.status, http_client.NOT_MODIFIED) self.ae(r.read(), b'') # Test gzip raw = b'a' * 20000 server.change_handler(lambda conn: raw) conn = server.connect() conn.request('GET', '/an_etagged_path', headers={'Accept-Encoding': 'gzip'}) r = conn.getresponse() self.ae(unicode_type(len(raw)), r.getheader('Calibre-Uncompressed-Length')) self.ae(r.status, http_client.OK), self.ae( zlib.decompress(r.read(), 16 + zlib.MAX_WBITS), raw) # Test dynamic etagged content num_calls = [0] def edfunc(): num_calls[0] += 1 return b'data' server.change_handler( lambda conn: conn.etagged_dynamic_response("xxx", edfunc)) conn = server.connect() conn.request('GET', '/an_etagged_path') r = conn.getresponse() self.ae(r.status, http_client.OK), self.ae(r.read(), b'data') etag = r.getheader('ETag') self.ae(etag, '"xxx"') self.ae(r.getheader('Content-Length'), '4') conn.request('GET', '/an_etagged_path', headers={'If-None-Match': etag}) r = conn.getresponse() self.ae(r.status, http_client.NOT_MODIFIED) self.ae(r.read(), b'') self.ae(num_calls[0], 1) # Test getting a filesystem file for use_sendfile in (True, False): server.change_handler(lambda conn: f) server.loop.opts.use_sendfile = use_sendfile conn = server.connect() conn.request('GET', '/test') r = conn.getresponse() etag = unicode_type(r.getheader('ETag')) self.assertTrue(etag) self.ae(r.getheader('Content-Type'), guess_type(f.name)[0]) self.ae(unicode_type(r.getheader('Accept-Ranges')), 'bytes') self.ae(int(r.getheader('Content-Length')), len(fdata)) self.ae(r.status, http_client.OK), self.ae(r.read(), fdata) conn.request('GET', '/test', headers={'Range': 'bytes=2-25'}) r = conn.getresponse() self.ae(r.status, http_client.PARTIAL_CONTENT) self.ae(unicode_type(r.getheader('Accept-Ranges')), 'bytes') self.ae(unicode_type(r.getheader('Content-Range')), 'bytes 2-25/%d' % len(fdata)) self.ae(int(r.getheader('Content-Length')), 24) self.ae(r.read(), fdata[2:26]) conn.request('GET', '/test', headers={'Range': 'bytes=100000-'}) r = conn.getresponse() self.ae(r.status, http_client.REQUESTED_RANGE_NOT_SATISFIABLE) self.ae(unicode_type(r.getheader('Content-Range')), 'bytes */%d' % len(fdata)) conn.request('GET', '/test', headers={ 'Range': 'bytes=25-50', 'If-Range': etag }) r = conn.getresponse() self.ae(r.status, http_client.PARTIAL_CONTENT), self.ae( r.read(), fdata[25:51]) self.ae(int(r.getheader('Content-Length')), 26) conn.request('GET', '/test', headers={'Range': 'bytes=0-1000000'}) r = conn.getresponse() self.ae(r.status, http_client.PARTIAL_CONTENT), self.ae(r.read(), fdata) conn.request('GET', '/test', headers={ 'Range': 'bytes=25-50', 'If-Range': '"nomatch"' }) r = conn.getresponse() self.ae(r.status, http_client.OK), self.ae(r.read(), fdata) self.assertFalse(r.getheader('Content-Range')) self.ae(int(r.getheader('Content-Length')), len(fdata)) conn.request('GET', '/test', headers={'Range': 'bytes=0-25,26-50'}) r = conn.getresponse() self.ae(r.status, http_client.PARTIAL_CONTENT) clen = int(r.getheader('Content-Length')) data = r.read() self.ae(clen, len(data)) buf = BytesIO(data) self.ae( parse_multipart_byterange(buf, r.getheader('Content-Type')), [(0, fdata[:26]), (26, fdata[26:51])]) # Test sending of larger file start_time = monotonic() lf.seek(0) data = lf.read() server.change_handler(lambda conn: lf) conn = server.connect(timeout=1) conn.request('GET', '/test') r = conn.getresponse() self.ae(r.status, http_client.OK) rdata = r.read() self.ae(len(data), len(rdata)) self.ae( hashlib.sha1(data).hexdigest(), hashlib.sha1(rdata).hexdigest()) self.ae(data, rdata) time_taken = monotonic() - start_time self.assertLess(time_taken, 1, 'Large file transfer took too long')