blob: 495cf86949b82fdd9d18999a5776e39fe918bf6f [file] [log] [blame]
// 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
}