tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

eventlet-server.py (2851B)


      1 # -*- coding: utf-8 -*-
      2 """
      3 eventlet-server.py
      4 ~~~~~~~~~~~~~~~~~~
      5 
      6 A fully-functional HTTP/2 server written for Eventlet.
      7 """
      8 import collections
      9 import json
     10 
     11 import eventlet
     12 
     13 from eventlet.green.OpenSSL import SSL, crypto
     14 from h2.config import H2Configuration
     15 from h2.connection import H2Connection
     16 from h2.events import RequestReceived, DataReceived
     17 
     18 
     19 class ConnectionManager(object):
     20    """
     21    An object that manages a single HTTP/2 connection.
     22    """
     23    def __init__(self, sock):
     24        config = H2Configuration(client_side=False)
     25        self.sock = sock
     26        self.conn = H2Connection(config=config)
     27 
     28    def run_forever(self):
     29        self.conn.initiate_connection()
     30        self.sock.sendall(self.conn.data_to_send())
     31 
     32        while True:
     33            data = self.sock.recv(65535)
     34            if not data:
     35                break
     36 
     37            events = self.conn.receive_data(data)
     38 
     39            for event in events:
     40                if isinstance(event, RequestReceived):
     41                    self.request_received(event.headers, event.stream_id)
     42                elif isinstance(event, DataReceived):
     43                    self.conn.reset_stream(event.stream_id)
     44 
     45            self.sock.sendall(self.conn.data_to_send())
     46 
     47    def request_received(self, headers, stream_id):
     48        headers = collections.OrderedDict(headers)
     49        data = json.dumps({'headers': headers}, indent=4).encode('utf-8')
     50 
     51        response_headers = (
     52            (':status', '200'),
     53            ('content-type', 'application/json'),
     54            ('content-length', str(len(data))),
     55            ('server', 'eventlet-h2'),
     56        )
     57        self.conn.send_headers(stream_id, response_headers)
     58        self.conn.send_data(stream_id, data, end_stream=True)
     59 
     60 
     61 def alpn_callback(conn, protos):
     62    if b'h2' in protos:
     63        return b'h2'
     64 
     65    raise RuntimeError("No acceptable protocol offered!")
     66 
     67 
     68 def npn_advertise_cb(conn):
     69    return [b'h2']
     70 
     71 
     72 # Let's set up SSL. This is a lot of work in PyOpenSSL.
     73 options = (
     74    SSL.OP_NO_COMPRESSION |
     75    SSL.OP_NO_SSLv2 |
     76    SSL.OP_NO_SSLv3 |
     77    SSL.OP_NO_TLSv1 |
     78    SSL.OP_NO_TLSv1_1
     79 )
     80 context = SSL.Context(SSL.SSLv23_METHOD)
     81 context.set_options(options)
     82 context.set_verify(SSL.VERIFY_NONE, lambda *args: True)
     83 context.use_privatekey_file('server.key')
     84 context.use_certificate_file('server.crt')
     85 context.set_npn_advertise_callback(npn_advertise_cb)
     86 context.set_alpn_select_callback(alpn_callback)
     87 context.set_cipher_list(
     88    "ECDHE+AESGCM"
     89 )
     90 context.set_tmp_ecdh(crypto.get_elliptic_curve(u'prime256v1'))
     91 
     92 server = eventlet.listen(('0.0.0.0', 443))
     93 server = SSL.Connection(context, server)
     94 pool = eventlet.GreenPool()
     95 
     96 while True:
     97    try:
     98        new_sock, _ = server.accept()
     99        manager = ConnectionManager(new_sock)
    100        pool.spawn_n(manager.run_forever)
    101    except (SystemExit, KeyboardInterrupt):
    102        break