/* * MinIO .NET Library for Amazon S3 Compatible Cloud Storage, (C) 2017 MinIO, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System.Globalization; using System.Net; using System.Reflection; using System.Runtime.InteropServices; using Minio.Credentials; using Minio.DataModel; using Minio.DataModel.Result; using Minio.Exceptions; using Minio.Handlers; using Minio.Helper; namespace Minio; public static class MinioClientExtensions { internal static string SystemUserAgent { get { var assembly = Assembly.GetExecutingAssembly(); var version = assembly.GetName().Version; var release = $"minio-dotnet/{version}"; #if NET46 string arch = Environment.Is64BitOperatingSystem ? "x86_64" : "x86"; return $"MinIO ({Environment.OSVersion};{arch}) {release}"; #else var arch = RuntimeInformation.OSArchitecture.ToString(); return $"MinIO ({RuntimeInformation.OSDescription};{arch}) {release}"; #endif } } public static IMinioClient WithEndpoint(this IMinioClient minioClient, string endpoint) { if (minioClient is null) throw new ArgumentNullException(nameof(minioClient)); minioClient.Config.Endpoint = endpoint; minioClient.SetBaseURL(GetBaseUrl(endpoint)); return minioClient; } public static IMinioClient WithEndpoint(this IMinioClient minioClient, string endpoint, int port) { if (minioClient is null) throw new ArgumentNullException(nameof(minioClient)); if (port < 1 || port > 65535) throw new ArgumentException( string.Format(CultureInfo.InvariantCulture, "Port {0} is not a number between 1 and 65535", port), nameof(port)); endpoint = endpoint + ":" + port.ToString(CultureInfo.InvariantCulture); minioClient.Config.Endpoint = endpoint; minioClient.SetBaseURL(GetBaseUrl(endpoint)); return minioClient; } public static IMinioClient WithEndpoint(this IMinioClient minioClient, Uri url) { if (minioClient is null) throw new ArgumentNullException(nameof(minioClient)); if (url is null) throw new ArgumentNullException(nameof(url)); minioClient.SetBaseURL(url); minioClient.Config.Endpoint = url.AbsoluteUri; return minioClient; } public static IMinioClient WithRegion(this IMinioClient minioClient, string region) { if (minioClient is null) throw new ArgumentNullException(nameof(minioClient)); if (string.IsNullOrEmpty(region)) throw new ArgumentException( string.Format(CultureInfo.InvariantCulture, "{0} the region value can't be null or empty.", region), nameof(region)); minioClient.Config.Region = region; return minioClient; } public static IMinioClient WithRegion(this IMinioClient minioClient) { if (minioClient is null) throw new ArgumentNullException(nameof(minioClient)); // Set region to its default value if empty or null minioClient.Config.Region ??= "us-east-1"; return minioClient; } public static IMinioClient WithCredentials(this IMinioClient minioClient, string accessKey, string secretKey) { if (minioClient is null) throw new ArgumentNullException(nameof(minioClient)); minioClient.Config.AccessKey = accessKey; minioClient.Config.SecretKey = secretKey; return minioClient; } public static IMinioClient WithSessionToken(this IMinioClient minioClient, string st) { if (minioClient is null) throw new ArgumentNullException(nameof(minioClient)); minioClient.Config.SessionToken = st; return minioClient; } /// /// Connects to Cloud Storage with HTTPS if this method is invoked on client object /// /// public static IMinioClient WithSSL(this IMinioClient minioClient, bool secure = true) { if (minioClient is null) throw new ArgumentNullException(nameof(minioClient)); minioClient.Config.Secure = secure; return minioClient; } /// /// Uses webproxy for all requests if this method is invoked on client object. /// /// The MinioClient instance used /// Information on the proxy server in the setup. /// public static IMinioClient WithProxy(this IMinioClient minioClient, IWebProxy proxy) { if (minioClient is null) throw new ArgumentNullException(nameof(minioClient)); minioClient.Config.Proxy = proxy; return minioClient; } /// /// Uses the set timeout for all requests if this method is invoked on client object /// /// The MinioClient instance used /// Timeout in milliseconds. /// public static IMinioClient WithTimeout(this IMinioClient minioClient, int timeout) { if (minioClient is null) throw new ArgumentNullException(nameof(minioClient)); minioClient.Config.RequestTimeout = timeout; return minioClient; } /// /// Allows to add retry policy handler /// /// The MinioClient instance used /// Delegate that will wrap execution of http client requests. /// public static IMinioClient WithRetryPolicy(this IMinioClient minioClient, IRetryPolicyHandler retryPolicyHandler) { if (minioClient is null) throw new ArgumentNullException(nameof(minioClient)); minioClient.Config.RetryPolicyHandler = retryPolicyHandler; return minioClient; } /// /// Allows to add retry policy handler /// /// The MinioClient instance used /// Delegate that will wrap execution of http client requests. /// public static IMinioClient WithRetryPolicy(this IMinioClient minioClient, Func>, Task> retryPolicyHandler) { if (minioClient is null) throw new ArgumentNullException(nameof(minioClient)); _ = minioClient.WithRetryPolicy(new DefaultRetryPolicyHandler(retryPolicyHandler)); return minioClient; } /// /// Allows end user to define the Http server and pass it as a parameter /// /// The MinioClient instance used /// Instance of HttpClient /// Dispose the HttpClient when leaving /// public static IMinioClient WithHttpClient(this IMinioClient minioClient, HttpClient httpClient, bool disposeHttpClient = false) { if (minioClient is null) throw new ArgumentNullException(nameof(minioClient)); if (httpClient is not null) minioClient.Config.HttpClient = httpClient; minioClient.Config.DisposeHttpClient = disposeHttpClient; return minioClient; } /// /// With provider for credentials and session token if being used /// /// public static IMinioClient WithCredentialsProvider(this IMinioClient minioClient, IClientProvider provider) { if (minioClient is null) throw new ArgumentNullException(nameof(minioClient)); minioClient.Config.Provider = provider; AccessCredentials credentials; if (minioClient.Config.Provider is IAMAWSProvider) // Empty object, we need the Minio client completely credentials = new AccessCredentials(); else credentials = minioClient.Config.Provider.GetCredentials(); if (credentials is null) // Unable to fetch credentials. return minioClient; minioClient.Config.AccessKey = credentials.AccessKey; minioClient.Config.SecretKey = credentials.SecretKey; var isSessionTokenAvailable = !string.IsNullOrEmpty(credentials.SessionToken); if ((minioClient.Config.Provider is AWSEnvironmentProvider || minioClient.Config.Provider is IAMAWSProvider || minioClient.Config.Provider is CertificateIdentityProvider || (minioClient.Config.Provider is ChainedProvider chainedProvider && chainedProvider.CurrentProvider is AWSEnvironmentProvider)) && isSessionTokenAvailable) minioClient.Config.SessionToken = credentials.SessionToken; return minioClient; } public static IMinioClient Build(this IMinioClient minioClient) { if (minioClient is null) throw new ArgumentNullException(nameof(minioClient)); // Instantiate a region cache minioClient.Config.RegionCache = BucketRegionCache.Instance; if (string.IsNullOrEmpty(minioClient.Config.BaseUrl)) throw new MinioException("Endpoint not initialized."); if (minioClient.Config.Provider is not null && minioClient.Config.Provider.GetType() != typeof(ChainedProvider) && minioClient.Config.SessionToken is null) throw new MinioException("User Access Credentials Provider not initialized correctly."); if (minioClient.Config.Provider is null && (string.IsNullOrEmpty(minioClient.Config.AccessKey) || string.IsNullOrEmpty(minioClient.Config.SecretKey))) throw new MinioException("User Access Credentials not initialized."); var host = minioClient.Config.BaseUrl; var scheme = minioClient.Config.Secure ? Utils.UrlEncode("https") : Utils.UrlEncode("http"); if (!minioClient.Config.BaseUrl.StartsWith("http", StringComparison.OrdinalIgnoreCase)) minioClient.Config.Endpoint = string.Format(CultureInfo.InvariantCulture, "{0}://{1}", scheme, host); else minioClient.Config.Endpoint = host; var httpClientHandler = new HttpClientHandler { Proxy = minioClient.Config.Proxy }; minioClient.Config.HttpClient ??= minioClient.Config.Proxy is null ? new HttpClient() : new HttpClient(httpClientHandler); _ = minioClient.Config.HttpClient.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", minioClient.Config.FullUserAgent); minioClient.Config.HttpClient.Timeout = TimeSpan.FromMinutes(30); return minioClient; } /// /// Sets app version and name. Used for constructing User-Agent header in all HTTP requests /// /// /// /// public static IMinioClient SetAppInfo(this IMinioClient minioClient, string appName, string appVersion) { if (string.IsNullOrEmpty(appName)) throw new ArgumentException("Appname cannot be null or empty", nameof(appName)); if (string.IsNullOrEmpty(appVersion)) throw new ArgumentException("Appversion cannot be null or empty", nameof(appVersion)); minioClient.Config.CustomUserAgent = $"{appName}/{appVersion}"; return minioClient; } internal static Uri GetBaseUrl(string endpoint) { if (string.IsNullOrEmpty(endpoint)) throw new ArgumentException( string.Format(CultureInfo.InvariantCulture, "{0} is the value of the endpoint. It can't be null or empty.", endpoint), nameof(endpoint)); if (endpoint.EndsWith("/", StringComparison.OrdinalIgnoreCase)) endpoint = endpoint[..^1]; if (!BuilderUtil.IsValidHostnameOrIPAddress(endpoint)) throw new InvalidEndpointException( string.Format(CultureInfo.InvariantCulture, "{0} is invalid hostname.", endpoint), "endpoint"); string conn_url; if (endpoint.StartsWith("http", StringComparison.OrdinalIgnoreCase)) throw new InvalidEndpointException( string.Format(CultureInfo.InvariantCulture, "{0} the value of the endpoint has the scheme (http/https) in it.", endpoint), "endpoint"); var enable_https = Environment.GetEnvironmentVariable("ENABLE_HTTPS"); var scheme = enable_https?.Equals("1", StringComparison.OrdinalIgnoreCase) == true ? "https://" : "http://"; conn_url = scheme + endpoint; var url = new Uri(conn_url); var hostnameOfUri = url.Authority; if (!string.IsNullOrEmpty(hostnameOfUri) && !BuilderUtil.IsValidHostnameOrIPAddress(hostnameOfUri)) throw new InvalidEndpointException( string.Format(CultureInfo.InvariantCulture, "{0}, {1} is invalid hostname.", endpoint, hostnameOfUri), "endpoint"); return url; } internal static void SetBaseURL(this IMinioClient minioClient, Uri url) { if (url.IsDefaultPort) minioClient.Config.BaseUrl = url.Host; else minioClient.Config.BaseUrl = url.Host + ":" + url.Port; } }