github.com/lologarithm/mattermost-server@v5.3.2-0.20181002060438-c82a84ed765b+incompatible/services/httpservice/client.go (about) 1 // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package httpservice 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, you may need to set AllowedUntrustedInternalConnections to allow an integration access to your internal network") 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 }