Apacheで同じURLでもwebsocketとhttpを両方見れるようにする

半年ほど悩んでいたのですがやっと解決できたのでまとめます。
WebsocketではHTTPをUpgradeしたプロトコルとして扱われ、COMETとは違いajaxやiframeなどによるコネクションをポーリングする技術とは異なる双方向通信を実現するプロトコルです。
なのでWebsocket自体に予約されたポートは無く、クライアントからは80や443で通信することも可能です。といっても、サーバー側がWebsocketに対応している必要があります。

前提条件

同じURIだけど、websocketとhttp両方見れるようにしたい

例えばストリーミングを行うAPIがあったとします。

/api/v1/streaming

このAPIのリクエスト時に、 http://(https://)であれば、 COMET等の技術によりコネクションがポーリングされストリーミングで受信処理が行われる用にしたいが、
ws://(wss://) の場合は、websocketでストリーミングで受信処理を行わるようにしたい場合、Apacheでは下記のようにすると問題が出ます。

ProxyPass /api/v1/streaming/ ws://localhost:4000/
ProxyPassReverse /api/v1/streaming/ ws://localhost:4000/
ProxyPass /api/v1/streaming/ http://localhost:4000/
ProxyPassReverse /api/v1/streaming/ http://localhost:4000/

これだと、最初のProxyPassで潰されてしまい、httpの通信に失敗してしまいます。
この場合、wsの場合はmod_rewriteを使うことで解決できます。
websocketは、ConnectionがUpgradeになるので、それをrewriteの条件に指定することでwebsocketの場合のみwsに接続するように変更する事ができます。

 RewriteEngine On RewriteCond %{HTTP:Connection} Upgrade [NC] RewriteCond %{HTTP:Upgrade} websocket [NC] RewriteRule /api/v1/streaming/(.*) ws://localhost:4000/api/v1/streaming/$1 [P,L] ProxyPreserveHost On ProxyPass /api/v1/streaming/ http://localhost:4000/api/v1/streaming/ ProxyPassReverse /api/v1/streaming/ http://localhost:4000/api/v1/streaming/ ProxyPass / http://localhost:3000/ ProxyPassReverse / http://localhost:3000/

参考URL