WAFをつくってる話
大阪Pythonユーザの集まり 2016/04
MASASHI SHIBATA
! c_bata_
@c_bata_
django / flask / pandas
明石高専
話すこと
1. WSGIの話とか
2. WAFに欲しい機能を考える
3. Kobinの紹介
4. Kobinにおける取り組み
WSGI
Web Server Gateway Interface
WSGI??
• Pythonで作るWebアプリケーションの実装方
法の標準化仕様
• 実装を切り離して、WebサーバとWAFの組み
合わせを柔軟に
https://www.python.org/dev/peps/pep-3333/
https://www.python.org/dev/peps/pep-333/
WSGI v1.0.1 (PEP3333)
• 2つの引数を持った呼び出し可能なオブジェクト
• 第2引数として渡されたオブジェクトにHTTPス
テータスコードと (header_name,
header_value) タプルのリストを渡す
• 返り値はバイト文字列を yield する iterableなオ
ブジェクト
https://www.python.org/dev/peps/pep-3333/
WSGI v1.0.1 (PEP3333)
def app(env, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
return [b"Hello World"]
• 2つの引数を持った呼び出し可能なオブジェクト
https://www.python.org/dev/peps/pep-3333/
WSGI v1.0.1 (PEP3333)
def app(env, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
return [b"Hello World"]
• 第2引数として渡されたオブジェクトの引数は

HTTPステータスコードと

(header_name, header_value) タプルのリスト
https://www.python.org/dev/peps/pep-3333/
WSGI v1.0.1 (PEP3333)
def app(env, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
return [b"Hello World"]
• バイト文字列を yield する iterable を返さなければ
ならない
• 例えば、バイト文字列のリストを返すようにする
https://www.python.org/dev/peps/pep-3333/
Hello World with gunicorn
$ gunicorn -w 1 main:app
[2016-04-15 10:17:00 +0900] [1873] [INFO] Starting gunicorn 19.4.5
[2016-04-15 10:17:00 +0900] [1873] [INFO] Listening at: http://127.0.0.1:8000 (1873)
[2016-04-15 10:17:00 +0900] [1873] [INFO] Using worker: sync
[2016-04-15 10:17:00 +0900] [1878] [INFO] Booting worker with pid: 1878
^C[2016-04-15 10:17:08 +0900] [1873] [INFO] Handling signal: int
[2016-04-15 10:17:08 +0900] [1878] [INFO] Worker exiting (pid: 1878)
[2016-04-15 10:17:08 +0900] [1873] [INFO] Shutting down: Master
def app(env, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
return [b"Hello World"]
Hello World with wsgiref
$ python main.py
127.0.0.1 - - [15/Apr/2016 10:24:21] "GET / HTTP/1.1" 200 11
127.0.0.1 - - [15/Apr/2016 10:24:21] "GET /favicon.ico HTTP/1.1" 200 11
def app(env, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
return [b"Hello World"]



if __name__ == '__main__':
from wsgiref.simple_server import make_server
httpd = make_server('', 8080, app)
httpd.serve_forever()
WAFで欲しい機能を考える
最低限欲しい機能
• ルーティング
• どこにアクセスしても Hello World
• HTMLテンプレート
• 今はplain text返してるだけ
• Jinja2のTemplate Loaderを用意
最低限欲しい機能
• 静的ファイルをいい感じに返す機能
• 本番だとNginxとか使うけど、開発中は...
• リクエスト・レスポンスオブジェクト
• リクエスト情報はenvが渡されてるけど、

ただのdictなのでうまくパースしてほしい
• ステータスとかヘッダ情報をコントロール
a small and statically-typed web framework
kobin
https://github.com/c-bata/kobin
a small and statically-typed web framework
kobin
https://github.com/c-bata/kobin
Hello World
from kobin import Kobin
app = Kobin()
@app.route('^/(?P<name>w*)$')
def hello(name: str):
return "Hello {}!!".format(name)
if __name__ == '__main__':
app.run()
• 日本語: https://kobin.readthedocs.org/ja/latest/
• English: https://kobin.readthedocs.org/en/latest/
Hello World with Jinja2
import os
from kobin import Kobin, template
app = Kobin()
@app.route('^/$')
def index():
return template('index')
• https://github.com/c-bata/kobin-example
• https://kobin.herokuapp.com/
特徴
• Type Hintsの活用
• Bottleのコードを読んでる時に結構混乱
• mypy 使いたい
• ルーティングへの活用
Routing in Django
from django.conf.urls import url
urlpatterns = [
url(r'^blog/page(?P<num>[0-9]+)/$', page),
]
def page(request, num="1"):
# Output the appropriate page of blog entries
...
https://docs.djangoproject.com/en/1.9/topics/http/urls/
• 引数が全て文字列
Routing in Bottle
from bottle import route
@route('/object/<id:int>')
def callback(id):
assert isinstance(id, int)
http://bottlepy.org/docs/dev/tutorial.html#request-routing
• 独自DSL
• 型が分かるため、view関数には型変換したオブジェ
クトを渡すことができる
ルーティングに対する考察
• 正規表現ベース (Django等)
• 自由度は高い
• 型情報を取得出来ない
• 独自DSL (Bottle, Flask等)
• 自由度は低い
• 型情報を自由に付けれる
Routing in Kobin
from kobin import Kobin
app = Kobin()
@app.route('^/years/(?P<year>d{4})$')
def casted_year( year: int ):
return 'A "year" argument is integer? : {}’ 
.format(isinstance(year, int))
https://github.com/c-bata/kobin/blob/master/example/hello_world.py
• 正規表現により自由度が高い
• Type Hintsの恩恵(IDE, mypy)をそのまま受けれる
Ecosystem Threats to Python
• Pythonのエコシステムはその巨大さゆえに、バー
ジョンアップについていきづらい
• コミュニティとしてもPython3に移行していきたい
• Type Hintsを移行のきっかけに
PyCon APAC/Taiwan 2015: Keynote
References
• Documentations
• https://kobin.readthedocs.org/ja/latest/
• https://kobin.readthedocs.org/en/latest/
• Kobin Example
• https://github.com/c-bata/kobin-example
• https://kobin.herokuapp.com/
おまけ
PEP333とPEP3333
PEP333 と PEP3333
$ python2.7
>>> b'hoge' + u'日本語'
u'hogeu65e5u672cu8a9e'
$ python3.5
>>> b'hoge' + u'日本語'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't concat bytes to str
PEP333 と PEP3333
• PEP333 (WSGI v1.0)
• Python2ではbytesとstrを結合出来たり…
• Python3で文字列の扱いが大きく変わった
• PEP3333 (WSGI v1.0.1)
• 後方互換は保ったまま、文字列の扱いを整備して
長年たまってたデファクトの修正案も取り込み
Thanks!

Webフレームワークを作ってる話 #osakapy

  • 1.
  • 2.
    @c_bata_ django / flask/ pandas 明石高専
  • 3.
    話すこと 1. WSGIの話とか 2. WAFに欲しい機能を考える 3.Kobinの紹介 4. Kobinにおける取り組み
  • 4.
  • 5.
  • 6.
    WSGI v1.0.1 (PEP3333) •2つの引数を持った呼び出し可能なオブジェクト • 第2引数として渡されたオブジェクトにHTTPス テータスコードと (header_name, header_value) タプルのリストを渡す • 返り値はバイト文字列を yield する iterableなオ ブジェクト https://www.python.org/dev/peps/pep-3333/
  • 7.
    WSGI v1.0.1 (PEP3333) defapp(env, start_response): start_response('200 OK', [('Content-Type', 'text/plain')]) return [b"Hello World"] • 2つの引数を持った呼び出し可能なオブジェクト https://www.python.org/dev/peps/pep-3333/
  • 8.
    WSGI v1.0.1 (PEP3333) defapp(env, start_response): start_response('200 OK', [('Content-Type', 'text/plain')]) return [b"Hello World"] • 第2引数として渡されたオブジェクトの引数は
 HTTPステータスコードと
 (header_name, header_value) タプルのリスト https://www.python.org/dev/peps/pep-3333/
  • 9.
    WSGI v1.0.1 (PEP3333) defapp(env, start_response): start_response('200 OK', [('Content-Type', 'text/plain')]) return [b"Hello World"] • バイト文字列を yield する iterable を返さなければ ならない • 例えば、バイト文字列のリストを返すようにする https://www.python.org/dev/peps/pep-3333/
  • 10.
    Hello World withgunicorn $ gunicorn -w 1 main:app [2016-04-15 10:17:00 +0900] [1873] [INFO] Starting gunicorn 19.4.5 [2016-04-15 10:17:00 +0900] [1873] [INFO] Listening at: http://127.0.0.1:8000 (1873) [2016-04-15 10:17:00 +0900] [1873] [INFO] Using worker: sync [2016-04-15 10:17:00 +0900] [1878] [INFO] Booting worker with pid: 1878 ^C[2016-04-15 10:17:08 +0900] [1873] [INFO] Handling signal: int [2016-04-15 10:17:08 +0900] [1878] [INFO] Worker exiting (pid: 1878) [2016-04-15 10:17:08 +0900] [1873] [INFO] Shutting down: Master def app(env, start_response): start_response('200 OK', [('Content-Type', 'text/plain')]) return [b"Hello World"]
  • 11.
    Hello World withwsgiref $ python main.py 127.0.0.1 - - [15/Apr/2016 10:24:21] "GET / HTTP/1.1" 200 11 127.0.0.1 - - [15/Apr/2016 10:24:21] "GET /favicon.ico HTTP/1.1" 200 11 def app(env, start_response): start_response('200 OK', [('Content-Type', 'text/plain')]) return [b"Hello World"]
 
 if __name__ == '__main__': from wsgiref.simple_server import make_server httpd = make_server('', 8080, app) httpd.serve_forever()
  • 12.
  • 13.
    最低限欲しい機能 • ルーティング • どこにアクセスしてもHello World • HTMLテンプレート • 今はplain text返してるだけ • Jinja2のTemplate Loaderを用意
  • 14.
    最低限欲しい機能 • 静的ファイルをいい感じに返す機能 • 本番だとNginxとか使うけど、開発中は... •リクエスト・レスポンスオブジェクト • リクエスト情報はenvが渡されてるけど、
 ただのdictなのでうまくパースしてほしい • ステータスとかヘッダ情報をコントロール
  • 15.
    a small andstatically-typed web framework kobin https://github.com/c-bata/kobin
  • 16.
    a small andstatically-typed web framework kobin https://github.com/c-bata/kobin
  • 17.
    Hello World from kobinimport Kobin app = Kobin() @app.route('^/(?P<name>w*)$') def hello(name: str): return "Hello {}!!".format(name) if __name__ == '__main__': app.run() • 日本語: https://kobin.readthedocs.org/ja/latest/ • English: https://kobin.readthedocs.org/en/latest/
  • 18.
    Hello World withJinja2 import os from kobin import Kobin, template app = Kobin() @app.route('^/$') def index(): return template('index') • https://github.com/c-bata/kobin-example • https://kobin.herokuapp.com/
  • 19.
    特徴 • Type Hintsの活用 •Bottleのコードを読んでる時に結構混乱 • mypy 使いたい • ルーティングへの活用
  • 20.
    Routing in Django fromdjango.conf.urls import url urlpatterns = [ url(r'^blog/page(?P<num>[0-9]+)/$', page), ] def page(request, num="1"): # Output the appropriate page of blog entries ... https://docs.djangoproject.com/en/1.9/topics/http/urls/ • 引数が全て文字列
  • 21.
    Routing in Bottle frombottle import route @route('/object/<id:int>') def callback(id): assert isinstance(id, int) http://bottlepy.org/docs/dev/tutorial.html#request-routing • 独自DSL • 型が分かるため、view関数には型変換したオブジェ クトを渡すことができる
  • 22.
    ルーティングに対する考察 • 正規表現ベース (Django等) •自由度は高い • 型情報を取得出来ない • 独自DSL (Bottle, Flask等) • 自由度は低い • 型情報を自由に付けれる
  • 23.
    Routing in Kobin fromkobin import Kobin app = Kobin() @app.route('^/years/(?P<year>d{4})$') def casted_year( year: int ): return 'A "year" argument is integer? : {}’ .format(isinstance(year, int)) https://github.com/c-bata/kobin/blob/master/example/hello_world.py • 正規表現により自由度が高い • Type Hintsの恩恵(IDE, mypy)をそのまま受けれる
  • 24.
    Ecosystem Threats toPython • Pythonのエコシステムはその巨大さゆえに、バー ジョンアップについていきづらい • コミュニティとしてもPython3に移行していきたい • Type Hintsを移行のきっかけに PyCon APAC/Taiwan 2015: Keynote
  • 25.
    References • Documentations • https://kobin.readthedocs.org/ja/latest/ •https://kobin.readthedocs.org/en/latest/ • Kobin Example • https://github.com/c-bata/kobin-example • https://kobin.herokuapp.com/
  • 26.
  • 27.
    PEP333 と PEP3333 $python2.7 >>> b'hoge' + u'日本語' u'hogeu65e5u672cu8a9e' $ python3.5 >>> b'hoge' + u'日本語' Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: can't concat bytes to str
  • 28.
    PEP333 と PEP3333 •PEP333 (WSGI v1.0) • Python2ではbytesとstrを結合出来たり… • Python3で文字列の扱いが大きく変わった • PEP3333 (WSGI v1.0.1) • 後方互換は保ったまま、文字列の扱いを整備して 長年たまってたデファクトの修正案も取り込み
  • 29.