فهرست منبع

sepearte raft handlers and client handlers to two files and clean up related codes

Xiang Li 13 سال پیش
والد
کامیت
a3173bfd31
4فایلهای تغییر یافته به همراه245 افزوده شده و 197 حذف شده
  1. 59 118
      client_handlers.go
  2. 88 79
      etcd.go
  3. 95 0
      raft_handlers.go
  4. 3 0
      version.go

+ 59 - 118
handlers.go → client_handlers.go

@@ -1,101 +1,16 @@
 package main
 
 import (
-	"encoding/json"
-	"github.com/coreos/go-raft"
 	"net/http"
 	"strconv"
 	"time"
 )
 
-//--------------------------------------
-// Internal HTTP Handlers via server port
-//--------------------------------------
-
-// Get all the current logs
-func GetLogHttpHandler(w http.ResponseWriter, req *http.Request) {
-	debug("[recv] GET http://%v/log", raftServer.Name())
-	w.Header().Set("Content-Type", "application/json")
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(raftServer.LogEntries())
-}
-
-func VoteHttpHandler(w http.ResponseWriter, req *http.Request) {
-	rvreq := &raft.RequestVoteRequest{}
-	err := decodeJsonRequest(req, rvreq)
-	if err == nil {
-		debug("[recv] POST http://%v/vote [%s]", raftServer.Name(), rvreq.CandidateName)
-		if resp := raftServer.RequestVote(rvreq); resp != nil {
-			w.WriteHeader(http.StatusOK)
-			json.NewEncoder(w).Encode(resp)
-			return
-		}
-	}
-	warn("[vote] ERROR: %v", err)
-	w.WriteHeader(http.StatusInternalServerError)
-}
-
-func AppendEntriesHttpHandler(w http.ResponseWriter, req *http.Request) {
-	aereq := &raft.AppendEntriesRequest{}
-	err := decodeJsonRequest(req, aereq)
-
-	if err == nil {
-		debug("[recv] POST http://%s/log/append [%d]", raftServer.Name(), len(aereq.Entries))
-		if resp := raftServer.AppendEntries(aereq); resp != nil {
-			w.WriteHeader(http.StatusOK)
-			json.NewEncoder(w).Encode(resp)
-			if !resp.Success {
-				debug("[Append Entry] Step back")
-			}
-			return
-		}
-	}
-	warn("[Append Entry] ERROR: %v", err)
-	w.WriteHeader(http.StatusInternalServerError)
-}
-
-func SnapshotHttpHandler(w http.ResponseWriter, req *http.Request) {
-	aereq := &raft.SnapshotRequest{}
-	err := decodeJsonRequest(req, aereq)
-	if err == nil {
-		debug("[recv] POST http://%s/snapshot/ ", raftServer.Name())
-		if resp, _ := raftServer.SnapshotRecovery(aereq); resp != nil {
-			w.WriteHeader(http.StatusOK)
-			json.NewEncoder(w).Encode(resp)
-			return
-		}
-	}
-	warn("[Snapshot] ERROR: %v", err)
-	w.WriteHeader(http.StatusInternalServerError)
-}
-
-// Get the port that listening for client connecting of the server
-func clientHttpHandler(w http.ResponseWriter, req *http.Request) {
-	debug("[recv] Get http://%v/client/ ", raftServer.Name())
-	w.WriteHeader(http.StatusOK)
-	client := address + ":" + strconv.Itoa(clientPort)
-	w.Write([]byte(client))
-}
-
-//
-func JoinHttpHandler(w http.ResponseWriter, req *http.Request) {
-
-	command := &JoinCommand{}
-
-	if err := decodeJsonRequest(req, command); err == nil {
-		debug("Receive Join Request from %s", command.Name)
-		excute(command, &w, req)
-	} else {
-		w.WriteHeader(http.StatusInternalServerError)
-		return
-	}
-}
-
-//--------------------------------------
-// external HTTP Handlers via client port
-//--------------------------------------
+//-------------------------------------------------------------------
+// Handlers to handle etcd-store related request via raft client port
+//-------------------------------------------------------------------
 
-// Dispatch GET/POST/DELETE request to corresponding handlers
+// Multiplex GET/POST/DELETE request to corresponding handlers
 func Multiplexer(w http.ResponseWriter, req *http.Request) {
 
 	if req.Method == "GET" {
@@ -110,6 +25,11 @@ func Multiplexer(w http.ResponseWriter, req *http.Request) {
 	}
 }
 
+//--------------------------------------
+// State sensitive handlers 
+// Set/Delte will dispatch to leader
+//--------------------------------------
+
 // Set Command Handler
 func SetHttpHandler(w *http.ResponseWriter, req *http.Request) {
 	key := req.URL.Path[len("/v1/keys/"):]
@@ -122,23 +42,20 @@ func SetHttpHandler(w *http.ResponseWriter, req *http.Request) {
 	command.Value = req.FormValue("value")
 	strDuration := req.FormValue("ttl")
 
-	if strDuration != "" {
-		duration, err := strconv.Atoi(strDuration)
+	var err error
 
-		if err != nil {
-			warn("Bad duration: %v", err)
-			(*w).WriteHeader(http.StatusInternalServerError)
-			return
-		}
-		command.ExpireTime = time.Now().Add(time.Second * (time.Duration)(duration))
-	} else {
-		command.ExpireTime = time.Unix(0, 0)
+	command.ExpireTime, err = durationToExpireTime(strDuration)
+
+	if err != nil {
+		warn("The given duration is not a number: %v", err)
+		(*w).WriteHeader(http.StatusInternalServerError)
 	}
 
-	excute(command, w, req)
+	dispatch(command, w, req)
 
 }
 
+// TestAndSet handler
 func TestAndSetHttpHandler(w http.ResponseWriter, req *http.Request) {
 	key := req.URL.Path[len("/v1/testAndSet/"):]
 
@@ -151,23 +68,20 @@ func TestAndSetHttpHandler(w http.ResponseWriter, req *http.Request) {
 	command.Value = req.FormValue("value")
 	strDuration := req.FormValue("ttl")
 
-	if strDuration != "" {
-		duration, err := strconv.Atoi(strDuration)
+	var err error
 
-		if err != nil {
-			warn("Bad duration: %v", err)
-			w.WriteHeader(http.StatusInternalServerError)
-			return
-		}
-		command.ExpireTime = time.Now().Add(time.Second * (time.Duration)(duration))
-	} else {
-		command.ExpireTime = time.Unix(0, 0)
-	}
+	command.ExpireTime, err = durationToExpireTime(strDuration)
 
-	excute(command, &w, req)
+	if err != nil {
+		warn("The given duration is not a number: %v", err)
+		w.WriteHeader(http.StatusInternalServerError)
+	}
+	
+	dispatch(command, &w, req)
 
 }
 
+// Delete Handler
 func DeleteHttpHandler(w *http.ResponseWriter, req *http.Request) {
 	key := req.URL.Path[len("/v1/keys/"):]
 
@@ -176,10 +90,11 @@ func DeleteHttpHandler(w *http.ResponseWriter, req *http.Request) {
 	command := &DeleteCommand{}
 	command.Key = key
 
-	excute(command, w, req)
+	dispatch(command, w, req)
 }
 
-func excute(c Command, w *http.ResponseWriter, req *http.Request) {
+// Dispatch the command to leader
+func dispatch(c Command, w *http.ResponseWriter, req *http.Request) {
 	if raftServer.State() == "leader" {
 		if body, err := raftServer.Do(c); err != nil {
 			warn("Commit failed %v", err)
@@ -208,7 +123,6 @@ func excute(c Command, w *http.ResponseWriter, req *http.Request) {
 		}
 
 		// tell the client where is the leader
-		debug("Redirect to the leader %s", raftServer.Leader())
 
 		path := req.URL.Path
 
@@ -220,7 +134,7 @@ func excute(c Command, w *http.ResponseWriter, req *http.Request) {
 
 		url := scheme + raftTransporter.GetLeaderClientAddress() + path
 
-		debug("redirect to %s", url)
+		debug("Redirect to %s", url)
 
 		http.Redirect(*w, req, url, http.StatusTemporaryRedirect)
 		return
@@ -231,11 +145,20 @@ func excute(c Command, w *http.ResponseWriter, req *http.Request) {
 	return
 }
 
-func MasterHttpHandler(w http.ResponseWriter, req *http.Request) {
+//--------------------------------------
+// State non-sensitive handlers 
+// will not dispatch to leader
+// TODO: add sensitive version for these
+// command?
+//--------------------------------------
+
+// Handler to return the current leader name
+func LeaderHttpHandler(w http.ResponseWriter, req *http.Request) {
 	w.WriteHeader(http.StatusOK)
 	w.Write([]byte(raftServer.Leader()))
 }
 
+// Get Handler
 func GetHttpHandler(w *http.ResponseWriter, req *http.Request) {
 	key := req.URL.Path[len("/v1/keys/"):]
 
@@ -262,6 +185,7 @@ func GetHttpHandler(w *http.ResponseWriter, req *http.Request) {
 
 }
 
+// List Handler
 func ListHttpHandler(w http.ResponseWriter, req *http.Request) {
 	prefix := req.URL.Path[len("/v1/list/"):]
 
@@ -288,6 +212,7 @@ func ListHttpHandler(w http.ResponseWriter, req *http.Request) {
 
 }
 
+// Watch handler
 func WatchHttpHandler(w http.ResponseWriter, req *http.Request) {
 	key := req.URL.Path[len("/v1/watch/"):]
 
@@ -299,6 +224,8 @@ func WatchHttpHandler(w http.ResponseWriter, req *http.Request) {
 		command.SinceIndex = 0
 
 	} else if req.Method == "POST" {
+		// watch from a specific index
+
 		debug("[recv] POST http://%v/watch/%s", raftServer.Name(), key)
 		content := req.FormValue("index")
 
@@ -314,7 +241,7 @@ func WatchHttpHandler(w http.ResponseWriter, req *http.Request) {
 	}
 
 	if body, err := command.Apply(raftServer); err != nil {
-		warn("Unable to write file: %v", err)
+		warn("Unable to do watch command: %v", err)
 		w.WriteHeader(http.StatusInternalServerError)
 		return
 	} else {
@@ -330,3 +257,17 @@ func WatchHttpHandler(w http.ResponseWriter, req *http.Request) {
 	}
 
 }
+
+// Convert string duration to time format
+func durationToExpireTime(strDuration string) (time.Time, error){
+	if strDuration != "" {
+		duration, err := strconv.Atoi(strDuration)
+
+		if err != nil {
+			return time.Unix(0, 0),err
+		}
+		return time.Now().Add(time.Second * (time.Duration)(duration)), nil
+	} else {
+		return time.Unix(0, 0), nil
+	}
+}

+ 88 - 79
etcd.go

@@ -11,12 +11,10 @@ import (
 	"github.com/coreos/etcd/store"
 	"github.com/coreos/etcd/web"
 	"github.com/coreos/go-raft"
-	//"io"
 	"io/ioutil"
 	"log"
 	"net/http"
 	"os"
-	//"strconv"
 	"strings"
 	"time"
 )
@@ -130,58 +128,52 @@ func main() {
 	flag.Parse()
 
 	// Setup commands.
-	raft.RegisterCommand(&JoinCommand{})
-	raft.RegisterCommand(&SetCommand{})
-	raft.RegisterCommand(&GetCommand{})
-	raft.RegisterCommand(&DeleteCommand{})
-	raft.RegisterCommand(&WatchCommand{})
-	raft.RegisterCommand(&ListCommand{})
-	raft.RegisterCommand(&TestAndSetCommand{})
+	registerCommands()
 
+	// Read server info from file or grab it from user.
 	if err := os.MkdirAll(dirPath, 0744); err != nil {
 		fatal("Unable to create path: %v", err)
 	}
-
-	// Read server info from file or grab it from user.
 	var info *Info = getInfo(dirPath)
 
 	name := fmt.Sprintf("%s:%d", info.Address, info.ServerPort)
 
-	fmt.Printf("ServerName: %s\n\n", name)
-
 	// secrity type
 	st := securityType(SERVER)
 
-	if st == -1 {
-		panic("ERROR type")
+	clientSt := securityType(CLIENT)
+
+	if st == -1 || clientSt == -1 {
+		fatal("Please specify cert and key file or cert and key file and CAFile or none of the three")
 	}
 
+
+	// Create transporter for raft
 	raftTransporter = createTransporter(st)
 
-	// Setup new raft server.
+	// Create etcd key-value store
 	etcdStore = store.CreateStore(maxSize)
 
-	// create raft server
+	// Create raft server
 	raftServer, err = raft.NewServer(name, dirPath, raftTransporter, etcdStore, nil)
 
 	if err != nil {
-		fatal("%v", err)
+		fmt.Println(err)
+		os.Exit(1)
 	}
 
-	err = raftServer.LoadSnapshot()
+	// LoadSnapshot
+	// err = raftServer.LoadSnapshot()
 
-	if err == nil {
-		debug("%s finished load snapshot", raftServer.Name())
-	} else {
-		fmt.Println(err)
-		debug("%s bad snapshot", raftServer.Name())
-	}
+	// if err == nil {
+	// 	debug("%s finished load snapshot", raftServer.Name())
+	// } else {
+	// 	debug(err)
+	// }
 
 	raftServer.Initialize()
-	debug("%s finished init", raftServer.Name())
 	raftServer.SetElectionTimeout(ELECTIONTIMTOUT)
 	raftServer.SetHeartbeatTimeout(HEARTBEATTIMEOUT)
-	debug("%s finished set timeout", raftServer.Name())
 
 	if raftServer.IsLogEmpty() {
 
@@ -206,9 +198,9 @@ func main() {
 		} else {
 			raftServer.StartFollower()
 
-			err := Join(raftServer, cluster)
+			err := joinCluster(raftServer, cluster)
 			if err != nil {
-				panic(err)
+				fatal(fmt.Sprintln(err))
 			}
 			debug("%s success join to the cluster", raftServer.Name())
 		}
@@ -220,7 +212,7 @@ func main() {
 	}
 
 	// open the snapshot
-	//go server.Snapshot()
+	// go server.Snapshot()
 
 	if webPort != -1 {
 		// start web
@@ -229,11 +221,15 @@ func main() {
 		go web.Start(raftServer, webPort)
 	}
 
-	go startServTransport(info.ServerPort, st)
-	startClientTransport(info.ClientPort, securityType(CLIENT))
+	go startRaftTransport(info.ServerPort, st)
+
+	startClientTransport(info.ClientPort, clientSt)
 
 }
 
+// Create transporter using by raft server
+// Create http or https transporter based on 
+// wether the user give the server cert and key
 func createTransporter(st int) transporter {
 	t := transporter{}
 
@@ -248,7 +244,7 @@ func createTransporter(st int) transporter {
 		tlsCert, err := tls.LoadX509KeyPair(serverCertFile, serverKeyFile)
 
 		if err != nil {
-			panic(err)
+			fatal(fmt.Sprintln(err))
 		}
 
 		tr := &http.Transport{
@@ -267,7 +263,8 @@ func createTransporter(st int) transporter {
 	return transporter{}
 }
 
-func startServTransport(port int, st int) {
+// Start to listen and response raft command
+func startRaftTransport(port int, st int) {
 
 	// internal commands
 	http.HandleFunc("/join", JoinHttpHandler)
@@ -275,41 +272,29 @@ func startServTransport(port int, st int) {
 	http.HandleFunc("/log", GetLogHttpHandler)
 	http.HandleFunc("/log/append", AppendEntriesHttpHandler)
 	http.HandleFunc("/snapshot", SnapshotHttpHandler)
-	http.HandleFunc("/client", clientHttpHandler)
+	http.HandleFunc("/client", ClientHttpHandler)
 
 	switch st {
 
 	case HTTP:
-		debug("raft server [%s] listen on http port %v", address, port)
+		fmt.Println("raft server [%s] listen on http port %v", address, port)
 		log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil))
 
 	case HTTPS:
-		debug("raft server [%s] listen on https port %v", address, port)
+		fmt.Println("raft server [%s] listen on https port %v", address, port)
 		log.Fatal(http.ListenAndServeTLS(fmt.Sprintf(":%d", port), serverCertFile, serverKeyFile, nil))
 
 	case HTTPSANDVERIFY:
-		pemByte, _ := ioutil.ReadFile(serverCAFile)
-
-		block, pemByte := pem.Decode(pemByte)
-
-		cert, err := x509.ParseCertificate(block.Bytes)
-
-		if err != nil {
-			fmt.Println(err)
-		}
-
-		certPool := x509.NewCertPool()
-
-		certPool.AddCert(cert)
 
 		server := &http.Server{
 			TLSConfig: &tls.Config{
 				ClientAuth: tls.RequireAndVerifyClientCert,
-				ClientCAs:  certPool,
+				ClientCAs:  createCertPool(serverCAFile),
 			},
 			Addr: fmt.Sprintf(":%d", port),
 		}
-		err = server.ListenAndServeTLS(serverCertFile, serverKeyFile)
+		fmt.Println("raft server [%s] listen on https port %v", address, port)
+		err := server.ListenAndServeTLS(serverCertFile, serverKeyFile)
 
 		if err != nil {
 			log.Fatal(err)
@@ -318,49 +303,40 @@ func startServTransport(port int, st int) {
 
 }
 
+// Start to listen and response client command
 func startClientTransport(port int, st int) {
 	// external commands
-	http.HandleFunc("/v1/keys/", Multiplexer)
-	http.HandleFunc("/v1/watch/", WatchHttpHandler)
-	http.HandleFunc("/v1/list/", ListHttpHandler)
-	http.HandleFunc("/v1/testAndSet/", TestAndSetHttpHandler)
-	http.HandleFunc("/master", MasterHttpHandler)
+	http.HandleFunc("/" + version + "/keys/", Multiplexer)
+	http.HandleFunc("/" + version + "/watch/", WatchHttpHandler)
+	http.HandleFunc("/" + version + "/list/", ListHttpHandler)
+	http.HandleFunc("/" + version + "/testAndSet/", TestAndSetHttpHandler)
+	http.HandleFunc("/leader", LeaderHttpHandler)
 
 	switch st {
 
 	case HTTP:
-		debug("etcd [%s] listen on http port %v", address, clientPort)
+		fmt.Println("etcd [%s] listen on http port %v", address, clientPort)
 		log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil))
 
 	case HTTPS:
+		fmt.Println("etcd [%s] listen on https port %v", address, clientPort)
 		http.ListenAndServeTLS(fmt.Sprintf(":%d", port), clientCertFile, clientKeyFile, nil)
 
 	case HTTPSANDVERIFY:
-		pemByte, _ := ioutil.ReadFile(clientCAFile)
-
-		block, pemByte := pem.Decode(pemByte)
-
-		cert, err := x509.ParseCertificate(block.Bytes)
-
-		if err != nil {
-			fmt.Println(err)
-		}
-
-		certPool := x509.NewCertPool()
-
-		certPool.AddCert(cert)
 
 		server := &http.Server{
 			TLSConfig: &tls.Config{
 				ClientAuth: tls.RequireAndVerifyClientCert,
-				ClientCAs:  certPool,
+				ClientCAs:  createCertPool(clientCAFile),
 			},
 			Addr: fmt.Sprintf(":%d", port),
 		}
-		err = server.ListenAndServeTLS(clientCertFile, clientKeyFile)
+		fmt.Println("etcd [%s] listen on https port %v", address, clientPort)
+		err := server.ListenAndServeTLS(clientCertFile, clientKeyFile)
 
 		if err != nil {
 			log.Fatal(err)
+			os.Exit(1)
 		}
 	}
 }
@@ -374,6 +350,7 @@ func securityType(source int) int {
 	var keyFile, certFile, CAFile string
 
 	switch source {
+
 	case SERVER:
 		keyFile = serverKeyFile
 		certFile = serverCertFile
@@ -385,6 +362,8 @@ func securityType(source int) int {
 		CAFile = clientCAFile
 	}
 
+	// If the user do not specify key file, cert file and
+	// CA file, the type will be HTTP  
 	if keyFile == "" && certFile == "" && CAFile == "" {
 
 		return HTTP
@@ -392,14 +371,18 @@ func securityType(source int) int {
 	}
 
 	if keyFile != "" && certFile != "" {
-
 		if CAFile != "" {
+			// If the user specify all the three file, the type 
+			// will be HTTPS with client cert auth
 			return HTTPSANDVERIFY
 		}
-
+		// If the user specify key file and cert file but not
+		// CA file, the type will be HTTPS without client cert 
+		// auth
 		return HTTPS
 	}
 
+	// bad specification
 	return -1
 }
 
@@ -455,12 +438,27 @@ func getInfo(path string) *Info {
 	return info
 }
 
-//--------------------------------------
-// Handlers
-//--------------------------------------
+func createCertPool(CAFile string) *x509.CertPool {
+	pemByte, _ := ioutil.ReadFile(CAFile)
+
+	block, pemByte := pem.Decode(pemByte)
+
+	cert, err := x509.ParseCertificate(block.Bytes)
+
+	if err != nil {
+		fmt.Println(err)
+		os.Exit(1)
+	}
+
+	certPool := x509.NewCertPool()
+
+	certPool.AddCert(cert)
+
+	return certPool
+}
 
 // Send join requests to the leader.
-func Join(s *raft.Server, serverName string) error {
+func joinCluster(s *raft.Server, serverName string) error {
 	var b bytes.Buffer
 
 	command := &JoinCommand{}
@@ -493,3 +491,14 @@ func Join(s *raft.Server, serverName string) error {
 	}
 	return fmt.Errorf("Unable to join: %v", err)
 }
+
+// register commands to raft server
+func registerCommands() {
+	raft.RegisterCommand(&JoinCommand{})
+	raft.RegisterCommand(&SetCommand{})
+	raft.RegisterCommand(&GetCommand{})
+	raft.RegisterCommand(&DeleteCommand{})
+	raft.RegisterCommand(&WatchCommand{})
+	raft.RegisterCommand(&ListCommand{})
+	raft.RegisterCommand(&TestAndSetCommand{})
+}

+ 95 - 0
raft_handlers.go

@@ -0,0 +1,95 @@
+package main
+
+import (
+	"net/http"
+	"strconv"
+	"encoding/json"
+	"github.com/coreos/go-raft"
+)
+
+//-------------------------------------------------------------
+// Handlers to handle raft related request via raft server port
+//-------------------------------------------------------------
+
+// Get all the current logs
+func GetLogHttpHandler(w http.ResponseWriter, req *http.Request) {
+	debug("[recv] GET http://%v/log", raftServer.Name())
+	w.Header().Set("Content-Type", "application/json")
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(raftServer.LogEntries())
+}
+
+
+// Response to vote request
+func VoteHttpHandler(w http.ResponseWriter, req *http.Request) {
+	rvreq := &raft.RequestVoteRequest{}
+	err := decodeJsonRequest(req, rvreq)
+	if err == nil {
+		debug("[recv] POST http://%v/vote [%s]", raftServer.Name(), rvreq.CandidateName)
+		if resp := raftServer.RequestVote(rvreq); resp != nil {
+			w.WriteHeader(http.StatusOK)
+			json.NewEncoder(w).Encode(resp)
+			return
+		}
+	}
+	warn("[vote] ERROR: %v", err)
+	w.WriteHeader(http.StatusInternalServerError)
+}
+
+// Response to append entries request
+func AppendEntriesHttpHandler(w http.ResponseWriter, req *http.Request) {
+	aereq := &raft.AppendEntriesRequest{}
+	err := decodeJsonRequest(req, aereq)
+
+	if err == nil {
+		debug("[recv] POST http://%s/log/append [%d]", raftServer.Name(), len(aereq.Entries))
+		if resp := raftServer.AppendEntries(aereq); resp != nil {
+			w.WriteHeader(http.StatusOK)
+			json.NewEncoder(w).Encode(resp)
+			if !resp.Success {
+				debug("[Append Entry] Step back")
+			}
+			return
+		}
+	}
+	warn("[Append Entry] ERROR: %v", err)
+	w.WriteHeader(http.StatusInternalServerError)
+}
+
+// Response to recover from snapshot request
+func SnapshotHttpHandler(w http.ResponseWriter, req *http.Request) {
+	aereq := &raft.SnapshotRequest{}
+	err := decodeJsonRequest(req, aereq)
+	if err == nil {
+		debug("[recv] POST http://%s/snapshot/ ", raftServer.Name())
+		if resp, _ := raftServer.SnapshotRecovery(aereq); resp != nil {
+			w.WriteHeader(http.StatusOK)
+			json.NewEncoder(w).Encode(resp)
+			return
+		}
+	}
+	warn("[Snapshot] ERROR: %v", err)
+	w.WriteHeader(http.StatusInternalServerError)
+}
+
+// Get the port that listening for client connecting of the server
+func ClientHttpHandler(w http.ResponseWriter, req *http.Request) {
+	debug("[recv] Get http://%v/client/ ", raftServer.Name())
+	w.WriteHeader(http.StatusOK)
+	client := address + ":" + strconv.Itoa(clientPort)
+	w.Write([]byte(client))
+}
+
+// Response to the join request
+func JoinHttpHandler(w http.ResponseWriter, req *http.Request) {
+
+	command := &JoinCommand{}
+
+	if err := decodeJsonRequest(req, command); err == nil {
+		debug("Receive Join Request from %s", command.Name)
+		dispatch(command, &w, req)
+	} else {
+		w.WriteHeader(http.StatusInternalServerError)
+		return
+	}
+}

+ 3 - 0
version.go

@@ -0,0 +1,3 @@
+package main
+
+var version = "v1"