mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Add support for HTTP redirects
This commit is contained in:
parent
ab81fe992b
commit
f527f41389
@ -6,9 +6,25 @@ from __future__ import (unicode_literals, division, absolute_import,
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
|
import httplib
|
||||||
class HTTP404(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class JobQueueFull(Exception):
|
class JobQueueFull(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class HTTPSimpleResponse(Exception):
|
||||||
|
|
||||||
|
def __init__(self, http_code, http_message='', close_connection=False, location=None):
|
||||||
|
Exception.__init__(self, http_message)
|
||||||
|
self.http_code = http_code
|
||||||
|
self.close_connection = close_connection
|
||||||
|
self.location = location
|
||||||
|
|
||||||
|
class HTTPRedirect(HTTPSimpleResponse):
|
||||||
|
|
||||||
|
def __init__(self, location, http_code=httplib.MOVED_PERMANENTLY, http_message='', close_connection=False):
|
||||||
|
HTTPSimpleResponse.__init__(self, http_code, http_message, close_connection, location)
|
||||||
|
|
||||||
|
class HTTPNotFound(HTTPSimpleResponse):
|
||||||
|
|
||||||
|
def __init__(self, http_message='', close_connection=False):
|
||||||
|
HTTPSimpleResponse.__init__(self, httplib.NOT_FOUND, http_message, close_connection)
|
||||||
|
@ -16,7 +16,7 @@ from functools import wraps
|
|||||||
from calibre import guess_type, force_unicode
|
from calibre import guess_type, force_unicode
|
||||||
from calibre.constants import __version__
|
from calibre.constants import __version__
|
||||||
from calibre.srv.loop import WRITE
|
from calibre.srv.loop import WRITE
|
||||||
from calibre.srv.errors import HTTP404
|
from calibre.srv.errors import HTTPSimpleResponse
|
||||||
from calibre.srv.http_request import HTTPRequest, read_headers
|
from calibre.srv.http_request import HTTPRequest, read_headers
|
||||||
from calibre.srv.sendfile import file_metadata, sendfile_to_socket_async, CannotSendfile, SendfileInterrupted
|
from calibre.srv.sendfile import file_metadata, sendfile_to_socket_async, CannotSendfile, SendfileInterrupted
|
||||||
from calibre.srv.utils import MultiDict, http_date, HTTP1, HTTP11, socket_errors_socket_closed
|
from calibre.srv.utils import MultiDict, http_date, HTTP1, HTTP11, socket_errors_socket_closed
|
||||||
@ -284,10 +284,15 @@ class HTTPConnection(HTTPRequest):
|
|||||||
buf.seek(pos + sent)
|
buf.seek(pos + sent)
|
||||||
return buf.tell() == end
|
return buf.tell() == end
|
||||||
|
|
||||||
def simple_response(self, status_code, msg='', close_after_response=True):
|
def simple_response(self, status_code, msg='', close_after_response=True, extra_headers=None):
|
||||||
if self.response_protocol is HTTP1 and status_code in (httplib.REQUEST_ENTITY_TOO_LARGE, httplib.REQUEST_URI_TOO_LONG):
|
if self.response_protocol is HTTP1:
|
||||||
# HTTP/1.0 has no 413/414 codes
|
# HTTP/1.0 has no 413/414/303 codes
|
||||||
status_code = httplib.BAD_REQUEST
|
status_code = {
|
||||||
|
httplib.REQUEST_ENTITY_TOO_LARGE:httplib.BAD_REQUEST,
|
||||||
|
httplib.REQUEST_URI_TOO_LONG:httplib.BAD_REQUEST,
|
||||||
|
httplib.SEE_OTHER:httplib.FOUND
|
||||||
|
}.get(status_code, status_code)
|
||||||
|
|
||||||
self.close_after_response = close_after_response
|
self.close_after_response = close_after_response
|
||||||
msg = msg.encode('utf-8')
|
msg = msg.encode('utf-8')
|
||||||
ct = 'http' if self.method == 'TRACE' else 'plain'
|
ct = 'http' if self.method == 'TRACE' else 'plain'
|
||||||
@ -299,6 +304,9 @@ class HTTPConnection(HTTPRequest):
|
|||||||
]
|
]
|
||||||
if self.close_after_response and self.response_protocol is HTTP11:
|
if self.close_after_response and self.response_protocol is HTTP11:
|
||||||
buf.append("Connection: close")
|
buf.append("Connection: close")
|
||||||
|
if extra_headers is not None:
|
||||||
|
for h, v in extra_headers.iteritems():
|
||||||
|
buf.append('%s: %s' % (h, v))
|
||||||
buf.append('')
|
buf.append('')
|
||||||
buf = [(x + '\r\n').encode('ascii') for x in buf]
|
buf = [(x + '\r\n').encode('ascii') for x in buf]
|
||||||
if self.method != 'HEAD':
|
if self.method != 'HEAD':
|
||||||
@ -346,8 +354,11 @@ class HTTPConnection(HTTPRequest):
|
|||||||
def job_done(self, ok, result):
|
def job_done(self, ok, result):
|
||||||
if not ok:
|
if not ok:
|
||||||
etype, e, tb = result
|
etype, e, tb = result
|
||||||
if isinstance(e, HTTP404):
|
if isinstance(e, HTTPSimpleResponse):
|
||||||
return self.simple_response(httplib.NOT_FOUND, msg=e.message or '', close_after_response=False)
|
eh = {}
|
||||||
|
if e.location:
|
||||||
|
eh['Location'] = e.location
|
||||||
|
return self.simple_response(e.http_code, msg=e.message or '', close_after_response=e.close_connection, extra_headers=eh)
|
||||||
raise e, None, tb
|
raise e, None, tb
|
||||||
|
|
||||||
data, output = result
|
data, output = result
|
||||||
|
@ -89,10 +89,10 @@ class TestHTTP(BaseTest):
|
|||||||
|
|
||||||
def test_http_basic(self): # {{{
|
def test_http_basic(self): # {{{
|
||||||
'Test basic HTTP protocol conformance'
|
'Test basic HTTP protocol conformance'
|
||||||
from calibre.srv.errors import HTTP404
|
from calibre.srv.errors import HTTPNotFound, HTTPRedirect
|
||||||
body = 'Requested resource not found'
|
body = 'Requested resource not found'
|
||||||
def handler(data):
|
def handler(data):
|
||||||
raise HTTP404(body)
|
raise HTTPNotFound(body)
|
||||||
def raw_send(conn, raw):
|
def raw_send(conn, raw):
|
||||||
conn.send(raw)
|
conn.send(raw)
|
||||||
conn._HTTPConnection__state = httplib._CS_REQ_SENT
|
conn._HTTPConnection__state = httplib._CS_REQ_SENT
|
||||||
@ -146,6 +146,17 @@ class TestHTTP(BaseTest):
|
|||||||
self.ae(r.status, httplib.INTERNAL_SERVER_ERROR)
|
self.ae(r.status, httplib.INTERNAL_SERVER_ERROR)
|
||||||
server.loop.log.filter_level = orig
|
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'))
|
server.change_handler(lambda data:data.path[0] + data.read().decode('ascii'))
|
||||||
conn = server.connect()
|
conn = server.connect()
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user