github.com/jpetazzo/etcd@v0.2.1-0.20140113055439-97f1363afac5/server/server.go (about) 1 package server 2 3 import ( 4 "crypto/tls" 5 "encoding/json" 6 "fmt" 7 "net" 8 "net/http" 9 "strings" 10 "time" 11 12 etcdErr "github.com/coreos/etcd/error" 13 "github.com/coreos/etcd/log" 14 "github.com/coreos/etcd/mod" 15 "github.com/coreos/etcd/server/v1" 16 "github.com/coreos/etcd/server/v2" 17 "github.com/coreos/etcd/store" 18 _ "github.com/coreos/etcd/store/v2" 19 "github.com/coreos/raft" 20 "github.com/gorilla/mux" 21 ) 22 23 // This is the default implementation of the Server interface. 24 type Server struct { 25 http.Server 26 peerServer *PeerServer 27 registry *Registry 28 listener net.Listener 29 store store.Store 30 name string 31 url string 32 tlsConf *TLSConfig 33 tlsInfo *TLSInfo 34 router *mux.Router 35 corsHandler *corsHandler 36 } 37 38 // Creates a new Server. 39 func New(name string, urlStr string, bindAddr string, tlsConf *TLSConfig, tlsInfo *TLSInfo, peerServer *PeerServer, registry *Registry, store store.Store) *Server { 40 r := mux.NewRouter() 41 cors := &corsHandler{router: r} 42 43 s := &Server{ 44 Server: http.Server{ 45 Handler: cors, 46 TLSConfig: &tlsConf.Server, 47 Addr: bindAddr, 48 }, 49 name: name, 50 store: store, 51 registry: registry, 52 url: urlStr, 53 tlsConf: tlsConf, 54 tlsInfo: tlsInfo, 55 peerServer: peerServer, 56 router: r, 57 corsHandler: cors, 58 } 59 60 // Install the routes. 61 s.handleFunc("/version", s.GetVersionHandler).Methods("GET") 62 s.installV1() 63 s.installV2() 64 s.installMod() 65 66 return s 67 } 68 69 // The current state of the server in the cluster. 70 func (s *Server) State() string { 71 return s.peerServer.RaftServer().State() 72 } 73 74 // The node name of the leader in the cluster. 75 func (s *Server) Leader() string { 76 return s.peerServer.RaftServer().Leader() 77 } 78 79 // The current Raft committed index. 80 func (s *Server) CommitIndex() uint64 { 81 return s.peerServer.RaftServer().CommitIndex() 82 } 83 84 // The current Raft term. 85 func (s *Server) Term() uint64 { 86 return s.peerServer.RaftServer().Term() 87 } 88 89 // The server URL. 90 func (s *Server) URL() string { 91 return s.url 92 } 93 94 // Retrives the Peer URL for a given node name. 95 func (s *Server) PeerURL(name string) (string, bool) { 96 return s.registry.PeerURL(name) 97 } 98 99 // ClientURL retrieves the Client URL for a given node name. 100 func (s *Server) ClientURL(name string) (string, bool) { 101 return s.registry.ClientURL(name) 102 } 103 104 // Returns a reference to the Store. 105 func (s *Server) Store() store.Store { 106 return s.store 107 } 108 109 func (s *Server) installV1() { 110 s.handleFuncV1("/v1/keys/{key:.*}", v1.GetKeyHandler).Methods("GET") 111 s.handleFuncV1("/v1/keys/{key:.*}", v1.SetKeyHandler).Methods("POST", "PUT") 112 s.handleFuncV1("/v1/keys/{key:.*}", v1.DeleteKeyHandler).Methods("DELETE") 113 s.handleFuncV1("/v1/watch/{key:.*}", v1.WatchKeyHandler).Methods("GET", "POST") 114 s.handleFunc("/v1/leader", s.GetLeaderHandler).Methods("GET") 115 s.handleFunc("/v1/machines", s.GetPeersHandler).Methods("GET") 116 s.handleFunc("/v1/peers", s.GetPeersHandler).Methods("GET") 117 s.handleFunc("/v1/stats/self", s.GetStatsHandler).Methods("GET") 118 s.handleFunc("/v1/stats/leader", s.GetLeaderStatsHandler).Methods("GET") 119 s.handleFunc("/v1/stats/store", s.GetStoreStatsHandler).Methods("GET") 120 } 121 122 func (s *Server) installV2() { 123 s.handleFuncV2("/v2/keys/{key:.*}", v2.GetHandler).Methods("GET") 124 s.handleFuncV2("/v2/keys/{key:.*}", v2.PostHandler).Methods("POST") 125 s.handleFuncV2("/v2/keys/{key:.*}", v2.PutHandler).Methods("PUT") 126 s.handleFuncV2("/v2/keys/{key:.*}", v2.DeleteHandler).Methods("DELETE") 127 s.handleFunc("/v2/leader", s.GetLeaderHandler).Methods("GET") 128 s.handleFunc("/v2/machines", s.GetPeersHandler).Methods("GET") 129 s.handleFunc("/v2/peers", s.GetPeersHandler).Methods("GET") 130 s.handleFunc("/v2/stats/self", s.GetStatsHandler).Methods("GET") 131 s.handleFunc("/v2/stats/leader", s.GetLeaderStatsHandler).Methods("GET") 132 s.handleFunc("/v2/stats/store", s.GetStoreStatsHandler).Methods("GET") 133 s.handleFunc("/v2/speedTest", s.SpeedTestHandler).Methods("GET") 134 } 135 136 func (s *Server) installMod() { 137 r := s.router 138 r.PathPrefix("/mod").Handler(http.StripPrefix("/mod", mod.HttpHandler(s.url))) 139 } 140 141 // Adds a v1 server handler to the router. 142 func (s *Server) handleFuncV1(path string, f func(http.ResponseWriter, *http.Request, v1.Server) error) *mux.Route { 143 return s.handleFunc(path, func(w http.ResponseWriter, req *http.Request) error { 144 return f(w, req, s) 145 }) 146 } 147 148 // Adds a v2 server handler to the router. 149 func (s *Server) handleFuncV2(path string, f func(http.ResponseWriter, *http.Request, v2.Server) error) *mux.Route { 150 return s.handleFunc(path, func(w http.ResponseWriter, req *http.Request) error { 151 return f(w, req, s) 152 }) 153 } 154 155 // Adds a server handler to the router. 156 func (s *Server) handleFunc(path string, f func(http.ResponseWriter, *http.Request) error) *mux.Route { 157 r := s.router 158 159 // Wrap the standard HandleFunc interface to pass in the server reference. 160 return r.HandleFunc(path, func(w http.ResponseWriter, req *http.Request) { 161 // Log request. 162 log.Debugf("[recv] %s %s %s [%s]", req.Method, s.url, req.URL.Path, req.RemoteAddr) 163 164 // Execute handler function and return error if necessary. 165 if err := f(w, req); err != nil { 166 if etcdErr, ok := err.(*etcdErr.Error); ok { 167 log.Debug("Return error: ", (*etcdErr).Error()) 168 w.Header().Set("Content-Type", "application/json") 169 etcdErr.Write(w) 170 } else { 171 http.Error(w, err.Error(), http.StatusInternalServerError) 172 } 173 } 174 }) 175 } 176 177 // Start to listen and response etcd client command 178 func (s *Server) ListenAndServe() error { 179 log.Infof("etcd server [name %s, listen on %s, advertised url %s]", s.name, s.Server.Addr, s.url) 180 181 if s.tlsConf.Scheme == "http" { 182 return s.listenAndServe() 183 } else { 184 return s.listenAndServeTLS(s.tlsInfo.CertFile, s.tlsInfo.KeyFile) 185 } 186 } 187 188 // Overridden version of net/http added so we can manage the listener. 189 func (s *Server) listenAndServe() error { 190 addr := s.Server.Addr 191 if addr == "" { 192 addr = ":http" 193 } 194 l, e := net.Listen("tcp", addr) 195 if e != nil { 196 return e 197 } 198 s.listener = l 199 return s.Server.Serve(l) 200 } 201 202 // Overridden version of net/http added so we can manage the listener. 203 func (s *Server) listenAndServeTLS(certFile, keyFile string) error { 204 addr := s.Server.Addr 205 if addr == "" { 206 addr = ":https" 207 } 208 config := &tls.Config{} 209 if s.Server.TLSConfig != nil { 210 *config = *s.Server.TLSConfig 211 } 212 if config.NextProtos == nil { 213 config.NextProtos = []string{"http/1.1"} 214 } 215 216 var err error 217 config.Certificates = make([]tls.Certificate, 1) 218 config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile) 219 if err != nil { 220 return err 221 } 222 223 conn, err := net.Listen("tcp", addr) 224 if err != nil { 225 return err 226 } 227 228 tlsListener := tls.NewListener(conn, config) 229 s.listener = tlsListener 230 return s.Server.Serve(tlsListener) 231 } 232 233 // Stops the server. 234 func (s *Server) Close() { 235 if s.listener != nil { 236 s.listener.Close() 237 s.listener = nil 238 } 239 } 240 241 // Dispatch command to the current leader 242 func (s *Server) Dispatch(c raft.Command, w http.ResponseWriter, req *http.Request) error { 243 ps := s.peerServer 244 if ps.raftServer.State() == raft.Leader { 245 result, err := ps.raftServer.Do(c) 246 if err != nil { 247 return err 248 } 249 250 if result == nil { 251 return etcdErr.NewError(300, "Empty result from raft", s.Store().Index()) 252 } 253 254 // response for raft related commands[join/remove] 255 if b, ok := result.([]byte); ok { 256 w.WriteHeader(http.StatusOK) 257 w.Write(b) 258 return nil 259 } 260 261 var b []byte 262 if strings.HasPrefix(req.URL.Path, "/v1") { 263 b, _ = json.Marshal(result.(*store.Event).Response(0)) 264 w.WriteHeader(http.StatusOK) 265 } else { 266 e, _ := result.(*store.Event) 267 b, _ = json.Marshal(e) 268 269 w.Header().Set("Content-Type", "application/json") 270 // etcd index should be the same as the event index 271 // which is also the last modified index of the node 272 w.Header().Add("X-Etcd-Index", fmt.Sprint(e.Index())) 273 w.Header().Add("X-Raft-Index", fmt.Sprint(s.CommitIndex())) 274 w.Header().Add("X-Raft-Term", fmt.Sprint(s.Term())) 275 276 if e.IsCreated() { 277 w.WriteHeader(http.StatusCreated) 278 } else { 279 w.WriteHeader(http.StatusOK) 280 } 281 } 282 283 w.Write(b) 284 285 return nil 286 287 } else { 288 leader := ps.raftServer.Leader() 289 290 // No leader available. 291 if leader == "" { 292 return etcdErr.NewError(300, "", s.Store().Index()) 293 } 294 295 var url string 296 switch c.(type) { 297 case *JoinCommand, *RemoveCommand: 298 url, _ = ps.registry.PeerURL(leader) 299 default: 300 url, _ = ps.registry.ClientURL(leader) 301 } 302 redirect(url, w, req) 303 304 return nil 305 } 306 } 307 308 // OriginAllowed determines whether the server will allow a given CORS origin. 309 func (s *Server) OriginAllowed(origin string) bool { 310 return s.corsHandler.OriginAllowed(origin) 311 } 312 313 // AllowOrigins sets a comma-delimited list of origins that are allowed. 314 func (s *Server) AllowOrigins(origins []string) error { 315 return s.corsHandler.AllowOrigins(origins) 316 } 317 318 // Handler to return the current version of etcd. 319 func (s *Server) GetVersionHandler(w http.ResponseWriter, req *http.Request) error { 320 w.WriteHeader(http.StatusOK) 321 fmt.Fprintf(w, "etcd %s", ReleaseVersion) 322 return nil 323 } 324 325 // Handler to return the current leader's raft address 326 func (s *Server) GetLeaderHandler(w http.ResponseWriter, req *http.Request) error { 327 leader := s.peerServer.RaftServer().Leader() 328 if leader == "" { 329 return etcdErr.NewError(etcdErr.EcodeLeaderElect, "", s.Store().Index()) 330 } 331 w.WriteHeader(http.StatusOK) 332 url, _ := s.registry.PeerURL(leader) 333 w.Write([]byte(url)) 334 return nil 335 } 336 337 // Handler to return all the known peers in the current cluster. 338 func (s *Server) GetPeersHandler(w http.ResponseWriter, req *http.Request) error { 339 peers := s.registry.ClientURLs(s.peerServer.RaftServer().Leader(), s.name) 340 w.WriteHeader(http.StatusOK) 341 w.Write([]byte(strings.Join(peers, ", "))) 342 return nil 343 } 344 345 // Retrieves stats on the Raft server. 346 func (s *Server) GetStatsHandler(w http.ResponseWriter, req *http.Request) error { 347 w.Write(s.peerServer.Stats()) 348 return nil 349 } 350 351 // Retrieves stats on the leader. 352 func (s *Server) GetLeaderStatsHandler(w http.ResponseWriter, req *http.Request) error { 353 if s.peerServer.RaftServer().State() == raft.Leader { 354 w.Write(s.peerServer.PeerStats()) 355 return nil 356 } 357 358 leader := s.peerServer.RaftServer().Leader() 359 if leader == "" { 360 return etcdErr.NewError(300, "", s.Store().Index()) 361 } 362 hostname, _ := s.registry.ClientURL(leader) 363 redirect(hostname, w, req) 364 return nil 365 } 366 367 // Retrieves stats on the leader. 368 func (s *Server) GetStoreStatsHandler(w http.ResponseWriter, req *http.Request) error { 369 w.Write(s.store.JsonStats()) 370 return nil 371 } 372 373 // Executes a speed test to evaluate the performance of update replication. 374 func (s *Server) SpeedTestHandler(w http.ResponseWriter, req *http.Request) error { 375 count := 1000 376 c := make(chan bool, count) 377 for i := 0; i < count; i++ { 378 go func() { 379 for j := 0; j < 10; j++ { 380 c := s.Store().CommandFactory().CreateSetCommand("foo", false, "bar", time.Unix(0, 0)) 381 s.peerServer.RaftServer().Do(c) 382 } 383 c <- true 384 }() 385 } 386 387 for i := 0; i < count; i++ { 388 <-c 389 } 390 391 w.WriteHeader(http.StatusOK) 392 w.Write([]byte("speed test success")) 393 return nil 394 }