Skip to content

Commit 10eff8a

Browse files
Merge branch 'main' into v028
2 parents d591c42 + 102b51e commit 10eff8a

File tree

11 files changed

+224
-108
lines changed

11 files changed

+224
-108
lines changed

.github/workflows/codeql-analysis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ jobs:
5353

5454
# Initializes the CodeQL tools for scanning.
5555
- name: Initialize CodeQL
56-
uses: github/codeql-action/init@28deaeda66b76a05916b6923827895f2b14ab387 # v3.28.16
56+
uses: github/codeql-action/init@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
5757
with:
5858
languages: ${{ matrix.language }}
5959
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -81,4 +81,4 @@ jobs:
8181
run: dotnet build --no-restore -c Release
8282

8383
- name: Perform CodeQL Analysis
84-
uses: github/codeql-action/analyze@28deaeda66b76a05916b6923827895f2b14ab387 # v3.28.16
84+
uses: github/codeql-action/analyze@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17

dev-proxy-abstractions/CommandLineExtensions.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,37 @@ public static class CommandLineExtensions
2121

2222
return parseResult.GetValueForOption(option);
2323
}
24+
25+
private static string ByName<T>(T symbol) where T : Symbol => symbol.Name;
26+
27+
public static IEnumerable<T> OrderByName<T>(this IEnumerable<T> symbols) where T : Symbol
28+
{
29+
ArgumentNullException.ThrowIfNull(symbols);
30+
31+
return symbols.OrderBy(ByName, StringComparer.Ordinal);
32+
}
33+
34+
public static Command AddCommands(this Command command, IEnumerable<Command> subcommands)
35+
{
36+
ArgumentNullException.ThrowIfNull(command);
37+
ArgumentNullException.ThrowIfNull(subcommands);
38+
39+
foreach (var subcommand in subcommands)
40+
{
41+
command.AddCommand(subcommand);
42+
}
43+
return command;
44+
}
45+
46+
public static Command AddOptions(this Command command, IEnumerable<Option> options)
47+
{
48+
ArgumentNullException.ThrowIfNull(command);
49+
ArgumentNullException.ThrowIfNull(options);
50+
51+
foreach (var option in options)
52+
{
53+
command.AddOption(option);
54+
}
55+
return command;
56+
}
2457
}

dev-proxy-plugins/Inspection/DevToolsPlugin.cs

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -148,23 +148,14 @@ private void InitInspector()
148148
return;
149149
}
150150

151-
// find if the process is already running
152-
var processes = GetBrowserProcesses(browserPath);
153-
154-
if (processes.Any())
155-
{
156-
var ids = string.Join(", ", processes.Select(p => p.Id.ToString()));
157-
Logger.LogError("Found existing browser process {processName} with IDs {processIds}. Could not start {plugin}. Please close existing browser processes and restart Dev Proxy", browserPath, ids, Name);
158-
return;
159-
}
160-
161151
var port = GetFreePort();
162152
webSocket = new WebSocketServer(port, Logger);
163153
webSocket.MessageReceived += SocketMessageReceived;
164154
_ = webSocket.StartAsync();
165155

166156
var inspectionUrl = $"http://localhost:9222/devtools/inspector.html?ws=localhost:{port}";
167-
var args = $"{inspectionUrl} --remote-debugging-port=9222 --profile-directory=devproxy";
157+
var profilePath = Path.Combine(Path.GetTempPath(), "devtools-devproxy");
158+
var args = $"{inspectionUrl} --remote-debugging-port=9222 --user-data-dir=\"{profilePath}\"";
168159

169160
Logger.LogInformation("{name} available at {inspectionUrl}", Name, inspectionUrl);
170161

dev-proxy-plugins/Mocks/MockResponsePlugin.cs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
using DevProxy.Plugins.Behavior;
1616
using System.Text;
1717
using Microsoft.Extensions.Logging;
18+
using System.Collections.Concurrent;
1819

1920
namespace DevProxy.Plugins.Mocks;
2021

@@ -42,7 +43,7 @@ public class MockResponsePlugin(IPluginEvents pluginEvents, IProxyContext contex
4243
private IProxyConfiguration? _proxyConfiguration;
4344
// tracks the number of times a mock has been applied
4445
// used in combination with mocks that have an Nth property
45-
private readonly Dictionary<string, int> _appliedMocks = [];
46+
private readonly ConcurrentDictionary<string, int> _appliedMocks = [];
4647

4748
public override Option[] GetOptions()
4849
{
@@ -243,12 +244,7 @@ _configuration.Mocks is null ||
243244

244245
if (mockResponse is not null && mockResponse.Request is not null)
245246
{
246-
if (!_appliedMocks.TryGetValue(mockResponse.Request.Url, out int value))
247-
{
248-
value = 0;
249-
_appliedMocks.Add(mockResponse.Request.Url, value);
250-
}
251-
_appliedMocks[mockResponse.Request.Url] = ++value;
247+
_appliedMocks.AddOrUpdate(mockResponse.Request.Url, 1, (_, value) => ++value);
252248
}
253249

254250
return mockResponse;

dev-proxy/Logging/ProxyConsoleFormatter.cs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
using System.Collections.Concurrent;
56
using System.Text;
67
using DevProxy.Abstractions;
78
using Microsoft.Extensions.Logging.Abstractions;
@@ -17,7 +18,7 @@ public class ProxyConsoleFormatter : ConsoleFormatter
1718
private const string _boxBottomLeft = "\u2570 ";
1819
// used to align single-line messages
1920
private const string _boxSpacing = " ";
20-
private readonly Dictionary<int, List<RequestLog>> _requestLogs = [];
21+
private readonly ConcurrentDictionary<int, List<RequestLog>> _requestLogs = [];
2122
private readonly ProxyConsoleFormatterOptions _options;
2223
const string labelSpacing = " ";
2324
// label length + 2
@@ -62,24 +63,21 @@ private void LogRequest(RequestLog requestLog, string category, IExternalScopePr
6263
{
6364
if (messageType == MessageType.FinishedProcessingRequest)
6465
{
65-
var lastMessage = _requestLogs[requestId.Value].Last();
6666
// log all request logs for the request
67-
foreach (var log in _requestLogs[requestId.Value])
67+
var currentRequestLogs = _requestLogs[requestId.Value];
68+
var lastIndex = currentRequestLogs.Count - 1;
69+
for (var i = 0; i < currentRequestLogs.Count; ++i)
6870
{
69-
WriteLogMessageBoxedWithInvertedLabels(log, scopeProvider, textWriter, log == lastMessage);
71+
var log = currentRequestLogs[i];
72+
WriteLogMessageBoxedWithInvertedLabels(log, scopeProvider, textWriter, i == lastIndex);
7073
}
71-
_requestLogs.Remove(requestId.Value);
74+
_requestLogs.Remove(requestId.Value, out _);
7275
}
7376
else
7477
{
7578
// buffer request logs until the request is finished processing
76-
if (!_requestLogs.TryGetValue(requestId.Value, out List<RequestLog>? value))
77-
{
78-
value = ([]);
79-
_requestLogs[requestId.Value] = value;
80-
}
81-
8279
requestLog.PluginName = category == DefaultCategoryName ? null : category;
80+
var value = _requestLogs.GetOrAdd(requestId.Value, []);
8381
value.Add(requestLog);
8482
}
8583
}

dev-proxy/Program.cs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,6 @@
4141
IProxyContext context = new ProxyContext(ProxyCommandHandler.Configuration, ProxyEngine.Certificate, lmClient);
4242
ProxyHost proxyHost = new();
4343

44-
// this is where the root command is created which contains all commands and subcommands
45-
RootCommand rootCommand = proxyHost.GetRootCommand(logger);
46-
4744
// store the global options that are created automatically for us
4845
// rootCommand doesn't return the global options, so we have to store them manually
4946
string[] globalOptions = ["--version"];
@@ -62,20 +59,22 @@
6259
// load plugins to get their options and commands
6360
var pluginLoader = new PluginLoader(isDiscover, logger, loggerFactory);
6461
PluginLoaderResult loaderResults = await pluginLoader.LoadPluginsAsync(pluginEvents, context);
65-
var options = loaderResults.ProxyPlugins
62+
63+
var pluginOptions = loaderResults.ProxyPlugins
6664
.SelectMany(p => p.GetOptions())
6765
// remove duplicates by comparing the option names
6866
.GroupBy(o => o.Name)
6967
.Select(g => g.First())
70-
.ToList();
71-
options.ForEach(rootCommand.AddOption);
72-
// register all plugin commands
73-
loaderResults.ProxyPlugins
68+
.ToArray();
69+
70+
var pluginCommands = loaderResults.ProxyPlugins
7471
.SelectMany(p => p.GetCommands())
75-
.ToList()
76-
.ForEach(rootCommand.AddCommand);
72+
.ToArray();
73+
74+
// this is where the root command is created which contains all commands and subcommands
75+
RootCommand rootCommand = proxyHost.CreateRootCommand(logger, pluginOptions, pluginCommands);
7776

78-
// get the list of available subcommands
77+
// get the list of available subcommand's names
7978
var subCommands = rootCommand.Children.OfType<Command>().Select(c => c.Name).ToArray();
8079

8180
// check if any of the subcommands are present
@@ -132,7 +131,7 @@
132131
pluginEvents.RaiseInit(new InitArgs());
133132
}
134133

135-
rootCommand.Handler = proxyHost.GetCommandHandler(pluginEvents, [.. options], loaderResults.UrlsToWatch, logger);
134+
rootCommand.Handler = proxyHost.GetCommandHandler(pluginEvents, [.. pluginOptions], loaderResults.UrlsToWatch, logger);
136135
var exitCode = await rootCommand.InvokeAsync(args);
137136
loggerFactory.Dispose();
138137
Environment.Exit(exitCode);

dev-proxy/ProxyEngine.cs

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

55
using DevProxy.Abstractions;
66
using Microsoft.VisualStudio.Threading;
7+
using System.Collections.Concurrent;
78
using System.Diagnostics;
89
using System.Net;
910
using System.Security.Cryptography.X509Certificates;
@@ -38,7 +39,7 @@ public class ProxyEngine(IProxyConfiguration config, ISet<UrlToWatch> urlsToWatc
3839
private readonly IProxyState _proxyState = proxyState ?? throw new ArgumentNullException(nameof(proxyState));
3940
// Dictionary for plugins to store data between requests
4041
// the key is HashObject of the SessionEventArgs object
41-
private readonly Dictionary<int, Dictionary<string, object>> _pluginData = [];
42+
private readonly ConcurrentDictionary<int, Dictionary<string, object>> _pluginData = [];
4243
private InactivityTimer? _inactivityTimer;
4344

4445
public static X509Certificate2? Certificate => _proxyServer?.CertificateManager.RootCertificate;
@@ -469,7 +470,10 @@ async Task OnRequestAsync(object sender, SessionEventArgs e)
469470
if (IsProxiedHost(e.HttpClient.Request.RequestUri.Host) &&
470471
IsIncludedByHeaders(e.HttpClient.Request.Headers))
471472
{
472-
_pluginData.Add(e.GetHashCode(), []);
473+
if (!_pluginData.TryAdd(e.GetHashCode(), []))
474+
{
475+
throw new Exception($"Unable to initialize the plugin data storage for hash key {e.GetHashCode()}");
476+
}
473477
var responseState = new ResponseState();
474478
var proxyRequestArgs = new ProxyRequestArgs(e, responseState)
475479
{
@@ -607,7 +611,7 @@ async Task OnAfterResponseAsync(object sender, SessionEventArgs e)
607611
if (!proxyResponseArgs.HasRequestUrlMatch(_urlsToWatch))
608612
{
609613
// clean up
610-
_pluginData.Remove(e.GetHashCode());
614+
_pluginData.Remove(e.GetHashCode(), out _);
611615
return;
612616
}
613617

@@ -623,7 +627,7 @@ async Task OnAfterResponseAsync(object sender, SessionEventArgs e)
623627
_logger.LogRequest(message, MessageType.FinishedProcessingRequest, new LoggingContext(e));
624628

625629
// clean up
626-
_pluginData.Remove(e.GetHashCode());
630+
_pluginData.Remove(e.GetHashCode(), out _);
627631
}
628632
}
629633

0 commit comments

Comments
 (0)