From 6ca876c762d8dc141c62fae3fe2853130bd1fb02 Mon Sep 17 00:00:00 2001 From: TekH Date: Fri, 17 Apr 2026 12:17:41 +0200 Subject: [PATCH] Improve HTTP header handling and error resilience Enhance InvokeRecActionViewCommandHandler to robustly handle invalid or non-strict HTTP header values. "Content-Type" and "Authorization" headers are now set using strict parsing with fallback to TryAddWithoutValidation on failure, logging warnings for easier debugging. General headers and API key headers also use this strict-then-fallback approach, making HTTP request construction more tolerant of malformed values and reducing runtime errors. --- .../Commands/InvokeRecActionViewCommand.cs | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/ReC.Application/RecActions/Commands/InvokeRecActionViewCommand.cs b/src/ReC.Application/RecActions/Commands/InvokeRecActionViewCommand.cs index a95c520..5118f0e 100644 --- a/src/ReC.Application/RecActions/Commands/InvokeRecActionViewCommand.cs +++ b/src/ReC.Application/RecActions/Commands/InvokeRecActionViewCommand.cs @@ -59,10 +59,21 @@ public class InvokeRecActionViewCommandHandler( using var httpReq = CreateHttpRequestMessage(restType, action.EndpointUri); if (action.Body is not null) + { httpReq.Content = new StringContent(action.Body); + var contentType = action.Headers?.FirstOrDefault(h => h.Key.Equals("Content-Type", StringComparison.OrdinalIgnoreCase)); + if (contentType is not null && !string.IsNullOrWhiteSpace(contentType.Value.Value)) + try { httpReq.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(contentType.Value.Value); } + catch (FormatException ex) + { + logger?.LogWarning(ex, "Content-Type '{Value}' could not be parsed with strict validation, falling back to TryAddWithoutValidation. ActionId: {ActionId}, ProfileId: {ProfileId}", contentType.Value.Value, action.Id, action.ProfileId); + httpReq.Content.Headers.TryAddWithoutValidation("Content-Type", contentType.Value.Value); + } + } + if (action.Headers is not null) - foreach (var header in action.Headers) + foreach (var header in action.Headers.Where(h => !h.Key.Equals("Content-Type", StringComparison.OrdinalIgnoreCase))) try { httpReq.Headers.Add(header.Key, header.Value); } catch (FormatException ex) { @@ -81,7 +92,12 @@ public class InvokeRecActionViewCommandHandler( switch (action.EndpointAuthApiKeyAddTo) { case ApiKeyLocation.Header: - httpReq.Headers.Add(apiKey, apiValue); + try { httpReq.Headers.Add(apiKey, apiValue); } + catch (FormatException ex) + { + logger?.LogWarning(ex, "ApiKey header '{Key}' could not be added with strict validation, falling back to TryAddWithoutValidation. ActionId: {ActionId}, ProfileId: {ProfileId}", apiKey, action.Id, action.ProfileId); + httpReq.Headers.TryAddWithoutValidation(apiKey, apiValue); + } break; case ApiKeyLocation.Query: var uriBuilder = new UriBuilder(httpReq.RequestUri!); @@ -104,14 +120,24 @@ public class InvokeRecActionViewCommandHandler( case EndpointAuthType.JwtBearer: case EndpointAuthType.OAuth2: if (action.EndpointAuthToken is string authToken) - httpReq.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authToken); + try { httpReq.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authToken); } + catch (FormatException ex) + { + logger?.LogWarning(ex, "Bearer token could not be set with strict validation, falling back to TryAddWithoutValidation. ActionId: {ActionId}, ProfileId: {ProfileId}", action.Id, action.ProfileId); + httpReq.Headers.TryAddWithoutValidation("Authorization", $"Bearer {authToken}"); + } break; case EndpointAuthType.BasicAuth: if (action.EndpointAuthUsername is string authUsername && action.EndpointAuthPassword is string authPassword) { var basicAuth = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{authUsername}:{authPassword}")); - httpReq.Headers.Authorization = new AuthenticationHeaderValue("Basic", basicAuth); + try { httpReq.Headers.Authorization = new AuthenticationHeaderValue("Basic", basicAuth); } + catch (FormatException ex) + { + logger?.LogWarning(ex, "Basic auth could not be set with strict validation, falling back to TryAddWithoutValidation. ActionId: {ActionId}, ProfileId: {ProfileId}", action.Id, action.ProfileId); + httpReq.Headers.TryAddWithoutValidation("Authorization", $"Basic {basicAuth}"); + } } break;