最近はDjango上でWebsocketを使えるようにするChannelsを仕事でひたすら触っています。さて、Channelsで実装したWebSocketサーバに対して負荷テストをしたいと思ったときには、ログイン処理について考えなければいけません。
ほとんどの場合、ChannelsではDjangoのsessionオブジェクトを使うことができるので、大抵の場合はDjangoでログインしているかを接続受け入れの条件になっています。これを踏まえると、ログイン処理とWebsocketでのテスト処理を用意しなければいけません。これを実現するために少し試行錯誤したのでまとめておきます。
必要な要件
今回必要になるのは、以下の2つです。
- ログインしたHTTPセッションを保持すること
- ログインしたセッション情報を持ってWebsocket経由で任意のメッセージを送受信できること
PythonでHTTPリクエストということであれば、requests
がデファクトでしょう。
WebsocketのClientはいくつかライブラリがありましたがwebsocket-client
が良さそうだったのでこちらを使うことにしました。
この2つを組み合わせ、以下のような処理をします。
- requestsでログイン処理を実行
- ログインセッションのsessionidを取得
- websocket-clientでの接続時にsessionidをCookieに含めて通信
ログイン処理
Djangoではusername
とpassword
をLOGIN_URLにPOSTしてやればログインできます。ただし、csrf
対策のミドルウェアがほとんどの場合有効化されているので、それを考慮する必要があります。
import requests client = requests.session() # csrftokenを含むCookie取得のためGETする client.get(LOGIN_URL) csrftoken = client.cookies['csrftoken'] login_data = dict( username=self.username, password=self.password, csrfmiddlewaretoken=csrftoken, next='/' ) r = client.post(self.login_url, data=login_data, headers=dict(Referer=LOGIN_URL))
事前にログインページにGETアクセスし、csrftoken
のCookieを取得してからそれを含めてPOSTすることでログインできます。Djangoのセッション情報はサーバ側(バックエンドのDB内)に格納されており、ブラウザのCookieにsessionid
だけを記録させる実装になっています(たしか。。。)。そのため、ログイン済のセッションのsessionid
さえわかれば、セッションオブジェクトへのアクセスはDjangoがよろしくやってくれます。
ログインさえ、成功していれば以下でsessionid
にアクセスできます。
session_id = client.cookies['sessionid']
websocket-clientでの接続
websocket-client
ではWebSocketApp
というクラスをインスタンス化して直接操作する方法もあるようですが、スクリプト的に使うのであればwebsocket.create_connection
関数を利用してメッセージを送受信するほうが簡単でした。create_connection
は引数にcookie
やheader
を取れますので、そこで先程取得したsessionid
を渡してやります。
ws = create_connection( WEBSOCKET_URL, #ws://localhost:3000/ws/page cookie='sessionid={}'.format(session_id), )
これでChannels側では該当のWebsocketでのリクエストはDjangoで認証済のセッションとして扱われます。後はsend
やrecv
メソッドでデータの送受信ができます。
import json # jsonを期待されている場合はjson.dumpsすれば良い message = ws.send(json.dumps({'action': 'hello'})) # 返答の受け取り message = ws.recv()
これで、認証が必要なWebsocketサーバに対していろいろなテストをすることができるようになりました。
まとめ
Django Channelsで実装したWebsocketサーバへの接続スクリプトの流れをまとめました。まとめてしまえば簡単な話でしたが、いざ0からといわれるとちょっと面倒だったので誰かの参考になれば幸いです。
なお、Flask等はセッションオブジェクトはクライアントのCookie内にすべて保持するはずですので、その場合はsessionid以外のCookieすべてをwebsocket-clientにわたす必要があるかと思います。