func MakeHandler(f http.Handler) (Handler) {
  return func (w http.ResponseWriter, r *Request) {
    values := make(url.Values)
    req := &r.Request
    for k, v := range r.URLParams {
      values.Set(k, v)
    }
    if len(req.URL.RawQuery) > 0 {
      req.URL.RawQuery = values.Encode() + "&" + req.URL.RawQuery
    } else {
      req.URL.RawQuery = values.Encode()
    }
    f.ServeHTTP(w, req)
  }
}
Turns a http.HandlerFunc into a goober.Handler
package goober
import  (
  "net/http"
  "strings"
  "io"
  "time"
  "fmt"
  "net/url"
)
type Goober struct {
  head map[string]*routeTreeNode
  ErrorPages map[int]string
}
type Handler func(http.ResponseWriter, *Request)
type PipeHandler func(http.ResponseWriter, *Request) (error)
type RouteMap map[string]*routeTreeNode
type routeTreeNode struct {
  handler Handler  
  pre []PipeHandler
  post []PipeHandler
  children RouteMap  
  variables RouteMap  
}
func (n *routeTreeNode) AddPreFunc(f PipeHandler) (*routeTreeNode) {
  n.pre = append(n.pre[:], f)
  return n
}
func (n *routeTreeNode) AddPostFunc(f PipeHandler) (*routeTreeNode) {
  n.post = append(n.post[:], f)
  return n
}
type Request struct {
  http.Request
  URLParams map[string]string
}
func newRouteTreeNode() (node *routeTreeNode) {
  node = &routeTreeNode{
    children: make(RouteMap),
    variables: make(RouteMap),
    pre: []PipeHandler{},
    post: []PipeHandler{},
  }
  return
}
func New() (* Goober) {
  var head = make(RouteMap)
  head["GET"] = newRouteTreeNode()
  head["HEAD"] = newRouteTreeNode()
  head["POST"] = newRouteTreeNode()
  head["PUT"] = newRouteTreeNode()
  head["DELETE"] = newRouteTreeNode()
  g := &Goober{
    head: head,
    ErrorPages: make(map[int]string),
  }
  return g
}
func isSlash(s rune) (bool) {
  return s == '/'
}
type BadRouteError struct {
  Route string
  Reason string
}
func (e BadRouteError) Error() string {
  return "\"" + e.Route + "\" is an invalid route because " + e.Reason + "."
}
func (g *Goober) AddHandler(method string, route string, handler Handler) (cur *routeTreeNode){
  route = strings.TrimFunc(route, isSlash)
  var parts = strings.Split(route, "/")
  cur = g.head[method]
  if len(route) > 0 {
    for i := range parts {
      var part = parts[i]
      if len(part) == 0 {
        return nil
      }
      if strings.HasPrefix(part, ":") {
        if (cur.variables[part] != nil) {
          cur = cur.variables[part]
        } else {
          cur.variables[part] = newRouteTreeNode()
          cur = cur.variables[part]
        }
      } else {
        if (cur.children[part] != nil) {
          cur = cur.children[part]
        } else {
          cur.children[part] = newRouteTreeNode()
          cur = cur.children[part]
        }
      }
    }
  }
  cur.handler = handler
  return
}
func (g *Goober) Get(route string, handler Handler) (node *routeTreeNode) {
  return g.AddHandler("GET", route, handler)
}
func (g *Goober) Head(route string, handler Handler) (node *routeTreeNode) {
  return g.AddHandler("HEAD", route, handler)
}
func (g *Goober) Post(route string, handler Handler) (node *routeTreeNode) {
  return g.AddHandler("POST", route, handler)
}
func (g *Goober) Put(route string, handler Handler) (node *routeTreeNode) {
  return g.AddHandler("PUT", route, handler)
}
func (g *Goober) Delete(route string, handler Handler) (node *routeTreeNode) {
  return g.AddHandler("DELETE", route, handler)
}
type RouteNotFoundError struct {
  Route string
}
func (e RouteNotFoundError) Error() string {
  return "Route \"" + e.Route + "\" was not found."
}
func walkTree(node *routeTreeNode, parts []string, r *Request) (*routeTreeNode, error) {
  var err error = nil
  if len(parts) == 0 {
    if node.handler == nil {
      err = &RouteNotFoundError{Route: r.URL.Path}
    }
    return node, err
  } else {
    var part = parts[0]
    if child, ok := node.children["*"]; ok {
      node = child
      r.URLParams["*"] = strings.Join(parts, "/")
    } else if node.children[part] != nil {
      return walkTree(node.children[part], parts[1:], r)
    } else {
      for k, v := range node.variables {
        node, err = walkTree(v, parts[1:], r)
        if err == nil {
          r.URLParams[k] = part
          return node, err
        }
      }
      err = &RouteNotFoundError{Route: r.URL.Path}
    }
  }
  return node, err
}
func (g *Goober) GetHandler(r *Request) (node *routeTreeNode, err error) {
  path := strings.TrimFunc(r.URL.Path, isSlash)
  if len(path) == 0 {
    if g.head[r.Method].handler == nil {
      err := &RouteNotFoundError{Route: r.URL.Path}
      return g.head[r.Method], err
    } else {
      return g.head[r.Method], nil
    }
  }
  parts := strings.Split(path, "/")
  return walkTree(g.head[r.Method], parts, r)
}
func (g *Goober) errorHandler(w http.ResponseWriter, r *Request, code int) {
  if page, ok := g.ErrorPages[code]; ok {
    w.Header().Set("Content-Type", "text/html; charset=utf-8")
    w.WriteHeader(code)
    io.WriteString(w, page)
  }
}
func webTime(t time.Time) string {
  ftime := t.Format(time.RFC1123)
  if strings.HasSuffix(ftime, "UTC") {
    ftime = ftime[0:len(ftime)-3] + "GMT"
  }
  return ftime
}
func (g *Goober) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  var startTime = time.Now()
  defer func() {
    fmt.Printf("[%s] %s - took %s\n", r.Method, r.URL.Path, time.Since(startTime))
    r.Body.Close()
  }()
  var request = &Request{
    Request: *r,
    URLParams: make(map[string]string),
  }
  node, err := g.GetHandler(request)
  if err == nil {
    w.Header().Set("Server", "goober.go")
    w.Header().Set("Date", webTime(time.Now().UTC()))
    for _, f := range node.pre {
      if e := f(w, request); e != nil {
        fmt.Println("[ERROR] " + e.Error())
        g.errorHandler(w, request, 404)
        return
      }
    }
    node.handler(w, request)
    for _, f := range node.post {
      if e := f(w, request); e != nil {
        fmt.Println("[ERROR] " + e.Error())
        return
      }
    }
  } else {
    fmt.Println("[ERROR] " + err.Error())
    g.errorHandler(w, request, 404)
  }
}