mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-04-20 23:41:19 +02:00
...
This commit is contained in:
@@ -20,7 +20,7 @@ public static class Program
|
|||||||
Console.WriteLine("Choose an example to run:");
|
Console.WriteLine("Choose an example to run:");
|
||||||
Console.WriteLine("1. Echo Server");
|
Console.WriteLine("1. Echo Server");
|
||||||
Console.WriteLine("2. Custom Message Handler");
|
Console.WriteLine("2. Custom Message Handler");
|
||||||
Console.WriteLine("3. Broadcast Server");
|
Console.WriteLine("3. ...");
|
||||||
Console.WriteLine("4. Scenario/State Machine");
|
Console.WriteLine("4. Scenario/State Machine");
|
||||||
Console.WriteLine("5. WebSocket Proxy");
|
Console.WriteLine("5. WebSocket Proxy");
|
||||||
Console.WriteLine("6. Multiple WebSocket Endpoints");
|
Console.WriteLine("6. Multiple WebSocket Endpoints");
|
||||||
@@ -419,28 +419,6 @@ public static class Program
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Endpoint 3: JSON service
|
|
||||||
server
|
|
||||||
.Given(Request.Create()
|
|
||||||
.WithPath("/ws/json")
|
|
||||||
.WithWebSocketUpgrade()
|
|
||||||
)
|
|
||||||
.RespondWith(Response.Create()
|
|
||||||
.WithWebSocket(ws => ws
|
|
||||||
.WithMessageHandler(async (msg, ctx) =>
|
|
||||||
{
|
|
||||||
var response = new
|
|
||||||
{
|
|
||||||
timestamp = DateTime.UtcNow,
|
|
||||||
message = msg.Text,
|
|
||||||
length = msg.Text?.Length ?? 0,
|
|
||||||
type = msg.MessageType.ToString()
|
|
||||||
};
|
|
||||||
await ctx.SendAsJsonAsync(response);
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Endpoint 4: Protocol-specific
|
// Endpoint 4: Protocol-specific
|
||||||
server
|
server
|
||||||
.Given(Request.Create()
|
.Given(Request.Create()
|
||||||
@@ -586,27 +564,6 @@ public static class Program
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// JSON endpoint
|
|
||||||
server
|
|
||||||
.Given(Request.Create()
|
|
||||||
.WithPath("/ws/json")
|
|
||||||
.WithWebSocketUpgrade()
|
|
||||||
)
|
|
||||||
.RespondWith(Response.Create()
|
|
||||||
.WithWebSocket(ws => ws
|
|
||||||
.WithMessageHandler(async (msg, ctx) =>
|
|
||||||
{
|
|
||||||
var response = new
|
|
||||||
{
|
|
||||||
timestamp = DateTime.UtcNow,
|
|
||||||
message = msg.Text,
|
|
||||||
connectionId = ctx.ConnectionId
|
|
||||||
};
|
|
||||||
await ctx.SendAsJsonAsync(response);
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SetupGameScenario(WireMockServer server)
|
private static void SetupGameScenario(WireMockServer server)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using System;
|
|
||||||
using WireMock.Settings;
|
using WireMock.Settings;
|
||||||
using WireMock.WebSockets;
|
using WireMock.WebSockets;
|
||||||
|
|
||||||
@@ -18,7 +17,7 @@ public partial class Response
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public IResponseBuilder WithWebSocket(Action<IWebSocketBuilder> configure)
|
public IResponseBuilder WithWebSocket(Action<IWebSocketBuilder> configure)
|
||||||
{
|
{
|
||||||
var builder = new WebSocketBuilder();
|
var builder = new WebSocketBuilder(this);
|
||||||
configure(builder);
|
configure(builder);
|
||||||
|
|
||||||
WebSocketBuilder = builder;
|
WebSocketBuilder = builder;
|
||||||
@@ -39,7 +38,7 @@ public partial class Response
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public IResponseBuilder WithWebSocketProxy(ProxyAndRecordSettings settings)
|
public IResponseBuilder WithWebSocketProxy(ProxyAndRecordSettings settings)
|
||||||
{
|
{
|
||||||
var builder = new WebSocketBuilder();
|
var builder = new WebSocketBuilder(this);
|
||||||
builder.WithProxy(settings);
|
builder.WithProxy(settings);
|
||||||
|
|
||||||
WebSocketBuilder = builder;
|
WebSocketBuilder = builder;
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
using WireMock.Matchers;
|
using WireMock.Matchers;
|
||||||
|
using WireMock.ResponseBuilders;
|
||||||
using WireMock.Settings;
|
using WireMock.Settings;
|
||||||
using WireMock.Transformers;
|
using WireMock.Transformers;
|
||||||
using WireMock.Types;
|
|
||||||
|
|
||||||
namespace WireMock.WebSockets;
|
namespace WireMock.WebSockets;
|
||||||
|
|
||||||
internal class WebSocketBuilder : IWebSocketBuilder
|
internal class WebSocketBuilder(Response response) : IWebSocketBuilder
|
||||||
{
|
{
|
||||||
private readonly List<(IMatcher matcher, List<WebSocketMessageBuilder> messages)> _conditionalMessages = [];
|
private readonly List<(IMatcher matcher, List<WebSocketMessageBuilder> messages)> _conditionalMessages = [];
|
||||||
|
|
||||||
@@ -42,17 +41,6 @@ internal class WebSocketBuilder : IWebSocketBuilder
|
|||||||
public TimeSpan? KeepAliveIntervalSeconds { get; private set; }
|
public TimeSpan? KeepAliveIntervalSeconds { get; private set; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool UseTransformer { get; private set; }
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public TransformerType TransformerType { get; private set; }
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool UseTransformerForBodyAsFile { get; private set; }
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public ReplaceNodeOptions TransformerReplaceNodeOptions { get; private set; }
|
|
||||||
|
|
||||||
public IWebSocketBuilder WithAcceptProtocol(string protocol)
|
public IWebSocketBuilder WithAcceptProtocol(string protocol)
|
||||||
{
|
{
|
||||||
AcceptProtocol = Guard.NotNull(protocol);
|
AcceptProtocol = Guard.NotNull(protocol);
|
||||||
@@ -70,7 +58,7 @@ internal class WebSocketBuilder : IWebSocketBuilder
|
|||||||
Guard.NotNull(configure);
|
Guard.NotNull(configure);
|
||||||
var messageBuilder = new WebSocketMessageBuilder();
|
var messageBuilder = new WebSocketMessageBuilder();
|
||||||
configure(messageBuilder);
|
configure(messageBuilder);
|
||||||
|
|
||||||
return WithMessageHandler(async (message, context) =>
|
return WithMessageHandler(async (message, context) =>
|
||||||
{
|
{
|
||||||
if (messageBuilder.Delay.HasValue)
|
if (messageBuilder.Delay.HasValue)
|
||||||
@@ -78,7 +66,7 @@ internal class WebSocketBuilder : IWebSocketBuilder
|
|||||||
await Task.Delay(messageBuilder.Delay.Value);
|
await Task.Delay(messageBuilder.Delay.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
await SendMessageAsync(this, context, messageBuilder, message);
|
await SendMessageAsync(context, messageBuilder, message);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,7 +85,7 @@ internal class WebSocketBuilder : IWebSocketBuilder
|
|||||||
await Task.Delay(messageBuilder.Delay.Value);
|
await Task.Delay(messageBuilder.Delay.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
await SendMessageAsync(this, context, messageBuilder, message);
|
await SendMessageAsync(context, messageBuilder, message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -166,18 +154,6 @@ internal class WebSocketBuilder : IWebSocketBuilder
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IWebSocketBuilder WithTransformer(
|
|
||||||
TransformerType transformerType = TransformerType.Handlebars,
|
|
||||||
bool useTransformerForBodyAsFile = false,
|
|
||||||
ReplaceNodeOptions transformerReplaceNodeOptions = ReplaceNodeOptions.EvaluateAndTryToConvert)
|
|
||||||
{
|
|
||||||
UseTransformer = true;
|
|
||||||
TransformerType = transformerType;
|
|
||||||
UseTransformerForBodyAsFile = useTransformerForBodyAsFile;
|
|
||||||
TransformerReplaceNodeOptions = transformerReplaceNodeOptions;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal IWebSocketBuilder AddConditionalMessage(IMatcher matcher, WebSocketMessageBuilder messageBuilder)
|
internal IWebSocketBuilder AddConditionalMessage(IMatcher matcher, WebSocketMessageBuilder messageBuilder)
|
||||||
{
|
{
|
||||||
_conditionalMessages.Add((matcher, new List<WebSocketMessageBuilder> { messageBuilder }));
|
_conditionalMessages.Add((matcher, new List<WebSocketMessageBuilder> { messageBuilder }));
|
||||||
@@ -205,7 +181,7 @@ internal class WebSocketBuilder : IWebSocketBuilder
|
|||||||
foreach (var (matcher, messages) in _conditionalMessages)
|
foreach (var (matcher, messages) in _conditionalMessages)
|
||||||
{
|
{
|
||||||
// Try to match the message
|
// Try to match the message
|
||||||
if (await MatchMessageAsync(message, matcher) )
|
if (await MatchMessageAsync(message, matcher))
|
||||||
{
|
{
|
||||||
// Execute the corresponding messages
|
// Execute the corresponding messages
|
||||||
foreach (var messageBuilder in messages)
|
foreach (var messageBuilder in messages)
|
||||||
@@ -215,7 +191,7 @@ internal class WebSocketBuilder : IWebSocketBuilder
|
|||||||
await Task.Delay(messageBuilder.Delay.Value);
|
await Task.Delay(messageBuilder.Delay.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
await SendMessageAsync(this, context, messageBuilder, message);
|
await SendMessageAsync(context, messageBuilder, message);
|
||||||
|
|
||||||
// If this message should close the connection, do it after sending
|
// If this message should close the connection, do it after sending
|
||||||
if (messageBuilder.ShouldClose)
|
if (messageBuilder.ShouldClose)
|
||||||
@@ -237,6 +213,54 @@ internal class WebSocketBuilder : IWebSocketBuilder
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task SendMessageAsync(IWebSocketContext context, WebSocketMessageBuilder messageBuilder, WebSocketMessage incomingMessage)
|
||||||
|
{
|
||||||
|
switch (messageBuilder.Type)
|
||||||
|
{
|
||||||
|
case WebSocketMessageType.Text:
|
||||||
|
var text = messageBuilder.MessageText!;
|
||||||
|
if (response.UseTransformer)
|
||||||
|
{
|
||||||
|
text = ApplyTransformer(context, incomingMessage, text);
|
||||||
|
}
|
||||||
|
await context.SendAsync(text);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WebSocketMessageType.Binary:
|
||||||
|
await context.SendAsync(messageBuilder.MessageBytes!);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ApplyTransformer(IWebSocketContext context, WebSocketMessage incomingMessage, string text)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (incomingMessage == null)
|
||||||
|
{
|
||||||
|
// No incoming message, can't apply transformer
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
var transformer = TransformerFactory.Create(response.TransformerType, context.Mapping.Settings);
|
||||||
|
|
||||||
|
var model = new WebSocketTransformModel
|
||||||
|
{
|
||||||
|
Mapping = context.Mapping,
|
||||||
|
Request = context.RequestMessage,
|
||||||
|
Message = incomingMessage,
|
||||||
|
Data = incomingMessage.MessageType == WebSocketMessageType.Text ? incomingMessage.Text : null
|
||||||
|
};
|
||||||
|
|
||||||
|
return transformer.Transform(text, model);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// If transformation fails, return original text
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static async Task<bool> MatchMessageAsync(WebSocketMessage message, IMatcher matcher)
|
private static async Task<bool> MatchMessageAsync(WebSocketMessage message, IMatcher matcher)
|
||||||
{
|
{
|
||||||
if (message.MessageType == WebSocketMessageType.Text)
|
if (message.MessageType == WebSocketMessageType.Text)
|
||||||
@@ -263,61 +287,4 @@ internal class WebSocketBuilder : IWebSocketBuilder
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task SendMessageAsync(WebSocketBuilder builder, IWebSocketContext context, WebSocketMessageBuilder messageBuilder, WebSocketMessage incomingMessage)
|
|
||||||
{
|
|
||||||
switch (messageBuilder.Type)
|
|
||||||
{
|
|
||||||
case WebSocketMessageBuilder.MessageType.Text:
|
|
||||||
var text = messageBuilder.MessageText!;
|
|
||||||
if (builder.UseTransformer)
|
|
||||||
{
|
|
||||||
text = ApplyTransformer(builder, context, incomingMessage, text);
|
|
||||||
}
|
|
||||||
await context.SendAsync(text);
|
|
||||||
break;
|
|
||||||
case WebSocketMessageBuilder.MessageType.Bytes:
|
|
||||||
await context.SendAsync(messageBuilder.MessageBytes!);
|
|
||||||
break;
|
|
||||||
case WebSocketMessageBuilder.MessageType.Json:
|
|
||||||
var jsonData = messageBuilder.MessageData!;
|
|
||||||
if (builder.UseTransformer)
|
|
||||||
{
|
|
||||||
var jsonString = JsonConvert.SerializeObject(jsonData);
|
|
||||||
jsonString = ApplyTransformer(builder, context, incomingMessage, jsonString);
|
|
||||||
jsonData = JsonConvert.DeserializeObject(jsonString) ?? jsonData;
|
|
||||||
}
|
|
||||||
await context.SendAsJsonAsync(jsonData);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string ApplyTransformer(WebSocketBuilder builder, IWebSocketContext context, WebSocketMessage incomingMessage, string text)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (incomingMessage == null)
|
|
||||||
{
|
|
||||||
// No incoming message, can't apply transformer
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
var transformer = TransformerFactory.Create(builder.TransformerType, context.Mapping.Settings);
|
|
||||||
|
|
||||||
var model = new WebSocketTransformModel
|
|
||||||
{
|
|
||||||
Mapping = context.Mapping,
|
|
||||||
Request = context.RequestMessage,
|
|
||||||
Message = incomingMessage,
|
|
||||||
Data = incomingMessage.MessageType == WebSocketMessageType.Text ? incomingMessage.Text : null
|
|
||||||
};
|
|
||||||
|
|
||||||
return transformer.Transform(text, model);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// If transformation fails, return original text
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,6 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace WireMock.WebSockets;
|
namespace WireMock.WebSockets;
|
||||||
|
|
||||||
@@ -57,13 +56,4 @@ internal class WebSocketConnectionRegistry
|
|||||||
|
|
||||||
await Task.WhenAll(tasks);
|
await Task.WhenAll(tasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Broadcast JSON to all connections
|
|
||||||
/// </summary>
|
|
||||||
public async Task BroadcastJsonAsync(object data, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var json = JsonConvert.SerializeObject(data);
|
|
||||||
await BroadcastTextAsync(json, cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
|
using System.Net.WebSockets;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
|
||||||
namespace WireMock.WebSockets;
|
namespace WireMock.WebSockets;
|
||||||
@@ -14,28 +15,21 @@ internal class WebSocketMessageBuilder : IWebSocketMessageBuilder
|
|||||||
|
|
||||||
public TimeSpan? Delay { get; private set; }
|
public TimeSpan? Delay { get; private set; }
|
||||||
|
|
||||||
public MessageType Type { get; private set; }
|
public WebSocketMessageType Type { get; private set; }
|
||||||
|
|
||||||
public bool ShouldClose { get; private set; }
|
public bool ShouldClose { get; private set; }
|
||||||
|
|
||||||
public IWebSocketMessageBuilder WithText(string text)
|
public IWebSocketMessageBuilder WithText(string text)
|
||||||
{
|
{
|
||||||
MessageText = Guard.NotNull(text);
|
MessageText = Guard.NotNull(text);
|
||||||
Type = MessageType.Text;
|
Type = WebSocketMessageType.Text;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IWebSocketMessageBuilder WithBytes(byte[] bytes)
|
public IWebSocketMessageBuilder WithBinary(byte[] bytes)
|
||||||
{
|
{
|
||||||
MessageBytes = Guard.NotNull(bytes);
|
MessageBytes = Guard.NotNull(bytes);
|
||||||
Type = MessageType.Bytes;
|
Type = WebSocketMessageType.Binary;
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IWebSocketMessageBuilder WithJson(object data)
|
|
||||||
{
|
|
||||||
MessageData = Guard.NotNull(data);
|
|
||||||
Type = MessageType.Json;
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,22 +46,11 @@ internal class WebSocketMessageBuilder : IWebSocketMessageBuilder
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IWebSocketMessageBuilder AndClose()
|
|
||||||
{
|
|
||||||
ShouldClose = true;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IWebSocketMessageBuilder Close()
|
public IWebSocketMessageBuilder Close()
|
||||||
{
|
{
|
||||||
ShouldClose = true;
|
ShouldClose = true;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal enum MessageType
|
public IWebSocketMessageBuilder AndClose() => Close();
|
||||||
{
|
}
|
||||||
Text,
|
|
||||||
Bytes,
|
|
||||||
Json
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,10 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace WireMock.WebSockets;
|
namespace WireMock.WebSockets;
|
||||||
|
|
||||||
internal class WebSocketMessagesBuilder : IWebSocketMessagesBuilder
|
internal class WebSocketMessagesBuilder : IWebSocketMessagesBuilder
|
||||||
{
|
{
|
||||||
internal List<WebSocketMessageBuilder> Messages { get; } = new();
|
internal List<WebSocketMessageBuilder> Messages { get; } = [];
|
||||||
|
|
||||||
public IWebSocketMessagesBuilder AddMessage(Action<IWebSocketMessageBuilder> configure)
|
public IWebSocketMessagesBuilder AddMessage(Action<IWebSocketMessageBuilder> configure)
|
||||||
{
|
{
|
||||||
@@ -15,4 +13,4 @@ internal class WebSocketMessagesBuilder : IWebSocketMessagesBuilder
|
|||||||
Messages.Add(messageBuilder);
|
Messages.Add(messageBuilder);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ using System.Text;
|
|||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
using WireMock.Extensions;
|
||||||
using WireMock.Owin;
|
using WireMock.Owin;
|
||||||
|
|
||||||
namespace WireMock.WebSockets;
|
namespace WireMock.WebSockets;
|
||||||
@@ -54,9 +55,9 @@ public class WireMockWebSocketContext : IWebSocketContext
|
|||||||
Builder = Guard.NotNull(builder);
|
Builder = Guard.NotNull(builder);
|
||||||
|
|
||||||
// Get options from HttpContext
|
// Get options from HttpContext
|
||||||
if (httpContext.Items.TryGetValue(nameof(WireMockMiddlewareOptions), out var options))
|
if (httpContext.Items.TryGetValue<IWireMockMiddlewareOptions>(nameof(WireMockMiddlewareOptions), out var options))
|
||||||
{
|
{
|
||||||
_options = (IWireMockMiddlewareOptions)options!;
|
_options = options;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -87,13 +88,6 @@ public class WireMockWebSocketContext : IWebSocketContext
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task SendAsJsonAsync(object data, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var json = JsonConvert.SerializeObject(data);
|
|
||||||
return SendAsync(json, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Task CloseAsync(WebSocketCloseStatus closeStatus, string statusDescription)
|
public Task CloseAsync(WebSocketCloseStatus closeStatus, string statusDescription)
|
||||||
{
|
{
|
||||||
@@ -181,13 +175,4 @@ public class WireMockWebSocketContext : IWebSocketContext
|
|||||||
await Registry.BroadcastTextAsync(text, cancellationToken);
|
await Registry.BroadcastTextAsync(text, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task BroadcastJsonAsync(object data, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
if (Registry != null)
|
|
||||||
{
|
|
||||||
await Registry.BroadcastJsonAsync(data, cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -28,4 +28,18 @@ internal static class DictionaryExtensions
|
|||||||
value = default;
|
value = default;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public static bool TryGetValue<T>(this IDictionary<object, object?> dictionary, string key, [NotNullWhen(true)] out T? value)
|
||||||
|
{
|
||||||
|
Guard.NotNull(dictionary);
|
||||||
|
|
||||||
|
if (dictionary[key] is T typedValue)
|
||||||
|
{
|
||||||
|
value = typedValue;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -100,13 +100,4 @@ public interface IWebSocketBuilder
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
IWebSocketBuilder WithKeepAliveInterval(TimeSpan interval);
|
IWebSocketBuilder WithKeepAliveInterval(TimeSpan interval);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Enable transformer support (Handlebars/Scriban)
|
|
||||||
/// </summary>
|
|
||||||
[PublicAPI]
|
|
||||||
IWebSocketBuilder WithTransformer(
|
|
||||||
TransformerType transformerType = TransformerType.Handlebars,
|
|
||||||
bool useTransformerForBodyAsFile = false,
|
|
||||||
ReplaceNodeOptions transformerReplaceNodeOptions = ReplaceNodeOptions.EvaluateAndTryToConvert);
|
|
||||||
}
|
}
|
||||||
@@ -45,11 +45,6 @@ public interface IWebSocketContext
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
Task SendAsync(byte[] bytes, CancellationToken cancellationToken = default);
|
Task SendAsync(byte[] bytes, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Send JSON message to the client
|
|
||||||
/// </summary>
|
|
||||||
Task SendAsJsonAsync(object data, CancellationToken cancellationToken = default);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Close the WebSocket connection
|
/// Close the WebSocket connection
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -74,9 +69,4 @@ public interface IWebSocketContext
|
|||||||
/// Broadcast text message to all connections in this mapping
|
/// Broadcast text message to all connections in this mapping
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task BroadcastTextAsync(string text, CancellationToken cancellationToken = default);
|
Task BroadcastTextAsync(string text, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Broadcast JSON message to all connections in this mapping
|
|
||||||
/// </summary>
|
|
||||||
Task BroadcastJsonAsync(object data, CancellationToken cancellationToken = default);
|
|
||||||
}
|
}
|
||||||
@@ -21,14 +21,7 @@ public interface IWebSocketMessageBuilder
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="bytes">The binary data to send</param>
|
/// <param name="bytes">The binary data to send</param>
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
IWebSocketMessageBuilder WithBytes(byte[] bytes);
|
IWebSocketMessageBuilder WithBinary(byte[] bytes);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Send a JSON object
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="data">The object to serialize and send as JSON</param>
|
|
||||||
[PublicAPI]
|
|
||||||
IWebSocketMessageBuilder WithJson(object data);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set a delay before sending the message (using TimeSpan)
|
/// Set a delay before sending the message (using TimeSpan)
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using WireMock.Matchers;
|
using WireMock.Matchers;
|
||||||
using WireMock.Net.Xunit;
|
using WireMock.Net.Xunit;
|
||||||
using WireMock.RequestBuilders;
|
using WireMock.RequestBuilders;
|
||||||
@@ -137,7 +134,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task WithBytes_Should_Send_Configured_Bytes()
|
public async Task WithBinary_Should_Send_Configured_Bytes()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
using var server = WireMockServer.Start(new WireMockServerSettings
|
using var server = WireMockServer.Start(new WireMockServerSettings
|
||||||
@@ -155,7 +152,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
|
|||||||
)
|
)
|
||||||
.RespondWith(Response.Create()
|
.RespondWith(Response.Create()
|
||||||
.WithWebSocket(ws => ws
|
.WithWebSocket(ws => ws
|
||||||
.SendMessage(m => m.WithBytes(responseBytes))
|
.SendMessage(m => m.WithBinary(responseBytes))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -177,7 +174,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task WithBytes_Should_Send_Same_Bytes_For_Multiple_Messages()
|
public async Task WithBinary_Should_Send_Same_Bytes_For_Multiple_Messages()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
using var server = WireMockServer.Start(new WireMockServerSettings
|
using var server = WireMockServer.Start(new WireMockServerSettings
|
||||||
@@ -195,7 +192,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
|
|||||||
)
|
)
|
||||||
.RespondWith(Response.Create()
|
.RespondWith(Response.Create()
|
||||||
.WithWebSocket(ws => ws
|
.WithWebSocket(ws => ws
|
||||||
.SendMessage(m => m.WithBytes(responseBytes))
|
.SendMessage(m => m.WithBinary(responseBytes))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -216,103 +213,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
|
|||||||
|
|
||||||
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", CancellationToken.None);
|
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task WithJson_Should_Send_Configured_Json()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
using var server = WireMockServer.Start(new WireMockServerSettings
|
|
||||||
{
|
|
||||||
Logger = new TestOutputHelperWireMockLogger(output),
|
|
||||||
Urls = ["ws://localhost:0"]
|
|
||||||
});
|
|
||||||
|
|
||||||
var responseData = new
|
|
||||||
{
|
|
||||||
status = "ok",
|
|
||||||
message = "This is a predefined JSON response",
|
|
||||||
timestamp = new DateTime(2024, 1, 1, 12, 0, 0, DateTimeKind.Utc)
|
|
||||||
};
|
|
||||||
|
|
||||||
server
|
|
||||||
.Given(Request.Create()
|
|
||||||
.WithPath("/ws/json")
|
|
||||||
.WithWebSocketUpgrade()
|
|
||||||
)
|
|
||||||
.RespondWith(Response.Create()
|
|
||||||
.WithWebSocket(ws => ws
|
|
||||||
.SendMessage(m => m.WithJson(responseData))
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
using var client = new ClientWebSocket();
|
|
||||||
var uri = new Uri($"{server.Url!}/ws/json");
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await client.ConnectAsync(uri, CancellationToken.None);
|
|
||||||
client.State.Should().Be(WebSocketState.Open);
|
|
||||||
|
|
||||||
var testMessage = "Any message from client";
|
|
||||||
await client.SendAsync(testMessage);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
var received = await client.ReceiveAsTextAsync();
|
|
||||||
|
|
||||||
var json = JObject.Parse(received);
|
|
||||||
json["status"]!.ToString().Should().Be("ok");
|
|
||||||
json["message"]!.ToString().Should().Be("This is a predefined JSON response");
|
|
||||||
json["timestamp"].Should().NotBeNull();
|
|
||||||
|
|
||||||
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", CancellationToken.None);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task WithJson_Should_Send_Same_Json_For_Multiple_Messages()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
using var server = WireMockServer.Start(new WireMockServerSettings
|
|
||||||
{
|
|
||||||
Logger = new TestOutputHelperWireMockLogger(output),
|
|
||||||
Urls = ["ws://localhost:0"]
|
|
||||||
});
|
|
||||||
|
|
||||||
var responseData = new
|
|
||||||
{
|
|
||||||
id = 42,
|
|
||||||
name = "Fixed JSON Response"
|
|
||||||
};
|
|
||||||
|
|
||||||
server
|
|
||||||
.Given(Request.Create()
|
|
||||||
.WithPath("/ws/json")
|
|
||||||
.WithWebSocketUpgrade()
|
|
||||||
)
|
|
||||||
.RespondWith(Response.Create()
|
|
||||||
.WithWebSocket(ws => ws
|
|
||||||
.SendMessage(m => m.WithJson(responseData))
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
using var client = new ClientWebSocket();
|
|
||||||
var uri = new Uri($"{server.Url!}/ws/json");
|
|
||||||
await client.ConnectAsync(uri, CancellationToken.None);
|
|
||||||
|
|
||||||
var testMessages = new[] { "First", "Second", "Third" };
|
|
||||||
|
|
||||||
// Act & Assert
|
|
||||||
foreach (var testMessage in testMessages)
|
|
||||||
{
|
|
||||||
await client.SendAsync(testMessage);
|
|
||||||
|
|
||||||
var received = await client.ReceiveAsTextAsync();
|
|
||||||
|
|
||||||
var json = JObject.Parse(received);
|
|
||||||
json["id"]!.Value<int>().Should().Be(42);
|
|
||||||
json["name"]!.ToString().Should().Be("Fixed JSON Response", $"should always return the fixed JSON regardless of input message '{testMessage}'");
|
|
||||||
}
|
|
||||||
|
|
||||||
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", CancellationToken.None);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task EchoServer_Should_Echo_Multiple_Messages()
|
public async Task EchoServer_Should_Echo_Multiple_Messages()
|
||||||
@@ -676,8 +577,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
|
|||||||
)
|
)
|
||||||
.RespondWith(Response.Create()
|
.RespondWith(Response.Create()
|
||||||
.WithWebSocket(ws => ws
|
.WithWebSocket(ws => ws
|
||||||
.WithTransformer()
|
.SendMessage(m => m.WithText("{{request.Path}} {{[String.Lowercase] message.Text}}"))
|
||||||
.SendMessage(m => m.WithText("{{[String.Lowercase] message.Text}}"))
|
|
||||||
)
|
)
|
||||||
.WithTransformer()
|
.WithTransformer()
|
||||||
);
|
);
|
||||||
@@ -694,7 +594,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var received = await client.ReceiveAsTextAsync();
|
var received = await client.ReceiveAsTextAsync();
|
||||||
received.Should().Be("hello");
|
received.Should().Be("/ws/transform hello");
|
||||||
|
|
||||||
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", CancellationToken.None);
|
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user