Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions src/Http/Wolverine.Http.Tests/Samples/ExternalHttpServer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Wolverine;
using Wolverine.Http;
using Wolverine.Http.Transport;

var builder = WebApplication.CreateBuilder(args);
const string httpNamedClient = "https://extenal/";
builder.UseWolverine(opts =>
{
var transport = new HttpTransport();
opts.Transports.Add(transport);
// Publish all messages to the external http endpoint using the named http client
// The .SendInline() method fails, so do not use it here
opts.PublishAllMessages().ToHttpEndpoint(httpNamedClient);
});
builder.Services.AddWolverineHttp();
// Configure the named http client to point to the external wolverine server
builder.Services.AddHttpClient(
httpNamedClient,
client =>
{
client.BaseAddress = new Uri("https://where-ever-you want-message-to-go/");
//client.DefaultRequestHeaders.Add("Authorization", $"Bearer eyJ***");
});
// To handle the messages over HTTP, send them to https://where-your-app-with-message-handlers/_wolverine/batch/queue
// Register the WolverineHttpTransportClient for sending messages over HTTP
builder.Services.AddScoped<WolverineHttpTransportClient>();
var app = builder.Build();
app.MapWolverineEndpoints();
app.MapPost(
"/test-command", async (TestCommand command, IMessageBus bus) =>
{
await bus.SendAsync(command);
return Results.Ok();
});
app.MapWolverineHttpTransportEndpoints();

app.Run();

public record TestCommand(string message);

public class TestCommandHandler
{
public TestCommand1 Handle(TestCommand command)
{
Console.WriteLine(
$"Handled command with message: {command.message}");
return new TestCommand1(command.message + "x");
}
}

public record TestCommand1(string message);

public class TestCommand1Handler(IMessageContext messageBus)
{
public void Handle(TestCommand1 command)
{
Console.WriteLine($"Handled TestCommand1 with message: {command.message}");
}
}
68 changes: 68 additions & 0 deletions src/Http/Wolverine.Http.Tests/Transport/HttpTransportTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using JasperFx.Core;
using Shouldly;
using Wolverine.Configuration;
using Wolverine.Http.Transport;
using Wolverine.Runtime;
using NSubstitute;
using Xunit;

namespace Wolverine.Http.Tests.Transport;

public class HttpTransportTests
{
[Fact]
public void protocol_is_https()
{
var transport = new HttpTransport();
transport.Protocol.ShouldBe("https");
}

[Fact]
public void can_get_endpoint_by_uri()
{
var transport = new HttpTransport();
var uri = "https://localhost:5500".ToUri();
var endpoint = transport.EndpointFor(uri.ToString());

endpoint.Uri.ShouldBe(uri);
endpoint.Role.ShouldBe(EndpointRole.Application);
}

[Fact]
public void endpoints_are_cached()
{
var transport = new HttpTransport();
var uri = "https://localhost:5500".ToUri();
var endpoint1 = transport.EndpointFor(uri.ToString());
var endpoint2 = transport.EndpointFor(uri.ToString());

endpoint1.ShouldBeSameAs(endpoint2);
}

[Fact]
public async Task initialize_compiles_all_endpoints()
{
var transport = new HttpTransport();
var uri1 = "https://localhost:5501".ToUri();
var uri2 = "https://localhost:5502".ToUri();

var endpoint1 = transport.EndpointFor(uri1.ToString());
var endpoint2 = transport.EndpointFor(uri2.ToString());

var runtime = NSubstitute.Substitute.For<IWolverineRuntime>();
runtime.Options.Returns(new WolverineOptions());

await transport.InitializeAsync(runtime);
}

[Fact]
public void can_find_endpoint_by_uri()
{
var transport = new HttpTransport();
var uri = "https://localhost:5503".ToUri();
var endpoint = transport.EndpointFor(uri.ToString());

var found = transport.TryGetEndpoint(uri);
found.ShouldBeSameAs(endpoint);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using System.Net;
using System.Net.Http.Headers;
using NSubstitute;
using Shouldly;
using Wolverine.Http.Transport;
using Wolverine.Runtime.Serialization;
using Wolverine.Transports;
using Xunit;

namespace Wolverine.Http.Tests.Transport;

public class WolverineHttpTransportClientTests
{
private readonly IHttpClientFactory _clientFactory;
private readonly MockHttpMessageHandler _handler;
private readonly HttpClient _httpClient;
private readonly WolverineHttpTransportClient _client;

public WolverineHttpTransportClientTests()
{
_clientFactory = Substitute.For<IHttpClientFactory>();
_handler = new MockHttpMessageHandler();
_httpClient = new HttpClient(_handler);
_client = new WolverineHttpTransportClient(_clientFactory);
}

[Fact]
public async Task send_envelope_async()
{
var uri = "https://localhost:5001/messages";
_clientFactory.CreateClient(uri).Returns(_httpClient);

var envelope = new Envelope
{
Data = new byte[] { 1, 2, 3 },
Destination = new Uri(uri)
};

await _client.SendAsync(uri, envelope);

_handler.LastRequest.ShouldNotBeNull();
_handler.LastRequest.Method.ShouldBe(HttpMethod.Post);
_handler.LastRequest.RequestUri.ToString().ShouldBe(uri);
_handler.LastRequest.Content.Headers.ContentType.MediaType.ShouldBe(HttpTransportExecutor.EnvelopeContentType);

var expectedData = EnvelopeSerializer.Serialize(envelope);
_handler.LastContent.ShouldBe(expectedData);
}

[Fact]
public async Task send_batch_async()
{
var uri = "https://localhost:5001/messages/batch";
_httpClient.BaseAddress = new Uri("https://target-url");
_clientFactory.CreateClient(uri).Returns(_httpClient);

var envelopes = new[]
{
new Envelope { Data = new byte[] { 1 } },
new Envelope { Data = new byte[] { 2 } }
};

var batch = new OutgoingMessageBatch(new Uri(uri), envelopes);

await _client.SendBatchAsync(uri, batch);

_handler.LastRequest.ShouldNotBeNull();
_handler.LastRequest.Method.ShouldBe(HttpMethod.Post);
_handler.LastRequest.RequestUri.ToString().ShouldBe("https://target-url/");
_handler.LastRequest.Content.Headers.ContentType.MediaType.ShouldBe(HttpTransportExecutor.EnvelopeBatchContentType);

var expectedData = EnvelopeSerializer.Serialize(envelopes);
_handler.LastContent.ShouldBe(expectedData);
}
}

public class MockHttpMessageHandler : HttpMessageHandler
{
public HttpRequestMessage LastRequest { get; private set; }
public byte[] LastContent { get; private set; }

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
LastRequest = request;
if (request.Content != null)
{
LastContent = await request.Content.ReadAsByteArrayAsync(cancellationToken);
}
return new HttpResponseMessage(HttpStatusCode.OK);
}
}
40 changes: 16 additions & 24 deletions src/Http/Wolverine.Http/Transport/HttpTransport.cs
Original file line number Diff line number Diff line change
@@ -1,50 +1,42 @@
using JasperFx.Core;
using JasperFx.Resources;
using Wolverine.Configuration;
using Wolverine.Runtime;
using Wolverine.Transports;

namespace Wolverine.Http.Transport;

internal class HttpTransport : ITransport
public class HttpTransport : TransportBase<HttpEndpoint>
{
private readonly Cache<Uri, HttpEndpoint> _endpoints
= new(uri => new HttpEndpoint(uri, EndpointRole.Application));

public string Protocol { get; } = "http";
public string Name { get; } = "Http Transport";
public Endpoint? ReplyEndpoint()
{
throw new NotImplementedException();
}
private readonly LightweightCache<Uri, HttpEndpoint> _endpoints
= new(uri => new HttpEndpoint(uri, EndpointRole.Application){OutboundUri = uri.ToString()});

public Endpoint GetOrCreateEndpoint(Uri uri)
public HttpTransport() : base("https", "HTTP Transport")
{
throw new NotImplementedException();
}

public Endpoint? TryGetEndpoint(Uri uri)
protected override IEnumerable<HttpEndpoint> endpoints()
{
throw new NotImplementedException();
return _endpoints;
}

public IEnumerable<Endpoint> Endpoints()
protected override HttpEndpoint findEndpointByUri(Uri uri)
{
throw new NotImplementedException();
return _endpoints[uri];
}

public async ValueTask InitializeAsync(IWolverineRuntime runtime)
public override ValueTask InitializeAsync(IWolverineRuntime runtime)
{
throw new NotImplementedException();
}
foreach (var endpoint in _endpoints)
{
endpoint.Compile(runtime);
}

public bool TryBuildStatefulResource(IWolverineRuntime runtime, out IStatefulResource? resource)
{
throw new NotImplementedException();
return ValueTask.CompletedTask;
}

public HttpEndpoint EndpointFor(string url)
{
throw new NotImplementedException();
var uri = new Uri(url);
return _endpoints[uri];
}
}
13 changes: 11 additions & 2 deletions src/Http/Wolverine.Http/Transport/WolverineHttpTransportClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,21 @@

namespace Wolverine.Http.Transport;

internal class WolverineHttpTransportClient : HttpClient
public class WolverineHttpTransportClient(IHttpClientFactory clientFactory)
{
public async Task SendBatchAsync(string uri, OutgoingMessageBatch batch)
{
var client = clientFactory.CreateClient(uri);
var content = new ByteArrayContent(EnvelopeSerializer.Serialize(batch.Messages));
content.Headers.ContentType = new MediaTypeHeaderValue(HttpTransportExecutor.EnvelopeBatchContentType);
await PostAsync(uri, content);
await client.PostAsync(client.BaseAddress, content);
}

public async Task SendAsync(string uri, Envelope envelope)
{
var client = clientFactory.CreateClient(uri);
var content = new ByteArrayContent(EnvelopeSerializer.Serialize(envelope));
content.Headers.ContentType = new MediaTypeHeaderValue(HttpTransportExecutor.EnvelopeContentType);
await client.PostAsync(uri, content);
}
}
Loading