peerlink/peerlink/nodeserver.py

108 lines
3.2 KiB
Python

# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
import json
from tornado.httpserver import HTTPServer
from tornado.ioloop import PeriodicCallback
import tornado.web
from .proxy import ProxyHandler
from .utils import get_public_ipv6, valid
from . import directory
from . import settings
from . import state
import logging
logger = logging.getLogger('nodeserver')
class NodeHandler(ProxyHandler):
def validate_request(self):
if 'From' in self.request.headers:
del self.request.headers['From']
sig = self.request.headers.get('X-Ed25519-Signature')
key = self.request.headers.get('X-Ed25519-Key')
if sig or key:
try:
is_valid = valid(key, self.request.body, sig)
except:
is_valid = False
if is_valid:
self.request.headers['From'] = key
return is_valid
return True
@tornado.web.asynchronous
def _handle_response(self, response):
# sign json responses from local services
if not response.error and \
response.headers.get('Content-Type') == 'application/json':
if response.body:
response.data = response.body.read()
response.body = None
sig = settings.sk.sign(response.data, encoding=settings.ENCODING).decode()
response.headers['X-Ed25519-Key'] = settings.USER_ID
response.headers['X-Ed25519-Signature'] = sig
return ProxyHandler._handle_response(self, response)
def remote_url(self):
if not self.validate_request():
url = None
self.set_status(403)
self.write(json.dumps({'status': 'Invalid Signature'}))
self.finish()
return None
try:
service, uri = self.request.uri[1:].split('/', 1)
except:
logger.debug('Invalid Request %s', self.request.uri)
return None
if service in settings.services:
url = settings.services[service] + uri
else:
url = None
self.set_status(404)
self.write(json.dumps({'status': 'unknown app'}))
self.finish()
return url
class StaticHandler(tornado.web.RequestHandler):
def get(self):
self.write('')
self.finish()
def publish_node():
update_online()
state._online = PeriodicCallback(update_online, 60000)
state._online.start()
def update_online():
host = get_public_ipv6()
if not host:
state.online = False
else:
if host != state.host:
state.host = host
online = directory.put(settings.sk, {
'host': host,
'port': settings.server['node_port'],
'cert': settings.server['cert']
})
state.online = online
def start():
application = tornado.web.Application([
(r"/", StaticHandler),
(r".*", NodeHandler),
], gzip=True)
http_server = HTTPServer(application, ssl_options={
"certfile": settings.tls_cert_path,
"keyfile": settings.tls_key_path
})
http_server.listen(settings.server['node_port'], settings.server['node_address'])
state.main.add_callback(publish_node)
return http_server