# WebSocket Implementation Code Templates
This file contains ready-to-use code templates for implementing WebSocket support in WireMock.Net.Minimal.
---
## 1. Abstraction Layer (WireMock.Net.Abstractions)
### File: `Admin/Mappings/WebSocketModel.cs`
```csharp
// Copyright © WireMock.Net
namespace WireMock.Admin.Mappings;
///
/// WebSocket message model for admin API serialization
///
public class WebSocketMessageModel
{
///
/// Delay before sending this message (in milliseconds)
///
public int? DelayMs { get; set; }
///
/// Text message payload
///
public string? BodyAsString { get; set; }
///
/// Binary message payload (base64 encoded when serialized)
///
public byte[]? BodyAsBytes { get; set; }
///
/// Indicates if message is text (true) or binary (false)
///
public bool IsText { get; set; } = true;
///
/// Message index (for ordering)
///
public int Index { get; set; }
}
///
/// WebSocket response configuration model
///
public class WebSocketResponseModel
{
///
/// Messages to send after WebSocket connection is established
///
public List Messages { get; set; } = new();
///
/// Whether to apply transformers to message content
///
public bool UseTransformer { get; set; }
///
/// Type of transformer to use (Handlebars, Scriban, etc.)
///
public string? TransformerType { get; set; }
///
/// Close code when connection is terminated (default 1000 = normal closure)
///
public int? CloseCode { get; set; }
///
/// Close reason/message
///
public string? CloseMessage { get; set; }
///
/// Indicates if a callback is used for dynamic message generation
///
public bool HasCallback { get; set; }
///
/// Subprotocol if negotiated
///
public string? Subprotocol { get; set; }
}
```
### File: `BuilderExtensions/WebSocketResponseModelBuilder.cs`
```csharp
// Copyright © WireMock.Net
// ReSharper disable once CheckNamespace
namespace WireMock.Admin.Mappings;
///
/// Fluent builder for WebSocket response models
///
public partial class WebSocketResponseModelBuilder
{
public WebSocketResponseModelBuilder WithMessage(string body, int? delayMs = null)
{
Messages.Add(new WebSocketMessageModel
{
BodyAsString = body,
DelayMs = delayMs,
IsText = true,
Index = Messages.Count
});
return this;
}
public WebSocketResponseModelBuilder WithBinaryMessage(byte[] data, int? delayMs = null)
{
Messages.Add(new WebSocketMessageModel
{
BodyAsBytes = data,
DelayMs = delayMs,
IsText = false,
Index = Messages.Count
});
return this;
}
public WebSocketResponseModelBuilder WithClose(int? closeCode = 1000, string? reason = null)
{
CloseCode = closeCode;
CloseMessage = reason;
return this;
}
public WebSocketResponseModelBuilder WithTransformer(string transformerType)
{
UseTransformer = true;
TransformerType = transformerType;
return this;
}
public List Messages { get; } = new();
public bool UseTransformer { get; set; }
public string? TransformerType { get; set; }
public int? CloseCode { get; set; }
public string? CloseMessage { get; set; }
public WebSocketResponseModel Build()
{
return new WebSocketResponseModel
{
Messages = Messages,
UseTransformer = UseTransformer,
TransformerType = TransformerType,
CloseCode = CloseCode,
CloseMessage = CloseMessage
};
}
}
```
---
## 2. Domain Models (WireMock.Net.Minimal)
### File: `Models/WebSocketMessage.cs`
```csharp
// Copyright © WireMock.Net
namespace WireMock.Models;
///
/// Represents a single WebSocket message to be sent
///
public class WebSocketMessage : IWebSocketMessage
{
///
/// Delay before sending this message (milliseconds)
///
public int DelayMs { get; set; }
///
/// Text message payload
///
public string? BodyAsString { get; set; }
///
/// Binary message payload
///
public byte[]? BodyAsBytes { get; set; }
///
/// True if text message, false if binary
///
public bool IsText { get; set; } = true;
///
/// Optional message ID for tracking
///
public string? Id { get; set; }
///
/// Optional correlation ID from request
///
public string? CorrelationId { get; set; }
}
```
### File: `Models/WebSocketResponse.cs`
```csharp
// Copyright © WireMock.Net
using WireMock.Transformers;
using WireMock.Types;
namespace WireMock.Models;
///
/// Defines the WebSocket response configuration
///
public class WebSocketResponse : IWebSocketResponse
{
///
/// Messages to send after connection established
///
public List Messages { get; set; } = new();
///
/// Whether to apply transformers to messages
///
public bool UseTransformer { get; set; }
///
/// Type of transformer (Handlebars or Scriban)
///
public TransformerType TransformerType { get; set; } = TransformerType.Handlebars;
///
/// WebSocket close code
///
public int? CloseCode { get; set; } = 1000;
///
/// Close reason
///
public string? CloseMessage { get; set; }
///
/// Optional subprotocol negotiation
///
public string? Subprotocol { get; set; }
///
/// Time to wait before closing (if empty close message list, milliseconds)
///
public int? AutoCloseDelayMs { get; set; }
}
```
---
## 3. Request Builder Extension (WireMock.Net.Minimal)
### File: `RequestBuilders/Request.WithWebSocket.cs`
```csharp
// Copyright © WireMock.Net
using Stef.Validation;
using WireMock.Matchers;
using WireMock.Matchers.Request;
namespace WireMock.RequestBuilders;
///
/// WebSocket-specific request matching extensions
///
public partial class Request
{
///
/// Match WebSocket upgrade requests (Connection: Upgrade, Upgrade: websocket headers)
///
/// The request builder for chaining
public IRequestBuilder WithWebSocketUpgrade()
{
Add(new RequestMessageHeaderMatcher("Upgrade", "websocket", matchBehaviour: MatchBehaviour.AcceptOnMatch));
Add(new RequestMessageHeaderMatcher("Connection", new WildcardMatcher("*Upgrade*")));
return this;
}
///
/// Match WebSocket connection by path and automatically add upgrade headers
///
/// WebSocket path (e.g., "/ws", "/api/chat")
/// The request builder for chaining
public IRequestBuilder WithWebSocketPath(string path)
{
Guard.NotNullOrWhiteSpace(path);
return WithPath(path).WithWebSocketUpgrade();
}
///
/// Match specific WebSocket subprotocol in Sec-WebSocket-Protocol header
///
/// Subprotocol name (e.g., "chat", "superchat")
/// The request builder for chaining
public IRequestBuilder WithWebSocketSubprotocol(string subprotocol)
{
Guard.NotNullOrWhiteSpace(subprotocol);
Add(new RequestMessageHeaderMatcher("Sec-WebSocket-Protocol", subprotocol));
return this;
}
///
/// Match WebSocket with specific version (typically 13)
///
/// WebSocket version number
/// The request builder for chaining
public IRequestBuilder WithWebSocketVersion(string version = "13")
{
Guard.NotNullOrWhiteSpace(version);
Add(new RequestMessageHeaderMatcher("Sec-WebSocket-Version", version));
return this;
}
///
/// Match WebSocket with client origin (CORS)
///
/// Origin URL
/// The request builder for chaining
public IRequestBuilder WithWebSocketOrigin(string origin)
{
Guard.NotNullOrWhiteSpace(origin);
Add(new RequestMessageHeaderMatcher("Origin", origin));
return this;
}
}
```
---
## 4. Response Builder Extension (WireMock.Net.Minimal)
### File: `ResponseBuilders/Response.WithWebSocket.cs`
```csharp
// Copyright © WireMock.Net
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using Stef.Validation;
using WireMock.Models;
using WireMock.Transformers;
using WireMock.Types;
namespace WireMock.ResponseBuilders;
///
/// WebSocket response builder extensions
///
public partial class Response
{
///
/// WebSocket response configuration
///
public IWebSocketResponse? WebSocketResponse { get; private set; }
///
/// Indicates if WithWebSocket was used
///
public bool WithWebSocketUsed { get; private set; }
///
/// Callback for dynamic WebSocket message generation
///
[MemberNotNullWhen(true, nameof(WithWebSocketCallbackUsed))]
public Func>>? WebSocketCallbackAsync { get; private set; }
///
/// Indicates if WebSocket callback is used
///
public bool WithWebSocketCallbackUsed => WebSocketCallbackAsync != null;
///
/// Subprotocol to negotiate in WebSocket handshake
///
public string? WebSocketSubprotocol { get; private set; }
///
/// Configure WebSocket response with fluent builder
///
/// Configuration action
/// Response builder for chaining
public IResponseBuilder WithWebSocket(Action configure)
{
Guard.NotNull(configure);
var builder = new WebSocketResponseBuilder();
configure(builder);
WithWebSocketUsed = true;
WebSocketResponse = builder.Build();
return this;
}
///
/// Configure WebSocket response with a single message
///
/// Message text
/// Optional delay before sending (milliseconds)
/// Response builder for chaining
public IResponseBuilder WithWebSocketMessage(string message, int? delayMs = null)
{
Guard.NotNullOrWhiteSpace(message);
return WithWebSocket(b => b.WithMessage(message, delayMs));
}
///
/// Configure WebSocket response with JSON message
///
/// Object to serialize as JSON
/// Optional delay before sending (milliseconds)
/// Response builder for chaining
public IResponseBuilder WithWebSocketJsonMessage(object data, int? delayMs = null)
{
Guard.NotNull(data);
return WithWebSocket(b => b.WithJsonMessage(data, delayMs));
}
///
/// Configure WebSocket response with binary message
///
/// Binary payload
/// Optional delay before sending (milliseconds)
/// Response builder for chaining
public IResponseBuilder WithWebSocketBinaryMessage(byte[] data, int? delayMs = null)
{
Guard.NotNull(data);
return WithWebSocket(b => b.WithBinaryMessage(data, delayMs));
}
///
/// Configure dynamic WebSocket messages via callback
///
/// Async handler to generate messages based on request
/// Response builder for chaining
public IResponseBuilder WithWebSocketCallback(
Func>> handler)
{
Guard.NotNull(handler);
WithWebSocketUsed = true;
WebSocketCallbackAsync = handler;
return this;
}
///
/// Configure dynamic WebSocket messages via synchronous callback
///
/// Handler to generate messages based on request
/// Response builder for chaining
public IResponseBuilder WithWebSocketCallback(
Func> handler)
{
Guard.NotNull(handler);
return WithWebSocketCallback(req =>
Task.FromResult(handler(req))
);
}
///
/// Enable transformer for WebSocket messages
///
/// Whether to apply transformer
/// Transformer type (Handlebars or Scriban)
/// Response builder for chaining
public IResponseBuilder WithWebSocketTransformer(
bool use = true,
TransformerType transformerType = TransformerType.Handlebars)
{
if (WebSocketResponse != null)
{
WebSocketResponse.UseTransformer = use;
WebSocketResponse.TransformerType = transformerType;
}
return this;
}
///
/// Configure WebSocket close frame for graceful disconnect
///
/// WebSocket close code (default 1000 = normal)
/// Optional close reason
/// Response builder for chaining
public IResponseBuilder WithWebSocketClose(int closeCode = 1000, string? reason = null)
{
if (WebSocketResponse != null)
{
WebSocketResponse.CloseCode = closeCode;
WebSocketResponse.CloseMessage = reason;
}
return this;
}
///
/// Configure WebSocket subprotocol for negotiation
///
/// Subprotocol name
/// Response builder for chaining
public IResponseBuilder WithWebSocketSubprotocol(string subprotocol)
{
Guard.NotNullOrWhiteSpace(subprotocol);
WebSocketSubprotocol = subprotocol;
if (WebSocketResponse != null)
{
WebSocketResponse.Subprotocol = subprotocol;
}
return this;
}
///
/// Auto-close WebSocket connection after delay if no messages
///
/// Delay before auto-close (milliseconds)
/// Response builder for chaining
public IResponseBuilder WithWebSocketAutoClose(int delayMs)
{
Guard.GreaterThan(delayMs, 0);
if (WebSocketResponse != null)
{
WebSocketResponse.AutoCloseDelayMs = delayMs;
}
return this;
}
}
```
---
## 5. WebSocket Response Builder (WireMock.Net.Minimal)
### File: `ResponseBuilders/WebSocketResponseBuilder.cs`
```csharp
// Copyright © WireMock.Net
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Stef.Validation;
using WireMock.Models;
using WireMock.Transformers;
using WireMock.Types;
namespace WireMock.ResponseBuilders;
///
/// Fluent builder for WebSocket responses
///
public class WebSocketResponseBuilder : IWebSocketResponseBuilder
{
private readonly List _messages = new();
private bool _useTransformer;
private TransformerType _transformerType = TransformerType.Handlebars;
private int? _closeCode = 1000;
private string? _closeMessage;
private string? _subprotocol;
private int? _autoCloseDelayMs;
///
/// Add a text message
///
public IWebSocketResponseBuilder WithMessage(string message, int? delayMs = null)
{
Guard.NotNullOrWhiteSpace(message);
_messages.Add(new WebSocketMessage
{
BodyAsString = message,
DelayMs = delayMs ?? 0,
IsText = true,
Id = Guid.NewGuid().ToString()
});
return this;
}
///
/// Add binary message
///
public IWebSocketResponseBuilder WithBinaryMessage(byte[] data, int? delayMs = null)
{
Guard.NotNull(data);
_messages.Add(new WebSocketMessage
{
BodyAsBytes = data,
DelayMs = delayMs ?? 0,
IsText = false,
Id = Guid.NewGuid().ToString()
});
return this;
}
///
/// Add JSON message (auto-serialized)
///
public IWebSocketResponseBuilder WithJsonMessage(object data, int? delayMs = null)
{
Guard.NotNull(data);
var json = JsonConvert.SerializeObject(data);
_messages.Add(new WebSocketMessage
{
BodyAsString = json,
DelayMs = delayMs ?? 0,
IsText = true,
Id = Guid.NewGuid().ToString()
});
return this;
}
///
/// Enable message transformation
///
public IWebSocketResponseBuilder WithTransformer(
bool use = true,
TransformerType transformerType = TransformerType.Handlebars)
{
_useTransformer = use;
_transformerType = transformerType;
return this;
}
///
/// Configure connection close
///
public IWebSocketResponseBuilder WithClose(int closeCode = 1000, string? reason = null)
{
_closeCode = closeCode;
_closeMessage = reason;
return this;
}
///
/// Set subprotocol for negotiation
///
public IWebSocketResponseBuilder WithSubprotocol(string subprotocol)
{
Guard.NotNullOrWhiteSpace(subprotocol);
_subprotocol = subprotocol;
return this;
}
///
/// Auto-close after delay if no more messages
///
public IWebSocketResponseBuilder WithAutoClose(int delayMs)
{
Guard.GreaterThan(delayMs, 0);
_autoCloseDelayMs = delayMs;
return this;
}
///
/// Build the WebSocket response
///
public IWebSocketResponse Build()
{
return new WebSocketResponse
{
Messages = _messages,
UseTransformer = _useTransformer,
TransformerType = _transformerType,
CloseCode = _closeCode,
CloseMessage = _closeMessage,
Subprotocol = _subprotocol,
AutoCloseDelayMs = _autoCloseDelayMs
};
}
}
```
---
## 6. Interfaces (WireMock.Net.Abstractions)
### File: `Models/IWebSocketMessage.cs`
```csharp
// Copyright © WireMock.Net
namespace WireMock.Models;
///
/// Represents a WebSocket message to be sent
///
public interface IWebSocketMessage
{
int DelayMs { get; set; }
string? BodyAsString { get; set; }
byte[]? BodyAsBytes { get; set; }
bool IsText { get; set; }
string? Id { get; set; }
string? CorrelationId { get; set; }
}
```
### File: `Models/IWebSocketResponse.cs`
```csharp
// Copyright © WireMock.Net
using WireMock.Transformers;
using WireMock.Types;
namespace WireMock.Models;
///
/// Defines WebSocket response configuration
///
public interface IWebSocketResponse
{
List Messages { get; set; }
bool UseTransformer { get; set; }
TransformerType TransformerType { get; set; }
int? CloseCode { get; set; }
string? CloseMessage { get; set; }
string? Subprotocol { get; set; }
int? AutoCloseDelayMs { get; set; }
}
```
### File: `BuilderExtensions/WebSocketResponseBuilder.cs`
```csharp
// Copyright © WireMock.Net
using System;
using System.Collections.Generic;
using WireMock.Models;
using WireMock.Transformers;
using WireMock.Types;
// ReSharper disable once CheckNamespace
namespace WireMock.ResponseBuilders;
///
/// Interface for WebSocket response builder
///
public interface IWebSocketResponseBuilder
{
IWebSocketResponseBuilder WithMessage(string message, int? delayMs = null);
IWebSocketResponseBuilder WithBinaryMessage(byte[] data, int? delayMs = null);
IWebSocketResponseBuilder WithJsonMessage(object data, int? delayMs = null);
IWebSocketResponseBuilder WithTransformer(bool use = true, TransformerType transformerType = TransformerType.Handlebars);
IWebSocketResponseBuilder WithClose(int closeCode = 1000, string? reason = null);
IWebSocketResponseBuilder WithSubprotocol(string subprotocol);
IWebSocketResponseBuilder WithAutoClose(int delayMs);
IWebSocketResponse Build();
}
```
---
## 7. Integration Points
### Update to `Response.cs` base class
```csharp
// In Response.cs, add to IResponseBuilder interface implementation:
public interface IResponseBuilder
{
// ... existing members ...
IResponseBuilder WithWebSocket(Action configure);
IResponseBuilder WithWebSocketMessage(string message, int? delayMs = null);
IResponseBuilder WithWebSocketJsonMessage(object data, int? delayMs = null);
IResponseBuilder WithWebSocketBinaryMessage(byte[] data, int? delayMs = null);
IResponseBuilder WithWebSocketCallback(Func>> handler);
IResponseBuilder WithWebSocketCallback(Func> handler);
IResponseBuilder WithWebSocketTransformer(bool use = true, TransformerType transformerType = TransformerType.Handlebars);
IResponseBuilder WithWebSocketClose(int closeCode = 1000, string? reason = null);
IResponseBuilder WithWebSocketSubprotocol(string subprotocol);
IResponseBuilder WithWebSocketAutoClose(int delayMs);
}
```
### Update to `Request.cs` base class
```csharp
// In Request.cs, add to IRequestBuilder interface implementation:
public interface IRequestBuilder
{
// ... existing members ...
IRequestBuilder WithWebSocketUpgrade();
IRequestBuilder WithWebSocketPath(string path);
IRequestBuilder WithWebSocketSubprotocol(string subprotocol);
IRequestBuilder WithWebSocketVersion(string version = "13");
IRequestBuilder WithWebSocketOrigin(string origin);
}
```
---
## 8. Unit Test Templates
### File: `Tests/ResponseBuilders/WebSocketResponseBuilderTests.cs`
```csharp
using Xunit;
using WireMock.ResponseBuilders;
namespace WireMock.Net.Tests.ResponseBuilders;
public class WebSocketResponseBuilderTests
{
[Fact]
public void Build_WithSingleMessage_ReturnsValidResponse()
{
// Arrange
var builder = new WebSocketResponseBuilder();
// Act
var response = builder
.WithMessage("Hello World")
.Build();
// Assert
Assert.NotNull(response);
Assert.Single(response.Messages);
Assert.Equal("Hello World", response.Messages[0].BodyAsString);
}
[Fact]
public void Build_WithMultipleMessages_MaintainsOrder()
{
// Arrange
var builder = new WebSocketResponseBuilder();
// Act
var response = builder
.WithMessage("First", 0)
.WithMessage("Second", 100)
.WithMessage("Third", 200)
.Build();
// Assert
Assert.Equal(3, response.Messages.Count);
Assert.Equal("First", response.Messages[0].BodyAsString);
Assert.Equal("Second", response.Messages[1].BodyAsString);
Assert.Equal("Third", response.Messages[2].BodyAsString);
}
[Fact]
public void Build_WithJsonMessage_SerializesObject()
{
// Arrange
var builder = new WebSocketResponseBuilder();
var testData = new { id = 1, name = "test" };
// Act
var response = builder
.WithJsonMessage(testData)
.Build();
// Assert
Assert.Single(response.Messages);
Assert.Contains("\"id\"", response.Messages[0].BodyAsString);
}
[Fact]
public void Build_WithTransformer_SetsTransformerFlag()
{
// Arrange & Act
var response = new WebSocketResponseBuilder()
.WithMessage("{{request.path}}")
.WithTransformer()
.Build();
// Assert
Assert.True(response.UseTransformer);
}
[Fact]
public void Build_WithClose_SetsCloseCode()
{
// Arrange & Act
var response = new WebSocketResponseBuilder()
.WithMessage("Closing")
.WithClose(1001, "Going away")
.Build();
// Assert
Assert.Equal(1001, response.CloseCode);
Assert.Equal("Going away", response.CloseMessage);
}
}
```
---
## Quick Start Template
```csharp
// Basic echo server
server.Given(Request.Create().WithWebSocketPath("/echo"))
.RespondWith(Response.Create()
.WithWebSocket(ws => ws
.WithMessage("Echo server ready")
)
.WithWebSocketCallback(async request =>
{
return new[]
{
new WebSocketMessage
{
BodyAsString = $"Echo: {request.Body}"
}
};
})
);
// Real-time notifications
server.Given(Request.Create()
.WithWebSocketPath("/notifications")
.WithWebSocketSubprotocol("notifications"))
.RespondWith(Response.Create()
.WithWebSocketSubprotocol("notifications")
.WithWebSocket(ws => ws
.WithJsonMessage(new { type = "connected" }, delayMs: 0)
.WithJsonMessage(new { type = "notification", message = "New message" }, delayMs: 2000)
.WithTransformer()
.WithClose(1000, "Session ended")
)
);
// Data streaming
server.Given(Request.Create().WithWebSocketPath("/stream"))
.RespondWith(Response.Create()
.WithWebSocketCallback(async request =>
{
var messages = new List();
for (int i = 0; i < 5; i++)
{
messages.Add(new WebSocketMessage
{
BodyAsString = $"{{\"index\":{i},\"timestamp\":\"{{now}}\"}}",
DelayMs = i * 1000,
IsText = true
});
}
return messages;
})
.WithWebSocketTransformer()
);
```
---
This implementation guide provides all the necessary templates to implement WebSocket support following WireMock.Net's established fluent interface patterns.