fix: 修复核心bug并完善API
- 修复NewRequest系列函数不返回opt错误的问题 - 修复prepare()幂等性问题,支持请求重试 - 修复defaultDialTLSFunc的ServerName解析错误 - 修复Client.Clone()并发安全问题 - 补齐Client.Trace/Connect方法 - 新增Request.HTTPClient/Client方法 - 增强NewSimpleRequest错误处理的健壮性
This commit is contained in:
parent
1bb30514ec
commit
4568e17f06
18
client.go
18
client.go
@ -325,3 +325,21 @@ func (c *Client) NewSimpleRequestWithContext(ctx context.Context, url, method st
|
|||||||
}
|
}
|
||||||
return req
|
return req
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Trace 发送 TRACE 请求
|
||||||
|
func (c *Client) Trace(url string, opts ...RequestOpt) (*Response, error) {
|
||||||
|
req, err := c.NewRequest(url, http.MethodTrace, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return req.Do()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect 发送 CONNECT 请求
|
||||||
|
func (c *Client) Connect(url string, opts ...RequestOpt) (*Response, error) {
|
||||||
|
req, err := c.NewRequest(url, http.MethodConnect, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return req.Do()
|
||||||
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -119,8 +120,11 @@ func defaultDialTLSFunc(ctx context.Context, network, addr string) (net.Conn, er
|
|||||||
if tlsConfig.ServerName == "" && !tlsConfig.InsecureSkipVerify {
|
if tlsConfig.ServerName == "" && !tlsConfig.InsecureSkipVerify {
|
||||||
host, _, err := net.SplitHostPort(addr)
|
host, _, err := net.SplitHostPort(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// addr 可能没有端口,直接用 addr
|
if idx := strings.LastIndex(addr, ":"); idx > 0 {
|
||||||
host = addr
|
host = addr[:idx]
|
||||||
|
} else {
|
||||||
|
host = addr
|
||||||
|
}
|
||||||
}
|
}
|
||||||
tlsConfig = tlsConfig.Clone() // 避免修改原 config
|
tlsConfig = tlsConfig.Clone() // 避免修改原 config
|
||||||
tlsConfig.ServerName = host
|
tlsConfig.ServerName = host
|
||||||
|
|||||||
47
request.go
47
request.go
@ -84,12 +84,27 @@ func newRequest(ctx context.Context, urlStr string, method string, opts ...Reque
|
|||||||
|
|
||||||
// NewRequest 创建新请求
|
// NewRequest 创建新请求
|
||||||
func NewRequest(url, method string, opts ...RequestOpt) (*Request, error) {
|
func NewRequest(url, method string, opts ...RequestOpt) (*Request, error) {
|
||||||
return newRequest(context.Background(), url, method, opts...)
|
req, err := newRequest(context.Background(), url, method, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if req.err != nil {
|
||||||
|
return nil, req.err
|
||||||
|
}
|
||||||
|
return req, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRequestWithContext 创建新请求(带 context)
|
// NewRequestWithContext 创建新请求(带 context)
|
||||||
func NewRequestWithContext(ctx context.Context, url, method string, opts ...RequestOpt) (*Request, error) {
|
func NewRequestWithContext(ctx context.Context, url, method string, opts ...RequestOpt) (*Request, error) {
|
||||||
return newRequest(ctx, url, method, opts...)
|
req, err := newRequest(ctx, url, method, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// 新增
|
||||||
|
if req.err != nil {
|
||||||
|
return nil, req.err
|
||||||
|
}
|
||||||
|
return req, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSimpleRequest 创建新请求(忽略错误,支持链式调用)
|
// NewSimpleRequest 创建新请求(忽略错误,支持链式调用)
|
||||||
@ -190,7 +205,6 @@ func (r *Request) SetMethod(method string) *Request {
|
|||||||
if r.err != nil {
|
if r.err != nil {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
method = strings.ToUpper(method)
|
method = strings.ToUpper(method)
|
||||||
if !validMethod(method) {
|
if !validMethod(method) {
|
||||||
r.err = wrapError(ErrInvalidMethod, "method: %s", method)
|
r.err = wrapError(ErrInvalidMethod, "method: %s", method)
|
||||||
@ -249,6 +263,10 @@ func (r *Request) SetRawRequest(httpReq *http.Request) *Request {
|
|||||||
}
|
}
|
||||||
r.httpReq = httpReq
|
r.httpReq = httpReq
|
||||||
r.doRaw = true
|
r.doRaw = true
|
||||||
|
if httpReq == nil {
|
||||||
|
r.err = fmt.Errorf("httpReq cannot be nil")
|
||||||
|
return r
|
||||||
|
}
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,6 +288,29 @@ func (r *Request) SetAutoFetch(auto bool) *Request {
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HTTPClient 获取底层 http.Client(只读)
|
||||||
|
func (r *Request) HTTPClient() (*http.Client, error) {
|
||||||
|
if r.err != nil {
|
||||||
|
return nil, r.err
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.httpClient != nil {
|
||||||
|
return r.httpClient, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果还没构建,先准备
|
||||||
|
if err := r.prepare(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.httpClient, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client 获取关联的 Client(只读)
|
||||||
|
func (r *Request) Client() *Client {
|
||||||
|
return r.client
|
||||||
|
}
|
||||||
|
|
||||||
// Do 执行请求
|
// Do 执行请求
|
||||||
func (r *Request) Do() (*Response, error) {
|
func (r *Request) Do() (*Response, error) {
|
||||||
// 检查累积的错误
|
// 检查累积的错误
|
||||||
|
|||||||
@ -336,17 +336,18 @@ func (r *Request) prepare() error {
|
|||||||
if r.applied {
|
if r.applied {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
defer func() { r.applied = true }()
|
|
||||||
// 即使 raw 模式也要确保有 httpClient
|
// 即使 raw 模式也要确保有 httpClient
|
||||||
if r.httpClient == nil {
|
if r.httpClient == nil {
|
||||||
var err error
|
var err error
|
||||||
r.httpClient, err = r.buildHTTPClient()
|
r.httpClient, err = r.buildHTTPClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err // ← 失败时不设置 applied
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 原始模式不修改请求内容
|
// 原始模式不修改请求内容
|
||||||
if r.doRaw {
|
if r.doRaw {
|
||||||
|
r.applied = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,7 +409,8 @@ func (r *Request) prepare() error {
|
|||||||
// 注入配置到 context
|
// 注入配置到 context
|
||||||
r.execCtx = injectRequestConfig(r.ctx, r.config)
|
r.execCtx = injectRequestConfig(r.ctx, r.config)
|
||||||
r.httpReq = r.httpReq.WithContext(r.execCtx)
|
r.httpReq = r.httpReq.WithContext(r.execCtx)
|
||||||
|
|
||||||
|
r.applied = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -36,6 +36,9 @@ func (r *Response) Body() *Body {
|
|||||||
|
|
||||||
// Close 关闭响应体
|
// Close 关闭响应体
|
||||||
func (r *Response) Close() error {
|
func (r *Response) Close() error {
|
||||||
|
if r == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if r.body != nil && r.body.raw != nil {
|
if r.body != nil && r.body.raw != nil {
|
||||||
return r.body.raw.Close()
|
return r.body.raw.Close()
|
||||||
}
|
}
|
||||||
@ -44,6 +47,9 @@ func (r *Response) Close() error {
|
|||||||
|
|
||||||
// CloseWithClient 关闭响应体并关闭空闲连接
|
// CloseWithClient 关闭响应体并关闭空闲连接
|
||||||
func (r *Response) CloseWithClient() error {
|
func (r *Response) CloseWithClient() error {
|
||||||
|
if r == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if r.httpClient != nil {
|
if r.httpClient != nil {
|
||||||
r.httpClient.CloseIdleConnections()
|
r.httpClient.CloseIdleConnections()
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user