Skip to content

fix(proxy): 代理字段解析遗漏一种格式 - #3495

Open
tangge233 wants to merge 1 commit into
devfrom
fix/proxy-string-format
Open

fix(proxy): 代理字段解析遗漏一种格式#3495
tangge233 wants to merge 1 commit into
devfrom
fix/proxy-string-format

Conversation

@tangge233

@tangge233 tangge233 commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Summary by Sourcery

通过拓展代理字符串解析方式并收紧应用 HTTP 代理的条件,改进系统代理处理。

Bug Fixes:

  • 处理更多代理字符串格式,包括键值对片段和带协议前缀的地址,避免遗漏有效代理设置。
  • 确保只有在代理已启用且存在有效的 HTTP 代理地址时才应用系统 HTTP 代理。

Enhancements:

  • 将代理解析重构为专门的辅助方法,用于处理键值对片段和地址,并简化 ProxyItem 的表示形式。
Original summary in English

Summary by Sourcery

Improve system proxy handling by broadening proxy string parsing and tightening HTTP proxy application conditions.

Bug Fixes:

  • Handle additional proxy string formats including key-value segments and scheme-prefixed addresses to avoid missing valid proxies.
  • Ensure the system HTTP proxy is only applied when proxy is enabled and a valid HTTP proxy address is present.

Enhancements:

  • Refactor proxy parsing into dedicated helpers for key-value segments and addresses and simplify ProxyItem representation.

@pcl-ce-automation pcl-ce-automation Bot added 🛠️ 等待审查 Pull Request 已完善,等待维护者或负责人进行代码审查 size: M PR 大小评估:中型 labels Aug 4, 2026
@sourcery-ai

sourcery-ai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

重构代理字符串解析,以支持更多格式并改进校验,将逻辑集中到可复用的辅助方法中,并更加严格地控制系统 HTTP 代理的应用。

更新后的系统代理应用顺序图

sequenceDiagram
    participant HttpProxyManager
    participant _GetProxyFromString
    participant _ParseKeyValueSegment
    participant _ParseAddress
    participant _systemWebProxy

    HttpProxyManager->>_GetProxyFromString: _GetProxyFromString(systemProxyString)
    alt proxyString contains =
        _GetProxyFromString->>_ParseKeyValueSegment: _ParseKeyValueSegment(segment)
        _ParseKeyValueSegment->>_ParseAddress: _ParseAddress(address, protocol)
        _ParseAddress-->>_GetProxyFromString: ProxyItem
    else proxyString without =
        _GetProxyFromString->>_ParseAddress: _ParseAddress(proxyString.Trim())
        _ParseAddress-->>_GetProxyFromString: ProxyItem or null
    end

    HttpProxyManager->>HttpProxyManager: selectedProxy = FirstOrDefault(Protocol == Http)
    HttpProxyManager->>_systemWebProxy: set Address based on selectedProxy and isSystemProxyEnabled
Loading

File-Level Changes

Change Details Files
重构代理项表示方式和解析辅助方法,以同时支持键值对格式和独立地址格式,并对地址进行规范化处理。
  • 将 ProxyItem 从带字段的可变 record 改为不可变的密封位置 record。
  • protocol=address 片段的解析提取到专用的 _ParseKeyValueSegment 辅助方法中,用于校验片段格式并忽略无效条目。
  • 将单个地址(带或不带 scheme)的解析提取到 _ParseAddress 中,对基于 URI 的输入规范化为 host:port,并在非 URI 的 host:port 字符串中默认协议为 Http。
  • 调整 _GetProxyFromString,使其委托给新的辅助方法,处理片段的去空格,并根据解析出的条目或单个地址正确返回数组。
PCL.Core/IO/Net/Http/HttpProxyManager.cs
收紧系统代理应用逻辑,仅在启用系统代理时应用有效的 HTTP 代理。
  • 使用 FirstOrDefault 结合直接协议比较简化 HTTP 代理的选择。
  • 更新 _systemWebProxy.Address 赋值逻辑,在创建 Uri 之前同时检查系统代理启用标志和所选代理的有效性。
  • 移除基于 HTTP 协议存在与否以及空数组的冗余代理过滤逻辑。
PCL.Core/IO/Net/Http/HttpProxyManager.cs

Tips and commands

Interacting with Sourcery

  • 触发新的评审: 在 pull request 中评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的评审评论。
  • 从评审评论生成 GitHub issue: 通过回复评审评论来让 Sourcery 从该评论创建一个 issue。你也可以在评审评论下回复 @sourcery-ai issue 来从该评论创建 issue。
  • 生成 pull request 标题: 在 pull request 标题中任意位置写入 @sourcery-ai,即可随时生成标题。你也可以在 pull request 中评论 @sourcery-ai title 来(重新)生成标题。
  • 生成 pull request 摘要: 在 pull request 正文中任意位置写入 @sourcery-ai summary,即可在指定位置生成 PR 摘要。你也可以在 pull request 中评论 @sourcery-ai summary 来随时(重新)生成摘要。
  • 生成评审者指南: 在 pull request 中评论 @sourcery-ai guide,即可随时(重新)生成评审者指南。
  • 解决所有 Sourcery 评论: 在 pull request 中评论 @sourcery-ai resolve,即可解决所有 Sourcery 评论。适用于你已经处理完所有评论且不再需要看到它们的情况。
  • 撤销所有 Sourcery 评审: 在 pull request 中评论 @sourcery-ai dismiss,即可撤销所有现有的 Sourcery 评审。如果你想从一个新的评审开始,这将非常有用——别忘了评论 @sourcery-ai review 来触发新的评审!

Customizing Your Experience

访问你的 dashboard 以:

  • 启用或禁用评审功能,例如 Sourcery 生成的 pull request 摘要、评审者指南等。
  • 更改评审语言。
  • 添加、移除或编辑自定义评审指引。
  • 调整其他评审设置。

Getting Help

Original review guide in English

Reviewer's Guide

Refactors proxy string parsing to support additional formats and improve validation, centralizing the logic into reusable helpers and tightening application of the system HTTP proxy.

Sequence diagram for updated system proxy application

sequenceDiagram
    participant HttpProxyManager
    participant _GetProxyFromString
    participant _ParseKeyValueSegment
    participant _ParseAddress
    participant _systemWebProxy

    HttpProxyManager->>_GetProxyFromString: _GetProxyFromString(systemProxyString)
    alt proxyString contains =
        _GetProxyFromString->>_ParseKeyValueSegment: _ParseKeyValueSegment(segment)
        _ParseKeyValueSegment->>_ParseAddress: _ParseAddress(address, protocol)
        _ParseAddress-->>_GetProxyFromString: ProxyItem
    else proxyString without =
        _GetProxyFromString->>_ParseAddress: _ParseAddress(proxyString.Trim())
        _ParseAddress-->>_GetProxyFromString: ProxyItem or null
    end

    HttpProxyManager->>HttpProxyManager: selectedProxy = FirstOrDefault(Protocol == Http)
    HttpProxyManager->>_systemWebProxy: set Address based on selectedProxy and isSystemProxyEnabled
Loading

File-Level Changes

Change Details Files
Refactor proxy item representation and parsing helpers to support both key-value and standalone address formats while normalizing addresses.
  • Change ProxyItem from a mutable record with fields to an immutable sealed positional record.
  • Extract parsing of protocol=address segments into a dedicated _ParseKeyValueSegment helper that validates segment format and ignores invalid entries.
  • Extract parsing of individual addresses (with or without scheme) into _ParseAddress, normalizing URI-based inputs to host:port and defaulting protocol to Http for non-URI host:port strings.
  • Adjust _GetProxyFromString to delegate to the new helpers, handle segments with trimming, and correctly return arrays based on parsed items or a single address.
PCL.Core/IO/Net/Http/HttpProxyManager.cs
Tighten system proxy application logic to only apply a valid HTTP proxy when system proxy is enabled.
  • Simplify selection of HTTP proxy using FirstOrDefault with direct protocol comparison.
  • Update _systemWebProxy.Address assignment to check both system proxy enabled flag and selected proxy validity before creating Uri.
  • Remove redundant proxy filtering logic based on HTTP protocol presence and empty arrays.
PCL.Core/IO/Net/Http/HttpProxyManager.cs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 1 个问题,并给出了一些整体性的反馈:

  • RefreshSystemProxy 中,当没有找到 HTTP 代理时,你不再将 isSystemProxyEnabled 重置为 0;如果其他逻辑依赖这个标志来准确反映有效的 HTTP 代理可用性,建议考虑保留之前的行为,或者进一步澄清它的语义。
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
-`RefreshSystemProxy` 中,当没有找到 HTTP 代理时,你不再将 `isSystemProxyEnabled` 重置为 0;如果其他逻辑依赖这个标志来准确反映有效的 HTTP 代理可用性,建议考虑保留之前的行为,或者进一步澄清它的语义。

## Individual Comments

### Comment 1
<location path="PCL.Core/IO/Net/Http/HttpProxyManager.cs" line_range="90-93" />
<code_context>
-        }
-        else
+        // 能解析出主机名的地址,规范化为 host:port
+        if (Uri.TryCreate(address, new UriCreationOptions(), out var uri) && !uri.Host.IsNullOrEmpty())
         {
-            ret.Add(new ProxyItem { Protocol = ProxyProtocol.Http, Address = proxyString.Trim() });
+            var hostPort = uri.Port > 0 ? $"{uri.Host}:{uri.Port}" : uri.Host;
+            return new ProxyItem(protocol ?? _ParseProtocol(uri.Scheme), hostPort);
         }

</code_context>
<issue_to_address>
**suggestion:** 当 URI 没有主机名时(例如文件/系统 URI),这种行为对代理而言可能并不理想。

当 `Uri.TryCreate` 成功但 `Host` 为空时,当前代码会退回到将 `address` 视为原始代理端点。对于代理配置来说,像 `file:///c:/path` 这样的值在实际效果上是无效的,但仍会被通过,并在后续用于构建 HTTP URI。为了避免这种错误配置传播到 HTTP 栈,作为代理而言,对主机名为空的 URI 进行拒绝(例如返回 `null`)会更安全。

建议实现如下:

```csharp
        // 能解析出主机名的地址,规范化为 host:port

    private sealed record ProxyItem(ProxyProtocol Protocol, string Address);

    // 返回 ProxyItem?,以便在 URI 有效但没有主机名时能够返回 null
    private static ProxyItem? _ParseProxyItem(string address, ProxyProtocol? protocol = null)
    {
        // 能解析出主机名的地址,规范化为 host:port
        if (Uri.TryCreate(address, new UriCreationOptions(), out var uri))
        {
            if (uri.Host.IsNullOrEmpty())
            {
                // 对于没有主机名的 URI(例如 file://),认为是无效的代理配置
                return null;
            }

            var hostPort = uri.Port > 0 ? $"{uri.Host}:{uri.Port}" : uri.Host;
            return new ProxyItem(protocol ?? _ParseProtocol(uri.Scheme), hostPort);
        }

        // 非 URI 格式的地址,按原始代理端点处理
        return new ProxyItem(protocol ?? ProxyProtocol.Http, address.Trim());
    }

    private static ProxyItem[] _GetProxyFromString(string? proxyString)
    {
        if (proxyString.IsNullOrWhiteSpace()) return [];

```

要完整应用这一改动,需要对文件的其他部分进行更新:

1. 用对 `_ParseProxyItem` 的调用替换当前内联的 `Uri.TryCreate` + `Host` 处理逻辑,并且:
   - 跳过 `_ParseProxyItem` 返回 `null` 的条目(即不要将它们包含在 `ProxyItem[]` 结果中)。
2. 更新目前在 `Uri.TryCreate` 分支中直接返回 `ProxyItem` 的方法,使其改用 `_ParseProxyItem`,或者如果需要向上传递 `null`,则将其签名更新为返回 `ProxyItem?`。
3. 确保在未显式指定协议时,能像新的辅助方法所示那样一致地使用 `_ParseProtocol` 和 `ProxyProtocol.Http`。
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
请帮我变得更有用!请在每条评论上点击 👍 或 👎,我会根据这些反馈来改进后续的评审。
Original comment in English

Hey - I've found 1 issue, and left some high level feedback:

  • In RefreshSystemProxy, you no longer reset isSystemProxyEnabled to 0 when no HTTP proxy is found; if other logic relies on this flag accurately reflecting effective HTTP proxy availability, consider preserving the previous behavior or clarifying the semantics.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `RefreshSystemProxy`, you no longer reset `isSystemProxyEnabled` to 0 when no HTTP proxy is found; if other logic relies on this flag accurately reflecting effective HTTP proxy availability, consider preserving the previous behavior or clarifying the semantics.

## Individual Comments

### Comment 1
<location path="PCL.Core/IO/Net/Http/HttpProxyManager.cs" line_range="90-93" />
<code_context>
-        }
-        else
+        // 能解析出主机名的地址,规范化为 host:port
+        if (Uri.TryCreate(address, new UriCreationOptions(), out var uri) && !uri.Host.IsNullOrEmpty())
         {
-            ret.Add(new ProxyItem { Protocol = ProxyProtocol.Http, Address = proxyString.Trim() });
+            var hostPort = uri.Port > 0 ? $"{uri.Host}:{uri.Port}" : uri.Host;
+            return new ProxyItem(protocol ?? _ParseProtocol(uri.Scheme), hostPort);
         }

</code_context>
<issue_to_address>
**suggestion:** The behavior when the URI has no host (e.g., file/system URIs) may not be ideal for proxies.

When `Uri.TryCreate` succeeds but `Host` is empty, the code currently falls back to treating `address` as a raw proxy endpoint. For proxy settings, values like `file:///c:/path` are effectively invalid but would still pass through and later be used to build an HTTP URI. It would be safer to reject URIs with an empty host for proxy purposes (e.g., return `null`) so such misconfigurations don’t propagate into the HTTP stack.

Suggested implementation:

```csharp
        // 能解析出主机名的地址,规范化为 host:port

    private sealed record ProxyItem(ProxyProtocol Protocol, string Address);

    // 返回 ProxyItem?,以便在 URI 有效但没有主机名时能够返回 null
    private static ProxyItem? _ParseProxyItem(string address, ProxyProtocol? protocol = null)
    {
        // 能解析出主机名的地址,规范化为 host:port
        if (Uri.TryCreate(address, new UriCreationOptions(), out var uri))
        {
            if (uri.Host.IsNullOrEmpty())
            {
                // 对于没有主机名的 URI(例如 file://),认为是无效的代理配置
                return null;
            }

            var hostPort = uri.Port > 0 ? $"{uri.Host}:{uri.Port}" : uri.Host;
            return new ProxyItem(protocol ?? _ParseProtocol(uri.Scheme), hostPort);
        }

        // 非 URI 格式的地址,按原始代理端点处理
        return new ProxyItem(protocol ?? ProxyProtocol.Http, address.Trim());
    }

    private static ProxyItem[] _GetProxyFromString(string? proxyString)
    {
        if (proxyString.IsNullOrWhiteSpace()) return [];

```

To fully apply this change, the rest of the file needs to be updated to:

1. Replace the existing inline `Uri.TryCreate` + `Host` handling logic with calls to `_ParseProxyItem`, and:
   - Skip entries where `_ParseProxyItem` returns `null` (i.e., do not include them in the `ProxyItem[]` result).
2. Update any current method that was returning `ProxyItem` directly from the `Uri.TryCreate` branch to use `_ParseProxyItem` instead, or update its signature to return `ProxyItem?` if it needs to propagate the `null`.
3. Ensure that `_ParseProtocol` and `ProxyProtocol.Http` are used consistently when the protocol is not explicitly specified, as shown in the new helper method.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +90 to +93
if (Uri.TryCreate(address, new UriCreationOptions(), out var uri) && !uri.Host.IsNullOrEmpty())
{
ret.Add(new ProxyItem { Protocol = ProxyProtocol.Http, Address = proxyString.Trim() });
var hostPort = uri.Port > 0 ? $"{uri.Host}:{uri.Port}" : uri.Host;
return new ProxyItem(protocol ?? _ParseProtocol(uri.Scheme), hostPort);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: 当 URI 没有主机名时(例如文件/系统 URI),这种行为对代理而言可能并不理想。

Uri.TryCreate 成功但 Host 为空时,当前代码会退回到将 address 视为原始代理端点。对于代理配置来说,像 file:///c:/path 这样的值在实际效果上是无效的,但仍会被通过,并在后续用于构建 HTTP URI。为了避免这种错误配置传播到 HTTP 栈,作为代理而言,对主机名为空的 URI 进行拒绝(例如返回 null)会更安全。

建议实现如下:

        // 能解析出主机名的地址,规范化为 host:port

    private sealed record ProxyItem(ProxyProtocol Protocol, string Address);

    // 返回 ProxyItem?,以便在 URI 有效但没有主机名时能够返回 null
    private static ProxyItem? _ParseProxyItem(string address, ProxyProtocol? protocol = null)
    {
        // 能解析出主机名的地址,规范化为 host:port
        if (Uri.TryCreate(address, new UriCreationOptions(), out var uri))
        {
            if (uri.Host.IsNullOrEmpty())
            {
                // 对于没有主机名的 URI(例如 file://),认为是无效的代理配置
                return null;
            }

            var hostPort = uri.Port > 0 ? $"{uri.Host}:{uri.Port}" : uri.Host;
            return new ProxyItem(protocol ?? _ParseProtocol(uri.Scheme), hostPort);
        }

        // 非 URI 格式的地址,按原始代理端点处理
        return new ProxyItem(protocol ?? ProxyProtocol.Http, address.Trim());
    }

    private static ProxyItem[] _GetProxyFromString(string? proxyString)
    {
        if (proxyString.IsNullOrWhiteSpace()) return [];

要完整应用这一改动,需要对文件的其他部分进行更新:

  1. 用对 _ParseProxyItem 的调用替换当前内联的 Uri.TryCreate + Host 处理逻辑,并且:
    • 跳过 _ParseProxyItem 返回 null 的条目(即不要将它们包含在 ProxyItem[] 结果中)。
  2. 更新目前在 Uri.TryCreate 分支中直接返回 ProxyItem 的方法,使其改用 _ParseProxyItem,或者如果需要向上传递 null,则将其签名更新为返回 ProxyItem?
  3. 确保在未显式指定协议时,能像新的辅助方法所示那样一致地使用 _ParseProtocolProxyProtocol.Http
Original comment in English

suggestion: The behavior when the URI has no host (e.g., file/system URIs) may not be ideal for proxies.

When Uri.TryCreate succeeds but Host is empty, the code currently falls back to treating address as a raw proxy endpoint. For proxy settings, values like file:///c:/path are effectively invalid but would still pass through and later be used to build an HTTP URI. It would be safer to reject URIs with an empty host for proxy purposes (e.g., return null) so such misconfigurations don’t propagate into the HTTP stack.

Suggested implementation:

        // 能解析出主机名的地址,规范化为 host:port

    private sealed record ProxyItem(ProxyProtocol Protocol, string Address);

    // 返回 ProxyItem?,以便在 URI 有效但没有主机名时能够返回 null
    private static ProxyItem? _ParseProxyItem(string address, ProxyProtocol? protocol = null)
    {
        // 能解析出主机名的地址,规范化为 host:port
        if (Uri.TryCreate(address, new UriCreationOptions(), out var uri))
        {
            if (uri.Host.IsNullOrEmpty())
            {
                // 对于没有主机名的 URI(例如 file://),认为是无效的代理配置
                return null;
            }

            var hostPort = uri.Port > 0 ? $"{uri.Host}:{uri.Port}" : uri.Host;
            return new ProxyItem(protocol ?? _ParseProtocol(uri.Scheme), hostPort);
        }

        // 非 URI 格式的地址,按原始代理端点处理
        return new ProxyItem(protocol ?? ProxyProtocol.Http, address.Trim());
    }

    private static ProxyItem[] _GetProxyFromString(string? proxyString)
    {
        if (proxyString.IsNullOrWhiteSpace()) return [];

To fully apply this change, the rest of the file needs to be updated to:

  1. Replace the existing inline Uri.TryCreate + Host handling logic with calls to _ParseProxyItem, and:
    • Skip entries where _ParseProxyItem returns null (i.e., do not include them in the ProxyItem[] result).
  2. Update any current method that was returning ProxyItem directly from the Uri.TryCreate branch to use _ParseProxyItem instead, or update its signature to return ProxyItem? if it needs to propagate the null.
  3. Ensure that _ParseProtocol and ProxyProtocol.Http are used consistently when the protocol is not explicitly specified, as shown in the new helper method.

XueL1ng added a commit to PCL-Nex-Developer/PCL2-Nex that referenced this pull request Aug 26, 2026
XueL1ng added a commit to PCL-Nex-Developer/PCL2-Nex that referenced this pull request Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size: M PR 大小评估:中型 🛠️ 等待审查 Pull Request 已完善,等待维护者或负责人进行代码审查

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant