| // Licensed to the Apache Software Foundation (ASF) under one or more |
| // contributor license agreements. See the NOTICE file distributed with |
| // this work for additional information regarding copyright ownership. |
| // The ASF licenses this file to You under the Apache License, Version 2.0 |
| // (the "License"); you may not use this file except in compliance with |
| // the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package rest |
| |
| import ( |
| "bytes" |
| "crypto/tls" |
| "errors" |
| "fmt" |
| "github.com/apache/servicecomb-service-center/pkg/tlsutil" |
| "github.com/apache/servicecomb-service-center/pkg/util" |
| "net/http" |
| "net/url" |
| "os" |
| "strings" |
| "time" |
| ) |
| |
| var defaultURLClientOption = URLClientOption{ |
| Compressed: true, |
| VerifyPeer: true, |
| SSLVersion: tls.VersionTLS12, |
| HandshakeTimeout: 30 * time.Second, |
| ResponseHeaderTimeout: 180 * time.Second, |
| RequestTimeout: 300 * time.Second, |
| ConnsPerHost: DEFAULT_CONN_POOL_PER_HOST_SIZE, |
| } |
| |
| var defaultClientTLSOptions = tlsutil.DefaultClientTLSOptions() |
| |
| type URLClientOption struct { |
| SSLEnabled bool |
| Compressed bool |
| VerifyPeer bool |
| CAFile string |
| CertFile string |
| CertKeyFile string |
| CertKeyPWD string |
| SSLVersion uint16 |
| HandshakeTimeout time.Duration |
| ResponseHeaderTimeout time.Duration |
| RequestTimeout time.Duration |
| ConnsPerHost int |
| } |
| |
| type URLClient struct { |
| *http.Client |
| |
| TLS *tls.Config |
| |
| Cfg URLClientOption |
| } |
| |
| func (client *URLClient) HttpDo(method string, rawURL string, headers http.Header, body []byte) (resp *http.Response, err error) { |
| if strings.HasPrefix(rawURL, "https") { |
| if transport, ok := client.Client.Transport.(*http.Transport); ok { |
| transport.TLSClientConfig = client.TLS |
| } |
| } |
| |
| if headers == nil { |
| headers = make(http.Header) |
| } |
| |
| if _, ok := headers["Host"]; !ok { |
| parsedURL, err := url.Parse(rawURL) |
| if err != nil { |
| return nil, err |
| } |
| headers.Set("Host", parsedURL.Host) |
| } |
| if _, ok := headers["Accept"]; !ok { |
| headers.Set("Accept", "*/*") |
| } |
| if _, ok := headers["Accept-Encoding"]; !ok && client.Cfg.Compressed { |
| headers.Set("Accept-Encoding", "deflate, gzip") |
| } |
| |
| req, err := http.NewRequest(method, rawURL, bytes.NewBuffer(body)) |
| if err != nil { |
| return nil, errors.New(fmt.Sprintf("create request failed: %s", err.Error())) |
| } |
| req.Header = headers |
| |
| resp, err = client.Client.Do(req) |
| if err != nil { |
| return nil, err |
| } |
| |
| if os.Getenv("DEBUG_MODE") == "1" { |
| fmt.Println("--- BEGIN ---") |
| fmt.Printf("> %s %s %s\n", req.Method, req.URL.RequestURI(), req.Proto) |
| for key, header := range req.Header { |
| for _, value := range header { |
| fmt.Printf("> %s: %s\n", key, value) |
| } |
| } |
| fmt.Println(">") |
| fmt.Println(util.BytesToStringWithNoCopy(body)) |
| fmt.Printf("< %s %s\n", resp.Proto, resp.Status) |
| for key, header := range resp.Header { |
| for _, value := range header { |
| fmt.Printf("< %s: %s\n", key, value) |
| } |
| } |
| fmt.Println("<") |
| fmt.Println("--- END ---") |
| } |
| return resp, nil |
| } |
| |
| func setOptionDefaultValue(o *URLClientOption) URLClientOption { |
| if o == nil { |
| return defaultURLClientOption |
| } |
| |
| option := *o |
| if option.RequestTimeout <= 0 { |
| option.RequestTimeout = defaultURLClientOption.RequestTimeout |
| } |
| if option.HandshakeTimeout <= 0 { |
| option.HandshakeTimeout = defaultURLClientOption.HandshakeTimeout |
| } |
| if option.ResponseHeaderTimeout <= 0 { |
| option.ResponseHeaderTimeout = defaultURLClientOption.ResponseHeaderTimeout |
| } |
| if option.SSLVersion == 0 { |
| option.SSLVersion = defaultURLClientOption.SSLVersion |
| } |
| return option |
| } |
| |
| func GetURLClient(o URLClientOption) (client *URLClient, err error) { |
| option := setOptionDefaultValue(&o) |
| client = &URLClient{ |
| Client: &http.Client{ |
| Transport: &http.Transport{ |
| MaxIdleConnsPerHost: option.ConnsPerHost, |
| TLSHandshakeTimeout: option.HandshakeTimeout, |
| ResponseHeaderTimeout: option.ResponseHeaderTimeout, |
| DisableCompression: !option.Compressed, |
| }, |
| Timeout: option.RequestTimeout, |
| }, |
| Cfg: option, |
| } |
| |
| if option.SSLEnabled { |
| opts := append(defaultClientTLSOptions, |
| tlsutil.WithVerifyPeer(option.VerifyPeer), |
| tlsutil.WithCA(option.CAFile), |
| tlsutil.WithCert(option.CertFile), |
| tlsutil.WithKey(option.CertKeyFile), |
| tlsutil.WithKeyPass(option.CertKeyPWD)) |
| |
| client.TLS, err = tlsutil.GetClientTLSConfig(opts...) |
| if err != nil { |
| return nil, err |
| } |
| } |
| return |
| } |