refactor(server): treat Server as an http.Handler

This commit is contained in:
Brian Waldon
2014-01-20 16:24:58 -08:00
parent 074099a1b2
commit 5c3a3db2d8
3 changed files with 69 additions and 74 deletions

View File

@ -19,6 +19,7 @@ package main
import ( import (
"fmt" "fmt"
"net" "net"
"net/http"
"os" "os"
"runtime" "runtime"
"time" "time"
@ -157,7 +158,6 @@ func main() {
sConfig := server.ServerConfig{ sConfig := server.ServerConfig{
Name: info.Name, Name: info.Name,
URL: info.EtcdURL, URL: info.EtcdURL,
CORS: corsInfo,
} }
s := server.New(sConfig, ps, registry, store, &mb) s := server.New(sConfig, ps, registry, store, &mb)
@ -181,5 +181,8 @@ func main() {
go func() { go func() {
log.Fatal(ps.Serve(psListener, config.Snapshot, config.Peers)) log.Fatal(ps.Serve(psListener, config.Snapshot, config.Peers))
}() }()
log.Fatal(s.Serve(sListener))
log.Infof("etcd server [name %s, listen on %s, advertised url %s]", s.Config.Name, sListener.Addr(), s.Config.URL)
sHTTP := &server.CORSHTTPMiddleware{s, corsInfo}
log.Fatal(http.Serve(sListener, sHTTP))
} }

View File

@ -45,24 +45,24 @@ func (c corsInfo) OriginAllowed(origin string) bool {
return c["*"] || c[origin] return c["*"] || c[origin]
} }
type corsHTTPMiddleware struct { type CORSHTTPMiddleware struct {
next http.Handler Handler http.Handler
info *corsInfo Info *corsInfo
} }
// addHeader adds the correct cors headers given an origin // addHeader adds the correct cors headers given an origin
func (h *corsHTTPMiddleware) addHeader(w http.ResponseWriter, origin string) { func (h *CORSHTTPMiddleware) addHeader(w http.ResponseWriter, origin string) {
w.Header().Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE") w.Header().Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
w.Header().Add("Access-Control-Allow-Origin", origin) w.Header().Add("Access-Control-Allow-Origin", origin)
} }
// ServeHTTP adds the correct CORS headers based on the origin and returns immediatly // ServeHTTP adds the correct CORS headers based on the origin and returns immediatly
// with a 200 OK if the method is OPTIONS. // with a 200 OK if the method is OPTIONS.
func (h *corsHTTPMiddleware) ServeHTTP(w http.ResponseWriter, req *http.Request) { func (h *CORSHTTPMiddleware) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// Write CORS header. // Write CORS header.
if h.info.OriginAllowed("*") { if h.Info.OriginAllowed("*") {
h.addHeader(w, "*") h.addHeader(w, "*")
} else if origin := req.Header.Get("Origin"); h.info.OriginAllowed(origin) { } else if origin := req.Header.Get("Origin"); h.Info.OriginAllowed(origin) {
h.addHeader(w, origin) h.addHeader(w, origin)
} }
@ -71,5 +71,5 @@ func (h *corsHTTPMiddleware) ServeHTTP(w http.ResponseWriter, req *http.Request)
return return
} }
h.next.ServeHTTP(w, req) h.Handler.ServeHTTP(w, req)
} }

View File

@ -25,51 +25,36 @@ import (
type ServerConfig struct { type ServerConfig struct {
Name string Name string
URL string URL string
CORS *corsInfo
} }
// This is the default implementation of the Server interface. // This is the default implementation of the Server interface.
type Server struct { type Server struct {
http.Server
Config ServerConfig Config ServerConfig
peerServer *PeerServer peerServer *PeerServer
registry *Registry registry *Registry
store store.Store store store.Store
router *mux.Router
corsMiddleware *corsHTTPMiddleware
metrics *metrics.Bucket metrics *metrics.Bucket
listener net.Listener listener net.Listener
trace bool
} }
// Creates a new Server. // Creates a new Server.
func New(sConfig ServerConfig, peerServer *PeerServer, registry *Registry, store store.Store, mb *metrics.Bucket) *Server { func New(sConfig ServerConfig, peerServer *PeerServer, registry *Registry, store store.Store, mb *metrics.Bucket) *Server {
r := mux.NewRouter()
cors := &corsHTTPMiddleware{r, sConfig.CORS}
s := &Server{ s := &Server{
Config: sConfig, Config: sConfig,
Server: http.Server{
Handler: cors,
},
store: store, store: store,
registry: registry, registry: registry,
peerServer: peerServer, peerServer: peerServer,
router: r,
corsMiddleware: cors,
metrics: mb, metrics: mb,
} }
// Install the routes.
s.handleFunc("/version", s.GetVersionHandler).Methods("GET")
s.installV1()
s.installV2()
s.installMod()
return s return s
} }
func (s *Server) EnableTracing() { func (s *Server) EnableTracing() {
s.installDebug() s.trace = true
} }
// The current state of the server in the cluster. // The current state of the server in the cluster.
@ -112,64 +97,62 @@ func (s *Server) Store() store.Store {
return s.store return s.store
} }
func (s *Server) installV1() { func (s *Server) installV1(r *mux.Router) {
s.handleFuncV1("/v1/keys/{key:.*}", v1.GetKeyHandler).Methods("GET") s.handleFuncV1(r, "/v1/keys/{key:.*}", v1.GetKeyHandler).Methods("GET")
s.handleFuncV1("/v1/keys/{key:.*}", v1.SetKeyHandler).Methods("POST", "PUT") s.handleFuncV1(r, "/v1/keys/{key:.*}", v1.SetKeyHandler).Methods("POST", "PUT")
s.handleFuncV1("/v1/keys/{key:.*}", v1.DeleteKeyHandler).Methods("DELETE") s.handleFuncV1(r, "/v1/keys/{key:.*}", v1.DeleteKeyHandler).Methods("DELETE")
s.handleFuncV1("/v1/watch/{key:.*}", v1.WatchKeyHandler).Methods("GET", "POST") s.handleFuncV1(r, "/v1/watch/{key:.*}", v1.WatchKeyHandler).Methods("GET", "POST")
s.handleFunc("/v1/leader", s.GetLeaderHandler).Methods("GET") s.handleFunc(r, "/v1/leader", s.GetLeaderHandler).Methods("GET")
s.handleFunc("/v1/machines", s.GetPeersHandler).Methods("GET") s.handleFunc(r, "/v1/machines", s.GetPeersHandler).Methods("GET")
s.handleFunc("/v1/peers", s.GetPeersHandler).Methods("GET") s.handleFunc(r, "/v1/peers", s.GetPeersHandler).Methods("GET")
s.handleFunc("/v1/stats/self", s.GetStatsHandler).Methods("GET") s.handleFunc(r, "/v1/stats/self", s.GetStatsHandler).Methods("GET")
s.handleFunc("/v1/stats/leader", s.GetLeaderStatsHandler).Methods("GET") s.handleFunc(r, "/v1/stats/leader", s.GetLeaderStatsHandler).Methods("GET")
s.handleFunc("/v1/stats/store", s.GetStoreStatsHandler).Methods("GET") s.handleFunc(r, "/v1/stats/store", s.GetStoreStatsHandler).Methods("GET")
} }
func (s *Server) installV2() { func (s *Server) installV2(r *mux.Router) {
s.handleFuncV2("/v2/keys/{key:.*}", v2.GetHandler).Methods("GET") s.handleFuncV2(r, "/v2/keys/{key:.*}", v2.GetHandler).Methods("GET")
s.handleFuncV2("/v2/keys/{key:.*}", v2.PostHandler).Methods("POST") s.handleFuncV2(r, "/v2/keys/{key:.*}", v2.PostHandler).Methods("POST")
s.handleFuncV2("/v2/keys/{key:.*}", v2.PutHandler).Methods("PUT") s.handleFuncV2(r, "/v2/keys/{key:.*}", v2.PutHandler).Methods("PUT")
s.handleFuncV2("/v2/keys/{key:.*}", v2.DeleteHandler).Methods("DELETE") s.handleFuncV2(r, "/v2/keys/{key:.*}", v2.DeleteHandler).Methods("DELETE")
s.handleFunc("/v2/leader", s.GetLeaderHandler).Methods("GET") s.handleFunc(r, "/v2/leader", s.GetLeaderHandler).Methods("GET")
s.handleFunc("/v2/machines", s.GetPeersHandler).Methods("GET") s.handleFunc(r, "/v2/machines", s.GetPeersHandler).Methods("GET")
s.handleFunc("/v2/peers", s.GetPeersHandler).Methods("GET") s.handleFunc(r, "/v2/peers", s.GetPeersHandler).Methods("GET")
s.handleFunc("/v2/stats/self", s.GetStatsHandler).Methods("GET") s.handleFunc(r, "/v2/stats/self", s.GetStatsHandler).Methods("GET")
s.handleFunc("/v2/stats/leader", s.GetLeaderStatsHandler).Methods("GET") s.handleFunc(r, "/v2/stats/leader", s.GetLeaderStatsHandler).Methods("GET")
s.handleFunc("/v2/stats/store", s.GetStoreStatsHandler).Methods("GET") s.handleFunc(r, "/v2/stats/store", s.GetStoreStatsHandler).Methods("GET")
s.handleFunc("/v2/speedTest", s.SpeedTestHandler).Methods("GET") s.handleFunc(r, "/v2/speedTest", s.SpeedTestHandler).Methods("GET")
} }
func (s *Server) installMod() { func (s *Server) installMod(r *mux.Router) {
r := s.router
r.PathPrefix("/mod").Handler(http.StripPrefix("/mod", mod.HttpHandler(s.Config.URL))) r.PathPrefix("/mod").Handler(http.StripPrefix("/mod", mod.HttpHandler(s.Config.URL)))
} }
func (s *Server) installDebug() { func (s *Server) installDebug(r *mux.Router) {
s.handleFunc("/debug/metrics", s.GetMetricsHandler).Methods("GET") s.handleFunc(r, "/debug/metrics", s.GetMetricsHandler).Methods("GET")
s.router.HandleFunc("/debug/pprof", pprof.Index) r.HandleFunc("/debug/pprof", pprof.Index)
s.router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) r.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
s.router.HandleFunc("/debug/pprof/profile", pprof.Profile) r.HandleFunc("/debug/pprof/profile", pprof.Profile)
s.router.HandleFunc("/debug/pprof/symbol", pprof.Symbol) r.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
s.router.HandleFunc("/debug/pprof/{name}", pprof.Index) r.HandleFunc("/debug/pprof/{name}", pprof.Index)
} }
// Adds a v1 server handler to the router. // Adds a v1 server handler to the router.
func (s *Server) handleFuncV1(path string, f func(http.ResponseWriter, *http.Request, v1.Server) error) *mux.Route { func (s *Server) handleFuncV1(r *mux.Router, path string, f func(http.ResponseWriter, *http.Request, v1.Server) error) *mux.Route {
return s.handleFunc(path, func(w http.ResponseWriter, req *http.Request) error { return s.handleFunc(r, path, func(w http.ResponseWriter, req *http.Request) error {
return f(w, req, s) return f(w, req, s)
}) })
} }
// Adds a v2 server handler to the router. // Adds a v2 server handler to the router.
func (s *Server) handleFuncV2(path string, f func(http.ResponseWriter, *http.Request, v2.Server) error) *mux.Route { func (s *Server) handleFuncV2(r *mux.Router, path string, f func(http.ResponseWriter, *http.Request, v2.Server) error) *mux.Route {
return s.handleFunc(path, func(w http.ResponseWriter, req *http.Request) error { return s.handleFunc(r, path, func(w http.ResponseWriter, req *http.Request) error {
return f(w, req, s) return f(w, req, s)
}) })
} }
// Adds a server handler to the router. // Adds a server handler to the router.
func (s *Server) handleFunc(path string, f func(http.ResponseWriter, *http.Request) error) *mux.Route { func (s *Server) handleFunc(r *mux.Router, path string, f func(http.ResponseWriter, *http.Request) error) *mux.Route {
r := s.router
// Wrap the standard HandleFunc interface to pass in the server reference. // Wrap the standard HandleFunc interface to pass in the server reference.
return r.HandleFunc(path, func(w http.ResponseWriter, req *http.Request) { return r.HandleFunc(path, func(w http.ResponseWriter, req *http.Request) {
@ -189,11 +172,20 @@ func (s *Server) handleFunc(path string, f func(http.ResponseWriter, *http.Reque
}) })
} }
// Start to listen and response etcd client command func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
func (s *Server) Serve(listener net.Listener) error { router := mux.NewRouter()
log.Infof("etcd server [name %s, listen on %s, advertised url %s]", s.Config.Name, listener.Addr(), s.Config.URL)
s.listener = listener // Install the routes.
return s.Server.Serve(listener) s.handleFunc(router, "/version", s.GetVersionHandler).Methods("GET")
s.installV1(router)
s.installV2(router)
s.installMod(router)
if s.trace {
s.installDebug(router)
}
router.ServeHTTP(w, r)
} }
// Stops the server. // Stops the server.