I am writing a web server wherein I need to register handlers at runtime. E.g. "/create" would create a new handler for all URLs like "/123/*" and so on. I need a corresponding "/destroy/123" which would unregister the handler for "/123/*".
Here's the code for handling "/create"
package main
import (
"fmt"
"net/http"
)
type MyHandler struct {
id int
}
func (hf *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, r.URL.Path)
}
// Creates MyHandler instances and registers them as handlers at runtime
type HandlerFactory struct {
handler_id int
}
func (hf *HandlerFactory) ServeHTTP(w http.ResponseWriter, r *http.Request) {
hf.handler_id++
handler := MyHandler{hf.handler_id}
handle := fmt.Sprintf("/%d/", hf.handler_id)
http.Handle(handle, &handler)
}
func main() {
factory := HandlerFactory{0}
http.Handle("/create", &factory)
http.ListenAndServe("localhost:8080", nil)
}
I tried implementing my own multiplexer by embedding http.ServeMux
but it holds its pattern-to-Handler mapping in a private variable (ServeMux.m
)
What I would do is create a custom ServerMux
. Copy the code from GOROOT/src/pkg/net/http/server.go
. It starts on line 837 and ends at 939.
The custom ServerMux would need a method for deregistration. This should be easy to implement. Just grab the lock and del()
the map entry. For example (all code untested):
// TODO: check if registered and return error if not.
// TODO: possibly remove the automatic permanent link between /dir and /dir/.
func (mux *MyMux) Deregister(pattern string) error {
mux.mu.Lock()
defer mux.mu.Unlock()
del(mux.m, pattern)
return nil
}
In order to use this new mux, you would do something like this:
mux := newMux()
mux.Handle("/create", &factory)
srv := &http.Server {
Addr: localhost:8080
Handler: mux,
}
srv.ListenAndServe()
Modifying mux by calling deregister()
from another goroutine is completely safe and will modify the way ListenAndServe()
routes messages.