Skip to content

Commit

Permalink
feat(connector): add optional debugging targets
Browse files Browse the repository at this point in the history
  • Loading branch information
thomashilzendegen authored and gingters committed Jul 2, 2024
1 parent caf5bf6 commit 8de2829
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using Thinktecture.Relay.Acknowledgement;
using Thinktecture.Relay.Connector.DependencyInjection;
using Thinktecture.Relay.Connector.Targets;
using Thinktecture.Relay.Transport;

// ReSharper disable once CheckNamespace; (extension methods on IServiceCollection namespace)
namespace Microsoft.Extensions.DependencyInjection;

/// <summary>
/// Extension methods for the <see cref="IRelayConnectorBuilder{TRequest,TResponse,TAcknowledge}"/>.
/// </summary>
public static class RelayConnectorBuilderExtensions
{
/// <summary>
/// Adds the <see cref="PingTarget{TRequest,TResponse}"/>.
/// </summary>
/// <remarks>The default target key is "$ping".</remarks>
/// <param name="builder">The <see cref="IRelayConnectorBuilder{TRequest,TResponse,TAcknowledge}"/>.</param>
/// <param name="targetKey">The target key for the <see cref="PingTarget{TRequest,TResponse}"/>.</param>
/// <typeparam name="TRequest">The type of request.</typeparam>
/// <typeparam name="TResponse">The type of response.</typeparam>
/// <typeparam name="TAcknowledge">The type of acknowledge.</typeparam>
/// <returns>The <see cref="IRelayConnectorBuilder{TRequest,TResponse,TAcknowledge}"/>.</returns>
public static IRelayConnectorBuilder<TRequest, TResponse, TAcknowledge> AddPingTarget<TRequest, TResponse,
TAcknowledge>(this IRelayConnectorBuilder<TRequest, TResponse, TAcknowledge> builder, string targetKey = "$ping")
where TRequest : IClientRequest
where TResponse : ITargetResponse, new()
where TAcknowledge : IAcknowledgeRequest
{
builder.AddTarget<TRequest, TResponse, TAcknowledge, PingTarget<TRequest, TResponse>>(targetKey);

return builder;
}

/// <summary>
/// Adds the <see cref="EchoTarget{TRequest,TResponse}"/>.
/// </summary>
/// <remarks>The default target key is "$echo" and should only be added for debugging purposes.</remarks>
/// <param name="builder">The <see cref="IRelayConnectorBuilder{TRequest,TResponse,TAcknowledge}"/>.</param>
/// <param name="targetKey">The target key for the <see cref="EchoTarget{TRequest,TResponse}"/>.</param>
/// <typeparam name="TRequest">The type of request.</typeparam>
/// <typeparam name="TResponse">The type of response.</typeparam>
/// <typeparam name="TAcknowledge">The type of acknowledge.</typeparam>
/// <returns>The <see cref="IRelayConnectorBuilder{TRequest,TResponse,TAcknowledge}"/>.</returns>
public static IRelayConnectorBuilder<TRequest, TResponse, TAcknowledge> AddEchoTarget<TRequest, TResponse,
TAcknowledge>(this IRelayConnectorBuilder<TRequest, TResponse, TAcknowledge> builder, string targetKey = "$echo")
where TRequest : IClientRequest
where TResponse : ITargetResponse, new()
where TAcknowledge : IAcknowledgeRequest
{
builder.AddTarget<TRequest, TResponse, TAcknowledge, EchoTarget<TRequest, TResponse>>(targetKey);

return builder;
}
}
83 changes: 83 additions & 0 deletions src/Thinktecture.Relay.Connector/Targets/EchoTarget.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
using System;
using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Thinktecture.Relay.Transport;

namespace Thinktecture.Relay.Connector.Targets;

/// <inheritdoc />
public class EchoTarget<TRequest, TResponse> : IRelayTargetFunc<TRequest, TResponse>
where TRequest : IClientRequest
where TResponse : ITargetResponse, new()
{
private class CopyStream(Stream source, long length) : Stream
{
private long _length = Math.Max(0, length);

public override void Flush()
=> throw new NotImplementedException();

public override int Read(byte[] buffer, int offset, int count)
{
if (Position == _length) return 0;

var read = 0;

while (read < count && Position < _length)
{
var remaining = Math.Min(_length - Position, count);
var available = source.Read(buffer, offset, (int)remaining);
if (available == 0)
{
source.Position = 0;
}

offset += available;
read += available;
count -= available;

Position += available;
}

return read;
}

public override long Seek(long offset, SeekOrigin origin)
=> throw new NotSupportedException();

public override void SetLength(long value)
=> _length = value;

public override void Write(byte[] buffer, int offset, int count)
=> throw new NotSupportedException();

public override bool CanRead => true;

public override bool CanSeek => false;

public override bool CanWrite => false;

public override long Length => _length;

public override long Position { get; set; }
}

/// <inheritdoc />
public Task<TResponse> HandleAsync(TRequest request, CancellationToken cancellationToken = default)
{
if (!request.HttpMethod.Equals("POST", StringComparison.OrdinalIgnoreCase) || request.BodyContent is null)
return Task.FromResult(request.CreateResponse<TResponse>(HttpStatusCode.NoContent));

var result = request.CreateResponse<TResponse>(HttpStatusCode.OK);

result.BodyContent = int.TryParse(request.Url, out var size)
? new CopyStream(request.BodyContent, size)
: request.BodyContent;
result.BodySize = result.BodyContent.Length;
result.BodyContent.Position = 0;

return Task.FromResult(result);
}
}
33 changes: 33 additions & 0 deletions src/Thinktecture.Relay.Connector/Targets/PingTarget.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Thinktecture.Relay.Transport;

namespace Thinktecture.Relay.Connector.Targets;

/// <inheritdoc />
public class PingTarget<TRequest, TResponse> : IRelayTargetFunc<TRequest, TResponse>
where TRequest : IClientRequest
where TResponse : ITargetResponse, new()
{
/// <inheritdoc />
public Task<TResponse> HandleAsync(TRequest request, CancellationToken cancellationToken = default)
{
if (!request.HttpMethod.Equals("GET", StringComparison.OrdinalIgnoreCase))
return Task.FromResult(request.CreateResponse<TResponse>(HttpStatusCode.NotFound));

var result = request.CreateResponse<TResponse>(HttpStatusCode.OK);

result.HttpHeaders = new Dictionary<string, string[]>()
{
{ "Content-Type", ["text/plain"] },
};
result.BodyContent = new MemoryStream("PONG"u8.ToArray());
result.BodySize = result.BodyContent.Length;

return Task.FromResult(result);
}
}
2 changes: 2 additions & 0 deletions src/docker/Thinktecture.Relay.Connector.Docker/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public static void ConfigureServices(HostBuilderContext hostBuilderContext, ISer
services
.AddRelayConnector(options => configuration.GetSection("RelayConnector").Bind(options))
.AddSignalRConnectorTransport()
.AddPingTarget()
.AddEchoTarget()
.AddTarget<InProcFunc>("inprocfunc")
.AddTarget<InProcAction>("inprocaction");

Expand Down

0 comments on commit 8de2829

Please sign in to comment.