[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