github.com/demisto/mattermost-server@v4.9.0-rc3+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 }