diff --git a/curlx.go b/curlx.go index c38dc3b..7c97793 100644 --- a/curlx.go +++ b/curlx.go @@ -19,9 +19,20 @@ import ( * Date: 2023年7月12日11:35:01 */ -var ( - // 默认的transport - transport http.Transport = http.Transport{ +// type DialContext func(ctx context.Context, network, addr string) (net.Conn, error) + +type Curlx struct { + opts clientOptions + transport *http.Transport +} + +func NewCurlx(opts ...Option) *Curlx { + defaultOpts := defaultOptions() + for _, apply := range opts { + apply(&defaultOpts) + } + + transport := &http.Transport{ // Dial: func(netw, addr string) (net.Conn, error) { // // 这里指定域名访问的IP // // if addr == "api.hk.blueoceanpay.com:443" { @@ -48,22 +59,16 @@ var ( MaxConnsPerHost: 0, // 每个host的最大连接数量 IdleConnTimeout: time.Second * 2, // 空闲连接超时关闭的时间 } - // client = &http.Client{} -) - -type DialContext func(ctx context.Context, network, addr string) (net.Conn, error) - -type Curlx struct { - transport *http.Transport - timeOutSecond int -} - -func NewCurlx() *Curlx { - return &Curlx{ - transport: &transport, - timeOutSecond: 180, + if defaultOpts.InsecureSkipVerify { + transport.TLSClientConfig.InsecureSkipVerify = true } + + return &Curlx{ + opts: defaultOpts, + transport: transport, + } + } /** @@ -72,8 +77,8 @@ func NewCurlx() *Curlx { */ func (c *Curlx) WithProxySocks5(address string) error { baseDialer := &net.Dialer{ - Timeout: 180 * time.Second, - KeepAlive: 180 * time.Second, + // Timeout: 180 * time.Second, + // KeepAlive: 180 * time.Second, } dialSocksProxy, err := proxy.SOCKS5("tcp", address, nil, baseDialer) if err != nil { @@ -101,30 +106,20 @@ func (c *Curlx) WithProxyHttp(proxyAddr string) error { return nil } -/** - * 不校验HTTPS证书 - */ -func (c *Curlx) WithInsecureSkipVerify() { - c.transport.TLSClientConfig.InsecureSkipVerify = true -} - -/** - * 设置超时时间,单位秒 - */ -func (c *Curlx) WithTimeout(timeout int) { - c.timeOutSecond = timeout -} - // 指定访问的IP -// func(c *curlx) WithIp(ip string) { -// c.transport.Dial -// } +// 127.0.0.1:8080 +func (c *Curlx) WithAddress(ctx context.Context, addr string) { + // network tcp/udp + c.transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) { + return net.Dial(network, addr) + } +} /** * 简单请求 */ -func (c *Curlx) Send(ctx context.Context, p *CurlParams) (res string, httpcode int, err error) { - response, err := c.sendExec(ctx, p) +func (c *Curlx) Send(ctx context.Context, p ...Param) (res string, httpcode int, err error) { + _, response, err := c.sendExec(ctx, p...) if err != nil { return "", -1, err } @@ -163,27 +158,32 @@ func (c *Curlx) Send(ctx context.Context, p *CurlParams) (res string, httpcode i * 执行发送 * 注意:外部使用需要加这一句 defer response.Body.Close() */ -func (c *Curlx) sendExec(ctx context.Context, p *CurlParams) (resp *http.Response, err error) { +func (c *Curlx) sendExec(ctx context.Context, ps ...Param) (req *http.Request, resp *http.Response, err error) { client := &http.Client{ - Timeout: time.Second * time.Duration(c.timeOutSecond), // 整个请求的超时时间 设置该条连接的超时 - Transport: c.transport, // + Timeout: c.opts.TimeOut, // 整个请求的超时时间 设置该条连接的超时 + Transport: c.transport, // + } + + p := defaultParams() + for _, param := range ps { + param(&p) } err = p.parseMethod() if err != nil { - return nil, err + return nil, nil, err } // 判断和处理url err = p.parseUrl() if err != nil { - return nil, err + return nil, nil, err } // 处理参数 reqParams, err := p.parseParams() if err != nil { - return nil, err + return nil, nil, err } // 初始化句柄 @@ -193,7 +193,7 @@ func (c *Curlx) sendExec(ctx context.Context, p *CurlParams) (resp *http.Respons reqParams, ) if err != nil { - return nil, err + return nil, nil, err } // 这里指定要访问的HOST,到时候服务器获取主机是获取到这个 @@ -211,16 +211,16 @@ func (c *Curlx) sendExec(ctx context.Context, p *CurlParams) (resp *http.Respons // 发起请求 response, err := client.Do(request) if err != nil { - return nil, err + return nil, nil, err } // response.StatusCode - return response, nil + return request, response, nil } /** * 流式请求 */ -func (c *Curlx) SendChan(ctx context.Context, p *CurlParams) (<-chan string, error) { +func (c *Curlx) SendChan(ctx context.Context, ps ...Param) (<-chan string, error) { data := make(chan string, 1000) @@ -230,7 +230,7 @@ func (c *Curlx) SendChan(ctx context.Context, p *CurlParams) (<-chan string, err ctx, cancel := context.WithTimeout(context.Background(), time.Minute*30) defer cancel() - response, err := c.sendExec(ctx, p) + _, response, err := c.sendExec(ctx, ps...) if err != nil { return } diff --git a/curlx_test.go b/curlx_test.go index d3e6e77..1536a07 100644 --- a/curlx_test.go +++ b/curlx_test.go @@ -9,11 +9,10 @@ import ( ) func TestGet(t *testing.T) { - p := CurlParams{} - p.Url = "http://www.baidu.com" - p.Method = "GET" - - resp, code, err := NewCurlx().Send(context.Background(), &p) + resp, code, err := NewCurlx().Send(context.Background(), + SetUrl("https://www.baidu.com"), + SetMethod(MethodGet), + ) t.Log(resp, code, err) } @@ -37,7 +36,7 @@ func TestForm(t *testing.T) { }, } - p := &CurlParams{ + p := ClientParams{ Url: "http://tech-dev.sealmoo.com/api/material/upload", Method: "POST", Params: s, @@ -46,6 +45,6 @@ func TestForm(t *testing.T) { }, ContentType: ContentTypeForm, } - resp, code, err := NewCurlx().Send(context.Background(), p) + resp, code, err := NewCurlx().Send(context.Background(), SetAll(p)) fmt.Println(resp, code, err) } diff --git a/options.go b/options.go new file mode 100644 index 0000000..d074bbd --- /dev/null +++ b/options.go @@ -0,0 +1,34 @@ +package curlx + +import "time" + +type clientOptions struct { + TimeOut time.Duration + InsecureSkipVerify bool +} + +func defaultOptions() clientOptions { + return clientOptions{ + TimeOut: time.Second * 120, // 默认超时120 + } +} + +type Option func(*clientOptions) + +/** + * 设置超时时间 + */ +func SetTimeOut(t time.Duration) Option { + return func(options *clientOptions) { + options.TimeOut = t + } +} + +/** + * 不校验HTTPS证书 + */ +func SetTLSInsecureSkipVerify() Option { + return func(options *clientOptions) { + options.InsecureSkipVerify = true + } +} diff --git a/params.go b/params.go new file mode 100644 index 0000000..ba928b9 --- /dev/null +++ b/params.go @@ -0,0 +1,144 @@ +package curlx + +type ClientParams struct { + Url string + Method Method // GET/POST + Params interface{} + Headers map[string]interface{} + Cookies interface{} + ContentType ContentType // FORM,JSON,XML +} + +func defaultParams() ClientParams { + return ClientParams{} +} + +type Param func(*ClientParams) + +func SetAll(cp ClientParams) Param { + return func(param *ClientParams) { + param.Url = cp.Url + param.Method = cp.Method + param.Params = cp.Params + param.Headers = cp.Headers + param.Cookies = cp.Cookies + param.ContentType = cp.ContentType + } +} + +/** + * 设置URL + */ +func SetUrl(url string) Param { + return func(param *ClientParams) { + param.Url = url + } +} + +/** + * 设置方法 + */ +func SetMethod(m Method) Param { + return func(param *ClientParams) { + param.Method = m + } +} + +/** + * 设置参数 + */ +func SetParams(p interface{}) Param { + return func(param *ClientParams) { + param.Params = p + } +} + +/** + * 表单文本参数 + */ +func SetParamsFormText(fieldName, fieldValue string) Param { + return func(param *ClientParams) { + fp := param.Params.([]FormParam) + fp = append(fp, FormParam{ + FieldName: fieldName, + FieldValue: fieldValue, + FieldType: FieldTypeText, + }) + param.Params = fp + } +} + +/** + * 表单文件上传 + */ +func SetParamsFormFile(fieldName, fileName string, fileBytes []byte) Param { + return func(param *ClientParams) { + fp := param.Params.([]FormParam) + fp = append(fp, FormParam{ + FieldName: fieldName, + FieldType: FieldTypeFile, + FileName: fileName, + FileBytes: fileBytes, + }) + param.Params = fp + } +} + +/** + * 设置请求头 + */ +func SetHeaders(h map[string]interface{}) Param { + return func(param *ClientParams) { + param.Headers = h + } +} + +/** + * 设置cookies + */ +func SetCookies(c interface{}) Param { + return func(param *ClientParams) { + param.Cookies = c + } +} + +/** + * 设置请求方法 + */ +func SetContentType(t ContentType) Param { + return func(param *ClientParams) { + param.ContentType = t + } +} + +type FieldType string + +const ( + FieldTypeFile FieldType = "file" + FieldTypeText FieldType = "text" +) + +type FormParam struct { + FieldName string `json:"field_name"` // 字段名 + FieldValue string `json:"field_value"` // 字段值 + FieldType FieldType `json:"field_type"` // 动作(file/text) + FileName string `json:"file_name"` // 文件名 + FileBytes []byte `json:"file_bytes"` // 文件内容 +} + +type ContentType string + +const ( + ContentTypeForm ContentType = "multipart/form-data" + ContentTypeJson ContentType = "application/json" + ContentTypeXml ContentType = "application/xml" + ContentTypeText ContentType = "text/plain" + ContentTypeUrlEncoded ContentType = "application/x-www-form-urlencoded" +) + +type Method string + +const ( + MethodGet Method = "GET" + MethodPost Method = "POST" +) diff --git a/request.go b/request.go index cc9495a..d4a2ae1 100644 --- a/request.go +++ b/request.go @@ -16,7 +16,7 @@ import ( /** * 处理请求类型 */ -func (p *CurlParams) parseMethod() error { +func (p *ClientParams) parseMethod() error { if p.Method == "" { return errors.New("请求类型不能为空") } @@ -26,7 +26,7 @@ func (p *CurlParams) parseMethod() error { /** * 处理URL */ -func (p *CurlParams) parseUrl() error { +func (p *ClientParams) parseUrl() error { _, err := url.Parse(p.Url) if err != nil { return err @@ -37,7 +37,7 @@ func (p *CurlParams) parseUrl() error { /** * 处理请求头Header */ -func (p *CurlParams) parseHeaders(r *http.Request) { +func (p *ClientParams) parseHeaders(r *http.Request) { if p.Headers != nil { if r.Header.Get("User-Agent") == "" { r.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:87.0) Gecko/20100101 Firefox/87.0 Send By Golang") @@ -60,7 +60,7 @@ func (p *CurlParams) parseHeaders(r *http.Request) { /** * 处理请求参数 */ -func (p *CurlParams) parseParams() (str io.Reader, err error) { +func (p *ClientParams) parseParams() (str io.Reader, err error) { err = nil // 初始化(如未初始化) @@ -233,7 +233,7 @@ func (p *CurlParams) parseParams() (str io.Reader, err error) { /** * 处理Cookie */ -func (p *CurlParams) parseCookies(r *http.Request) { +func (p *ClientParams) parseCookies(r *http.Request) { switch p.Cookies.(type) { case string: cookies := p.Cookies.(string) diff --git a/types.go b/types.go index fb0251f..941e781 100644 --- a/types.go +++ b/types.go @@ -1,46 +1,5 @@ package curlx -type FieldType string - -const ( - FieldTypeFile FieldType = "file" - FieldTypeText FieldType = "text" -) - -type FormParam struct { - FieldName string `json:"field_name"` // 字段名 - FieldValue string `json:"field_value"` // 字段值 - FieldType FieldType `json:"field_type"` // 动作(file/text) - FileName string `json:"file_name"` // 文件名 - FileBytes []byte `json:"file_bytes"` // 文件内容 -} - -type ContentType string - -const ( - ContentTypeForm ContentType = "multipart/form-data" - ContentTypeJson ContentType = "application/json" - ContentTypeXml ContentType = "application/xml" - ContentTypeText ContentType = "text/plain" - ContentTypeUrlEncoded ContentType = "application/x-www-form-urlencoded" -) - -type method string - -const ( - MethodGet method = "GET" - MethodPost method = "POST" -) - -type CurlParams struct { - Url string - Method method // GET/POST - Params interface{} - Headers map[string]interface{} - Cookies interface{} - ContentType ContentType // FORM,JSON,XML -} - type UserAgent string