|
1 | 1 | from hashlib import md5 |
| 2 | +import os |
| 3 | +import ssl |
2 | 4 | import unittest |
3 | 5 |
|
4 | 6 | from tornado.escape import utf8 |
5 | | -from tornado.testing import AsyncHTTPTestCase |
| 7 | +from tornado.netutil import ssl_options_to_context |
6 | 8 | from tornado.test import httpclient_test |
| 9 | +from tornado.testing import AsyncHTTPSTestCase, AsyncHTTPTestCase |
7 | 10 | from tornado.web import Application, RequestHandler |
8 | 11 |
|
9 | | - |
10 | 12 | try: |
11 | 13 | import pycurl |
12 | 14 | except ImportError: |
@@ -123,3 +125,98 @@ def test_digest_auth_non_ascii(self): |
123 | 125 | auth_password="barユ£", |
124 | 126 | ) |
125 | 127 | self.assertEqual(response.body, b"ok") |
| 128 | + |
| 129 | + |
| 130 | +class ProxyAuthEchoHandler(RequestHandler): |
| 131 | + def get(self): |
| 132 | + if self.request.headers.get("Proxy-Authorization", None) is not None: |
| 133 | + self.write(f"proxy auth: {self.request.headers['Proxy-Authorization']}") |
| 134 | + else: |
| 135 | + self.write("no proxy auth") |
| 136 | + |
| 137 | + |
| 138 | +@unittest.skipIf(pycurl is None, "pycurl module not present") |
| 139 | +class CurlHTTPClientReuseProxyAuthTestCase(AsyncHTTPTestCase): |
| 140 | + def get_app(self): |
| 141 | + # Note that we don't properly support proxy-style requests, but it works well enough |
| 142 | + # for this test if we start the url matcher with a wildcard. |
| 143 | + return Application([(".*/proxy_auth", ProxyAuthEchoHandler)]) |
| 144 | + |
| 145 | + def get_http_client(self): |
| 146 | + # max_clients=1 forces us to reuse curl "easy handles". This is a regression test for |
| 147 | + # a bug in which proxy credentials were not cleared between requests. |
| 148 | + return CurlAsyncHTTPClient( |
| 149 | + force_instance=True, |
| 150 | + defaults=dict( |
| 151 | + allow_ipv6=False, |
| 152 | + ), |
| 153 | + max_clients=1, |
| 154 | + ) |
| 155 | + |
| 156 | + def test_reuse_proxy_credentials(self): |
| 157 | + # Proxy credentials used on one request should not be automatically reused |
| 158 | + # by another request. |
| 159 | + response = self.fetch( |
| 160 | + "/proxy_auth", |
| 161 | + proxy_host="127.0.0.1", |
| 162 | + proxy_port=self.get_http_port(), |
| 163 | + proxy_username="foo", |
| 164 | + proxy_password="bar", |
| 165 | + ) |
| 166 | + self.assertEqual(response.body, b"proxy auth: Basic Zm9vOmJhcg==") |
| 167 | + response = self.fetch( |
| 168 | + "/proxy_auth", |
| 169 | + proxy_host="127.0.0.1", |
| 170 | + proxy_port=self.get_http_port(), |
| 171 | + ) |
| 172 | + self.assertEqual(response.body, b"no proxy auth") |
| 173 | + |
| 174 | + |
| 175 | +class ClientCertEchoHandler(RequestHandler): |
| 176 | + def get(self): |
| 177 | + cert = self.request.get_ssl_certificate() |
| 178 | + if cert is not None: |
| 179 | + assert isinstance(cert, dict) |
| 180 | + self.write(f"client cert: {cert['subject']}") |
| 181 | + else: |
| 182 | + self.write("no client cert") |
| 183 | + |
| 184 | + |
| 185 | +@unittest.skipIf(pycurl is None, "pycurl module not present") |
| 186 | +class CurlHTTPClientReuseCertsTestCase(AsyncHTTPSTestCase): |
| 187 | + def get_app(self): |
| 188 | + return Application([(".*/client_cert", ClientCertEchoHandler)]) |
| 189 | + |
| 190 | + def get_http_client(self): |
| 191 | + return CurlAsyncHTTPClient( |
| 192 | + force_instance=True, |
| 193 | + defaults=dict( |
| 194 | + allow_ipv6=False, |
| 195 | + validate_cert=False, |
| 196 | + ), |
| 197 | + max_clients=1, |
| 198 | + ) |
| 199 | + |
| 200 | + def get_httpserver_options(self): |
| 201 | + ssl_ctx = ssl_options_to_context(self.get_ssl_options(), server_side=True) |
| 202 | + ssl_ctx.verify_mode = ssl.CERT_OPTIONAL |
| 203 | + return dict(ssl_options=ssl_ctx) |
| 204 | + |
| 205 | + def get_ssl_options(self): |
| 206 | + opts = super().get_ssl_options() |
| 207 | + opts["ca_certs"] = os.path.join(os.path.dirname(__file__), "test.crt") |
| 208 | + return opts |
| 209 | + |
| 210 | + def test_reuse_certs(self): |
| 211 | + # Client certs used on one request should not be automatically reused |
| 212 | + # by another request. |
| 213 | + response = self.fetch( |
| 214 | + self.get_url("/client_cert"), |
| 215 | + client_cert=os.path.join(os.path.dirname(__file__), "test.crt"), |
| 216 | + client_key=os.path.join(os.path.dirname(__file__), "test.key"), |
| 217 | + ) |
| 218 | + self.assertEqual( |
| 219 | + response.body, b"client cert: ((('commonName', 'foo.example.com'),),)" |
| 220 | + ) |
| 221 | + response = self.fetch(self.get_url("/client_cert")) |
| 222 | + self.assertEqual(response.body, b"no client cert") |
0 commit comments