Compare commits
No commits in common. 'master' and 'develop' have entirely different histories.
@ -1,228 +0,0 @@
|
|||||||
"""
|
|
||||||
Cryto.net Tahoe-LAFS -> WSGI Gateway Proxy
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
Everyone is permitted to copy and distribute verbatim or modified
|
|
||||||
copies of this license document, and changing it is allowed as long
|
|
||||||
as the name is changed.
|
|
||||||
|
|
||||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
||||||
|
|
||||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
|
||||||
|
|
||||||
This wsgi application's entry point is `app`, you can run it with gunicorn:
|
|
||||||
gunicorn gateway:app [options]
|
|
||||||
|
|
||||||
Or you can run it stand-alone:
|
|
||||||
python gateway.py [options, --help]
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
from wsgiref.util import is_hop_by_hop
|
|
||||||
import base64
|
|
||||||
import socket
|
|
||||||
import urllib
|
|
||||||
import urllib2
|
|
||||||
import logging
|
|
||||||
import time
|
|
||||||
|
|
||||||
gatewayLog = logging.getLogger("gateway-wsgi")
|
|
||||||
|
|
||||||
_config_gatewayTimeout = 15
|
|
||||||
_config_tahoeServer = 'localhost:3456'
|
|
||||||
_config_chunkSize = 1 << 13 # 8192, 8kb chunks.
|
|
||||||
|
|
||||||
_response_skeleton =\
|
|
||||||
"""
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>%(page_title)s</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h2>%(page_header)s</h2>
|
|
||||||
%(page_text)s<br><br>
|
|
||||||
<a href="http://cryto.net/">Learn more about the Cryto Coding Collective</a>.
|
|
||||||
<br><br><br><hr>
|
|
||||||
<div style="text-align: right; font-style: italic;">Cryto-Tahoe-Gateway v1.1</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
"""
|
|
||||||
|
|
||||||
def error_404(environ, start_response):
|
|
||||||
"""
|
|
||||||
Returns a 404 page to the client.
|
|
||||||
"""
|
|
||||||
start_response("404 Not Found", [("Content-Type", "text/html")])
|
|
||||||
return [_response_skeleton % dict(
|
|
||||||
page_title = 'Cryto-Tahoe-Gateway: 404',
|
|
||||||
page_header = 'The specified resource was not found.',
|
|
||||||
page_text = 'The file may have expired, or the hyperlink you followed may have been broken.'
|
|
||||||
)]
|
|
||||||
|
|
||||||
def error_500(environ, start_response):
|
|
||||||
"""
|
|
||||||
Returns a 500 page to the client.
|
|
||||||
"""
|
|
||||||
start_response("500 Internal Server Error", [("Content-Type", "text/html")])
|
|
||||||
return [_response_skeleton % dict(
|
|
||||||
page_title = 'Cryto-Tahoe-Gateway: 500',
|
|
||||||
page_header = 'An error has occurred, and the gateway could not process your request, please try again.',
|
|
||||||
page_text = ''
|
|
||||||
)]
|
|
||||||
|
|
||||||
def error_50x(environ, start_response, code = '502', message = "Gateway Timeout"):
|
|
||||||
"""
|
|
||||||
Generic error page... can return any type of error, not only 502.
|
|
||||||
"""
|
|
||||||
start_response("%s %s" % (code, message), [("Content-Type", "text/html")])
|
|
||||||
return [_response_skeleton % dict(
|
|
||||||
page_title = 'Cryto-Tahoe-Gateway: %s' % code,
|
|
||||||
page_header = 'Gateway Error %s: %s'% (code, message),
|
|
||||||
page_text = ''
|
|
||||||
)]
|
|
||||||
|
|
||||||
def index(environ, start_response):
|
|
||||||
"""
|
|
||||||
Generic index.
|
|
||||||
"""
|
|
||||||
start_response("200 OK", [("Content-Type", "text/html")])
|
|
||||||
return [_response_skeleton % dict(
|
|
||||||
page_title = "Cryto-Tahoe-Gateway: Index",
|
|
||||||
page_header = "This gateway does not provide an index page.",
|
|
||||||
page_text = "Please use a direct URL to download a file hosted on this storage grid."
|
|
||||||
)]
|
|
||||||
|
|
||||||
def proxy_pass(environ, start_response):
|
|
||||||
"""
|
|
||||||
Proxy the request to tahoe.
|
|
||||||
"""
|
|
||||||
|
|
||||||
path = environ['PATH_INFO']
|
|
||||||
pathParts = path.split('/')
|
|
||||||
if len(pathParts) != 4:
|
|
||||||
raise NotFoundError()
|
|
||||||
|
|
||||||
# Convert url to tahoe-type URL.
|
|
||||||
_, _, urlIdentifier, fileName = pathParts
|
|
||||||
urlIdentifier = urllib.quote(base64.urlsafe_b64decode(urlIdentifier))
|
|
||||||
fileName = urllib.quote(fileName)
|
|
||||||
localUri = "http://%s/file/%s/@@named=/%s" % (_config_tahoeServer, urlIdentifier, fileName)
|
|
||||||
gatewayLog.debug("Proxy passing request (ident: %s, file: %s)", urlIdentifier, fileName)
|
|
||||||
|
|
||||||
# The actual proxying starts here.
|
|
||||||
try:
|
|
||||||
fp = urllib2.urlopen(localUri, timeout = _config_gatewayTimeout)
|
|
||||||
|
|
||||||
except urllib2.HTTPError, e:
|
|
||||||
|
|
||||||
# Eat any non 200 errors
|
|
||||||
gatewayLog.exception("HTTP Error")
|
|
||||||
if int(e.code) == 404:
|
|
||||||
raise NotFoundError()
|
|
||||||
else:
|
|
||||||
return error_50x(environ, start_response, e.code, e.msg)
|
|
||||||
|
|
||||||
except urllib2.URLError, e:
|
|
||||||
|
|
||||||
# Something went awry connecting to the backend.
|
|
||||||
gatewayLog.exception("Error connecting to backend...")
|
|
||||||
try:
|
|
||||||
if isinstance(e.args[0], Exception):
|
|
||||||
raise e.args[0]
|
|
||||||
else:
|
|
||||||
raise e
|
|
||||||
except socket.timeout:
|
|
||||||
return error_50x(environ, start_response, '504', "Gateway Timeout")
|
|
||||||
except Exception, e:
|
|
||||||
return error_50x(environ, start_response, '503', "Service Unavailable")
|
|
||||||
|
|
||||||
else:
|
|
||||||
|
|
||||||
# Do the actual proxying
|
|
||||||
data_sent = 0
|
|
||||||
req_start = time.time()
|
|
||||||
content_length = fp.info().getheader("Content-Length", 0)
|
|
||||||
|
|
||||||
try:
|
|
||||||
response_headers = [(k, v) for k, v in fp.info().items() if not is_hop_by_hop(k)]
|
|
||||||
write = start_response("200 OK", response_headers)
|
|
||||||
|
|
||||||
while True:
|
|
||||||
chunk = fp.read(_config_chunkSize)
|
|
||||||
if not chunk:
|
|
||||||
break
|
|
||||||
write(chunk)
|
|
||||||
data_sent += len(chunk)
|
|
||||||
|
|
||||||
gatewayLog.debug("Finished proxied request of %s, elapsed: %.02fs, transfer: %s bytes.", fileName,
|
|
||||||
time.time() - req_start, data_sent)
|
|
||||||
|
|
||||||
except Exception, e:
|
|
||||||
gatewayLog.exception("Error transfering proxied content... %s, sent %s of %s, elapsed %.02f",
|
|
||||||
fileName, data_sent, content_length, time.time() - req_start)
|
|
||||||
|
|
||||||
finally:
|
|
||||||
fp.close()
|
|
||||||
|
|
||||||
# Wsgi spec says we have to return an empty iterable ._.
|
|
||||||
return ()
|
|
||||||
|
|
||||||
class NotFoundError(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def app(environ, start_response):
|
|
||||||
"""
|
|
||||||
Application entry point, provide me to a wsgi handler!
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
path = environ['PATH_INFO']
|
|
||||||
if not path or path == '/':
|
|
||||||
return index(environ, start_response)
|
|
||||||
elif path.startswith('/download/'):
|
|
||||||
return proxy_pass(environ, start_response)
|
|
||||||
else:
|
|
||||||
raise NotFoundError()
|
|
||||||
|
|
||||||
except NotFoundError:
|
|
||||||
return error_404(environ, start_response)
|
|
||||||
|
|
||||||
except Exception, e:
|
|
||||||
gatewayLog.exception("WSGI Application encountered error")
|
|
||||||
return error_500(environ, start_response)
|
|
||||||
|
|
||||||
def main():
|
|
||||||
from optparse import OptionParser
|
|
||||||
|
|
||||||
parser = OptionParser()
|
|
||||||
parser.add_option("-i", '--interface', dest = "interface", help = "interface to bind on", default = "127.0.0.1")
|
|
||||||
parser.add_option("-p", '--listen-port', dest = "listen_port", help = "port to listen on", type="int",
|
|
||||||
default = 3719)
|
|
||||||
parser.add_option("-u", '--tahoe-url', dest = "tahoe_url", help = "address that tahoe is listening on, in form"
|
|
||||||
" host:port", default = 'localhost:3456')
|
|
||||||
parser.add_option("-c", '--proxy-chunk-size', dest = "chunk_size", help = "chunk size to read while proxying",
|
|
||||||
type = "int", default = 1 << 13)
|
|
||||||
|
|
||||||
parser.add_option("-t", '--gateway-timeout', dest = "gateway_timeout", help = "timeout while connecting to gateway",
|
|
||||||
type = "int", default = 15)
|
|
||||||
|
|
||||||
parser.add_option("-d", '--debug', dest = "debug", action = "store_true", default = False,
|
|
||||||
help = "debug logging level")
|
|
||||||
|
|
||||||
options, args = parser.parse_args()
|
|
||||||
global _config_chunkSize, _config_gatewayTimeout, _config_tahoeServer
|
|
||||||
_config_chunkSize = options.chunk_size
|
|
||||||
_config_gatewayTimeout = options.gateway_timeout
|
|
||||||
_config_tahoeServer = options.tahoe_url
|
|
||||||
|
|
||||||
if options.debug:
|
|
||||||
gatewayLog.setLevel(logging.DEBUG)
|
|
||||||
logging.basicConfig()
|
|
||||||
|
|
||||||
import wsgiref.simple_server
|
|
||||||
wsgiref.simple_server.make_server(options.interface, options.listen_port, app).serve_forever()
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -1,74 +0,0 @@
|
|||||||
#! /bin/bash
|
|
||||||
### BEGIN INIT INFO
|
|
||||||
# Provides: Tahoe-LAFS Gateway
|
|
||||||
### END INIT INFO
|
|
||||||
|
|
||||||
#### SERVER SPECIFIC CONFIGURATION
|
|
||||||
GATEWAY_PATH="/home/tahoe/gateway/run"
|
|
||||||
PID_PATH="/home/tahoe/gateway/gateway.pid"
|
|
||||||
RUN_AS=tahoe
|
|
||||||
#### DO NOT CHANGE ANYTHING AFTER THIS LINE!
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
|
|
||||||
DESC="Tahoe-LAFS Gateway"
|
|
||||||
NAME=$0
|
|
||||||
SCRIPTNAME=/etc/init.d/$NAME
|
|
||||||
|
|
||||||
#
|
|
||||||
# Function that starts the daemon/service.
|
|
||||||
#
|
|
||||||
d_start()
|
|
||||||
{
|
|
||||||
# Starting Tahoe-LAFS Gateway
|
|
||||||
if [ -f $PID_PATH ]; then
|
|
||||||
echo -n ", already running"
|
|
||||||
else
|
|
||||||
start-stop-daemon --start --quiet --pidfile $PID_PATH \
|
|
||||||
--chuid $RUN_AS --exec /usr/bin/env -- $GATEWAY_PATH
|
|
||||||
chmod 400 $PID_PATH
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Function that stops the daemon/service.
|
|
||||||
#
|
|
||||||
d_stop() {
|
|
||||||
# Killing the Tahoe-LAFS Gateway
|
|
||||||
start-stop-daemon --stop --quiet --pidfile $PID_PATH \
|
|
||||||
|| echo -n ", not running"
|
|
||||||
if [ -f $PID_PATH ]; then
|
|
||||||
rm $PID_PATH
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
ACTION="$1"
|
|
||||||
case "$ACTION" in
|
|
||||||
start)
|
|
||||||
echo -n "Starting $DESC: $NAME"
|
|
||||||
d_start
|
|
||||||
echo "."
|
|
||||||
;;
|
|
||||||
|
|
||||||
stop)
|
|
||||||
echo -n "Stopping $DESC: $NAME"
|
|
||||||
d_stop
|
|
||||||
echo "."
|
|
||||||
;;
|
|
||||||
|
|
||||||
restart|force-reload)
|
|
||||||
echo -n "Restarting $DESC: $NAME"
|
|
||||||
d_stop
|
|
||||||
sleep 1
|
|
||||||
d_start
|
|
||||||
echo "."
|
|
||||||
;;
|
|
||||||
|
|
||||||
*)
|
|
||||||
echo "Usage: $NAME {start|stop|restart|force-reload}" >&2
|
|
||||||
exit 3
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
exit 0
|
|
@ -1,4 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
cd /home/tahoe/gateway
|
|
||||||
gunicorn -b 0.0.0.0:3719 -k gevent gateway_wsgi:app >/dev/null 2>/dev/null &
|
|
||||||
echo $! > gateway.pid
|
|
Loading…
Reference in New Issue