Cleanup the API a bit add debug functions

This commit is contained in:
ssobolewski 2019-01-03 17:38:02 -07:00
parent 7ed387dee5
commit eab56d649f
3 changed files with 139 additions and 36 deletions

View File

@ -1,8 +1,11 @@
package snap package snap
import ( import (
"net/http"
"encoding/json" "encoding/json"
"io/ioutil"
"net/http"
"strconv"
"git.thirdmartini.com/pub/snap/auth" "git.thirdmartini.com/pub/snap/auth"
) )
@ -29,7 +32,6 @@ func (c *Context) Writer() http.ResponseWriter {
return c.w return c.w
} }
func (c *Context) GetUser() string { func (c *Context) GetUser() string {
if c.auth == nil { if c.auth == nil {
return "" return ""
@ -41,12 +43,10 @@ func (c *Context) Error(code int, msg string) {
c.srv.renderError(c.w, code, msg) c.srv.renderError(c.w, code, msg)
} }
func (c *Context) Reply(msg string) { func (c *Context) Reply(msg string) {
c.srv.reply(c.w, msg) c.srv.reply(c.w, msg)
} }
func (c *Context) ReplyObject(obj interface{}) { func (c *Context) ReplyObject(obj interface{}) {
msg, err := json.Marshal(obj) msg, err := json.Marshal(obj)
if err != nil { if err != nil {
@ -55,13 +55,19 @@ func (c *Context) ReplyObject(obj interface{}) {
c.Reply(string(msg)) c.Reply(string(msg))
} }
func (c *Context) GetObject(obj interface{}) error {
data, err := ioutil.ReadAll(c.r.Body)
defer c.r.Body.Close()
if err != nil {
return err
}
return json.Unmarshal(data, obj)
}
func (c *Context) Render(tmpl string, content interface{}) { func (c *Context) Render(tmpl string, content interface{}) {
c.srv.render(c.w, tmpl, content) c.srv.render(c.w, tmpl, content)
} }
func (c *Context) RenderEx(tmpl string, content interface{}) { func (c *Context) RenderEx(tmpl string, content interface{}) {
cnt := SnapContent{ cnt := SnapContent{
User: c.GetUser(), User: c.GetUser(),
@ -76,7 +82,7 @@ func (c *Context) GetVar(k string) ( string, bool) {
return v, ok return v, ok
} }
func (c *Context) GetVarDefault(k string,def string) (string) { func (c *Context) GetVarDefault(k string, def string) string {
v, ok := c.vars[k] v, ok := c.vars[k]
if !ok { if !ok {
return def return def
@ -91,3 +97,22 @@ func (c *Context) ParseForm() error {
func (c *Context) FormValue(k string) string { func (c *Context) FormValue(k string) string {
return c.r.FormValue(k) return c.r.FormValue(k)
} }
func (c *Context) FormValueUint64(k string) uint64 {
str := c.r.FormValue(k)
val, err := strconv.ParseUint(str, 10, 64)
if err != nil {
return 0
}
return val
}
func (c *Context) Redirect(url string) {
http.Redirect(c.w, c.r, url, http.StatusSeeOther)
}
func (c *Context) QueryValue(key string) string {
return c.r.URL.Query().Get(key)
}

57
debug.go Normal file
View File

@ -0,0 +1,57 @@
package snap
import (
"bytes"
"encoding/json"
"expvar"
"fmt"
"log"
"net/http"
"net/http/pprof"
"runtime"
"github.com/gorilla/mux"
)
func stackHandler(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/sovereign/debug/pprof/goroutine?debug=2", http.StatusSeeOther)
}
// copy pasted from http://golang.org/src/expvar/expvar.go#L308
func varsHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
buf := make([]byte, 0, 16384)
body := bytes.NewBuffer(buf)
fmt.Fprintf(body, "{")
expvar.Do(func(kv expvar.KeyValue) {
fmt.Fprintf(body, "%q: %s,", kv.Key, kv.Value)
})
fmt.Fprintf(body, "%q: %d", "NumGoRoutines", runtime.NumGoroutine())
fmt.Fprintf(body, "}")
var prettyJSON bytes.Buffer
err := json.Indent(&prettyJSON, body.Bytes(), "", " ")
if err != nil {
log.Println(err, string(body.Bytes()))
}
w.Write(prettyJSON.Bytes())
}
func setupDebugHandler(r *mux.Router) {
r.HandleFunc("/stack", stackHandler)
r.HandleFunc("/vars", varsHandler)
r.HandleFunc("/pprof/", http.HandlerFunc(pprof.Index))
r.HandleFunc("/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
r.HandleFunc("/pprof/profile", http.HandlerFunc(pprof.Profile))
r.HandleFunc("/pprof/symbol", http.HandlerFunc(pprof.Symbol))
r.HandleFunc("/pprof/trace", http.HandlerFunc(pprof.Trace))
r.HandleFunc("/pprof/goroutine", pprof.Handler("goroutine").ServeHTTP)
r.HandleFunc("/pprof/allocs", pprof.Handler("allocs").ServeHTTP)
r.HandleFunc("/pprof/block", pprof.Handler("block").ServeHTTP)
r.HandleFunc("/pprof/heap", pprof.Handler("heap").ServeHTTP)
r.HandleFunc("/pprof/mutex", pprof.Handler("mutex").ServeHTTP)
}

View File

@ -1,12 +1,13 @@
package snap package snap
import ( import (
"fmt"
"html/template" "html/template"
"io"
"io/ioutil"
"net/http" "net/http"
"os" "os"
"path" "path"
"fmt"
"io/ioutil"
"path/filepath" "path/filepath"
"strings" "strings"
"time" "time"
@ -64,11 +65,13 @@ func (s *Server) wrapper(handle func(c *Context)) http.HandlerFunc {
log.Debug("request: ", r.RequestURI) log.Debug("request: ", r.RequestURI)
c := s.makeContext(nil, w, r) c := s.makeContext(nil, w, r)
handle(c) handle(c)
// discard the rest of the body content
io.Copy(ioutil.Discard, r.Body)
defer r.Body.Close()
} }
} }
// This is a bit different then the standard template.parseFiles code in that it gives us hiarchial templates // This is a bit different then the standard template.parseFiles code in that it gives us hiarchial templates
// header.html // header.html
// mydirectory/service.html ... // mydirectory/service.html ...
@ -88,7 +91,7 @@ func (s *Server) parseTemplates(t *template.Template, filenames ...string) (*tem
} }
data := string(b) data := string(b)
name := strings.TrimPrefix(filename, s.templates+"/") name := strings.TrimPrefix(filename, s.templates+"/")
log.Println("Template:", name) // log.Println("Template:", name)
// First template becomes return value if not already defined, // First template becomes return value if not already defined,
// and we use that one for subsequent New calls to associate // and we use that one for subsequent New calls to associate
@ -182,9 +185,8 @@ func (s *Server) HandleFuncCustomAuth(auth auth.AuthManager, path string, f func
return s.router.HandleFunc(path, s.authenticated(auth, f)) return s.router.HandleFunc(path, s.authenticated(auth, f))
} }
func (s *Server) HandleFunc(path string, f func(c *Context)) error { func (s *Server) HandleFunc(path string, f func(c *Context)) *mux.Route {
s.router.HandleFunc(path, s.wrapper(f)) return s.router.HandleFunc(path, s.wrapper(f))
return nil
} }
func (s *Server) SetDebug(enable bool) { func (s *Server) SetDebug(enable bool) {
@ -202,6 +204,9 @@ func (s *Server) SetTemplatePath(path string) {
s.templates = path s.templates = path
} }
func (s *Server) Router() *mux.Router {
return s.router
}
func (s *Server) ServeTLS(keyPath string, certPath string) error { func (s *Server) ServeTLS(keyPath string, certPath string) error {
srv := &http.Server{ srv := &http.Server{
@ -228,6 +233,22 @@ func (s *Server) Serve() error {
return srv.ListenAndServe() return srv.ListenAndServe()
} }
func (s *Server) WithStaticFiles(prefix string, path string) *Server {
s.router.PathPrefix(prefix).Handler(http.StripPrefix(prefix, http.FileServer(http.Dir(path))))
return s
}
func (s *Server) WithTheme(themeURL string) *Server {
s.theme = themeURL
return s
}
func (s *Server) WithDebug(debugURL string) *Server {
sub := s.router.PathPrefix("/sovereign/debug").Subrouter()
setupDebugHandler(sub)
return s
}
func New(address string, path string, auth auth.AuthManager) *Server { func New(address string, path string, auth auth.AuthManager) *Server {
s := Server{ s := Server{
router: mux.NewRouter(), router: mux.NewRouter(),
@ -238,6 +259,6 @@ func New(address string, path string, auth auth.AuthManager) *Server {
theme: "/static/css/default.css", theme: "/static/css/default.css",
} }
s.router.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(s.path+"static/")))) //s.router.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(s.path+"static/"))))
return &s return &s
} }