Proxymanを使う！

長年Web Debugging Proxy ToolとしてCharlesを使用してきた。
たくさんの恩恵を与えてもらったが最近、さまざまな不具合や使い勝手の悪さが目立ってきたように思う。

* UIの見栄えやレイアウトも使い勝手が悪いと感じてきた
* DarkMode時のエディタのキャレットが見えないためどこを編集しているのかわからない
* 時折OSのネットワークのプロキシ設定が戻らないままになることがある（再現手順は取れてないので確証なし）

そんな折、社内OJTチャンネルで「Charlesより使い勝手が格段に上」の**Proxyman**なるツールがあることを知る。

* Charlesより高機能でCharlesで行えることはほぼ全てカバーされている
* プロセス毎に見れるため便利！
* Scripting機能が便利！
* どの通信でどういう風にレスポンス弄ったか後からも見れる
* ホスト毎のネットワーク速度絞り込みが可能（**Goodbye, Network Link Conditioner**）
* UIが洗練されている。美しい。とにかく見やすいし親切。
* バックグラウンドで常時起動していてもさほど気にならない。（ほぼCPU使用率1%未満）
* トラブルシューティングへの導線も見事。リアルタイムチャットでの即レスもあり、サポートが非常に手厚い印象。

Goodbye, Charles...
Hello, Proxyman!

https://proxyman.io/

ということでProxymanの機能や使い方を一部紹介したい。

# 使い方
## 導入
[https://proxyman.io/](https://proxyman.io/) の右上からディスクイメージがダウンロードできる。
![0_download.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156085/316fbaf9-ca86-7bb9-9156-506a8b2ba842.png)

## 起動
アプリを起動すると初めにProxy Helper Toolのインストールが促される。
![1_install_proxy_helper_tool.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156085/05579e49-ae2d-6c3b-7b55-93b3579186be.png)
調べたらHTTP/HTTPSプロキシ構成のオーバーライドにnetworksetupを使用しているが、
これが起動/終了時のボトルネックになるのを防ぐために入れるヘルパーツールの模様。
Preferences > Advancedからいつでもアンインストールできるのでとりあえず入れる。

メイン画面。
![proxyman_main.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156085/5b900e25-0bbc-c19a-1f9b-f1df04b297e8.png)

プロセス毎に通信一覧が表示されるため非常に探しやすい。
HTTPS通信の場合は証明書をインストールして信頼することで該当のトラフィックについてはProxymanが複合化して表示/編集することが可能になる。
![proxyman_install_certificate.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156085/b1518dec-9a21-e0de-3e36-420b714a9675.png)

レスポンス表示領域に表示されている「Enable only this domain」「Enable all domains from "Proxyman"」ボタンを押すとインストール＆信頼する画面が表示される。
![proxyman_install_annd_truct_certificate.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156085/841ccbcc-32eb-bc3b-4365-66d1da860cc4.png)

「Install & Trust」を押すと完了。
![proxyman_install_annd_truct_certificate_done.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156085/5565758f-c165-bd6b-1400-e07824171242.png)

もう一度同じ通信を行うとちゃんと内容が表示される :tada:
![proxyman_main_decrypt.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156085/b987629b-dcde-f4c6-4c7c-897019175939.png)

上記はmacOSアプリの設定方法でiOSアプリの場合はステップが異なり
Certificate > Install Certificate on iOS で表示されたウインドウで証明書をインストールする必要がある。
![スクリーンショット 2021-04-28 10.15.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156085/99b0be33-5226-25c5-61cd-2a0bba08c7c0.png)
表示されたウインドウの指示通りにインストールすれば問題なし。
![スクリーンショット 2021-04-28 10.15.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156085/cf8b9cb2-42ef-ab86-2e66-99b455452ff7.png)

複数のシミュレータで確認の必要がある場合、都度このステップを行う必要がある。
それでも動かない場合、「Troubleshooting」ボタンを押すと丁寧に設定方法が記されたページが表示される。トラブルシューティングへの導線が見事。

## Breakpoint
特定の通信のリクエスト/レスポンス内容を編集する機能。
Charlesでもお馴染みの機能だが、UI/UXが秀逸。

Tools > Breakpoint > Rules でルールを作り、
Tools > Breakpoint > Breakpointsで実際にでデバッグする画面を表示することができる。
それぞれ機能毎に画面を表示することで下手にOne-Window化して複雑化するより確かにシンプルで良い。

メイン画面から対象の通信からコンテキストメニューを表示して追加すると楽。
![proxyman_add_breakpoint_from_menu.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156085/448e991e-cbaf-a733-cd0a-13e6019a5547.png)
適当な名称にしてDone。
因みにこのスクショ撮るためにサンプルで使ってるGoogle Apps ScriptのリクエストURLにはパラメータが付加されるが、順不同になるので最後の?より後ろの部分は*（ワイルドカード）で指定している。
![スクリーンショット 2021-04-28 10.24.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156085/7999637b-6cb1-16ea-53aa-233521aa6a01.png)

通信を行ってみるとちゃんとRequestでもブレークする。右下の「Execute」を押して続行。
![スクリーンショット 2021-04-28 10.24.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156085/be84dd53-e255-4e98-95ec-7068f0662d29.png)
Responseでもブレークする。
![スクリーンショット 2021-04-28 10.24.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156085/8b5a9318-7464-32a7-8441-9a6de795f64e.png)

レスポンス内容を編集してExecute（続行）するとEditedされたものがどれなのかとその編集内容が後からでも見れるので非常に便利！
![proxyman_breakpoint_edited.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156085/13e73996-b206-69f1-ce1c-d87d2839641e.png)


## Scripting
Javascriptを使って特定のリクエスト/レスポンスをフックして弄って返せるので
毎回Breakpointで止めて手動編集しなくて済む神機能！！
Breakpointだとタイムアウトが短い場合に焦るのも解消できそう。

Scripting > Script List...で管理・編集できる。
Breakpointと同じように対象の通信からコンテキストメニューを表示してTools > Scripting...を選択することで追加することもできる。

追加するとデフォルトでこのようなスクリプトが入っている。
![スクリーンショット 2021-04-28 11.12.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156085/b426feed-d223-2528-5395-b0ea2287f061.png)

```Javascript
// Addons List: https://docs.proxyman.io/scripting/addons
const { sayHello } = require("@addons/HelloWorld.js");

///
/// This func is called if the Request Checkbox is Enabled
/// You can manipulate the Request Data here before the request hits on the server
/// Ex: Add/Update/Remove: host, scheme, port, path, headers, queries and body (json, form, plain-text, base64 encoded string...)
/// Use console.log(request) to see all available fields
/// You can import a JSON file and use in the script. Action Menu -> Import File
/// Use global object `sharedState` to share data between onRequest and onResponse. Ex: sharedState.data = "My-Data"
///
function onRequest(context, url, request) {
  // console.log(request);
  console.log(url);

  // Update or Add new headers
  // request.headers["X-New-Headers"] = "My-Value";

  // Update or Add new queries
  // request.queries["name"] = "Proxyman";

  // Body
  // var body = request.body;
  // body["new-key"] = "new-value"
  // request.body = body;

  // Done
  return request;
}

///
/// This func is called if the Response Checkbox is Enabled
/// You can manipulate the Response Data here before it goes to the client
/// Ex: Add/Update/Remove: headers, statusCode and body (json, plain-text, base64 encoded string)
/// Use console.log(response) to see all available fields
///
function onResponse(context, url, request, response) {
  // console.log(response);

  // Update or Add new headers
  // response.headers["Content-Type"] = "application/json";

  // Update status Code
  // response.statusCode = 500;

  // Update Body
  // var body = response.body;
  // body["new-key"] = "Proxyman";
  // response.body = body;

  // Done
  return response;
}
```

これ見ただけで大体何ができるのかどうやって書くのかわかるのが凄いよな。
試しにレスポンス要素を削ってみる。

```Javascript
function onResponse(context, url, request, response) {
  // Update Body
  var body = response.body;
  var items = body["items"];
  body["items"] = items.slice(0, 1);
  response.body = body;

  // Done
  return response;
}
```

スクリプト内容を変更して右下の「Save & Activate」しておき再度通信する。
ちゃんと削られてた素晴らしい！！
![スクリーンショット 2021-04-28 11.19.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156085/09fbcf74-6a31-1d2d-f7cb-610dda490b7b.png)

## Map Local/Map Remote
Tools > Map Local...
Tools > Map Remote...
から管理追加が可能。
これもCharlesでお馴染み機能かもしれないけどとにかくUIが見やすくわかりやすい。
詳細な説明は割愛するが、Map Localの場合、HTTP Messageかローカルのjsonを直指定してMapすることができる。
![スクリーンショット 2021-04-28 11.31.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156085/fc717914-b37b-b7ae-2c17-52f960aaec77.png)

## Network Conditions
これは地味に待ち望んだ機能かもしれない。
今まで私が知りうる限りネットワーク速度の絞り込みはAdditional Tools for Xcodeに含まれる標準ツールの一つ、「Network Link Conditioner」に依存していた。
ただ、これを使うとOS全体のネットワーク帯域が制限されてしまい、デバッグ以外に必要な通信も巻き添えを喰らうためSlack更新されないなど陸の孤島化していた。。。

Proxymanではホスト毎に帯域を絞れるのでデバッグ以外の通信帯域はそのままにデバッグが可能！
Tools > Network Conditions...から管理・追加できる。
試しに`script.googleusercontent.com`のみ制限する設定を追加する。
![スクリーンショット 2021-04-28 11.23.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156085/07fb146f-6dd4-0787-ce4b-542eb87a26fd.png)
![スクリーンショット 2021-04-28 11.27.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156085/c11d5f77-5e31-1db6-c8bc-40e629119ddc.png)

これも便利！
ただし別プロセスでも同じホストの通信は制限されるので注意。

# 価格
トライアル版でも割と使えるかもしれない。

```
The Trial version is limited to 4 pinned domains, 4 matching rules for each advanced debugging tool (e.g. SSL Proxying List, Map Local, Map Remote, Breakpoint, Allow/Block List, Protobuf, Scripting, Network Conditions, Multiple Filters), and no new tabs. 
```

私は仕事用と仕事用Macが使えない時の代打で使う個人Mac用に２台分を購入。
https://proxyman.io/pricing
価格も40%セーブされるしお得。
![スクリーンショット 2021-04-28 11.37.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156085/2b1870bf-abb1-3a7b-7d8a-070620528d35.png)

macOS版のライセンス持ってるとiOS版のpremium機能もアンロックできるらしいので今度試してみる。

```
If you have a valid macOS License, you can unlock Premium Proxyman for iOS. 1 seat can redeem 2 iOS Devices.
```

![proxyman.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156085/72cbe744-dd26-5810-a7f0-b6d6e10ced80.png)