Skip to content

Commit 1ed097e

Browse files
[release/8.0.4xx] [Containers] Fix insecure registry handling to use the correct port for the HTTP protocol (#44234)
Co-authored-by: Dameng <[email protected]>
1 parent 20505b9 commit 1ed097e

File tree

3 files changed

+97
-7
lines changed

3 files changed

+97
-7
lines changed

src/Containers/Microsoft.NET.Build.Containers/FallbackToHttpMessageHandler.cs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,16 @@ namespace Microsoft.NET.Build.Containers;
1212
/// </summary>
1313
internal sealed partial class FallbackToHttpMessageHandler : DelegatingHandler
1414
{
15+
private readonly string _registryName;
1516
private readonly string _host;
1617
private readonly int _port;
1718
private readonly ILogger _logger;
1819
private bool _fallbackToHttp;
1920

20-
public FallbackToHttpMessageHandler(string host, int port, HttpMessageHandler innerHandler, ILogger logger) : base(innerHandler)
21+
public FallbackToHttpMessageHandler(string registryName, string host, int port, HttpMessageHandler innerHandler, ILogger logger)
22+
: base(innerHandler)
2123
{
24+
_registryName = registryName;
2225
_host = host;
2326
_port = port;
2427
_logger = logger;
@@ -38,7 +41,7 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
3841
{
3942
if (canFallback && _fallbackToHttp)
4043
{
41-
FallbackToHttp(request);
44+
FallbackToHttp(_registryName, request);
4245
canFallback = false;
4346
}
4447

@@ -51,7 +54,7 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
5154
{
5255
// Try falling back.
5356
_logger.LogTrace("Attempt to fall back to http for {uri}.", uri);
54-
FallbackToHttp(request);
57+
FallbackToHttp(_registryName, request);
5558
HttpResponseMessage response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
5659

5760
// Fall back was successful. Use http for all new requests.
@@ -76,10 +79,22 @@ internal static bool ShouldAttemptFallbackToHttp(HttpRequestException exception)
7679
return exception.HttpRequestError == HttpRequestError.SecureConnectionError;
7780
}
7881

79-
private static void FallbackToHttp(HttpRequestMessage request)
82+
private static bool RegistryNameContainsPort(string registryName)
83+
{
84+
// use `container` scheme which does not have a default port.
85+
return new Uri($"container://{registryName}").Port != -1;
86+
}
87+
88+
private static void FallbackToHttp(string registryName, HttpRequestMessage request)
8089
{
8190
var uriBuilder = new UriBuilder(request.RequestUri!);
8291
uriBuilder.Scheme = "http";
92+
if (RegistryNameContainsPort(registryName) == false)
93+
{
94+
// registeryName does not contains port number, so reset the port number to -1, otherwise it will be https default port 443
95+
uriBuilder.Port = -1;
96+
}
97+
8398
request.RequestUri = uriBuilder.Uri;
8499
}
85100
}

src/Containers/Microsoft.NET.Build.Containers/Registry/DefaultRegistryAPI.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ internal DefaultRegistryAPI(string registryName, Uri baseUri, bool isInsecureReg
3737

3838
private static HttpClient CreateClient(string registryName, Uri baseUri, ILogger logger, bool isInsecureRegistry, RegistryMode mode)
3939
{
40-
HttpMessageHandler innerHandler = CreateHttpHandler(baseUri, isInsecureRegistry, logger);
40+
HttpMessageHandler innerHandler = CreateHttpHandler(registryName, baseUri, isInsecureRegistry, logger);
4141

4242
HttpMessageHandler clientHandler = new AuthHandshakeMessageHandler(registryName, innerHandler, logger, mode);
4343

@@ -56,7 +56,7 @@ private static HttpClient CreateClient(string registryName, Uri baseUri, ILogger
5656
return client;
5757
}
5858

59-
private static HttpMessageHandler CreateHttpHandler(Uri baseUri, bool allowInsecure, ILogger logger)
59+
private static HttpMessageHandler CreateHttpHandler(string registryName, Uri baseUri, bool allowInsecure, ILogger logger)
6060
{
6161
var socketsHttpHandler = new SocketsHttpHandler()
6262
{
@@ -75,7 +75,7 @@ private static HttpMessageHandler CreateHttpHandler(Uri baseUri, bool allowInsec
7575
RemoteCertificateValidationCallback = IgnoreCertificateErrorsForSpecificHost(baseUri.Host)
7676
};
7777

78-
return new FallbackToHttpMessageHandler(baseUri.Host, baseUri.Port, socketsHttpHandler, logger);
78+
return new FallbackToHttpMessageHandler(registryName, baseUri.Host, baseUri.Port, socketsHttpHandler, logger);
7979
}
8080

8181
private static RemoteCertificateValidationCallback IgnoreCertificateErrorsForSpecificHost(string host)
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Net;
8+
using System.Text;
9+
using System.Threading.Tasks;
10+
using Microsoft.Extensions.Logging.Abstractions;
11+
12+
namespace Microsoft.NET.Build.Containers.UnitTests
13+
{
14+
public class FallbackToHttpMessageHandlerTests
15+
{
16+
[Theory]
17+
[InlineData("mcr.microsoft.com", 80)]
18+
[InlineData("mcr.microsoft.com:443", 443)]
19+
[InlineData("mcr.microsoft.com:80", 80)]
20+
[InlineData("mcr.microsoft.com:5555", 5555)]
21+
[InlineData("[2408:8120:245:49a0:f041:d7bb:bb13:5b64]", 80)]
22+
[InlineData("[2408:8120:245:49a0:f041:d7bb:bb13:5b64]:443", 443)]
23+
[InlineData("[2408:8120:245:49a0:f041:d7bb:bb13:5b64]:80", 80)]
24+
[InlineData("[2408:8120:245:49a0:f041:d7bb:bb13:5b64]:5555", 5555)]
25+
public async Task FallBackToHttpPortShouldAsExpected(string registry, int expectedPort)
26+
{
27+
var uri = new Uri($"https://{registry}");
28+
var handler = new FallbackToHttpMessageHandler(
29+
registry,
30+
uri.Host,
31+
uri.Port,
32+
new ServerMessageHandler(request =>
33+
{
34+
// only accept http requests, reject https requests with a secure connection error
35+
36+
if (request.RequestUri!.Scheme == Uri.UriSchemeHttps)
37+
{
38+
throw new HttpRequestException(
39+
httpRequestError: HttpRequestError.SecureConnectionError
40+
);
41+
}
42+
else
43+
{
44+
return new HttpResponseMessage(HttpStatusCode.OK)
45+
{
46+
RequestMessage = request,
47+
};
48+
}
49+
}),
50+
NullLogger.Instance
51+
);
52+
using var httpClient = new HttpClient(handler);
53+
var response = await httpClient.GetAsync(uri);
54+
Assert.Equal(expectedPort, response.RequestMessage?.RequestUri?.Port);
55+
}
56+
57+
private sealed class ServerMessageHandler : HttpMessageHandler
58+
{
59+
private readonly Func<HttpRequestMessage, HttpResponseMessage> _server;
60+
61+
public ServerMessageHandler(Func<HttpRequestMessage, HttpResponseMessage> server)
62+
{
63+
_server = server;
64+
}
65+
66+
protected override Task<HttpResponseMessage> SendAsync(
67+
HttpRequestMessage request,
68+
CancellationToken cancellationToken
69+
)
70+
{
71+
return Task.FromResult(_server(request));
72+
}
73+
}
74+
}
75+
}

0 commit comments

Comments
 (0)