github.com/projecteru2/core@v0.0.0-20240321043226-06bcc1c23f58/utils/http.go (about) 1 package utils 2 3 import ( 4 "context" 5 "fmt" 6 "net" 7 "net/http" 8 "os" 9 "runtime" 10 "strings" 11 "time" 12 13 "github.com/alphadose/haxmap" 14 "github.com/docker/go-connections/tlsconfig" 15 16 "github.com/projecteru2/core/log" 17 "github.com/projecteru2/core/types" 18 ) 19 20 var defaultHTTPClient = &http.Client{ 21 CheckRedirect: checkRedirect, 22 Transport: getDefaultTransport(), 23 } 24 25 var defaultUnixSockClient = &http.Client{ 26 Transport: getDefaultUnixSockTransport(), 27 } 28 29 var httpsClientCache = haxmap.New[string, *http.Client]() 30 31 // GetHTTPClient returns a HTTP client 32 func GetHTTPClient() *http.Client { 33 return defaultHTTPClient 34 } 35 36 // GetUnixSockClient . 37 func GetUnixSockClient() *http.Client { 38 return defaultUnixSockClient 39 } 40 41 // GetHTTPSClient returns an HTTPS client 42 // if cert_path/ca/cert/key is empty, it returns an HTTP client instead 43 func GetHTTPSClient(ctx context.Context, certPath, name, ca, cert, key string) (client *http.Client, err error) { 44 if certPath == "" || ca == "" || cert == "" || key == "" { 45 return GetHTTPClient(), nil 46 } 47 48 cacheKey := name + SHA256(fmt.Sprintf("%s-%s-%s-%s-%s", certPath, name, ca, cert, key))[:8] 49 if httpsClient, ok := httpsClientCache.Get(cacheKey); ok { 50 return httpsClient, nil 51 } 52 53 caFile, err := os.CreateTemp(certPath, fmt.Sprintf("ca-%s", name)) 54 if err != nil { 55 return nil, err 56 } 57 certFile, err := os.CreateTemp(certPath, fmt.Sprintf("cert-%s", name)) 58 if err != nil { 59 return nil, err 60 } 61 keyFile, err := os.CreateTemp(certPath, fmt.Sprintf("key-%s", name)) 62 if err != nil { 63 return nil, err 64 } 65 if err = dumpFromString(ctx, caFile, certFile, keyFile, ca, cert, key); err != nil { 66 return nil, err 67 } 68 options := tlsconfig.Options{ 69 CAFile: caFile.Name(), 70 CertFile: certFile.Name(), 71 KeyFile: keyFile.Name(), 72 InsecureSkipVerify: true, 73 } 74 defer os.Remove(caFile.Name()) 75 defer os.Remove(certFile.Name()) 76 defer os.Remove(keyFile.Name()) 77 tlsc, err := tlsconfig.Client(options) 78 if err != nil { 79 return nil, err 80 } 81 transport := getDefaultTransport() 82 transport.TLSClientConfig = tlsc 83 84 client = &http.Client{ 85 CheckRedirect: checkRedirect, 86 Transport: transport, 87 } 88 httpsClientCache.Set(cacheKey, client) 89 return client, nil 90 } 91 92 func getDefaultTransport() *http.Transport { 93 return &http.Transport{ 94 DialContext: (&net.Dialer{ 95 KeepAlive: time.Second * 30, 96 Timeout: time.Second * 30, 97 }).DialContext, 98 99 IdleConnTimeout: time.Second * 90, 100 MaxIdleConnsPerHost: runtime.GOMAXPROCS(0) + 1, 101 Proxy: http.ProxyFromEnvironment, 102 } 103 } 104 105 func getDefaultUnixSockTransport() *http.Transport { 106 return &http.Transport{ 107 DialContext: func(_ context.Context, _, addr string) (net.Conn, error) { 108 return net.DialTimeout("unix", strings.Split(addr, ":")[0], time.Second*30) 109 }, 110 111 IdleConnTimeout: time.Second * 90, 112 MaxIdleConnsPerHost: runtime.GOMAXPROCS(0) + 1, 113 DisableCompression: true, 114 } 115 } 116 117 func dumpFromString(ctx context.Context, ca, cert, key *os.File, caStr, certStr, keyStr string) error { 118 files := []*os.File{ca, cert, key} 119 data := []string{caStr, certStr, keyStr} 120 for i := 0; i < 3; i++ { 121 if _, err := files[i].WriteString(data[i]); err != nil { 122 return err 123 } 124 if err := files[i].Chmod(0444); err != nil { 125 return err 126 } 127 if err := files[i].Close(); err != nil { 128 return err 129 } 130 } 131 log.WithFunc("utils.dumpFromString").Debug(ctx, "Dump ca.pem, cert.pem, key.pem from string") 132 return nil 133 } 134 135 func checkRedirect(_ *http.Request, via []*http.Request) error { 136 if via[0].Method == http.MethodGet { 137 return http.ErrUseLastResponse 138 } 139 return types.ErrUnexpectedRedirect 140 }