commit 91156ec9a54651b4c1eef7020e3b4cd71e3e23e7
parent 52a2ba0fdeb3677dd9675afce2b8be84d8a31d48
Author: Michał M. Sapka <michal@sapka.me>
Date: Sat, 15 Jul 2023 22:46:46 +0200
feat: proxy calls to Plausible
and fix tests
and fix readme
Diffstat:
M | README | | | 24 | +++++++++++++++++++++--- |
M | main.go | | | 73 | +++++++++++++++++++++++++++++++++++++------------------------------------ |
M | main_test.go | | | 54 | +++++++++++++++++++++++++++--------------------------- |
3 files changed, 85 insertions(+), 66 deletions(-)
diff --git a/README b/README
@@ -21,6 +21,10 @@ OpenBSD/Httpd/Relayd. I love this setup, but it does not provide proxy_pass.
So I wrote this little fella.
+License
+-------
+Refer to LICENSE
+
Architecture
------------
@@ -41,10 +45,19 @@ Contributing
Found this interesting? Cool! Know how to improve things? Even better! Email me
a git patch - my email can be found at https://michal.sapka.me/about/.
+Telling me how to do Go properly is also an option.
-Known problems
---------------
+
+Known problems and ideas
+-----------------------
- GET requests can be cached. Plaprox does not currently do this.
+- Script will break when handling gzip responses. For now we remove headers, so
+ Plausible will not compress response. It would be cool to work with gzip and
+ return gzip if requestes.
+- An option to force not running as a deamon would be nice. Currently one needs
+ to uncomment two lines in code.
+- An option to set port. Currently one needs to modify the code.
+- An option to define location of logs.
Testing
@@ -53,6 +66,7 @@ You can run the tests via:
go test
+Please note that I run this on my server running OpenBSD. No other OS is tested.
Compiling
---------
@@ -71,7 +85,7 @@ You can run the app as a process:
./plaprox
-But the preferred way is to treat it a demon. Please refer to documentation of
+But the preferred way is to treat it a daemon. Please refer to documentation of
your OS of choice
@@ -82,3 +96,7 @@ serveProxy() in main.go. Add another handleFunc for the required path, and any
requests to the proxy will be handled automatically:
http.HandleFunc("/another/proxied/path.js", Proxy)
+
+Donate
+-----
+Want to donate? Great! Try to find a good cause, like OpenBSD fundation.
diff --git a/main.go b/main.go
@@ -1,20 +1,5 @@
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || plan9p
-/*
- Copyright 2023 Michał Sapka (https://michal.sapka.me)
-
- Permission to use, copy, modify, and/or distribute this software for
- any purpose with or without fee is hereby granted.
-
- THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL
- WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
- OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE
- FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
- DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
- AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
- OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
package main
import (
@@ -41,8 +26,9 @@ func init() {
func main() {
- serveProxy()
- return
+ // Remove comments to act as standard process
+ serveProxy()
+ return
cntxt := &daemon.Context{
PidFileName: "plaprox.pid",
@@ -66,24 +52,47 @@ func main() {
serveProxy()
}
-// Proxies requests to Plausible.io and returns the response to the client
+func serveProxy() {
+ // Add more endpoints if needed
+ http.HandleFunc("/js/script.js", Proxy)
+ http.HandleFunc("/api/event", Proxy)
+
+ if err := http.ListenAndServe(":9090", nil); err != nil {
+ log.Fatal(err)
+ }
+}
+
func Proxy(w http.ResponseWriter, r *http.Request) {
- req, _ := http.NewRequest(r.Method, "https://plausible.io"+r.URL.Path, r.Body)
+ log.Print("trying to proxy " + r.URL.Path)
+ req, err := http.NewRequest(r.Method, "https://plausible.io"+r.URL.Path, r.Body)
+ if err != nil {
+ log.Fatal(err)
+ return
+ }
- /*
- Forwarding headers breaks reading the returned file in browsers. Seems to be
- not needed, so I'll just comment this logic out
- for key, values := range r.Header {
+ for key, values := range r.Header {
+ // We don't want to deal with gzip
+ if key != "Accept-Encoding" {
req.Header.Del(key)
for _, value := range values {
req.Header.Add(key, value)
}
}
- */
- resp, _ := Client.Do(req)
+ }
+
+ resp, err := Client.Do(req)
+ if err != nil {
+ log.Fatal(err)
+ return
+ }
+
defer resp.Body.Close()
- bodyBytes, _ := io.ReadAll(resp.Body)
+ bodyBytes, err := io.ReadAll(resp.Body)
+ if err != nil {
+ log.Fatal(err)
+ return
+ }
bodyString := string(bodyBytes)
for key, values := range resp.Header {
@@ -93,15 +102,7 @@ func Proxy(w http.ResponseWriter, r *http.Request) {
}
}
- fmt.Fprintf(w, bodyString)
-}
+ log.Print("ok " + r.URL.Path)
-func serveProxy() {
-
- http.HandleFunc("/js/script.js", Proxy)
- http.HandleFunc("/api/event", Proxy)
-
- if err := http.ListenAndServe(":9090", nil); err != nil {
- log.Fatal(err)
- }
+ fmt.Fprintf(w, bodyString)
}
diff --git a/main_test.go b/main_test.go
@@ -2,13 +2,10 @@ package main
import (
"bytes"
-
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
- // "net/http/httputil"
-
"testing"
)
@@ -21,8 +18,7 @@ func (m *MockClient) Do(req *http.Request) (*http.Response, error) {
return m.MockDo(req)
}
-func mockProxiedCall(method string, url string, responseBody string, sentBody string, headers map[string][]string, t *testing.T) {
-
+func mockCall(method string, url string, responseBody string, sentBody string, sentHeaders map[string][]string, t *testing.T) {
r := ioutil.NopCloser(bytes.NewReader([]byte(responseBody)))
Client = &MockClient{
MockDo: func(req *http.Request) (*http.Response, error) {
@@ -37,27 +33,23 @@ func mockProxiedCall(method string, url string, responseBody string, sentBody st
t.Errorf("Invalid method. Got: %q, want: %q", req.Method, method)
}
- // if req.Method == "POST" {
- // req.ParseForm()
- // responseBody2 := req.Form
-
- // fmt.Println("inm test", req.Body)
+ if req.Method == "POST" {
+ reqBody, _ := ioutil.ReadAll(req.Body)
+ stringReqBody := string(reqBody)
+ if stringReqBody != string(sentBody) {
+ t.Errorf("Invalid body sent to Plausible. Got: %q, want: %q", sentBody, stringReqBody)
- // jo, _ := httputil.DumpRequest(req, true)
- // fmt.Println("fuck", string(jo[:]))
-
- // }
-
- /*
- if fmt.Sprint(req.Header) != fmt.Sprint(headers) {
- t.Errorf("Invalid proxied headers. Got: %q, want: %q", req.Header, headers)
}
- */
+ }
+
+ if fmt.Sprint(req.Header) != fmt.Sprint(sentHeaders) {
+ t.Errorf("Invalid sent headers. Got: %q, want: %q", req.Header, sentHeaders)
+ }
return &http.Response{
StatusCode: 200,
Body: r,
- Header: headers,
+ Header: sentHeaders,
}, nil
},
@@ -67,7 +59,7 @@ func mockProxiedCall(method string, url string, responseBody string, sentBody st
func TestProxy(t *testing.T) {
t.Run("GET request", func(t *testing.T) {
- proxiedBody := `[{"full_name": "mock-repo" }]`
+ responseBody := `[{"full_name": "mock-repo" }]`
sentBody := "{}"
wantedHeaders := map[string][]string{
"Key": []string{"value"},
@@ -75,7 +67,7 @@ func TestProxy(t *testing.T) {
"Content-Type": []string{"application/javascript"},
}
- mockProxiedCall(http.MethodGet, "/js/script.js", proxiedBody, sentBody, wantedHeaders, t)
+ mockCall(http.MethodGet, "/js/script.js", responseBody, sentBody, wantedHeaders, t)
request, _ := http.NewRequest(http.MethodGet, "/js/script.js", nil)
@@ -83,13 +75,15 @@ func TestProxy(t *testing.T) {
request.Header.Add("key2", "value1")
request.Header.Add("key2", "value2")
request.Header.Set("Content-Type", "application/javascript")
+ // we remove Accept-Encoding header not to deal with gzip
+ request.Header.Set("Accept-Encoding", "gzip")
response := httptest.NewRecorder()
Proxy(response, request)
- if response.Body.String() != proxiedBody {
- t.Errorf("got %q, want %q", response.Body.String(), proxiedBody)
+ if response.Body.String() != responseBody {
+ t.Errorf("Invalid returned body. got %q, want %q", response.Body.String(), responseBody)
}
if fmt.Sprint(response.Header()) != fmt.Sprint(wantedHeaders) {
@@ -98,11 +92,13 @@ func TestProxy(t *testing.T) {
})
t.Run("POST request", func(t *testing.T) {
- proxiedBody := "{}"
+ responseBody := "ok"
sentBody := `{"full_name": "mock-repo" }`
- wantedHeaders := map[string][]string{}
+ wantedHeaders := map[string][]string{
+ "Content-Type": []string{"application/javascript"},
+ }
- mockProxiedCall(http.MethodPost, "/js/script.js", proxiedBody, sentBody, wantedHeaders, t)
+ mockCall(http.MethodPost, "/js/script.js", responseBody, sentBody, wantedHeaders, t)
request, _ := http.NewRequest(
http.MethodPost,
@@ -115,5 +111,9 @@ func TestProxy(t *testing.T) {
Proxy(response, request)
+ if response.Body.String() != responseBody {
+ t.Errorf("Invalid returned body. got %q, want %q", response.Body.String(), responseBody)
+ }
+
})
}