mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge pull request #8421 from francoisfreitag/test-self-signed
linkcheck: Verify behavior with self-signed certs
This commit is contained in:
commit
a638658510
50
tests/certs/cert.pem
Normal file
50
tests/certs/cert.pem
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDLbT8rECMOtm7H
|
||||||
|
6KwFnCiS2kUjZCy/3yJGpIeGoTLXP+N8a1LUUIX3MJ8qu1vsPK6Yz1h/+Xiv73xy
|
||||||
|
WjwK6Di17q35VyYVYiRjuhIsiH1dwwYfNE/hO3QhLOEtBK7+BTWUQ36K+wRFBf/Y
|
||||||
|
VynHh7EvKawCWSmaZVyqjj2XS83mp1A8Jt0MTA4A4RyhV4aS16VviSeRf3OcVd69
|
||||||
|
qtvf3MClrDFRyFnCIE6ajfp1hrZ5wC7Fv5d0A0dKJ2MGYoblUPT/qq+b8jn1nxPv
|
||||||
|
uSHo4XhjWXq+k05idIl7mLk7wurmgHjmslB0/ZdE4wNa6oANLevF4vxiCGJ6c9gf
|
||||||
|
8Q56gaUZAgMBAAECggEAXoXguC3DXG7AgvtGE0VARRxOy+ccM/uGfbStlI0KhqIV
|
||||||
|
HhbwYd8YoIdjLgPo7pgzuKV/xdcxkO6CsM/k3lyRHVhOVnF8LKtxpTUshKzXM94O
|
||||||
|
1ikEhO+PQmsMJlLqzPW2s7G49vM0RK5I90lpDGGsnvGKD31Gq4s1x7pYPBjpD6cS
|
||||||
|
LBzbvPgrrfUNgLGeaIyyJDJSyBrSaNbtHTDjtF/i72KSNUEIfRepCm8IQYrvnEYU
|
||||||
|
E8+fzJNvDz2qF1lA58AkkaDznCLTa9pV1DEFwvXJ3N4nN+CZdsZ9jmrSPQ4uTcJG
|
||||||
|
bSU9jPrfFvsP7nQJHx66uD5qEy/SWqDfN7pDqsgFkQKBgQD1TWxI0CihfxM/z0mu
|
||||||
|
V6nkaYBNtuJxllFu1azJpMBAqu/40GW4EtEq6s2lEig/6ct2+bLZcT+u6DVUHeGv
|
||||||
|
DzA3Wu8mrzIChT7+4c080CMBKX8zcADK1IpeTV5DMR1sGgUV+OqYG7z8Vz+PyfMs
|
||||||
|
UzR1dCGjcdAcRFVg6nlIeDAlBwKBgQDUTFHvHMyPhzY+jwKFHxMIIvMaZZQ1z7uN
|
||||||
|
Oks0DHl7yD+4qH/fRWJBS0YtxWXIuA7xnnz1MMWVEJAXrR/CvnrBuyYWYhZnziU4
|
||||||
|
rWwqEK1o+sTS8QY+5NcQ4Hrzmszx3qtNjBBUwudkmwiCP5b9eyk68Xj2mbdNjhfK
|
||||||
|
xCIqPc583wKBgHk5KLEXBW1Bwj5/btcUhWXWaUx+e4tMkLOoLrp7i3Kpxut7+Tit
|
||||||
|
O+bsoHHZ9kAXhrAmF6dzWthR8sC9/6Cmbdp9OsAwRhOOy6Hj7qwF47aYTj8aM5oI
|
||||||
|
zNRrgZDM/dBFT4wbNbuzwYImj8e8MksOV1dP66u8++5sKpE5bnRMyOYTAoGACkZ3
|
||||||
|
YL9gF0JQGc8KLC9I2If4hDqOZdxcE4XSxf4kkx0qGGHvbnsJOmfOScDYIFLoRkGJ
|
||||||
|
gsSNi511m+/BLcfSYTYRrdupgfS0UH30UkTkX8RjamJIDxs8XZC/4rKHYN2KJQK2
|
||||||
|
d6PHV1M5ojQ5tqMTZ8rwM99Uw+gwtpuvm6PKLrkCgYAxlz2Aw4VWDuHhGOYM3htw
|
||||||
|
6mdU0kcC9oupQGFbWrw6S8kJhSN3cS2e3ZtIfy841fFvi0zNIxPNW25CiP+aplZ3
|
||||||
|
EFUTEUpVg7OQP57KBRcztp2N5FZUKSwq/FUOuxJWCxIYe/nsqQ7Xet5cxRaK7vb4
|
||||||
|
hFJQbfh2LfWCK7O+kpXXtg==
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDozCCAougAwIBAgIUfuExHknCb9E7HvWowDBr/pvOcx8wDQYJKoZIhvcNAQEL
|
||||||
|
BQAwYTELMAkGA1UEBhMCRlIxETAPBgNVBAgMCEJyZXRhZ25lMQ8wDQYDVQQHDAZS
|
||||||
|
ZW5uZXMxGjAYBgNVBAoMEVNwaGlueCB0ZXN0IHN1aXRlMRIwEAYDVQQDDAlsb2Nh
|
||||||
|
bGhvc3QwHhcNMjAxMTE0MTE0MzQ3WhcNMzAxMTEyMTE0MzQ3WjBhMQswCQYDVQQG
|
||||||
|
EwJGUjERMA8GA1UECAwIQnJldGFnbmUxDzANBgNVBAcMBlJlbm5lczEaMBgGA1UE
|
||||||
|
CgwRU3BoaW54IHRlc3Qgc3VpdGUxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJ
|
||||||
|
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMttPysQIw62bsforAWcKJLaRSNkLL/f
|
||||||
|
Ikakh4ahMtc/43xrUtRQhfcwnyq7W+w8rpjPWH/5eK/vfHJaPAroOLXurflXJhVi
|
||||||
|
JGO6EiyIfV3DBh80T+E7dCEs4S0Erv4FNZRDfor7BEUF/9hXKceHsS8prAJZKZpl
|
||||||
|
XKqOPZdLzeanUDwm3QxMDgDhHKFXhpLXpW+JJ5F/c5xV3r2q29/cwKWsMVHIWcIg
|
||||||
|
TpqN+nWGtnnALsW/l3QDR0onYwZihuVQ9P+qr5vyOfWfE++5IejheGNZer6TTmJ0
|
||||||
|
iXuYuTvC6uaAeOayUHT9l0TjA1rqgA0t68Xi/GIIYnpz2B/xDnqBpRkCAwEAAaNT
|
||||||
|
MFEwHQYDVR0OBBYEFNkkFFS7VnquYF3ncePQNfFzpU+hMB8GA1UdIwQYMBaAFNkk
|
||||||
|
FFS7VnquYF3ncePQNfFzpU+hMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
|
||||||
|
BQADggEBAGgfOaXba4tnb+DORKUWm0WQ5umV8LKDr3BMjh8BZ2m/GWpFoL19o6LB
|
||||||
|
7S0oPdWnuRSy92SqAODt10e/PhepSpBrRa12g92AgAicgfnEutlY4zB8amCJBN/m
|
||||||
|
TemAAjMrBwxd6gD8tadu06Ye5ml+eMZ3S2JFPjQQgij//sDv89nl0JP+jOiwbDGn
|
||||||
|
cPmQSdP+6EH5ng5uKb6FXGKt29g3ZSfAzHS3+eCY0rkQnZWye9ZS2AaBTW/IKd2N
|
||||||
|
0VcJWXcWLx0XRi5zWVp7TkwGdiZRmB7NxXDbGuHSfq8n42FiKQnyIyNQyTmMeOgw
|
||||||
|
WHqLRjX6vLio3RwWAWleprLfHq6Jyf8=
|
||||||
|
-----END CERTIFICATE-----
|
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
import http.server
|
import http.server
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
import textwrap
|
import textwrap
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
@ -17,7 +18,7 @@ from unittest import mock
|
|||||||
import pytest
|
import pytest
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from utils import http_server
|
from utils import CERT_FILE, http_server, https_server
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('linkcheck', testroot='linkcheck', freshenv=True)
|
@pytest.mark.sphinx('linkcheck', testroot='linkcheck', freshenv=True)
|
||||||
@ -270,15 +271,19 @@ def test_follows_redirects_on_GET(app, capsys):
|
|||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class OKHandler(http.server.BaseHTTPRequestHandler):
|
||||||
|
def do_HEAD(self):
|
||||||
|
self.send_response(200, "OK")
|
||||||
|
self.end_headers()
|
||||||
|
|
||||||
|
def do_GET(self):
|
||||||
|
self.do_HEAD()
|
||||||
|
self.wfile.write(b"ok\n")
|
||||||
|
|
||||||
@pytest.mark.sphinx('linkcheck', testroot='linkcheck-localserver-https', freshenv=True)
|
@pytest.mark.sphinx('linkcheck', testroot='linkcheck-localserver-https', freshenv=True)
|
||||||
def test_invalid_ssl(app):
|
def test_invalid_ssl(app):
|
||||||
# Link indicates SSL should be used (https) but the server does not handle it.
|
# Link indicates SSL should be used (https) but the server does not handle it.
|
||||||
class OKHandler(http.server.BaseHTTPRequestHandler):
|
|
||||||
def do_GET(self):
|
|
||||||
self.send_response(200, "OK")
|
|
||||||
self.end_headers()
|
|
||||||
self.wfile.write(b"ok\n")
|
|
||||||
|
|
||||||
with http_server(OKHandler):
|
with http_server(OKHandler):
|
||||||
app.builder.build_all()
|
app.builder.build_all()
|
||||||
|
|
||||||
@ -289,3 +294,67 @@ def test_invalid_ssl(app):
|
|||||||
assert content["lineno"] == 1
|
assert content["lineno"] == 1
|
||||||
assert content["uri"] == "https://localhost:7777/"
|
assert content["uri"] == "https://localhost:7777/"
|
||||||
assert "SSLError" in content["info"]
|
assert "SSLError" in content["info"]
|
||||||
|
|
||||||
|
@pytest.mark.sphinx('linkcheck', testroot='linkcheck-localserver-https', freshenv=True)
|
||||||
|
def test_connect_to_selfsigned_fails(app):
|
||||||
|
with https_server(OKHandler):
|
||||||
|
app.builder.build_all()
|
||||||
|
|
||||||
|
with open(app.outdir / 'output.json') as fp:
|
||||||
|
content = json.load(fp)
|
||||||
|
assert content["status"] == "broken"
|
||||||
|
assert content["filename"] == "index.rst"
|
||||||
|
assert content["lineno"] == 1
|
||||||
|
assert content["uri"] == "https://localhost:7777/"
|
||||||
|
assert "[SSL: CERTIFICATE_VERIFY_FAILED]" in content["info"]
|
||||||
|
|
||||||
|
@pytest.mark.sphinx('linkcheck', testroot='linkcheck-localserver-https', freshenv=True)
|
||||||
|
def test_connect_to_selfsigned_with_tls_verify_false(app):
|
||||||
|
app.config.tls_verify = False
|
||||||
|
with https_server(OKHandler):
|
||||||
|
app.builder.build_all()
|
||||||
|
|
||||||
|
with open(app.outdir / 'output.json') as fp:
|
||||||
|
content = json.load(fp)
|
||||||
|
assert content == {
|
||||||
|
"code": 0,
|
||||||
|
"status": "working",
|
||||||
|
"filename": "index.rst",
|
||||||
|
"lineno": 1,
|
||||||
|
"uri": "https://localhost:7777/",
|
||||||
|
"info": "",
|
||||||
|
}
|
||||||
|
|
||||||
|
@pytest.mark.sphinx('linkcheck', testroot='linkcheck-localserver-https', freshenv=True)
|
||||||
|
def test_connect_to_selfsigned_with_tls_cacerts(app):
|
||||||
|
app.config.tls_cacerts = CERT_FILE
|
||||||
|
with https_server(OKHandler):
|
||||||
|
app.builder.build_all()
|
||||||
|
|
||||||
|
with open(app.outdir / 'output.json') as fp:
|
||||||
|
content = json.load(fp)
|
||||||
|
assert content == {
|
||||||
|
"code": 0,
|
||||||
|
"status": "working",
|
||||||
|
"filename": "index.rst",
|
||||||
|
"lineno": 1,
|
||||||
|
"uri": "https://localhost:7777/",
|
||||||
|
"info": "",
|
||||||
|
}
|
||||||
|
|
||||||
|
@pytest.mark.sphinx('linkcheck', testroot='linkcheck-localserver-https', freshenv=True)
|
||||||
|
def test_connect_to_selfsigned_with_requests_env_var(app):
|
||||||
|
os.environ["REQUESTS_CA_BUNDLE"] = CERT_FILE
|
||||||
|
with https_server(OKHandler):
|
||||||
|
app.builder.build_all()
|
||||||
|
|
||||||
|
with open(app.outdir / 'output.json') as fp:
|
||||||
|
content = json.load(fp)
|
||||||
|
assert content == {
|
||||||
|
"code": 0,
|
||||||
|
"status": "working",
|
||||||
|
"filename": "index.rst",
|
||||||
|
"lineno": 1,
|
||||||
|
"uri": "https://localhost:7777/",
|
||||||
|
"info": "",
|
||||||
|
}
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
import contextlib
|
import contextlib
|
||||||
import http.server
|
import http.server
|
||||||
|
import pathlib
|
||||||
|
import ssl
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
|
# Generated with:
|
||||||
|
# $ openssl req -new -x509 -days 3650 -nodes -out cert.pem -keyout cert.pem
|
||||||
|
CERT_FILE = str(pathlib.Path(__file__).parent / "certs" / "cert.pem")
|
||||||
|
|
||||||
|
|
||||||
class HttpServerThread(threading.Thread):
|
class HttpServerThread(threading.Thread):
|
||||||
def __init__(self, handler, *args, **kwargs):
|
def __init__(self, handler, *args, **kwargs):
|
||||||
@ -17,11 +23,26 @@ class HttpServerThread(threading.Thread):
|
|||||||
self.join()
|
self.join()
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
class HttpsServerThread(HttpServerThread):
|
||||||
def http_server(handler):
|
def __init__(self, handler, *args, **kwargs):
|
||||||
server_thread = HttpServerThread(handler, daemon=True)
|
super().__init__(handler, *args, **kwargs)
|
||||||
server_thread.start()
|
self.server.socket = ssl.wrap_socket(
|
||||||
try:
|
self.server.socket,
|
||||||
yield server_thread
|
certfile=CERT_FILE,
|
||||||
finally:
|
server_side=True,
|
||||||
server_thread.terminate()
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def create_server(thread_class):
|
||||||
|
def server(handler):
|
||||||
|
server_thread = thread_class(handler, daemon=True)
|
||||||
|
server_thread.start()
|
||||||
|
try:
|
||||||
|
yield server_thread
|
||||||
|
finally:
|
||||||
|
server_thread.terminate()
|
||||||
|
return contextlib.contextmanager(server)
|
||||||
|
|
||||||
|
|
||||||
|
http_server = create_server(HttpServerThread)
|
||||||
|
https_server = create_server(HttpsServerThread)
|
||||||
|
Loading…
Reference in New Issue
Block a user