go https代理认证

 

curl --proxy-header "代理认证信息" --proxy "代理地址" https://xxx

这里--proxy-header和普通header -H不等价,虽然两者类型相同,但是前者只发送给代理服务端(仅限https场景),后者只发送给服务端。

go Transport实现了http RoundTripper接口,支持http和https请求发送和接收响应。
dialConn方法
根据代理配置与代理服务端建立tcp连接并发送读取数据。

省略
if cm.scheme() == "https" && t.hasCustomTLSDialer() {
	var err error
	pconn.conn, err = t.customDialTLS(ctx, "tcp", cm.addr())
	if err != nil {
		return nil, wrapErr(err)
	}
	if tc, ok := pconn.conn.(*tls.Conn); ok {
		// Handshake here, in case DialTLS didn't. TLSNextProto below
		// depends on it for knowing the connection state.
		if trace != nil && trace.TLSHandshakeStart != nil {
			trace.TLSHandshakeStart()
		}
		if err := tc.HandshakeContext(ctx); err != nil {
			go pconn.conn.Close()
			if trace != nil && trace.TLSHandshakeDone != nil {
				trace.TLSHandshakeDone(tls.ConnectionState{}, err)
			}
			return nil, err
		}
		cs := tc.ConnectionState()
		if trace != nil && trace.TLSHandshakeDone != nil {
			trace.TLSHandshakeDone(cs, nil)
		}
		pconn.tlsState = &cs
	}
} else {
省略
case cm.targetScheme == "https":
	conn := pconn.conn
	var hdr Header
	if t.GetProxyConnectHeader != nil {
		var err error
		hdr, err = t.GetProxyConnectHeader(ctx, cm.proxyURL, cm.targetAddr)
		if err != nil {
			conn.Close()
			return nil, err
		}
	} else {
		hdr = t.ProxyConnectHeader
	}
	if hdr == nil {
		hdr = make(Header)
	}
	if pa := cm.proxyAuth(); pa != "" {
		hdr = hdr.Clone()
		hdr.Set("Proxy-Authorization", pa)
	}
	connectReq := &Request{
		Method: "CONNECT",
		URL:    &url.URL{Opaque: cm.targetAddr},
		Host:   cm.targetAddr,
		Header: hdr,
	}

	// Set a (long) timeout here to make sure we don't block forever
	// and leak a goroutine if the connection stops replying after
	// the TCP connect.
	connectCtx, cancel := testHookProxyConnectTimeout(ctx, 1*time.Minute)
	defer cancel()

	didReadResponse := make(chan struct{}) // closed after CONNECT write+read is done or fails
	var (
		resp *Response
		err  error // write or read error
	)
	// Write the CONNECT request & read the response.
	go func() {
		defer close(didReadResponse)
		err = connectReq.Write(conn)
		if err != nil {
			return
		}
		// Okay to use and discard buffered reader here, because
		// TLS server will not speak until spoken to.
		br := bufio.NewReader(conn)
		resp, err = ReadResponse(br, connectReq)
	}()
	select {
	case <-connectCtx.Done():
		conn.Close()
		<-didReadResponse
		return nil, connectCtx.Err()
	case <-didReadResponse:
		// resp or err now set
	}
	if err != nil {
		conn.Close()
		return nil, err
	}

	if t.OnProxyConnectResponse != nil {
		err = t.OnProxyConnectResponse(ctx, cm.proxyURL, connectReq, resp)
		if err != nil {
			conn.Close()
			return nil, err
		}
	}

	if resp.StatusCode != 200 {
		_, text, ok := strings.Cut(resp.Status, " ")
		conn.Close()
		if !ok {
			return nil, errors.New("unknown status code")
		}
		return nil, errors.New(text)
	}
}
省略

1. 与代理服务端建立tcp连接。
2. 根据Transport字段ProxyConnectHeader和GetProxyConnectHeader构造header。
3. 构造请求体,发送CONNECT方法类型请求。
4. 确认响应码是200。

posted on 2025-06-20 22:04  王景迁  阅读(14)  评论(0)    收藏  举报

导航