plaprox

Proxy for plausible.io scripts written in Go.
git clone git://vcs.sapka.me/plaprox
Log | Files | Refs | README | LICENSE

commit 72320321465c1bf8564dd05524ff12742f4fe71b
parent b1193b66930246396763b35cd823002f3ce972a0
Author: MichaƂ M. Sapka <michal@sapka.me>
Date:   Thu, 13 Jul 2023 23:26:41 +0200

test: add first batch of tests

Diffstat:
Mmain.go | 108++++++++++++++++++++++++++++++++++++-------------------------------------------
Amain_test.go | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 127 insertions(+), 59 deletions(-)

diff --git a/main.go b/main.go @@ -19,78 +19,33 @@ package main import ( "fmt" + "io" "log" "net/http" - "io" - "strings" - + "gopkg.in/sevlyar/go-daemon.v0" ) -// Proxies requests to Plausible.io and returns the response to the client -func proxy(w http.ResponseWriter, r *http.Request) { - - client := &http.Client{} - - proxiedr := r.clone - - - /* - req, err := http.NewRequest(r.Method, "https://plausible.io" + r.URL.Path, r.Body) - - fmt.Println(r.Body) - - - - if err != nil { - log.Fatal(err) - } - - /* - for k,v := range r.Header { - value := strings.Join(v[:], " ") - req.Header.Set(string(k), value) - } - */ - - /* - resp, err := client.Do(req) - if err != nil { - log.Fatal(err) - } - - defer resp.Body.Close() - - bodyBytes, err := io.ReadAll(resp.Body) - if err != nil { - log.Fatal(err) - } - - bodyString := string(bodyBytes) - for k, v := range resp.Header { - fmt.Println(string(k)) - value := strings.Join(v[:], " ") - w.Header().Set(string(k), value) - } - */ - - fmt.Fprintf(w, bodyString) +// HTTPClient interface +type HTTPClient interface { + Do(req *http.Request) (*http.Response, error) } +var ( + Client HTTPClient +) -func serveProxy() { - http.HandleFunc("/js/script.js", proxy) - http.HandleFunc("/api/event", proxy) - - if err := http.ListenAndServe(":9090", nil); err != nil { - log.Fatal(err) - } +func init() { + Client = &http.Client{} } +const PlausibleHost = "plausible.io" + func main() { + serveProxy() return - + cntxt := &daemon.Context{ PidFileName: "plaprox.pid", PidFilePerm: 0644, @@ -112,3 +67,38 @@ func main() { serveProxy() } + +// Proxies requests to Plausible.io and returns the response to the client +func Proxy(w http.ResponseWriter, r *http.Request) { + req, _ := http.NewRequest(r.Method, "https://"+PlausibleHost+r.URL.Path, r.Body) + + for key, values := range r.Header { + for _, value := range values { + req.Header.Add(key, value) + } + } + + resp, _ := Client.Do(req) + defer resp.Body.Close() + + bodyBytes, _ := io.ReadAll(resp.Body) + + bodyString := string(bodyBytes) + for key, values := range resp.Header { + w.Header().Del(key) + for _, value := range values { + w.Header().Add(key, value) + } + } + + fmt.Fprintf(w, bodyString) +} + +func serveProxy() { + http.HandleFunc("/js/script.js", Proxy) + http.HandleFunc("/api/event", Proxy) + + if err := http.ListenAndServe(":9090", nil); err != nil { + log.Fatal(err) + } +} diff --git a/main_test.go b/main_test.go @@ -0,0 +1,78 @@ +package main + +import ( + "bytes" + + "fmt" + "io/ioutil" + "net/http" + "net/http/httptest" + "testing" +) + +// Custom type that allows setting the func that our Mock Do func will run instead +type MockDoType func(req *http.Request) (*http.Response, error) // MockClient is the mock client +type MockClient struct { + MockDo MockDoType +} // Overriding what the Do function should "do" in our MockClient +func (m *MockClient) Do(req *http.Request) (*http.Response, error) { + return m.MockDo(req) +} + +func mockProxiedCall(url string, responseBody string, headers map[string][]string, t *testing.T) { + + r := ioutil.NopCloser(bytes.NewReader([]byte(responseBody))) + Client = &MockClient{ + MockDo: func(req *http.Request) (*http.Response, error) { + if req.Host != "plausible.io" { + t.Errorf("Invalid host. Got: %q, want: %q", req.Host, "plausible.io") + + } + if req.URL.Path != url { + t.Errorf("Invalid url. Got: %q, want: %q", req.URL.Path, url) + } + if fmt.Sprint(req.Header) != fmt.Sprint(headers) { + t.Errorf("Invalid proxied headers. Got: %q, want: %q", req.Header, headers) + + } + + return &http.Response{ + StatusCode: 200, + Body: r, + Header: headers, + }, nil + + }, + } + +} + +func TestProxy(t *testing.T) { + proxiedBody := `[{"full_name": "mock-repo" }]` + wantedHeaders := map[string][]string{ + "Key": []string{"value"}, + "Key2": []string{"value1", "value2"}, + "Content-Type": []string{"application/javascript"}, + } + + mockProxiedCall("/js/script.js", proxiedBody, wantedHeaders, t) + + request, _ := http.NewRequest(http.MethodGet, "/js/script.js", nil) + + request.Header.Add("key", "value") + request.Header.Add("key2", "value1") + request.Header.Add("key2", "value2") + request.Header.Set("Content-Type", "application/javascript") + + response := httptest.NewRecorder() + + Proxy(response, request) + + if response.Body.String() != proxiedBody { + t.Errorf("got %q, want %q", response.Body.String(), proxiedBody) + } + + if fmt.Sprint(response.Header()) != fmt.Sprint(wantedHeaders) { + t.Errorf("Invalid returned headers. Got: %q, want: %q", response.Header(), wantedHeaders) + } +}