"""All webserver logic lives here, including routes""" from flask import Flask, Response from flask import request, jsonify import sys, os, re import cherrypy from paste.translogger import TransLogger import simplejson as json sys.path.append("./lib") from pb import * from db import Db from param import BadParamError from config import SERVER_HOST, SERVER_PORT #FIXME add remote_addr and this jsonp thing class InvalidUsage(Exception): """error class for InvalidUsage""" status_code = 400 def __init__(self, message, status_code=None, payload=None): Exception.__init__(self) self.message = message if status_code is not None: self.status_code = status_code self.payload = payload def to_dict(self): rv = dict(self.payload or ()) rv['message'] = self.message return rv class Server(object): """Main server class""" def __init__(self): self.app = Flask(__name__) self._wsgi_server = None @self.app.route('/test', methods=['GET']) def test(): return "HELLO WORLD!" @self.app.route('/im/api/', methods=['POST']) def pb(pb_classname): x_forwarded_headers = request.headers.getlist("X-Forwarded-For") if x_forwarded_headers: host = x_forwarded_headers[0] regex = re.compile(r'[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+') forwarded_ip = regex.search(host).group() else: forwarded_ip = None return self._response_post( pb_classname, request.form.to_dict(), remote_addr=forwarded_ip ) @self.app.errorhandler(InvalidUsage) def handle_invalid_usage(error): response = jsonify(error.to_dict()) response.status_code = error.status_code return response self._classname_aliases = { 'generate' : 'PbGenerate', 'imgrid' : 'PbGrid', 'imbreak' : 'PbBreaker', 'impattern' : 'PbPattern', 'imgradient' : 'PbGradient', 'landscape' : 'PbLandscape', } @self.app.route('/im/data', methods=['GET']) def get_data(): args_dict = request.args.to_dict() query_dict = {} for elem in ["newfile", "time"]: if args_dict.get(elem): query_dict[elem] = args_dict.get(elem) if args_dict: try: db = Db() return Response( #flask prevents from returning arrays, so we need the json module json.dumps(db.search(**query_dict)), mimetype='application/json' ) except Exception as e: return str(e) else: raise InvalidUsage('Improper Usage', status_code=410) def _find_class_by_name(self, pb_classname): pb_classname = self._classname_aliases.get(pb_classname, None) or pb_classname try: return filter(lambda c: c.__name__ == pb_classname, Pb.__subclasses__())[0] except IndexError: raise InvalidUsage('No such api', status_code=410) def _response_post(self, pb_classname, request_form, remote_addr=None): pb_class = self._find_class_by_name(pb_classname) classnames = map(lambda c: c.__name__, Pb.__subclasses__()) try: pb = pb_class(**request_form) pb.create() pb.file_s3move() pb.db_send(remote_addr=remote_addr) json_data = jsonify(pb.file_dict()) if pb.params.callback: #accounts for jsonp return "%s(%s)" % (pb.params.callback, json_data) return json_data except BadParamError: for i in request_form.keys(): sys.stderr.write('\'%s\':\'%s\'\n' % (i, request_form[i] or None)) return jsonify({'error' : 'Bad Params'}) except PbProcessError: sys.stderr.write(dict(request_form)) return jsonify({'error' : 'Problem with server-side processing' }) def run(self, host=SERVER_HOST, port=SERVER_PORT): self.app.run(host=host, port=port) def run_wsgi(self, server_port=SERVER_PORT, host=SERVER_HOST): #http://fgimian.github.io/blog/2012/12/08/setting-up-a-rock-solid-python-development-web-server/ # Enable WSGI access logging via Paste app_logged = TransLogger(self.app) # Mount the WSGI callable object (app) on the root directory cherrypy.tree.graft(app_logged, '/') # Set the configuration of the web server cherrypy.config.update({ 'engine.autoreload_on': True, 'log.screen': True, 'server.socket_port': server_port, 'server.socket_host': host }) cherrypy.engine.start() cherrypy.engine.block()