# Scripting

> Proxyman の Scripting ツールを使い、JavaScript で HTTP リクエストとレスポンスを変更したり、モック API を作成したり、onWebsocket で WebSocket メッセージをログに記録したりできます。

## 1. 概要 {#1-whats-it}

Proxyman には Scripting ツールがあります。JavaScript コードを書くことで、リクエストとレスポンスを柔軟に変更できます。

## 2. メリット {#2-benefits}

* JS コードを使って Map Local / Map Remote / Breakpoint を実装できます。**100 倍高速**
* **リクエストの内容**を変更できます。対象は Domain、Host、Scheme、Port、Path、HTTP Method、HTTP Headers、Query、Body（Encoded-Form、JSON、plain-text）です。
* `onWebsocket` で WebSocket メッセージをログに記録し、確認できます（**macOS、Windows、Linux 版 Proxyman 26.1.0 以降**）。
* `onWebsocket` で [WebSocket メッセージの編集、ドロップ、送信](https://proxyman.com/docs/ja/scripting/modify-websocket-messages)ができます（**macOS 版 Proxyman 26.2.0 以降、Windows/Linux 版 Proxyman 26.1.0 以降**）。
* **レスポンスの内容**を変更できます。対象は HTTP Status Code、HTTP Headers、Body（JSON、Encoded-Form、plain-text、binary...）です。
* 組み込みのアドオンとライブラリが豊富にあり、Hashing、Encode/Decode、JSON-Text transformer、Beautify などの一般的な処理に使えます。
* **独自の JS アドオンやライブラリを作成できます**
* Charles Proxy の Rewrite GUI Tool を置き換えるために設計されています。
* [Shared State または Environment Variables](https://proxyman.com/docs/ja/scripting/environment-variables) を使うと、現在のセッション内のスクリプト間で状態を共有できます。

![POST ルール、Request と Response のチェック、Run as Mock API、onRequest と onResponse のコードを表示した Proxyman Script Editor](https://proxyman.com/docs/assets/proxyman-script-editor.jpg)

_Scripting ツール_

- [スニペットコード](https://proxyman.com/docs/ja/scripting/snippet-code)

## 3. 使い方 {#3-how-to-use-it}

次の方法で Scripting ツールを開けます：

* **Scripting** メニュー → **Script List…**（⌥⌘I）
* フローを右クリック → **Tools** → **Scripting…**

Scripting ツール向けの JS スニペットをまとめた [スニペットコード](https://proxyman.com/docs/ja/scripting/snippet-code) ページもご覧ください。

## 4. GraphQL リクエストでの Scripting {#4-scripting-with-graphql-requests}

Proxyman 2.27.0 以降では、Scripting ツールは QueryName を使って GraphQL リクエストをマッチさせられます。次の GraphQL のドキュメントもご覧ください。

- [Proxyman での GraphQL デバッグ](https://proxyman.com/docs/ja/advanced-features/graphql)

## 4.1 WebSocket での Scripting {#41-scripting-with-websocket}

macOS 版 Proxyman 6.2.0 以降では、`onRequest` と `onResponse` を使って WebSocket のリクエスト URL とハンドシェイクのヘッダーを変更できます。[WebSocket の URL とヘッダーを変更する](https://proxyman.com/docs/ja/scripting/snippet-code#change-websocket-url-requestsresponse-header) を参照してください。

### onWebsocket でメッセージをログに記録 {#log-messages-with-onwebsocket}

> **警告:** `onWebsocket` は **macOS、Windows、Linux 版 Proxyman 26.1.0 以降**で利用できます。iOS では利用できません。

`onWebsocket(context, url, request, response, message)` を使うと、クライアントとサーバーの両方からキャプチャしたメッセージを確認し、ログに記録できます：

```javascript
function onWebsocket(context, url, request, response, message) {
  console.log(`[${message.from}] ${message.type}: ${message.data}`);
}
```

デフォルトでは、このフックは**読み取り専用**です。Proxyman はスクリプトを実行する前にメッセージを転送します。引数を変更したり値を返したりしても、通信やキャプチャされたメッセージは変更されません。return 文は不要です。

メッセージを編集、ドロップ、送信するには、ルールで **Modify WebSocket Messages** にチェックを入れます（macOS 版 Proxyman 26.2.0 以降、Windows/Linux 版 Proxyman 26.1.0 以降）。詳しくは [Modify WebSocket Messages](https://proxyman.com/docs/ja/scripting/modify-websocket-messages) を参照してください。

**使い方**

1. WebSocket 接続をキャプチャします。`wss://` の場合は、Proxyman がメッセージを読み取れるように、そのホストで SSL Proxying を有効にしてください。
2. キャプチャしたフローを右クリックし、**Tools → Scripting** を選択します。生成されたスクリプトには、`onResponse` の下に `onWebsocket` が含まれています。
3. ログ出力のコードを追加し、**Save & Activate** をクリックします。
4. WebSocket に再接続し、メッセージを送信して、スクリプトの **Console Log** を確認します。

接続を開いたときにルールが有効になっている必要があります。ルールを作成した後や、接続を開いた時点で `onWebsocket` が含まれていなかったスクリプトに `onWebsocket` を追加した後は、再接続してください。通常の新規スクリプトのテンプレートにはこのフックは含まれていないため、手動で追加できます。

**引数**

引数は 5 つすべてを、この順番どおりに指定してください。`context` と `url` は、フローと一致したルールを表します。`request` と `response` には、`onRequest`、`onResponse`、Breakpoint による変更を含む、最終的なハンドシェイクが入ります。`message` には次の情報が含まれます：

| フィールド | 値 |
| --- | --- |
| `message.type` | `"text"`、`"binary"`、`"ping"`、`"pong"`、または `"close"` です。 |
| `message.from` | `"client"` または `"server"` です。 |
| `message.data` | 常に文字列です。バイナリデータは可能な場合 UTF-8 テキストとして表示され、それ以外の場合は `Binary (length=N)` と表示されます。WebSocket タブのオプションのデコーダーは適用されません。 |
| `message.rawData` | コピーされた `Uint8Array` で、バイナリメッセージでのみ利用できます。明示的にログ出力してください。`message` を列挙しても含まれません。 |
| `message.closeCode` | 数値で、close メッセージでのみ利用できます（例：`1000`）。 |

**注意事項**

* 分割されたテキストフレームとバイナリフレームは、フックを実行する前に再構成されます。継続フレームごとに個別のコールバックが呼ばれるのではなく、完全な 1 つのメッセージを受け取ります。
* Ping/pong のペイロードと close の理由は公開されません。
* 同期フックと `async` フックに対応しています。`console.log`、`require`、`sharedState`、`$http` などのヘルパーも利用できます。
* 既存のフックの編集や、ルール、フォルダ、ツールの有効化状態は、メッセージが届くたびに最大で 1 秒に 1 回確認されます。キューに入っている呼び出しは完了することがあります。
* Console Log には最新の 200 件のログエントリが保持されます。スクリプトの処理が追いつかない場合、通信は続く一方でフックの呼び出しが警告付きでスキップされることがあります。Console Log はデバッグ用であり、通信の完全なアーカイブとしては使えません。

テキスト、JSON、バイナリ、ping/pong、close の例については、[WebSocket メッセージのスニペット](https://proxyman.com/docs/ja/scripting/snippet-code#log-websocket-messages-with-onwebsocket) を参照してください。

## 5. 例 {#5-examples}

次のガイドでは、JS コードでリクエストのドメインを本番環境から localhost に変更し、レスポンスのボディを変更する方法を説明します。

1. スクリプトを作成する前に、このドメインで SSL Proxying を有効にしてください。
2. Scripting ツールを開き、新しい Script Entry を作成します（⌘N）。リクエストを右クリックして Tools → Scripting… を選択することもできます。すると、Proxyman がスクリプトを作成します。
3. 名前を付けて、Matching Rule を定義します。
4. 例：**Name**=Test on Localhost endpoint、**URL**=https://proxyman.io
5. **Run Script on** で **Request** と **Response** のチェックボックスをオンにします。
6. `onRequest` 関数の JS コードを書き始めます。

**onRequest(context, url, request) {}**

```javascript
// Import UUID addons
const { uuidv4 } = require("@addons/UUID.js");

function onRequest(context, url, request) {
  // print log
  console.log(request);

  // Change Production domain -> Localhost
  request.method = "GET";
  request.scheme = "http";
  request.host = "localhost";
  request.port = 8000;
  
  // Add new header
  request.headers["X-New-Header"] = "Hello From Scripting feature";
  request.headers["UUID"] = uuidv4(); // generate random UUIDv4
  delete request.headers["Key-Need-Delete"];

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

  // Update Body
  var body = request.body;
  body["name"] = "Proxyman";
  request.body = body;
  
  // Or Map the body with a local file (Proxyman 2.25.0+)
  // request.bodyFilePath = "~/Desktop/mockdata.json";
  
  // Done
  return request;
}
```

## onRequest() オブジェクトの形式 {#onrequest-object-format}

`context`、`url`、`request` オブジェクトは次のように定義されます：

```javascript
// context (readonly)
{
    "scriptName": "<String> Your Script Name",
    "matchingRule": "<String> Your Matching Rule",
    "matchingMethod": "<String> Method",
    "isEnableOnRequest": "Bool",
    "isEnableOnResponse": "Bool",
    "filePath": "<String> Script path",
    "flow": { // Available from 2.16.0+
        "serverPort": "443",
        "serverIpAddress": "104.18.230.83",
        "clientIpAddress": "192.168.0.102",
        "remoteDeviceName": "iPhone XR",
        "remoteDeviceIP": "192.168.0.102",
        "id": "51",
        "clientPath": null,
        "clientPort": "51494",
        "clientName": null,
        "mapRemoteOriginalURL": "<String> Original URL before Map Remote modification (nullable)"
    },
}

// url (readonly)
url: String // => Present the full URL

// request
{
    "method": "<String> HTTP Method. Accept string method. Ex: GET, POST, ...",
    "scheme": "<String> Accept http or https",
    "host": "<String> Host of the request. Ex: api.proxyman.io, localhost, ...",
    "path": "<String>: Path of the URL. Ex: /v1/data",
    "port": "<Int> Accept int port number. Ex: 443, 8080, ..",
    "queries": "<[String: Any]> A JS Object (Dictionary) contains key values of the query",
    "headers": "<[String: Any]> A JS Object (Dictionary) contains key values of the header",
    "body": "Depend on the Content-Type header. It might be a dictionary for JSON and form, Plain Text or Uint8Array",
    "bodyFilePath": "<String><Optional> Set a body with a local file. See example in Snippet Code Page"
    "rawBody": "<Readonly>: A raw body String or Uint8Array",
    "preserveHostHeader": "<Bool> Preserve the Host",
    "isURLEncoding": "<Bool> Determine if Proxyman will perform URLEncoding when constructing the final URL. Default is True",
    "upstream": "<Object><Optional> Upstream TLS ALPN and HTTP/2 settings. Proxyman v26.0.0 or later for macOS."
}

```

> **備考:** `rawBody` を除く、request オブジェクトのすべての値を変更できます。

`onRequest` で `request.upstream` を設定します。例と対応しているフィールドについては、[Upstream ALPN と HTTP/2 の設定](https://proxyman.com/docs/ja/scripting/snippet-code#upstream-alpn-and-http2-settings) を参照してください。

> **備考:** Content-Type ヘッダーが正しくないために **body** の形式が不正になる場合は、**rawBody** を使い、文字列を手動で解析してください。

`request.body` の型は Content-Type ヘッダーによって決まります。

| Content-Type ヘッダー | request.body |
| --- | --- |
| application/json または JSON 系 | Javascript Object |
| application/x-www-form-urlencoded | Javascript Object |
| plain-text またはテキストベースの Content-Type（例：application/js、text/css、text/html など） | String |
| それ以外：バイナリデータ（application/zip、application/octet-stream） | Uint8Array |

よく使う JS コードは、スニペットコードのページにまとめています：

- [スニペットコード](https://proxyman.com/docs/ja/scripting/snippet-code)

7\. `onResponse` 関数でコードを書き始めます

```javascript
function onResponse(context, url, request, response) {
  console.log(response);

  // Update or Add a new header
  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;

  // Or Map the body with a local file (Proxyman 2.25.0+)
  // response.bodyFilePath = "~/Desktop/mockdata.json";
  
  // Done
  return response;
}
```

## onResponse() オブジェクトの形式 {#onresponse-objects-format}

`context`、`url`、`request`、`response` オブジェクトは次のように定義されます：

```javascript
// context (readonly): Same with onRequest

// url (readonly): Same with onRequest

// request (readonly): Same with onRequest

// response
{
    "statusCode": "<Int> Status Code. Ex: 200, 400, 404,...",
    "httpVersion": "<String><Readonly> The HTTP Version",
    "statusPhrase": "<String><Readonly> HTTP Status Phrase. Ex Not Found, OK, ...",
    "headers": "<[String: Any]> A JS Object (Dictionary) contains key values of the header",
    "body": "Depend on the Content-Type header. It might be a dictionary for JSON and form, PlainText or Uint8Array",
    "rawBody": "<Readonly>: A raw body String or Uint8Array",
    "bodyFilePath": "<String><Optional> Set a body with a local file. See example in Snippet Code Page"
}
```

> **備考:** response オブジェクトの `statusCode`、`headers`、`body` を変更できます。

> **備考:** Content-Type ヘッダーが正しくないために **body** の形式が不正になる場合は、**rawBody** を使い、文字列を手動で解析してください。

`response.body` の型は Content-Type ヘッダーによって決まります。

| Content-Type ヘッダー | response.body |
| --- | --- |
| application/json または JSON 系 | Javascript Object |
| application/x-www-form-urlencoded | Javascript Object |
| plain-text またはテキストベースの Content-Type（例：application/js、text/css、text/html など） | String |
| それ以外（application/zip、application/octet-stream） | Uint8Array |

> **備考:** `onRequest` と `onResponse` 関数では、`request` と `response` オブジェクトを必ず return してください。

## 6. 組み込みのアドオンとライブラリ {#6-built-in-addons-and-libraries}

Proxyman には、一般的な処理向けのアドオンとライブラリが豊富に用意されています：Hashing、Encode/Decode など。

- [アドオン](https://proxyman.com/docs/ja/scripting/addons)

- [組み込み JS ライブラリ](https://proxyman.com/docs/ja/scripting/built-in-js-libraries)

- [スニペットコード](https://proxyman.com/docs/ja/scripting/snippet-code)

## 7. JS エラーのデバッグ {#7-debugging-js-error}

構文エラーや不正なコードなどが原因で、JavaScript のエラーが発生することがあります。コンソールのエラーメッセージを読むか、`console.log()` を使うことで、デバッグできます。

## 8. Scripting をモック API として使う {#8-use-scripting-as-a-mock-api}

Proxyman 2.32.0 以降では、Scripting ツールをモック API として使えます。リクエストはサーバーに送信されず、レスポンスのボディは自分で定義します。この仕組みは Map Local と同じように動作します。

実際の REST API がまだ利用できない場合に便利です。ローカルで定義してテストできます。

モック API を有効にするには：

1. Scripting ツールを開いて、スクリプトを選択します。
2. Script Editor の **Request** と **Response** のチェックボックスの横にある **Run as Mock API** のチェックボックスをオンにします（上の Script Editor のスクリーンショットを参照）。

その後は、通常どおりにレスポンスを定義できます：

```javascript
function onResponse(context, url, request, response) {

  // Init new body
  var body = {};
  body["new-key"] = "Proxyman";
  response.body = body;

  // Or map from a file
  // response.bodyFilePath = "~/Desktop/myfile.json"
  
  // Done
  return response;
}
```

## 9. 注意事項 {#9-notes}

* `onRequest` と `onResponse` 関数では、`request` と `response` オブジェクトを必ず return してください。
* **Proxyman 4.16.0 以降**：Proxyman はバイナリデータを **Uint8Array** に変換するようになりました。
* Proxyman 4.15.0 以前：JavaScript には Data 型がないため、バイナリのボディは JavaScript で **Base64 Encoded String** に変換されていました。Uint8Array、blob、ArrayBuffer をボディとして渡すには、先に **Base64 Encoded String** に変換してください。

## 関連情報 {#learn-more}

* [GraphQL リクエストにローカルレスポンスをマップする方法](https://proxyman.com/posts/2021-01-24-How-To-Map_Response_For_GraphQL_Requests)
* [CORS ポリシーをバイパスする方法](https://proxyman.com/posts/2020-08-31-How-to-by-pass-CORS-policy)
