Files
WireMock.Net/copilot/WebSockets/v1/WEBSOCKET_QUICK_REFERENCE.md
Stef Heyenrath a3da39a9ec ws1
2026-02-08 10:30:59 +01:00

12 KiB

WebSocket Fluent Interface - Quick Reference

At a Glance

Current Architecture (HTTP Only)

// Request matching
Request.Create()
    .WithPath("/api")
    .WithHeader("...")
    .UsingGet()

// Response building
Response.Create()
    .WithStatusCode(200)
    .WithBodyAsJson(...)
    .WithTransformer()

// Mapping
server.Given(request)
    .AtPriority(1)
    .InScenario("...")
    .RespondWith(response)

Proposed Addition (WebSocket Support)

// WebSocket request matching
Request.Create()
    .WithWebSocketPath("/ws")
    .WithWebSocketSubprotocol("chat")

// WebSocket response building
Response.Create()
    .WithWebSocket(ws => ws
        .WithMessage("Hello")
        .WithJsonMessage(obj)
        .WithTransformer()
    )
    .WithWebSocketCallback(async req => ... messages ...)

// Mapping (same as HTTP)
server.Given(request)
    .RespondWith(response)

Quick Comparison: HTTP vs WebSocket Fluent API

Request Builder

HTTP WebSocket
WithPath(string) WithWebSocketPath(string)
WithHeader(string, string) WithHeader(...) (same)
UsingGet() WithWebSocketUpgrade() (implicit)
WithParam(string, string) (not applicable)
WithBody(string) (connection is upgrade, no body)

Response Builder

HTTP WebSocket
WithStatusCode(int) WithWebSocketClose(int)
WithBody(string) WithMessage(string)
WithBodyAsJson(object) WithJsonMessage(object)
(binary: rarely used) WithBinaryMessage(byte[])
WithCallback(...) WithWebSocketCallback(...)
WithTransformer() WithTransformer() (same)

Mapping Configuration

Feature HTTP WebSocket
Priority AtPriority(int) AtPriority(int)
Scenario InScenario(...) InScenario(...)
Webhook WithWebhook(...) WithWebhook(...)
Title/Desc WithTitle(...) WithTitle(...)

Implementation Checklist

Phase 1: Abstractions

  • Create IWebSocketMessage interface
  • Create IWebSocketResponse interface
  • Create IWebSocketResponseBuilder interface
  • Add WebSocketModel to admin mappings
  • Extend IRequestBuilder with WebSocket methods
  • Extend IResponseBuilder with WebSocket methods

Phase 2: Domain Models

  • Implement WebSocketMessage class
  • Implement WebSocketResponse class

Phase 3: Request Builder Extension

  • Create Request.WithWebSocket.cs partial class
  • Implement WithWebSocketUpgrade()
  • Implement WithWebSocketPath()
  • Implement WithWebSocketSubprotocol()
  • Add unit tests

Phase 4: Response Builder Extension

  • Create Response.WithWebSocket.cs partial class
  • Implement WebSocket response methods
  • Create WebSocketResponseBuilder.cs
  • Add transformer support
  • Add callback support
  • Add unit tests

Phase 5: Server Integration

  • Update WireMockMiddleware.cs to handle WebSocket upgrades
  • Implement WebSocket connection handling
  • Implement message delivery
  • Add connection lifecycle management
  • Add integration tests

Phase 6: Admin Interface

  • Extend MappingModel with WebSocket config
  • Update mapping serialization
  • Add REST API endpoints for WebSocket management

File Changes Summary

New Files (Abstractions)

src/WireMock.Net.Abstractions/
├── Models/IWebSocketMessage.cs
├── Models/IWebSocketResponse.cs
├── BuilderExtensions/IWebSocketResponseBuilder.cs
└── Admin/Mappings/WebSocketModel.cs

New Files (Implementation)

src/WireMock.Net.Minimal/
├── Models/WebSocketMessage.cs
├── Models/WebSocketResponse.cs
├── RequestBuilders/Request.WithWebSocket.cs
├── ResponseBuilders/Response.WithWebSocket.cs
└── ResponseBuilders/WebSocketResponseBuilder.cs

Modified Files

src/WireMock.Net.Minimal/
├── ResponseBuilders/Response.cs (add interface definitions)
├── RequestBuilders/Request.cs (add interface definitions)
├── Server/WireMockServer.cs (WebSocket support)
├── Owin/WireMockMiddleware.cs (handle upgrades)
└── Owin/MappingMatcher.cs (WebSocket routing)

Code Examples

Simple WebSocket

// Echo server
server.Given(Request.Create().WithWebSocketPath("/echo"))
    .RespondWith(Response.Create()
        .WithWebSocketCallback(async req =>
            new[] { new WebSocketMessage { BodyAsString = req.Body } }
        )
    );

Messages with Delays

// Multi-message response
server.Given(Request.Create().WithWebSocketPath("/stream"))
    .RespondWith(Response.Create()
        .WithWebSocket(ws => ws
            .WithMessage("First", 0)
            .WithMessage("Second", 500)
            .WithMessage("Third", 1000)
        )
    );

Dynamic Messages

// Request-based generation
server.Given(Request.Create().WithWebSocketPath("/api"))
    .RespondWith(Response.Create()
        .WithWebSocketCallback(async request =>
        {
            var userId = request.Headers["X-User-Id"].First();
            return new[]
            {
                new WebSocketMessage 
                { 
                    BodyAsString = $"Hello {userId}" 
                }
            };
        })
    );

Templated Messages

// Handlebars in message content
server.Given(Request.Create().WithWebSocketPath("/api"))
    .RespondWith(Response.Create()
        .WithWebSocket(ws => ws
            .WithJsonMessage(new 
            { 
                user = "{{request.headers.X-User}}"
            })
            .WithTransformer()
        )
    );

Subprotocol Negotiation

// Version-specific behavior
server.Given(Request.Create()
    .WithWebSocketPath("/api")
    .WithWebSocketSubprotocol("v2"))
    .RespondWith(Response.Create()
        .WithWebSocketSubprotocol("v2")
        .WithWebSocket(ws => ws
            .WithMessage("v2 protocol")
        )
    );

With State Management

// Scenario-aware behavior
server.Given(Request.Create().WithWebSocketPath("/chat"))
    .InScenario("ChatSession")
    .WhenStateIs("LoggedIn")
    .WillSetStateTo("ChatActive")
    .RespondWith(Response.Create()
        .WithWebSocket(ws => ws
            .WithJsonMessage(new { status = "logged-in" })
        )
    );

Design Principles

  1. Fluent First: All builder methods return builder for chaining
  2. Composable: Messages, transformers, callbacks combine naturally
  3. Consistent: Follows HTTP mocking patterns and conventions
  4. Extensible: Partial classes allow feature additions without refactoring
  5. Testable: Deterministic, controllable message delivery

Integration Points

With Existing Features

// Scenario management
server.Given(Request.Create().WithWebSocketPath("/ws"))
    .InScenario("Session")
    .WhenStateIs("Connected")
    .WillSetStateTo("Active")
    .RespondWith(...)

// Priority ordering
server.Given(Request.Create().WithWebSocketPath("/ws"))
    .AtPriority(1)
    .RespondWith(...)

// Webhooks
server.Given(Request.Create().WithWebSocketPath("/ws"))
    .WithWebhook(new Webhook { ... })
    .RespondWith(...)

// Admin interface
var mappings = server.MappingModels
    .Where(m => m.Response.WebSocket != null)
    .ToList()

Testing Patterns

Unit Test Template

[Fact]
public void WebSocket_WithMultipleMessages_MaintainsOrder()
{
    // Arrange
    var builder = new WebSocketResponseBuilder();

    // Act
    var response = builder
        .WithMessage("First", 0)
        .WithMessage("Second", 100)
        .Build();

    // Assert
    Assert.Equal("First", response.Messages[0].BodyAsString);
    Assert.Equal("Second", response.Messages[1].BodyAsString);
    Assert.Equal(100, response.Messages[1].DelayMs);
}

Integration Test Template

[Fact]
public async Task WebSocket_Client_ReceivesMessages()
{
    // Arrange
    var server = WireMockServer.Start();
    server.Given(Request.Create().WithWebSocketPath("/test"))
        .RespondWith(Response.Create()
            .WithWebSocket(ws => ws
                .WithMessage("Hello")
                .WithMessage("World")
            )
        );

    // Act
    using var client = new ClientWebSocket();
    await client.ConnectAsync(new Uri($"ws://localhost:{server.Port}/test"), CancellationToken.None);
    
    // Receive first message
    var buffer = new byte[1024];
    var result = await client.ReceiveAsync(buffer, CancellationToken.None);
    var message1 = Encoding.UTF8.GetString(buffer, 0, result.Count);

    // Assert
    Assert.Equal("Hello", message1);
}

Performance Considerations

Aspect Impact Mitigation
Message queuing Linear with message count Use callbacks for large streams
Memory One message per connection Implement cleanup on close
Concurrency Handle multiple connections Use async callbacks
Delays Thread pool usage Use reasonable delays (< 60s)

Common Issues & Solutions

Issue 1: Message ordering

Problem: Messages delivered out of order
Solution: Use explicit delayMs, avoid concurrent message generation

Issue 2: Connection timeout

Problem: Client disconnects before messages sent
Solution: Reduce message delays, increase test timeout

Issue 3: Memory leak

Problem: Connections not closing properly
Solution: Always call WithClose() or WithAutoClose()

Issue 4: Transformer not working

Problem: Template variables not substituted
Solution: Ensure WithTransformer() is called, check variable syntax


Request Builder

  • Request.Create() - Start building
  • WithPath(string) - HTTP path or WebSocket path
  • WithHeader(string, string) - Custom headers
  • UsingGet(), UsingPost(), etc. - HTTP methods
  • WithWebSocketUpgrade() - Mark as WebSocket
  • WithWebSocketPath(string) - Convenience method
  • WithWebSocketSubprotocol(string) - Protocol version

Response Builder

  • Response.Create() - Start building
  • WithStatusCode(int) - HTTP status
  • WithBody(string) - HTTP body
  • WithBodyAsJson(object) - JSON response
  • WithCallback(...) - Dynamic HTTP response
  • WithWebSocket(...) - WebSocket configuration
  • WithWebSocketMessage(string) - Single message
  • WithWebSocketCallback(...) - Dynamic WebSocket messages
  • WithWebSocketTransformer() - Template support
  • WithWebSocketClose(int, string) - Graceful close

Mapping Builder

  • Given(IRequestMatcher) - Start mapping
  • AtPriority(int) - Execution priority
  • InScenario(string) - Scenario grouping
  • WhenStateIs(string) - State condition
  • WillSetStateTo(string) - State change
  • WithTitle(string) - Display name
  • WithDescription(string) - Documentation
  • WithWebhook(...) - Side effects
  • RespondWith(IResponseProvider) - Terminal method

Versioning Strategy

Version 1.0

  • Basic WebSocket support
  • Static messages
  • Message delays
  • Callback support
  • Transformer integration

Version 1.1

  • Subprotocol negotiation
  • Binary message support
  • Auto-close functionality
  • WebSocket metrics

Version 2.0

  • Streaming responses
  • Backpressure handling
  • Message compression
  • Custom close codes

References


Contact & Support

For questions or contributions regarding WebSocket support:

  1. Review the comprehensive design documents
  2. Check the implementation templates for code examples
  3. Refer to the best practices guide for patterns
  4. File issues with detailed reproduction steps