mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-01-28 08:03:36 +00:00
150 lines
4.4 KiB
Python
150 lines
4.4 KiB
Python
"""The HTTPS Santiago listener and sender.
|
|
|
|
"""
|
|
|
|
|
|
from santiago import SantiagoListener, SantiagoSender
|
|
|
|
import cherrypy
|
|
import httplib, urllib, urlparse
|
|
import sys
|
|
import logging
|
|
|
|
|
|
def setup(santiago):
|
|
"""Module-level setup function.
|
|
|
|
Called after listener and senders are set up, before they're started.
|
|
|
|
# TODO call this bugger to prep the dispatcher, objects, etc.
|
|
|
|
"""
|
|
pass
|
|
|
|
def start():
|
|
"""Module-level start function, called after listener and sender started.
|
|
|
|
TODO: integrate multiple servers:
|
|
|
|
http://docs.cherrypy.org/dev/refman/process/servers.html
|
|
|
|
"""
|
|
cherrypy.engine.start()
|
|
|
|
class Listener(SantiagoListener):
|
|
|
|
def __init__(self, santiago, socket_port=0,
|
|
ssl_certificate="", ssl_private_key=""):
|
|
|
|
super(SantiagoListener, self).__init__(santiago)
|
|
|
|
cherrypy.server.socket_port = socket_port
|
|
cherrypy.server.ssl_certificate = ssl_certificate
|
|
cherrypy.server.ssl_private_key = ssl_private_key
|
|
cherrypy.tree.mount(cherrypy.Application(self, "/"))
|
|
|
|
def start(self):
|
|
"""Starts the listener."""
|
|
pass
|
|
|
|
def stop(self):
|
|
"""Shuts down the listener."""
|
|
pass
|
|
|
|
@cherrypy.expose
|
|
def index(self, **kwargs):
|
|
"""Receive an incoming Santiago request from another Santiago client."""
|
|
|
|
logging.debug("protocols.https.index: Received request {0}".format(str(kwargs)))
|
|
try:
|
|
self.incoming_request(kwargs["request"])
|
|
except Exception as e:
|
|
logging.exception(e)
|
|
|
|
@cherrypy.expose
|
|
def learn(self, host, service):
|
|
"""Request a resource from another Santiago client.
|
|
|
|
TODO: add request whitelisting.
|
|
|
|
"""
|
|
# TODO enforce restfulness, POST, and build a request form.
|
|
# if not cherrypy.request.method == "POST":
|
|
# return
|
|
|
|
if not cherrypy.request.remote.ip.startswith("127.0.0."):
|
|
logging.debug("protocols.https.query: Request from non-local IP")
|
|
return
|
|
|
|
return super(Listener, self).learn(host, service)
|
|
|
|
@cherrypy.expose
|
|
def where(self, host, service):
|
|
"""Show where a host is providing me services.
|
|
|
|
TODO: make the output format a parameter.
|
|
|
|
"""
|
|
if not cherrypy.request.remote.ip.startswith("127.0.0."):
|
|
logging.debug("protocols.https.query: Request from non-local IP")
|
|
return
|
|
|
|
return list(super(Listener, self).where(host, service))
|
|
|
|
@cherrypy.expose
|
|
def provide(self, client, service, location):
|
|
"""Provide a service for the client at the location."""
|
|
|
|
if not cherrypy.request.remote.ip.startswith("127.0.0."):
|
|
logging.debug("protocols.https.query: Request from non-local IP")
|
|
return
|
|
|
|
return super(Listener, self).provide(client, service, location)
|
|
|
|
@cherrypy.expose
|
|
def pdb(self):
|
|
"""Set a trace."""
|
|
|
|
if not cherrypy.request.remote.ip.startswith("127.0.0."):
|
|
logging.debug("protocols.https.query: Request from non-local IP")
|
|
return
|
|
|
|
import pdb; pdb.set_trace()
|
|
|
|
class Sender(SantiagoSender):
|
|
|
|
def __init__(self, santiago, proxy_host, proxy_port):
|
|
|
|
super(SantiagoSender, self).__init__(santiago)
|
|
self.proxy_host = proxy_host
|
|
self.proxy_port = proxy_port
|
|
|
|
def outgoing_request(self, request, destination):
|
|
"""Send an HTTPS request to each Santiago client.
|
|
|
|
Don't queue, just immediately send the reply to each location we know.
|
|
|
|
It's both simple and as reliable as possible.
|
|
|
|
``request`` is literally the request's text. It needs to be wrapped for
|
|
transport across the protocol.
|
|
|
|
"""
|
|
logging.debug("protocols.https.Sender.outgoing_request: request {0}".format(str(request)))
|
|
to_send = { "request": request }
|
|
|
|
params = urllib.urlencode(to_send)
|
|
logging.debug("protocols.https.Sender.outgoing_request: params {0}".format(str(params)))
|
|
|
|
# TODO: Does HTTPSConnection require the cert and key?
|
|
# Is the fact that the server has it sufficient? I think so.
|
|
connection = httplib.HTTPSConnection(destination.split("//")[1])
|
|
|
|
# proxying required and available only in Python 2.7 or later.
|
|
# TODO: fail if Python version < 2.7.
|
|
if sys.version_info >= (2, 7):
|
|
connection.set_tunnel(self.proxy_host, self.proxy_port)
|
|
|
|
connection.request("GET", "/?%s" % params)
|
|
connection.close()
|