[Python-modules-commits] [python-daphne] 01/02: importing python-daphne_1.3.0.orig.tar.gz
Michael Fladischer
fladi at moszumanska.debian.org
Tue Nov 21 19:19:40 UTC 2017
This is an automated email from the git hooks/post-receive script.
fladi pushed a commit to branch debian/master
in repository python-daphne.
commit 38b15068ac3ea743de97fdb6493c039781949617
Author: Michael Fladischer <FladischerMichael at fladi.at>
Date: Mon Nov 20 12:51:25 2017 +0100
importing python-daphne_1.3.0.orig.tar.gz
---
.gitignore | 9 +
.travis.yml | 13 +
CHANGELOG.txt | 284 +++++++++++++++++++++
LICENSE | 27 ++
Makefile | 16 ++
README.rst | 135 ++++++++++
daphne/__init__.py | 1 +
daphne/access.py | 67 +++++
daphne/cli.py | 218 +++++++++++++++++
daphne/http_protocol.py | 449 ++++++++++++++++++++++++++++++++++
daphne/server.py | 221 +++++++++++++++++
daphne/tests/__init__.py | 0
daphne/tests/asgi.py | 3 +
daphne/tests/factories.py | 128 ++++++++++
daphne/tests/http_strategies.py | 121 +++++++++
daphne/tests/test_endpoints.py | 200 +++++++++++++++
daphne/tests/test_http_request.py | 197 +++++++++++++++
daphne/tests/test_http_response.py | 127 ++++++++++
daphne/tests/test_utils.py | 115 +++++++++
daphne/tests/test_ws.py | 245 +++++++++++++++++++
daphne/tests/testcases.py | 239 ++++++++++++++++++
daphne/twisted/plugins/fd_endpoint.py | 24 ++
daphne/utils.py | 55 +++++
daphne/ws_protocol.py | 268 ++++++++++++++++++++
setup.cfg | 2 +
setup.py | 51 ++++
tox.ini | 10 +
27 files changed, 3225 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e3535d3
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,9 @@
+*.egg-info
+*.pyc
+__pycache__
+dist/
+build/
+/.tox
+.hypothesis
+.cache
+.eggs
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..9672140
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,13 @@
+sudo: false
+
+language: python
+
+python:
+ - "2.7"
+ - "3.4"
+ - "3.5"
+ - "3.6"
+
+install: pip install tox tox-travis
+
+script: tox
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
new file mode 100644
index 0000000..5a1b551
--- /dev/null
+++ b/CHANGELOG.txt
@@ -0,0 +1,284 @@
+1.3.0 (2017-06-16)
+------------------
+
+* Ability to set the websocket connection timeout
+
+* Server no longer reveals the exact Autobahn version number for security
+
+* A few unicode fixes for Python 2/3 compatability
+
+* Stopped logging messages to already-closed connections as ERROR
+
+
+1.2.0 (2017-04-01)
+------------------
+
+* The new process-specific channel support is now implemented, resulting in
+ significantly less traffic to your channel backend.
+
+* Native twisted blocking support for channel layers that support it is now
+ used. While it is a lot more efficient, it is also sometimes slightly more
+ latent; you can disable it using --force-sync.
+
+* Native SSL termination is now correctly reflected in the ASGI-HTTP `scheme`
+ key.
+
+* accept: False is now a valid way to deny a connection, as well as close: True.
+
+* HTTP version is now correctly sent as one of "1.0", "1.1" or "2".
+
+* More command line options for websocket timeouts
+
+
+1.1.0 (2017-03-18)
+------------------
+
+* HTTP/2 termination is now supported natively. The Twisted dependency has been
+ increased to at least 17.1 as a result; for more information about setting up
+ HTTP/2, see the README.
+
+* X-Forwarded-For decoding support understands IPv6 addresses, and picks the
+ most remote (leftmost) entry if there are multiple relay hosts.
+
+* Fixed an error where `disconnect` messages would still try and get sent even
+ if the client never finished a request.
+
+
+1.0.3 (2017-02-12)
+------------------
+
+* IPv6 addresses are correctly accepted as bind targets on the command line
+
+* Twisted 17.1 compatability fixes for WebSocket receiving/keepalive and
+ proxy header detection.
+
+
+1.0.2 (2017-02-01)
+------------------
+
+* The "null" WebSocket origin (including file:// and no value) is now accepted
+ by Daphne and passed onto the application to accept/deny.
+
+* Listening on file descriptors works properly again.
+
+* The DeprecationError caused by not passing endpoints into a Server class
+ directly is now a warning instead.
+
+
+1.0.1 (2017-01-09)
+------------------
+
+* Endpoint unicode strings now work correctly on Python 2 and Python 3
+
+
+1.0.0 (2017-01-08)
+------------------
+
+* BREAKING CHANGE: Daphne now requires acceptance of WebSocket connections
+ before it finishes the socket handshake and relays incoming packets.
+ You must upgrade to at least Channels 1.0.0 as well; see
+ http://channels.readthedocs.io/en/latest/releases/1.0.0.html for more.
+
+* http.disconnect now has a `path` key
+
+* WebSockets can now be closed with a specific code
+
+* X-Forwarded-For header support; defaults to X-Forwarded-For, override with
+ --proxy-headers on the commandline.
+
+* Twisted endpoint description string support with `-e` on the command line
+ (allowing for SNI/ACME support, among other things)
+
+* Logging/error verbosity fixes and access log flushes properly
+
+
+0.15.0 (2016-08-28)
+-------------------
+
+* Connections now force-close themselves after pings fail for a certain
+ timeframe, controllable via the new --ping-timeout option.
+
+* Badly-formatted websocket response messages now log to console in
+ all situations
+
+* Compatability with Twisted 16.3 and up
+
+
+0.14.3 (2016-07-21)
+-------------------
+
+* File descriptors can now be passed on the commandline for process managers
+ that pass sockets along like this.
+
+* websocket.disconnect messages now come with a "code" attribute matching the
+ WebSocket spec.
+
+* A memory leak in request logging has been fixed.
+
+
+0.14.2 (2016-07-07)
+-------------------
+
+* Marked as incompatible with twisted 16.3 and above until we work out why
+ it stops incoming websocket messages working
+
+
+0.14.1 (2016-07-06)
+-------------------
+
+* Consumption of websocket.receive is also now required.
+
+
+0.14.0 (2016-07-06)
+-------------------
+
+* Consumption of websocket.connect is now required (channels 0.16 enforces
+ this); getting backpressure on it now results in the socket being
+ force closed.
+
+
+0.13.1 (2016-06-28)
+-------------------
+
+* Bad WebSocket handshakes now return 400 and an error messages
+ rather than 500 with no content.
+
+
+0.13.0 (2016-06-22)
+-------------------
+
+* Query strings are now sent as bytestrings and the application is responsible
+ for decoding. Ensure you're running Channels 0.15 or higher.
+
+
+0.12.2 (2016-06-21)
+-------------------
+
+* Plus signs in query string are now handled by Daphne, not Django-by-mistake.
+ Ensure you're running Channels 0.14.3 or higher.
+
+* New --root-path and DAPHNE_ROOT_PATH options for setting root path.
+
+
+0.12.1 (2016-05-18)
+-------------------
+
+* Fixed bug where a non-ASCII byte in URL paths would crash the HTTP parser
+ without a response; now returns 400, and hardening in place to catch most
+ other errors and return a 500.
+
+* WebSocket header format now matches HTTP header format and the ASGI spec.
+ No update needed to channels library, but user code may need updating.
+
+
+0.12.0 (2016-05-07)
+-------------------
+
+* Backpressure on http.request now causes incoming requests to drop with 503.
+ Websockets will drop connection/disconnection messages/received frames if
+ backpressure is encountered; options are coming soon to instead drop the
+ connection if this happens.
+
+
+0.11.4 (2016-05-04)
+-------------------
+
+* Don't try to send TCP host info in message for unix sockets
+
+
+0.11.3 (2016-04-27)
+-------------------
+
+* Don't decode + as a space in URLs
+
+
+0.11.2 (2016-04-27)
+-------------------
+
+* Correctly encode all path params for WebSockets
+
+
+0.11.1 (2016-04-26)
+-------------------
+
+* Fix bugs with WebSocket path parsing under Python 2
+
+
+0.11.0 (2016-04-26)
+-------------------
+
+* HTTP paths and query strings are now pre-decoded before going to ASGI
+
+
+0.10.3 (2016-04-05)
+-------------------
+
+* Error on badly formatted websocket reply messages
+
+
+0.10.2 (2016-04-03)
+-------------------
+
+* Access logging in NCSAish format now printed to stdout, configurable to
+ another file using --access-log=filename
+
+
+0.10.1 (2016-03-29)
+-------------------
+
+* WebSockets now close after they've been open for longer than the channel
+ layer group expiry (86400 seconds by default for most layers).
+
+* Binding to UNIX sockets is now possible (use the -u argument)
+
+* WebSockets now send keepalive pings if they've had no data for a certain
+ amount of time (20 seconds by default, set with --ping-interval)
+
+
+0.10.0 (2016-03-21)
+-------------------
+
+* Multiple cookies are now set correctly
+
+* Follows new ASGI single-response-channel spec for !
+
+* Follows new ASGI header encoding spec for HTTP
+
+
+0.9.3 (2016-03-08)
+------------------
+
+* WebSocket query strings are correctly encoded
+
+
+0.9.2 (2016-03-02)
+------------------
+
+* HTTP requests now time out after a configurable amount of time and return 503
+ (default is 2 minutes)
+
+
+0.9.1 (2016-03-01)
+------------------
+
+* Main thread actually idles rather than sitting at 100%
+
+* WebSocket packets have an "order" attribute attached
+
+* WebSocket upgrade header detection is now case insensitive
+
+
+0.9 (2016-02-21)
+----------------
+
+* Signal handlers can now be disabled if you want to run inside a thread
+ (e.g. inside Django autoreloader)
+
+* Logging hooks that can be used to allow calling code to show requests
+ and other events.
+
+* Headers are now transmitted for websocket.connect
+
+* http.disconnect messages are now sent
+
+* Request handling speed significantly improved
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..5f4f225
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) Django Software Foundation and individual contributors.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of Django nor the names of its contributors may be used
+ to endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..1a1f55e
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,16 @@
+.PHONY: release
+
+all:
+
+release:
+ifndef version
+ $(error Please supply a version)
+endif
+ @echo Releasing version $(version)
+ifeq (,$(findstring $(version),$(shell git log --oneline -1)))
+ $(error Last commit does not match version)
+endif
+ git tag $(version)
+ git push
+ git push --tags
+ python setup.py sdist bdist_wheel upload
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..bab5110
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,135 @@
+daphne
+======
+
+.. image:: https://api.travis-ci.org/django/daphne.svg
+ :target: https://travis-ci.org/django/daphne
+
+.. image:: https://img.shields.io/pypi/v/daphne.svg
+ :target: https://pypi.python.org/pypi/daphne
+
+Daphne is a HTTP, HTTP2 and WebSocket protocol server for
+`ASGI <https://channels.readthedocs.io/en/latest/asgi.html>`_, and developed
+to power Django Channels.
+
+It supports automatic negotiation of protocols; there's no need for URL
+prefixing to determine WebSocket endpoints versus HTTP endpoints.
+
+
+Running
+-------
+
+Simply point Daphne to your ASGI channel layer instance, and optionally
+set a bind address and port (defaults to localhost, port 8000)::
+
+ daphne -b 0.0.0.0 -p 8001 django_project.asgi:channel_layer
+
+If you intend to run daphne behind a proxy server you can use UNIX
+sockets to communicate between the two::
+
+ daphne -u /tmp/daphne.sock django_project.asgi:channel_layer
+
+If daphne is being run inside a process manager such as
+`Circus <https://github.com/circus-tent/circus/>`_ you might
+want it to bind to a file descriptor passed down from a parent process.
+To achieve this you can use the --fd flag::
+
+ daphne --fd 5 django_project.asgi:channel_layer
+
+If you want more control over the port/socket bindings you can fall back to
+using `twisted's endpoint description strings
+<http://twistedmatrix.com/documents/current/api/twisted.internet.endpoints.html#serverFromString>`_
+by using the `--endpoint (-e)` flag, which can be used multiple times.
+This line would start a SSL server on port 443, assuming that `key.pem` and `crt.pem`
+exist in the current directory (requires pyopenssl to be installed)::
+
+ daphne -e ssl:443:privateKey=key.pem:certKey=crt.pem django_project.asgi:channel_layer
+
+Endpoints even let you use the ``txacme`` endpoint syntax to get automatic certificates
+from Let's Encrypt, which you can read more about at http://txacme.readthedocs.io/en/stable/.
+
+To see all available command line options run daphne with the *-h* flag.
+
+
+HTTP/2 Support
+--------------
+
+Daphne 1.1 and above supports terminating HTTP/2 connections natively. You'll
+need to do a couple of things to get it working, though. First, you need to
+make sure you install the Twisted ``http2`` and ``tls`` extras::
+
+ pip install -U Twisted[tls,http2]
+
+Next, because all current browsers only support HTTP/2 when using TLS, you will
+need to start Daphne with TLS turned on, which can be done using the Twisted endpoint syntax::
+
+ daphne -e ssl:443:privateKey=key.pem:certKey=crt.pem django_project.asgi:channel_layer
+
+Alternatively, you can use the ``txacme`` endpoint syntax or anything else that
+enables TLS under the hood.
+
+You will also need to be on a system that has **OpenSSL 1.0.2 or greater**; if you are
+using Ubuntu, this means you need at least 16.04.
+
+Now, when you start up Daphne, it should tell you this in the log::
+
+ 2017-03-18 19:14:02,741 INFO Starting server at ssl:port=8000:privateKey=privkey.pem:certKey=cert.pem, channel layer django_project.asgi:channel_layer.
+ 2017-03-18 19:14:02,742 INFO HTTP/2 support enabled
+
+Then, connect with a browser that supports HTTP/2, and everything should be
+working. It's often hard to tell that HTTP/2 is working, as the log Daphne gives you
+will be identical (it's HTTP, after all), and most browsers don't make it obvious
+in their network inspector windows. There are browser extensions that will let
+you know clearly if it's working or not.
+
+Daphne only supports "normal" requests over HTTP/2 at this time; there is not
+yet support for extended features like Server Push. It will, however, result in
+much faster connections and lower overheads.
+
+If you have a reverse proxy in front of your site to serve static files or
+similar, HTTP/2 will only work if that proxy understands and passes through the
+connection correctly.
+
+
+Root Path (SCRIPT_NAME)
+-----------------------
+
+In order to set the root path for Daphne, which is the equivalent of the
+WSGI ``SCRIPT_NAME`` setting, you have two options:
+
+* Pass a header value ``Daphne-Root-Path``, with the desired root path as a
+ URLencoded ASCII value. This header will not be passed down to applications.
+
+* Set the ``--root-path`` commandline option with the desired root path as a
+ URLencoded ASCII value.
+
+The header takes precedence if both are set. As with ``SCRIPT_ALIAS``, the value
+should start with a slash, but not end with one; for example::
+
+ daphne --root-path=/forum django_project.asgi:channel_layer
+
+Dependencies
+------------
+
+All Channels projects currently support Python 2.7, 3.4 and 3.5. `daphne` requires Twisted 17.1 or
+greater.
+
+
+Contributing
+------------
+
+Please refer to the
+`main Channels contributing docs <https://github.com/django/channels/blob/master/CONTRIBUTING.rst>`_.
+That also contains advice on how to set up the development environment and run the tests.
+
+
+Maintenance and Security
+------------------------
+
+To report security issues, please contact security at djangoproject.com. For GPG
+signatures and more security process information, see
+https://docs.djangoproject.com/en/dev/internals/security/.
+
+To report bugs or request new features, please open a new GitHub issue.
+
+This repository is part of the Channels project. For the shepherd and maintenance team, please see the
+`main Channels readme <https://github.com/django/channels/blob/master/README.rst>`_.
diff --git a/daphne/__init__.py b/daphne/__init__.py
new file mode 100755
index 0000000..67bc602
--- /dev/null
+++ b/daphne/__init__.py
@@ -0,0 +1 @@
+__version__ = "1.3.0"
diff --git a/daphne/access.py b/daphne/access.py
new file mode 100644
index 0000000..c5fa69c
--- /dev/null
+++ b/daphne/access.py
@@ -0,0 +1,67 @@
+import datetime
+
+
+class AccessLogGenerator(object):
+ """
+ Object that implements the Daphne "action logger" internal interface in
+ order to provide an access log in something resembling NCSA format.
+ """
+
+ def __init__(self, stream):
+ self.stream = stream
+
+ def __call__(self, protocol, action, details):
+ """
+ Called when an action happens; use it to generate log entries.
+ """
+ # HTTP requests
+ if protocol == "http" and action == "complete":
+ self.write_entry(
+ host=details['client'],
+ date=datetime.datetime.now(),
+ request="%(method)s %(path)s" % details,
+ status=details['status'],
+ length=details['size'],
+ )
+ # Websocket requests
+ elif protocol == "websocket" and action == "connecting":
+ self.write_entry(
+ host=details['client'],
+ date=datetime.datetime.now(),
+ request="WSCONNECTING %(path)s" % details,
+ )
+ elif protocol == "websocket" and action == "rejected":
+ self.write_entry(
+ host=details['client'],
+ date=datetime.datetime.now(),
+ request="WSREJECT %(path)s" % details,
+ )
+ elif protocol == "websocket" and action == "connected":
+ self.write_entry(
+ host=details['client'],
+ date=datetime.datetime.now(),
+ request="WSCONNECT %(path)s" % details,
+ )
+ elif protocol == "websocket" and action == "disconnected":
+ self.write_entry(
+ host=details['client'],
+ date=datetime.datetime.now(),
+ request="WSDISCONNECT %(path)s" % details,
+ )
+
+ def write_entry(self, host, date, request, status=None, length=None, ident=None, user=None):
+ """
+ Writes an NCSA-style entry to the log file (some liberty is taken with
+ what the entries are for non-HTTP)
+ """
+ self.stream.write(
+ "%s %s %s [%s] \"%s\" %s %s\n" % (
+ host,
+ ident or "-",
+ user or "-",
+ date.strftime("%d/%b/%Y:%H:%M:%S"),
+ request,
+ status or "-",
+ length or "-",
+ )
+ )
diff --git a/daphne/cli.py b/daphne/cli.py
new file mode 100755
index 0000000..b27cb06
--- /dev/null
+++ b/daphne/cli.py
@@ -0,0 +1,218 @@
+import sys
+import argparse
+import logging
+import importlib
+from .server import Server, build_endpoint_description_strings
+from .access import AccessLogGenerator
+
+
+logger = logging.getLogger(__name__)
+
+DEFAULT_HOST = '127.0.0.1'
+DEFAULT_PORT = 8000
+
+class CommandLineInterface(object):
+ """
+ Acts as the main CLI entry point for running the server.
+ """
+
+ description = "Django HTTP/WebSocket server"
+
+ def __init__(self):
+ self.parser = argparse.ArgumentParser(
+ description=self.description,
+ )
+ self.parser.add_argument(
+ '-p',
+ '--port',
+ type=int,
+ help='Port number to listen on',
+ default=None,
+ )
+ self.parser.add_argument(
+ '-b',
+ '--bind',
+ dest='host',
+ help='The host/address to bind to',
+ default=None,
+ )
+ self.parser.add_argument(
+ '--websocket_timeout',
+ type=int,
+ help='Maximum time to allow a websocket to be connected. -1 for infinite.',
+ default=None,
+ )
+ self.parser.add_argument(
+ '--websocket_connect_timeout',
+ type=int,
+ help='Maximum time to allow a connection to handshake. -1 for infinite',
+ default=5,
+ )
+ self.parser.add_argument(
+ '-u',
+ '--unix-socket',
+ dest='unix_socket',
+ help='Bind to a UNIX socket rather than a TCP host/port',
+ default=None,
+ )
+ self.parser.add_argument(
+ '--fd',
+ type=int,
+ dest='file_descriptor',
+ help='Bind to a file descriptor rather than a TCP host/port or named unix socket',
+ default=None,
+ )
+ self.parser.add_argument(
+ '-e',
+ '--endpoint',
+ dest='socket_strings',
+ action='append',
+ help='Use raw server strings passed directly to twisted',
+ default=[],
+ )
+ self.parser.add_argument(
+ '-v',
+ '--verbosity',
+ type=int,
+ help='How verbose to make the output',
+ default=1,
+ )
+ self.parser.add_argument(
+ '-t',
+ '--http-timeout',
+ type=int,
+ help='How long to wait for worker server before timing out HTTP connections',
+ default=120,
+ )
+ self.parser.add_argument(
+ '--access-log',
+ help='Where to write the access log (- for stdout, the default for verbosity=1)',
+ default=None,
+ )
+ self.parser.add_argument(
+ '--ping-interval',
+ type=int,
+ help='The number of seconds a WebSocket must be idle before a keepalive ping is sent',
+ default=20,
+ )
+ self.parser.add_argument(
+ '--ping-timeout',
+ type=int,
+ help='The number of seconds before a WebSocket is closed if no response to a keepalive ping',
+ default=30,
+ )
+ self.parser.add_argument(
+ '--ws-protocol',
+ nargs='*',
+ dest='ws_protocols',
+ help='The WebSocket protocols you wish to support',
+ default=None,
+ )
+ self.parser.add_argument(
+ '--root-path',
+ dest='root_path',
+ help='The setting for the ASGI root_path variable',
+ default="",
+ )
+ self.parser.add_argument(
+ '--proxy-headers',
+ dest='proxy_headers',
+ help='Enable parsing and using of X-Forwarded-For and X-Forwarded-Port headers and using that as the '
+ 'client address',
+ default=False,
+ action='store_true',
+ )
+ self.parser.add_argument(
+ '--force-sync',
+ dest='force_sync',
+ action='store_true',
+ help='Force the server to use synchronous mode on its ASGI channel layer',
+ default=False,
+ )
+ self.parser.add_argument(
+ 'channel_layer',
+ help='The ASGI channel layer instance to use as path.to.module:instance.path',
+ )
+
+ self.server = None
+
+ @classmethod
+ def entrypoint(cls):
+ """
+ Main entrypoint for external starts.
+ """
+ cls().run(sys.argv[1:])
+
+ def run(self, args):
+ """
+ Pass in raw argument list and it will decode them
+ and run the server.
+ """
+ # Decode args
+ args = self.parser.parse_args(args)
+ # Set up logging
+ logging.basicConfig(
+ level={
+ 0: logging.WARN,
+ 1: logging.INFO,
+ 2: logging.DEBUG,
+ }[args.verbosity],
+ format="%(asctime)-15s %(levelname)-8s %(message)s",
+ )
+ # If verbosity is 1 or greater, or they told us explicitly, set up access log
+ access_log_stream = None
+ if args.access_log:
+ if args.access_log == "-":
+ access_log_stream = sys.stdout
+ else:
+ access_log_stream = open(args.access_log, "a", 1)
+ elif args.verbosity >= 1:
+ access_log_stream = sys.stdout
+ # Import channel layer
+ sys.path.insert(0, ".")
+ module_path, object_path = args.channel_layer.split(":", 1)
+ channel_layer = importlib.import_module(module_path)
+ for bit in object_path.split("."):
+ channel_layer = getattr(channel_layer, bit)
+
+ if not any([args.host, args.port, args.unix_socket, args.file_descriptor, args.socket_strings]):
+ # no advanced binding options passed, patch in defaults
+ args.host = DEFAULT_HOST
+ args.port = DEFAULT_PORT
+ elif args.host and not args.port:
+ args.port = DEFAULT_PORT
+ elif args.port and not args.host:
+ args.host = DEFAULT_HOST
+
+ # build endpoint description strings from (optional) cli arguments
+ endpoints = build_endpoint_description_strings(
+ host=args.host,
+ port=args.port,
+ unix_socket=args.unix_socket,
+ file_descriptor=args.file_descriptor
+ )
+ endpoints = sorted(
+ args.socket_strings + endpoints
+ )
+ logger.info(
+ 'Starting server at %s, channel layer %s.' %
+ (', '.join(endpoints), args.channel_layer)
+ )
+
+ self.server = Server(
+ channel_layer=channel_layer,
+ endpoints=endpoints,
+ http_timeout=args.http_timeout,
+ ping_interval=args.ping_interval,
+ ping_timeout=args.ping_timeout,
+ websocket_timeout=args.websocket_timeout,
+ websocket_connect_timeout=args.websocket_connect_timeout,
+ action_logger=AccessLogGenerator(access_log_stream) if access_log_stream else None,
+ ws_protocols=args.ws_protocols,
+ root_path=args.root_path,
+ verbosity=args.verbosity,
+ proxy_forwarded_address_header='X-Forwarded-For' if args.proxy_headers else None,
+ proxy_forwarded_port_header='X-Forwarded-Port' if args.proxy_headers else None,
+ force_sync=args.force_sync,
+ )
+ self.server.run()
diff --git a/daphne/http_protocol.py b/daphne/http_protocol.py
new file mode 100755
index 0000000..32dc276
--- /dev/null
+++ b/daphne/http_protocol.py
@@ -0,0 +1,449 @@
+from __future__ import unicode_literals
+
+import logging
+import random
+import six
+import string
+import time
+import traceback
+
+from zope.interface import implementer
+
+from six.moves.urllib_parse import unquote, unquote_plus
+from twisted.internet.interfaces import IProtocolNegotiationFactory
+from twisted.protocols.policies import ProtocolWrapper
+from twisted.web import http
+
+from .utils import parse_x_forwarded_for
+from .ws_protocol import WebSocketProtocol, WebSocketFactory
+
+logger = logging.getLogger(__name__)
+
+
+class WebRequest(http.Request):
+ """
+ Request that either hands off information to channels, or offloads
+ to a WebSocket class.
+
+ Does some extra processing over the normal Twisted Web request to separate
+ GET and POST out.
+ """
+
+ error_template = """
+ <html>
+ <head>
+ <title>%(title)s</title>
+ <style>
+ body { font-family: sans-serif; margin: 0; padding: 0; }
+ h1 { padding: 0.6em 0 0.2em 20px; color: #896868; margin: 0; }
+ p { padding: 0 0 0.3em 20px; margin: 0; }
+ footer { padding: 1em 0 0.3em 20px; color: #999; font-size: 80%%; font-style: italic; }
+ </style>
+ </head>
+ <body>
+ <h1>%(title)s</h1>
+ <p>%(body)s</p>
+ <footer>Daphne</footer>
+ </body>
+ </html>
+ """.replace("\n", "").replace(" ", " ").replace(" ", " ").replace(" ", " ") # Shorten it a bit, bytes wise
+
+ def __init__(self, *args, **kwargs):
+ try:
+ http.Request.__init__(self, *args, **kwargs)
+ # Easy factory link
+ self.factory = self.channel.factory
+ # Make a name for our reply channel
+ self.reply_channel = self.factory.make_send_channel()
+ # Tell factory we're that channel's client
+ self.last_keepalive = time.time()
+ self.factory.reply_protocols[self.reply_channel] = self
+ self._got_response_start = False
+ except Exception:
+ logger.error(traceback.format_exc())
+ raise
+
+ def process(self):
+ try:
+ self.request_start = time.time()
+ # Get upgrade header
+ upgrade_header = None
+ if self.requestHeaders.hasHeader(b"Upgrade"):
+ upgrade_header = self.requestHeaders.getRawHeaders(b"Upgrade")[0]
+ # Get client address if possible
+ if hasattr(self.client, "host") and hasattr(self.client, "port"):
+ # client.host and host.host are byte strings in Python 2, but spec
+ # requires unicode string.
+ self.client_addr = [six.text_type(self.client.host), self.client.port]
+ self.server_addr = [six.text_type(self.host.host), self.host.port]
+ else:
+ self.client_addr = None
+ self.server_addr = None
+
+ if self.factory.proxy_forwarded_address_header:
+ self.client_addr = parse_x_forwarded_for(
+ self.requestHeaders,
+ self.factory.proxy_forwarded_address_header,
+ self.factory.proxy_forwarded_port_header,
+ self.client_addr
+ )
+
+ # Check for unicodeish path (or it'll crash when trying to parse)
+ try:
+ self.path.decode("ascii")
+ except UnicodeDecodeError:
+ self.path = b"/"
+ self.basic_error(400, b"Bad Request", "Invalid characters in path")
+ return
+ # Calculate query string
+ self.query_string = b""
+ if b"?" in self.uri:
+ self.query_string = self.uri.split(b"?", 1)[1]
+ # Is it WebSocket? IS IT?!
+ if upgrade_header and upgrade_header.lower() == b"websocket":
+ # Make WebSocket protocol to hand off to
+ protocol = self.factory.ws_factory.buildProtocol(self.transport.getPeer())
+ if not protocol:
+ # If protocol creation fails, we signal "internal server error"
+ self.setResponseCode(500)
+ logger.warn("Could not make WebSocket protocol")
+ self.finish()
+ # Give it the raw query string
+ protocol._raw_query_string = self.query_string
+ # Port across transport
+ protocol.set_main_factory(self.factory)
+ transport, self.transport = self.transport, None
+ if isinstance(transport, ProtocolWrapper):
+ # i.e. TLS is a wrapping protocol
+ transport.wrappedProtocol = protocol
+ else:
+ transport.protocol = protocol
+ protocol.makeConnection(transport)
+ # Re-inject request
+ data = self.method + b' ' + self.uri + b' HTTP/1.1\x0d\x0a'
+ for h in self.requestHeaders.getAllRawHeaders():
+ data += h[0] + b': ' + b",".join(h[1]) + b'\x0d\x0a'
+ data += b"\x0d\x0a"
+ data += self.content.read()
+ protocol.dataReceived(data)
+ # Remove our HTTP reply channel association
+ if hasattr(protocol, "reply_channel"):
+ logger.debug("Upgraded connection %s to WebSocket %s", self.reply_channel, protocol.reply_channel)
+ else:
+ logger.debug("Connection %s did not get successful WS handshake.", self.reply_channel)
+ del self.factory.reply_protocols[self.reply_channel]
+ self.reply_channel = None
... 2420 lines suppressed ...
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-daphne.git
More information about the Python-modules-commits
mailing list