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 json
|
||||
import os
|
||||
import re
|
||||
import textwrap
|
||||
from unittest import mock
|
||||
@ -17,7 +18,7 @@ from unittest import mock
|
||||
import pytest
|
||||
import requests
|
||||
|
||||
from utils import http_server
|
||||
from utils import CERT_FILE, http_server, https_server
|
||||
|
||||
|
||||
@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)
|
||||
def test_invalid_ssl(app):
|
||||
# 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):
|
||||
app.builder.build_all()
|
||||
|
||||
@ -289,3 +294,67 @@ def test_invalid_ssl(app):
|
||||
assert content["lineno"] == 1
|
||||
assert content["uri"] == "https://localhost:7777/"
|
||||
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 http.server
|
||||
import pathlib
|
||||
import ssl
|
||||
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):
|
||||
def __init__(self, handler, *args, **kwargs):
|
||||
@ -17,11 +23,26 @@ class HttpServerThread(threading.Thread):
|
||||
self.join()
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def http_server(handler):
|
||||
server_thread = HttpServerThread(handler, daemon=True)
|
||||
class HttpsServerThread(HttpServerThread):
|
||||
def __init__(self, handler, *args, **kwargs):
|
||||
super().__init__(handler, *args, **kwargs)
|
||||
self.server.socket = ssl.wrap_socket(
|
||||
self.server.socket,
|
||||
certfile=CERT_FILE,
|
||||
server_side=True,
|
||||
)
|
||||
|
||||
|
||||
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