diff --git a/src/calibre/library/__init__.py b/src/calibre/library/__init__.py index 18aec71fc8..0f8e5e5496 100644 --- a/src/calibre/library/__init__.py +++ b/src/calibre/library/__init__.py @@ -2,7 +2,7 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal ' ''' Code to manage ebook library''' -def db(): +def db(path=None): from calibre.library.database2 import LibraryDatabase2 from calibre.utils.config import prefs - return LibraryDatabase2(prefs['library_path']) + return LibraryDatabase2(path if path else prefs['library_path']) diff --git a/src/calibre/library/server/base.py b/src/calibre/library/server/base.py index feae6aed88..c9025a28f8 100644 --- a/src/calibre/library/server/base.py +++ b/src/calibre/library/server/base.py @@ -28,11 +28,13 @@ from calibre.library.server.browse import BrowseServer class DispatchController(object): # {{{ - def __init__(self, prefix): + def __init__(self, prefix, wsgi=False): self.dispatcher = cherrypy.dispatch.RoutesDispatcher() self.funcs = [] self.seen = set([]) self.prefix = prefix if prefix else '' + if wsgi: + self.prefix = '' def __call__(self, name, route, func, **kwargs): if name in self.seen: @@ -96,7 +98,9 @@ class LibraryServer(ContentServer, MobileServer, XMLServer, OPDSServer, Cache, server_name = __appname__ + '/' + __version__ - def __init__(self, db, opts, embedded=False, show_tracebacks=True): + def __init__(self, db, opts, embedded=False, show_tracebacks=True, + wsgi=False): + self.is_wsgi = bool(wsgi) self.opts = opts self.embedded = embedded self.state_callback = None @@ -124,25 +128,36 @@ class LibraryServer(ContentServer, MobileServer, XMLServer, OPDSServer, Cache, 'server.socket_timeout' : opts.timeout, #seconds 'server.thread_pool' : opts.thread_pool, # number of threads }) - if embedded: + if embedded or wsgi: cherrypy.config.update({'engine.SIGHUP' : None, 'engine.SIGTERM' : None,}) - self.config = {'global': { - 'tools.gzip.on' : True, - 'tools.gzip.mime_types': ['text/html', 'text/plain', 'text/xml', 'text/javascript', 'text/css'], - }} - if opts.password: - self.config['/'] = { - 'tools.digest_auth.on' : True, - 'tools.digest_auth.realm' : (_('Password to access your calibre library. Username is ') + opts.username.strip()).encode('ascii', 'replace'), - 'tools.digest_auth.users' : {opts.username.strip():opts.password.strip()}, - } - - + self.config = {} self.is_running = False self.exception = None - self.setup_loggers() - cherrypy.engine.bonjour.subscribe() + if not wsgi: + self.setup_loggers() + cherrypy.engine.bonjour.subscribe() + self.config['global'] = { + 'tools.gzip.on' : True, + 'tools.gzip.mime_types': ['text/html', 'text/plain', + 'text/xml', 'text/javascript', 'text/css'], + } + if opts.password: + self.config['/'] = { + 'tools.digest_auth.on' : True, + 'tools.digest_auth.realm' : ( + _('Password to access your calibre library. Username is ') + + opts.username.strip()), + 'tools.digest_auth.users' : {opts.username.strip():opts.password.strip()}, + } + + self.__dispatcher__ = DispatchController(self.opts.url_prefix, wsgi) + for x in self.__class__.__bases__: + if hasattr(x, 'add_routes'): + x.add_routes(self, self.__dispatcher__) + root_conf = self.config.get('/', {}) + root_conf['request.dispatch'] = self.__dispatcher__.dispatcher + self.config['/'] = root_conf def set_database(self, db): self.db = db @@ -183,14 +198,6 @@ class LibraryServer(ContentServer, MobileServer, XMLServer, OPDSServer, Cache, def start(self): self.is_running = False - d = DispatchController(self.opts.url_prefix) - for x in self.__class__.__bases__: - if hasattr(x, 'add_routes'): - x.add_routes(self, d) - root_conf = self.config.get('/', {}) - root_conf['request.dispatch'] = d.dispatcher - self.config['/'] = root_conf - cherrypy.tree.mount(root=None, config=self.config) try: try: diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index c874fa5041..9c442acc11 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -460,13 +460,14 @@ class BrowseServer(object): @Endpoint() def browse_catalog(self, category=None, category_sort=None): 'Entry point for top-level, categories and sub-categories' + prefix = '' if self.is_wsgi else self.opts.url_prefix if category == None: ans = self.browse_toplevel() elif category == 'newest': - raise cherrypy.InternalRedirect(self.opts.url_prefix + + raise cherrypy.InternalRedirect(prefix + '/browse/matches/newest/dummy') elif category == 'allbooks': - raise cherrypy.InternalRedirect(self.opts.url_prefix + + raise cherrypy.InternalRedirect(prefix + '/browse/matches/allbooks/dummy') else: ans = self.browse_category(category, category_sort) diff --git a/src/calibre/library/server/main.py b/src/calibre/library/server/main.py index ca36f998bf..fbd811a1ab 100644 --- a/src/calibre/library/server/main.py +++ b/src/calibre/library/server/main.py @@ -24,6 +24,17 @@ def stop_threaded_server(server): server.exit() server.thread = None +def create_wsgi_app(path_to_library=None, prefix=''): + 'WSGI entry point' + from calibre.library import db + cherrypy.config.update({'environment': 'embedded'}) + db = db(path_to_library) + parser = option_parser() + opts, args = parser.parse_args(['calibre-server']) + opts.url_prefix = prefix + server = LibraryServer(db, opts, wsgi=True, show_tracebacks=True) + return cherrypy.Application(server, script_name=None, config=server.config) + def option_parser(): parser = config().option_parser('%prog '+ _( '''[options] diff --git a/src/calibre/library/server/mobile.py b/src/calibre/library/server/mobile.py index ae50098043..a889089109 100644 --- a/src/calibre/library/server/mobile.py +++ b/src/calibre/library/server/mobile.py @@ -121,7 +121,7 @@ def build_index(books, num, search, sort, order, start, total, url_base, CKEYS, A( fmt.lower(), href=prefix+'/get/%s/%s-%s_%d.%s' % (fmt, a, t, - book['id'], fmt) + book['id'], fmt.lower()) ), CLASS('button')) s.tail = u'' diff --git a/src/calibre/manual/server.rst b/src/calibre/manual/server.rst new file mode 100644 index 0000000000..70814de3e1 --- /dev/null +++ b/src/calibre/manual/server.rst @@ -0,0 +1,104 @@ +.. include:: global.rst + +.. _servertutorial: + +Integrating the |app| content server into other servers +========================================================== + +Here, we will show you how to integrate the |app| content server into another server. The most common reason for this is to make use of SSL or more sophisticated authentication. There are two main techniques: Running the |app| content server as a standalone process and using a reverse proxy to connect it with your main server or running the content server in process in your main server with WSGI. The examples below are all for Apache 2.x on linux, but should be easily adaptable to other platforms. + +.. contents:: Contents + :depth: 2 + :local: + +Using a reverse proxy +----------------------- + +This is the simplest approach as it allows you to use the binary calibre install with no external dependencies/system integration requirements. + +First start the |app| content server as shown below:: + + calibre-server --url-prefix /calibre --port 8080 + +Now suppose you are using Apache as your main server. First enable the proxy modules in apache, by adding the following to :file:`httpd.conf`:: + + LoadModule proxy_module modules/mod_proxy.so + LoadModule proxy_http_module modules/mod_proxy_http.so + +The exact technique for enabling the proxy modules will vary depending on your Apache installation. Once you have the proxy modules enabled, add the following rules to httpd.conf (or if you are using virtual hosts to the conf file for the virtual host in question:: + + RewriteEngine on + RewriteRule ^/calibre/(.*) http://localhost:8080/calibre/$1 [proxy] + RewriteRule ^/calibre http://localhost:8080 [proxy] + +That's all, you will now be able to access the |app| Content Server under the /calibre URL in your apache server. + +.. note:: If you are willing to devote an entire VirtualHost to the content server, then there is no need to use --url-prefix and RewriteRule, instead just use the ProxyPass directive. + +Using WSGI +------------ + +The calibre content server can be run directly, in process, inside a host server like Apache using the WSGI framework. + +First, we have to create a WSGI *adapter* for the calibre content server. Here is a template you can use for the purpose. Replace the paths as directed in the comments + +.. code-block:: python + + # WSGI script file to run calibre content server as a WSGI app + + import sys, os + + + # You can get the paths referenced here by running + # calibre-debug --paths + # on your server + + # The first entry from CALIBRE_PYTHON_PATH + sys.path.insert(0, '/home/kovid/work/calibre/src') + + # CALIBRE_RESOURCES_PATH + sys.resources_location = '/home/kovid/work/calibre/resources' + + # CALIBRE_EXTENSIONS_PATH + sys.extensions_location = '/home/kovid/work/calibre/src/calibre/plugins' + + # Path to directory containing calibre executables + sys.executables_location = '/usr/bin' + + # Path to a directory for which the server has read/write permissions + # calibre config will be stored here + os.environ['CALIBRE_CONFIG_DIRECTORY'] = '/var/www/localhost/calibre-config' + + del sys + del os + + from calibre.library.server.main import create_wsgi_app + application = create_wsgi_app( + # The mount point of this WSGI application (i.e. the first argument to + # the WSGIScriptAlias directive). Set to empty string is mounted at / + prefix='/calibre', + + # Path to the calibre library to be served + # The server process must have write permission for all files/dirs + # in this directory or BAD things will happen + path_to_library='/home/kovid/documents/demo library' + ) + + del create_wsgi_app + +Save this adapter as :file:`calibre-wsgi-adpater.py` somewhere your server will have access to it. + +Let's suppose that we want to use WSGI in Apache. First enable WSGI in Apache by adding the following to :file:`httpd.conf`:: + + LoadModule proxy_module modules/mod_wsgi.so + +The exact technique for enabling the wsgi module will vary depending on your Apache installation. Once you have the proxy modules enabled, add the following rules to httpd.conf (or if you are using virtual hosts to the conf file for the virtual host in question:: + + WSGIScriptAlias /calibre /var/www/localhost/cgi-bin/calibre-wsgi-adapter.py + +Change the path to :file:`calibre-wsgi-adapter.py` to wherever you saved it previously (make sure Apache has access to it). + +That's all, you will now be able to access the |app| Content Server under the /calibre URL in your apache server. + +.. note:: For more help with using mod_wsgi in Apache, see `mod_wsgi `_. + diff --git a/src/calibre/manual/tutorials.rst b/src/calibre/manual/tutorials.rst index 084c44ff64..ecd40222d4 100644 --- a/src/calibre/manual/tutorials.rst +++ b/src/calibre/manual/tutorials.rst @@ -16,4 +16,5 @@ Here you will find tutorials to get you started using |app|'s more advanced feat template_lang regexp portable + server