summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortjpcc <tjp@ctrl-c.club>2023-01-19 16:03:52 -0700
committertjpcc <tjp@ctrl-c.club>2023-01-19 16:03:52 -0700
commite1aa19f1e8c4a68b43add79374d6dfeae8bd9921 (patch)
tree8ef65638aa24175984cb461acd1a4c3ac3cc7092
parent7d8fd812785ec924b238944e7b5d7c397f721ec9 (diff)
New sharedhost contrib package.
ReplaceTilde simply replaces a leading ~ in the URL. Fixes #9.
-rw-r--r--README.gmi3
-rw-r--r--README.md3
-rw-r--r--contrib/sharedhost/replacement.go46
-rw-r--r--contrib/sharedhost/replacement_test.go57
4 files changed, 107 insertions, 2 deletions
diff --git a/README.gmi b/README.gmi
index a4e9946..9eb12be 100644
--- a/README.gmi
+++ b/README.gmi
@@ -34,10 +34,11 @@ The gemtext package provides a parser and converters for gemtext documents. It e
This is where useful building blocks themselves start to come in. Sub-packages of contrib include Handler and Middleware implementations which accomplish the things your servers actually need to do.
-So far there are at least 3 packages:
+So far there are at least 4 packages:
* log contains a simple request-logging middleware
* fs has handlers that make file servers possible: serve files, build directory listings, etc
* cgi includes handlers which can execute CGI programs
+* sharedhost which provides means of handling /~username URLs
* ...with more to come
## Get it
diff --git a/README.md b/README.md
index 98a3861..2b4f53b 100644
--- a/README.md
+++ b/README.md
@@ -36,11 +36,12 @@ The gemtext package provides a parser and converters for gemtext documents. It e
This is where useful building blocks themselves start to come in. Sub-packages of contrib include Handler and Middleware implementations which accomplish the things your servers actually need to do.
-So far there are at least 3 packages:
+So far there are at least 4 packages:
* log contains a simple request-logging middleware
* fs has handlers that make file servers possible: serve files, build directory listings, etc
* cgi includes handlers which can execute CGI programs
+* sharedhost which provides means of handling /~username URLs
* ...with more to come
## Get it
diff --git a/contrib/sharedhost/replacement.go b/contrib/sharedhost/replacement.go
new file mode 100644
index 0000000..1fb2a0d
--- /dev/null
+++ b/contrib/sharedhost/replacement.go
@@ -0,0 +1,46 @@
+package sharedhost
+
+import (
+ "context"
+ "crypto/tls"
+ "net/url"
+
+ "tildegit.org/tjp/gus"
+)
+
+// ReplaceTilde builds a middleware which substitutes a leading '~' in the request path.
+//
+// It makes the alteration to a copy of the request which is then passed into the
+// wrapped Handler. This way middlewares outside of this one inspecting the request
+// afterwards will see the original URL.
+//
+// Typically the replacement should end with a "/", so that the ~ ends up mapping to a
+// particular directory on the filesystem. For instance with a replacement string of
+// "users/", "domain.com/~jim/index.gmi" maps to "domain.com/users/jim/index.gmi".
+func ReplaceTilde(replacement string) gus.Middleware {
+ return func(inner gus.Handler) gus.Handler {
+ return func(ctx context.Context, request *gus.Request) *gus.Response {
+ if len(request.Path) > 1 && request.Path[0] == '/' && request.Path[1] == '~' {
+ request = cloneRequest(request)
+ request.Path = "/" + replacement + request.Path[2:]
+ }
+
+ return inner(ctx, request)
+ }
+ }
+}
+
+func cloneRequest(start *gus.Request) *gus.Request {
+ next := &gus.Request{}
+ *next = *start
+
+ next.URL = &url.URL{}
+ *next.URL = *start.URL
+
+ if start.TLSState != nil {
+ next.TLSState = &tls.ConnectionState{}
+ *next.TLSState = *start.TLSState
+ }
+
+ return next
+}
diff --git a/contrib/sharedhost/replacement_test.go b/contrib/sharedhost/replacement_test.go
new file mode 100644
index 0000000..cab80bb
--- /dev/null
+++ b/contrib/sharedhost/replacement_test.go
@@ -0,0 +1,57 @@
+package sharedhost_test
+
+import (
+ "context"
+ "net/url"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+
+ "tildegit.org/tjp/gus"
+ "tildegit.org/tjp/gus/contrib/sharedhost"
+)
+
+func TestReplaceTilde(t *testing.T) {
+ tests := []struct {
+ replacement string
+ inputURL string
+ replacedPath string
+ }{
+ {
+ replacement: "users/",
+ inputURL: "gemini://domain.com/~username/foo/bar",
+ replacedPath: "/users/username/foo/bar",
+ },
+ {
+ replacement: "people-",
+ inputURL: "gemini://domain.com/non/match",
+ replacedPath: "/non/match",
+ },
+ {
+ replacement: "people-",
+ inputURL: "gemini://domain.com/~someone/dir/file",
+ replacedPath: "/people-someone/dir/file",
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.inputURL, func(t *testing.T) {
+ u, err := url.Parse(test.inputURL)
+ assert.Nil(t, err)
+
+ originalPath := u.Path
+
+ replacer := sharedhost.ReplaceTilde(test.replacement)
+ request := &gus.Request{URL: u}
+ handler := replacer(func(_ context.Context, request *gus.Request) *gus.Response {
+ assert.Equal(t, test.replacedPath, request.Path)
+ return &gus.Response{}
+ })
+
+ handler(context.Background(), request)
+
+ // original request was unmodified
+ assert.Equal(t, originalPath, request.Path)
+ })
+ }
+}