mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
py3: misc fixes for the server
20 out of 34 tests for the server now pass
This commit is contained in:
parent
2e8f88394b
commit
333dab88c2
@ -68,12 +68,18 @@ def as_bytestring(x):
|
||||
return x
|
||||
|
||||
|
||||
def as_unicodestring(x):
|
||||
if isinstance(x, bytes):
|
||||
x = x.decode('utf-8')
|
||||
return x
|
||||
|
||||
|
||||
def md5_hex(s):
|
||||
return md5(as_bytestring(s)).hexdigest().decode('ascii')
|
||||
return as_unicodestring(md5(as_bytestring(s)).hexdigest())
|
||||
|
||||
|
||||
def sha256_hex(s):
|
||||
return sha256(as_bytestring(s)).hexdigest().decode('ascii')
|
||||
return as_unicodestring(sha256(as_bytestring(s)).hexdigest())
|
||||
|
||||
|
||||
def base64_decode(s):
|
||||
|
@ -9,7 +9,7 @@ __copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
import os, errno
|
||||
from io import BytesIO
|
||||
from threading import Lock
|
||||
from polyglot.builtins import map
|
||||
from polyglot.builtins import map, unicode_type
|
||||
from functools import partial
|
||||
|
||||
from calibre import fit_image, sanitize_file_name
|
||||
@ -167,7 +167,7 @@ def book_filename(rd, book_id, mi, fmt, as_encoded_unicode=False):
|
||||
if as_encoded_unicode:
|
||||
# See https://tools.ietf.org/html/rfc6266
|
||||
fname = sanitize_file_name(fname).encode('utf-8')
|
||||
fname = quote(fname).decode('ascii')
|
||||
fname = unicode_type(quote(fname))
|
||||
else:
|
||||
fname = ascii_filename(fname).replace('"', '_')
|
||||
return fname
|
||||
|
@ -266,6 +266,7 @@ class HTTPRequest(Connection):
|
||||
|
||||
try:
|
||||
method, uri, req_protocol = line.strip().split(b' ', 2)
|
||||
req_protocol = req_protocol.decode('ascii')
|
||||
rp = int(req_protocol[5]), int(req_protocol[7])
|
||||
self.method = method.decode('ascii').upper()
|
||||
except Exception:
|
||||
|
@ -13,7 +13,7 @@ from itertools import chain, repeat
|
||||
from operator import itemgetter
|
||||
from functools import wraps
|
||||
|
||||
from polyglot.builtins import iteritems, itervalues, reraise, map, is_py3
|
||||
from polyglot.builtins import iteritems, itervalues, reraise, map, is_py3, unicode_type, string_or_bytes
|
||||
|
||||
from calibre import guess_type, force_unicode
|
||||
from calibre.constants import __version__, plugins, ispy3
|
||||
@ -247,8 +247,7 @@ class RequestData(object): # {{{
|
||||
|
||||
def filesystem_file_with_custom_etag(self, output, *etag_parts):
|
||||
etag = hashlib.sha1()
|
||||
string = type('')
|
||||
tuple(map(lambda x:etag.update(string(x)), etag_parts))
|
||||
tuple(map(lambda x:etag.update(unicode_type(x).encode('utf-8')), etag_parts))
|
||||
return ETaggedFile(output, etag.hexdigest())
|
||||
|
||||
def filesystem_file_with_constant_etag(self, output, etag_as_hexencoded_string):
|
||||
@ -312,7 +311,10 @@ class ReadableOutput(object):
|
||||
def filesystem_file_output(output, outheaders, stat_result):
|
||||
etag = getattr(output, 'etag', None)
|
||||
if etag is None:
|
||||
etag = hashlib.sha1(type('')(stat_result.st_mtime) + force_unicode(output.name or '')).hexdigest()
|
||||
oname = output.name or ''
|
||||
if not isinstance(oname, string_or_bytes):
|
||||
oname = unicode_type(oname)
|
||||
etag = hashlib.sha1((unicode_type(stat_result.st_mtime) + force_unicode(oname)).encode('utf-8')).hexdigest()
|
||||
else:
|
||||
output = output.output
|
||||
etag = '"%s"' % etag
|
||||
@ -356,7 +358,7 @@ class GeneratedOutput(object):
|
||||
class StaticOutput(object):
|
||||
|
||||
def __init__(self, data):
|
||||
if isinstance(data, type('')):
|
||||
if isinstance(data, unicode_type):
|
||||
data = data.encode('utf-8')
|
||||
self.data = data
|
||||
self.etag = '"%s"' % hashlib.sha1(data).hexdigest()
|
||||
@ -649,12 +651,17 @@ class HTTPConnection(HTTPRequest):
|
||||
if stat_result is not None:
|
||||
output = filesystem_file_output(output, outheaders, stat_result)
|
||||
if 'Content-Type' not in outheaders:
|
||||
mt = guess_type(output.name)[0]
|
||||
output_name = output.name
|
||||
if not isinstance(output_name, string_or_bytes):
|
||||
output_name = unicode_type(output_name)
|
||||
mt = guess_type(output_name)[0]
|
||||
if mt:
|
||||
if mt in {'text/plain', 'text/html', 'application/javascript', 'text/css'}:
|
||||
mt += '; charset=UTF-8'
|
||||
outheaders['Content-Type'] = mt
|
||||
elif isinstance(output, (bytes, type(''))):
|
||||
else:
|
||||
outheaders['Content-Type'] = 'application/octet-stream'
|
||||
elif isinstance(output, string_or_bytes):
|
||||
output = dynamic_output(output, outheaders)
|
||||
elif hasattr(output, 'read'):
|
||||
output = ReadableOutput(output)
|
||||
|
@ -168,7 +168,7 @@ class ContentTest(LibraryBaseTest):
|
||||
ae = self.assertEqual
|
||||
|
||||
def a(filename, data=None, status=OK, method='POST', username='12', add_duplicates='n', job_id=1):
|
||||
r, data = make_request(conn, '/cdb/add-book/{}/{}/{}'.format(job_id, add_duplicates, quote(filename.encode('utf-8')).decode('ascii')),
|
||||
r, data = make_request(conn, '/cdb/add-book/{}/{}/{}'.format(job_id, add_duplicates, quote(filename.encode('utf-8'))),
|
||||
username=username, password='test', prefix='', method=method, data=data)
|
||||
ae(status, r.status)
|
||||
return data
|
||||
|
@ -43,8 +43,8 @@ class ContentTest(LibraryBaseTest):
|
||||
missing('/%s/C:/out.html' % prefix, b'Naughty, naughty!')
|
||||
|
||||
def test_response(r):
|
||||
self.assertIn(b'max-age=', r.getheader('Cache-Control'))
|
||||
self.assertIn(b'public', r.getheader('Cache-Control'))
|
||||
self.assertIn('max-age=', r.getheader('Cache-Control'))
|
||||
self.assertIn('public', r.getheader('Cache-Control'))
|
||||
self.assertIsNotNone(r.getheader('Expires'))
|
||||
self.assertIsNotNone(r.getheader('ETag'))
|
||||
self.assertIsNotNone(r.getheader('Content-Type'))
|
||||
@ -182,7 +182,7 @@ class ContentTest(LibraryBaseTest):
|
||||
self.ae(r.status, http_client.OK)
|
||||
self.ae(data, db.cover(2))
|
||||
self.ae(r.getheader('Used-Cache'), 'no')
|
||||
path = from_hex_unicode(r.getheader('Tempfile')).decode('utf-8')
|
||||
path = from_hex_unicode(r.getheader('Tempfile'))
|
||||
f, fdata = share_open(path, 'rb'), data
|
||||
# Now force an update
|
||||
change_cover(1)
|
||||
@ -190,7 +190,7 @@ class ContentTest(LibraryBaseTest):
|
||||
self.ae(r.status, http_client.OK)
|
||||
self.ae(data, db.cover(2))
|
||||
self.ae(r.getheader('Used-Cache'), 'no')
|
||||
path = from_hex_unicode(r.getheader('Tempfile')).decode('utf-8')
|
||||
path = from_hex_unicode(r.getheader('Tempfile'))
|
||||
f2, f2data = share_open(path, 'rb'), data
|
||||
# Do it again
|
||||
change_cover(2)
|
||||
|
@ -175,7 +175,7 @@ class TestHTTP(BaseTest):
|
||||
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(), '')
|
||||
self.ae(r.read(), b'')
|
||||
conn.request('GET', '/choose')
|
||||
r = conn.getresponse()
|
||||
self.ae(r.status, http_client.NOT_FOUND)
|
||||
@ -200,7 +200,7 @@ class TestHTTP(BaseTest):
|
||||
r = conn.getresponse()
|
||||
self.ae(r.status, http_client.MOVED_PERMANENTLY)
|
||||
self.ae(r.getheader('Location'), '/somewhere-else')
|
||||
self.ae('', r.read())
|
||||
self.ae(b'', r.read())
|
||||
|
||||
server.change_handler(lambda data:data.path[0] + data.read().decode('ascii'))
|
||||
conn = server.connect(timeout=base_timeout * 5)
|
||||
@ -277,6 +277,9 @@ class TestHTTP(BaseTest):
|
||||
for i in range(10):
|
||||
conn._HTTPConnection__state = http_client._CS_IDLE
|
||||
conn.request('GET', '/%d'%i)
|
||||
if ispy3:
|
||||
responses.append(conn.response_class(conn.sock, method=conn._method))
|
||||
else:
|
||||
responses.append(conn.response_class(conn.sock, strict=conn.strict, method=conn._method))
|
||||
for i in range(10):
|
||||
r = responses[i]
|
||||
@ -288,7 +291,7 @@ class TestHTTP(BaseTest):
|
||||
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')
|
||||
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:
|
||||
@ -302,8 +305,8 @@ class TestHTTP(BaseTest):
|
||||
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))
|
||||
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_response(self): # {{{
|
||||
|
@ -284,13 +284,13 @@ def get_translator_for_lang(cache, bcp_47_code):
|
||||
|
||||
def encode_path(*components):
|
||||
'Encode the path specified as a list of path components using URL encoding'
|
||||
return '/' + '/'.join(urlquote(x.encode('utf-8'), '').decode('ascii') for x in components)
|
||||
return '/' + '/'.join(urlquote(x.encode('utf-8'), '') for x in components)
|
||||
|
||||
|
||||
class Cookie(SimpleCookie):
|
||||
|
||||
def _BaseCookie__set(self, key, real_value, coded_value):
|
||||
if not isinstance(key, bytes):
|
||||
if not ispy3 and not isinstance(key, bytes):
|
||||
key = key.encode('ascii') # Python 2.x cannot handle unicode keys
|
||||
return SimpleCookie._BaseCookie__set(self, key, real_value, coded_value)
|
||||
|
||||
|
@ -295,7 +295,7 @@ class WebSocketConnection(HTTPConnection):
|
||||
if self.method != 'GET':
|
||||
return self.simple_response(http_client.BAD_REQUEST, 'Invalid WebSocket method: %s' % self.method)
|
||||
|
||||
response = HANDSHAKE_STR % as_base64_unicode(sha1(key + GUID_STR).digest())
|
||||
response = HANDSHAKE_STR % as_base64_unicode(sha1((key + GUID_STR).encode('utf-8')).digest())
|
||||
self.optimize_for_sending_packet()
|
||||
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||
self.set_state(WRITE, self.upgrade_connection_to_ws, ReadOnlyFileBuffer(response.encode('ascii')), inheaders)
|
||||
|
@ -7,6 +7,7 @@ from __future__ import (unicode_literals, division, absolute_import,
|
||||
from struct import unpack, error
|
||||
import os
|
||||
from calibre.utils.speedups import ReadOnlyFileBuffer
|
||||
from calibre.constants import ispy3
|
||||
from polyglot.builtins import string_or_bytes
|
||||
|
||||
""" Recognize image file formats and sizes based on their first few bytes."""
|
||||
@ -125,16 +126,23 @@ def jpeg_dimensions(stream):
|
||||
raise ValueError('Truncated JPEG data')
|
||||
return ans
|
||||
|
||||
x = b''
|
||||
if ispy3:
|
||||
def read_byte():
|
||||
return read(1)[0]
|
||||
else:
|
||||
def read_byte():
|
||||
return ord(read(1)[0])
|
||||
|
||||
x = None
|
||||
while True:
|
||||
# Find next marker
|
||||
while x != b'\xff':
|
||||
x = read(1)
|
||||
while x != 0xff:
|
||||
x = read_byte()
|
||||
# Soak up padding
|
||||
marker = b'\xff'
|
||||
while marker == b'\xff':
|
||||
marker = read(1)
|
||||
q = ord(marker[0]) # [0] needed for memoryview
|
||||
marker = 0xff
|
||||
while marker == 0xff:
|
||||
marker = read_byte()
|
||||
q = marker
|
||||
if 0xc0 <= q <= 0xcf and q != 0xc4 and q != 0xcc:
|
||||
# SOFn marker
|
||||
stream.seek(3, os.SEEK_CUR)
|
||||
|
@ -100,6 +100,8 @@ def svg_path_to_painter_path(d):
|
||||
while data.tell() < end:
|
||||
last_cmd = cmd
|
||||
cmd = data.read(1) if repeated_command is None else repeated_command
|
||||
if isinstance(cmd, memoryview):
|
||||
cmd = cmd.tobytes()
|
||||
repeated_command = None
|
||||
|
||||
if cmd == b' ':
|
||||
@ -176,7 +178,7 @@ def svg_path_to_painter_path(d):
|
||||
x1, y1 = x, y
|
||||
x, y = parse_floats(2, x, y)
|
||||
path.quadTo(x1, y1, x, y)
|
||||
elif cmd[0] in b'-.' or b'0' <= cmd[0] <= b'9':
|
||||
elif cmd[0:1] in b'-.0123456789':
|
||||
# A new number begins
|
||||
# In this case, multiple parameters tuples are specified for the last command
|
||||
# We rewind to reparse data correctly
|
||||
|
Loading…
x
Reference in New Issue
Block a user