Navigation Menu

Skip to content

Commit

Permalink
Initial working Hypercorn
Browse files Browse the repository at this point in the history
This takes the serving code from Quart 0.5.0 and turns it into a
working ASGI server. This will then allow Quart to be an ASGI
Framework with Hypercorn as the ASGI Server. This in turn allows
for a richer asyncio based web ecosystem, as different frameworks
can be paired with differing servers.

The name Hypercorn has been chosen as the code is built on the
excellent sans-io Hyper libraries, h11, h2, and wsproto whilst being
inspired by Gunicorn.
  • Loading branch information
pgjones committed May 27, 2018
0 parents commit b0ccb73
Show file tree
Hide file tree
Showing 42 changed files with 2,540 additions and 0 deletions.
14 changes: 14 additions & 0 deletions .gitignore
@@ -0,0 +1,14 @@
*~
venv/
__pycache__/
Hypercorn.egg-info/
.cache/
.tox/
TODO
.mypy_cache/
.pytest_cache/
.hypothesis/
docs/_build/
docs/source/

.coverage
41 changes: 41 additions & 0 deletions .gitlab-ci.yml
@@ -0,0 +1,41 @@
image: python:3.6.4-stretch

tox:
script:
- pip install tox
- tox

pages:
script:
- pip install sphinx
- python setup.py install
- rm -rf docs/source && sphinx-apidoc -e -f -o docs/source/ hypercorn/
- cd docs/ && make html && cd ../
- mv docs/_build/html/ public/
artifacts:
paths:
- public
only:
- master

h2spec:
script:
- python setup.py install
- cd compliance/h2spec && nohup python server.py &
- wget https://github.com/summerwind/h2spec/releases/download/v2.1.0/h2spec_linux_amd64.tar.gz
- tar -xvf h2spec_linux_amd64.tar.gz
- sleep 10
- ./h2spec -tk -h 127.0.0.1 -p 5000

autobahn:
image: python:2.7.14-alpine3.7
script:
- apk --update add build-base libressl libressl-dev ca-certificates libffi-dev python3
- pip install autobahntestsuite
- python3 setup.py install
- cd compliance/autobahn && nohup python3 server.py &
- while ! netstat -l -t | grep -q 5000; do sleep 1; done
- cd compliance/autobahn && wstest -m fuzzingclient && python summarise.py
artifacts:
paths:
- compliance/autobahn/reports/servers/
4 changes: 4 additions & 0 deletions CHANGELOG.rst
@@ -0,0 +1,4 @@
0.1.0
-----

* Released initial pre alpha version.
22 changes: 22 additions & 0 deletions LICENSE
@@ -0,0 +1,22 @@
Copyright P G Jones 2018.

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
10 changes: 10 additions & 0 deletions MANIFEST.in
@@ -0,0 +1,10 @@
include CHANGELOG.rst
include LICENSE
include README.rst
recursive-include hypercorn *.py
recursive-include tests *.py
recursive-exclude compliance *
recursive-exclude docs *
exclude .gitlab-ci.yml
exclude compliance
exclude docs
82 changes: 82 additions & 0 deletions README.rst
@@ -0,0 +1,82 @@
Hypercorn
=========

|Build Status| |docs| |pypi| |http| |python| |license|

Hypercorn is an `ASGI
<https://github.com/django/asgiref/blob/master/specs/asgi.rst>`_ web
server based on the sans-io hyper libraries and inspired by
Gunicorn. Hypercorn supports HTTP/1, HTTP/2, and websockets and the
ASGI 2 specification.

Hypercorn was initially part of `Quart
<https://gitlab.com/pgjones/quart>`_ before being separated out into a
standalone ASGI server. Hypercorn forked from version 0.5.0 of Quart.

Quickstart
----------

Hypercorn can be installed via `pipenv
<https://docs.pipenv.org/install/#installing-packages-for-your-project>`_ or
`pip <https://docs.python.org/3/installing/index.html>`_,

.. code-block:: console
$ pipenv install hypercorn
$ pip install hypercorn
and requires Python 3.6.1 or higher.

With hypercorn installed ASGI frameworks (or apps) can be served via
Hypercorn via the command line,

.. code-block:: console
$ hypercorn module:app
Contributing
------------

Hypercorn is developed on `GitLab
<https://gitlab.com/pgjones/hypercorn>`_. You are very welcome to open
`issues <https://gitlab.com/pgjones/hypercorn/issues>`_ or propose
`merge requests
<https://gitlab.com/pgjones/hypercorn/merge_requests>`_.

Testing
~~~~~~~

The best way to test Hypercorn is with Tox,

.. code-block:: console
$ pipenv install tox
$ tox
this will check the code style and run the tests.

Help
----

The Hypercorn `documentation <https://pgjones.gitlab.io/hypercorn/>`_
is the best place to start, after that try opening an `issue
<https://gitlab.com/pgjones/hypercorn/issues>`_.


.. |Build Status| image:: https://gitlab.com/pgjones/hypercorn/badges/master/build.svg
:target: https://gitlab.com/pgjones/hypercorn/commits/master

.. |docs| image:: https://img.shields.io/badge/docs-passing-brightgreen.svg
:target: https://pgjones.gitlab.io/hypercorn/

.. |pypi| image:: https://img.shields.io/pypi/v/hypercorn.svg
:target: https://pypi.python.org/pypi/Hypercorn/

.. |http| image:: https://img.shields.io/badge/http-1.0,1.1,2-orange.svg
:target: https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol

.. |python| image:: https://img.shields.io/pypi/pyversions/hypercorn.svg
:target: https://pypi.python.org/pypi/Hypercorn/

.. |license| image:: https://img.shields.io/badge/license-MIT-blue.svg
:target: https://gitlab.com/pgjones/hypercorn/blob/master/LICENSE
10 changes: 10 additions & 0 deletions compliance/autobahn/fuzzingclient.json
@@ -0,0 +1,10 @@
{
"options": {"failByDrop": false},
"outdir": "./reports/servers",

"servers": [{"agent": "websockets", "url": "ws://localhost:5000/", "options": {"version": 18}}],

"cases": ["*"],
"exclude-cases": ["12.*", "13.*"],
"exclude-agent-cases": {}
}
27 changes: 27 additions & 0 deletions compliance/autobahn/server.py
@@ -0,0 +1,27 @@
from hypercorn.config import Config
from hypercorn.run import run_single


class App:

def __init__(self, scope):
pass

async def __call__(self, receive, send):
while True:
event = await receive()
if event['type'] == 'websocket.disconnect':
break
elif event['type'] == 'websocket.connect':
await send({'type': 'websocket.accept'})
elif event['type'] == 'websocket.receive':
await send({
'type': 'websocket.send',
'bytes': event['bytes'],
'text': event['text'],
})


if __name__ == '__main__':
config = Config()
run_single(App, config)
12 changes: 12 additions & 0 deletions compliance/autobahn/summarise.py
@@ -0,0 +1,12 @@
import json
import sys

with open('reports/servers/index.json') as file_:
report = json.load(file_)

failures = sum(value['behavior'] == 'FAILED' for value in report['websockets'].values())

if failures > 0:
sys.exit(1)
else:
sys.exit(0)
27 changes: 27 additions & 0 deletions compliance/h2spec/cert.pem
@@ -0,0 +1,27 @@
-----BEGIN CERTIFICATE-----
MIIEljCCAn4CCQCri/HA0BA3TDANBgkqhkiG9w0BAQsFADANMQswCQYDVQQGEwJV
SzAeFw0xNzEyMjgxNjQyMTNaFw0xODEyMjgxNjQyMTNaMA0xCzAJBgNVBAYTAlVL
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuwrDLc6o0Ctfc0NYzIUq
xE+D+4Gc5u+Is3/wIjGYdzMdEdu1x/cnFAbLYp9AGC5Api1w4ntHGcZENRI+aJ6D
8Ev4FlqdVtlbcyNO9MqQw4GUTsidVhoZzU5N9IEDs5ZPbO6blSHoxSxatllqgJs/
8ws+E6ED7F6sp3bwrsEEYSvbddJ5L/7T8I88IqpZ0dlNeNl7q1tD2x4ea63RvyL8
MKnnhnBa7G/UnjidDqf/mzVF2fOOqr6PFrQa2TOdKiZEXwnlZSYc/nZD7cC2KVZE
+6gv7QbZA1vD8vXzWGjAhQODr3l7xuAug1kA0CZgnQXIS9UaedS5dNm5Go79d97Q
+fE4u2ZdE1RTpbGTzSsPKMZFvFQrdi0UKFCp0+QxV8wyZ32arSjyQCaSBt6rKCpm
zAfK5GstiKWM21gRwfsuo8oKSNE3VI7zfcSlxwRD9Ns3SwvIKo2Xr0K1K0aIOXgD
P85cjAq3OJsWizZ1BhfN3f5pq0TQAojjj4q71y1t4mkPYShEygBY3wX6av2KtuSz
Q4/ARC6pmnclL5tYFICOFuPCPz5m7/coIhhF21tgeDSLhNJ7PgVtp+n+1XWfCTLD
GpW7nURNIy2fQqA2cBpYIbPlFx+mgJwlqsg55XFqVoUHpY6/HftuI4Zm/LRx8/fe
mYNyIalJ3P+4iMFhd2DGp10CAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAbHVdOrc1
XZ8T6kvvbbSYtf6CQsIbTBmIcJ3Ply7LVMs8B5uBSe1pzM2hZGKbrpbs9ZmPRJiH
vl2m3UeDW+N12xkjgY/S6BCj95JotxQVTt5OlbZLuQ4a3BEFgX6oZO7dN7JA0XLx
bfUMCvGFR+Nkx6bTnCzcxPtbBiOL4ZymRaoM8NFd5jTybRlkCOFOg4HewmmZ/kif
cN0qFZekcDsHcqBUz3HVGNc1MGMRGvRcFyMOCq9t5FF2Zoa0AoExFdjotHqc9tLm
VdGey5me38T0PaPIqO870b43ZDdL6QFO5LvEo8ca78fVGnUksLxJ46n8C5ivdztY
+kdH3Lbp8OmA3ZodGq5okSxVFn3eTa19epOuKz5NietCfpsfmXiIvbIrgZO0u114
5QiJZ5tJtbOPexi8Jzy3vDNK1wAO7RXQ5JrNqogz9emjBL1PAHqNMAfEJnb5/oro
eqj45w/qQmoDqblK8Q8U+lSFpVq82e76m5YItvMe1VmLmPGUoq4JFSpjzmzMlW+w
o3g+4RW9LsvolFMdQbpi1XV1LsPQuaJxczVOR74DJHbGeuE61tCxjx9lpaWAJJVg
PbPHGc1WBSnMb/nCloYUN7UU0wqzOUx3yqvveP8w2Fb8pv3wEqHY/cGu1+pHSn5M
x3Xnu9MXCkttgGltMoWgUuiz55iLqAzkOQY=
-----END CERTIFICATE-----
52 changes: 52 additions & 0 deletions compliance/h2spec/key.pem
@@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
/h39p3qBT+YHuuM7DwN83zVLIgyBzw==
-----END PRIVATE KEY-----
39 changes: 39 additions & 0 deletions compliance/h2spec/server.py
@@ -0,0 +1,39 @@
import ssl

from hypercorn.config import Config
from hypercorn.run import run_single


class App:

def __init__(self, scope):
pass

async def __call__(self, receive, send):
while True:
event = await receive()
if event['type'] == 'http.disconnect':
break
elif event['type'] == 'http.request' and not event.get('more_body', False):
await send({
'type': 'http.response.start',
'status': 200,
'headers': [(b'content-length', b'5')],
})
await send({
'type': 'http.response.body',
'body': b'Hello',
'more_body': False,
})
break


if __name__ == '__main__':
ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ssl_context.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 | ssl.OP_NO_COMPRESSION
ssl_context.set_ciphers('ECDHE+AESGCM')
ssl_context.load_cert_chain(certfile='cert.pem', keyfile='key.pem')
config = Config()
config.ssl = ssl_context
config.debug = True
run_single(App, config)
20 changes: 20 additions & 0 deletions docs/Makefile
@@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#

# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = python -msphinx
SPHINXPROJ = Hypercorn
SOURCEDIR = .
BUILDDIR = _build

# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
8 changes: 8 additions & 0 deletions docs/api.rst
@@ -0,0 +1,8 @@
API Reference
=============

.. toctree::
:maxdepth: 2
:caption: Contents:

source/modules.rst

0 comments on commit b0ccb73

Please sign in to comment.