websocket ناجح

Specialized skill for WebSocket protocol implementation and testing. Generate RFC 6455 compliant implementations, validate handshake and framing, test with Autobahn Test Suite, implement compression, and debug connection issues.

٤٨٠
نجوم
٣
تنزيلات
١٤
مشاهدات

// تثبيت المهارة

تثبيت المهارة

المهارات هي كود تابع لأطراف ثالثة من مستودعات GitHub العامة. يفحص SkillHub الأنماط الخبيثة المعروفة، لكنه لا يستطيع ضمان السلامة. راجع الكود المصدري قبل التثبيت.

تثبيت عام (على مستوى المستخدم):

npx skillhub install majiayu000/claude-skill-registry/websocket

تثبيت في المشروع الحالي:

npx skillhub install majiayu000/claude-skill-registry/websocket --project

skill.install.customTargetHelp

npx skillhub install majiayu000/claude-skill-registry/websocket --target-dir /path/to/skills

المسار المقترح: ~/.claude/skills/websocket/

محتوى SKILL.md

---
name: websocket
description: Specialized skill for WebSocket protocol implementation and testing. Generate RFC 6455 compliant implementations, validate handshake and framing, test with Autobahn Test Suite, implement compression, and debug connection issues.
allowed-tools: Bash(*) Read Write Edit Glob Grep WebFetch
metadata:
  author: babysitter-sdk
  version: "1.0.0"
  category: real-time-communication
  backlog-id: SK-005
---

# websocket

You are **websocket** - a specialized skill for WebSocket protocol implementation and testing, providing deep expertise in RFC 6455 compliance, real-time messaging, and performance optimization.

## Overview

This skill enables AI-powered WebSocket operations including:
- Generating RFC 6455 compliant implementations
- Validating WebSocket handshake and framing
- Testing with Autobahn Test Suite
- Implementing permessage-deflate compression
- Debugging WebSocket connection issues
- Generating subprotocol handlers
- Analyzing WebSocket traffic

## Prerequisites

- WebSocket-capable runtime (Node.js, Python, Go, etc.)
- Optional: `wscat` or `websocat` for CLI testing
- Optional: Autobahn Test Suite for compliance testing

## Capabilities

### 1. WebSocket Handshake

Implement RFC 6455 compliant handshake:

```javascript
const crypto = require('crypto');
const http = require('http');

const WS_MAGIC_STRING = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';

function computeAcceptKey(secWebSocketKey) {
  return crypto
    .createHash('sha1')
    .update(secWebSocketKey + WS_MAGIC_STRING)
    .digest('base64');
}

function handleUpgrade(req, socket) {
  // Validate upgrade request
  if (req.headers['upgrade']?.toLowerCase() !== 'websocket') {
    socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
    return false;
  }

  const key = req.headers['sec-websocket-key'];
  if (!key) {
    socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
    return false;
  }

  // Validate key format (16 bytes base64 encoded)
  const keyBytes = Buffer.from(key, 'base64');
  if (keyBytes.length !== 16) {
    socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
    return false;
  }

  const acceptKey = computeAcceptKey(key);

  // Optional: Handle subprotocol negotiation
  const requestedProtocols = req.headers['sec-websocket-protocol']?.split(',').map(p => p.trim()) || [];
  const selectedProtocol = negotiateProtocol(requestedProtocols);

  // Build response headers
  let response = [
    'HTTP/1.1 101 Switching Protocols',
    'Upgrade: websocket',
    'Connection: Upgrade',
    `Sec-WebSocket-Accept: ${acceptKey}`
  ];

  if (selectedProtocol) {
    response.push(`Sec-WebSocket-Protocol: ${selectedProtocol}`);
  }

  // Optional: Handle extensions
  const extensions = negotiateExtensions(req.headers['sec-websocket-extensions']);
  if (extensions) {
    response.push(`Sec-WebSocket-Extensions: ${extensions}`);
  }

  socket.write(response.join('\r\n') + '\r\n\r\n');
  return true;
}

function negotiateProtocol(requested) {
  const supported = ['graphql-ws', 'wamp.2.json', 'mqtt'];
  return requested.find(p => supported.includes(p)) || null;
}

function negotiateExtensions(extensionHeader) {
  // Example: permessage-deflate negotiation
  if (extensionHeader?.includes('permessage-deflate')) {
    return 'permessage-deflate; server_no_context_takeover; client_no_context_takeover';
  }
  return null;
}
```

### 2. WebSocket Frame Parsing

Parse and create WebSocket frames:

```javascript
const OPCODES = {
  CONTINUATION: 0x0,
  TEXT: 0x1,
  BINARY: 0x2,
  CLOSE: 0x8,
  PING: 0x9,
  PONG: 0xA
};

class WebSocketFrame {
  constructor() {
    this.fin = true;
    this.rsv1 = false;
    this.rsv2 = false;
    this.rsv3 = false;
    this.opcode = OPCODES.TEXT;
    this.masked = false;
    this.maskingKey = null;
    this.payload = Buffer.alloc(0);
  }

  static parse(buffer) {
    if (buffer.length < 2) return { frame: null, consumed: 0 };

    const frame = new WebSocketFrame();
    let offset = 0;

    // First byte: FIN, RSV1-3, Opcode
    const byte0 = buffer[offset++];
    frame.fin = (byte0 & 0x80) !== 0;
    frame.rsv1 = (byte0 & 0x40) !== 0;
    frame.rsv2 = (byte0 & 0x20) !== 0;
    frame.rsv3 = (byte0 & 0x10) !== 0;
    frame.opcode = byte0 & 0x0F;

    // Second byte: MASK, Payload length
    const byte1 = buffer[offset++];
    frame.masked = (byte1 & 0x80) !== 0;
    let payloadLength = byte1 & 0x7F;

    // Extended payload length
    if (payloadLength === 126) {
      if (buffer.length < offset + 2) return { frame: null, consumed: 0 };
      payloadLength = buffer.readUInt16BE(offset);
      offset += 2;
    } else if (payloadLength === 127) {
      if (buffer.length < offset + 8) return { frame: null, consumed: 0 };
      // JavaScript can't handle 64-bit integers precisely
      const high = buffer.readUInt32BE(offset);
      const low = buffer.readUInt32BE(offset + 4);
      payloadLength = high * 0x100000000 + low;
      offset += 8;
    }

    // Masking key (if masked)
    if (frame.masked) {
      if (buffer.length < offset + 4) return { frame: null, consumed: 0 };
      frame.maskingKey = buffer.slice(offset, offset + 4);
      offset += 4;
    }

    // Payload
    if (buffer.length < offset + payloadLength) {
      return { frame: null, consumed: 0 };
    }

    frame.payload = buffer.slice(offset, offset + payloadLength);
    offset += payloadLength;

    // Unmask payload if needed
    if (frame.masked) {
      frame.payload = Buffer.from(frame.payload); // Create copy
      for (let i = 0; i < frame.payload.length; i++) {
        frame.payload[i] ^= frame.maskingKey[i % 4];
      }
    }

    return { frame, consumed: offset };
  }

  serialize(mask = false) {
    const payloadLength = this.payload.length;
    let headerLength = 2;

    if (payloadLength > 65535) headerLength += 8;
    else if (payloadLength > 125) headerLength += 2;

    if (mask) headerLength += 4;

    const buffer = Buffer.alloc(headerLength + payloadLength);
    let offset = 0;

    // First byte
    buffer[offset++] = (this.fin ? 0x80 : 0) |
                       (this.rsv1 ? 0x40 : 0) |
                       (this.rsv2 ? 0x20 : 0) |
                       (this.rsv3 ? 0x10 : 0) |
                       this.opcode;

    // Second byte and extended length
    let lengthByte = mask ? 0x80 : 0;

    if (payloadLength > 65535) {
      lengthByte |= 127;
      buffer[offset++] = lengthByte;
      buffer.writeUInt32BE(Math.floor(payloadLength / 0x100000000), offset);
      buffer.writeUInt32BE(payloadLength % 0x100000000, offset + 4);
      offset += 8;
    } else if (payloadLength > 125) {
      lengthByte |= 126;
      buffer[offset++] = lengthByte;
      buffer.writeUInt16BE(payloadLength, offset);
      offset += 2;
    } else {
      lengthByte |= payloadLength;
      buffer[offset++] = lengthByte;
    }

    // Masking key
    if (mask) {
      const maskingKey = crypto.randomBytes(4);
      maskingKey.copy(buffer, offset);
      offset += 4;

      // Copy and mask payload
      for (let i = 0; i < payloadLength; i++) {
        buffer[offset + i] = this.payload[i] ^ maskingKey[i % 4];
      }
    } else {
      this.payload.copy(buffer, offset);
    }

    return buffer;
  }
}
```

### 3. WebSocket Server Implementation

Complete WebSocket server:

```javascript
const http = require('http');
const crypto = require('crypto');
const EventEmitter = require('events');

class WebSocketServer extends EventEmitter {
  constructor(options = {}) {
    super();
    this.port = options.port || 8080;
    this.maxPayload = options.maxPayload || 100 * 1024 * 1024; // 100MB
    this.clients = new Set();

    this.server = http.createServer((req, res) => {
      res.writeHead(426, { 'Content-Type': 'text/plain' });
      res.end('WebSocket server - upgrade required');
    });

    this.server.on('upgrade', (req, socket, head) => {
      this.handleUpgrade(req, socket, head);
    });
  }

  handleUpgrade(req, socket, head) {
    const key = req.headers['sec-websocket-key'];
    if (!key) {
      socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
      return;
    }

    const acceptKey = crypto
      .createHash('sha1')
      .update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
      .digest('base64');

    socket.write([
      'HTTP/1.1 101 Switching Protocols',
      'Upgrade: websocket',
      'Connection: Upgrade',
      `Sec-WebSocket-Accept: ${acceptKey}`,
      '',
      ''
    ].join('\r\n'));

    const client = new WebSocketConnection(socket, this);
    this.clients.add(client);

    client.on('close', () => {
      this.clients.delete(client);
    });

    this.emit('connection', client, req);
  }

  broadcast(message, excludeClient = null) {
    for (const client of this.clients) {
      if (client !== excludeClient && client.readyState === 'OPEN') {
        client.send(message);
      }
    }
  }

  listen(callback) {
    this.server.listen(this.port, callback);
  }

  close(callback) {
    for (const client of this.clients) {
      client.close(1001, 'Server shutting down');
    }
    this.server.close(callback);
  }
}

class WebSocketConnection extends EventEmitter {
  constructor(socket, server) {
    super();
    this.socket = socket;
    this.server = server;
    this.readyState = 'OPEN';
    this.buffer = Buffer.alloc(0);
    this.fragments = [];

    socket.on('data', (data) => this.handleData(data));
    socket.on('close', () => this.handleClose());
    socket.on('error', (err) => this.emit('error', err));
  }

  handleData(data) {
    this.buffer = Buffer.concat([this.buffer, data]);

    while (this.buffer.length > 0) {
      const { frame, consumed } = WebSocketFrame.parse(this.buffer);
      if (!frame) break;

      this.buffer = this.buffer.slice(consumed);
      this.handleFrame(frame);
    }
  }

  handleFrame(frame) {
    switch (frame.opcode) {
      case OPCODES.TEXT:
      case OPCODES.BINARY:
        if (frame.fin) {
          const data = frame.opcode === OPCODES.TEXT
            ? frame.payload.toString('utf8')
            : frame.payload;
          this.emit('message', data);
        } else {
          this.fragments.push(frame);
        }
        break;

      case OPCODES.CONTINUATION:
        this.fragments.push(frame);
        if (frame.fin) {
          const firstFrame = this.fragments[0];
          const payload = Buffer.concat(this.fragments.map(f => f.payload));
          const data = firstFrame.opcode === OPCODES.TEXT
            ? payload.toString('utf8')
            : payload;
          this.emit('message', data);
          this.fragments = [];
        }
        break;

      case OPCODES.PING:
        this.pong(frame.payload);
        break;

      case OPCODES.PONG:
        this.emit('pong', frame.payload);
        break;

      case OPCODES.CLOSE:
        let code = 1005;
        let reason = '';
        if (frame.payload.length >= 2) {
          code = frame.payload.readUInt16BE(0);
          reason = frame.payload.slice(2).toString('utf8');
        }
        this.close(code, reason);
        break;
    }
  }

  send(data) {
    if (this.readyState !== 'OPEN') return;

    const frame = new WebSocketFrame();
    if (typeof data === 'string') {
      frame.opcode = OPCODES.TEXT;
      frame.payload = Buffer.from(data, 'utf8');
    } else {
      frame.opcode = OPCODES.BINARY;
      frame.payload = data;
    }

    this.socket.write(frame.serialize(false)); // Server doesn't mask
  }

  ping(data = Buffer.alloc(0)) {
    const frame = new WebSocketFrame();
    frame.opcode = OPCODES.PING;
    frame.payload = Buffer.isBuffer(data) ? data : Buffer.from(data);
    this.socket.write(frame.serialize(false));
  }

  pong(data = Buffer.alloc(0)) {
    const frame = new WebSocketFrame();
    frame.opcode = OPCODES.PONG;
    frame.payload = Buffer.isBuffer(data) ? data : Buffer.from(data);
    this.socket.write(frame.serialize(false));
  }

  close(code = 1000, reason = '') {
    if (this.readyState === 'CLOSED') return;

    this.readyState = 'CLOSING';

    const frame = new WebSocketFrame();
    frame.opcode = OPCODES.CLOSE;

    const codeBuffer = Buffer.alloc(2);
    codeBuffer.writeUInt16BE(code, 0);
    const reasonBuffer = Buffer.from(reason, 'utf8');
    frame.payload = Buffer.concat([codeBuffer, reasonBuffer]);

    this.socket.write(frame.serialize(false));
    this.socket.end();
  }

  handleClose() {
    this.readyState = 'CLOSED';
    this.emit('close');
  }
}
```

### 4. permessage-deflate Compression

Implement WebSocket compression:

```javascript
const zlib = require('zlib');

class PerMessageDeflate {
  constructor(options = {}) {
    this.serverNoContextTakeover = options.serverNoContextTakeover || false;
    this.clientNoContextTakeover = options.clientNoContextTakeover || false;
    this.serverMaxWindowBits = options.serverMaxWindowBits || 15;
    this.clientMaxWindowBits = options.clientMaxWindowBits || 15;

    this.inflateContext = null;
    this.deflateContext = null;
  }

  compress(data, callback) {
    if (!this.deflateContext || this.serverNoContextTakeover) {
      this.deflateContext = zlib.createDeflateRaw({
        windowBits: this.serverMaxWindowBits
      });
    }

    const chunks = [];
    this.deflateContext.on('data', (chunk) => chunks.push(chunk));
    this.deflateContext.on('end', () => {
      let result = Buffer.concat(chunks);
      // Remove trailing 0x00 0x00 0xFF 0xFF
      if (result.length >= 4 &&
          result[result.length - 4] === 0x00 &&
          result[result.length - 3] === 0x00 &&
          result[result.length - 2] === 0xFF &&
          result[result.length - 1] === 0xFF) {
        result = result.slice(0, -4);
      }
      callback(null, result);
    });

    this.deflateContext.write(data);
    this.deflateContext.flush(zlib.Z_SYNC_FLUSH);
  }

  decompress(data, callback) {
    if (!this.inflateContext || this.clientNoContextTakeover) {
      this.inflateContext = zlib.createInflateRaw({
        windowBits: this.clientMaxWindowBits
      });
    }

    // Add trailing bytes for decompression
    const trailer = Buffer.from([0x00, 0x00, 0xFF, 0xFF]);
    const input = Buffer.concat([data, trailer]);

    const chunks = [];
    this.inflateContext.on('data', (chunk) => chunks.push(chunk));
    this.inflateContext.on('end', () => {
      callback(null, Buffer.concat(chunks));
    });
    this.inflateContext.on('error', (err) => callback(err));

    this.inflateContext.write(input);
    this.inflateContext.flush();
  }
}
```

### 5. WebSocket Testing

Test WebSocket implementations:

```bash
# Using wscat
wscat -c ws://localhost:8080

# Using websocat
websocat ws://localhost:8080

# Autobahn Test Suite (fuzzing)
docker run -it --rm \
  -v "${PWD}/reports:/reports" \
  -p 9001:9001 \
  crossbario/autobahn-testsuite \
  wstest --mode fuzzingclient --spec /config/fuzzingclient.json
```

## MCP Server Integration

This skill can leverage the following MCP servers for enhanced capabilities:

| Server | Description | Integration |
|--------|-------------|-------------|
| MCP-WebSocket Architecture | WebSocket transport with MCP | Real-time AI integration |
| claude-agent-server | WebSocket server for Claude Agent SDK | Agent orchestration |
| Claude-Flow | Multi-agent communication via WebSocket | Distributed agents |

## Best Practices

1. **Handle fragmented messages** - Large messages may be split across frames
2. **Implement heartbeat** - Use ping/pong for connection health
3. **Set payload limits** - Prevent memory exhaustion attacks
4. **Close gracefully** - Send close frame before disconnecting
5. **Validate UTF-8** - Text frames must be valid UTF-8
6. **Handle backpressure** - Don't overwhelm slow clients

## Process Integration

This skill integrates with the following processes:
- `websocket-server.js` - WebSocket server implementation
- `websocket-client.js` - WebSocket client implementation
- `realtime-messaging-system.js` - Real-time messaging architecture

## Output Format

When executing operations, provide structured output:

```json
{
  "operation": "test",
  "target": "ws://localhost:8080",
  "status": "success",
  "handshake": {
    "protocol": "graphql-ws",
    "extensions": ["permessage-deflate"]
  },
  "metrics": {
    "messagesReceived": 1000,
    "messagesSent": 1000,
    "avgLatencyMs": 2.5,
    "compressionRatio": 0.65
  },
  "compliance": {
    "rfc6455": true,
    "autobahnPassed": 512,
    "autobahnFailed": 0
  }
}
```

## Constraints

- Follow RFC 6455 strictly for interoperability
- Server must not mask frames (clients must)
- Validate close codes (1000-1015, 3000-4999)
- Handle UTF-8 validation for text frames
- Limit concurrent connections per client

الترخيص

الترخيص المُعلن: MIT

MIT License

Copyright (c) 2025 majiayu000

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

عرض الترخيص في المستودع المصدريالنسخة المنشورة هناك هي المرجع.