Go net/http POST请求遇wsarecv错误:远程主机强制关闭连接求助
Go net/http POST请求获取AccessToken时遭遇连接被远程强制关闭的问题
我用Go的net/http包发送POST请求获取access token时,遇到错误:
Post "https://some.url.gov.ss/tgd/STS/oauth2/token": read tcp 123.123.104.104:53387->123.123.153.251:443: wsarecv: An existing connection was forcibly closed by the remote host
诡异的是,用Python aiohttp请求这个特定URL完全正常,而且相同的Go代码请求其他URL也没问题。我查了大部分wsarecv相关的解决方案都没用,参照API提供方的PHP示例关闭了SSL验证也没解决。
我的Go代码(main.go)
type JSONcontent interface{} var ( transport = &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, }, } client = &http.Client{Transport: transport} vissPayload VISSPayload results JSONcontent ) func requestAccessToken(token string, vissPayload VISSPayload) error { data := url.Values{} data.Add("grant_type", "client_credentials") data.Add("client_id", vissPayload.CERT_CLIENT_ID) data.Add("client_secret", vissPayload.CERT_CLIENT_SECRET) data.Add("client_assertion_type", vissPayload.CERT_CLIENT_ASSERTION_TYPE) data.Add("client_assertion", token) request, err := http.NewRequest(http.MethodPost, vissPayload.CERT_TOKEN_URL, bytes.NewBufferString(data.Encode())) if err != nil { return fmt.Errorf("request error: %v", err) } request.Header.Add("Content-Type", "application/x-www-form-urlencoded") request.Header.Add("Accept", "application/json") request.Header.Add("Cache-Control", "no-cache") response, err := client.Do(request) if err != nil { return fmt.Errorf("request failed error: %v", err) } defer response.Body.Close() body, err := io.ReadAll(request.Body) if err != nil { return fmt.Errorf("failed ot read body error: %v", err) } if err := json.Unmarshal(body, &results); err != nil { return fmt.Errorf("failed to unmarshal body error: %v", err) } return nil }
API提供方的PHP示例(index.php,未本地测试)
function getAccessTokenWithSertificate(){ ... CERT stuff processing ... //Configuring URL & SSL $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, self::CERTIFICATE_TOKEN_URL); //Turn off all TLS security issues curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($curl, CURLINFO_HEADER_OUT, true); //Return result in string curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); //Configuring headers $headers = array( "cache-control: no-cache", "Accept: application/json" ); curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); //Configuring post request curl_setopt($curl, CURLOPT_POST, true); curl_setopt($curl, CURLOPT_POSTFIELDS, [ "grant_type" => "client_credentials", "client_assertion_type" => "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", "client_secret" => self::CERTIFICATE_CLIENT_SECRET, "client_id" => self::CERTIFICATE_CLIENT_ID, "client_assertion" => $jwt, "scope" => self::CERTIFICATE_SCOPE ]); //Getting result $result = curl_exec($curl); $json = json_decode($result, true); curl_close($curl); return $json["access_token"]; }
可正常运行的Python aiohttp示例(main.py)
async def test_request_access_token(self) -> None: ... CERT stuff processing ... # Request access token request_headers = { "cache-control": "no-cache", "Accept": "application/json", } # Set request payload request_payload = { "grant_type": "client_credentials", "client_id": client_id, "client_secret": client_secret, "client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", "client_assertion": JWT, } # Execute async POST request # Trace request trace_config = aiohttp.TraceConfig() trace_config.on_request_start.append(on_request_start) trace_config.on_request_end.append(on_request_end) async with aiohttp.ClientSession(trace_configs=[trace_config]) as session: async with session.post( certificate_token_url, data=request_payload, headers=request_headers, ) as response: payload: dict = await response.json() print("Request date: %s" % response.headers.get("Date")) if response.status != 200: print(f"Error: request returned: {response.status}") for key in payload.keys(): print(f"{key}: {payload[key]}") else: for key in payload.keys(): print(f"{key}: {payload[key]}") self.access_token = payload["access_token"]
可能的解决方案
1. 修复请求体读取错误
代码里犯了低级错误:读取的是request.Body而非response.Body,这会导致读取空内容甚至影响连接状态。修改为:
body, err := io.ReadAll(response.Body)
2. 强制指定TLS版本
部分服务器仅支持特定TLS版本(如TLS 1.2),Go默认尝试最高版本可能不兼容。强制指定版本:
transport = &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, MinVersion: tls.VersionTLS12, MaxVersion: tls.VersionTLS12, }, }
3. 禁用HTTP/2强制使用HTTP/1.1
部分服务器对HTTP/2支持不佳,Go默认启用HTTP/2,可强制切换:
transport = &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, }, ForceAttemptHTTP2: false, }
4. 统一请求头大小写
虽然HTTP头大小写不敏感,但部分服务器可能有严格要求,将请求头改为小写与其他示例保持一致:
request.Header.Add("content-type", "application/x-www-form-urlencoded") request.Header.Add("accept", "application/json") request.Header.Add("cache-control", "no-cache")
5. 添加缺失的scope参数
对比PHP示例,你的Go代码未传递scope参数,若API要求必填则需补充:
data.Add("scope", vissPayload.CERT_SCOPE)
6. 启用TLS握手日志排查
开启日志查看TLS握手细节,定位版本或套件兼容性问题:
import "log" // 初始化transport前添加日志配置 log.SetFlags(log.LstdFlags | log.Lshortfile) transport.TLSClientConfig.VerifyConnection = func(cs tls.ConnectionState) error { log.Printf("TLS Version: %s", cs.Version.String()) log.Printf("Cipher Suite: %s", cs.CipherSuite.String()) log.Printf("Server Name: %s", cs.ServerName) return nil }
内容的提问来源于stack exchange,提问作者Reinis Gaņģis
相关产品推荐
相关产品推荐

