mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 10:54:59 +00:00
* Remove MDS and replace it with Controller This commit removes the MDS layer (and package) and shifts Controller package into its place. * add pprof/fgprof to serverless http router --------- Co-authored-by: Matthew Jaffee <jaffee@pilosa.com>
172 lines
4 KiB
Go
172 lines
4 KiB
Go
package http
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"net/http"
|
|
"runtime/debug"
|
|
"time"
|
|
|
|
"github.com/featurebasedb/featurebase/v3/dax/controller"
|
|
"github.com/featurebasedb/featurebase/v3/dax/queryer"
|
|
"github.com/featurebasedb/featurebase/v3/dax/snapshotter"
|
|
"github.com/featurebasedb/featurebase/v3/dax/writelogger"
|
|
"github.com/featurebasedb/featurebase/v3/errors"
|
|
"github.com/featurebasedb/featurebase/v3/logger"
|
|
)
|
|
|
|
// Handler represents an HTTP handler.
|
|
type Handler struct {
|
|
Handler http.Handler
|
|
|
|
bind string
|
|
|
|
ln net.Listener
|
|
// url is used to hold the advertise bind address for printing a log during startup.
|
|
url string
|
|
|
|
closeTimeout time.Duration
|
|
|
|
server *http.Server
|
|
|
|
controller *controller.Controller
|
|
writeLogger *writelogger.Writelogger
|
|
snapshotter *snapshotter.Snapshotter
|
|
queryer *queryer.Queryer
|
|
|
|
computer http.Handler
|
|
|
|
logger logger.Logger
|
|
}
|
|
|
|
// HandlerOption is a functional option type for Handler
|
|
type HandlerOption func(s *Handler) error
|
|
|
|
func OptHandlerBind(b string) HandlerOption {
|
|
return func(h *Handler) error {
|
|
h.bind = b
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func OptHandlerController(c *controller.Controller) HandlerOption {
|
|
return func(h *Handler) error {
|
|
h.controller = c
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func OptHandlerWritelogger(w *writelogger.Writelogger) HandlerOption {
|
|
return func(h *Handler) error {
|
|
h.writeLogger = w
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func OptHandlerSnapshotter(s *snapshotter.Snapshotter) HandlerOption {
|
|
return func(h *Handler) error {
|
|
h.snapshotter = s
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func OptHandlerQueryer(q *queryer.Queryer) HandlerOption {
|
|
return func(h *Handler) error {
|
|
h.queryer = q
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func OptHandlerLogger(l logger.Logger) HandlerOption {
|
|
return func(h *Handler) error {
|
|
h.logger = l
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// OptHandlerCloseTimeout controls how long to wait for the http Server to
|
|
// shutdown cleanly before forcibly destroying it. Default is 30 seconds.
|
|
func OptHandlerCloseTimeout(d time.Duration) HandlerOption {
|
|
return func(h *Handler) error {
|
|
h.closeTimeout = d
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// OptHandlerListener set the listener that will be used by the HTTP server.
|
|
// Url must be the advertised URL. It will be used to show a log to the user
|
|
// about where the Web UI is. This option is mandatory.
|
|
func OptHandlerListener(ln net.Listener, url string) HandlerOption {
|
|
return func(h *Handler) error {
|
|
h.ln = ln
|
|
h.url = url
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func OptHandlerComputer(handler http.Handler) HandlerOption {
|
|
return func(h *Handler) error {
|
|
h.computer = handler
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// NewHandler returns a new instance of Handler with a default logger.
|
|
func NewHandler(router http.Handler, opts ...HandlerOption) (*Handler, error) {
|
|
handler := &Handler{
|
|
logger: logger.NopLogger,
|
|
closeTimeout: time.Second * 30,
|
|
}
|
|
|
|
for _, opt := range opts {
|
|
err := opt(handler)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "applying option")
|
|
}
|
|
}
|
|
|
|
handler.Handler = router
|
|
|
|
handler.server = &http.Server{Handler: handler}
|
|
|
|
return handler, nil
|
|
}
|
|
|
|
func (h *Handler) Serve() error {
|
|
err := h.server.Serve(h.ln)
|
|
if err != nil && err.Error() != "http: Server closed" {
|
|
h.logger.Errorf("HTTP handler terminated with error: %s\n", err)
|
|
return errors.Wrap(err, "serve http")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Close tries to cleanly shutdown the HTTP server, and failing that, after a
|
|
// timeout, calls Server.Close.
|
|
func (h *Handler) Close() error {
|
|
deadlineCtx, cancelFunc := context.WithDeadline(context.Background(), time.Now().Add(h.closeTimeout))
|
|
defer cancelFunc()
|
|
err := h.server.Shutdown(deadlineCtx)
|
|
if err != nil {
|
|
err = h.server.Close()
|
|
}
|
|
return errors.Wrap(err, "shutdown/close http server")
|
|
}
|
|
|
|
// ServeHTTP handles an HTTP request.
|
|
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
defer func() {
|
|
if err := recover(); err != nil {
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
stack := debug.Stack()
|
|
h.logger.Printf("PANIC: %s\n%s", err, stack)
|
|
}
|
|
}()
|
|
|
|
h.Handler.ServeHTTP(w, r)
|
|
}
|
|
|
|
// GET /health
|
|
func (h *Handler) handleGetHealth(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
}
|