aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/pytest-localserver/py3/pytest_localserver/https.py
blob: 856e222ac5c2db55454c50a590b02d1e70ee308b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# Copyright (C) 2010-2013 Sebastian Rahlf and others (see AUTHORS).
#
# This program is release under the MIT license. You can find the full text of
# the license in the LICENSE file.
import os.path

from pytest_localserver.http import ContentServer

#: default server certificate
DEFAULT_CERTIFICATE = os.path.join(os.getcwd(), "server.pem")


class SecureContentServer(ContentServer):

    """
    Small test server which works just like :class:`http.Server` over HTTP::

        server = SecureContentServer(
            port=8080, key='/srv/my.key', cert='my.certificate')
        server.start()
        print 'Test server running at %s' % server.url
        server.serve_content(open('/path/to/some.file').read())
        # any call to https://localhost:8080 will get the contents of
        # /path/to/some.file as a response.

    To avoid *ssl handshake failures* you can import the `pytest-localserver
    CA`_ into your browser of choice.

    How to create a self-signed certificate
    ---------------------------------------

    If you want to create your own server certificate, you need `OpenSSL`_
    installed on your machine. A self-signed certificate consists of a
    certificate and a private key for your server. It can be created with
    a command like this, using OpenSSL 1.1.1::

        openssl req \
            -x509 \
            -newkey rsa:4096 \
            -sha256 \
            -days 3650 \
            -nodes \
            -keyout server.pem \
            -out server.pem \
            -subj "/CN=127.0.0.1/O=pytest-localserver/OU=Testing Dept." \
            -addext "subjectAltName=DNS:localhost"

    Note that both key and certificate are in a single file now named
    ``server.pem``.

    How to create your own Certificate Authority
    --------------------------------------------

    Generate a server key and request for signing (csr). Make sure that the
    common name (CN) is your IP address/domain name (e.g. ``localhost``). ::

        openssl genpkey \
            -algorithm RSA \
            -pkeyopt rsa_keygen_bits:4096 \
            -out server.key
        openssl req \
            -new \
            -addext "subjectAltName=DNS:localhost" \
            -key server.key \
            -out server.csr

    Generate your own CA. Make sure that this time the CN is *not* your IP
    address/domain name (e.g. ``localhost CA``). ::

        openssl genpkey \
            -algorithm RSA \
            -pkeyopt rsa_keygen_bits:4096 \
            -aes256 \
            -out ca.key
        openssl req \
            -new \
            -x509 \
            -key ca.key \
            -out ca.crt

    Sign the certificate signing request (csr) with the self-created CA that
    you made earlier. Note that OpenSSL does not copy the subjectAltName field
    from the request (csr), so you have to provide it again as a file. If you
    issue subsequent certificates and your browser already knows about previous
    ones simply increment the serial number. ::

        echo "subjectAltName=DNS:localhost" >server-extensions.txt
        openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
            -set_serial 01 -extfile server-extensions.txt -out server.crt

    Create a single file for both key and certificate::

        cat server.key server.crt > server.pem

    Now you only need to import ``ca.crt`` as a CA in your browser.

    Want to know more?
    ------------------

    This information was compiled from the following sources, which you might
    find helpful if you want to dig deeper into `pyOpenSSH`_, certificates and
    CAs:

    - http://code.activestate.com/recipes/442473/
    - http://www.tc.umn.edu/~brams006/selfsign.html
    -

    A more advanced tutorial can be found `here`_.

    .. _pytest-localserver CA: https://raw.githubusercontent.com/pytest-dev/pytest-localserver/master/pytest_localserver/ca.crt  # noqa: E501
    .. _pyOpenSSH: https://launchpad.net/pyopenssl
    """

    def __init__(self, host="localhost", port=0, key=DEFAULT_CERTIFICATE, cert=DEFAULT_CERTIFICATE):
        """
        :param key: location of file containing the server private key.
        :param cert: location of file containing server certificate.
        """

        super().__init__(host, port, ssl_context=(key, cert))


if __name__ == "__main__":  # pragma: no cover

    import sys
    import time

    print("Using certificate %s." % DEFAULT_CERTIFICATE)

    server = SecureContentServer()
    server.start()
    server.logging = True

    print("HTTPS server is running at %s" % server.url)
    print("Type <Ctrl-C> to stop")

    try:
        path = sys.argv[1]
    except IndexError:
        path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "README.rst")

    server.serve_content(open(path).read(), 302)

    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        print("\rstopping...")
    server.stop()