mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Implement BonJour for the new server
This commit is contained in:
parent
24a392e9de
commit
dd1c304626
@ -65,6 +65,11 @@ raw_options = (
|
|||||||
'url_prefix', None,
|
'url_prefix', None,
|
||||||
'Useful if you wish to run this server behind a reverse proxy.',
|
'Useful if you wish to run this server behind a reverse proxy.',
|
||||||
|
|
||||||
|
'Advertise OPDS feeds via BonJour',
|
||||||
|
'use_bonjour', True,
|
||||||
|
'Advertise the OPDS feeds via the BonJour service, so that OPDS based'
|
||||||
|
' reading apps can detect and connect to the server automatically.',
|
||||||
|
|
||||||
'The interface on which to listen for connections',
|
'The interface on which to listen for connections',
|
||||||
'listen_on', '0.0.0.0',
|
'listen_on', '0.0.0.0',
|
||||||
'The default is to listen on all available interfaces. You can change this to, for'
|
'The default is to listen on all available interfaces. You can change this to, for'
|
||||||
|
51
src/calibre/srv/plugins.py
Normal file
51
src/calibre/srv/plugins.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#!/usr/bin/env python2
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import,
|
||||||
|
print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
|
from threading import Event
|
||||||
|
|
||||||
|
class BonJour(object): # {{{
|
||||||
|
|
||||||
|
def __init__(self, name='Books in calibre', service_type='_calibre._tcp', path='/opds', add_hostname=True):
|
||||||
|
self.service_name = name
|
||||||
|
self.service_type = service_type
|
||||||
|
self.add_hostname = add_hostname
|
||||||
|
self.path = path
|
||||||
|
self.shutdown = Event()
|
||||||
|
self.stop = self.shutdown.set
|
||||||
|
self.started = Event()
|
||||||
|
self.stopped = Event()
|
||||||
|
self.services = []
|
||||||
|
|
||||||
|
def start(self, loop):
|
||||||
|
from calibre.utils.mdns import publish, unpublish, get_external_ip, verify_ipV4_address
|
||||||
|
ip_address, port = loop.bound_address[:2]
|
||||||
|
self.zeroconf_ip_address = zipa = verify_ipV4_address(ip_address) or get_external_ip()
|
||||||
|
prefix = loop.opts.url_prefix or ''
|
||||||
|
# The Zeroconf module requires everything to be bytestrings
|
||||||
|
def enc(x):
|
||||||
|
if not isinstance(x, bytes):
|
||||||
|
x = x.encode('ascii')
|
||||||
|
return x
|
||||||
|
mdns_services = (
|
||||||
|
(enc(self.service_name), enc(self.service_type), port, {b'path':enc(prefix + self.path)}),
|
||||||
|
)
|
||||||
|
if self.shutdown.is_set():
|
||||||
|
return
|
||||||
|
self.services = []
|
||||||
|
|
||||||
|
for s in mdns_services:
|
||||||
|
self.services.append(publish(*s, use_ip_address=zipa, add_hostname=self.add_hostname))
|
||||||
|
loop.log('OPDS feeds advertised via BonJour at: %s port: %s' % (zipa, port))
|
||||||
|
self.advertised_port = port
|
||||||
|
self.started.set()
|
||||||
|
|
||||||
|
self.shutdown.wait()
|
||||||
|
for s in mdns_services:
|
||||||
|
unpublish(*s, add_hostname=self.add_hostname)
|
||||||
|
self.stopped.set()
|
||||||
|
# }}}
|
@ -11,6 +11,7 @@ import sys, os
|
|||||||
from calibre import as_unicode
|
from calibre import as_unicode
|
||||||
from calibre.constants import plugins, iswindows
|
from calibre.constants import plugins, iswindows
|
||||||
from calibre.srv.loop import ServerLoop
|
from calibre.srv.loop import ServerLoop
|
||||||
|
from calibre.srv.plugins import BonJour
|
||||||
from calibre.srv.opts import opts_to_parser
|
from calibre.srv.opts import opts_to_parser
|
||||||
from calibre.srv.http_response import create_http_handler
|
from calibre.srv.http_response import create_http_handler
|
||||||
from calibre.srv.handler import Handler
|
from calibre.srv.handler import Handler
|
||||||
@ -60,7 +61,10 @@ class Server(object):
|
|||||||
if opts.log:
|
if opts.log:
|
||||||
log = RotatingLog(opts.log, max_size=opts.max_log_size)
|
log = RotatingLog(opts.log, max_size=opts.max_log_size)
|
||||||
self.handler = Handler(libraries, opts)
|
self.handler = Handler(libraries, opts)
|
||||||
self.loop = ServerLoop(create_http_handler(self.handler.dispatch), opts=opts, log=log)
|
plugins = []
|
||||||
|
if opts.use_bonjour:
|
||||||
|
plugins.append(BonJour())
|
||||||
|
self.loop = ServerLoop(create_http_handler(self.handler.dispatch), opts=opts, log=log, plugins=plugins)
|
||||||
self.handler.set_log(self.loop.log)
|
self.handler.set_log(self.loop.log)
|
||||||
self.serve_forever = self.loop.serve_forever
|
self.serve_forever = self.loop.serve_forever
|
||||||
|
|
||||||
|
@ -101,6 +101,23 @@ class LoopTest(BaseTest):
|
|||||||
with TestServer(lambda data:(data.path[0] + data.read()), listen_on='1.1.1.1', fallback_to_detected_interface=True, specialize=specialize) as server:
|
with TestServer(lambda data:(data.path[0] + data.read()), listen_on='1.1.1.1', fallback_to_detected_interface=True, specialize=specialize) as server:
|
||||||
self.assertNotEqual('1.1.1.1', server.address[0])
|
self.assertNotEqual('1.1.1.1', server.address[0])
|
||||||
|
|
||||||
|
def test_bonjour(self):
|
||||||
|
'Test advertising via BonJour'
|
||||||
|
from calibre.srv.plugins import BonJour
|
||||||
|
from calibre.utils.Zeroconf import Zeroconf
|
||||||
|
b = BonJour()
|
||||||
|
with TestServer(lambda data:(data.path[0] + data.read()), plugins=(b,), shutdown_timeout=5) as server:
|
||||||
|
self.assertTrue(b.started.wait(5), 'BonJour not started')
|
||||||
|
self.ae(b.advertised_port, server.address[1])
|
||||||
|
service = b.services[0]
|
||||||
|
self.ae(service.type, b'_calibre._tcp.local.')
|
||||||
|
r = Zeroconf()
|
||||||
|
info = r.getServiceInfo(service.type, service.name)
|
||||||
|
self.assertIsNotNone(info)
|
||||||
|
self.ae(info.text, b'\npath=/opds')
|
||||||
|
|
||||||
|
self.assertTrue(b.stopped.wait(5), 'BonJour not stopped')
|
||||||
|
|
||||||
def test_ring_buffer(self):
|
def test_ring_buffer(self):
|
||||||
'Test the ring buffer used for reads'
|
'Test the ring buffer used for reads'
|
||||||
class FakeSocket(object):
|
class FakeSocket(object):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user