~emersion/public-inbox

kimchi: allow wildcard sites v3 PROPOSED

Jonathan Halmen: 1
 allow wildcard sites

 1 files changed, 52 insertions(+), 2 deletions(-)
I haven't had the time to implement your proposed solution, wanted to do
that today, but I got stuck setting up working test-cases.
Next
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~emersion/public-inbox/patches/15288/mbox | git am -3
Learn more about email & git
View this thread in the archives

[PATCH kimchi v3] allow wildcard sites Export this patch

closes: https://todo.sr.ht/~emersion/kimchi/14
---
This now works better. I'm unsure whether the NewServeMux function is
the best way to actually allocate a new Mux, or if it could be done more
easily as well. Also the naming of the new types could be improved.

 server.go | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 52 insertions(+), 2 deletions(-)

diff --git a/server.go b/server.go
index 18b8520..b169d83 100644
--- a/server.go
+++ b/server.go
@@ -7,6 +7,7 @@ import (
	"log"
	"net"
	"net/http"
	"strings"
	"sync"

	"github.com/pires/go-proxyproto"
@@ -65,10 +66,59 @@ func (srv *Server) AddListener(network, addr string) *Listener {
	return ln
}

type wildcardHandler map[string]http.Handler

func (w wildcardHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	reqHost, _, err := net.SplitHostPort(req.Host)
	if err != nil {
		http.NotFound(rw, req)
		return
	}
	reqPath := req.URL.EscapedPath()

	for pattern, handler := range w {
		i := strings.Index(pattern, "/")
		host, path := pattern[:i], pattern[i:]
		if strings.HasSuffix(reqHost, host) &&
			strings.HasPrefix(reqPath, path) {
			handler.ServeHTTP(rw, req)
			return
		}
	}
	http.NotFound(rw, req)
}

type wildServeMux struct {
	Mux *http.ServeMux
	Map wildcardHandler
}

func NewServeMux() *wildServeMux {
	m := new(wildServeMux)
	m.Mux = http.NewServeMux()
	m.Map = wildcardHandler{}
	return m
}

func (m wildServeMux) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	m.Mux.ServeHTTP(rw, req)
}

func (m wildServeMux) Handle(pattern string, handler http.Handler) {
	if !strings.HasPrefix(pattern, "*") {
		m.Mux.Handle(pattern, handler)
	} else {
		if len(m.Map) == 0 {
			m.Mux.Handle("/", m.Map)
		}
		m.Map[pattern[1:]] = handler
	}
}

type Listener struct {
	Network  string
	Address  string
	Mux      *http.ServeMux
	Mux      *wildServeMux
	Insecure bool
	Server   *Server

@@ -92,7 +142,7 @@ func newListener(srv *Server, network, addr string) *Listener {
		},
	}
	ln.h2Server = &http2.Server{}
	ln.Mux = http.NewServeMux()
	ln.Mux = NewServeMux()
	return ln
}

-- 
2.29.2
We want to properly handle fallbacks to more general patterns if we
don't find a handler. For instance with this config:

    foo.example.org/foo {
        file_server /srv/http/foo.example.org
    }
    *.example.org/bar {
        file_server /srv/http/fallback.example.org
    }
    :80/asdf {
        file_server /srv/http/fallback
    }

We'll want to route:

* http://foo.example.org/foo to the first handler
* http://foo.example.org/bar to the second one
* http://foo.example.org/asdf to the third one

I think that would be achievable with something like:

    type hostServeMux map[string]*http.ServeMux

And in the hostServeMux.ServeHTTP method, first try to find a handler
for the full host:

    if pathMux, ok := mux[req.Host]; ok {
        if h, pattern := pathMux.Handler(r); pattern != "" {
            h.ServeHTTP(w, r)
            return
        }
    }

Then repeat for a potential wildcard name, then repeat with an empty
host.