# How WebSocket Message Blocking Works in law-chain-hot/websocket-devtools: Complete Architecture Guide

> Explore WebSocket message blocking in law-chain-hot/websocket-devtools. Learn how it intercepts API calls to suppress messages and events with full DevTools visibility.

- Repository: [Brian 阿布/websocket-devtools](https://github.com/law-chain-hot/websocket-devtools)
- Tags: architecture
- Published: 2026-03-05

---

**The WebSocket message blocking system intercepts native WebSocket API calls through an injected page proxy, allowing selective suppression of outgoing sends and incoming message events while maintaining full visibility in the DevTools panel.**

The `law-chain-hot/websocket-devtools` extension provides developers with granular control over bidirectional WebSocket traffic through its message blocking functionality. This feature enables selective suppression of outgoing messages from the page or incoming messages from the server, creating a robust debugging environment for testing network failure scenarios. The implementation relies on a coordinated three-layer architecture spanning the DevTools UI, background script routing, and an in-page proxy that intercepts native WebSocket operations in [`src/content/injected.js`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/content/injected.js).

## Three-Layer Architecture for WebSocket Message Blocking

The extension implements blocking through three coordinated layers that bridge user interaction with native API interception.

### UI Control Layer in ControlPanel.jsx

The user interface in [`src/components/ControlPanel.jsx`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/components/ControlPanel.jsx) provides toggle switches that dispatch Chrome runtime messages to enable or disable blocking for each direction. When a user toggles the switch, the component sends a message with the type `block-outgoing` or `block-incoming` along with the enabled state and target tab ID.

```jsx
chrome.runtime.sendMessage({
  type: "block-outgoing",   // or "block-incoming"
  enabled: newState,
  tabId: currentTabId,
});

```

### Background Routing in background.js

The background script in [`src/background/background.js`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/background/background.js) listens for these block commands and forwards them to the appropriate content scripts. The handler calls `notifyAllTabs` to propagate the state change to every active tab, ensuring the blocking preference applies across the browser session.

```javascript
case "block-outgoing":
case "block-incoming":
  notifyAllTabs(request.type, request.enabled, request.tabId);
  break;

```

### In-Page Proxy Layer in injected.js

The content script in [`src/content/content.js`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/content/content.js) receives these commands and relays them to the injected proxy via `window.postMessage`. The proxy in [`src/content/injected.js`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/content/injected.js) maintains blocking state in a shared `proxyState` object that controls the interception logic.

```javascript
let proxyState = {
  isMonitoring: true,
  blockOutgoing: false,
  blockIncoming: false,
};

```

## Blocking Outgoing WebSocket Messages

The proxy overrides `WebSocket.prototype.send` to intercept all outgoing traffic. When `proxyState.blockOutgoing` is enabled and monitoring is active, the wrapper prevents the original native send from executing, effectively dropping the message before it reaches the network stack.

```javascript
ws.send = function (data) {
  const eventData = { /* common fields … */ };
  
  // Outgoing blocking check
  if (proxyState.blockOutgoing && proxyState.isMonitoring) {
    eventData.blocked = true;
    eventData.reason = "Outgoing messages blocked";

    connectionInfo.blockedMessages.push({
      data,
      timestamp: Date.now(),
      direction: "outgoing",
    });

    sendEvent(eventData);   // Report to DevTools panel
    return;                 // Abort: do not call originalSend
  }

  // Normal flow: forward to real WebSocket and log
  if (proxyState.isMonitoring) sendEvent(eventData);
  return originalSend(data);
};

```

When blocking is active, the function returns early without invoking `originalSend`, ensuring the message never leaves the browser. The proxy still reports the blocked event to the DevTools panel via `sendEvent`, allowing developers to see the suppressed message with a "blocked" tag.

## Blocking Incoming WebSocket Messages

For incoming traffic, the proxy registers a `message` event listener with `capture: true` to intercept events during the capture phase before they reach any user-registered handlers. When `proxyState.blockIncoming` is active, the listener swallows the event entirely.

```javascript
const ourMessageListener = function (event) {
  // Simulated messages bypass blocking
  if (event._isSimulated) { forwardToUser(event); return; }

  // Incoming blocking check
  if (proxyState.blockIncoming && proxyState.isMonitoring) {
    connectionInfo.blockedMessages.push({
      data: event.data,
      timestamp: Date.now(),
      direction: "incoming",
    });

    sendEvent({
      blocked: true,
      reason: "Incoming messages blocked",
      /* … other fields … */
    });
    return; // Swallow: do not forward to page listeners
  }

  // Normal processing: log then forward to user handlers
  if (proxyState.isMonitoring) { /* sendEvent */ }
  forwardToUser(event);
};

```

By stopping execution before calling `forwardToUser`, the proxy ensures that user code (such as `ws.onmessage` callbacks) never receives the blocked message. As with outgoing blocking, the event is still reported to the DevTools panel with `blocked: true` metadata for debugging visibility.

## Simulated Messages Bypass Blocking

Both outgoing and incoming simulation paths intentionally ignore block settings to allow developers to force test messages through the UI. The proxy checks for the `_isSimulated` flag at the entry point of both interception handlers, causing it to skip blocking logic and forward simulated traffic regardless of the current `proxyState` configuration.

## Summary

- The WebSocket message blocking system uses a three-layer flow: **UI toggles** → **background routing** (`notifyAllTabs`) → **in-page proxy** state updates
- **Outgoing blocking** wraps `WebSocket.send` in [`src/content/injected.js`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/content/injected.js) and aborts before calling the native implementation, preventing network transmission
- **Incoming blocking** uses capture-phase event listeners to swallow messages before they reach page handlers, ensuring user code never processes blocked data
- **Blocked events** still emit to the DevTools panel via `sendEvent` with `blocked: true` and reason metadata, maintaining full debugging visibility
- **Simulated messages** bypass all blocking logic via the `_isSimulated` flag, allowing forced testing regardless of current block settings

## Frequently Asked Questions

### How does the extension intercept WebSocket traffic without modifying the server?

The extension injects a proxy script into the page context via the content script, which overrides the native `WebSocket` constructor and its prototype methods. This client-side interception happens entirely within the browser, requiring no server-side changes or network-level proxy configuration.

### Why do blocked messages still appear in the DevTools panel?

Even when blocking is active, the proxy calls `sendEvent` with `blocked: true` and a descriptive reason string before aborting the operation. This ensures developers can see exactly which messages were suppressed, when they were blocked, and in which direction, preserving debugging visibility while preventing actual data transmission.

### Can I block messages for specific WebSocket connections only?

The current implementation in [`src/content/injected.js`](https://github.com/law-chain-hot/websocket-devtools/blob/main/src/content/injected.js) applies blocking globally through the shared `proxyState` object. The proxy checks `proxyState.blockOutgoing` and `proxyState.blockIncoming` flags for every WebSocket instance in the page, meaning all connections are affected uniformly by the toggle settings.

### Do simulated messages respect the blocking settings?

No. Simulated messages explicitly set an `_isSimulated` flag on the event object. Both the outgoing send wrapper and incoming message listener check for this flag first and bypass all blocking logic when present, allowing developers to force test messages through regardless of current block configurations.