From 9aee12decdc32352c6d44a4129b0380eb744b606 Mon Sep 17 00:00:00 2001 From: Ruben van de Ven Date: Fri, 1 Nov 2019 17:02:38 +0100 Subject: [PATCH] Small status changes --- README.md | 31 ++++++++++++++++- sorteerhoed/HITStore.py | 2 ++ sorteerhoed/central_management.py | 6 ++-- sorteerhoed/webserver.py | 58 +++++++++++++++++++++---------- 4 files changed, 75 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index afe4418..1b2f68b 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Webserver is published to the web trough ssh remote forward. In /etc/ssh/sshd_co Then start `autossh` to maintain the connection: ```bash -autossh -M 0 -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -R 8888:localhost:8888 here.rubenvandeven.com +autossh -M 0 -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -R 8127:localhost:8888 here.rubenvandeven.com ``` @@ -59,3 +59,32 @@ sudo udevadm control --reload-rules && sudo udevadm trigger ``` + +## Apache on here.rubenvandeven.com + +Unfortunately an SSH remote port-forward does change the ip of the requester into ::1/127.0.0.1. One solution would be to run a proxy on the server itself, which forwards a port to our server port, while adding a X-Forwarded-For header. + +Example of apache host setup to forward remote port 8888 to local port 8127, to which we connect our (auto)ssh remote tunnel (see above). + +``` +Listen 8888 + + + Servername here.rubenvandeven.com + + RewriteEngine On + RewriteCond %{HTTP:Upgrade} =websocket [NC] + RewriteRule /(.*) ws://localhost:8127/$1 [P,L] + RewriteCond %{HTTP:Upgrade} !=websocket [NC] + RewriteRule /(.*) http://localhost:8127/$1 [P,L] + + ProxyPass / http://localhost:8127/ + ProxyPassReverse / http://localhost:8127/ + ProxyPreserveHost On + + +``` + +requires `a2enmod rewrite proxy proxy_http proxy_wstunnel` + + diff --git a/sorteerhoed/HITStore.py b/sorteerhoed/HITStore.py index 68cae14..65c7abc 100644 --- a/sorteerhoed/HITStore.py +++ b/sorteerhoed/HITStore.py @@ -89,6 +89,8 @@ class Store: Base.metadata.create_all(self.engine) self.Session = sessionmaker(bind=self.engine) self.session = self.Session() + + self.currentHit = None # mirrors Centralmanagmenet, stored here so we can quickly access it from webserver classes @contextmanager def getSession(self): diff --git a/sorteerhoed/central_management.py b/sorteerhoed/central_management.py index 9e93cf1..a5fcee0 100644 --- a/sorteerhoed/central_management.py +++ b/sorteerhoed/central_management.py @@ -151,12 +151,12 @@ class CentralManagement(): self.currentHit.open_page_at = datetime.datetime.utcnow() self.store.saveHIT(self.currentHit) self.setLight(True) - + self.server.statusPage.set('state', self.currentHit.getStatus()) elif signal.name == 'server.submit': self.currentHit.submit_page_at = datetime.datetime.utcnow() self.store.saveHIT(self.currentHit) self.plotter.park() - self.setLight(False) + self.server.statusPage.set('state', self.currentHit.getStatus()) # park always triggers a plotter.finished after being processed elif signal.name[:4] == 'sqs.': @@ -210,6 +210,7 @@ class CentralManagement(): self.server.statusPage.set('state', sqsHit.getStatus()) elif signal.name == 'plotter.finished': if self.currentHit.submit_page_at: + self.setLight(False) scan = threading.Thread(target=self.scanImage, name='scan') scan.start() else: @@ -219,6 +220,7 @@ class CentralManagement(): def makeHit(self): self.server.statusPage.reset() self.currentHit = self.store.createHIT() + self.store.currentHit = self.currentHit self.logger.info(f"Make HIT {self.currentHit.id}") diff --git a/sorteerhoed/webserver.py b/sorteerhoed/webserver.py index abf0468..23687a6 100644 --- a/sorteerhoed/webserver.py +++ b/sorteerhoed/webserver.py @@ -39,12 +39,11 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler): CORS_ORIGINS = ['localhost', '.mturk.com', 'here.rubenvandeven.com'] connections = set() - def initialize(self, config, plotterQ: Queue, eventQ: Queue, store: HITStore, geoip_reader: geoip2.database.Reader): + def initialize(self, config, plotterQ: Queue, eventQ: Queue, store: HITStore): self.config = config self.plotterQ = plotterQ self.eventQ = eventQ self.store = store - self.geoip_reader = geoip_reader def check_origin(self, origin): parsed_origin = urlparse(origin) @@ -55,14 +54,17 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler): # the client connected def open(self, p = None): self.__class__.connections.add(self) - hit_id = self.get_query_argument('id') - self.hit = self.store.getHitById(hit_id) + hit_id = int(self.get_query_argument('id')) + if hit_id != self.store.currentHit.id: + self.close() + return + + self.hit = self.store.currentHit if self.hit.submit_hit_at: raise Exception("Opening websocket for already submitted hit") - logger.info(f"New client connected: {self.request.remote_ip} for {self.hit.id}/{self.hit.hit_id}") - self.eventQ.put(Signal('hit.info', dict(hit_id=self.hit.id, ip=self.request.remote_ip))) + #logger.info(f"New client connected: {self.request.remote_ip} for {self.hit.id}/{self.hit.hit_id}") self.eventQ.put(Signal('server.open', dict(hit_id=self.hit.id))) self.strokes = [] @@ -71,14 +73,7 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler): if ua: ua_info = httpagentparser.detect(ua) self.eventQ.put(Signal('hit.info', dict(hit_id=self.hit.id, os=ua_info['os']['name'], browser=ua_info['browser']['name']))) - try: - geoip = self.geoip_reader.country(self.request.remote_ip) - logger.info(f"Geo {geoip}") - self.eventQ.put(Signal('hit.info', dict(hit_id=self.hit.id, location=geoip.country.name))) - except Exception as e: - logger.exception(e) - logger.info("No geo IP possible") - self.eventQ.put(Signal('hit.info', dict(hit_id=self.hit.id, location='Unknown'))) + # self.write_message("hello!") # the client sent the message @@ -109,6 +104,7 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler): 'action': 'submitted', 'msg': f"Submission ok, please refer to your submission as: {self.hit.uuid}" })) + self.close() elif msg['action'] == 'down': # not used, implicit in move? @@ -233,7 +229,7 @@ def strokes2D(strokes): return d; class DrawPageHandler(tornado.web.RequestHandler): - def initialize(self, store: HITStore, path: str, width: int, height: int, draw_width: int, draw_height: int, top_padding: int, left_padding: int): + def initialize(self, store: HITStore, eventQ: Queue, path: str, width: int, height: int, draw_width: int, draw_height: int, top_padding: int, left_padding: int, geoip_reader: geoip2.database.Reader): self.store = store self.path = path self.width = width @@ -242,11 +238,16 @@ class DrawPageHandler(tornado.web.RequestHandler): self.draw_height = draw_height self.top_padding = top_padding self.left_padding = left_padding + self.eventQ = eventQ + self.geoip_reader = geoip_reader def get(self): try: - hit_id = self.get_query_argument('id') - hit = self.store.getHitById(hit_id) + hit_id = int(self.get_query_argument('id')) + if hit_id != self.store.currentHit.id: + self.write("Invalid HIT") + return + hit = self.store.currentHit except Exception: self.write("HIT not found") else: @@ -273,6 +274,24 @@ class DrawPageHandler(tornado.web.RequestHandler): .replace("{TOP_PADDING}", str(self.top_padding))\ .replace("{LEFT_PADDING}", str(self.left_padding)) self.write(contents) + + if 'X-Forwarded-For' in self.request.headers: + ip = self.request.headers['X-Forwarded-For'] + else: + ip = self.request.remote_ip + + + logger.info(f"Request from {ip}") + self.eventQ.put(Signal('hit.info', dict(hit_id=hit.id, ip=ip))) + + try: + geoip = self.geoip_reader.country(ip) + logger.info(f"Geo {geoip}") + self.eventQ.put(Signal('hit.info', dict(hit_id=hit.id, location=geoip.country.name))) + except Exception as e: + logger.exception(e) + logger.info("No geo IP possible") + self.eventQ.put(Signal('hit.info', dict(hit_id=hit.id, location='Unknown'))) class BackendHandler(tornado.web.RequestHandler): def initialize(self, store: HITStore, path: str): @@ -385,19 +404,20 @@ class Server: 'plotterQ': self.plotterQ, 'eventQ': self.eventQ, 'store': self.store, - 'geoip_reader': self.geoip_reader }), (r"/status/ws", StatusWebSocketHandler), (r"/draw", DrawPageHandler, dict( store = self.store, + eventQ = self.eventQ, path=self.web_root, width=self.config['scanner']['width'], height=self.config['scanner']['height'], draw_width=self.config['scanner']['draw_width'], draw_height=self.config['scanner']['draw_height'], top_padding=self.config['scanner']['top_padding'], - left_padding=self.config['scanner']['left_padding'] + left_padding=self.config['scanner']['left_padding'], + geoip_reader= self.geoip_reader )), (r"/backend", BackendHandler, dict(