better error reporting
* use a template based on the Privoxy one * print a stack of the exceptions * link the documentation and issue tracker
This commit is contained in:
parent
05a6db87dd
commit
5498e1de87
4
cert.py
4
cert.py
@ -56,9 +56,9 @@ def get_cert(name, config):
|
|||||||
cafile: the CA file to create dummpy cert files
|
cafile: the CA file to create dummpy cert files
|
||||||
certdir: the path where cert files are looked for or created
|
certdir: the path where cert files are looked for or created
|
||||||
'''
|
'''
|
||||||
certfile = os.path.join(config.CERTDIR, name + '.crt')
|
certfile = os.path.join(config.certdir, name + '.crt')
|
||||||
if not os.path.exists(certfile):
|
if not os.path.exists(certfile):
|
||||||
dummy_cert(config.CA, certfile, name)
|
dummy_cert(config.ca, certfile, name)
|
||||||
return certfile
|
return certfile
|
||||||
|
|
||||||
|
|
||||||
|
107
data/error.html
Normal file
107
data/error.html
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta encoding="utf-8">
|
||||||
|
<style>
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: Helvetica, Arial, sans-serif;
|
||||||
|
color: black;
|
||||||
|
background: white;
|
||||||
|
margin: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
article, footer {
|
||||||
|
border: 1px solid black;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 0.6em;
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
height: 7em;
|
||||||
|
display: flex;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
header div {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border: 1px solid black;
|
||||||
|
margin: 0 0.3rem;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
header div:first-child { margin-left: 0; }
|
||||||
|
header div:last-child { margin-right: 0; }
|
||||||
|
|
||||||
|
#status {
|
||||||
|
background: red;
|
||||||
|
color: white;
|
||||||
|
font-size: 300%;
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
#title {
|
||||||
|
background: #ddd;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#message { background: #fdd; }
|
||||||
|
article { background: #eee; }
|
||||||
|
footer { background: #ccf; }
|
||||||
|
|
||||||
|
h1, h2, h3 { margin: 0; }
|
||||||
|
h1 { font-size: 140% }
|
||||||
|
h2 { font-size: 120% }
|
||||||
|
h3 { font-size: 110% }
|
||||||
|
|
||||||
|
a { text-decoration: none; }
|
||||||
|
a:link { color: #00d; }
|
||||||
|
a:visited { color: #309; }
|
||||||
|
a:active { color: #33f; }
|
||||||
|
</style>
|
||||||
|
<title>$server: Error $code</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<div id="status">$code</div>
|
||||||
|
|
||||||
|
<div id="title">
|
||||||
|
<h1>
|
||||||
|
This is <a href="https://maxwell.ydns.eu/git/rnhmjoj/privoxy-tls">$server</a>
|
||||||
|
on $hostname ($address), port $port
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<article id="message">
|
||||||
|
<h2>$message:</h2>
|
||||||
|
<p>
|
||||||
|
Your request for <b><a href="$url">$url</a></b> could not be fulfilled
|
||||||
|
because an error occured.
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article>
|
||||||
|
<h2>More information:</h2>
|
||||||
|
$explain
|
||||||
|
<p>Error generated on $now.</p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<h2>Support:</h2>
|
||||||
|
<p>
|
||||||
|
Please have a look at the
|
||||||
|
<a href="https://maxwell.ydns.eu/git/rnhmjoj/privoxy-tls">documentation</a>
|
||||||
|
to learn how to configure Privoxy TLS correctly.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
If you are encountering a problem consider opening an
|
||||||
|
<a href="https://maxwell.ydns.eu/git/rnhmjoj/privoxy-tls/issues">issue</a>.
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
49
main.py
49
main.py
@ -29,16 +29,19 @@ _name = 'privoxy-tls'
|
|||||||
|
|
||||||
class LoadConfig:
|
class LoadConfig:
|
||||||
def __init__(self, configfile):
|
def __init__(self, configfile):
|
||||||
self.config = configparser.ConfigParser(allow_no_value=True,
|
self.config = configparser.ConfigParser(
|
||||||
inline_comment_prefixes=('#',))
|
allow_no_value=True, delimiters=('=',),
|
||||||
|
inline_comment_prefixes=('#',))
|
||||||
self.config.read(configfile)
|
self.config.read(configfile)
|
||||||
self.PROXADDR = self.config['General'].get('ProxAddr')
|
self.proxy_name = self.config['General'].get('ProxAddr')
|
||||||
self.FRONTPORT = int(self.config['General'].get('FrontPort'))
|
self.front_name = self.config['General'].get('FrontAddr', 'localhost')
|
||||||
self.REARPORT = int(self.config['General'].get('RearPort'))
|
self.rear_name = self.config['General'].get('RearAddr', 'localhost')
|
||||||
self.GeneralPROXY = self.config['General'].get('DefaultProxy')
|
self.front_port = int(self.config['General'].get('FrontPort'))
|
||||||
self.LOGLEVEL = self.config['General'].get('LogLevel')
|
self.rear_port = int(self.config['General'].get('RearPort'))
|
||||||
self.CA = self.config['General'].get('CACert')
|
self.proxy = self.config['General'].get('DefaultProxy')
|
||||||
self.CERTDIR = self.config['General'].get('CertDir')
|
self.loglevel = self.config['General'].get('LogLevel')
|
||||||
|
self.ca = self.config['General'].get('CACert')
|
||||||
|
self.certdir = self.config['General'].get('CertDir')
|
||||||
|
|
||||||
|
|
||||||
class ConnectionPools:
|
class ConnectionPools:
|
||||||
@ -307,7 +310,7 @@ class FrontRequestHandler(ProxyRequestHandler):
|
|||||||
logger.warning(f'{self.reqNum:03d} [F] {e} on '
|
logger.warning(f'{self.reqNum:03d} [F] {e} on '
|
||||||
f'"{self.command} {url}"')
|
f'"{self.command} {url}"')
|
||||||
except (urllib3.exceptions.HTTPError,) as e:
|
except (urllib3.exceptions.HTTPError,) as e:
|
||||||
self.sendout_error(url, 502, message="HTTPError", explain=e)
|
self.sendout_error(url, 502, message="HTTP Error", explain=e)
|
||||||
logger.warning(f'{self.reqNum:03d} [F] {e} on '
|
logger.warning(f'{self.reqNum:03d} [F] {e} on '
|
||||||
f'"{self.command} {url}"')
|
f'"{self.command} {url}"')
|
||||||
finally:
|
finally:
|
||||||
@ -323,7 +326,7 @@ class RearRequestHandler(ProxyRequestHandler):
|
|||||||
Supposed to be the parent proxy for Privoxy for tagged requests
|
Supposed to be the parent proxy for Privoxy for tagged requests
|
||||||
Convert http request to https
|
Convert http request to https
|
||||||
"""
|
"""
|
||||||
server_version = f'{_name} front/{__version__}'
|
server_version = f'{_name} rear/{__version__}'
|
||||||
|
|
||||||
def do_METHOD(self):
|
def do_METHOD(self):
|
||||||
"Convert http request to https"
|
"Convert http request to https"
|
||||||
@ -409,7 +412,7 @@ class RearRequestHandler(ProxyRequestHandler):
|
|||||||
logger.warning(f'{self.reqNum:03d} [R]{prefix} '
|
logger.warning(f'{self.reqNum:03d} [R]{prefix} '
|
||||||
f'"{self.command} {url}" {e}')
|
f'"{self.command} {url}" {e}')
|
||||||
except (urllib3.exceptions.HTTPError,) as e:
|
except (urllib3.exceptions.HTTPError,) as e:
|
||||||
self.sendout_error(url, 502, message="HTTPError", explain=e)
|
self.sendout_error(url, 502, message="HTTP Error", explain=e)
|
||||||
logger.warning(f'{self.reqNum:03d} [R]{prefix} '
|
logger.warning(f'{self.reqNum:03d} [R]{prefix} '
|
||||||
f'"{self.command} {url}" {e}')
|
f'"{self.command} {url}" {e}')
|
||||||
|
|
||||||
@ -423,16 +426,18 @@ class RearRequestHandler(ProxyRequestHandler):
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
urllib3.disable_warnings()
|
urllib3.disable_warnings()
|
||||||
logger.setLevel(getattr(logging, config.LOGLEVEL, logging.INFO))
|
logger.setLevel(getattr(logging, config.loglevel, logging.INFO))
|
||||||
handler = logging.StreamHandler()
|
handler = logging.StreamHandler()
|
||||||
formatter = logging.Formatter('%(asctime)s %(message)s', datefmt='[%H:%M]')
|
formatter = logging.Formatter('%(asctime)s %(message)s', datefmt='[%H:%M]')
|
||||||
handler.setFormatter(formatter)
|
handler.setFormatter(formatter)
|
||||||
logger.addHandler(handler)
|
logger.addHandler(handler)
|
||||||
|
|
||||||
frontserver = FrontServer(('', config.FRONTPORT), FrontRequestHandler)
|
frontserver = FrontServer((config.front_name, config.front_port),
|
||||||
rearserver = RearServer(('', config.REARPORT), RearRequestHandler)
|
FrontRequestHandler)
|
||||||
frontserver.config = config
|
rearserver = RearServer((config.rear_name, config.rear_port),
|
||||||
for worker in (frontserver.serve_forever, rearserver.serve_forever,
|
RearRequestHandler)
|
||||||
|
for worker in (frontserver.serve_forever,
|
||||||
|
rearserver.serve_forever,
|
||||||
pools.reloadConfig):
|
pools.reloadConfig):
|
||||||
thread = threading.Thread(target=worker)
|
thread = threading.Thread(target=worker)
|
||||||
thread.daemon = True
|
thread.daemon = True
|
||||||
@ -440,10 +445,10 @@ def main():
|
|||||||
|
|
||||||
print('=' * 40)
|
print('=' * 40)
|
||||||
print(f'{_name} {__version__} (urllib3/{urllib3.__version__})')
|
print(f'{_name} {__version__} (urllib3/{urllib3.__version__})')
|
||||||
print(f'Front : localhost:{config.FRONTPORT}')
|
print(f'Front : {config.front_name}:{config.front_port}')
|
||||||
print(f'Privoxy : {config.PROXADDR}')
|
print(f'Privoxy : {config.proxy_name}')
|
||||||
print(f'Rear : localhost:{config.REARPORT}')
|
print(f'Rear : {config.rear_name}:{config.rear_port}')
|
||||||
print(f'Parent : {config.GeneralPROXY}')
|
print(f'Proxy : {config.proxy}')
|
||||||
print('=' * 40)
|
print('=' * 40)
|
||||||
while True:
|
while True:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
@ -461,7 +466,7 @@ if __name__ == '__main__':
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
config = LoadConfig(CONFIG)
|
config = LoadConfig(CONFIG)
|
||||||
proxpool = urllib3.ProxyManager(
|
proxpool = urllib3.ProxyManager(
|
||||||
config.PROXADDR, num_pools=10, maxsize=8,
|
config.proxy_name, num_pools=10, maxsize=8,
|
||||||
timeout=urllib3.util.timeout.Timeout(
|
timeout=urllib3.util.timeout.Timeout(
|
||||||
connect=90.0, read=310.0))
|
connect=90.0, read=310.0))
|
||||||
pools = ConnectionPools(CONFIG)
|
pools = ConnectionPools(CONFIG)
|
||||||
|
60
proxy.py
60
proxy.py
@ -12,6 +12,8 @@ import cgi
|
|||||||
import socket
|
import socket
|
||||||
import select
|
import select
|
||||||
import ssl
|
import ssl
|
||||||
|
import string
|
||||||
|
import pathlib
|
||||||
|
|
||||||
from http.server import HTTPServer, BaseHTTPRequestHandler
|
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||||
from socketserver import ThreadingMixIn
|
from socketserver import ThreadingMixIn
|
||||||
@ -20,23 +22,36 @@ from cert import get_cert
|
|||||||
_name = 'proxy'
|
_name = 'proxy'
|
||||||
logger = logging.getLogger('__main__')
|
logger = logging.getLogger('__main__')
|
||||||
|
|
||||||
message_format = '''\
|
data = pathlib.Path(__file__).parent / 'data'
|
||||||
<!doctype html>
|
error_template = string.Template(open(data / 'error.html').read())
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
def walk_traceback(e, n=0):
|
||||||
<title>Proxy Error: {code}</title>
|
'''
|
||||||
</head>
|
Produce an HTML formatted stack trace
|
||||||
<body>
|
given an exception.
|
||||||
<h1>{code}: {message}</h1>
|
'''
|
||||||
<p>The following error occurred while
|
partial = []
|
||||||
trying to access <strong>{url}</strong>
|
ul = lambda xs: ('<ul><li>'
|
||||||
</p>
|
+ '</li>\n<li>'.join(xs)
|
||||||
<p><strong>{explain}</strong></p>
|
+ '</li></ul>')
|
||||||
<hr>Generated on {now} by {server}.
|
|
||||||
</body>
|
for i, arg in enumerate(e.args):
|
||||||
</html>
|
name = (('<strong>'
|
||||||
'''
|
+ type(e).__name__
|
||||||
|
+ '</strong>') if i == 0 else '')
|
||||||
|
if isinstance(arg, str):
|
||||||
|
partial.append(
|
||||||
|
name + ' - '
|
||||||
|
+ arg.replace('<', '<').replace('>', '>'))
|
||||||
|
else:
|
||||||
|
partial.append(name)
|
||||||
|
|
||||||
|
if isinstance(arg, Exception):
|
||||||
|
partial.append(walk_traceback(arg, n+1))
|
||||||
|
if n == 0:
|
||||||
|
partial.append(walk_traceback(e.reason, n+1))
|
||||||
|
return ul(partial)
|
||||||
|
|
||||||
|
|
||||||
def read_write(socket1, socket2, max_idling=10):
|
def read_write(socket1, socket2, max_idling=10):
|
||||||
@ -157,10 +172,15 @@ class ProxyRequestHandler(BaseHTTPRequestHandler):
|
|||||||
message = shortmsg
|
message = shortmsg
|
||||||
if explain is None:
|
if explain is None:
|
||||||
explain = longmsg
|
explain = longmsg
|
||||||
content = message_format.format(
|
|
||||||
|
content = error_template.substitute(
|
||||||
code=code, message=message,
|
code=code, message=message,
|
||||||
explain=explain, url=url,
|
explain=walk_traceback(explain),
|
||||||
now=datetime.today(),
|
url=url,
|
||||||
|
hostname=self.server.server_name,
|
||||||
|
address=self.server.server_address[0],
|
||||||
|
port=self.server.server_port,
|
||||||
|
now=datetime.today().isoformat(sep=' ', timespec='seconds'),
|
||||||
server=self.server_version)
|
server=self.server_version)
|
||||||
body = content.encode('UTF-8', 'replace')
|
body = content.encode('UTF-8', 'replace')
|
||||||
self.send_response_only(code, message)
|
self.send_response_only(code, message)
|
||||||
|
Loading…
Reference in New Issue
Block a user