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
|
||||
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
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:
|
||||
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)
|
||||
|
60
proxy.py
60
proxy.py
@ -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('<', '<').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):
|
||||
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user