|
- package capstan
-
- import (
- "fmt"
- "net"
- "net/http"
- "path"
- "strconv"
- "strings"
-
- "git.destrealm.org/go/capstan/mappers"
- "git.destrealm.org/go/logging"
- "github.com/go-chi/chi"
- )
-
- // DependencyMapper is a convenience function for creating new dependency
- // mappers.
- func DependencyMapper() *mappers.DependencyMapper {
- return mappers.NewDependencyMapper()
- }
-
- // Private method for binding individual routers to the backend Chi muxer.
- // Routes may be bound multiple times, depending on the configuration of their
- // trailing slash handlers, and some bindings may redirect to other routes.
- func bindRoute(router *Router, route *Route) {
- var mux chi.Router
- if len(route.Middleware) > 0 {
- mux = router.Mux().With(route.Middleware...)
- } else {
- mux = router.Mux()
- }
-
- route.Path = path.Join(router.basePath, route.Path)
- path := route.Path
-
- if path == "/" {
- mux.Method(route.Method, route.Path, route)
- return
- }
-
- switch {
- case route.OptionalSlash && route.SlashIsDefault:
- if !strings.HasSuffix(route.Path, "/") {
- route.Path = route.Path + "/"
- }
- if strings.HasSuffix(path, "/") {
- path = path[:len(path)-1]
- }
- mux.Method(route.Method, route.Path, route)
- mux.Method(route.Method, path,
- redirectRouteHandler(route))
- return
- case route.OptionalSlash && !route.SlashIsDefault:
- if strings.HasSuffix(route.Path, "/") {
- route.Path = route.Path[:len(route.Path)-1]
- }
- if !strings.HasSuffix(path, "/") {
- path = path + "/"
- }
- mux.Method(route.Method, route.Path, route)
- mux.Method(route.Method, path,
- redirectRouteHandler(route))
- return
- case route.MandatorySlash:
- if !strings.HasSuffix(path, "/") {
- path = path + "/"
- }
- mux.Method(route.Method, path, route)
- return
- case route.MandatoryNoSlash:
- if strings.HasSuffix(path, "/") {
- path = path[:len(path)-1]
- }
- mux.Method(route.Method, path, route)
- return
- default:
- // TODO: Remove me or panic as this shouldn't be triggered. Possibly
- // consider unit tests to trigger this path.
- mux.Method(route.Method, route.Path, route)
- }
-
- }
-
- // Utility helper for determining if addr contains a privileged port, i.e. one
- // that is < 1024.
- func isPrivPort(addr string) bool {
- _, p, err := net.SplitHostPort(addr)
- if err != nil {
- return false
- }
- if port, err := strconv.Atoi(p); err == nil {
- return port < 1024
- }
- return false
- }
-
- // Redirect handler for routes that redirect based on their trailing slash
- // configuration.
- func redirectRouteHandler(route *Route) http.HandlerFunc {
- return func(w http.ResponseWriter, r *http.Request) {
- switch {
- case route.OptionalSlash && route.SlashIsDefault:
- r.URL.Host = ""
- r.URL.Scheme = ""
- url := r.URL.String()
- if url[len(url)-1:] != "/" {
- url = url + "/"
- }
- w.Header().Add("location", url)
- w.WriteHeader(301)
- case route.OptionalSlash && !route.SlashIsDefault:
- r.URL.Host = ""
- r.URL.Scheme = ""
- url := r.URL.String()
- if url[len(url)-1:] == "/" {
- url = url[:len(url)-1]
- }
- w.Header().Add("location", url)
- w.WriteHeader(301)
- }
- }
- }
-
- // URLForGen returns a URLFor generator for each configured renderer. This will
- // be attached to the global renderer first, if configured, and then to each
- // route-specific view renderer if supplied.
- func URLForGen(router *Router, external bool) func(string, ...string) string {
- logger := logging.MustInheritLogger("urlfor", "main")
- return func(name string, params ...string) string {
- builder := router.urls.For(name)
- err := builder.SetQuery(strings.Join(params, "&"))
- if err != nil {
- logger.Warning("URLFor query builder returned error:", err)
- return ""
- }
-
- if external {
- builder.External = true
- }
-
- s := builder.Encode()
- if err != nil {
- logger.Warning("URLFor encoding failed with error:", err)
- return ""
- }
-
- return s
- }
- }
-
- func httpErrorPage(ctx Context, json bool) error {
- code := 500
- text := http.StatusText(code)
-
- if ctx.Code() >= 400 {
- code = ctx.Code()
- text = http.StatusText(code)
- }
-
- msg, ok := httpErrorDesc[code]
- if !ok {
- msg = ""
- }
-
- if !json {
- tpl := `<!doctype>
- <html>
- <head><title>%d - %s</title></head>
- <body>
- <h1>%d - %s</h1>
- <p>%s</p>
- </body>
- </html>`
-
- _, err := ctx.Write([]byte(fmt.Sprintf(tpl, code, text, code, text, msg)))
- return err
- }
-
- return ctx.WriteJSON(struct {
- Code int `json:"code"`
- Message string `json:"message"`
- //Error string `json:"error,omitempty"`
- }{
- Code: code,
- Message: msg,
- //Error: ctx.Error(),
- })
- }
|