better error reporting

* use a template based on the Privoxy one
  * print a stack of the exceptions
  * link the documentation and issue tracker
master
Michele Guerini Rocco 2019-08-23 01:31:58 +02:00
parent 05a6db87dd
commit 5498e1de87
Signed by: rnhmjoj
GPG Key ID: BFBAF4C975F76450
4 changed files with 176 additions and 44 deletions

View File

@ -56,9 +56,9 @@ def get_cert(name, config):
cafile: the CA file to create dummpy cert files
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):
dummy_cert(config.CA, certfile, name)
dummy_cert(config.ca, certfile, name)
return certfile

107
data/error.html Normal file
View 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
View File

@ -29,16 +29,19 @@ _name = 'privoxy-tls'
class LoadConfig:
def __init__(self, configfile):
self.config = configparser.ConfigParser(allow_no_value=True,
inline_comment_prefixes=('#',))
self.config = configparser.ConfigParser(
allow_no_value=True, delimiters=('=',),
inline_comment_prefixes=('#',))
self.config.read(configfile)
self.PROXADDR = self.config['General'].get('ProxAddr')
self.FRONTPORT = int(self.config['General'].get('FrontPort'))
self.REARPORT = int(self.config['General'].get('RearPort'))
self.GeneralPROXY = self.config['General'].get('DefaultProxy')
self.LOGLEVEL = self.config['General'].get('LogLevel')
self.CA = self.config['General'].get('CACert')
self.CERTDIR = self.config['General'].get('CertDir')
self.proxy_name = self.config['General'].get('ProxAddr')
self.front_name = self.config['General'].get('FrontAddr', 'localhost')
self.rear_name = self.config['General'].get('RearAddr', 'localhost')
self.front_port = int(self.config['General'].get('FrontPort'))
self.rear_port = int(self.config['General'].get('RearPort'))
self.proxy = self.config['General'].get('DefaultProxy')
self.loglevel = self.config['General'].get('LogLevel')
self.ca = self.config['General'].get('CACert')
self.certdir = self.config['General'].get('CertDir')
class ConnectionPools:
@ -307,7 +310,7 @@ class FrontRequestHandler(ProxyRequestHandler):
logger.warning(f'{self.reqNum:03d} [F] {e} on '
f'"{self.command} {url}"')
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 '
f'"{self.command} {url}"')
finally:
@ -323,7 +326,7 @@ class RearRequestHandler(ProxyRequestHandler):
Supposed to be the parent proxy for Privoxy for tagged requests
Convert http request to https
"""
server_version = f'{_name} front/{__version__}'
server_version = f'{_name} rear/{__version__}'
def do_METHOD(self):
"Convert http request to https"
@ -409,7 +412,7 @@ class RearRequestHandler(ProxyRequestHandler):
logger.warning(f'{self.reqNum:03d} [R]{prefix} '
f'"{self.command} {url}" {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} '
f'"{self.command} {url}" {e}')
@ -423,16 +426,18 @@ class RearRequestHandler(ProxyRequestHandler):
def main():
urllib3.disable_warnings()
logger.setLevel(getattr(logging, config.LOGLEVEL, logging.INFO))
logger.setLevel(getattr(logging, config.loglevel, logging.INFO))
handler = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s %(message)s', datefmt='[%H:%M]')
handler.setFormatter(formatter)
logger.addHandler(handler)
frontserver = FrontServer(('', config.FRONTPORT), FrontRequestHandler)
rearserver = RearServer(('', config.REARPORT), RearRequestHandler)
frontserver.config = config
for worker in (frontserver.serve_forever, rearserver.serve_forever,
frontserver = FrontServer((config.front_name, config.front_port),
FrontRequestHandler)
rearserver = RearServer((config.rear_name, config.rear_port),
RearRequestHandler)
for worker in (frontserver.serve_forever,
rearserver.serve_forever,
pools.reloadConfig):
thread = threading.Thread(target=worker)
thread.daemon = True
@ -440,10 +445,10 @@ def main():
print('=' * 40)
print(f'{_name} {__version__} (urllib3/{urllib3.__version__})')
print(f'Front : localhost:{config.FRONTPORT}')
print(f'Privoxy : {config.PROXADDR}')
print(f'Rear : localhost:{config.REARPORT}')
print(f'Parent : {config.GeneralPROXY}')
print(f'Front : {config.front_name}:{config.front_port}')
print(f'Privoxy : {config.proxy_name}')
print(f'Rear : {config.rear_name}:{config.rear_port}')
print(f'Proxy : {config.proxy}')
print('=' * 40)
while True:
time.sleep(1)
@ -461,7 +466,7 @@ if __name__ == '__main__':
logger = logging.getLogger(__name__)
config = LoadConfig(CONFIG)
proxpool = urllib3.ProxyManager(
config.PROXADDR, num_pools=10, maxsize=8,
config.proxy_name, num_pools=10, maxsize=8,
timeout=urllib3.util.timeout.Timeout(
connect=90.0, read=310.0))
pools = ConnectionPools(CONFIG)

View File

@ -12,6 +12,8 @@ import cgi
import socket
import select
import ssl
import string
import pathlib
from http.server import HTTPServer, BaseHTTPRequestHandler
from socketserver import ThreadingMixIn
@ -20,23 +22,36 @@ from cert import get_cert
_name = 'proxy'
logger = logging.getLogger('__main__')
message_format = '''\
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>Proxy Error: {code}</title>
</head>
<body>
<h1>{code}: {message}</h1>
<p>The following error occurred while
trying to access <strong>{url}</strong>
</p>
<p><strong>{explain}</strong></p>
<hr>Generated on {now} by {server}.
</body>
</html>
'''
data = pathlib.Path(__file__).parent / 'data'
error_template = string.Template(open(data / 'error.html').read())
def walk_traceback(e, n=0):
'''
Produce an HTML formatted stack trace
given an exception.
'''
partial = []
ul = lambda xs: ('<ul><li>'
+ '</li>\n<li>'.join(xs)
+ '</li></ul>')
for i, arg in enumerate(e.args):
name = (('<strong>'
+ type(e).__name__
+ '</strong>') if i == 0 else '')
if isinstance(arg, str):
partial.append(
name + ' - '
+ arg.replace('<', '&lt;').replace('>', '&gt;'))
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):
@ -157,10 +172,15 @@ class ProxyRequestHandler(BaseHTTPRequestHandler):
message = shortmsg
if explain is None:
explain = longmsg
content = message_format.format(
content = error_template.substitute(
code=code, message=message,
explain=explain, url=url,
now=datetime.today(),
explain=walk_traceback(explain),
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)
body = content.encode('UTF-8', 'replace')
self.send_response_only(code, message)