136 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			136 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import http.cookies
 | |
| import logging
 | |
| import json
 | |
| import collections
 | |
| from . import httpd
 | |
| from . import sse
 | |
| from . import fire, get_context
 | |
| from . import sessions
 | |
| 
 | |
| 
 | |
| # Logging
 | |
| logger = logging.getLogger(__name__)
 | |
| 
 | |
| # bring some external handlers into this module's scope
 | |
| # (now you can just import eca.http and have access to all standard handlers)
 | |
| StaticContent = httpd.StaticContent
 | |
| SessionManager = sessions.SessionManager
 | |
| 
 | |
| 
 | |
| class Cookies(httpd.Filter):
 | |
|     """Filter to read cookies from request."""
 | |
|     def handle(self):
 | |
|         # process available cookies
 | |
|         cookies = http.cookies.SimpleCookie()
 | |
|         if 'cookie' in self.request.headers:
 | |
|             cookies.load(self.request.headers['cookie'])
 | |
| 
 | |
|         # set cookies on request
 | |
|         self.request.cookies = cookies
 | |
| 
 | |
| 
 | |
| 
 | |
| class HelloWorld(httpd.Handler):
 | |
|     """The mandatory Hello World example."""
 | |
|     def handle_GET(self):
 | |
|         self.request.send_response(200)
 | |
|         self.request.send_header('content-type','text/html; charset=utf-8')
 | |
|         self.request.end_headers()
 | |
| 
 | |
|         output = "<!DOCTYPE html><html><body><h1>Hello world!</h1><p><i>eca-session:</i> {}</p></body></html>"
 | |
| 
 | |
|         try:
 | |
|             if not hasattr(self.request, 'cookies'): raise KeyError()
 | |
|             cookie = self.request.cookies['eca-session'].value
 | |
|         except KeyError:
 | |
|             cookie = '<i>no cookie</i>';
 | |
|         
 | |
|         self.request.wfile.write(output.format(cookie).encode('utf-8'))
 | |
| 
 | |
| 
 | |
| def Redirect(realpath):
 | |
|     """
 | |
|     Factory for redirection handlers.
 | |
|     """
 | |
|     class RedirectHandler(httpd.Handler):
 | |
|         def handle_GET(self):
 | |
|             location = None
 | |
| 
 | |
|             # check for absolute paths
 | |
|             if realpath.startswith("http://") or realpath.startswith('https://'):
 | |
|                 location = realpath
 | |
|             else:
 | |
|                 host = self.request.server.server_address[0]
 | |
|                 if self.request.server.server_address[1] != 80:
 | |
|                     host += ":{}".format(self.request.server.server_address[1])
 | |
|     
 | |
|                 if 'host' in self.request.headers:
 | |
|                     host = self.request.headers['host']
 | |
|     
 | |
|                 location = "http://{}{}".format(host, realpath)
 | |
| 
 | |
|             self.request.send_response(302)
 | |
|             self.request.send_header('content-type','text/html; charset=utf-8')
 | |
|             self.request.send_header('location',location)
 | |
|             self.request.end_headers()
 | |
|             output = "<!DOCTYPE html><html><body><p>Redirect to <a href='{0}'>{0}</a></p></body></html>"
 | |
|             self.request.wfile.write(output.format(location).encode('utf-8'))
 | |
| 
 | |
|     return RedirectHandler
 | |
| 
 | |
| 
 | |
| def GenerateEvent(name):
 | |
|     """
 | |
|     This function returns a handler class that creates the named event based
 | |
|     on the posted JSON data.
 | |
|     """
 | |
|     class EventGenerationHandler(httpd.Handler):
 | |
|         def handle_POST(self):
 | |
|             # handle weirdness
 | |
|             if 'content-length' not in self.request.headers:
 | |
|                 self.request.send_error(411)
 | |
|                 return
 | |
| 
 | |
|             # read content-length header
 | |
|             length = int(self.request.headers['content-length'])
 | |
| 
 | |
|             # grab data
 | |
|             data = self.request.rfile.read(length)
 | |
|             try:
 | |
|                 structured = json.loads(data.decode('utf-8'))
 | |
|             except ValueError as e:
 | |
|                 self.request.send_error(400, "Bad request: "+str(e))
 | |
|                 return
 | |
| 
 | |
|             if not isinstance(structured, collections.abc.Mapping):
 | |
|                 self.request.send_error(400, "Bad request: expect a JSON object")
 | |
|                 return
 | |
| 
 | |
|             try:
 | |
|                 fire(name, structured)
 | |
|             except NotImplementedError:
 | |
|                 logger.warn("Event generated by HTTP request without active session. Do you have a SessionManager configured?")
 | |
|                 self.request.send_error(500, "No current context available.")
 | |
|                 return
 | |
| 
 | |
|             self.request.send_response(202)
 | |
|             self.request.send_header('content-type', 'text/plain; charset=utf-8')
 | |
|             self.request.send_header('content-length', 0)
 | |
|             self.request.end_headers()
 | |
| 
 | |
|     return EventGenerationHandler
 | |
| 
 | |
| 
 | |
| class EventStream(sse.ServerSideEvents):
 | |
|     def go_subscribe(self):
 | |
|         def receiver(name, event):
 | |
|             self.send_event(event.data.get('json'), event.name, event.data.get('id'))
 | |
| 
 | |
|         self.receiver = receiver
 | |
|         context = get_context()
 | |
|         context.channel.subscribe(self.receiver, 'emit')
 | |
| 
 | |
|     def go_unsubscribe(self):
 | |
|         context = get_context()
 | |
|         context.channel.unsubscribe(self.receiver, 'emit')
 |