diff options
author | tjpcc <tjp@ctrl-c.club> | 2023-01-19 16:03:52 -0700 |
---|---|---|
committer | tjpcc <tjp@ctrl-c.club> | 2023-01-19 16:03:52 -0700 |
commit | e1aa19f1e8c4a68b43add79374d6dfeae8bd9921 (patch) | |
tree | 8ef65638aa24175984cb461acd1a4c3ac3cc7092 /contrib/sharedhost | |
parent | 7d8fd812785ec924b238944e7b5d7c397f721ec9 (diff) |
New sharedhost contrib package.
ReplaceTilde simply replaces a leading ~ in the URL.
Fixes #9.
Diffstat (limited to 'contrib/sharedhost')
-rw-r--r-- | contrib/sharedhost/replacement.go | 46 | ||||
-rw-r--r-- | contrib/sharedhost/replacement_test.go | 57 |
2 files changed, 103 insertions, 0 deletions
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) + }) + } +} |