Skip to content

Commit b30a3f7

Browse files
authoredJan 15, 2025··
Merge pull request #7 from DEFRA/task/cdms-216-connect-to-ibm-t2-using-ip
CDMS 216 Connect to IBM T2 using IP
2 parents 45a14ee + 6965987 commit b30a3f7

16 files changed

+415
-63
lines changed
 

‎BtmsGateway.Test/Middleware/MessageDataTests.cs

+333
Large diffs are not rendered by default.

‎BtmsGateway.Test/Services/Routing/MessageRoutesTests.cs

+6
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ public void When_routing_route_1_Then_should_route_correctly()
1717
route.RouteFound.Should().BeTrue();
1818
route.RouteName.Should().Be("route-1");
1919
route.FullRouteLink.Should().Be("http://legacy-link-url/sub/path");
20+
route.RouteHostHeader.Should().Be("legacy-host-header");
2021
route.ConvertedRoutedContentToJson.Should().BeFalse();
2122
route.FullForkLink.Should().Be("btms-link-queue");
23+
route.ForkHostHeader.Should().BeNull();
2224
route.ConvertedForkedContentToJson.Should().BeTrue();
2325
route.UrlPath.Should().Be("/sub/path");
2426
}
@@ -33,8 +35,10 @@ public void When_routing_route_2_Then_should_route_correctly()
3335
route.RouteFound.Should().BeTrue();
3436
route.RouteName.Should().Be("route-2");
3537
route.FullRouteLink.Should().Be("http://btms-link-url/sub/path");
38+
route.RouteHostHeader.Should().Be("btms-host-header");
3639
route.ConvertedRoutedContentToJson.Should().BeTrue();
3740
route.FullForkLink.Should().Be("legacy-link-queue");
41+
route.ForkHostHeader.Should().BeNull();
3842
route.ConvertedForkedContentToJson.Should().BeFalse();
3943
route.UrlPath.Should().Be("/sub/path");
4044
}
@@ -49,7 +53,9 @@ public void When_routing_unrecognised_route_Then_should_fail()
4953
route.RouteFound.Should().BeFalse();
5054
route.RouteName.Should().Be("route-3");
5155
route.FullRouteLink.Should().BeNull();
56+
route.RouteHostHeader.Should().BeNull();
5257
route.FullForkLink.Should().BeNull();
58+
route.ForkHostHeader.Should().BeNull();
5359
route.UrlPath.Should().Be("/sub/path");
5460
}
5561

‎BtmsGateway.Test/Services/Routing/RoutingConfigTests.cs

+4
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ public void When_getting_route_1_Then_should_retrieve_routed_links()
1212
route.Name.Should().Be("route-1");
1313
route.LegacyLink.Should().Be("http://legacy-link-url");
1414
route.LegacyLinkType.Should().Be(LinkType.Url);
15+
route.LegacyHostHeader.Should().Be("legacy-host-header");
1516
route.BtmsLink.Should().Be("btms-link-queue");
1617
route.BtmsLinkType.Should().Be(LinkType.Queue);
18+
route.BtmsHostHeader.Should().BeNull();
1719
route.SendLegacyResponseToBtms.Should().BeTrue();
1820
route.RouteTo.Should().Be(RouteTo.Legacy);
1921
}
@@ -25,8 +27,10 @@ public void When_getting_route_2_Then_should_retrieve_routed_links()
2527
route.Name.Should().Be("route-2");
2628
route.LegacyLink.Should().Be("legacy-link-queue");
2729
route.LegacyLinkType.Should().Be(LinkType.Queue);
30+
route.LegacyHostHeader.Should().BeNull();
2831
route.BtmsLink.Should().Be("http://btms-link-url");
2932
route.BtmsLinkType.Should().Be(LinkType.Url);
33+
route.BtmsHostHeader.Should().Be("btms-host-header");
3034
route.SendLegacyResponseToBtms.Should().BeTrue();
3135
route.RouteTo.Should().Be(RouteTo.Btms);
3236
}

‎BtmsGateway.Test/Services/Routing/TestRoutes.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ public static class TestRoutes
1313
},
1414
NamedLinks = new Dictionary<string, NamedLink>
1515
{
16-
{ "legacy_link_name_1", new NamedLink { Link = "http://legacy-link-url", LinkType = LinkType.Url } },
16+
{ "legacy_link_name_1", new NamedLink { Link = "http://legacy-link-url", LinkType = LinkType.Url, HostHeader = "legacy-host-header" } },
1717
{ "legacy_link_name_2", new NamedLink { Link = "legacy-link-queue", LinkType = LinkType.Queue } },
1818
{ "btms_link_name_1", new NamedLink { Link = "btms-link-queue", LinkType = LinkType.Queue } },
19-
{ "btms_link_name_2", new NamedLink { Link = "http://btms-link-url", LinkType = LinkType.Url } },
19+
{ "btms_link_name_2", new NamedLink { Link = "http://btms-link-url", LinkType = LinkType.Url, HostHeader = "btms-host-header" } },
2020
}
2121
};
2222

‎BtmsGateway/BtmsGateway.csproj

+3-6
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,6 @@
88
</PropertyGroup>
99

1010
<ItemGroup>
11-
<Content Include="../Dockerfile">
12-
<Link>Dockerfile</Link>
13-
</Content>
1411
<Content Include="..\.dockerignore">
1512
<Link>Properties\SolutionFiles\.dockerignore</Link>
1613
</Content>
@@ -20,12 +17,12 @@
2017
<Content Include="..\.gitignore">
2118
<Link>Properties\SolutionFiles\.gitignore</Link>
2219
</Content>
20+
<Content Include="..\Dockerfile">
21+
<Link>Properties\SolutionFiles\Dockerfile</Link>
22+
</Content>
2323
<Content Include="..\README.md">
2424
<Link>Properties\SolutionFiles\README.md</Link>
2525
</Content>
26-
<Content Include="..\run.sh">
27-
<Link>Properties\SolutionFiles\run.sh</Link>
28-
</Content>
2926
</ItemGroup>
3027

3128
<ItemGroup>

‎BtmsGateway/Middleware/MessageData.cs

+36-26
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public class MessageData
1919

2020

2121
public string CorrelationId { get; }
22-
public string OriginalContentAsString { get; }
22+
public string? OriginalContentAsString { get; }
2323
public string HttpString { get; }
2424
public string Url { get; }
2525
public string Path { get; }
@@ -36,7 +36,7 @@ public static async Task<MessageData> Create(HttpRequest request, ILogger logger
3636
return new MessageData(request, content, logger);
3737
}
3838

39-
private MessageData(HttpRequest request, string contentAsString, ILogger logger)
39+
private MessageData(HttpRequest request, string? contentAsString, ILogger logger)
4040
{
4141
_logger = logger;
4242
try
@@ -47,8 +47,8 @@ private MessageData(HttpRequest request, string contentAsString, ILogger logger)
4747
Path = request.Path.HasValue ? request.Path.Value.Trim('/') : string.Empty;
4848
OriginalContentType = RetrieveContentType(request);
4949
_headers = request.Headers;
50-
Url = $"{request.Scheme}://{request.Host}{request.Path}{request.QueryString}";
51-
HttpString = $"{request.Protocol} {Method} {Url} {OriginalContentType}";
50+
Url = $"{request.Protocol}://{request.Host}{request.Path}{request.QueryString}";
51+
HttpString = $"{Method} {Url} {request.Protocol.ToUpper()}/1.1 {OriginalContentType}";
5252
var correlationId = _headers[CorrelationIdHeaderName].FirstOrDefault();
5353
CorrelationId = string.IsNullOrWhiteSpace(correlationId) ? Guid.NewGuid().ToString("D") : correlationId;
5454
}
@@ -59,37 +59,44 @@ private MessageData(HttpRequest request, string contentAsString, ILogger logger)
5959
}
6060
}
6161

62-
public bool ShouldProcessRequest() => !(Method == HttpMethods.Get
63-
&& (Path.Equals("health", StringComparison.CurrentCultureIgnoreCase)
64-
|| Path.StartsWith("swagger", StringComparison.CurrentCultureIgnoreCase)
65-
|| Path.StartsWith(CheckRoutesEndpoints.Path, StringComparison.CurrentCultureIgnoreCase)));
62+
public bool ShouldProcessRequest => !(Method == HttpMethods.Get
63+
&& (Path.Equals("health", StringComparison.InvariantCultureIgnoreCase)
64+
|| Path.StartsWith("swagger", StringComparison.InvariantCultureIgnoreCase)
65+
|| Path.StartsWith(CheckRoutesEndpoints.Path, StringComparison.InvariantCultureIgnoreCase)));
6666

67-
public HttpRequestMessage CreateForwardingRequestAsJson(string? routeUrl)
67+
public HttpRequestMessage CreateForwardingRequestAsJson(string? routeUrl, string? hostHeader)
6868
{
6969
return OriginalContentType is MediaTypeNames.Application.Xml or MediaTypeNames.Application.Soap
70-
? CreateForwardingRequest(routeUrl, string.IsNullOrWhiteSpace(OriginalContentAsString)
70+
? CreateForwardingRequest(routeUrl, hostHeader, string.IsNullOrWhiteSpace(OriginalContentAsString)
7171
? string.Empty
7272
: XmlToJsonConverter.Convert(OriginalContentAsString, KnownArrays), MediaTypeNames.Application.Json)
73-
: CreateForwardingRequestAsOriginal(routeUrl);
73+
: CreateForwardingRequestAsOriginal(routeUrl, hostHeader);
7474
}
7575

76-
public HttpRequestMessage CreateForwardingRequestAsOriginal(string? routeUrl)
76+
public HttpRequestMessage CreateForwardingRequestAsOriginal(string? routeUrl, string? hostHeader)
7777
{
78-
return CreateForwardingRequest(routeUrl, OriginalContentAsString, OriginalContentType);
78+
return CreateForwardingRequest(routeUrl, hostHeader, OriginalContentAsString, OriginalContentType);
7979
}
8080

81-
private HttpRequestMessage CreateForwardingRequest(string? routeUrl, string contentAsString, string contentType)
81+
private HttpRequestMessage CreateForwardingRequest(string? routeUrl, string? hostHeader, string? contentAsString, string contentType)
8282
{
8383
try
8484
{
8585
var request = new HttpRequestMessage(new HttpMethod(Method), routeUrl);
86-
foreach (var header in _headers.Where(x => !x.Key.StartsWith("Content-") && x.Key != "Host" && x.Key != CorrelationIdHeaderName))
86+
foreach (var header in _headers.Where(x => !x.Key.StartsWith("Content-", StringComparison.InvariantCultureIgnoreCase)
87+
&& !string.Equals(x.Key, "Accept", StringComparison.InvariantCultureIgnoreCase)
88+
&& !string.Equals(x.Key, "Host", StringComparison.InvariantCultureIgnoreCase)
89+
&& !string.Equals(x.Key, CorrelationIdHeaderName, StringComparison.InvariantCultureIgnoreCase)))
8790
request.Headers.Add(header.Key, header.Value.ToArray());
8891
request.Headers.Add(CorrelationIdHeaderName, CorrelationId);
89-
90-
request.Content = contentType == MediaTypeNames.Application.Json
91-
? JsonContent.Create(JsonNode.Parse(string.IsNullOrWhiteSpace(contentAsString) ? "{}" : contentAsString), options: Json.SerializerOptions)
92-
: new StringContent(contentAsString, Encoding.UTF8, contentType);
92+
request.Headers.Add("Accept", contentType);
93+
if (!string.IsNullOrWhiteSpace(hostHeader)) request.Headers.TryAddWithoutValidation("host", hostHeader);
94+
95+
request.Content = contentAsString == null || Method == "GET"
96+
? null
97+
: contentType == MediaTypeNames.Application.Json
98+
? JsonContent.Create(JsonNode.Parse(string.IsNullOrWhiteSpace(contentAsString) ? "{}" : contentAsString), options: Json.SerializerOptions)
99+
: new StringContent(contentAsString, Encoding.UTF8, contentType);
93100

94101
return request;
95102
}
@@ -109,7 +116,7 @@ public async Task PopulateResponse(HttpResponse response, RoutingResult routingR
109116
response.Headers.Date = (routingResult.ResponseDate ?? DateTimeOffset.Now).ToString("R");
110117
response.Headers[CorrelationIdHeaderName] = CorrelationId;
111118
response.Headers[RequestedPathHeaderName] = routingResult.UrlPath;
112-
if (routingResult.ResponseContent != null && response.StatusCode != (int)HttpStatusCode.NoContent)
119+
if (routingResult.ResponseContent != null && response.StatusCode != (int)HttpStatusCode.NoContent)
113120
await response.BodyWriter.WriteAsync(new ReadOnlyMemory<byte>(Encoding.UTF8.GetBytes(routingResult.ResponseContent)));
114121
}
115122
catch (Exception ex)
@@ -119,26 +126,29 @@ public async Task PopulateResponse(HttpResponse response, RoutingResult routingR
119126
}
120127
}
121128

122-
private static async Task<string> RetrieveContent(HttpRequest request)
129+
private static async Task<string?> RetrieveContent(HttpRequest request)
123130
{
131+
if (request.Body == Stream.Null) return null;
124132
request.EnableBuffering();
125133
var content = await new StreamReader(request.Body).ReadToEndAsync();
126134
request.Body.Position = 0;
127135
return content;
128136
}
129137

130-
private string RetrieveContentType(HttpRequest request)
138+
private static string RetrieveContentType(HttpRequest request)
131139
{
132140
var contentTypeParts = request.ContentType?.Split(';');
133-
return contentTypeParts is { Length: > 0 } ? contentTypeParts[0] : MediaTypeNames.Application.Json;
141+
var contentType = contentTypeParts is { Length: > 0 } ? contentTypeParts[0] : null;
142+
if (request.Headers.Accept.Count > 0 && request.Headers.Accept[0] != "*/*") contentType ??= request.Headers.Accept[0];
143+
return contentType ?? "";
134144
}
135145
}
136146

137-
public partial class ContentMap(string content)
147+
public partial class ContentMap(string? content)
138148
{
139149
[GeneratedRegex("CHED[A-Z]+")] private static partial Regex RegexChed();
140150
[GeneratedRegex("DispatchCountryCode>(.+?)<")] private static partial Regex RegexCountry();
141151

142-
public string ChedType => RegexChed().Match(content).Value;
143-
public string CountryCode => RegexCountry().Match(content).Groups[1].Value;
152+
public string? ChedType => content == null ? null : RegexChed().Match(content).Value;
153+
public string? CountryCode => content == null ? null : RegexCountry().Match(content).Groups[1].Value;
144154
}

‎BtmsGateway/Middleware/RoutingInterceptor.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public async Task InvokeAsync(HttpContext context)
1515

1616
var messageData = await MessageData.Create(context.Request, logger);
1717

18-
if (messageData.ShouldProcessRequest())
18+
if (messageData.ShouldProcessRequest)
1919
{
2020
logger.Information("{CorrelationId} Received routing instruction {HttpString} {Content}", messageData.CorrelationId, messageData.HttpString, messageData.OriginalContentAsString);
2121

‎BtmsGateway/Services/Checking/CheckRouteConfig.cs

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ public record HealthCheckUrl
1111
public required bool Disabled { get; init; }
1212
public required string Method { get; init; }
1313
public required string Url { get; init; }
14+
public required string? HostHeader { get; init; }
1415
}
1516

1617
public record CheckRouteUrl
@@ -19,6 +20,7 @@ public record CheckRouteUrl
1920
public required bool Disabled { get; init; }
2021
public required string CheckType { get; init; }
2122
public required string Method { get; init; }
23+
public string? HostHeader { get; init; }
2224
public required string Url { get; init; }
2325
public Uri Uri => new(Url);
2426
}

‎BtmsGateway/Services/Checking/CheckRoutes.cs

+4-2
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,20 @@ public async Task<IEnumerable<CheckRouteResult>> CheckIpaffs()
2727

2828
private static CheckRouteUrl GetCheckRouteUrl(KeyValuePair<string, HealthCheckUrl> x)
2929
{
30-
return new CheckRouteUrl { Name = x.Key, Method = x.Value.Method, Disabled = x.Value.Disabled, Url = x.Value.Url, CheckType = "HTTP" };
30+
return new CheckRouteUrl { Name = x.Key, Method = x.Value.Method, Disabled = x.Value.Disabled, HostHeader = x.Value.HostHeader, Url = x.Value.Url, CheckType = "HTTP" };
3131
}
3232

3333
private async Task<IEnumerable<CheckRouteResult>> CheckAll(CheckRouteUrl checkRouteUrl, CancellationTokenSource cts)
3434
{
3535
var checks = new List<Task<CheckRouteResult>>
3636
{
3737
CheckHttp(checkRouteUrl, false, cts.Token),
38-
CheckHttp(checkRouteUrl with { CheckType = "HTTP HOST", Url = checkRouteUrl.Url.Replace(checkRouteUrl.Uri.PathAndQuery, "")}, false, cts.Token),
3938
CheckPing(checkRouteUrl, cts.Token),
4039
CheckNsLookup(checkRouteUrl, cts.Token),
4140
CheckDig(checkRouteUrl, cts.Token)
4241
};
42+
var hostOnlyUrl = $"{checkRouteUrl.Uri.Scheme}://{checkRouteUrl.Uri.Host}";
43+
if (checkRouteUrl.Url != hostOnlyUrl) checks.Insert(1, CheckHttp(checkRouteUrl with { CheckType = "HTTP HOST", Url = $"{checkRouteUrl.Uri.Scheme}://{checkRouteUrl.Uri.Host}" }, false, cts.Token));
4344

4445
return await Task.WhenAll(checks);
4546
}
@@ -54,6 +55,7 @@ private async Task<CheckRouteResult> CheckHttp(CheckRouteUrl checkRouteUrl, bool
5455
logger.Information("Start checking HTTP request for {Url}", checkRouteUrl.Url);
5556
var client = clientFactory.CreateClient(Proxy.ProxyClientWithoutRetry);
5657
var request = new HttpRequestMessage(new HttpMethod(checkRouteUrl.Method), checkRouteUrl.Url);
58+
if (checkRouteUrl.HostHeader != null) request.Headers.TryAddWithoutValidation("host", checkRouteUrl.HostHeader);
5759
stopwatch.Start();
5860
var response = await client.SendAsync(request, token);
5961
checkRouteResult = checkRouteResult with { ResponseResult = $"{response.StatusCode.ToString()} ({(int)response.StatusCode}){(includeResponseBody ? $"\n{await response.Content.ReadAsStringAsync(token)}" : string.Empty)}", Elapsed = stopwatch.Elapsed };

‎BtmsGateway/Services/Routing/MessageRouter.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ public async Task<RoutingResult> Route(MessageData messageData)
2424
var metrics = metricsHost.GetMetrics();
2525
var client = clientFactory.CreateClient(Proxy.RoutedClientWithRetry);
2626
var request = routingResult.ConvertedRoutedContentToJson
27-
? messageData.CreateForwardingRequestAsJson(routingResult.FullRouteLink)
28-
: messageData.CreateForwardingRequestAsOriginal(routingResult.FullRouteLink);
27+
? messageData.CreateForwardingRequestAsJson(routingResult.FullRouteLink, routingResult.RouteHostHeader)
28+
: messageData.CreateForwardingRequestAsOriginal(routingResult.FullRouteLink, routingResult.RouteHostHeader);
2929

3030
metrics.StartRoutedRequest();
3131
var response = await client.SendAsync(request);
@@ -51,8 +51,8 @@ public async Task<RoutingResult> Fork(MessageData messageData)
5151
var metrics = metricsHost.GetMetrics();
5252
var client = clientFactory.CreateClient(Proxy.ForkedClientWithRetry);
5353
var request = routingResult.ConvertedForkedContentToJson
54-
? messageData.CreateForwardingRequestAsJson(routingResult.FullForkLink)
55-
: messageData.CreateForwardingRequestAsOriginal(routingResult.FullForkLink);
54+
? messageData.CreateForwardingRequestAsJson(routingResult.FullForkLink, routingResult.ForkHostHeader)
55+
: messageData.CreateForwardingRequestAsOriginal(routingResult.FullForkLink, routingResult.ForkHostHeader);
5656

5757
metrics.StartForkedRequest();
5858
var response = await client.SendAsync(request);

‎BtmsGateway/Services/Routing/MessageRoutes.cs

+4
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ public RoutingResult GetRoute(string routePath)
5050
RouteName = routeName,
5151
FullRouteLink = $"{route.LegacyLink}{(route.LegacyLinkType == LinkType.Url ? routeUrlPath : null)}",
5252
FullForkLink = $"{route.BtmsLink}{(route.BtmsLinkType == LinkType.Url ? routeUrlPath : null)}",
53+
RouteHostHeader = route.LegacyHostHeader,
54+
ForkHostHeader = route.BtmsHostHeader,
5355
ConvertedForkedContentToJson = true,
5456
UrlPath = routeUrlPath
5557
},
@@ -59,6 +61,8 @@ public RoutingResult GetRoute(string routePath)
5961
RouteName = routeName,
6062
FullRouteLink = $"{route.BtmsLink}{(route.BtmsLinkType == LinkType.Url ? routeUrlPath : null)}",
6163
FullForkLink = $"{route.LegacyLink}{(route.LegacyLinkType == LinkType.Url ? routeUrlPath : null)}",
64+
RouteHostHeader = route.BtmsHostHeader,
65+
ForkHostHeader = route.LegacyHostHeader,
6266
ConvertedRoutedContentToJson = true,
6367
UrlPath = routeUrlPath
6468
},

0 commit comments

Comments
 (0)
Please sign in to comment.