通信系のデバッグには Charles が便利

# はじめに

Charles を使うと PC 上に HTTP プロキシを立てて端末の通信をキャプチャし、リクエストやレスポンスの内容を覗いたり書き換えることが出来る。類似のソフトウェアとして Wireshark や Fiddler, Paros がある。

![d65fbb99-6886-08ee-f042-1a4cdbbf320d.jpeg](https://qiita-image-store.s3.amazonaws.com/0/3603/2c0aa0f2-73cf-ea4c-5d05-2cd30a1df274.jpeg)

アプリの開発をしていてよくあるのは、APIがスタブで固定値しか返してくれない、異常系エラーのデバッグがやりづらい、という場面だが、Charles なら通信を好きに値を書き換えられるのでこれらに簡単に対処することができる。

Charles は Java アプリなので OS X だけでなく Windows や Linux でも利用する事が出来る。本稿では OS X + iOS での利用を前提として Charles の導入から簡単な使い方までを説明するが、環境依存の箇所は適宜読み替えてほしい。


# 導入

http://www.charlesproxy.com/

本来は有料ライセンスだが、無料でも数分間だけ使用する事が出来る。この制限は再起動すればまたリセットされる模様。（具体的な試用期限は確認できていない。）

## Java ランタイム

Java アプリケーションなので Java ランタイムが必要になる。OS X であればアプリを起動した直後に自動的にインストールが促されるはずなので、それに従う。インストール済みなら特に何も言われない。
v3.10 より Java 8 が推奨となった。


## Charles プロキシの設定

まず Charles を起動したら、`Proxy > Proxy Settings...` を開いて使用するポート番号等の設定を行う。このポート番号は控えておく。

![sslmenu1.png](https://qiita-image-store.s3.amazonaws.com/0/3603/c657c93c-7163-df07-62df-53e6d120493e.png)

![proxy.png](https://qiita-image-store.s3.amazonaws.com/0/3603/260cb7fa-fab8-e657-540d-bf44b8bcca90.png)
*任意のポート番号を設定*


## PC の IP アドレスを取得

プロキシとなる IP アドレスを調べて控えておく。
以下好きな方法で取得：

* Charles `Help` > `Local IP Address…`
* `システム環境設定` > `ネットワーク`
* `$ ifconfig en1`
* option キーを押下しながら Wi-Fi メニューエクストラを開く

![helpip.png](https://qiita-image-store.s3.amazonaws.com/0/3603/6a13e173-7e71-a4ba-5788-2be841ab7004.png)
*Charles の Help メニューからローカルIPアドレスを取得*

![w1.png](https://qiita-image-store.s3.amazonaws.com/0/3603/fca4a408-a11f-e1e4-5ed3-68ec8ddaa619.png)
*Menu Meters*


## 環境ごとのプロキシ接続設定

プロキシとなる PC と検査対象の端末は同一ネットワークに接続しておく。そして端末側でプロキシへ接続するための設定を行う。控えておいた IP アドレスとポート番号を各環境の設定画面に入力する。

### iOS

Wi-Fi 設定を開き、HTTP プロキシを「手動」とした上で、控えておいた IP アドレスとポート番号を入力する。

![w2.png](https://qiita-image-store.s3.amazonaws.com/0/3603/1d3f6264-c901-4a7d-5e13-8b503c917cff.png)
*プロキシ設定は放置しがちなので、作業を終えたら「オフ」に戻すことを忘れずに*

### Android

Wi-Fi 設定を開き、AP を長押しして ネットワークを変更 > 詳細設定項目 > 手動 とした上で、控えておいた IP アドレスとポート番号を入力する。
プロキシ設定は放置しがちなので、作業を終えたら「オフ」に戻すことを忘れずに。


## SSL プロキシ設定

`Proxy` > `SSL Proxying Settings…`
対象の SSL 通信をここで指定する。アスタリスク `*` 指定で全ての通信が対象になる。この設定をやっておかないと、証明書のインストールを行っていても錠前アイコンが付いて SSL 通信の内容を見ることができない。
※古い Charles では `*` が初期設定だったが、最近のは自分で設定する必要がある。

<img width="571" alt="ssl.png" src="https://qiita-image-store.s3.amazonaws.com/0/3603/d10fe7ce-ef59-2103-77a3-d9edeee7cb59.png">


## SSL 証明書

SSL 通信の中身を見る場合には証明書が必要になる。v3.10 より Charles のルート証明書が変更になった（Charles サーバーごとに証明書を発行するようになった）ため、すでに古い証明書を導入済みであっても以下に記す方法で証明書を更新する。

Charles Certificates
http://www.charlesproxy.com/documentation/using-charles/ssl-certificates/


### OS X

OS X アプリや Web ブラウザをキャプチャしたい場合。

1. Charles を起動
2. Help メニューから `SSL Proxying` > `Install Charles Root Certificate` を実行
3. Keychain Access.app が起動するので、確認シートより「常に信頼」を選ぶ

![helpmenu.png](https://qiita-image-store.s3.amazonaws.com/0/3603/06d96474-c0a3-37d4-6306-0f25ddd0d7d2.png)

#### 確認シートが現れない場合

Keychain Access.app で確認シートが現れず無効の状態でインストールされてしまう場合は次の手順をとる：

1. 証明書 `Charles Proxy Custom Root Certificate` を右クリック > 情報を見る
2. 「信頼」を開く
3. この証明書を使用するとき > 「常に信頼」を選択


#### 証明書の削除

1. Keychain Access.app を起動
2. 証明書 > `Charles Proxy Custom Root Certificate` を削除


### iOS Simulator

iOS シミュレーターでキャプチャしたい場合。

1. iOS Simulator を終了
2. Charles を起動
3. Help メニューから `SSL Proxying` > `Install Charles Root Certificate in iOS Simulator` を実行


### iOS

iOS 実機でキャプチャしたい場合。

1. Charles を起動
2. 端末でプロキシに接続するための設定を行う
3. Mobile Safari http://www.charlesproxy.com/getssl を開くと現れる画面からインストール
4. 設定 > 一般 > 情報 > 証明書信頼設定　からCharlesのルート証明書をオンにする（iOS 10.3以降）

または、

1. Charles を起動
2. Mac で http://www.charlesproxy.com/getssl を開いて .crt をダウンロードする
3. .crt ファイルを Air Drop で iOS 端末に転送すると現れる画面からインストール
4. 設定 > 一般 > 情報 > 証明書信頼設定　からCharlesのルート証明書をオンにする（iOS 10.3以降）

<img src="https://qiita-image-store.s3.amazonaws.com/0/3603/a95b5d71-b927-06ff-c0b6-9f1b86b418a7.jpeg" width=414>

参考記事：[iOS10.3以降でCharlesが使えない場合の対処法](https://qiita.com/shtnkgm/items/cd32df0c7b4bd424f107)

#### 証明書の削除

1. 設定 > 一般 > プロファイル
2. 削除したい項目を開いて、「削除」ボタンを押す


### Android

1. Charles を起動
2. 端末でプロキシに接続するための設定を行う
3. Chrome で http://www.charlesproxy.com/getssl を開くとインストール画面が現れる


#### 証明書の削除

1. 設定 > ユーザー設定：セキュリティ > 信頼できる認証情報 > ユーザー
2. 削除したい項目を開いて、「削除」ボタンを押す
3. 設定 > ユーザー設定：セキュリティ > 認証ストレージの消去 （必要なら）


### Charles v3.9.x 以前（古い証明書）

Charles v3.9.x 以前では以下のページ内のリンクから古い.crtファイルを取得して手動でインストールする。

> http://www.charlesproxy.com/documentation/additional/legacy-ssl-proxying/


### カスタム証明書

カスタム証明書を使用する場合は SSL プロキシ設定の画面から証明書ファイルを指定する。
なお、デフォルト証明書は `~/Library/Application Support/Charles/ca` で見つけることができる。

![sslmenu.png](https://qiita-image-store.s3.amazonaws.com/0/3603/2a75a7f4-d60e-ac27-d6bd-76b38c0a1aa5.png)
![sslwindow.png](https://qiita-image-store.s3.amazonaws.com/0/3603/894bd396-ecf8-dd0c-31a8-62ce5b139250.png)
*証明書ファイルを指定する*


# 通信をキャプチャ

設定が終わると端末の通信が Charles に上がってくる。初回であればアラートが出るので `Allow` で許可する。

![m1.png](https://qiita-image-store.s3.amazonaws.com/0/3603/8f5b3db1-632e-c5ec-4d01-022b2df70e16.png)
*Apple の「マップ」の通信を覗いてみるとマップチップが大量に GET されているのが分かる*

## iOS 9 “App Transport Security” 対策

iOS 9 では ATS を無効にするか、条件を緩和します。

http://stackoverflow.com/questions/32746968/how-to-get-charles-working-with-xcode-7-on-ssl-connections


## curl の通信をキャプチャするためのプロキシー指定

curl コマンドの通信を見たい場合にはプロキシーのオプションで `--proxy localhost:8888`（※設定したポート番号）を指定する。Charles には `Mac OS X Proxy` を有効にする機能もあるが curl には無意味なのでこれは使用しない。

```sh:curl
curl -X GET 'https://www.apple.com/' --proxy localhost:8888
```

[CURL AND LIBCURL](https://www.charlesproxy.com/documentation/configuration/browser-and-system-configuration/)


## Map Local

Map Local は、特定の通信のレスポンスデータをローカルファイルで置き換える機能。
例えば、特定 Web ページのリソースをデスクトップ上の画像ファイルで置き換える、特定APIの返り値をデスクトップ上の JSON で置き換える、といったことが可能になる。アプリケーションで意図的にエラーを発生させたい場合や特定条件を再現するなどの活用方法が考えられる。

リクエストがPOSTである場合はBodyの中身での判定ができないので、POSTリクエストに対して Map Local する場合にはリクエストごとに区別可能なダミーのクエリを付加しておくと良いかもしれない。

![l1.png](https://qiita-image-store.s3.amazonaws.com/0/3603/4e235b53-05df-184a-0d4f-26a746c6f289.png)

![l2.png](https://qiita-image-store.s3.amazonaws.com/0/3603/a20addd1-59ed-9988-b3ec-596bc1b7b980.png)
*`Host` 欄に対象 URL を直接ペーストすると `Path` 欄に自動で分割される*

![l3.png](https://qiita-image-store.s3.amazonaws.com/0/3603/485f757f-a71a-ad6d-8c78-2d00c7739f80.png)
*とある Web ページの画像をデスクトップ上の画像ファイルで置き換えた例*


## Breakpoints

特定の通信に対してブレークポイントを仕掛ける機能。条件に設定した URL にマッチするとそこで一時停止してリクエスト／レスポンスの内容を直に書き換えて再実行することができる（LLDBのような感覚）。長時間停止させると通信がタイムアウトになってしまうことがあるので、作業に時間をかけすぎないよう注意したい。

Map Local と同様に、リクエストがPOSTである場合はBodyの中身での判定ができないので、POSTリクエストにブレークポイントを仕掛ける場合にはリクエストごとに区別可能なダミーのクエリを付加しておくと良いかもしれない。

![b1.png](https://qiita-image-store.s3.amazonaws.com/0/3603/0e2803d5-af46-4437-fa2b-95380f4d718e.png)

![b2.png](https://qiita-image-store.s3.amazonaws.com/0/3603/39bf7b73-c86a-1803-543e-89eac639f6d8.png)


# Charles 以外の選択肢

Apple の Technical Q&A が役立つ。（Charles も紹介されている。）
[Technical Q&A QA1176 Getting a Packet Trace](https://developer.apple.com/library/mac/qa/qa1176/_index.html)

iOS 5 以降では Remote Virtual Interface という仕組みが利用できる。USB 接続した iOS 端末のネットワークインターフェースを Mac 上に構築するというものだとか。

```:rviを設定
$ rvictl -s 端末のUUID
```
```:rviを削除
$ rvictl -x 端末のUUID
```

こうすると `rvi0` というインターフェースが追加されるので、これを tcpdump や Wireshark につなげれば iOS の通信をキャプチャすることができる。

```:rvi0が追加されたかの確認
$ ifconfig -l
lo0 gif0 stf0 en0 en1 en2 en3 bridge0 p2p0 awdl0 rvi0
```

rvi0 を Charles に接続する方法があればいいのに。

# おわりに

Charles にはその他にも Map Remote など機能が豊富なので、これらを使いこなすとデバッグ作業も捗るかもしれない。