Google App Engineで動くpythonのWAFの作り方

angelosは元々Google App Engineで動かす事を目指して作っていたんですが、GAE for perlがでないので、pythonでWAF作るとどんな感じに作れるのかなと思って少し作ってみました。

http://github.com/dann/gae-ploudy/tree/master

これは基本部分の作り方のサンプルで本気でpython版を作るつもりはないんですが、試した限りではangelosをGAE上にpython porting版作るのは簡単ですね。

では、以下作り方です。

RequestDispatcher

まずはリクエスト受けて、dispatcherに処理を委譲する部分です
リクエストは、webapp.RequestHandlerが受けるので、そのクラスを継承して、requestをdispatcherにdispatchします。

class RequestHandler(webapp.RequestHandler):
  def get(self, *args):
    self._handle_request()
 
  def post(self, *args):
    self._handle_request()
 
  def head(self, *args):
    self._handle_request()
 
  def options(self, *args):
    self._handle_request()
 
  def put(self, *args):
    self._handle_request()
 
  def delete(self, *args):
    self._handle_request()
 
  def trace(self, *args):
    self._handle_request()
 
  def _handle_request(self):
    dispatcher.dispatch(self)

dispatcherでは、HTTP::Routerっぽいrouting機構を用意してroutingします。ただ、今回は手抜きして、/:controller/:actionにdispatchしてます。

Component Loader

controllerとか読み込むとこですね。今回はえらく手をぬいてるので、dispatcher部分にごりっと実装しちゃってますが、ここらは起動タイミングでインスタンス作っておくべきでしょうね。

def create_controller_instance(route, handler):
  exec('from controller import ' + route['controller'].capitalize() + 'Controller') in globals()
  controller = eval( route['controller'].capitalize() + 'Controller' )
  return controller(route,handler)

ApplicationのSetup

任意のパスに来たRequestをRequestHandlerに委譲する。これは、webapp.WSGIApplicationでやります。pathとRequestHandlerのマッピングを書きます。これで、requestが来たら、controller#actionが実行されますね。

def setup(app_base_dir):
  setup_app_home(app_base_dir)
  app = setup_application()
  app = setup_middlewares(app);
  return app 

def setup_application():
  app = webapp.WSGIApplication([
      (r'.*', RequestHandler),
    ], debug=True)
  return app


この作成したWSGI appは、Middlewareでラップすれば、middlewareが使えるようになります。

起動部分

起動部分は、main.pyというスクリプトに書くのだけれど、以下のような感じ。
webapp.utilでwsgiアプリを実行。

from google.appengine.ext.webapp.util import run_wsgi_app
import ploudy

def main():
  application = ploudy.setup(__file__) 
  run_wsgi_app(application)

if __name__ == '__main__':
  main()

HTTP::Engine使ってる人は、大体同じような感じで作れそうだなぁということがわかるんじゃないかと思います。

# See also: angelosで学ぶ優しいWAFの作り方
http://dann.github.com/presentation/yokohamapm4/start.html

Java版も同じような感じで作れます。