|
| 1 | +package utils |
| 2 | + |
| 3 | +import ( |
| 4 | + "context" |
| 5 | + "fmt" |
| 6 | + "io/ioutil" |
| 7 | + "net" |
| 8 | + "net/http" |
| 9 | + "os" |
| 10 | + "runtime" |
| 11 | + "strings" |
| 12 | + "time" |
| 13 | + |
| 14 | + "github.com/cornelk/hashmap" |
| 15 | + "github.com/docker/go-connections/tlsconfig" |
| 16 | + |
| 17 | + "github.com/projecteru2/core/log" |
| 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 = hashmap.New(32) |
| 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.(*http.Client), nil |
| 51 | + } |
| 52 | + |
| 53 | + caFile, err := ioutil.TempFile(certPath, fmt.Sprintf("ca-%s", name)) |
| 54 | + if err != nil { |
| 55 | + return nil, err |
| 56 | + } |
| 57 | + certFile, err := ioutil.TempFile(certPath, fmt.Sprintf("cert-%s", name)) |
| 58 | + if err != nil { |
| 59 | + return nil, err |
| 60 | + } |
| 61 | + keyFile, err := ioutil.TempFile(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.Debug(ctx, "[dumpFromString] Dump ca.pem, cert.pem, key.pem from string") |
| 132 | + return nil |
| 133 | +} |
| 134 | + |
| 135 | +func checkRedirect(req *http.Request, via []*http.Request) error { |
| 136 | + if via[0].Method == http.MethodGet { |
| 137 | + return http.ErrUseLastResponse |
| 138 | + } |
| 139 | + return fmt.Errorf("unexpected redirect") |
| 140 | +} |
0 commit comments