mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-03-29 05:42:14 +02:00
544 lines
25 KiB
Markdown
544 lines
25 KiB
Markdown
# WebSocket Implementation - Visual Architecture Overview
|
|
|
|
## System Architecture Diagram
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ WireMock.Net Solution │
|
|
├─────────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────────────────┐ │
|
|
│ │ Abstraction Layer (WireMock.Net.Abstractions) │ │
|
|
│ ├──────────────────────────────────────────────────────────┤ │
|
|
│ │ • IRequestBuilder │ │
|
|
│ │ ├─ WithPath(), WithHeader(), UsingGet(), ... │ │
|
|
│ │ └─ WithWebSocketPath() [NEW] │ │
|
|
│ │ └─ WithWebSocketSubprotocol() [NEW] │ │
|
|
│ │ │ │
|
|
│ │ • IResponseBuilder │ │
|
|
│ │ ├─ WithStatusCode(), WithBody(), WithCallback() │ │
|
|
│ │ └─ WithWebSocket() [NEW] │ │
|
|
│ │ └─ WithWebSocketCallback() [NEW] │ │
|
|
│ │ │ │
|
|
│ │ • IWebSocketResponseBuilder [NEW] │ │
|
|
│ │ ├─ WithMessage(string) │ │
|
|
│ │ ├─ WithJsonMessage(object) │ │
|
|
│ │ ├─ WithBinaryMessage(byte[]) │ │
|
|
│ │ └─ WithTransformer() │ │
|
|
│ └──────────────────────────────────────────────────────────┘ │
|
|
│ ▲ │
|
|
│ │ implements │
|
|
│ │ │
|
|
│ ┌──────────────────────────────────────────────────────────┐ │
|
|
│ │ Implementation Layer (WireMock.Net.Minimal) │ │
|
|
│ ├──────────────────────────────────────────────────────────┤ │
|
|
│ │ │ │
|
|
│ │ Request Building │ │
|
|
│ │ ├─ Request.cs (core builder) │ │
|
|
│ │ ├─ Request.With*.cs (HTTP extensions) │ │
|
|
│ │ └─ Request.WithWebSocket.cs [NEW] │ │
|
|
│ │ │ │
|
|
│ │ Response Building │ │
|
|
│ │ ├─ Response.cs (core builder) │ │
|
|
│ │ ├─ Response.With*.cs (HTTP extensions) │ │
|
|
│ │ ├─ Response.WithWebSocket.cs [NEW] │ │
|
|
│ │ └─ WebSocketResponseBuilder.cs [NEW] │ │
|
|
│ │ │ │
|
|
│ │ Domain Models │ │
|
|
│ │ ├─ RequestMessage, ResponseMessage (existing) │ │
|
|
│ │ ├─ WebSocketMessage [NEW] │ │
|
|
│ │ └─ WebSocketResponse [NEW] │ │
|
|
│ │ │ │
|
|
│ │ Mapping Management │ │
|
|
│ │ ├─ MappingBuilder.cs │ │
|
|
│ │ ├─ RespondWithAProvider.cs │ │
|
|
│ │ └─ Mapping.cs │ │
|
|
│ │ │ │
|
|
│ │ Server Integration [NEW] │ │
|
|
│ │ ├─ WireMockServer.cs (update for WebSocket) │ │
|
|
│ │ ├─ WireMockMiddleware.cs (upgrade handler) │ │
|
|
│ │ ├─ MappingMatcher.cs (WebSocket routing) │ │
|
|
│ │ └─ WebSocketConnectionManager [NEW] │ │
|
|
│ └──────────────────────────────────────────────────────────┘ │
|
|
│ ▲ │
|
|
│ │ │
|
|
│ ┌──────────────────────────────────────────────────────────┐ │
|
|
│ │ Integration Layers │ │
|
|
│ ├──────────────────────────────────────────────────────────┤ │
|
|
│ │ • WireMock.Net (extends Minimal) │ │
|
|
│ │ • WireMock.Net.StandAlone (OWIN hosting) │ │
|
|
│ │ • WireMock.Net.AspNetCore.Middleware (ASP.NET Core) │ │
|
|
│ └──────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Request Handling Flow (HTTP vs WebSocket)
|
|
|
|
### HTTP Request Flow
|
|
|
|
```
|
|
Client Server
|
|
│ │
|
|
├──── HTTP Request ────>│
|
|
│ │ Request.Create()
|
|
│ │ .WithPath("/api")
|
|
│ │ .UsingPost()
|
|
│ │ Match request matchers
|
|
│ │
|
|
│<─── HTTP Response ────┤ Response.Create()
|
|
│ │ .WithStatusCode(200)
|
|
│ │ .WithBody(...)
|
|
│ │
|
|
└───── Connection closed
|
|
```
|
|
|
|
### WebSocket Request Flow
|
|
|
|
```
|
|
Client Server
|
|
│ │
|
|
├─ WebSocket Upgrade ──>│
|
|
│ (HTTP with headers) │ Request.Create()
|
|
│ │ .WithWebSocketPath("/ws")
|
|
│ │ Match request matchers
|
|
│ │
|
|
│<─ 101 Switching ──────┤ Upgrade to WebSocket
|
|
│ Protocols │
|
|
│ │
|
|
│ ◄─────── Message 1 ───│ WebSocketResponse
|
|
│ │ .Messages[0]
|
|
│ ◄─────── Message 2 ───│ .Messages[1]
|
|
│ │ (delayed 500ms)
|
|
│ ◄─────── Message 3 ───│ .Messages[2]
|
|
│ │ (delayed 1000ms)
|
|
│ │
|
|
│ ◄─── Close Frame ─────│ .WithClose(1000)
|
|
│ │
|
|
└───── Connection closed
|
|
```
|
|
|
|
---
|
|
|
|
## Data Model Diagram
|
|
|
|
```
|
|
HTTP Request/Response Models WebSocket Models
|
|
═══════════════════════════════════ ═══════════════════════════
|
|
|
|
RequestMessage IWebSocketMessage
|
|
├─ Path ├─ DelayMs
|
|
├─ Method (GET, POST, etc.) ├─ BodyAsString
|
|
├─ Headers ├─ BodyAsBytes
|
|
├─ Body ├─ IsText
|
|
├─ Query Params ├─ Id
|
|
└─ Cookies └─ CorrelationId
|
|
|
|
ResponseMessage IWebSocketResponse
|
|
├─ StatusCode ├─ Messages[]
|
|
├─ Headers │ └─ IWebSocketMessage
|
|
├─ Body ├─ UseTransformer
|
|
├─ BodyAsJson ├─ TransformerType
|
|
└─ ContentType ├─ CloseCode
|
|
├─ CloseMessage
|
|
├─ Subprotocol
|
|
└─ AutoCloseDelayMs
|
|
|
|
WebSocketResponse
|
|
(implements IWebSocketResponse)
|
|
```
|
|
|
|
---
|
|
|
|
## Builder Pattern Hierarchy
|
|
|
|
```
|
|
IRequestBuilder (interface)
|
|
▲
|
|
│
|
|
└── Request (class)
|
|
│
|
|
├── Request.cs (core)
|
|
├── Request.WithPath.cs
|
|
├── Request.WithHeaders.cs
|
|
├── Request.UsingMethods.cs
|
|
├── Request.WithBody.cs
|
|
├── Request.WithParam.cs
|
|
└── Request.WithWebSocket.cs [NEW]
|
|
├── WithWebSocketUpgrade()
|
|
├── WithWebSocketPath()
|
|
├── WithWebSocketSubprotocol()
|
|
├── WithWebSocketVersion()
|
|
└── WithWebSocketOrigin()
|
|
|
|
|
|
IResponseBuilder (interface)
|
|
▲
|
|
│
|
|
└── Response (class)
|
|
│
|
|
├── Response.cs (core)
|
|
├── Response.WithStatusCode.cs
|
|
├── Response.WithHeaders.cs
|
|
├── Response.WithBody.cs
|
|
├── Response.WithCallback.cs
|
|
├── Response.WithTransformer.cs
|
|
├── Response.WithProxy.cs
|
|
├── Response.WithFault.cs
|
|
└── Response.WithWebSocket.cs [NEW]
|
|
├── WithWebSocket(builder)
|
|
├── WithWebSocketMessage()
|
|
├── WithWebSocketJsonMessage()
|
|
├── WithWebSocketBinaryMessage()
|
|
├── WithWebSocketCallback()
|
|
├── WithWebSocketTransformer()
|
|
├── WithWebSocketClose()
|
|
├── WithWebSocketSubprotocol()
|
|
└── WithWebSocketAutoClose()
|
|
|
|
|
|
IWebSocketResponseBuilder (interface) [NEW]
|
|
▲
|
|
│
|
|
└── WebSocketResponseBuilder (class) [NEW]
|
|
├── WithMessage()
|
|
├── WithJsonMessage()
|
|
├── WithBinaryMessage()
|
|
├── WithTransformer()
|
|
├── WithClose()
|
|
├── WithSubprotocol()
|
|
├── WithAutoClose()
|
|
└── Build() → IWebSocketResponse
|
|
```
|
|
|
|
---
|
|
|
|
## Mapping Configuration Chain
|
|
|
|
```
|
|
server.Given(request)
|
|
↓
|
|
IRespondWithAProvider
|
|
├── AtPriority(int) ✓ HTTP & WebSocket
|
|
├── WithTitle(string) ✓ HTTP & WebSocket
|
|
├── WithDescription(string) ✓ HTTP & WebSocket
|
|
├── WithPath(string) ✓ HTTP & WebSocket
|
|
├── InScenario(string) ✓ HTTP & WebSocket
|
|
├── WhenStateIs(string) ✓ HTTP & WebSocket
|
|
├── WillSetStateTo(string) ✓ HTTP & WebSocket
|
|
├── WithWebhook(...) ✓ HTTP & WebSocket
|
|
├── WithTimeSettings(...) ✓ HTTP & WebSocket
|
|
├── WithGuid(Guid) ✓ HTTP & WebSocket
|
|
├── WithData(object) ✓ HTTP & WebSocket
|
|
└── RespondWith(provider)
|
|
↓
|
|
IResponseProvider
|
|
├── Response (HTTP)
|
|
│ ├── WithStatusCode()
|
|
│ ├── WithBody()
|
|
│ ├── WithCallback()
|
|
│ └── ...
|
|
└── Response (WebSocket) [NEW]
|
|
├── WithWebSocket()
|
|
├── WithWebSocketCallback()
|
|
└── ...
|
|
```
|
|
|
|
---
|
|
|
|
## Fluent API Method Chains
|
|
|
|
### Simple Echo Server
|
|
|
|
```
|
|
Request.Create()
|
|
.WithWebSocketPath("/echo")
|
|
↓
|
|
Response.Create()
|
|
.WithWebSocketCallback(async request =>
|
|
new[] { new WebSocketMessage { BodyAsString = request.Body } }
|
|
)
|
|
```
|
|
|
|
### Stream with Multiple Messages
|
|
|
|
```
|
|
Request.Create()
|
|
.WithWebSocketPath("/stream")
|
|
↓
|
|
Response.Create()
|
|
.WithWebSocket(ws => ws
|
|
.WithMessage("Start", 0)
|
|
.WithMessage("Middle", 500)
|
|
.WithMessage("End", 1000)
|
|
.WithClose(1000, "Complete")
|
|
)
|
|
```
|
|
|
|
### Dynamic with Templates
|
|
|
|
```
|
|
Request.Create()
|
|
.WithWebSocketPath("/api")
|
|
.WithWebSocketSubprotocol("v2")
|
|
↓
|
|
Response.Create()
|
|
.WithWebSocketSubprotocol("v2")
|
|
.WithWebSocket(ws => ws
|
|
.WithJsonMessage(new {
|
|
user = "{{request.headers.X-User}}"
|
|
})
|
|
.WithTransformer()
|
|
)
|
|
```
|
|
|
|
---
|
|
|
|
## Transformer Integration
|
|
|
|
```
|
|
WebSocket Response
|
|
├─ Raw Content
|
|
│ └─ "Hello {{user}}, timestamp: {{now}}"
|
|
│
|
|
└─ WithTransformer() [Enable Handlebars/Scriban]
|
|
↓
|
|
Transformer Engine (existing)
|
|
├─ Request context injection
|
|
├─ Helper methods (Math, String, etc.)
|
|
└─ Custom helpers
|
|
↓
|
|
Transformed Content
|
|
└─ "Hello Alice, timestamp: 2024-01-15T10:30:00Z"
|
|
```
|
|
|
|
---
|
|
|
|
## Message Delivery Timeline
|
|
|
|
```
|
|
Client connects → WebSocket Upgrade
|
|
↓
|
|
Message Queue Created
|
|
↓
|
|
┌──────────────────────────────────────────────────────────┐
|
|
│ Message 1 (delayMs: 0) │
|
|
│ ═════════════════════════════════════════════════════► │
|
|
│ Sent immediately │
|
|
└──────────────────────────────────────────────────────────┘
|
|
↓ (wait 500ms)
|
|
┌──────────────────────────────────────────────────────────┐
|
|
│ Message 2 (delayMs: 500) │
|
|
│ ═════════════════════════════════════► │
|
|
│ Sent at T+500ms │
|
|
└──────────────────────────────────────────────────────────┘
|
|
↓ (wait 1000ms from start)
|
|
┌──────────────────────────────────────────────────────────┐
|
|
│ Message 3 (delayMs: 1000) │
|
|
│ ═════════════════════════════► │
|
|
│ Sent at T+1000ms │
|
|
└──────────────────────────────────────────────────────────┘
|
|
↓ (Close connection)
|
|
Close Frame (1000, "Complete")
|
|
↓
|
|
Connection Closed
|
|
```
|
|
|
|
---
|
|
|
|
## File Organization
|
|
|
|
```
|
|
src/WireMock.Net.Abstractions/
|
|
│
|
|
├── Models/
|
|
│ ├── IWebSocketMessage.cs [NEW]
|
|
│ ├── IWebSocketResponse.cs [NEW]
|
|
│ └── ...existing models
|
|
│
|
|
├── Admin/Mappings/
|
|
│ ├── WebSocketModel.cs [NEW]
|
|
│ ├── ResponseModel.cs (extend for WebSocket)
|
|
│ ├── RequestModel.cs (extend for WebSocket)
|
|
│ └── ...existing models
|
|
│
|
|
├── BuilderExtensions/
|
|
│ ├── IWebSocketResponseBuilder.cs [NEW]
|
|
│ ├── WebSocketResponseModelBuilder.cs [NEW]
|
|
│ └── ...existing builders
|
|
│
|
|
└── ...rest of abstractions
|
|
|
|
|
|
src/WireMock.Net.Minimal/
|
|
│
|
|
├── Models/
|
|
│ ├── WebSocketMessage.cs [NEW]
|
|
│ ├── WebSocketResponse.cs [NEW]
|
|
│ └── ...existing models
|
|
│
|
|
├── RequestBuilders/
|
|
│ ├── Request.cs (update interfaces)
|
|
│ ├── Request.WithWebSocket.cs [NEW]
|
|
│ ├── Request.WithPath.cs
|
|
│ ├── Request.WithHeaders.cs
|
|
│ └── ...existing builders
|
|
│
|
|
├── ResponseBuilders/
|
|
│ ├── Response.cs (update interfaces)
|
|
│ ├── Response.WithWebSocket.cs [NEW]
|
|
│ ├── WebSocketResponseBuilder.cs [NEW]
|
|
│ ├── Response.WithStatusCode.cs
|
|
│ ├── Response.WithBody.cs
|
|
│ └── ...existing builders
|
|
│
|
|
├── Server/
|
|
│ ├── WireMockServer.cs (update for WebSocket)
|
|
│ ├── WireMockServer.Fluent.cs
|
|
│ ├── MappingBuilder.cs
|
|
│ ├── RespondWithAProvider.cs
|
|
│ └── ...existing server code
|
|
│
|
|
├── Owin/
|
|
│ ├── WireMockMiddleware.cs (add WebSocket upgrade)
|
|
│ ├── MappingMatcher.cs (add WebSocket routing)
|
|
│ └── ...existing OWIN code
|
|
│
|
|
└── ...rest of implementation
|
|
```
|
|
|
|
---
|
|
|
|
## Dependency Graph
|
|
|
|
```
|
|
┌─────────────────────────────────────────┐
|
|
│ External Dependencies │
|
|
├─────────────────────────────────────────┤
|
|
│ • .NET Standard 2.0 / .NET Framework │
|
|
│ • ASP.NET Core (WebSocket support) │
|
|
│ • Newtonsoft.Json (serialization) │
|
|
│ • Handlebars.Core (transformers) │
|
|
│ • Scriban (transformers) │
|
|
└──────────────────┬──────────────────────┘
|
|
▲
|
|
│
|
|
┌──────────────────┴──────────────────────┐
|
|
│ WireMock.Net.Abstractions │
|
|
├──────────────────────────────────────────┤
|
|
│ • Interfaces (IRequestBuilder, etc.) │
|
|
│ • Models (RequestModel, ResponseModel) │
|
|
│ • WebSocket abstractions [NEW] │
|
|
└──────────────────┬──────────────────────┘
|
|
▲
|
|
│
|
|
┌──────────────────┴──────────────────────┐
|
|
│ WireMock.Net.Minimal │
|
|
├──────────────────────────────────────────┤
|
|
│ • Request builders │
|
|
│ • Response builders │
|
|
│ • WebSocket builders [NEW] │
|
|
│ • Server core │
|
|
│ • OWIN middleware │
|
|
└──────────────────┬──────────────────────┘
|
|
▲
|
|
│
|
|
┌──────────────────┴──────────────────────┐
|
|
│ WireMock.Net (Full) │
|
|
│ WireMock.Net.StandAlone (OWIN) │
|
|
│ Application Code │
|
|
└──────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Test Coverage Areas
|
|
|
|
```
|
|
Unit Tests (Request/Response Builders)
|
|
├── WebSocketResponseBuilder tests
|
|
│ ├── Message ordering
|
|
│ ├── Delay handling
|
|
│ ├── Transformer support
|
|
│ └── Close frame handling
|
|
├── Request builder tests
|
|
│ ├── WebSocket path matching
|
|
│ ├── Subprotocol matching
|
|
│ └── Upgrade header validation
|
|
└── Integration tests
|
|
├── Client connection handling
|
|
├── Message delivery
|
|
├── Scenario state management
|
|
├── Concurrent connections
|
|
├── Connection timeout
|
|
└── Error scenarios
|
|
```
|
|
|
|
---
|
|
|
|
## Phase Implementation Timeline
|
|
|
|
```
|
|
Week 1: Phase 1-2 (Abstractions & Models)
|
|
├─ Mon-Tue: Abstractions (IWebSocketMessage, etc.)
|
|
├─ Wed: Domain Models (WebSocketMessage, etc.)
|
|
└─ Thu: Code review & refinement
|
|
|
|
Week 2: Phase 3 (Request Builder)
|
|
├─ Mon-Tue: Request.WithWebSocket.cs
|
|
├─ Wed: Request matching tests
|
|
└─ Thu: Integration with server
|
|
|
|
Week 3: Phase 4 (Response Builder)
|
|
├─ Mon-Wed: Response.WithWebSocket.cs
|
|
├─ Wed-Thu: WebSocketResponseBuilder
|
|
└─ Fri: Message delivery tests
|
|
|
|
Week 4: Phase 5 (Server Integration)
|
|
├─ Mon-Tue: WireMockMiddleware updates
|
|
├─ Wed: Connection lifecycle management
|
|
├─ Thu: Integration tests
|
|
└─ Fri: Documentation & release prep
|
|
```
|
|
|
|
---
|
|
|
|
## Quick Reference: What's New vs What's Extended
|
|
|
|
```
|
|
┌─────────────────────┬─────────────────────┬──────────────────┐
|
|
│ Component │ New [NEW] │ Extended │
|
|
├─────────────────────┼─────────────────────┼──────────────────┤
|
|
│ Request Builder │ Request. │ Request.cs │
|
|
│ │ WithWebSocket.cs │ (interfaces) │
|
|
├─────────────────────┼─────────────────────┼──────────────────┤
|
|
│ Response Builder │ Response. │ Response.cs │
|
|
│ │ WithWebSocket.cs │ (interfaces) │
|
|
│ │ WebSocketResponse │ │
|
|
│ │ Builder.cs │ │
|
|
├─────────────────────┼─────────────────────┼──────────────────┤
|
|
│ Domain Models │ WebSocketMessage.cs │ None │
|
|
│ │ WebSocketResponse. │ │
|
|
│ │ cs │ │
|
|
├─────────────────────┼─────────────────────┼──────────────────┤
|
|
│ Admin API │ WebSocketModel.cs │ ResponseModel.cs │
|
|
│ │ │ RequestModel.cs │
|
|
├─────────────────────┼─────────────────────┼──────────────────┤
|
|
│ Server │ WebSocket │ WireMock │
|
|
│ │ ConnectionManager │ Server.cs │
|
|
│ │ [NEW] │ WireMock │
|
|
│ │ │ Middleware.cs │
|
|
├─────────────────────┼─────────────────────┼──────────────────┤
|
|
│ Interfaces │ IWebSocketMessage │ IRequestBuilder │
|
|
│ │ IWebSocketResponse │ IResponseBuilder │
|
|
│ │ IWebSocketResponse │ │
|
|
│ │ Builder │ │
|
|
└─────────────────────┴─────────────────────┴──────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
This visual guide helps understand the architecture, data flow, and implementation scope of the WebSocket support proposal.
|