You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Golang中检测WebSocket代理连接与认证错误的方法

如何在Go的WSS客户端中区分代理连接错误与代理认证错误

我有一个通过代理建立WSS连接的客户端,目前已经能检测代理连接的TCP类错误,但不知道如何统一识别不同代理返回的认证错误(比如Tiny Proxy返回"Unauthorized",Squid返回"Proxy Authentication required")。

现有客户端代码片段:

conn, _, err := websocket.Dial(ctx, "wss://localhost:8080/ws", opts)
if err != nil {
    var netErr *net.OpError
    switch {
    case errors.As(err, &netErr) && netErr.Op == "proxyconnect":
        log.Fatal("Proxy connect TCP error: ", netErr)
    // TODO: 检测代理认证错误
    default: // 其他错误
        log.Fatal("Failed to connect to WebSocket server: ", err)
    }
}
...

已实现的代理连接TCP错误检测

通过errors.As(err, &netErr) && netErr.Op == "proxyconnect"可以捕获代理连接的TCP类错误(如代理地址无法解析、连接被拒绝等),示例错误日志:

2024/06/27 06:38:18 Proxy connect TCP error: proxyconnect tcp: dial tcp 127.0.0.1:8887: connect: connection refused
exit status 1

对应的错误栈追踪:

error(*fmt.wrapError) *{msg: "failed to WebSocket dial: failed to send handshake request: Get \"https://localhost:8080/ws\": proxyconnect tcp: dial tcp 127.0.0.1:8887: connect: connection refused", err: error(*fmt.wrapError) *{msg: "failed to send handshake request: Get \"https://localhost:8080/ws\": proxyconnect tcp: dial tcp 127.0.0.1:8887: connect: connection refused", err: error(*net/url.Error) ...}}
error(*fmt.wrapError) *{msg: "failed to send handshake request: Get \"https://localhost:8080/ws\": proxyconnect tcp: dial tcp 127.0.0.1:8887: connect: connection refused", err: error(*net/url.Error) *{Op: "Get", URL: "https://localhost:8080/ws", Err: error(*net.OpError) ...}}
error(*net/url.Error) *{Op: "Get", URL: "https://localhost:8080/ws", Err: error(*net.OpError) *{Op: "proxyconnect", Net: "tcp", Source: net.Addr nil, Addr: net.Addr nil, Err: error(*net.OpError) ...}}
error(*net.OpError) *{Op: "proxyconnect", Net: "tcp", Source: net.Addr nil, Addr: net.Addr nil, Err: error(*net.OpError) *{Op: "dial", Net: "tcp", Source: net.Addr nil, Addr: net.Addr(*net.TCPAddr) ..., Err: error(*os.SyscallError) ...}}
error(*net.OpError) *{Op: "dial", Net: "tcp", Source: net.Addr nil, Addr: net.Addr(*net.TCPAddr) *{IP: net.IP len: 16, cap: 16, [0,0,0,0,0,0,0,0,0,0,255,255,127,0,0,1], Port: 8887, Zone: ""}, Err: error(*os.SyscallError) *{Syscall: "connect", Err: error(syscall.Errno) *(*error)(0xc000066110)}}

代理认证错误的识别问题

不同代理返回的认证错误信息不一致:

  • 使用Tiny Proxy时,错误日志为:
2024/06/26 17:48:07 Failed to connect to WebSocket server: failed to WebSocket dial: failed to send handshake request: Get "https://localhost:8080/ws": Unauthorized

对应的错误栈追踪:

error(*fmt.wrapError) *{msg: "failed to WebSocket dial: failed to send handshake request: Get \"https://localhost:8080/ws\": Unauthorized", err: error(*fmt.wrapError) *{msg: "failed to send handshake request: Get \"https://localhost:8080/ws\": Unauthorized", err: error(*net/url.Error) ...}}
error(*fmt.wrapError) *{msg: "failed to send handshake request: Get \"https://localhost:8080/ws\": Unauthorized", err: error(*net/url.Error) *{Op: "Get", URL: "https://localhost:8080/ws", Err: error(*errors.errorString) ...}}
error(*net/url.Error) *{Op: "Get", URL: "https://localhost:8080/ws", Err: error(*errors.errorString) *{s: "Unauthorized"}}
error(*errors.errorString) *{s: "Unauthorized"}
  • 使用Squid Proxy时,错误字符串为:
Proxy Authentication required

解决方案:统一识别代理认证错误

要兼容不同代理的错误提示,需从错误链中提取底层信息,匹配常见的代理认证关键词,修改后的代码如下:

import (
    "errors"
    "log"
    "net"
    "net/url"
    "strings"
)

// 递归获取最底层的错误信息
func getRootErrorMsg(err error) string {
    for {
        if unwrapped := errors.Unwrap(err); unwrapped != nil {
            err = unwrapped
        } else {
            break
        }
    }
    return err.Error()
}

func main() {
    conn, _, err := websocket.Dial(ctx, "wss://localhost:8080/ws", opts)
    if err != nil {
        var netErr *net.OpError
        var urlErr *url.Error
        switch {
        case errors.As(err, &netErr) && netErr.Op == "proxyconnect":
            log.Fatal("代理连接TCP错误: ", netErr)
        case errors.As(err, &urlErr):
            rootMsg := getRootErrorMsg(urlErr.Err)
            // 匹配常见的代理认证错误关键词
            authErrKeywords := []string{"Unauthorized", "Proxy Authentication required", "407"}
            for _, kw := range authErrKeywords {
                if strings.Contains(rootMsg, kw) {
                    log.Fatal("代理认证错误: ", rootMsg)
                }
            }
            // 非认证类URL相关错误
            log.Fatal("WebSocket握手请求失败: ", urlErr)
        default:
            log.Fatal("WebSocket连接失败: ", err)
        }
    }
    // 后续业务逻辑...
}

代码说明

  1. getRootErrorMsg函数递归解开多层包装的错误,拿到最原始的错误信息;
  2. 通过多关键词匹配覆盖不同代理的错误提示,加入"407"是因为代理认证失败对应的HTTP标准状态码为407,部分代理会直接返回该状态码相关信息;
  3. 分层处理错误类型,先区分TCP级别的代理连接错误,再识别认证错误,最后归类其他错误。

内容的提问来源于stack exchange,提问作者ray an

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 18:23:17