blog/go/wechat/curl/v1.go

185 lines
4.5 KiB
Go

package curl
import (
"bytes"
"context"
"crypto/tls"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
netURL "net/url"
"strings"
"time"
)
type curlV1 struct {
DefaultHeader http.Header
}
func V1(defaultHeader http.Header) *curlV1 {
return &curlV1{
DefaultHeader: defaultHeader,
}
}
func (v1 *curlV1) getDomain(url string) string {
strs := strings.Split(url, "//")
if len(strs) < 2 {
return ""
}
schema := v1.getSchema(url)
paths := strings.Split(strs[1], "/")
switch schema {
case "unix":
tmpstrs := []string{}
for _, p := range paths {
tmpstrs = append(tmpstrs, p)
if strings.Contains(p, ".sock") {
break
}
}
return strings.Join(tmpstrs, "/")
case "http":
return paths[0]
case "https":
return paths[0]
default:
return paths[0]
}
}
func (v1 *curlV1) getSchema(url string) string {
strs := strings.Split(url, ":")
return strs[0]
}
func (v1 *curlV1) getPath(url string) string {
schema := v1.getSchema(url)
domain := v1.getDomain(url)
s := fmt.Sprintf("%s://%s", schema, domain)
if len(url) <= len(s) {
return ""
}
return url[len(s):]
}
func (v1 *curlV1) NewClient(url string) (*http.Client, string) {
schema := v1.getSchema(url)
domain := v1.getDomain(url)
path := v1.getPath(url)
if schema == "unix" {
defaultTimeout := 60 * time.Second
tr := new(http.Transport)
tr.DisableCompression = true
tr.DialContext = func(_ context.Context, _, _ string) (net.Conn, error) {
return net.DialTimeout("unix", domain, defaultTimeout)
}
tr.TLSClientConfig = &tls.Config{
InsecureSkipVerify: true,
}
client := &http.Client{Transport: tr}
return client, path
}
// 绕过github等可能因为特征码返回503问题
// https://www.imwzk.com/posts/2021-03-14-why-i-always-get-503-with-golang/
defaultCipherSuites := []uint16{0xc02f, 0xc030, 0xc02b, 0xc02c, 0xcca8, 0xcca9, 0xc013, 0xc009,
0xc014, 0xc00a, 0x009c, 0x009d, 0x002f, 0x0035, 0xc012, 0x000a}
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
CipherSuites: append(defaultCipherSuites[8:], defaultCipherSuites[:8]...),
},
},
// 获取301重定向
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
return client, path
}
func (v1 *curlV1) DoRequest(method, url string, body interface{}, header http.Header) (*http.Response, error) {
var reqBody io.Reader
contentType := "application/json"
switch v := body.(type) {
case string:
reqBody = strings.NewReader(v)
contentType = "text/plain"
case []byte:
reqBody = bytes.NewReader(v)
case map[string]string:
val := netURL.Values{}
for k, v := range v {
val.Set(k, v)
}
reqBody = strings.NewReader(val.Encode())
contentType = "application/x-www-form-urlencoded"
default:
data, err := json.Marshal(v)
if err != nil {
return nil, err
}
reqBody = bytes.NewReader(data)
}
if header == nil {
header = http.Header{}
}
if header.Get("Content-Type") == "" {
header.Set("Content-Type", contentType)
}
for k, values := range v1.DefaultHeader {
if header.Get(k) == "" {
for _, v := range values {
header.Add(k, v)
}
}
}
client, location := v1.NewClient(url)
schema := v1.getSchema(url)
req, err := http.NewRequest(method, location, reqBody)
if err != nil {
return nil, err
}
req.URL.Scheme = schema
if schema == "unix" {
req.URL.Scheme = "http"
}
req.URL.Host = v1.getDomain(url)
req.Header = header
return client.Do(req)
}
func (v1 *curlV1) DoRequestReturnBytes(method, url string, body interface{}, header http.Header) ([]byte, error) {
resp, err := v1.DoRequest(method, url, body, header)
if err != nil {
return []byte{}, err
}
defer resp.Body.Close()
res, err := ioutil.ReadAll(resp.Body)
return res, err
}
func (v1 *curlV1) Get(url string, body interface{}, header http.Header) ([]byte, error) {
return v1.DoRequestReturnBytes(http.MethodGet, url, body, header)
}
func (v1 *curlV1) Put(url string, body interface{}, header http.Header) ([]byte, error) {
return v1.DoRequestReturnBytes(http.MethodPut, url, body, header)
}
func (v1 *curlV1) Post(url string, body interface{}, header http.Header) ([]byte, error) {
return v1.DoRequestReturnBytes(http.MethodPost, url, body, header)
}
func (v1 *curlV1) Patch(url string, body interface{}, header http.Header) ([]byte, error) {
return v1.DoRequestReturnBytes(http.MethodPatch, url, body, header)
}
func (v1 *curlV1) Delete(url string, body interface{}, header http.Header) ([]byte, error) {
return v1.DoRequestReturnBytes(http.MethodDelete, url, body, header)
}