Proxyman の Scripting で WebSocket メッセージを変更する方法

Proxyman の Scripting で WebSocket メッセージを変更する方法 のカバー画像

6 分で読めます

Proxyman で WebSocket メッセージをその場で変更 できるようになりました 🎉

JavaScript を数行書くだけで、メッセージを編集したり、破棄したり、新しいメッセージを送信したりできます。WS と WSS に対応しており、クライアントからサーバーへ、サーバーからクライアントへ、どちらの方向でも扱えます。

このブログでは次の内容を扱います:

  • WebSocket アプリのテストにおける課題。
  • 新しい Modify WebSocket Messages オプションの動作。
  • 自分で実行できるローカル WebSocket サーバーを使った、ステップごとのデモ。
  • すぐに使える WebSocket テンプレートと、変更が反映されないときのヒント。

Modify WebSocket Messages は Proxyman for macOS の新機能です。このガイドを試すには、ダウンロードページ から最新バージョンにアップデートしてください。

完成イメージは次のとおりです。サーバーは実際の BTC 価格を送信していますが、アプリが受け取るのは $1 です。アプリがパスワードを送信すると、Proxyman はそのメッセージを破棄し、警告をアプリに返します:

1. 課題

チャット、取引、ゲーム、ライブダッシュボードなどのアプリは、リアルタイムデータの取得に WebSocket を使います。これらのテストは難しく、理由は次のとおりです:

  • サーバーを制御できません。 サーバーは実際のデータを送信します。価格が 0 になる場合やエラーメッセージのような特殊なケースを送らせるには、サーバーのコードを変更する必要があります。
  • 再現しにくいケースがあります。 メッセージの欠落、奇妙な応答、接続の切断は、必要なときにはめったに起きません。
  • これまでの Proxyman は WebSocket メッセージを読むことしかできませんでした。 Scripting の onWebsocket() 関数でメッセージをログに出力できましたが、変更はできませんでした。

2. 「Modify WebSocket Messages」とは

これは Scripting ルールの新しいチェックボックスです。オンにすると、Proxyman は onWebsocket() 関数が値を返すまで、各 WebSocket メッセージを保留します。何を返すかによって結果が決まります:

onWebsocket() 内の記述結果
return message変更を加えたメッセージを送信
return nullメッセージを破棄。相手側には届かない
戻り値なし元のメッセージを送信
abort()両側で WebSocket を閉じる
sendToClient(data) / sendToServer(data)新しいメッセージを送信。String を渡すとテキストメッセージ、Uint8Array を渡すとバイナリメッセージになる

message オブジェクトには次のフィールドがあります:

フィールド値
message.from"client" または "server"
message.type"text"、"binary"、"ping"、"pong"、または "close"
message.dataメッセージのテキスト。テキストメッセージを編集するには、この値を変更します
message.rawDataバイナリメッセージのバイト列(Uint8Array)

このオプションはデフォルトでオフです。オフのときは、onWebsocket() は従来どおり動作します。メッセージを読み取るだけで、遅延させることはありません。

3. 手順:ライブ WebSocket を変更する

動画のデモを作っていきます。BTC 価格を送信する小さなサーバーと、Proxyman を経由してそのサーバーとやり取りするクライアントです。

3.1 WebSocket サーバーを起動する

新しいフォルダを作成し、ws パッケージをインストールします:

mkdir websocket-demo && cd websocket-demo
npm init -y
npm install ws

次の内容を server.js として保存します。1 秒ごとに価格を送信し、受け取ったチャットメッセージをすべてエコーバックします:

// server.js: a WebSocket server on ws://localhost:3456
const WebSocket = require('ws');
const server = new WebSocket.Server({ port: 3456 });

server.on('connection', (socket) => {
  // Send a BTC price every second
  const timer = setInterval(() => {
    const price = Number((67000 + Math.random() * 1000).toFixed(2));
    socket.send(JSON.stringify({ type: 'price', symbol: 'BTC', price: price }));
  }, 1000);

  // Print what the client sends, then echo it back
  socket.on('message', (data) => {
    console.log('SERVER gets:', data.toString());
    socket.send(JSON.stringify({ type: 'echo', text: JSON.parse(data).text }));
  });

  socket.on('close', () => clearInterval(timer));
});

console.log('WebSocket server on ws://localhost:3456');

ターミナルで実行します:

node server.js

3.2 Proxyman を経由してクライアントを接続する

Node.js は macOS のプロキシ設定を使わないため、クライアントは WebSocket のハンドシェイクを Proxyman に直接送信します。次の内容を client.js として保存します:

// client.js: connects to the server through Proxyman
const http = require('http');
const WebSocket = require('ws');

// Proxyman's port. You can see it in the Proxyman toolbar
const PROXYMAN_PORT = 9090;

// Sends the WebSocket handshake to Proxyman, like an app that uses an HTTP proxy
class ProxymanAgent extends http.Agent {
  addRequest(request, options) {
    request.path = 'http://' + options.host + ':' + options.port + request.path;
    super.addRequest(request, { ...options, host: '127.0.0.1', port: PROXYMAN_PORT });
  }
}

const socket = new WebSocket('ws://localhost:3456/prices', { agent: new ProxymanAgent() });

socket.on('open', () => {
  setTimeout(() => socket.send(JSON.stringify({ type: 'chat', text: 'Hello' })), 2000);
  setTimeout(() => socket.send(JSON.stringify({ type: 'chat', text: 'my password is hunter2' })), 4000);
});

socket.on('message', (data) => console.log('CLIENT gets:', data.toString()));

Proxyman を開き、別のターミナルでクライアントを実行します:

node client.js

Proxyman に ws://localhost:3456/prices が表示されます。Websocket タブを開くと、メッセージを確認できます。まだ何も変更されていません。Ctrl+C でクライアントを停止します。

3.3 Scripting ルールを作成する

  1. Proxyman で ws://localhost:3456/prices のフローを右クリックし、Tools → Scripting... を選択します。Proxyman が URL を入力済みのルールを作成します。
  2. Request はオンのままにして、Modify WebSocket Messages にチェックを入れます。
  3. スクリプトを下のコードに置き換えて、Save & Activate(⌘S)を押します。
Modify WebSocket Messages にチェックを入れ、onWebsocket() スクリプトを記述した Proxyman の Script Editor
Modify WebSocket Messages にチェックを入れ、onWebsocket() スクリプトを記述した Proxyman の Script Editor
function onWebsocket(context, url, request, response, message) {
  // Only change text messages. Binary, ping, pong, and close messages pass through
  if (message.type !== "text") {
    return message;
  }

  const json = JSON.parse(message.data);

  // 1. Server → client: every BTC price becomes $1
  if (message.from === "server" && json.type === "price") {
    json.price = 1;
    json.note = "edited by Proxyman";
    message.data = JSON.stringify(json);
    return message;
  }

  // 2. Client → server: drop a message with a password, and warn the client
  if (message.from === "client" && json.text.includes("password")) {
    sendToClient(JSON.stringify({ type: "warning", text: "Proxyman blocked a password" }));
    return null;
  }

  // 3. Everything else: send it as it is
  return message;
}

3.4 再実行して結果を確認する

node client.js をもう一度実行します。Proxyman はこのオプションを WebSocket の接続時に判断するため、ルールを保存したら必ず再接続してください。

これで、クライアントは "price": 1 と、note "edited by Proxyman" を含むメッセージを受け取ります。パスワードのメッセージはサーバーに届きません。サーバー側のターミナルには Hello だけが表示され、代わりにクライアントはスクリプトからの警告を受け取ります。

左:クライアントとサーバーが実際に送受信する内容。右:編集された価格を表示している Proxyman の Websocket タブ
左:クライアントとサーバーが実際に送受信する内容。右:編集された価格を表示している Proxyman の Websocket タブ

Proxyman の Websocket タブには、編集された価格、スクリプトが送信した警告、そして破棄されたパスワードのメッセージが表示されます。

編集された WebSocket メッセージを含む ws://localhost:3456/prices フローが表示されている Proxyman のメインウィンドウ
編集された WebSocket メッセージを含む ws://localhost:3456/prices フローが表示されている Proxyman のメインウィンドウ

4. その他の例

テキストを両方向で置き換えます(例:ホスト名の切り替え):

const FIND_REGEX = /staging\.example\.com/g;
const REPLACE_WITH = "api.example.com";

function onWebsocket(context, url, request, response, message) {
  if (message.type === "text") {
    message.data = message.data.replace(FIND_REGEX, REPLACE_WITH);
  }
  return message;
}

10 件のメッセージの後に接続切れをシミュレートし、アプリの再接続の挙動をテストします。関数の外にあるコードは接続ごとに 1 回だけ実行されるため、各接続は独自にメッセージ数を数えます:

const CLOSE_AFTER_MESSAGES = 10;
let messageCount = 0;

function onWebsocket(context, url, request, response, message) {
  messageCount += 1;
  if (messageCount === CLOSE_AFTER_MESSAGES) {
    console.log("Closing the WebSocket after " + messageCount + " messages");
    abort();
  }
  return message;
}

バイナリメッセージを変更するには、新しいバイト列を設定します:

function onWebsocket(context, url, request, response, message) {
  if (message.type === "binary" && message.from === "server") {
    message.rawData = new Uint8Array([1, 2, 3]);
  }
  return message;
}

5. WebSocket テンプレートですばやく始める

スクリプトをゼロから書く必要はありません。Script Editor で Templates をクリックし、「websocket」を検索します:

  • Log WebSocket Messages:すべてのメッセージを Console Log に出力します。メッセージを読み取るだけです。
  • Modify JSON WebSocket Messages:サーバーから届く JSON メッセージのフィールドを変更します。
  • Replace Text in WebSocket Messages:両方向でテキストを検索して置き換えます。
  • Drop WebSocket Messages:正規表現に一致するメッセージ(例:アナリティクスイベント)を破棄します。
  • Reply to a WebSocket Message:sendToClient() を使って、クライアントのメッセージに応答します。サーバーには届きません。
  • Simulate a WebSocket Disconnect:数件のメッセージの後に WebSocket を閉じます。

テンプレートを使うには Replace をクリックします。Proxyman は必要なチェックボックスも自動で設定します。その中には Modify WebSocket Messages も含まれます。

WebSocket テンプレートで絞り込まれた、Proxyman の Script Templates シート
WebSocket テンプレートで絞り込まれた、Proxyman の Script Templates シート

6. 問題と解決方法

メッセージが変わらない

  • Modify WebSocket Messages にチェックを入れ、Save & Activate(⌘S)を押します。
  • WebSocket に再接続します。このオプションは、メッセージごとではなく、WebSocket の接続時に読み込まれます。
  • Request か Response のどちらかをオンのままにします。両方がオフのルールは、onWebsocket() を含めて一切実行されません。
  • Script Editor の Console Log を開きます。スクリプトでエラーが発生した場合、Proxyman は元のメッセージを送信し、そこにエラーを出力します。

Proxyman で WebSocket が表示されない

  • wss:// の場合は、HTTPS のときと同様に、そのドメインで SSL Proxying を有効にしてください。
  • 多くのアプリやライブラリは、localhost へのアクセスでプロキシを経由しません。client.js と同じように Proxyman へ通信を送るか、お使いのプラットフォーム向けの WebSocket のドキュメント に従ってください。

知っておくと便利なこと

  • ルールに一致する WebSocket だけがスクリプトの処理を待ちます。ほかの通信はそのまま流れ続けます。
  • 16 MB を超えるメッセージは、スクリプトを通さずにそのまま送信されます。
  • AI エージェントを使っている場合は、Proxyman MCP で同じルールを作成できます。create_scripting_rule に modify_websocket_messages: true を指定します。

関連情報

Noah Tran
Noah Tran

サーバーに触れずに WebSocket メッセージを変更

Scripting ルールで Modify WebSocket Messages をオンにして、アプリが処理するすべてのメッセージをテストできます。

Proxyman for macOS をダウンロード