github.com/psyb0t/mattermost-server@v4.6.1-0.20180125161845-5503a1351abf+incompatible/utils/httpclient.go (about)

     1  // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package utils
     5  
     6  import (
     7  	"context"
     8  	"crypto/tls"
     9  	"errors"
    10  	"net"
    11  	"net/http"
    12  	"time"
    13  )
    14  
    15  const (
    16  	connectTimeout = 3 * time.Second
    17  	requestTimeout = 30 * time.Second
    18  )
    19  
    20  var reservedIPRanges []*net.IPNet
    21  
    22  func IsReservedIP(ip net.IP) bool {
    23  	for _, ipRange := range reservedIPRanges {
    24  		if ipRange.Contains(ip) {
    25  			return true
    26  		}
    27  	}
    28  	return false
    29  }
    30  
    31  func init() {
    32  	for _, cidr := range []string{
    33  		// See https://tools.ietf.org/html/rfc6890
    34  		"0.0.0.0/8",      // This host on this network
    35  		"10.0.0.0/8",     // Private-Use
    36  		"127.0.0.0/8",    // Loopback
    37  		"169.254.0.0/16", // Link Local
    38  		"172.16.0.0/12",  // Private-Use Networks
    39  		"192.168.0.0/16", // Private-Use Networks
    40  		"::/128",         // Unspecified Address
    41  		"::1/128",        // Loopback Address
    42  		"fc00::/7",       // Unique-Local
    43  		"fe80::/10",      // Linked-Scoped Unicast
    44  	} {
    45  		_, parsed, err := net.ParseCIDR(cidr)
    46  		if err != nil {
    47  			panic(err)
    48  		}
    49  		reservedIPRanges = append(reservedIPRanges, parsed)
    50  	}
    51  }
    52  
    53  type DialContextFunction func(ctx context.Context, network, addr string) (net.Conn, error)
    54  
    55  var AddressForbidden error = errors.New("address forbidden")
    56  
    57  func dialContextFilter(dial DialContextFunction, allowHost func(host string) bool, allowIP func(ip net.IP) bool) DialContextFunction {
    58  	return func(ctx context.Context, network, addr string) (net.Conn, error) {
    59  		host, port, err := net.SplitHostPort(addr)
    60  		if err != nil {
    61  			return nil, err
    62  		}
    63  
    64  		if allowHost != nil && allowHost(host) {
    65  			return dial(ctx, network, addr)
    66  		}
    67  
    68  		ips, err := net.LookupIP(host)
    69  		if err != nil {
    70  			return nil, err
    71  		}
    72  
    73  		var firstErr error
    74  		for _, ip := range ips {
    75  			select {
    76  			case <-ctx.Done():
    77  				return nil, ctx.Err()
    78  			default:
    79  			}
    80  
    81  			if allowIP == nil || !allowIP(ip) {
    82  				continue
    83  			}
    84  
    85  			conn, err := dial(ctx, network, net.JoinHostPort(ip.String(), port))
    86  			if err == nil {
    87  				return conn, nil
    88  			}
    89  			if firstErr == nil {
    90  				firstErr = err
    91  			}
    92  		}
    93  		if firstErr == nil {
    94  			return nil, AddressForbidden
    95  		}
    96  		return nil, firstErr
    97  	}
    98  }
    99  
   100  // NewHTTPClient returns a variation the default implementation of Client.
   101  // It uses a Transport with the same settings as the default Transport
   102  // but with the following modifications:
   103  // - shorter timeout for dial and TLS handshake (defined as constant
   104  //   "connectTimeout")
   105  // - timeout for the end-to-end request (defined as constant
   106  //   "requestTimeout")
   107  func NewHTTPClient(enableInsecureConnections bool, allowHost func(host string) bool, allowIP func(ip net.IP) bool) *http.Client {
   108  	dialContext := (&net.Dialer{
   109  		Timeout:   connectTimeout,
   110  		KeepAlive: 30 * time.Second,
   111  	}).DialContext
   112  
   113  	if allowHost != nil || allowIP != nil {
   114  		dialContext = dialContextFilter(dialContext, allowHost, allowIP)
   115  	}
   116  
   117  	client := &http.Client{
   118  		Transport: &http.Transport{
   119  			Proxy:                 http.ProxyFromEnvironment,
   120  			DialContext:           dialContext,
   121  			MaxIdleConns:          100,
   122  			IdleConnTimeout:       90 * time.Second,
   123  			TLSHandshakeTimeout:   connectTimeout,
   124  			ExpectContinueTimeout: 1 * time.Second,
   125  			TLSClientConfig: &tls.Config{
   126  				InsecureSkipVerify: enableInsecureConnections,
   127  			},
   128  		},
   129  		Timeout: requestTimeout,
   130  	}
   131  
   132  	return client
   133  }