XserverにおけるSocket.IOベースWebサービスの環境設定

# 概要
## 発端

* Xserverにおいて、listenした特定ポートにアクセスしても応答がない
* 直接、特定ポートへアクセスできないため、なんらかの形でNAPTを実現する必要があった
* root特権を得られないという提供サービスの性質上、サブドメインと.htaccessとへの追加設定で実現できた
* 明示的に、こうした情報がGoogle先生から得られなかったのでここに記す

---
## 得られた知見

* Xserverでは、PUSH通信サービスは運用できない
    * WebSocket通信で400エラーが生じるため
    * nginx設定でNAPTを構成できないことによる
* ただし、polling方式によるPULL通信はできるので、独自Webサービスそのものは提供できる
    * Socket.IOはPUSH/PULL両通信方式をデフォルトで試みる
    * 通信効率やサーバ負荷を考えると、PULL通信では大規模なものは不適

---

## 結論
1. フロントで動くWebサーバを変更できず、特定ポートでWebサービスを提供する場合
    * 正しくプロキシを構成すれば良い
        * nginxの場合([Nginxを使用して、配信したHTMLと同一のポート番号で、socket.ioに接続できるようにする。](https://qiita.com/roothybrid7/items/ccfb9617f340a9038a87))
        * apacheの場合([StackOverFlow/WebSockets and Apache proxy : how to configure mod_proxy_wstunnel?](https://stackoverflow.com/questions/27526281/websockets-and-apache-proxy-how-to-configure-mod-proxy-wstunnel/27534443#27534443))
2. 別サーバを立てる
3. 無理やり動かす
    * 本稿では、Xserverで10080ポートで稼働させた設定を記す

---
# 設定例

1. 10080ポートでSocket.IOベースのWebサービスを起動
2. サブドメインを追加設定(Rewriteルールがややこしくなるため)
3. io.connect()でwebsocketを外す
4. .htaccessに以下を追記

```
<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteRule socket.io/socket.io.js http://localhost:10080/socket.io/socket.io.js [L,P]
    RewriteRule ^(.*) http://localhost:10080/$1 [L,P]
</IfModule>
```

---
# 経緯

* Xserverはapacheが動作していると早合点
    * .htaccessを設定できたため
    * 実は、nginxが動作
* 特定ポートへの通信は許可されていない
    * `http://www.example.com:nnnn/` では全て応答なし
* 従って、`http://www.example.com/`だけの入力で、localhost:nnnnに通信するよう、NAPTの仕組みを導入する必要がある

---
# 試行錯誤

1. .htaccessにProxyPass=> 500エラー
2. .htaccessにLocation=> 500エラー
3. .htaccessでmod_rewrite(`www.example.com` -> localhost:10080へ)
    * 通常の書き方ではURL書き換えのリダイレクトになるので[P]を使う
    * listenしている10080ポートからの応答に成功、だがSocket.IO的に失敗
    * socket.IOのクライアント側で404 エラー
        
---
## 404 エラー？
* Failed to load resource: the server responded with a status of 404 (Not Found)

```html
<script src="/socket.io/socket.io.js"><!-- ここが見つからない　-->
```

`www.example.com/socket.io/socket.io.js`は存在しない。
`localhost:10080/socket.io/socket.io.js`として
アクセスさせないといけない。

```
<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteRule socket.io/socket.io.js http://localhost:10080/socket.io/socket.io.js [L,P]
    RewriteRule ^(.*) http://localhost:10080/$1 [L,P]
</IfModule>
```
---
## 続 404 not found

* io.connect()でサービス接続時にエラー
* socket.io/以下のRequestを、localhost:10080へ全て転送する必要がある
 
```
GET http://www.example.com/socket.io/?EIO=3&transport=polling&t=MPXhUo6 404 (Not Found)
webclient.js:95 connect_error Error: xhr poll error
    at o.r.onError (socket.io.js:7)
    at i.<anonymous> (socket.io.js:7)
    at i.r.emit (socket.io.js:6)
    at i.onError (socket.io.js:7)
    at socket.io.js:7
```

---
## まだ出るエラー； 400 エラー?

＊ WebSocket connection to '`ws://www.example.com/socket.io/?EIO=3&transport=websocket&sid=A-YcIDGgXoCEvPYyAAA`' failed: Error during WebSocket handshake: Unexpected response code: 400

* Socket.IOは、PUSH/PULL通信方法を用意していて, xhr-polling, websocket, polling等、うまくいくものの通信でコネクションを張る
    * そのうちPULL通信方式はmod_rewriteで解決
    * PUSH通信方式のWebSocket通信で失敗
* nginx + Socket.IOの組み合わせで必ず出る
    * 解決するには、[正確にNAPTがなされなければならない](https://stackoverflow.com/questions/27526281/websockets-and-apache-proxy-how-to-configure-mod-proxy-wstunnel/27534443#27534443)    
* なお、AWSで生じるのは[ロードバランサーのせい](https://qiita.com/glostuan/items/e734bd99c9201d5e359b)
    * この場合の[解決はこちら](https://www.checksite.jp/elb-websocket-unexpected-response/)
   
---
## 最終設定

* io.connect()時に、websocketを外す(敗北)

```javascript

    const server  = io.connect(this.host,
                               {'transports': ["xhr-polling","polling"],
                                 'reconnection': true,
                                 'reconnectionDelay': 1000,
                                 'reconnectionDelayMax' : 5000,
                                 'reconnectionAttempts': 5
                               })
 
```

---

# まとめ

* 通常ならmod_rewriteでのNAPT構成のためにプロキシ設定だけで終わると思われるところ
* Socket.IOの事情で、難航した。
* 何かのお役に立てればと。FYI.

# 余談

* どうもathenaのX-Window Systemと思ってしまう。astec-xとか。