using System.Net.Http; using System.Net.Mime; using BotSharp.Abstraction.Http; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; namespace BotSharp.Plugin.HttpHandler.Functions; public class HandleHttpRequestFn : IFunctionCallback { public string Name => "util-http-handle_http_request"; public string Indication => "Give me a second, I'm taking care of it!"; private readonly IServiceProvider _services; private readonly ILogger _logger; private readonly IHttpClientFactory _httpClientFactory; private readonly BotSharpOptions _options; public HandleHttpRequestFn(IServiceProvider services, ILogger logger, IHttpClientFactory httpClientFactory, IHttpContextAccessor context, BotSharpOptions options) { _services = services; _logger = logger; _httpClientFactory = httpClientFactory; _options = options; } public async Task Execute(RoleDialogModel message) { var args = JsonSerializer.Deserialize(message.FunctionArgs, _options.JsonSerializerOptions); var url = args?.RequestUrl; var method = args?.HttpMethod; var content = args?.RequestContent; try { var response = await SendHttpRequest(url, method, content); var responseContent = await HandleHttpResponse(response); message.Content = responseContent; return true; } catch (Exception ex) { var msg = $"Fail when sending http request. Url: {url}, method: {method}, content: {content}"; _logger.LogError($"{msg}\n(Error: {ex.Message}\r\n{ex.InnerException})"); message.Content = msg; return false; } } private async Task SendHttpRequest(string? url, string? method, string? content) { if (string.IsNullOrEmpty(url)) return null; using var client = _httpClientFactory.CreateClient(); PrepareRequestHeaders(client); var (uri, request) = BuildHttpRequest(url, method, content); var response = await client.SendAsync(request); if (response == null || !response.IsSuccessStatusCode) { _logger.LogWarning($"Response status code: {response?.StatusCode}"); } return response; } private void PrepareRequestHeaders(HttpClient client) { var hooks = _services.GetServices(); foreach (var hook in hooks) { hook.OnAddHttpHeaders(client.DefaultRequestHeaders); } } private (Uri, HttpRequestMessage) BuildHttpRequest(string url, string? method, string? content) { var httpMethod = GetHttpMethod(method); StringContent httpContent; var requestUrl = url; if (httpMethod == HttpMethod.Get) { httpContent = BuildHttpContent("{}"); requestUrl = BuildQuery(url, content); } else { httpContent = BuildHttpContent(content); } if (!Uri.TryCreate(requestUrl, UriKind.Absolute, out var uri)) { var settings = _services.GetRequiredService(); var baseUri = new Uri(settings.BaseAddress); uri = new Uri(baseUri, requestUrl); } return (uri, new HttpRequestMessage { RequestUri = uri, Method = httpMethod, Content = httpContent }); } private HttpMethod GetHttpMethod(string? method) { var localMethod = method?.Trim()?.ToUpper(); HttpMethod matchMethod; switch (localMethod) { case "GET": matchMethod = HttpMethod.Get; break; case "DELETE": matchMethod = HttpMethod.Delete; break; case "PUT": matchMethod = HttpMethod.Put; break; case "Patch": matchMethod = HttpMethod.Patch; break; default: matchMethod = HttpMethod.Post; break; } return matchMethod; } private StringContent BuildHttpContent(string? content) { var str = string.Empty; try { var json = JsonSerializer.Deserialize(content ?? "{}", _options.JsonSerializerOptions); str = JsonSerializer.Serialize(json, _options.JsonSerializerOptions); } catch (Exception ex) { _logger.LogWarning($"Error when build http content: {content}\n(Error: {ex.Message})"); } return new StringContent(str, Encoding.UTF8, MediaTypeNames.Application.Json); } private string BuildQuery(string url, string? content) { if (string.IsNullOrEmpty(url) || string.IsNullOrEmpty(content)) return url; try { var queries = new List(); var json = JsonSerializer.Deserialize(content, _options.JsonSerializerOptions); var root = json.RootElement; foreach (var prop in root.EnumerateObject()) { var name = prop.Name.Trim(); var value = prop.Value.ToString().Trim(); if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(value)) { continue; } queries.Add($"{name}={value}"); } if (!queries.IsNullOrEmpty()) { url += $"?{string.Join('&', queries)}"; } return url; } catch (Exception ex) { _logger.LogWarning($"Error when building url query. Url: {url}, Content: {content}\n(Error: {ex.Message})"); return url; } } private async Task HandleHttpResponse(HttpResponseMessage? response) { if (response == null) return string.Empty; return await response.Content.ReadAsStringAsync(); } }