mirror of
				https://github.com/tailscale/tailscale.git
				synced 2025-10-31 16:22:03 +01:00 
			
		
		
		
	These erroneously blocked a recent PR, which I fixed by simply re-running CI. But we might as well fix them anyway. These are mostly `printf` to `print` and a couple of `!=` to `!Equal()` Updates #cleanup Signed-off-by: Will Norris <will@tailscale.com>
		
			
				
	
	
		
			377 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			377 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright (c) Tailscale Inc & AUTHORS
 | |
| // SPDX-License-Identifier: BSD-3-Clause
 | |
| 
 | |
| //go:build linux
 | |
| 
 | |
| package tshttpproxy
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"net/http"
 | |
| 	"net/url"
 | |
| 	"os"
 | |
| 	"path/filepath"
 | |
| 	"strings"
 | |
| 	"testing"
 | |
| 	"time"
 | |
| 
 | |
| 	"tailscale.com/tstest"
 | |
| )
 | |
| 
 | |
| func TestSynologyProxyFromConfigCached(t *testing.T) {
 | |
| 	req, err := http.NewRequest("GET", "http://example.org/", nil)
 | |
| 	if err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 
 | |
| 	tstest.Replace(t, &synologyProxyConfigPath, filepath.Join(t.TempDir(), "proxy.conf"))
 | |
| 
 | |
| 	t.Run("no config file", func(t *testing.T) {
 | |
| 		if _, err := os.Stat(synologyProxyConfigPath); err == nil {
 | |
| 			t.Fatalf("%s must not exist for this test", synologyProxyConfigPath)
 | |
| 		}
 | |
| 
 | |
| 		cache.updated = time.Time{}
 | |
| 		cache.httpProxy = nil
 | |
| 		cache.httpsProxy = nil
 | |
| 
 | |
| 		if val, err := synologyProxyFromConfigCached(req); val != nil || err != nil {
 | |
| 			t.Fatalf("got %s, %v; want nil, nil", val, err)
 | |
| 		}
 | |
| 
 | |
| 		if got, want := cache.updated.UTC(), time.Unix(0, 0).UTC(); !got.Equal(want) {
 | |
| 			t.Fatalf("got %s, want %s", got, want)
 | |
| 		}
 | |
| 		if cache.httpProxy != nil {
 | |
| 			t.Fatalf("got %s, want nil", cache.httpProxy)
 | |
| 		}
 | |
| 		if cache.httpsProxy != nil {
 | |
| 			t.Fatalf("got %s, want nil", cache.httpsProxy)
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	t.Run("config file updated", func(t *testing.T) {
 | |
| 		cache.updated = time.Now()
 | |
| 		cache.httpProxy = nil
 | |
| 		cache.httpsProxy = nil
 | |
| 
 | |
| 		if err := os.WriteFile(synologyProxyConfigPath, []byte(`
 | |
| proxy_enabled=yes
 | |
| http_host=10.0.0.55
 | |
| http_port=80
 | |
| https_host=10.0.0.66
 | |
| https_port=443
 | |
| 		`), 0600); err != nil {
 | |
| 			t.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		val, err := synologyProxyFromConfigCached(req)
 | |
| 		if err != nil {
 | |
| 			t.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		if cache.httpProxy == nil {
 | |
| 			t.Fatal("http proxy was not cached")
 | |
| 		}
 | |
| 		if cache.httpsProxy == nil {
 | |
| 			t.Fatal("https proxy was not cached")
 | |
| 		}
 | |
| 
 | |
| 		if want := urlMustParse("http://10.0.0.55:80"); val.String() != want.String() {
 | |
| 			t.Fatalf("got %s; want %s", val, want)
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	t.Run("config file removed", func(t *testing.T) {
 | |
| 		cache.updated = time.Now()
 | |
| 		cache.httpProxy = urlMustParse("http://127.0.0.1/")
 | |
| 		cache.httpsProxy = urlMustParse("http://127.0.0.1/")
 | |
| 
 | |
| 		if err := os.Remove(synologyProxyConfigPath); err != nil && !os.IsNotExist(err) {
 | |
| 			t.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		val, err := synologyProxyFromConfigCached(req)
 | |
| 		if err != nil {
 | |
| 			t.Fatal(err)
 | |
| 		}
 | |
| 		if val != nil {
 | |
| 			t.Fatalf("got %s; want nil", val)
 | |
| 		}
 | |
| 		if cache.httpProxy != nil {
 | |
| 			t.Fatalf("got %s, want nil", cache.httpProxy)
 | |
| 		}
 | |
| 		if cache.httpsProxy != nil {
 | |
| 			t.Fatalf("got %s, want nil", cache.httpsProxy)
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	t.Run("picks proxy from request scheme", func(t *testing.T) {
 | |
| 		cache.updated = time.Now()
 | |
| 		cache.httpProxy = nil
 | |
| 		cache.httpsProxy = nil
 | |
| 
 | |
| 		if err := os.WriteFile(synologyProxyConfigPath, []byte(`
 | |
| proxy_enabled=yes
 | |
| http_host=10.0.0.55
 | |
| http_port=80
 | |
| https_host=10.0.0.66
 | |
| https_port=443
 | |
| 		`), 0600); err != nil {
 | |
| 			t.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		httpReq, err := http.NewRequest("GET", "http://example.com", nil)
 | |
| 		if err != nil {
 | |
| 			t.Fatal(err)
 | |
| 		}
 | |
| 		val, err := synologyProxyFromConfigCached(httpReq)
 | |
| 		if err != nil {
 | |
| 			t.Fatal(err)
 | |
| 		}
 | |
| 		if val == nil {
 | |
| 			t.Fatalf("got nil, want an http URL")
 | |
| 		}
 | |
| 		if got, want := val.String(), "http://10.0.0.55:80"; got != want {
 | |
| 			t.Fatalf("got %q, want %q", got, want)
 | |
| 		}
 | |
| 
 | |
| 		httpsReq, err := http.NewRequest("GET", "https://example.com", nil)
 | |
| 		if err != nil {
 | |
| 			t.Fatal(err)
 | |
| 		}
 | |
| 		val, err = synologyProxyFromConfigCached(httpsReq)
 | |
| 		if err != nil {
 | |
| 			t.Fatal(err)
 | |
| 		}
 | |
| 		if val == nil {
 | |
| 			t.Fatalf("got nil, want an http URL")
 | |
| 		}
 | |
| 		if got, want := val.String(), "http://10.0.0.66:443"; got != want {
 | |
| 			t.Fatalf("got %q, want %q", got, want)
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func TestSynologyProxiesFromConfig(t *testing.T) {
 | |
| 	var (
 | |
| 		openReader io.ReadCloser
 | |
| 		openErr    error
 | |
| 	)
 | |
| 	tstest.Replace(t, &openSynologyProxyConf, func() (io.ReadCloser, error) {
 | |
| 		return openReader, openErr
 | |
| 	})
 | |
| 
 | |
| 	t.Run("with config", func(t *testing.T) {
 | |
| 		mc := &mustCloser{Reader: strings.NewReader(`
 | |
| proxy_user=foo
 | |
| proxy_pwd=bar
 | |
| proxy_enabled=yes
 | |
| adv_enabled=yes
 | |
| bypass_enabled=yes
 | |
| auth_enabled=yes
 | |
| https_host=10.0.0.66
 | |
| https_port=8443
 | |
| http_host=10.0.0.55
 | |
| http_port=80
 | |
| 	`)}
 | |
| 		defer mc.check(t)
 | |
| 		openReader = mc
 | |
| 
 | |
| 		httpProxy, httpsProxy, err := synologyProxiesFromConfig()
 | |
| 
 | |
| 		if got, want := err, openErr; got != want {
 | |
| 			t.Fatalf("got %s, want %s", got, want)
 | |
| 		}
 | |
| 
 | |
| 		if got, want := httpsProxy, urlMustParse("http://foo:bar@10.0.0.66:8443"); got.String() != want.String() {
 | |
| 			t.Fatalf("got %s, want %s", got, want)
 | |
| 		}
 | |
| 
 | |
| 		if got, want := err, openErr; got != want {
 | |
| 			t.Fatalf("got %s, want %s", got, want)
 | |
| 		}
 | |
| 
 | |
| 		if got, want := httpProxy, urlMustParse("http://foo:bar@10.0.0.55:80"); got.String() != want.String() {
 | |
| 			t.Fatalf("got %s, want %s", got, want)
 | |
| 		}
 | |
| 
 | |
| 	})
 | |
| 
 | |
| 	t.Run("nonexistent config", func(t *testing.T) {
 | |
| 		openReader = nil
 | |
| 		openErr = os.ErrNotExist
 | |
| 
 | |
| 		httpProxy, httpsProxy, err := synologyProxiesFromConfig()
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("expected no error, got %s", err)
 | |
| 		}
 | |
| 		if httpProxy != nil {
 | |
| 			t.Fatalf("expected no url, got %s", httpProxy)
 | |
| 		}
 | |
| 		if httpsProxy != nil {
 | |
| 			t.Fatalf("expected no url, got %s", httpsProxy)
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	t.Run("error opening config", func(t *testing.T) {
 | |
| 		openReader = nil
 | |
| 		openErr = errors.New("example error")
 | |
| 
 | |
| 		httpProxy, httpsProxy, err := synologyProxiesFromConfig()
 | |
| 		if err != openErr {
 | |
| 			t.Fatalf("expected %s, got %s", openErr, err)
 | |
| 		}
 | |
| 		if httpProxy != nil {
 | |
| 			t.Fatalf("expected no url, got %s", httpProxy)
 | |
| 		}
 | |
| 		if httpsProxy != nil {
 | |
| 			t.Fatalf("expected no url, got %s", httpsProxy)
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| }
 | |
| 
 | |
| func TestParseSynologyConfig(t *testing.T) {
 | |
| 	cases := map[string]struct {
 | |
| 		input      string
 | |
| 		httpProxy  *url.URL
 | |
| 		httpsProxy *url.URL
 | |
| 		err        error
 | |
| 	}{
 | |
| 		"populated": {
 | |
| 			input: `
 | |
| proxy_user=foo
 | |
| proxy_pwd=bar
 | |
| proxy_enabled=yes
 | |
| adv_enabled=yes
 | |
| bypass_enabled=yes
 | |
| auth_enabled=yes
 | |
| https_host=10.0.0.66
 | |
| https_port=8443
 | |
| http_host=10.0.0.55
 | |
| http_port=80
 | |
| `,
 | |
| 			httpProxy:  urlMustParse("http://foo:bar@10.0.0.55:80"),
 | |
| 			httpsProxy: urlMustParse("http://foo:bar@10.0.0.66:8443"),
 | |
| 			err:        nil,
 | |
| 		},
 | |
| 		"no-auth": {
 | |
| 			input: `
 | |
| proxy_user=foo
 | |
| proxy_pwd=bar
 | |
| proxy_enabled=yes
 | |
| adv_enabled=yes
 | |
| bypass_enabled=yes
 | |
| auth_enabled=no
 | |
| https_host=10.0.0.66
 | |
| https_port=8443
 | |
| http_host=10.0.0.55
 | |
| http_port=80
 | |
| `,
 | |
| 			httpProxy:  urlMustParse("http://10.0.0.55:80"),
 | |
| 			httpsProxy: urlMustParse("http://10.0.0.66:8443"),
 | |
| 			err:        nil,
 | |
| 		},
 | |
| 		"http-only": {
 | |
| 			input: `
 | |
| proxy_user=foo
 | |
| proxy_pwd=bar
 | |
| proxy_enabled=yes
 | |
| adv_enabled=yes
 | |
| bypass_enabled=yes
 | |
| auth_enabled=yes
 | |
| https_host=
 | |
| https_port=8443
 | |
| http_host=10.0.0.55
 | |
| http_port=80
 | |
| `,
 | |
| 			httpProxy:  urlMustParse("http://foo:bar@10.0.0.55:80"),
 | |
| 			httpsProxy: nil,
 | |
| 			err:        nil,
 | |
| 		},
 | |
| 		"empty": {
 | |
| 			input: `
 | |
| proxy_user=
 | |
| proxy_pwd=
 | |
| proxy_enabled=
 | |
| adv_enabled=
 | |
| bypass_enabled=
 | |
| auth_enabled=
 | |
| https_host=
 | |
| https_port=
 | |
| http_host=
 | |
| http_port=
 | |
| `,
 | |
| 			httpProxy:  nil,
 | |
| 			httpsProxy: nil,
 | |
| 			err:        nil,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for name, example := range cases {
 | |
| 		t.Run(name, func(t *testing.T) {
 | |
| 			httpProxy, httpsProxy, err := parseSynologyConfig(strings.NewReader(example.input))
 | |
| 			if err != example.err {
 | |
| 				t.Fatal(err)
 | |
| 			}
 | |
| 			if example.err != nil {
 | |
| 				return
 | |
| 			}
 | |
| 
 | |
| 			if example.httpProxy == nil && httpProxy != nil {
 | |
| 				t.Fatalf("got %s, want nil", httpProxy)
 | |
| 			}
 | |
| 
 | |
| 			if example.httpProxy != nil {
 | |
| 				if httpProxy == nil {
 | |
| 					t.Fatalf("got nil, want %s", example.httpProxy)
 | |
| 				}
 | |
| 
 | |
| 				if got, want := example.httpProxy.String(), httpProxy.String(); got != want {
 | |
| 					t.Fatalf("got %s, want %s", got, want)
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			if example.httpsProxy == nil && httpsProxy != nil {
 | |
| 				t.Fatalf("got %s, want nil", httpProxy)
 | |
| 			}
 | |
| 
 | |
| 			if example.httpsProxy != nil {
 | |
| 				if httpsProxy == nil {
 | |
| 					t.Fatalf("got nil, want %s", example.httpsProxy)
 | |
| 				}
 | |
| 
 | |
| 				if got, want := example.httpsProxy.String(), httpsProxy.String(); got != want {
 | |
| 					t.Fatalf("got %s, want %s", got, want)
 | |
| 				}
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| func urlMustParse(u string) *url.URL {
 | |
| 	r, err := url.Parse(u)
 | |
| 	if err != nil {
 | |
| 		panic(fmt.Sprintf("urlMustParse: %s", err))
 | |
| 	}
 | |
| 	return r
 | |
| }
 | |
| 
 | |
| type mustCloser struct {
 | |
| 	io.Reader
 | |
| 	closed bool
 | |
| }
 | |
| 
 | |
| func (m *mustCloser) Close() error {
 | |
| 	m.closed = true
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (m *mustCloser) check(t *testing.T) {
 | |
| 	if !m.closed {
 | |
| 		t.Errorf("mustCloser wrapping %#v was not closed at time of check", m.Reader)
 | |
| 	}
 | |
| }
 |