ApacheにWebSocketサーバーをリバースプロキシでつないでみる
2024年11月11日 Posted 野々瀨(フロントエンドエンジニア)
今回は直接WebSocketサーバーと直接通信するのではなく、Apacheのリバースプロキシを使用して、Apache経由でWebSocketと通信する方法をご紹介しようと思います。
環境
今回は次の環境で説明を行います。
- OS:Ubuntu
- Webサーバー:Apache 2.4
- WebSocketサーバー:Node.js
- クライアント:JavaScript(ブラウザー)
WebSocketのサーバー準備
WebSocketのサーバーはPHPやPython、Node.jsなどで構築することができます。
ここでは次のようにNode.jsのwsモジュールを使用して構築します。
- * wsモジュールはインストールが必要です
- * コードの説明は省略させていただきます
const WebSocket = require('ws');
const server = new WebSocket.Server({
host: 'localhost',
port: 3000
});
server.on('connection', socket => {
socket.on('message', message => {
console.log('受信: ', message.toString());
server.clients.forEach(client => {
client.send('受け取ったよ');
});
});
socket.on('close', id => {
console.log('切断した: ', id);
});
socket.on('error', err => {
console.log('エラー: ', err);
});
});
次のコマンドでWebSocketサーバーを起動しておきます。
node server.js
Apacheの設定
a2enmod
コマンドを使用してリバースプロキシのモジュールを有効にします。
sudo a2enmod proxy proxy_wstunnel
モジュール名 | 指定値 | 説明 |
---|---|---|
proxy | mod_proxy | Apacheでリバースプロキシを利用するためのモジュール。 |
proxy_wstunnel | mod_proxy_wstunnel | リバースプロキシでWebSocketへの通信を可能にするためのモジュール。 |
なお、httpd.confファイルの場合は、次の二つの項目からコメントアウトを外します。
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
続いてホストに割り当てるリバースプロキシを設定します。
ここではバーチャルホストで設定する例をご紹介します。
「/etc/apache2/sites-available/000-default.conf」ファイルを編集します。
次の内容を記述します。
<VirtualHost *:80>
ServerName sample.localhost
DocumentRoot /var/www/html/sample
ProxyRequests Off
ProxyPreserveHost On
ProxyPass /api/foo/ ws://localhost:3000/
ProxyPassReverse /api/foo/ ws://localhost:3000/
</virtualhost>
SSLの場合も同様です。
<VirtualHost *:443>
# ...
# 省略
# ...
SSLProxyEngine On
ProxyRequests Off
ProxyPreserveHost On
ProxyPass /api/foo/ wss://localhost:3000/
ProxyPassReverse /api/foo/ wss://localhost:3000/
</VirtualHost>
ディレクティブ | 説明 |
---|---|
ProxyRequests | フォワードプロキシを有効にするかどうか。 |
ProxyPreserveHost | 転送先に転送元のヘッダー情報を保持するかどうか。 |
ProxyPass | どこからどこへ転送するか。 例えば「wss://sample.localhost/api/foo/でアクセスしてきたときに、「wss://localhost:3000/」へ転送する。 |
ProxyPassReverse | HTTPヘッダーのLocationを確認し、指定した値に書き換える。ProxyPassと同じで問題ない。 |
SSLProxyEngine | プロキシのSSL/TLSを有効にするかどうか。 |
次のコマンドでApacheの設定を再読み込みします。
sudo systemctl reload apache2
クライアントの準備
今回はブラウザーで確認するため、HTMLページを用意します。
次のように、入力フォームとサーバーから受け取った情報を視覚的に表示するためのHTMLを用意します。
<form>
<input name="keyword" autocomplete="off">
<button>送信</button>
</form>
<div id="logs"></div>
次にやり取りを行うためのJavaScriptを用意します。
- * コードの説明は省略させていただきます
(() => {
/**
* WebSocketサーバーのパス
*/
const API_PATH = 'ws://sample.localhost/api/foo/';
// 接続
const socket = new WebSocket(API_PATH);
// サーバーの接続に成功したとき
socket.addEventListener('open', () => {
console.log('サーバー接続開始');
});
// サーバーの接続に失敗したとき
socket.addEventListener('error', () => {
console.log('サーバー接続失敗');
});
// サーバーが切断されたとき
socket.addEventListener('close', () => {
console.log('サーバー切断');
});
window.addEventListener('DOMContentLoaded', () => {
const formElem = document.forms[0];
const logsElem = document.getElementById('logs');
// 送信
formElem.addEventListener('submit', event => {
event.preventDefault();
const sendValue = formElem.keyword.value;
if (sendValue) {
formElem.keyword.value = '';
socket.send(sendValue);
}
});
// サーバーからメッセージを受け取る
socket.addEventListener('message', event => {
const logElem = document.createElement('p');
const firstChildElem = logsElem.firstChild;
logElem.textContent = decodeURIComponent(event.data);
if (firstChildElem) {
logsElem.insertBefore(logElem, firstChildElem);
} else {
logsElem.appendChild(logElem);
}
});
});
window.addEventListener('unload', () => {
socket.close();
});
})();
ブラウザーで確認
用意したページをブラウザーで開きます。
入力欄に適当に入力し、「送信」ボタンを押します。
サーバー側には次の画像のように表示されます。
ブラウザー側は次の画像のように表示されます。
終わりに
今回はWebSocketサーバーをApacheのリバースプロキシを経由してやり取りをする方法をご紹介しました。
Apacheのリバースプロキシは、別のプログラムをApacheを経由して処理することができますので、対応していればさまざまなプログラムをWebサーバから動かすことができるようになります。