Browse Source

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

Xiang Li 12 years ago
parent
commit
a3173bfd31
4 changed files with 245 additions and 197 deletions
  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"