From 4e59a27293394b6d5529efd13ad711daebbc0eb3 Mon Sep 17 00:00:00 2001 From: Harrison Healey Date: Wed, 26 Sep 2018 12:42:51 -0400 Subject: Move HTTPService and ConfigService into services package (#9422) * Move HTTPService and ConfigService into utils package * Re-add StaticConfigService * Move config and http services into their own packages --- utils/httpclient.go | 133 ------------------------------- utils/httpclient_test.go | 120 ---------------------------- utils/testutils/mocked_http_service.go | 28 +++++++ utils/testutils/static_config_service.go | 24 ++++++ 4 files changed, 52 insertions(+), 253 deletions(-) delete mode 100644 utils/httpclient.go delete mode 100644 utils/httpclient_test.go create mode 100644 utils/testutils/mocked_http_service.go create mode 100644 utils/testutils/static_config_service.go (limited to 'utils') diff --git a/utils/httpclient.go b/utils/httpclient.go deleted file mode 100644 index cb68462e3..000000000 --- a/utils/httpclient.go +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package utils - -import ( - "context" - "crypto/tls" - "errors" - "net" - "net/http" - "time" -) - -const ( - connectTimeout = 3 * time.Second - requestTimeout = 30 * time.Second -) - -var reservedIPRanges []*net.IPNet - -func IsReservedIP(ip net.IP) bool { - for _, ipRange := range reservedIPRanges { - if ipRange.Contains(ip) { - return true - } - } - return false -} - -func init() { - for _, cidr := range []string{ - // See https://tools.ietf.org/html/rfc6890 - "0.0.0.0/8", // This host on this network - "10.0.0.0/8", // Private-Use - "127.0.0.0/8", // Loopback - "169.254.0.0/16", // Link Local - "172.16.0.0/12", // Private-Use Networks - "192.168.0.0/16", // Private-Use Networks - "::/128", // Unspecified Address - "::1/128", // Loopback Address - "fc00::/7", // Unique-Local - "fe80::/10", // Linked-Scoped Unicast - } { - _, parsed, err := net.ParseCIDR(cidr) - if err != nil { - panic(err) - } - reservedIPRanges = append(reservedIPRanges, parsed) - } -} - -type DialContextFunction func(ctx context.Context, network, addr string) (net.Conn, error) - -var AddressForbidden error = errors.New("address forbidden, you may need to set AllowedUntrustedInternalConnections to allow an integration access to your internal network") - -func dialContextFilter(dial DialContextFunction, allowHost func(host string) bool, allowIP func(ip net.IP) bool) DialContextFunction { - return func(ctx context.Context, network, addr string) (net.Conn, error) { - host, port, err := net.SplitHostPort(addr) - if err != nil { - return nil, err - } - - if allowHost != nil && allowHost(host) { - return dial(ctx, network, addr) - } - - ips, err := net.LookupIP(host) - if err != nil { - return nil, err - } - - var firstErr error - for _, ip := range ips { - select { - case <-ctx.Done(): - return nil, ctx.Err() - default: - } - - if allowIP == nil || !allowIP(ip) { - continue - } - - conn, err := dial(ctx, network, net.JoinHostPort(ip.String(), port)) - if err == nil { - return conn, nil - } - if firstErr == nil { - firstErr = err - } - } - if firstErr == nil { - return nil, AddressForbidden - } - return nil, firstErr - } -} - -// NewHTTPClient returns a variation the default implementation of Client. -// It uses a Transport with the same settings as the default Transport -// but with the following modifications: -// - shorter timeout for dial and TLS handshake (defined as constant -// "connectTimeout") -// - timeout for the end-to-end request (defined as constant -// "requestTimeout") -func NewHTTPClient(enableInsecureConnections bool, allowHost func(host string) bool, allowIP func(ip net.IP) bool) *http.Client { - dialContext := (&net.Dialer{ - Timeout: connectTimeout, - KeepAlive: 30 * time.Second, - }).DialContext - - if allowHost != nil || allowIP != nil { - dialContext = dialContextFilter(dialContext, allowHost, allowIP) - } - - client := &http.Client{ - Transport: &http.Transport{ - Proxy: http.ProxyFromEnvironment, - DialContext: dialContext, - MaxIdleConns: 100, - IdleConnTimeout: 90 * time.Second, - TLSHandshakeTimeout: connectTimeout, - ExpectContinueTimeout: 1 * time.Second, - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: enableInsecureConnections, - }, - }, - Timeout: requestTimeout, - } - - return client -} diff --git a/utils/httpclient_test.go b/utils/httpclient_test.go deleted file mode 100644 index e07c54d08..000000000 --- a/utils/httpclient_test.go +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package utils - -import ( - "context" - "fmt" - "io/ioutil" - "net" - "net/http" - "net/http/httptest" - "net/url" - "testing" -) - -func TestHTTPClient(t *testing.T) { - for _, allowInternal := range []bool{true, false} { - c := NewHTTPClient(false, func(_ string) bool { return false }, func(ip net.IP) bool { return allowInternal || !IsReservedIP(ip) }) - for _, tc := range []struct { - URL string - IsInternal bool - }{ - { - URL: "https://google.com", - IsInternal: false, - }, - { - URL: "https://127.0.0.1", - IsInternal: true, - }, - } { - _, err := c.Get(tc.URL) - if !tc.IsInternal { - if err != nil { - t.Fatal("google is down?") - } - } else { - allowed := !tc.IsInternal || allowInternal - success := err == nil - switch e := err.(type) { - case *net.OpError: - success = e.Err != AddressForbidden - case *url.Error: - success = e.Err != AddressForbidden - } - if success != allowed { - t.Fatalf("failed for %v. allowed: %v, success %v", tc.URL, allowed, success) - } - } - } - } -} - -func TestHTTPClientWithProxy(t *testing.T) { - proxy := createProxyServer() - defer proxy.Close() - - c := NewHTTPClient(true, nil, nil) - purl, _ := url.Parse(proxy.URL) - c.Transport.(*http.Transport).Proxy = http.ProxyURL(purl) - - resp, err := c.Get("http://acme.com") - if err != nil { - t.Fatal(err) - } - defer resp.Body.Close() - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Fatal(err) - } - if string(body) != "proxy" { - t.FailNow() - } -} - -func createProxyServer() *httptest.Server { - return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(200) - w.Header().Set("Content-Type", "text/plain; charset=us-ascii") - fmt.Fprint(w, "proxy") - })) -} - -func TestDialContextFilter(t *testing.T) { - for _, tc := range []struct { - Addr string - IsValid bool - }{ - { - Addr: "google.com:80", - IsValid: true, - }, - { - Addr: "8.8.8.8:53", - IsValid: true, - }, - { - Addr: "127.0.0.1:80", - }, - { - Addr: "10.0.0.1:80", - IsValid: true, - }, - } { - didDial := false - filter := dialContextFilter(func(ctx context.Context, network, addr string) (net.Conn, error) { - didDial = true - return nil, nil - }, func(host string) bool { return host == "10.0.0.1" }, func(ip net.IP) bool { return !IsReservedIP(ip) }) - _, err := filter(context.Background(), "", tc.Addr) - switch { - case tc.IsValid == (err == AddressForbidden) || (err != nil && err != AddressForbidden): - t.Errorf("unexpected err for %v (%v)", tc.Addr, err) - case tc.IsValid != didDial: - t.Errorf("unexpected didDial for %v", tc.Addr) - } - } -} diff --git a/utils/testutils/mocked_http_service.go b/utils/testutils/mocked_http_service.go new file mode 100644 index 000000000..b1e7f6963 --- /dev/null +++ b/utils/testutils/mocked_http_service.go @@ -0,0 +1,28 @@ +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package testutils + +import ( + "net/http" + "net/http/httptest" +) + +type MockedHTTPService struct { + Server *httptest.Server +} + +func MakeMockedHTTPService(handler http.Handler) *MockedHTTPService { + return &MockedHTTPService{ + Server: httptest.NewServer(handler), + } +} + +func (h *MockedHTTPService) MakeClient(trustURLs bool) *http.Client { + return h.Server.Client() +} + +func (h *MockedHTTPService) Close() { + h.Server.CloseClientConnections() + h.Server.Close() +} diff --git a/utils/testutils/static_config_service.go b/utils/testutils/static_config_service.go new file mode 100644 index 000000000..44705ff6e --- /dev/null +++ b/utils/testutils/static_config_service.go @@ -0,0 +1,24 @@ +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package testutils + +import ( + "github.com/mattermost/mattermost-server/model" +) + +type StaticConfigService struct { + Cfg *model.Config +} + +func (s StaticConfigService) Config() *model.Config { + return s.Cfg +} + +func (StaticConfigService) AddConfigListener(func(old, current *model.Config)) string { + return "" +} + +func (StaticConfigService) RemoveConfigListener(string) { + +} -- cgit v1.2.3-1-g7c22