diff --git a/docs/CONFIGURATION.rst b/docs/CONFIGURATION.rst index 1657f43..7c48904 100644 --- a/docs/CONFIGURATION.rst +++ b/docs/CONFIGURATION.rst @@ -91,25 +91,36 @@ purge-after Server ------ -HTTP server configuration, does **not** apply to uWSGI (except for `profile`). +HTTP server configuration. .. code-block:: ini [server] - host = localhost - port = 8080 + listen = http://localhost:8080 reload = off profile = off -host - listen on specified interface +listen + interface to listen on. Isso supports TCP/IP and unix domain sockets: -port - application port + .. code-block:: uni + + ; UNIX domain socket + listen = unix:///tmp/isso.sock + ; TCP/IP + listen = http:///localhost:1234/ + + When ``gevent`` is available, it is automatically used for `http://` + Currently, gevent can not handle http requests on unix domain socket + (see `#295 `_ and + `#299 `_ for details). + + Does not apply for `uWSGI`. reload reload application, when the source code has changed. Useful for - development (don't forget to use a fixed `session-key`). + development (don't forget to use a fixed `session-key`). Only works + when ``gevent`` and ``uwsgi`` are *not* available. profile show 10 most time consuming function in Isso after each request. Do diff --git a/isso/__init__.py b/isso/__init__.py index 79ae782..17e1f6a 100644 --- a/isso/__init__.py +++ b/isso/__init__.py @@ -30,8 +30,14 @@ from __future__ import print_function import pkg_resources dist = pkg_resources.get_distribution("isso") +try: + import gevent.monkey; gevent.monkey.patch_all() +except ImportError: + pass + import sys import os +import socket import logging from os.path import dirname, join @@ -46,11 +52,10 @@ import misaka from itsdangerous import URLSafeTimedSerializer from werkzeug.routing import Map, Rule -from werkzeug.wrappers import Response from werkzeug.exceptions import HTTPException, InternalServerError from werkzeug.wsgi import SharedDataMiddleware -from werkzeug.serving import run_simple +from werkzeug.serving import run_simple, WSGIRequestHandler from werkzeug.contrib.fixers import ProxyFix from isso import db, migrate, views, wsgi @@ -208,8 +213,52 @@ def main(): migrate.disqus(db.SQLite3(conf.get('general', 'dbpath'), conf), args.dump) sys.exit(0) - run_simple(conf.get('server', 'host'), conf.getint('server', 'port'), make_app(conf), - threaded=True, use_reloader=conf.getboolean('server', 'reload')) + if conf.get("server", "listen").startswith("http://"): + host, port, _ = parse.host(conf.get("server", "listen")) + try: + from gevent.pywsgi import WSGIServer + WSGIServer((host, port), make_app(conf)).serve_forever() + except ImportError: + run_simple(host, port, make_app(conf), threaded=True, + use_reloader=conf.getboolean('server', 'reload')) + else: + try: + from socketserver import ThreadingMixIn + from http.server import HTTPServer + except ImportError: + from SocketServer import ThreadingMixIn + from BaseHTTPServer import HTTPServer + + class SocketWSGIRequestHandler(WSGIRequestHandler): + + def run_wsgi(self): + self.client_address = ("", 0) + super(SocketWSGIRequestHandler, self).run_wsgi() + + class SocketHTTPServer(HTTPServer, ThreadingMixIn): + + multithread = True + multiprocess = False + + allow_reuse_address = 1 + address_family = socket.AF_UNIX + + request_queue_size = 128 + + def __init__(self, sock, app): + HTTPServer.__init__(self, sock, SocketWSGIRequestHandler) + self.app = app + self.ssl_context = None + self.shutdown_signal = False + + sock = conf.get("server", "listen").partition("unix://")[2] + + try: + os.unlink(sock) + except OSError: + pass + + SocketHTTPServer(sock, make_app(conf)).serve_forever() try: import uwsgi diff --git a/isso/core.py b/isso/core.py index 92eb666..3157966 100644 --- a/isso/core.py +++ b/isso/core.py @@ -92,7 +92,7 @@ class Config: "enabled = false", "purge-after = 30d", "[server]", - "host = localhost", "port = 8080", + "listen = http://localhost:8080/", "reload = off", "profile = off", "[smtp]", "username = ", "password = ",