Skip to content

Commit f66346d

Browse files
committed
Migration done?? 🏁
1 parent 7544807 commit f66346d

31 files changed

+1668
-1545
lines changed

DevProxy.Abstractions/Extensions/ILoggerExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ public static void LogRequest(this ILogger logger, string message, MessageType m
1818
logger.Log(new RequestLog(message, messageType, method, url));
1919
}
2020

21-
public static void LogRequest(this ILogger logger, string message, MessageType messageType, HttpRequestMessage httpRequestMessage)
21+
public static void LogRequest(this ILogger logger, string message, MessageType messageType, HttpRequestMessage httpRequestMessage, string? requestId = null, HttpResponseMessage? httpResponse = null)
2222
{
23-
logger.Log(new RequestLog(message, messageType, httpRequestMessage));
23+
logger.Log(new RequestLog(message, messageType, httpRequestMessage, requestId, httpResponse));
2424
}
2525

2626
public static void Log(this ILogger logger, RequestLog message)

DevProxy.Abstractions/Proxy/ProxyEvents.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ public class RequestLog
5858
[JsonIgnore]
5959
public HttpRequestMessage? Request { get; internal set; }
6060

61+
[JsonIgnore]
62+
public string? RequestId { get; internal set; }
63+
6164
[JsonIgnore]
6265
public HttpResponseMessage? Response { get; internal set; }
6366

@@ -72,11 +75,12 @@ public RequestLog(string message, MessageType messageType, object? context)
7275
throw new NotImplementedException("This constructor is not implemented. Use the other constructors instead.");
7376
}
7477

75-
public RequestLog(string message, MessageType messageType, HttpRequestMessage requestMessage, HttpResponseMessage? responseMessage = null) :
78+
public RequestLog(string message, MessageType messageType, HttpRequestMessage requestMessage, string? requestId = null, HttpResponseMessage? responseMessage = null) :
7679
this(message, messageType, requestMessage?.Method.Method, requestMessage?.RequestUri!.AbsoluteUri, _: null)
7780
{
7881
Request = requestMessage;
7982
Response = responseMessage;
83+
RequestId = requestId;
8084
}
8185

8286
public RequestLog(string message, MessageType messageType, string method, string url) :

DevProxy.Plugins/Behavior/GenericRandomErrorPlugin.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -114,19 +114,19 @@ public override void OptionsLoaded(OptionsLoadedArgs e)
114114

115115
if (!ProxyUtils.MatchesUrlToWatch(UrlsToWatch, args.Request.RequestUri))
116116
{
117-
Logger.LogRequest("URL not matched", MessageType.Skipped, args.Request);
117+
Logger.LogRequest("URL not matched", MessageType.Skipped, args.Request, args.RequestId);
118118
return Task.FromResult(PluginResponse.Continue());
119119
}
120120

121121
var failMode = ShouldFail();
122122

123123
if (failMode == GenericRandomErrorFailMode.PassThru && Configuration.Rate != 100)
124124
{
125-
Logger.LogRequest("Pass through", MessageType.Skipped, args.Request);
125+
Logger.LogRequest("Pass through", MessageType.Skipped, args.Request, args.RequestId);
126126
return Task.FromResult(PluginResponse.Continue());
127127
}
128128

129-
var response = FailResponse(args.Request);
129+
var response = FailResponse(args.Request, args.RequestId);
130130
if (response != null)
131131
{
132132
Logger.LogTrace("Left {Name}", nameof(OnRequestAsync));
@@ -140,19 +140,19 @@ public override void OptionsLoaded(OptionsLoadedArgs e)
140140
// uses config to determine if a request should be failed
141141
private GenericRandomErrorFailMode ShouldFail() => _random.Next(1, 100) <= Configuration.Rate ? GenericRandomErrorFailMode.Random : GenericRandomErrorFailMode.PassThru;
142142

143-
private HttpResponseMessage? FailResponse(HttpRequestMessage request)
143+
private HttpResponseMessage? FailResponse(HttpRequestMessage request, string requestId)
144144
{
145145
var matchingResponse = GetMatchingErrorResponse(request);
146146
if (matchingResponse is not null &&
147147
matchingResponse.Responses is not null)
148148
{
149149
// pick a random error response for the current request
150150
var error = matchingResponse.Responses.ElementAt(_random.Next(0, matchingResponse.Responses.Count()));
151-
return UpdateProxyResponse(request, error);
151+
return UpdateProxyResponse(request, error, requestId);
152152
}
153153
else
154154
{
155-
Logger.LogRequest("No matching error response found", MessageType.Skipped, request);
155+
Logger.LogRequest("No matching error response found", MessageType.Skipped, request, requestId);
156156
return null;
157157
}
158158
}
@@ -211,7 +211,7 @@ private ThrottlingInfo ShouldThrottle(HttpRequestMessage request, string throttl
211211
return errorResponse;
212212
}
213213

214-
private HttpResponseMessage UpdateProxyResponse(HttpRequestMessage request, GenericErrorResponseResponse error)
214+
private HttpResponseMessage UpdateProxyResponse(HttpRequestMessage request, GenericErrorResponseResponse error, string requestId)
215215
{
216216
var headers = new List<GenericErrorResponseHeader>();
217217
if (error.Headers is not null)
@@ -288,7 +288,7 @@ error.Headers is not null &&
288288
}
289289
}
290290

291-
Logger.LogRequest($"{error.StatusCode} {statusCode}", MessageType.Chaos, request);
291+
Logger.LogRequest($"{error.StatusCode} {statusCode}", MessageType.Chaos, request, requestId);
292292
return response;
293293
}
294294

DevProxy.Plugins/Behavior/GraphRandomErrorPlugin.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -173,14 +173,14 @@ public override void OptionsLoaded(OptionsLoadedArgs e)
173173

174174
if (!ProxyUtils.MatchesUrlToWatch(UrlsToWatch, args.Request.RequestUri))
175175
{
176-
Logger.LogRequest("URL not matched", MessageType.Skipped, args.Request);
176+
Logger.LogRequest("URL not matched", MessageType.Skipped, args.Request, args.RequestId);
177177
return Task.FromResult(PluginResponse.Continue());
178178
}
179179

180180
var failMode = ShouldFail();
181181
if (failMode == GraphRandomErrorFailMode.PassThru && Configuration.Rate != 100)
182182
{
183-
Logger.LogRequest("Pass through", MessageType.Skipped, args.Request);
183+
Logger.LogRequest("Pass through", MessageType.Skipped, args.Request, args.RequestId);
184184
return Task.FromResult(PluginResponse.Continue());
185185
}
186186
// If the request is a batch request, we will handle it in BeforeRequestAsync
@@ -192,7 +192,7 @@ public override void OptionsLoaded(OptionsLoadedArgs e)
192192
else
193193
{
194194
//Logger.LogRequest("Pass through", MessageType.Skipped, args.Request);
195-
return Task.FromResult(PluginResponse.Respond(FailResponse(args.Request)));
195+
return Task.FromResult(PluginResponse.Respond(FailResponse(args.Request, args.RequestId)));
196196
}
197197
};
198198

@@ -245,7 +245,7 @@ public override void OptionsLoaded(OptionsLoadedArgs e)
245245
// UpdateProxyResponse(e, errorStatus);
246246
//}
247247

248-
private HttpResponseMessage FailResponse(HttpRequestMessage e)
248+
private HttpResponseMessage FailResponse(HttpRequestMessage e, string requestId)
249249
{
250250
var methodStatusCodes = _methodStatusCode[e.Method.Method ?? "GET"];
251251
var errorStatus = methodStatusCodes[_random.Next(0, methodStatusCodes.Length)];
@@ -263,7 +263,7 @@ private HttpResponseMessage FailResponse(HttpRequestMessage e)
263263
}
264264
}), ProxyUtils.JsonSerializerOptions))
265265
};
266-
Logger.LogRequest($"{(int)errorStatus} {errorStatus}", MessageType.Chaos, e);
266+
Logger.LogRequest($"{(int)errorStatus} {errorStatus}", MessageType.Chaos, e, requestId);
267267
return response;
268268
}
269269

DevProxy.Plugins/Behavior/LanguageModelFailurePlugin.cs

Lines changed: 46 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -47,51 +47,43 @@ public sealed class LanguageModelFailurePlugin(
4747

4848
public override string Name => nameof(LanguageModelFailurePlugin);
4949

50-
public override async Task BeforeRequestAsync(ProxyRequestArgs e, CancellationToken cancellationToken)
50+
public override Func<RequestArguments, CancellationToken, Task<PluginResponse>>? OnRequestAsync => async (args, cancellationToken) =>
5151
{
52-
Logger.LogTrace("{Method} called", nameof(BeforeRequestAsync));
52+
Logger.LogTrace("{Method} called", nameof(OnRequestAsync));
5353

54-
ArgumentNullException.ThrowIfNull(e);
55-
56-
if (!e.HasRequestUrlMatch(UrlsToWatch))
57-
{
58-
Logger.LogRequest("URL not matched", MessageType.Skipped, new LoggingContext(e.Session));
59-
return;
60-
}
61-
if (e.ResponseState.HasBeenSet)
54+
if (!ProxyUtils.MatchesUrlToWatch(UrlsToWatch, args.Request.RequestUri))
6255
{
63-
Logger.LogRequest("Response already set", MessageType.Skipped, new LoggingContext(e.Session));
64-
return;
56+
Logger.LogRequest("URL not matched", MessageType.Skipped, args.Request, args.RequestId);
57+
return PluginResponse.Continue();
6558
}
6659

67-
var request = e.Session.HttpClient.Request;
68-
if (request.Method is null ||
69-
!request.Method.Equals("POST", StringComparison.OrdinalIgnoreCase) ||
70-
!request.HasBody)
60+
if (args.Request.Method != HttpMethod.Post || args.Request.Content == null)
7161
{
72-
Logger.LogRequest("Request is not a POST request with a body", MessageType.Skipped, new(e.Session));
73-
return;
62+
Logger.LogRequest("Request is not a POST request with a body", MessageType.Skipped, args.Request, args.RequestId);
63+
return PluginResponse.Continue();
7464
}
7565

76-
if (!TryGetOpenAIRequest(request.BodyString, out var openAiRequest))
66+
var requestBody = await args.Request.Content.ReadAsStringAsync(cancellationToken);
67+
if (!TryGetOpenAIRequest(requestBody, out var openAiRequest))
7768
{
78-
Logger.LogRequest("Skipping non-OpenAI request", MessageType.Skipped, new(e.Session));
79-
return;
69+
Logger.LogRequest("Skipping non-OpenAI request", MessageType.Skipped, args.Request, args.RequestId);
70+
return PluginResponse.Continue();
8071
}
8172

8273
var (faultName, faultPrompt) = GetFault();
8374
if (faultPrompt is null)
8475
{
8576
Logger.LogError("Failed to get fault prompt. Passing request as-is.");
86-
return;
77+
return PluginResponse.Continue();
8778
}
8879

80+
string modifiedRequestBody;
8981
if (openAiRequest is OpenAICompletionRequest completionRequest)
9082
{
9183
completionRequest.Prompt += "\n\n" + faultPrompt;
9284
Logger.LogDebug("Modified completion request prompt: {Prompt}", completionRequest.Prompt);
93-
Logger.LogRequest($"Simulating fault {faultName}", MessageType.Chaos, new(e.Session));
94-
e.Session.SetRequestBodyString(JsonSerializer.Serialize(completionRequest, ProxyUtils.JsonSerializerOptions));
85+
Logger.LogRequest($"Simulating fault {faultName}", MessageType.Chaos, args.Request, args.RequestId);
86+
modifiedRequestBody = JsonSerializer.Serialize(completionRequest, ProxyUtils.JsonSerializerOptions);
9587
}
9688
else if (openAiRequest is OpenAIChatCompletionRequest chatRequest)
9789
{
@@ -113,18 +105,43 @@ public override async Task BeforeRequestAsync(ProxyRequestArgs e, CancellationTo
113105
};
114106

115107
Logger.LogDebug("Added fault prompt to messages: {Prompt}", faultPrompt);
116-
Logger.LogRequest($"Simulating fault {faultName}", MessageType.Chaos, new(e.Session));
117-
e.Session.SetRequestBodyString(JsonSerializer.Serialize(newRequest, ProxyUtils.JsonSerializerOptions));
108+
Logger.LogRequest($"Simulating fault {faultName}", MessageType.Chaos, args.Request, args.RequestId);
109+
modifiedRequestBody = JsonSerializer.Serialize(newRequest, ProxyUtils.JsonSerializerOptions);
118110
}
119111
else
120112
{
121113
Logger.LogDebug("Unknown OpenAI request type. Passing request as-is.");
114+
return PluginResponse.Continue();
122115
}
123116

124-
await Task.CompletedTask;
117+
// Create new request with modified body
118+
var modifiedRequest = new HttpRequestMessage(args.Request.Method, args.Request.RequestUri)
119+
{
120+
Content = new StringContent(modifiedRequestBody, System.Text.Encoding.UTF8, "application/json")
121+
};
122+
123+
// Copy headers from original request
124+
foreach (var header in args.Request.Headers)
125+
{
126+
_ = modifiedRequest.Headers.TryAddWithoutValidation(header.Key, header.Value);
127+
}
125128

126-
Logger.LogTrace("Left {Name}", nameof(BeforeRequestAsync));
127-
}
129+
// Copy content headers if they exist
130+
if (args.Request.Content?.Headers != null)
131+
{
132+
foreach (var header in args.Request.Content.Headers)
133+
{
134+
if (!header.Key.Equals("Content-Type", StringComparison.OrdinalIgnoreCase) &&
135+
!header.Key.Equals("Content-Length", StringComparison.OrdinalIgnoreCase))
136+
{
137+
_ = modifiedRequest.Content.Headers.TryAddWithoutValidation(header.Key, header.Value);
138+
}
139+
}
140+
}
141+
142+
Logger.LogTrace("Left {Name}", nameof(OnRequestAsync));
143+
return PluginResponse.Continue(modifiedRequest);
144+
};
128145

129146
private bool TryGetOpenAIRequest(string content, out OpenAIRequest? request)
130147
{

DevProxy.Plugins/Behavior/LatencyPlugin.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
using DevProxy.Abstractions.Plugins;
66
using DevProxy.Abstractions.Proxy;
7+
using DevProxy.Abstractions.Utils;
78
using Microsoft.Extensions.Configuration;
89
using Microsoft.Extensions.Logging;
910

@@ -32,22 +33,21 @@ public sealed class LatencyPlugin(
3233

3334
public override string Name => nameof(LatencyPlugin);
3435

35-
public override async Task BeforeRequestAsync(ProxyRequestArgs e, CancellationToken cancellationToken)
36+
public override Func<RequestArguments, CancellationToken, Task<PluginResponse>>? OnRequestAsync => async (args, cancellationToken) =>
3637
{
37-
Logger.LogTrace("{Method} called", nameof(BeforeRequestAsync));
38+
Logger.LogTrace("{Method} called", nameof(OnRequestAsync));
3839

39-
ArgumentNullException.ThrowIfNull(e);
40-
41-
if (!e.HasRequestUrlMatch(UrlsToWatch))
40+
if (!ProxyUtils.MatchesUrlToWatch(UrlsToWatch, args.Request.RequestUri))
4241
{
43-
Logger.LogRequest("URL not matched", MessageType.Skipped, new(e.Session));
44-
return;
42+
Logger.LogRequest("URL not matched", MessageType.Skipped, args.Request, args.RequestId);
43+
return PluginResponse.Continue();
4544
}
4645

4746
var delay = _random.Next(Configuration.MinMs, Configuration.MaxMs);
48-
Logger.LogRequest($"Delaying request for {delay}ms", MessageType.Chaos, new(e.Session));
47+
Logger.LogRequest($"Delaying request for {delay}ms", MessageType.Chaos, args.Request, args.RequestId);
4948
await Task.Delay(delay, cancellationToken);
5049

51-
Logger.LogTrace("Left {Name}", nameof(BeforeRequestAsync));
52-
}
50+
Logger.LogTrace("Left {Name}", nameof(OnRequestAsync));
51+
return PluginResponse.Continue();
52+
};
5353
}

DevProxy.Plugins/Extensions/ApiCenterExtensions.cs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -189,17 +189,19 @@ internal static IEnumerable<string> GetUrls(this Api api)
189189
return apiVersion;
190190
}
191191

192-
// check headers
193-
Debug.Assert(request.Context is not null);
194-
var header = request.Context.Session.HttpClient.Request.Headers.FirstOrDefault(
195-
h =>
196-
(!string.IsNullOrEmpty(apiVersion.Name) && h.Value.Contains(apiVersion.Name, StringComparison.OrdinalIgnoreCase)) ||
197-
(!string.IsNullOrEmpty(apiVersion.Properties?.Title) && h.Value.Contains(apiVersion.Properties.Title, StringComparison.OrdinalIgnoreCase))
198-
);
199-
if (header is not null)
192+
// check headers - use the new Request property instead of Context.Session
193+
if (request.Request?.Headers != null)
200194
{
201-
logger.LogDebug("Version {Version} found in header {Header}", $"{apiVersion.Name}/{apiVersion.Properties?.Title}", header.Name);
202-
return apiVersion;
195+
var header = request.Request.Headers.FirstOrDefault(
196+
h =>
197+
(!string.IsNullOrEmpty(apiVersion.Name) && string.Join(", ", h.Value).Contains(apiVersion.Name, StringComparison.OrdinalIgnoreCase)) ||
198+
(!string.IsNullOrEmpty(apiVersion.Properties?.Title) && string.Join(", ", h.Value).Contains(apiVersion.Properties.Title, StringComparison.OrdinalIgnoreCase))
199+
);
200+
if (header.Key is not null)
201+
{
202+
logger.LogDebug("Version {Version} found in header {Header}", $"{apiVersion.Name}/{apiVersion.Properties?.Title}", header.Key);
203+
return apiVersion;
204+
}
203205
}
204206
}
205207

DevProxy.Plugins/Extensions/OpenApiDocumentExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public static ApiPermissionsInfo CheckMinimalPermissions(this OpenApiDocument op
3232
logger.LogDebug("Checking request {Request}...", methodAndUrl);
3333
var (method, url) = (methodAndUrlChunks[0].ToUpperInvariant(), methodAndUrlChunks[1]);
3434

35-
var scopesFromTheToken = MinimalPermissionsUtils.GetScopesFromToken(request.Context?.Session.HttpClient.Request.Headers.First(h => h.Name.Equals("authorization", StringComparison.OrdinalIgnoreCase)).Value, logger);
35+
var scopesFromTheToken = MinimalPermissionsUtils.GetScopesFromToken(request.Request!.Headers.Authorization?.Parameter, logger);
3636
if (scopesFromTheToken.Length != 0)
3737
{
3838
tokenPermissions.AddRange(scopesFromTheToken);

DevProxy.Plugins/Generation/ApiCenterOnboardingPlugin.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,15 @@ public sealed class ApiCenterOnboardingPlugin(
2929
ILogger<ApiCenterOnboardingPlugin> logger,
3030
ISet<UrlToWatch> urlsToWatch,
3131
IProxyConfiguration proxyConfiguration,
32-
IConfigurationSection pluginConfigurationSection) :
32+
IConfigurationSection pluginConfigurationSection,
33+
IProxyStorage proxyStorage) :
3334
BaseReportingPlugin<ApiCenterOnboardingPluginConfiguration>(
3435
httpClient,
3536
logger,
3637
urlsToWatch,
3738
proxyConfiguration,
38-
pluginConfigurationSection)
39+
pluginConfigurationSection,
40+
proxyStorage)
3941
{
4042
private ApiCenterClient? _apiCenterClient;
4143
private Api[]? _apis;
@@ -181,7 +183,7 @@ public override async Task AfterRecordingStopAsync(RecordingArgs e, Cancellation
181183
{
182184
ExistingApis = [.. existingApis],
183185
NewApis = []
184-
}, e);
186+
});
185187
return;
186188
}
187189

@@ -196,7 +198,7 @@ public override async Task AfterRecordingStopAsync(RecordingArgs e, Cancellation
196198
Method = a.method,
197199
Url = a.url
198200
})]
199-
}, e);
201+
});
200202

201203
var apisPerSchemeAndHost = newApis.GroupBy(x =>
202204
{
@@ -218,7 +220,7 @@ public override async Task AfterRecordingStopAsync(RecordingArgs e, Cancellation
218220
return;
219221
}
220222

221-
var generatedOpenApiSpecs = e.GlobalData.TryGetValue(OpenApiSpecGeneratorPlugin.GeneratedOpenApiSpecsKey, out var specs) ? specs as Dictionary<string, string> : [];
223+
var generatedOpenApiSpecs = ProxyStorage.GlobalData.TryGetValue(OpenApiSpecGeneratorPlugin.GeneratedOpenApiSpecsKey, out var specs) ? specs as Dictionary<string, string> : [];
222224
await CreateApisInApiCenterAsync(apisPerSchemeAndHost, generatedOpenApiSpecs!, cancellationToken);
223225

224226
Logger.LogTrace("Left {Name}", nameof(AfterRecordingStopAsync));

0 commit comments

Comments
 (0)