From 467a668add07a5119fb3eed8c19110014b924304 Mon Sep 17 00:00:00 2001 From: Mark Oliver Date: Mon, 30 Oct 2023 16:15:40 +0000 Subject: [PATCH 1/6] Fixes #358 MockGeneratorPlugin errors if the response has a duplicate header Resolved by only including distinct headers in the response list. --- .../MockResponses/MockResponsesLoader.cs | 2 +- m365-developer-proxy-plugins/RequestLogs/MockGeneratorPlugin.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/m365-developer-proxy-plugins/MockResponses/MockResponsesLoader.cs b/m365-developer-proxy-plugins/MockResponses/MockResponsesLoader.cs index 952b9bba..b40f396b 100644 --- a/m365-developer-proxy-plugins/MockResponses/MockResponsesLoader.cs +++ b/m365-developer-proxy-plugins/MockResponses/MockResponsesLoader.cs @@ -29,7 +29,7 @@ public void LoadResponses() { using (FileStream stream = new FileStream(_responsesFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { using (StreamReader reader = new StreamReader(stream)) { var responsesString = reader.ReadToEnd(); - var responsesConfig = JsonSerializer.Deserialize(responsesString); + var responsesConfig = JsonSerializer.Deserialize(responsesString, new JsonSerializerOptions { AllowTrailingCommas = true, ReadCommentHandling= JsonCommentHandling.Skip}); IEnumerable? configResponses = responsesConfig?.Responses; if (configResponses is not null) { _configuration.Responses = configResponses; diff --git a/m365-developer-proxy-plugins/RequestLogs/MockGeneratorPlugin.cs b/m365-developer-proxy-plugins/RequestLogs/MockGeneratorPlugin.cs index fe48ea42..bee344ec 100644 --- a/m365-developer-proxy-plugins/RequestLogs/MockGeneratorPlugin.cs +++ b/m365-developer-proxy-plugins/RequestLogs/MockGeneratorPlugin.cs @@ -33,7 +33,6 @@ private void AfterRecordingStop(object? sender, RecordingArgs e) return; } - var methodAndUrlComparer = new MethodAndUrlComparer(); var mocks = new List(); foreach (var request in e.RequestLogs) @@ -58,6 +57,7 @@ request.Context is null || ResponseCode = response.StatusCode, ResponseHeaders = response.Headers .Select(h => new KeyValuePair(h.Name, h.Value)) + .DistinctBy(d=> d.Key) .ToDictionary(kvp => kvp.Key, kvp => kvp.Value), ResponseBody = GetResponseBody(request.Context.Session).Result }; From 8b297c52cfb148e0219f5870f5ba2104644caae8 Mon Sep 17 00:00:00 2001 From: Mark Oliver Date: Fri, 8 Dec 2023 10:18:24 +0000 Subject: [PATCH 2/6] Fixes #358 MockGeneratorPlugin errors if the response has a duplicate header Headers are now List of Dictionaries --- .../GraphBatchResponsePayload.cs | 2 +- .../Behavior/RateLimitingPlugin.cs | 4 ++-- .../MockResponses/GraphMockResponsePlugin.cs | 13 ++++++------- .../MockResponses/MockResponse.cs | 2 +- .../MockResponses/MockResponsePlugin.cs | 4 ++-- .../RandomErrors/GraphRandomErrorPlugin.cs | 4 ++-- .../RequestLogs/MockGeneratorPlugin.cs | 5 ++--- 7 files changed, 16 insertions(+), 18 deletions(-) diff --git a/m365-developer-proxy-abstractions/GraphBatchResponsePayload.cs b/m365-developer-proxy-abstractions/GraphBatchResponsePayload.cs index cf8e4c90..f5f2885f 100644 --- a/m365-developer-proxy-abstractions/GraphBatchResponsePayload.cs +++ b/m365-developer-proxy-abstractions/GraphBatchResponsePayload.cs @@ -18,7 +18,7 @@ public class GraphBatchResponsePayloadResponse { [JsonPropertyName("body")] public dynamic? Body { get; set; } [JsonPropertyName("headers")] - public Dictionary? Headers { get; set; } + public List>? Headers { get; set; } } public class GraphBatchResponsePayloadResponseBody { diff --git a/m365-developer-proxy-plugins/Behavior/RateLimitingPlugin.cs b/m365-developer-proxy-plugins/Behavior/RateLimitingPlugin.cs index 714216b6..6e3905a0 100644 --- a/m365-developer-proxy-plugins/Behavior/RateLimitingPlugin.cs +++ b/m365-developer-proxy-plugins/Behavior/RateLimitingPlugin.cs @@ -242,8 +242,8 @@ _urlsToWatch is null || if (_configuration.CustomResponse is not null) { var headers = _configuration.CustomResponse.ResponseHeaders is not null ? - _configuration.CustomResponse.ResponseHeaders.Select(h => new HttpHeader(h.Key, h.Value)) : - Array.Empty(); + _configuration.CustomResponse.ResponseHeaders.SelectMany(dict => dict.Select(kv => new HttpHeader(kv.Key, kv.Value))).ToArray(): + Array.Empty(); // allow custom throttling response var responseCode = (HttpStatusCode)(_configuration.CustomResponse.ResponseCode ?? 200); diff --git a/m365-developer-proxy-plugins/MockResponses/GraphMockResponsePlugin.cs b/m365-developer-proxy-plugins/MockResponses/GraphMockResponsePlugin.cs index 026060e6..5c8207d1 100644 --- a/m365-developer-proxy-plugins/MockResponses/GraphMockResponsePlugin.cs +++ b/m365-developer-proxy-plugins/MockResponses/GraphMockResponsePlugin.cs @@ -4,6 +4,8 @@ using System.Net; using System.Text.Json; using System.Text.RegularExpressions; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft365.DeveloperProxy.Abstractions; namespace Microsoft365.DeveloperProxy.Plugins.MockResponses; @@ -34,7 +36,7 @@ protected override async Task OnRequest(object? sender, ProxyRequestArgs e) GraphBatchResponsePayloadResponse? response = null; var requestId = Guid.NewGuid().ToString(); var requestDate = DateTime.Now.ToString(); - var headers = ProxyUtils + Dictionary headers = ProxyUtils .BuildGraphResponseHeaders(e.Session.HttpClient.Request, requestId, requestDate) .ToDictionary(h => h.Name, h => h.Value); @@ -45,7 +47,7 @@ protected override async Task OnRequest(object? sender, ProxyRequestArgs e) { Id = request.Id, Status = (int)HttpStatusCode.BadGateway, - Headers = headers, + Headers = new List> { headers }, Body = new GraphBatchResponsePayloadResponseBody { Error = new GraphBatchResponsePayloadResponseBodyError @@ -69,10 +71,7 @@ protected override async Task OnRequest(object? sender, ProxyRequestArgs e) if (mockResponse.ResponseHeaders is not null) { - foreach (var key in mockResponse.ResponseHeaders.Keys) - { - headers[key] = mockResponse.ResponseHeaders[key]; - } + mockResponse.ResponseHeaders.SelectMany(dict => dict.Select(kv => headers[kv.Key] = kv.Value)); } // default the content type to application/json unless set in the mock response if (!headers.Any(h => h.Key.Equals("content-type", StringComparison.OrdinalIgnoreCase))) @@ -112,7 +111,7 @@ protected override async Task OnRequest(object? sender, ProxyRequestArgs e) { Id = request.Id, Status = (int)statusCode, - Headers = headers, + Headers = new List> { headers }, Body = body }; diff --git a/m365-developer-proxy-plugins/MockResponses/MockResponse.cs b/m365-developer-proxy-plugins/MockResponses/MockResponse.cs index 7a22f7e9..d0a1af1b 100644 --- a/m365-developer-proxy-plugins/MockResponses/MockResponse.cs +++ b/m365-developer-proxy-plugins/MockResponses/MockResponse.cs @@ -17,5 +17,5 @@ public class MockResponse { [JsonPropertyName("responseBody")] public dynamic? ResponseBody { get; set; } [JsonPropertyName("responseHeaders")] - public IDictionary? ResponseHeaders { get; set; } + public List>? ResponseHeaders { get; set; } } diff --git a/m365-developer-proxy-plugins/MockResponses/MockResponsePlugin.cs b/m365-developer-proxy-plugins/MockResponses/MockResponsePlugin.cs index 0ffd1726..858f1843 100644 --- a/m365-developer-proxy-plugins/MockResponses/MockResponsePlugin.cs +++ b/m365-developer-proxy-plugins/MockResponses/MockResponsePlugin.cs @@ -179,8 +179,8 @@ private void ProcessMockResponse(SessionEventArgs e, MockResponse matchingRespon } if (matchingResponse.ResponseHeaders is not null) { - foreach (var key in matchingResponse.ResponseHeaders.Keys) { - headers.Add(new HttpHeader(key, matchingResponse.ResponseHeaders[key])); + foreach (var key in matchingResponse.ResponseHeaders.SelectMany(dict => dict.Select(kv => new HttpHeader(kv.Key, kv.Value)))) { + headers.Add(key); } } // default the content type to application/json unless set in the mock response diff --git a/m365-developer-proxy-plugins/RandomErrors/GraphRandomErrorPlugin.cs b/m365-developer-proxy-plugins/RandomErrors/GraphRandomErrorPlugin.cs index 34397fc6..d5fe1cf6 100644 --- a/m365-developer-proxy-plugins/RandomErrors/GraphRandomErrorPlugin.cs +++ b/m365-developer-proxy-plugins/RandomErrors/GraphRandomErrorPlugin.cs @@ -135,9 +135,9 @@ private void FailBatch(ProxyRequestArgs e) { var retryAfterDate = DateTime.Now.AddSeconds(retryAfterInSeconds); var requestUrl = ProxyUtils.GetAbsoluteRequestUrlFromBatch(e.Session.HttpClient.Request.RequestUri, request.Url); e.ThrottledRequests.Add(new ThrottlerInfo(GraphUtils.BuildThrottleKey(requestUrl), ShouldThrottle, retryAfterDate)); - response.Headers = new Dictionary{ + response.Headers = new List>{new Dictionary{ { "Retry-After", retryAfterInSeconds.ToString() } - }; + }}; } responses.Add(response); diff --git a/m365-developer-proxy-plugins/RequestLogs/MockGeneratorPlugin.cs b/m365-developer-proxy-plugins/RequestLogs/MockGeneratorPlugin.cs index bee344ec..67a7d540 100644 --- a/m365-developer-proxy-plugins/RequestLogs/MockGeneratorPlugin.cs +++ b/m365-developer-proxy-plugins/RequestLogs/MockGeneratorPlugin.cs @@ -55,10 +55,9 @@ request.Context is null || Method = methodAndUrl.Item1, Url = methodAndUrl.Item2, ResponseCode = response.StatusCode, - ResponseHeaders = response.Headers + ResponseHeaders = new List>{ response.Headers .Select(h => new KeyValuePair(h.Name, h.Value)) - .DistinctBy(d=> d.Key) - .ToDictionary(kvp => kvp.Key, kvp => kvp.Value), + .ToDictionary(kvp => kvp.Key, kvp => kvp.Value) }, ResponseBody = GetResponseBody(request.Context.Session).Result }; // skip mock if it's 200 but has no body From ccd2f98e828592bd4c2187b12b9967c97fca1f9b Mon Sep 17 00:00:00 2001 From: Mark Oliver Date: Tue, 9 Jan 2024 19:18:30 +0000 Subject: [PATCH 3/6] Revert some styling changes --- CONTRIBUTING.md | 2 +- dev-proxy-plugins/MockResponses/GraphMockResponsePlugin.cs | 6 ++++-- dev-proxy-plugins/MockResponses/MockResponse.cs | 2 +- dev-proxy-plugins/MockResponses/MockResponsePlugin.cs | 6 ++---- dev-proxy-plugins/MockResponses/MockResponsesLoader.cs | 3 ++- dev-proxy-plugins/RequestLogs/MockGeneratorPlugin.cs | 2 +- 6 files changed, 11 insertions(+), 10 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6bcadbe9..b3bbfac4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ The best way to contribute initially is to download and try Dev Proxy and then g ## Our foundation -The Dev Proxy is built with .NET 6 and uses the [Titanium.Web.Proxy](https://github.com/justcoding121/titanium-web-proxy). +The Dev Proxy is built with .NET 8 and uses the [Titanium.Web.Proxy](https://github.com/justcoding121/titanium-web-proxy). ## Reporting issues and suggesting new features diff --git a/dev-proxy-plugins/MockResponses/GraphMockResponsePlugin.cs b/dev-proxy-plugins/MockResponses/GraphMockResponsePlugin.cs index c8a78944..866ed0e9 100644 --- a/dev-proxy-plugins/MockResponses/GraphMockResponsePlugin.cs +++ b/dev-proxy-plugins/MockResponses/GraphMockResponsePlugin.cs @@ -42,8 +42,8 @@ protected override async Task OnRequest(object? sender, ProxyRequestArgs e) GraphBatchResponsePayloadResponse? response = null; var requestId = Guid.NewGuid().ToString(); var requestDate = DateTime.Now.ToString(); - var headers = ProxyUtils.BuildGraphResponseHeaders(e.Session.HttpClient.Request, requestId, requestDate); - var headersDictionary = headers.ToDictionary(h => h.Name, h => h.Value); + var headers = ProxyUtils + .BuildGraphResponseHeaders(e.Session.HttpClient.Request, requestId, requestDate); if (e.PluginData.TryGetValue(nameof(RateLimitingPlugin), out var pluginData) && pluginData is List rateLimitingHeaders) @@ -51,6 +51,8 @@ protected override async Task OnRequest(object? sender, ProxyRequestArgs e) ProxyUtils.MergeHeaders(headers, rateLimitingHeaders); } + var headersDictionary = headers.ToDictionary(h => h.Name, h => h.Value); + var mockResponse = GetMatchingMockResponse(request, e.Session.HttpClient.Request.RequestUri); if (mockResponse == null) { diff --git a/dev-proxy-plugins/MockResponses/MockResponse.cs b/dev-proxy-plugins/MockResponses/MockResponse.cs index 89bf5037..12ffcdc9 100644 --- a/dev-proxy-plugins/MockResponses/MockResponse.cs +++ b/dev-proxy-plugins/MockResponses/MockResponse.cs @@ -30,5 +30,5 @@ public class MockResponseResponse [JsonPropertyName("body")] public dynamic? Body { get; set; } [JsonPropertyName("headers")] - public List>? ResponseHeaders { get; set; } + public List>? ResponseHeaders { get; set; } } \ No newline at end of file diff --git a/dev-proxy-plugins/MockResponses/MockResponsePlugin.cs b/dev-proxy-plugins/MockResponses/MockResponsePlugin.cs index 1ebf387c..8c7e051f 100644 --- a/dev-proxy-plugins/MockResponses/MockResponsePlugin.cs +++ b/dev-proxy-plugins/MockResponses/MockResponsePlugin.cs @@ -156,11 +156,9 @@ _configuration.Mocks is null || var mockResponse = _configuration.Mocks.FirstOrDefault(mockResponse => { - if (mockResponse.Request is null) - return false; + if (mockResponse.Request is null) return false; - if (mockResponse.Request.Method != request.Method) - return false; + if (mockResponse.Request.Method != request.Method) return false; if (mockResponse.Request.Url == request.Url && IsNthRequest(mockResponse)) { return true; diff --git a/dev-proxy-plugins/MockResponses/MockResponsesLoader.cs b/dev-proxy-plugins/MockResponses/MockResponsesLoader.cs index 453039b8..1b4d316e 100644 --- a/dev-proxy-plugins/MockResponses/MockResponsesLoader.cs +++ b/dev-proxy-plugins/MockResponses/MockResponsesLoader.cs @@ -38,7 +38,8 @@ public void LoadResponses() var responsesString = reader.ReadToEnd(); var responsesConfig = JsonSerializer.Deserialize(responsesString, new JsonSerializerOptions { AllowTrailingCommas = true, ReadCommentHandling= JsonCommentHandling.Skip}); IEnumerable? configResponses = responsesConfig?.Mocks; - if (configResponses is not null) { + if (configResponses is not null) + { _configuration.Mocks = configResponses; _logger.LogInfo($"Mock responses for {configResponses.Count()} url patterns loaded from {_configuration.MocksFile}"); } diff --git a/dev-proxy-plugins/RequestLogs/MockGeneratorPlugin.cs b/dev-proxy-plugins/RequestLogs/MockGeneratorPlugin.cs index f0ee2a21..3500167e 100644 --- a/dev-proxy-plugins/RequestLogs/MockGeneratorPlugin.cs +++ b/dev-proxy-plugins/RequestLogs/MockGeneratorPlugin.cs @@ -61,7 +61,7 @@ request.Context is null || Response = new() { StatusCode = response.StatusCode, - ResponseHeaders = new List> {response.Headers + ResponseHeaders = new List> {response.Headers .Select(h => new KeyValuePair(h.Name, h.Value)) .ToDictionary(kvp => kvp.Key, kvp => kvp.Value) }, Body = GetResponseBody(request.Context.Session).Result From 01fa17aa017f9fcfaeafd00cbdd8e4454df4ed18 Mon Sep 17 00:00:00 2001 From: Mark Oliver Date: Wed, 10 Jan 2024 10:24:06 +0000 Subject: [PATCH 4/6] Fixes #358 MockGeneratorPlugin errors if the response headers have a duplicate. Remove code that is not specific to this fix. --- CONTRIBUTING.md | 2 +- dev-proxy-plugins/MockResponses/MockResponsePlugin.cs | 2 +- dev-proxy-plugins/MockResponses/MockResponsesLoader.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b3bbfac4..6bcadbe9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ The best way to contribute initially is to download and try Dev Proxy and then g ## Our foundation -The Dev Proxy is built with .NET 8 and uses the [Titanium.Web.Proxy](https://github.com/justcoding121/titanium-web-proxy). +The Dev Proxy is built with .NET 6 and uses the [Titanium.Web.Proxy](https://github.com/justcoding121/titanium-web-proxy). ## Reporting issues and suggesting new features diff --git a/dev-proxy-plugins/MockResponses/MockResponsePlugin.cs b/dev-proxy-plugins/MockResponses/MockResponsePlugin.cs index 8c7e051f..2cc0ce43 100644 --- a/dev-proxy-plugins/MockResponses/MockResponsePlugin.cs +++ b/dev-proxy-plugins/MockResponses/MockResponsePlugin.cs @@ -269,4 +269,4 @@ private void ProcessMockResponse(ProxyRequestArgs e, MockResponse matchingRespon _logger?.LogRequest([$"{matchingResponse.Response?.StatusCode ?? 200} {matchingResponse.Request?.Url}"], MessageType.Mocked, new LoggingContext(e.Session)); } -} \ No newline at end of file +} diff --git a/dev-proxy-plugins/MockResponses/MockResponsesLoader.cs b/dev-proxy-plugins/MockResponses/MockResponsesLoader.cs index 1b4d316e..82362815 100644 --- a/dev-proxy-plugins/MockResponses/MockResponsesLoader.cs +++ b/dev-proxy-plugins/MockResponses/MockResponsesLoader.cs @@ -36,7 +36,7 @@ public void LoadResponses() using (StreamReader reader = new StreamReader(stream)) { var responsesString = reader.ReadToEnd(); - var responsesConfig = JsonSerializer.Deserialize(responsesString, new JsonSerializerOptions { AllowTrailingCommas = true, ReadCommentHandling= JsonCommentHandling.Skip}); + var responsesConfig = JsonSerializer.Deserialize(responsesString); IEnumerable? configResponses = responsesConfig?.Mocks; if (configResponses is not null) { From 3898fe756cc65be7adc4ed94e17a498aaf6ae3fc Mon Sep 17 00:00:00 2001 From: Mark Oliver Date: Wed, 10 Jan 2024 12:13:17 +0000 Subject: [PATCH 5/6] Fixes #358 MockGeneratorPlugin errors if the response headers have a duplicate. Changes following code review. --- dev-proxy-plugins/Behavior/RateLimitingPlugin.cs | 4 ++-- dev-proxy-plugins/MockResponses/GraphMockResponsePlugin.cs | 4 ++-- dev-proxy-plugins/MockResponses/MockResponse.cs | 2 +- dev-proxy-plugins/MockResponses/MockResponsePlugin.cs | 6 +++--- dev-proxy-plugins/RequestLogs/MockGeneratorPlugin.cs | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/dev-proxy-plugins/Behavior/RateLimitingPlugin.cs b/dev-proxy-plugins/Behavior/RateLimitingPlugin.cs index 0c56c9a5..33a6ae5a 100644 --- a/dev-proxy-plugins/Behavior/RateLimitingPlugin.cs +++ b/dev-proxy-plugins/Behavior/RateLimitingPlugin.cs @@ -202,8 +202,8 @@ _urlsToWatch is null || { if (_configuration.CustomResponse is not null) { - var headers = _configuration.CustomResponse.ResponseHeaders is not null ? - _configuration.CustomResponse.ResponseHeaders.SelectMany(dict => dict.Select(kv => new HttpHeader(kv.Key, kv.Value))).ToArray(): + var headers = _configuration.CustomResponse.Headers is not null ? + _configuration.CustomResponse.Headers.SelectMany(dict => dict.Select(kv => new HttpHeader(kv.Key, kv.Value))).ToArray(): Array.Empty(); // allow custom throttling response diff --git a/dev-proxy-plugins/MockResponses/GraphMockResponsePlugin.cs b/dev-proxy-plugins/MockResponses/GraphMockResponsePlugin.cs index 866ed0e9..22af9565 100644 --- a/dev-proxy-plugins/MockResponses/GraphMockResponsePlugin.cs +++ b/dev-proxy-plugins/MockResponses/GraphMockResponsePlugin.cs @@ -82,9 +82,9 @@ protected override async Task OnRequest(object? sender, ProxyRequestArgs e) statusCode = (HttpStatusCode)mockResponse.Response.StatusCode; } - if (mockResponse.Response?.ResponseHeaders is not null) + if (mockResponse.Response?.Headers is not null) { - mockResponse.Response.ResponseHeaders.SelectMany(dict => dict.Select(kv => headersDictionary[kv.Key] = kv.Value)); + mockResponse.Response.Headers.SelectMany(dict => dict.Select(kv => headersDictionary[kv.Key] = kv.Value)); } // default the content type to application/json unless set in the mock response if (!headersDictionary.Any(h => h.Key.Equals("content-type", StringComparison.OrdinalIgnoreCase))) diff --git a/dev-proxy-plugins/MockResponses/MockResponse.cs b/dev-proxy-plugins/MockResponses/MockResponse.cs index 12ffcdc9..6bab8e96 100644 --- a/dev-proxy-plugins/MockResponses/MockResponse.cs +++ b/dev-proxy-plugins/MockResponses/MockResponse.cs @@ -30,5 +30,5 @@ public class MockResponseResponse [JsonPropertyName("body")] public dynamic? Body { get; set; } [JsonPropertyName("headers")] - public List>? ResponseHeaders { get; set; } + public List>? Headers { get; set; } } \ No newline at end of file diff --git a/dev-proxy-plugins/MockResponses/MockResponsePlugin.cs b/dev-proxy-plugins/MockResponses/MockResponsePlugin.cs index 2cc0ce43..e5ab9f89 100644 --- a/dev-proxy-plugins/MockResponses/MockResponsePlugin.cs +++ b/dev-proxy-plugins/MockResponses/MockResponsePlugin.cs @@ -215,11 +215,11 @@ private void ProcessMockResponse(ProxyRequestArgs e, MockResponse matchingRespon statusCode = (HttpStatusCode)matchingResponse.Response.StatusCode; } - if (matchingResponse.Response?.ResponseHeaders is not null) + if (matchingResponse.Response?.Headers is not null) { - foreach (var key in matchingResponse.Response.ResponseHeaders.SelectMany(dict => dict.Select(kv => new HttpHeader(kv.Key, kv.Value)))) + foreach (var headerToAdd in matchingResponse.Response.Headers.SelectMany(dict => dict.Select(kv => new HttpHeader(kv.Key, kv.Value)))) { - headers.Add(key); + headers.Add(headerToAdd); } } diff --git a/dev-proxy-plugins/RequestLogs/MockGeneratorPlugin.cs b/dev-proxy-plugins/RequestLogs/MockGeneratorPlugin.cs index 3500167e..85ab73de 100644 --- a/dev-proxy-plugins/RequestLogs/MockGeneratorPlugin.cs +++ b/dev-proxy-plugins/RequestLogs/MockGeneratorPlugin.cs @@ -61,7 +61,7 @@ request.Context is null || Response = new() { StatusCode = response.StatusCode, - ResponseHeaders = new List> {response.Headers + Headers = new List> {response.Headers .Select(h => new KeyValuePair(h.Name, h.Value)) .ToDictionary(kvp => kvp.Key, kvp => kvp.Value) }, Body = GetResponseBody(request.Context.Session).Result From 200d52e609f0e53d76a88e2c0ceeb94d0729ba12 Mon Sep 17 00:00:00 2001 From: Mark Oliver Date: Wed, 10 Jan 2024 13:07:10 +0000 Subject: [PATCH 6/6] Fixes #358 MockGeneratorPlugin errors if the response headers have a duplicate. Changes following code review and further testing --- dev-proxy-abstractions/GraphBatchResponsePayload.cs | 2 +- dev-proxy-plugins/Behavior/RateLimitingPlugin.cs | 2 +- .../MockResponses/GraphMockResponsePlugin.cs | 11 ++++++++--- dev-proxy-plugins/MockResponses/MockResponse.cs | 2 +- dev-proxy-plugins/MockResponses/MockResponsePlugin.cs | 3 +-- .../RandomErrors/GraphRandomErrorPlugin.cs | 4 +--- dev-proxy-plugins/RequestLogs/MockGeneratorPlugin.cs | 6 +++--- 7 files changed, 16 insertions(+), 14 deletions(-) diff --git a/dev-proxy-abstractions/GraphBatchResponsePayload.cs b/dev-proxy-abstractions/GraphBatchResponsePayload.cs index 0f7d827f..1af2ff0d 100644 --- a/dev-proxy-abstractions/GraphBatchResponsePayload.cs +++ b/dev-proxy-abstractions/GraphBatchResponsePayload.cs @@ -20,7 +20,7 @@ public class GraphBatchResponsePayloadResponse [JsonPropertyName("body")] public dynamic? Body { get; set; } [JsonPropertyName("headers")] - public List>? Headers { get; set; } + public List>? Headers { get; set; } } public class GraphBatchResponsePayloadResponseBody diff --git a/dev-proxy-plugins/Behavior/RateLimitingPlugin.cs b/dev-proxy-plugins/Behavior/RateLimitingPlugin.cs index 33a6ae5a..4145d4d0 100644 --- a/dev-proxy-plugins/Behavior/RateLimitingPlugin.cs +++ b/dev-proxy-plugins/Behavior/RateLimitingPlugin.cs @@ -203,7 +203,7 @@ _urlsToWatch is null || if (_configuration.CustomResponse is not null) { var headers = _configuration.CustomResponse.Headers is not null ? - _configuration.CustomResponse.Headers.SelectMany(dict => dict.Select(kv => new HttpHeader(kv.Key, kv.Value))).ToArray(): + _configuration.CustomResponse.Headers.Select(kvp => new HttpHeader(kvp.Key, kvp.Value)).ToArray(): Array.Empty(); // allow custom throttling response diff --git a/dev-proxy-plugins/MockResponses/GraphMockResponsePlugin.cs b/dev-proxy-plugins/MockResponses/GraphMockResponsePlugin.cs index 22af9565..a46ab7c1 100644 --- a/dev-proxy-plugins/MockResponses/GraphMockResponsePlugin.cs +++ b/dev-proxy-plugins/MockResponses/GraphMockResponsePlugin.cs @@ -60,7 +60,7 @@ protected override async Task OnRequest(object? sender, ProxyRequestArgs e) { Id = request.Id, Status = (int)HttpStatusCode.BadGateway, - Headers = new List> { headersDictionary }, + Headers = headersDictionary.Select(x => new KeyValuePair(x.Key, x.Value)).ToList(), Body = new GraphBatchResponsePayloadResponseBody { Error = new GraphBatchResponsePayloadResponseBodyError @@ -84,8 +84,13 @@ protected override async Task OnRequest(object? sender, ProxyRequestArgs e) if (mockResponse.Response?.Headers is not null) { - mockResponse.Response.Headers.SelectMany(dict => dict.Select(kv => headersDictionary[kv.Key] = kv.Value)); + //Add all the mocked headers into the response we want + foreach (var kvp in mockResponse.Response.Headers) + { + headersDictionary.Add(kvp.Key, kvp.Value); + } } + // default the content type to application/json unless set in the mock response if (!headersDictionary.Any(h => h.Key.Equals("content-type", StringComparison.OrdinalIgnoreCase))) { @@ -124,7 +129,7 @@ protected override async Task OnRequest(object? sender, ProxyRequestArgs e) { Id = request.Id, Status = (int)statusCode, - Headers = new List> { headersDictionary }, + Headers = headersDictionary.Select(x => new KeyValuePair(x.Key, x.Value)).ToList(), Body = body }; diff --git a/dev-proxy-plugins/MockResponses/MockResponse.cs b/dev-proxy-plugins/MockResponses/MockResponse.cs index 6bab8e96..8d077def 100644 --- a/dev-proxy-plugins/MockResponses/MockResponse.cs +++ b/dev-proxy-plugins/MockResponses/MockResponse.cs @@ -30,5 +30,5 @@ public class MockResponseResponse [JsonPropertyName("body")] public dynamic? Body { get; set; } [JsonPropertyName("headers")] - public List>? Headers { get; set; } + public List>? Headers { get; set; } } \ No newline at end of file diff --git a/dev-proxy-plugins/MockResponses/MockResponsePlugin.cs b/dev-proxy-plugins/MockResponses/MockResponsePlugin.cs index e5ab9f89..7563c02e 100644 --- a/dev-proxy-plugins/MockResponses/MockResponsePlugin.cs +++ b/dev-proxy-plugins/MockResponses/MockResponsePlugin.cs @@ -9,7 +9,6 @@ using System.Text.Json; using System.Text.Json.Serialization; using System.Text.RegularExpressions; -using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Models; using Microsoft.DevProxy.Plugins.Behavior; @@ -217,7 +216,7 @@ private void ProcessMockResponse(ProxyRequestArgs e, MockResponse matchingRespon if (matchingResponse.Response?.Headers is not null) { - foreach (var headerToAdd in matchingResponse.Response.Headers.SelectMany(dict => dict.Select(kv => new HttpHeader(kv.Key, kv.Value)))) + foreach (HttpHeader headerToAdd in matchingResponse.Response.Headers.Select(kvp => new HttpHeader(kvp.Key, kvp.Value))) { headers.Add(headerToAdd); } diff --git a/dev-proxy-plugins/RandomErrors/GraphRandomErrorPlugin.cs b/dev-proxy-plugins/RandomErrors/GraphRandomErrorPlugin.cs index 4c5e91ef..c29f4bb2 100644 --- a/dev-proxy-plugins/RandomErrors/GraphRandomErrorPlugin.cs +++ b/dev-proxy-plugins/RandomErrors/GraphRandomErrorPlugin.cs @@ -147,9 +147,7 @@ private void FailBatch(ProxyRequestArgs e) var retryAfterDate = DateTime.Now.AddSeconds(retryAfterInSeconds); var requestUrl = ProxyUtils.GetAbsoluteRequestUrlFromBatch(e.Session.HttpClient.Request.RequestUri, request.Url); e.ThrottledRequests.Add(new ThrottlerInfo(GraphUtils.BuildThrottleKey(requestUrl), ShouldThrottle, retryAfterDate)); - response.Headers = new List>{new Dictionary{ - { "Retry-After", retryAfterInSeconds.ToString() } - }}; + response.Headers = new List>{new ( "Retry-After", retryAfterInSeconds.ToString())}; } responses.Add(response); diff --git a/dev-proxy-plugins/RequestLogs/MockGeneratorPlugin.cs b/dev-proxy-plugins/RequestLogs/MockGeneratorPlugin.cs index 85ab73de..d3ca5b5c 100644 --- a/dev-proxy-plugins/RequestLogs/MockGeneratorPlugin.cs +++ b/dev-proxy-plugins/RequestLogs/MockGeneratorPlugin.cs @@ -51,6 +51,8 @@ request.Context is null || var methodAndUrl = GetMethodAndUrl(methodAndUrlString); var response = request.Context.Session.HttpClient.Response; + var newHeaders = new List>(); + newHeaders.AddRange(response.Headers.Select(h => new KeyValuePair(h.Name, h.Value))); var mock = new MockResponse { Request = new() @@ -61,9 +63,7 @@ request.Context is null || Response = new() { StatusCode = response.StatusCode, - Headers = new List> {response.Headers - .Select(h => new KeyValuePair(h.Name, h.Value)) - .ToDictionary(kvp => kvp.Key, kvp => kvp.Value) }, + Headers = newHeaders, Body = GetResponseBody(request.Context.Session).Result } };