Use gorilla mux instead of big switch statement

This commit is contained in:
Cody Soyland 2017-03-17 14:46:20 -05:00 • committed by Travis
parent c10ef33042
commit 4397e13a82
3 changed files with 65 additions and 176 deletions

12
glide.lock generated
View file

@ -1,5 +1,5 @@
hash: 7de62dbaf3cc1dc4959f4f6d8213102cb182b4dd7a87b3ac29260ad6bc1b0cef
updated: 2017-03-03T12:25:48.088390296-06:00
hash: 743e8f978eb4ad8f80a2ab71b05caebbf50b6769b71aa457bc4f144fef8c6595
updated: 2017-03-15T15:39:07.186226037-05:00
imports:
- name: github.com/boltdb/bolt
version: 4b1ebc1869ad66568b313d0dc410e2be72670dda
@ -27,6 +27,10 @@ imports:
version: 888eb0692c857ec880338addf316bd662d5e630e
subpackages:
- proto
- name: github.com/gorilla/context
version: 08b5f424b9271eedf6f9f0ce86cb9396ed337a42
- name: github.com/gorilla/mux
version: 392c28fe23e1c45ddba891b0320b3b5df220beea
- name: github.com/hashicorp/hcl
version: 630949a3c5fa3c613328e1b8256052cbc2327c9b
subpackages:
@ -48,6 +52,10 @@ imports:
version: c37440a7cf42ac63b919c752ca73a85067e05992
- name: github.com/pelletier/go-toml
version: 13d49d4606eb801b8f01ae542b4afc4c6ee3d84a
- name: github.com/rakyll/statik
version: a2b9c3533409cccb4bb188346bcfc789629a424d
subpackages:
- fs
- name: github.com/satori/go.uuid
version: 879c5887cd475cd7864858769793b2ceb0d44feb
- name: github.com/spf13/afero

View file

@ -29,3 +29,5 @@ import:
version: ^1.1.0
- package: github.com/spf13/cobra
- package: github.com/spf13/viper
- package: github.com/gorilla/mux
version: ^1.3.0

View file

@ -11,13 +11,14 @@ import (
"io/ioutil"
"log"
"net/http"
"net/http/pprof"
_ "net/http/pprof"
"os"
"strconv"
"strings"
"time"
"github.com/gogo/protobuf/proto"
"github.com/gorilla/mux"
"github.com/pilosa/pilosa/internal"
"github.com/pilosa/pilosa/pql"
)
@ -30,6 +31,8 @@ type Handler struct {
Host string
Cluster *Cluster
Router *mux.Router
// The execution engine for running queries.
Executor interface {
Execute(context context.Context, db string, query *pql.Query, slices []uint64, opt *ExecOptions) ([]interface{}, error)
@ -44,178 +47,56 @@ type Handler struct {
// NewHandler returns a new instance of Handler with a default logger.
func NewHandler() *Handler {
return &Handler{
handler := &Handler{
LogOutput: os.Stderr,
}
handler.Router = NewRouter(handler)
return handler
}
func NewRouter(handler *Handler) *mux.Router {
router := mux.NewRouter()
router.PathPrefix("/debug/pprof/").Handler(http.DefaultServeMux).Methods("GET")
router.HandleFunc("/debug/vars", handler.handleExpvar).Methods("GET")
router.HandleFunc("/schema", handler.handleGetSchema).Methods("GET")
router.HandleFunc("/query", handler.handlePostQuery).Methods("POST")
router.HandleFunc("/import", handler.handlePostImport).Methods("POST")
router.HandleFunc("/export", handler.handleGetExport).Methods("GET")
router.HandleFunc("/slices/max", handler.handleGetSliceMax).Methods("GET")
router.HandleFunc("/db", handler.handlePostDB).Methods("POST")
router.HandleFunc("/db", handler.handleDeleteDB).Methods("DELETE")
router.HandleFunc("/db/time_quantum", handler.handlePatchDBTimeQuantum).Methods("PATCH")
router.HandleFunc("/db/attr/diff", handler.handlePostDBAttrDiff).Methods("POST")
router.HandleFunc("/frame", handler.handlePostFrame).Methods("POST")
router.HandleFunc("/frame", handler.handleDeleteFrame).Methods("DELETE")
router.HandleFunc("/frame/time_quantum", handler.handlePatchFrameTimeQuantum).Methods("PATCH")
router.HandleFunc("/frame/attr/diff", handler.handlePostFrameAttrDiff).Methods("POST")
router.HandleFunc("/fragment/nodes", handler.handleGetFragmentNodes).Methods("GET")
router.HandleFunc("/fragment/data", handler.handleGetFragmentData).Methods("GET")
router.HandleFunc("/fragment/data", handler.handlePostFragmentData).Methods("POST")
router.HandleFunc("/fragment/blocks", handler.handleGetFragmentBlocks).Methods("GET")
router.HandleFunc("/fragment/block/data", handler.handleGetFragmentBlockData).Methods("GET")
router.HandleFunc("/frame/restore", handler.handlePostFrameRestore).Methods("POST")
router.HandleFunc("/nodes", handler.handleGetNodes).Methods("GET")
router.HandleFunc("/version", handler.handleGetVersion).Methods("GET")
router.HandleFunc("/frame/views", handler.handleGetFrameViews).Methods("GET")
// TODO: Apply MethodNotAllowed statuses to all endpoints.
// Ideally this would be automatic, as described in this (wontfix) ticket:
// https://github.com/gorilla/mux/issues/6
// For now we just do it for the most commonly used handler, /query
router.HandleFunc("/query", handler.methodNotAllowedHandler).Methods("GET")
return router
}
func (h *Handler) methodNotAllowedHandler(w http.ResponseWriter, r *http.Request) {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
// ServeHTTP handles an HTTP request.
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Handle pprof requests separately.
if strings.HasPrefix(r.URL.Path, "/debug/pprof") {
switch r.URL.Path {
case "/debug/pprof/cmdline":
pprof.Cmdline(w, r)
case "/debug/pprof/profile":
pprof.Profile(w, r)
case "/debug/pprof/symbol":
pprof.Symbol(w, r)
case "/debug/pprof/trace":
pprof.Trace(w, r)
default:
pprof.Index(w, r)
}
return
}
// Route API calls to appropriate handler functions.
t := time.Now()
switch r.URL.Path {
case "/schema":
switch r.Method {
case "GET":
h.handleGetSchema(w, r)
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
case "/query":
switch r.Method {
case "POST":
h.handlePostQuery(w, r)
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
case "/import":
switch r.Method {
case "POST":
h.handlePostImport(w, r)
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
case "/export":
switch r.Method {
case "GET":
h.handleGetExport(w, r)
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
case "/slices/max":
switch r.Method {
case "GET":
h.handleGetSliceMax(w, r)
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
case "/db":
switch r.Method {
case "POST":
h.handlePostDB(w, r)
case "DELETE":
h.handleDeleteDB(w, r)
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
case "/db/time_quantum":
switch r.Method {
case "PATCH":
h.handlePatchDBTimeQuantum(w, r)
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
case "/db/attr/diff":
switch r.Method {
case "POST":
h.handlePostDBAttrDiff(w, r)
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
case "/frame":
switch r.Method {
case "POST":
h.handlePostFrame(w, r)
case "DELETE":
h.handleDeleteFrame(w, r)
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
case "/frame/time_quantum":
switch r.Method {
case "PATCH":
h.handlePatchFrameTimeQuantum(w, r)
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
case "/frame/views":
switch r.Method {
case "GET":
h.handleGetFrameViews(w, r)
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
case "/frame/attr/diff":
switch r.Method {
case "POST":
h.handlePostFrameAttrDiff(w, r)
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
case "/fragment/nodes":
switch r.Method {
case "GET":
h.handleGetFragmentNodes(w, r)
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
case "/fragment/data":
switch r.Method {
case "GET":
h.handleGetFragmentData(w, r)
case "POST":
h.handlePostFragmentData(w, r)
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
case "/fragment/blocks":
switch r.Method {
case "GET":
h.handleGetFragmentBlocks(w, r)
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
case "/fragment/block/data":
switch r.Method {
case "GET":
h.handleGetFragmentBlockData(w, r)
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
case "/frame/restore":
switch r.Method {
case "POST":
h.handlePostFrameRestore(w, r)
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
case "/nodes":
switch r.Method {
case "GET":
h.handleGetNodes(w, r)
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
case "/version":
h.handleVersion(w, r)
case "/debug/vars":
h.handleExpvar(w, r)
default:
http.NotFound(w, r)
}
dif := time.Since(t).Seconds()
if dif > 90 {
h.logger().Printf("%s %s %.03fs", r.Method, r.URL.String(), dif)
}
h.Router.ServeHTTP(w, r)
}
// handleGetSchema handles GET /schema requests.
@ -291,26 +172,24 @@ func (h *Handler) handlePostQuery(w http.ResponseWriter, r *http.Request) {
}
}
func (h *Handler) handleGetSliceMax(w http.ResponseWriter, r *http.Request) error {
func (h *Handler) handleGetSliceMax(w http.ResponseWriter, r *http.Request) {
var ms map[string]uint64
if inverse, _ := strconv.ParseBool(r.URL.Query().Get("inverse")); inverse {
ms = h.Index.MaxInverseSlices()
} else {
ms = h.Index.MaxSlices()
}
if strings.Contains(r.Header.Get("Accept"), "application/x-protobuf") {
pb := &internal.MaxSlicesResponse{
MaxSlices: ms,
}
if buf, err := proto.Marshal(pb); err != nil {
return err
h.logger().Printf("protobuf marshal error: %s", err)
} else if _, err := w.Write(buf); err != nil {
return err
h.logger().Printf("stream write error: %s", err)
}
return nil
}
return json.NewEncoder(w).Encode(sliceMaxResponse{
json.NewEncoder(w).Encode(sliceMaxResponse{
MaxSlices: ms,
})
}
@ -1275,7 +1154,7 @@ func (h *Handler) handleGetNodes(w http.ResponseWriter, r *http.Request) {
}
// handleGetVersion handles /version requests.
func (h *Handler) handleVersion(w http.ResponseWriter, r *http.Request) {
func (h *Handler) handleGetVersion(w http.ResponseWriter, r *http.Request) {
if err := json.NewEncoder(w).Encode(struct {
Version string `json:"version"`
}{