package server import ( "context" "crypto/subtle" "encoding/json" "log" "net" "net/http" "time" "github.com/julienschmidt/httprouter" ) type HttpServer struct { server *http.Server router *httprouter.Router } func NewHttpServer() *HttpServer { r := httprouter.New() return &HttpServer{ server: &http.Server{ ReadTimeout: 3 * time.Second, WriteTimeout: 3 * time.Second, Handler: r, }, router: r, } } func (s *HttpServer) GET(path string, handler http.HandlerFunc) { s.router.Handler(http.MethodGet, path, handler) } func (s *HttpServer) POST(path string, handler http.HandlerFunc) { s.router.Handler(http.MethodPost, path, handler) } func (s *HttpServer) PATCH(path string, handler http.HandlerFunc) { s.router.Handler(http.MethodPatch, path, handler) } func (s *HttpServer) PUT(path string, handler http.HandlerFunc) { s.router.Handler(http.MethodPut, path, handler) } func (s *HttpServer) DELETE(path string, handler http.HandlerFunc) { s.router.Handler(http.MethodDelete, path, handler) } func (s *HttpServer) ServeStatic(path string, fsys http.FileSystem) { s.router.ServeFiles(path, fsys) } func (s *HttpServer) SetNotFoundHandler(handler http.HandlerFunc) { s.router.NotFound = handler } // GetURLParam wrapper around underlying router for getting URL parameters. func GetURLParam(r *http.Request, param string) string { return httprouter.ParamsFromContext(r.Context()).ByName(param) } func (s *HttpServer) Start(network, address string) error { listener, err := net.Listen(network, address) if err != nil { return err } go func() { if err = s.server.Serve(listener); err != nil && err != http.ErrServerClosed { log.Fatalln(err) } }() return nil } func (s *HttpServer) Stop() error { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() if err := s.server.Shutdown(ctx); err != nil { return err } return nil } // AuthWithToken middleware that authenticates user by token (that is effectively just a password) // supplied in an 'Authentication-Token' HTTP header. func AuthWithToken(handler http.HandlerFunc, token string) http.HandlerFunc { return func(rw http.ResponseWriter, r *http.Request) { if subtle.ConstantTimeCompare([]byte(r.Header.Get("Authentication-Token")), []byte(token)) == 1 { handler(rw, r) } else { rw.WriteHeader(http.StatusUnauthorized) rw.Header().Add("Content-Type", "application/json") response := make(map[string]string) response["message"] = "Unauthorized" json.NewEncoder(rw).Encode(response) } } }