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) } } // 后续业务逻辑... }
代码说明
getRootErrorMsg函数递归解开多层包装的错误,拿到最原始的错误信息;- 通过多关键词匹配覆盖不同代理的错误提示,加入"407"是因为代理认证失败对应的HTTP标准状态码为407,部分代理会直接返回该状态码相关信息;
- 分层处理错误类型,先区分TCP级别的代理连接错误,再识别认证错误,最后归类其他错误。
内容的提问来源于stack exchange,提问作者ray an
相关产品推荐
相关产品推荐

