プロキシに苦しむ人が IoTHub DeviceStreamなWebJobsを踏み台にして インターネットに自由に出た話

# はじめに
WebProxyにアクセス制限されまくって仕事にならない皆さん、こんにちは。

最近、LINEMessageAPIを使うサーバーサイドの開発をしたのですが、
WebProxyがline.meをブロックするせいで、
アプリ登録のサイトや、マニュアル、デベロッパーコミュニティにアクセスできず、
とても生産性が低かったです。
仕方がないので、最近リリースされた、IoTHub DeviceStreamを活用して
WebProxyを無理矢理突き抜けてみた話です。

# DeviceStreamって何
簡単に言うと『アウトバウンド同士でWebSocketのハンドシェイクができちゃう素敵サービス』
詳しくは前に書いた↓を参照してください。
[Azure IoT Hub デバイス ストリームを試してみた](https://qiita.com/HiroyukiSakoh/items/0d699ba66f248f6b5588)

# 構成図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260166/18fe0e18-0ba2-6a13-3c42-265f78b662e5.png)

# 構成要素の説明
## [ServerSideProxy](https://github.com/HiroyukiSakoh/WebProxyOverIoTHub/tree/develop/ServerSideProxy)
* [WebJob上のWebプロキシ](https://github.com/HiroyukiSakoh/WebProxyOverIoTHub/blob/develop/ServerSideProxy/InternalWebProxyServer.cs)
    <img width="50%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260166/91b78675-0eca-692d-40d7-0650f8cf1898.png">

    * WebAppsのWebJobs上にWebプロキシを配置します
    * C#のライブラリ[Titanium-Web-Proxy](https://github.com/justcoding121/Titanium-Web-Proxy)を利用しています
    * 接続の待ち受けは環境変数`WEBJOBS_PORT`で渡されるポートを参照します
* [デバイス接続(DeviceStream)](https://github.com/HiroyukiSakoh/WebProxyOverIoTHub/blob/develop/ServerSideProxy/DeviceStream.cs)
    <img width="50%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260166/392c6d1e-9271-1a8b-46c8-334f61ada645.png">

    * 起動したら、WebJobsからIoTHubにデバイス接続で接続し、DeviceStream接続の待ち受けを行います
    * DeviceStreamの接続要求が来たら、受け入れて、`WebSocketの`接続を確立します
    * `WebSocket`から来たデータはすべて`WebJob上のWebプロキシ`に渡します。
    * `WebJob上のWebプロキシ`から来たデータはすべて`WebScoket`に渡します。

## [ClientSideProxy](https://github.com/HiroyukiSakoh/WebProxyOverIoTHub/tree/develop/ClientSideProxy)
* [ローカルWebプロキシ](https://github.com/HiroyukiSakoh/WebProxyOverIoTHub/blob/develop/ClientSideProxy/InternalWebProxyServer.cs)
    <img width="50%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260166/5711c72a-5128-19f7-9532-5a0e9df0c890.png">

    * 任意のポートでHTTP接続を待ち受けます
    * C#のライブラリ[Titanium-Web-Proxy](https://github.com/justcoding121/Titanium-Web-Proxy)を利用しています
    * 上流のWebプロキシとして、`サービス接続(DeviceStream)`の待ち受けポートを指定しています
* [サービス接続(DeviceStream)](https://github.com/HiroyukiSakoh/WebProxyOverIoTHub/blob/develop/ClientSideProxy/DeviceStream.cs)
    <img width="50%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260166/80e268f3-5827-9bce-766c-30041258161e.png">

    * IoTHubへの接続は、`インターネットに出られるWebプロキシ`を経由します
    * 起動したら、IoTHubにサービス接続で接続し、DeviceStream接続の要求を行います
    * DeviceStreamの接続要求の受託応答を受けて、`WebSocket`の接続を確立します
    * `WebSocket`から来たデータはすべてクライアント(`ローカルWebプロキシ`)に渡します。
    * クライアント(`ローカルWebプロキシ`)から来たデータはすべて`WebScoket`に渡します。


# 問題点
* IoTHubのDeviceStreamは**パブリックプレビュー中で、`米国中部`しか対応していない！！**
    * ⇒すべての通信が`米国中部`経由…
* **職場のセキュリティーポリシー的にやっちゃだめ！！**
    * ⇒ご利用は自己責任で

# FAQ
* ローカルWebプロキシいらなくね？
    * ⇒たしかに でも実装してみると、TCPコネクションの切断時の挙動が安定せず仕方なく
* SSHポートフォワーディングでいいじゃん？？ばかなの？？
    * ⇒**全部PaaSだよ？？？**^q^
* 認証プロキシは？
    * ソースを煮るなり焼くなり…

# 使い方
## ServerSideProxy
* リソース
    * IoTHubを`米国中部`に建てる
        * デバイスを適当に登録する
    * AppService B1以上を立てる
        * WebAppsをデプロイ
        * AppSettingsに`IOTHUB_CONNECTION_STRING`を指定する
            * HostName=&lt;HOST>.azure-devices.net;DeviceId=&lt;DEVICE_ID>;SharedAccessKey=&lt;KEY>
* WebJobsをPublish
* ポータルからWebJobを起動

## ClientSideProxy
* 起動する

```
dotnet WebProxyOverIoTHub.ClientSideProxy.dll  ^
--IoTHubServiceConnectionString=HostName=<HOST>.azure-devices.net;SharedAccessKeyName=<KEY_NAME>;SharedAccessKey=<KEY> ^
--TargetDeviceId=<DEVICE_ID> ^
--LocalDeviceStreamPort=<PORT_DEVICE_STREAM> ^
--LocalInternalWebProxyPort=<PORT_WEB_PROXY> ^
--UpstreamWebProxy=<UPSTREAM_WEB_PROXY> ^
--StreamName=<YOUR_CLIENT>
```
* ブラウザのプロキシをlocalhost:&lt;PORT_WEB_PROXY>に向ける

# 課題
* 異常系処理が弱い(気がする)
* コードが汚い(助けて)

# おわりに
**せいさんせい が たかまった ので ていじだっしゅ が はかどった**