github.com/jlevesy/mattermost-server@v5.3.2-0.20181003190404-7468f35cb0c8+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 "github.com/mattermost/mattermost-server/model" 15 ) 16 17 const ( 18 connectTimeout = 3 * time.Second 19 requestTimeout = 30 * time.Second 20 ) 21 22 var reservedIPRanges []*net.IPNet 23 24 func IsReservedIP(ip net.IP) bool { 25 for _, ipRange := range reservedIPRanges { 26 if ipRange.Contains(ip) { 27 return true 28 } 29 } 30 return false 31 } 32 33 var defaultUserAgent string 34 35 func init() { 36 for _, cidr := range []string{ 37 // See https://tools.ietf.org/html/rfc6890 38 "0.0.0.0/8", // This host on this network 39 "10.0.0.0/8", // Private-Use 40 "127.0.0.0/8", // Loopback 41 "169.254.0.0/16", // Link Local 42 "172.16.0.0/12", // Private-Use Networks 43 "192.168.0.0/16", // Private-Use Networks 44 "::/128", // Unspecified Address 45 "::1/128", // Loopback Address 46 "fc00::/7", // Unique-Local 47 "fe80::/10", // Linked-Scoped Unicast 48 } { 49 _, parsed, err := net.ParseCIDR(cidr) 50 if err != nil { 51 panic(err) 52 } 53 reservedIPRanges = append(reservedIPRanges, parsed) 54 } 55 defaultUserAgent = "mattermost-" + model.CurrentVersion 56 } 57 58 type DialContextFunction func(ctx context.Context, network, addr string) (net.Conn, error) 59 60 var AddressForbidden error = errors.New("address forbidden, you may need to set AllowedUntrustedInternalConnections to allow an integration access to your internal network") 61 62 func dialContextFilter(dial DialContextFunction, allowHost func(host string) bool, allowIP func(ip net.IP) bool) DialContextFunction { 63 return func(ctx context.Context, network, addr string) (net.Conn, error) { 64 host, port, err := net.SplitHostPort(addr) 65 if err != nil { 66 return nil, err 67 } 68 69 if allowHost != nil && allowHost(host) { 70 return dial(ctx, network, addr) 71 } 72 73 ips, err := net.LookupIP(host) 74 if err != nil { 75 return nil, err 76 } 77 78 var firstErr error 79 for _, ip := range ips { 80 select { 81 case <-ctx.Done(): 82 return nil, ctx.Err() 83 default: 84 } 85 86 if allowIP == nil || !allowIP(ip) { 87 continue 88 } 89 90 conn, err := dial(ctx, network, net.JoinHostPort(ip.String(), port)) 91 if err == nil { 92 return conn, nil 93 } 94 if firstErr == nil { 95 firstErr = err 96 } 97 } 98 if firstErr == nil { 99 return nil, AddressForbidden 100 } 101 return nil, firstErr 102 } 103 } 104 105 type Client struct { 106 *http.Client 107 } 108 109 func (c *Client) Do(req *http.Request) (*http.Response, error) { 110 req.Header.Set("User-Agent", defaultUserAgent) 111 return c.Client.Do(req) 112 } 113 114 // NewHTTPClient returns a variation the default implementation of Client. 115 // It uses a Transport with the same settings as the default Transport 116 // but with the following modifications: 117 // - shorter timeout for dial and TLS handshake (defined as constant 118 // "connectTimeout") 119 // - timeout for the end-to-end request (defined as constant 120 // "requestTimeout") 121 func NewHTTPClient(enableInsecureConnections bool, allowHost func(host string) bool, allowIP func(ip net.IP) bool) *Client { 122 dialContext := (&net.Dialer{ 123 Timeout: connectTimeout, 124 KeepAlive: 30 * time.Second, 125 }).DialContext 126 127 if allowHost != nil || allowIP != nil { 128 dialContext = dialContextFilter(dialContext, allowHost, allowIP) 129 } 130 131 client := &http.Client{ 132 Transport: &http.Transport{ 133 Proxy: http.ProxyFromEnvironment, 134 DialContext: dialContext, 135 MaxIdleConns: 100, 136 IdleConnTimeout: 90 * time.Second, 137 TLSHandshakeTimeout: connectTimeout, 138 ExpectContinueTimeout: 1 * time.Second, 139 TLSClientConfig: &tls.Config{ 140 InsecureSkipVerify: enableInsecureConnections, 141 }, 142 }, 143 Timeout: requestTimeout, 144 } 145 146 return &Client{Client: client} 147 }