| File: Registry\HttpExtensions.cs | Web Access |
| Project: src\sdk\src\Containers\Microsoft.NET.Build.Containers\Microsoft.NET.Build.Containers.csproj (Microsoft.NET.Build.Containers) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Net.Http.Headers; using Microsoft.Extensions.Logging; using NuGet.Packaging; namespace Microsoft.NET.Build.Containers; internal static class HttpExtensions { private static readonly MediaTypeWithQualityHeaderValue[] _knownManifestFormats = [ new("application/json"), new(SchemaTypes.DockerManifestListV2), new(SchemaTypes.OciImageIndexV1), new(SchemaTypes.DockerManifestV2), new(SchemaTypes.OciManifestV1), new(SchemaTypes.DockerContainerV1), ]; internal static HttpRequestMessage AcceptManifestFormats(this HttpRequestMessage request) { request.Headers.Accept.Clear(); request.Headers.Accept.AddRange(_knownManifestFormats); return request; } /// <summary> /// Servers send the Location header on each response, which tells us where to send the next chunk. /// </summary> public static Uri GetNextLocation(this HttpResponseMessage response) { if (response.Headers.Location is { IsAbsoluteUri: true }) { return response.Headers.Location; } else { // if we don't trim the BaseUri and relative Uri of slashes, you can get invalid urls. // Uri constructor does this on our behalf. return new Uri(response.RequestMessage!.RequestUri!, response.Headers.Location?.OriginalString ?? ""); } } internal static bool IsAmazonECRRegistry(this Uri uri) { // If this the registry is to public ECR the name will contain "public.ecr.aws". if (uri.Authority.Contains(RegistryConstants.PublicAmazonElasticContainerRegistryDomain)) { return true; } // If the registry is to a private ECR the registry will start with an account id which is a 12 digit number and will container either // ".ecr." or ".ecr-" if pushed to a FIPS endpoint. string accountId = uri.Authority.Split('.')[0]; if ((uri.Authority.Contains(".ecr.") || uri.Authority.Contains(".ecr-")) && accountId.Length == 12 && long.TryParse(accountId, out _)) { return true; } return false; } /// <summary> /// Logs the details of <paramref name="response"/> using <paramref name="logger"/> to trace level. /// </summary> internal static async Task LogHttpResponseAsync(this HttpResponseMessage response, ILogger logger, CancellationToken cancellationToken) { if (logger.IsEnabled(LogLevel.Trace)) { StringBuilder s = new(); s.AppendLine($"Request URI: {response.RequestMessage?.Method} {response.RequestMessage?.RequestUri?.ToString()}"); s.AppendLine($"Status code: {response.StatusCode}"); s.AppendLine($"Response headers:"); s.AppendLine(response.Headers.ToString()); string detail = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); s.AppendLine($"Response content: {(string.IsNullOrWhiteSpace(detail) ? "<empty>" : detail)}"); logger.LogTrace(s.ToString()); } } }