using DigitalData.Core.Client.Interface; using System.Diagnostics.CodeAnalysis; using System.Net; using System.Web; namespace DigitalData.Core.Client { public class BaseHttpClientService : IBaseHttpClientService { protected readonly HttpClient _client; protected readonly CookieContainer _cookies; [StringSyntax("Uri")] protected readonly string _uri; protected readonly string _path; protected IEnumerable>? _headers; protected IEnumerable>? _queryParams; internal BaseHttpClientService(HttpClient client, CookieContainer cookieContainer, IHttpClientOptions clientOptions) { _client = client; _cookies = cookieContainer; _uri = clientOptions.Uri.Trim(URI_TRIM_CHARS); _path = clientOptions.Path?.Trim(URI_TRIM_CHARS) ?? string.Empty; _headers = clientOptions.Headers; _queryParams = clientOptions.QueryParams; } public CookieCollection GetCookies(string path = "") => _cookies.GetCookies(uri: new Uri(UriCombine(_uri, path, path.Trim(URI_TRIM_CHARS)))); public async Task FetchAsync( string? scheme = null, int? port = null, string path = "", IEnumerable>? queryParams = null, HttpMethod? method = null, HttpContent? body = null, IEnumerable>? form = null, IEnumerable>? headers = null, bool sendWithCookie = true, bool saveCookie = true ) { // merge with default headers if(_headers is not null) { if (headers is null) headers = _headers; else { var mergedHeaders = headers.ToList(); mergedHeaders.AddRange(_headers); headers = mergedHeaders; } } // Add default query parameters if(_queryParams is not null) { if (queryParams is null) queryParams = _queryParams; else { var mergedQueryParams = queryParams.ToList(); mergedQueryParams.AddRange(_queryParams); queryParams = mergedQueryParams; } } // set default HTTP method as GET method ??= HttpMethod.Get; // create URL var uriBuilder = new UriBuilder(_uri); if (scheme is not null) uriBuilder.Scheme = scheme; if (port is int portInt) uriBuilder.Port = portInt; uriBuilder.Path = UriCombine(_path, path?.Trim(URI_TRIM_CHARS) ?? string.Empty); // Add query parameters if provided if (queryParams?.Any() ?? false) { var query = HttpUtility.ParseQueryString(uriBuilder.Query); var flagParams = queryParams.Where(param => param.Value is null).Select(param => param.Key); var valueParams = queryParams.Where(param => param.Value is not null); foreach (var param in valueParams) query[param.Key] = param.Value switch { bool b => b.ToString().ToLower(), _ => param.Value.ToString() }; if (flagParams.Any()) uriBuilder.Query = string.Join(QUERY_SEPARATOR, query.ToString(), string.Join(QUERY_SEPARATOR, flagParams)); else uriBuilder.Query = query.ToString(); } var requestUri = uriBuilder.Uri; var requestMessage = new HttpRequestMessage(method, requestUri); // Add headers if provided headers?.ForEach(header => requestMessage.Headers.Add(header.Key, header.Value.ToString())); // Add cookie to request if (sendWithCookie) { var cookieHeader = _cookies.GetCookieHeader(requestUri); if (!string.IsNullOrWhiteSpace(cookieHeader)) { requestMessage.Headers.Add("Cookie", cookieHeader); } } // Add body content if provided if (body != null && form != null) throw new InvalidOperationException("Body content and form data cannot both be set."); else if (body != null) requestMessage.Content = body; else if (form != null) requestMessage.Content = new FormUrlEncodedContent(form.Select(e => KeyValuePair.Create(e.Key, e.Value.ToString()))); var response = await _client.SendAsync(requestMessage); // Add response cookies to cookies if (saveCookie) if (response.Headers.TryGetValues("Set-Cookie", out var cookies)) foreach (var cookie in cookies) _cookies.SetCookies(requestUri, cookie); return response; } internal static readonly char[] URI_TRIM_CHARS = { '\\', '/', ' ' }; internal static string UriCombine(params string[] paths) => System.IO.Path.Combine(paths).Replace("\\", "/"); internal static readonly char QUERY_SEPARATOR = '&'; } }