New sharedhost contrib package.
continuous-integration/drone/push Build is passing Details

ReplaceTilde simply replaces a leading ~ in the URL.

Fixes #9.
This commit is contained in:
tjpcc 2023-01-19 16:03:52 -07:00
parent 7d8fd81278
commit e1aa19f1e8
4 changed files with 107 additions and 2 deletions

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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)
})
}
}