Go1.10以上版本客戶端使用https代理遇到oversized record received解決辦法
Go1.10以上版本客戶端使用https代理遇到oversized record received解決辦法
最近在專案開發過程中需要做http回撥,由於內網防火牆限制機器的外網訪問許可權,因此只能使用代理出口的方式進行訪問第三方的回撥介面,由於公司內部有一臺https的代理伺服器因此打算用此代理服務。在使用 Go1.10以上版本的時候,Go客戶端遇到下問題proxyconnect tcp: tls: oversized record received with length 20527 ,各大搜索引擎都沒有找到解決辦法,經過幾個小時的試錯後,居然發現Go1.8版本居然沒有這個問題,因此果斷對比Go1.8和Go1.10的原始碼,並在原始碼中加Debug資訊,後面找了出現問題的程式碼,具體解決辦法見如下博文。
具體部署如下
httpclient---->https-proxy ---->第三方http介面
httpclient程式碼如下
package main import ( "crypto/tls" "fmt" "io/ioutil" "net/http" "net/url" "os" "strings" "time" "github.com/cihub/seelog" ) func PostHttpRequest(address, data string, hdrs map[string]string) (string, error) { code, body, err := RawPostHttpRequest(address, data, hdrs, map[string]string{}) if code != 200 { seelog.Errorf("post request error %d %s %s %s\n", code, data, body, err) return "", err } return body, nil } func PostHttpRequestEx(address, data string, hdrs, config map[string]string) (string, error) { code, body, err := RawPostHttpRequest(address, data, hdrs, config) if code != 200 { seelog.Errorf("post request error %d %s %s %s\n", code, data, body, err) return "", err } return body, nil } func RawPostHttpRequest(address, data string, hdrs, configs map[string]string) (int, string, error) { var proxy func(*http.Request) (*url.URL, error) = nil if v := configs["proxy"]; v != "" { proxy = func(req *http.Request) (*url.URL, error) { return url.Parse(v) } } client := &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, }, Proxy: proxy, }, Timeout: time.Duration(6) * time.Second, } req, err := http.NewRequest( "POST", address, strings.NewReader(data), ) if err != nil { // handle error seelog.Error("new post request error ", err, data) return -1, "", err } if hdrs != nil { for k, v := range hdrs { if k == "Host" { req.Host = v continue } req.Header[k] = []string{v} } } if hdrs == nil || hdrs["Content-Type"] == "" { req.Header.Set("Content-Type", "application/json") } resp, err := client.Do(req) if err != nil { seelog.Error("post request error ", err, data) return -1, "", err } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { // handle error seelog.Error("post request error ", err, data) return -1, "", err } if resp.StatusCode != 200 { err = fmt.Errorf(resp.Status) } return resp.StatusCode, string(body), err } func main() { defer seelog.Flush() curl := "" if len(os.Args) >= 2 { curl = os.Args[1] } cfg := map[string]string{} if len(os.Args) >= 3 { cfg["proxy"] = os.Args[2] } data := "{}" seelog.Debugf("callback cfg %v", cfg) body, err := PostHttpRequestEx(curl, data, nil, cfg) if err != nil { seelog.Errorf("callback request error %s", err) return } seelog.Infof("callback %s post %s => %s", curl, data, body) }
編譯執行出現如下錯誤
taxue@linux:~$ go build HttpClient.go taxue@linux:~$ ./HttpClient https://www.baidu.com https://proxy.server.com:1101 1550890998458292103 [Debug] callback cfg map[proxy:https://proxy.server.com:1101] 1550890998467744401 [Error] post request error Post https://www.baidu.com: proxyconnect tcp: tls: oversized record received with length 20527{} 1550890998467789535 [Error] callback request error Post https://www.baidu.com: proxyconnect tcp: tls: oversized record received with length 20527
問題原因
在/usr/local/go/src/net/http/transport.go檔案中dialConn函式,Go1.10版本以後加了if cm.scheme() == "https" { 這個條件判斷,錯誤就是在這個條件塊裡面出現的,而Go1.8是沒有這段程式碼的,因此猜想可能是某些原因導致加入了這8行程式碼導致https代理失效。
func (t *Transport) dialConn(ctx context.Context, cm connectMethod) (*persistConn, error) { if cm.scheme() == "https" && t.DialTLS != nil { ... } else { conn, err := t.dial(ctx, "tcp", cm.addr()) if err != nil { return nil, wrapErr(err) } pconn.conn = conn if cm.scheme() == "https" { var firstTLSHost string if firstTLSHost, _, err = net.SplitHostPort(cm.addr()); err != nil { return nil, wrapErr(err) } if err = pconn.addTLS(firstTLSHost, trace); err != nil { return nil, wrapErr(err) } } }
解決辦法
改進辦法就是新增條件,當為代理模式的時候不進入這個條件。
if cm.scheme() == "https" && cm.proxyURL == nil { var firstTLSHost string if firstTLSHost, _, err = net.SplitHostPort(cm.addr()); err != nil { return nil, wrapErr(err) } if err = pconn.addTLS(firstTLSHost, trace); err != nil { return nil, wrapErr(err) } }
taxue@linux:~$ go build HttpClient.go taxue@linux:~$ ./HttpClient https://www.baidu.comhttps://zmcc.corp.cootek.com:1101 1550895140686682645 [Debug] callback cfg map[proxy:https://zmcc.corp.cootek.com:1101] 1550895140899872758 [Info] callback https://www.baidu.com post {} => <!DOCTYPE html> <!--STATUS OK--> <html> <head> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="content-type" content="text/html;charset=utf-8"> <meta content="always" name="referrer"> <script src="https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/nocache/imgdata/seErrorRec.js"></script> <title>頁面不存在_百度搜索</title> ......
總結
至此測試了 http、https直接訪問,https代理訪問http、https都正常了。是不是golang官方的bug不得而知,歡迎各路大神拍磚過來。