github.com/marineam/etcd@v0.1.2-0.20130821182615-9b7109b46686/etcd_handlers.go (about) 1 package main 2 3 import ( 4 "fmt" 5 etcdErr "github.com/coreos/etcd/error" 6 "github.com/coreos/etcd/store" 7 "github.com/coreos/go-raft" 8 "net/http" 9 "strconv" 10 "strings" 11 ) 12 13 //------------------------------------------------------------------- 14 // Handlers to handle etcd-store related request via etcd url 15 //------------------------------------------------------------------- 16 17 func NewEtcdMuxer() *http.ServeMux { 18 // external commands 19 etcdMux := http.NewServeMux() 20 etcdMux.Handle("/"+version+"/keys/", errorHandler(Multiplexer)) 21 etcdMux.Handle("/"+version+"/watch/", errorHandler(WatchHttpHandler)) 22 etcdMux.Handle("/"+version+"/leader", errorHandler(LeaderHttpHandler)) 23 etcdMux.Handle("/"+version+"/machines", errorHandler(MachinesHttpHandler)) 24 etcdMux.Handle("/"+version+"/stats", errorHandler(StatsHttpHandler)) 25 etcdMux.Handle("/version", errorHandler(VersionHttpHandler)) 26 etcdMux.HandleFunc("/test/", TestHttpHandler) 27 return etcdMux 28 } 29 30 type errorHandler func(http.ResponseWriter, *http.Request) error 31 32 func (fn errorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 33 if e := fn(w, r); e != nil { 34 if etcdErr, ok := e.(etcdErr.Error); ok { 35 debug("Return error: ", etcdErr.Error()) 36 etcdErr.Write(w) 37 } else { 38 http.Error(w, e.Error(), http.StatusInternalServerError) 39 } 40 } 41 } 42 43 // Multiplex GET/POST/DELETE request to corresponding handlers 44 func Multiplexer(w http.ResponseWriter, req *http.Request) error { 45 46 switch req.Method { 47 case "GET": 48 return GetHttpHandler(w, req) 49 case "POST": 50 return SetHttpHandler(w, req) 51 case "DELETE": 52 return DeleteHttpHandler(w, req) 53 default: 54 w.WriteHeader(http.StatusMethodNotAllowed) 55 return nil 56 } 57 } 58 59 //-------------------------------------- 60 // State sensitive handlers 61 // Set/Delete will dispatch to leader 62 //-------------------------------------- 63 64 // Set Command Handler 65 func SetHttpHandler(w http.ResponseWriter, req *http.Request) error { 66 key := req.URL.Path[len("/v1/keys/"):] 67 68 if store.CheckKeyword(key) { 69 return etcdErr.NewError(400, "Set") 70 } 71 72 debugf("[recv] POST %v/v1/keys/%s [%s]", e.url, key, req.RemoteAddr) 73 74 value := req.FormValue("value") 75 76 if len(value) == 0 { 77 return etcdErr.NewError(200, "Set") 78 } 79 80 prevValue := req.FormValue("prevValue") 81 82 strDuration := req.FormValue("ttl") 83 84 expireTime, err := durationToExpireTime(strDuration) 85 86 if err != nil { 87 return etcdErr.NewError(202, "Set") 88 } 89 90 if len(prevValue) != 0 { 91 command := &TestAndSetCommand{ 92 Key: key, 93 Value: value, 94 PrevValue: prevValue, 95 ExpireTime: expireTime, 96 } 97 98 return dispatch(command, w, req, true) 99 100 } else { 101 command := &SetCommand{ 102 Key: key, 103 Value: value, 104 ExpireTime: expireTime, 105 } 106 107 return dispatch(command, w, req, true) 108 } 109 } 110 111 // Delete Handler 112 func DeleteHttpHandler(w http.ResponseWriter, req *http.Request) error { 113 key := req.URL.Path[len("/v1/keys/"):] 114 115 debugf("[recv] DELETE %v/v1/keys/%s [%s]", e.url, key, req.RemoteAddr) 116 117 command := &DeleteCommand{ 118 Key: key, 119 } 120 121 return dispatch(command, w, req, true) 122 } 123 124 // Dispatch the command to leader 125 func dispatch(c Command, w http.ResponseWriter, req *http.Request, etcd bool) error { 126 127 if r.State() == raft.Leader { 128 if body, err := r.Do(c); err != nil { 129 return err 130 } else { 131 if body == nil { 132 return etcdErr.NewError(300, "Empty result from raft") 133 } else { 134 body, _ := body.([]byte) 135 w.WriteHeader(http.StatusOK) 136 w.Write(body) 137 return nil 138 } 139 } 140 141 } else { 142 leader := r.Leader() 143 // current no leader 144 if leader == "" { 145 return etcdErr.NewError(300, "") 146 } 147 148 // tell the client where is the leader 149 path := req.URL.Path 150 151 var url string 152 153 if etcd { 154 etcdAddr, _ := nameToEtcdURL(leader) 155 url = etcdAddr + path 156 } else { 157 raftAddr, _ := nameToRaftURL(leader) 158 url = raftAddr + path 159 } 160 161 debugf("Redirect to %s", url) 162 163 http.Redirect(w, req, url, http.StatusTemporaryRedirect) 164 return nil 165 } 166 return etcdErr.NewError(300, "") 167 } 168 169 //-------------------------------------- 170 // State non-sensitive handlers 171 // will not dispatch to leader 172 // TODO: add sensitive version for these 173 // command? 174 //-------------------------------------- 175 176 // Handler to return the current leader's raft address 177 func LeaderHttpHandler(w http.ResponseWriter, req *http.Request) error { 178 leader := r.Leader() 179 180 if leader != "" { 181 w.WriteHeader(http.StatusOK) 182 raftURL, _ := nameToRaftURL(leader) 183 w.Write([]byte(raftURL)) 184 return nil 185 } else { 186 return etcdErr.NewError(301, "") 187 } 188 } 189 190 // Handler to return all the known machines in the current cluster 191 func MachinesHttpHandler(w http.ResponseWriter, req *http.Request) error { 192 machines := getMachines(nameToEtcdURL) 193 194 w.WriteHeader(http.StatusOK) 195 w.Write([]byte(strings.Join(machines, ", "))) 196 return nil 197 } 198 199 // Handler to return the current version of etcd 200 func VersionHttpHandler(w http.ResponseWriter, req *http.Request) error { 201 w.WriteHeader(http.StatusOK) 202 fmt.Fprintf(w, "etcd %s", releaseVersion) 203 return nil 204 } 205 206 // Handler to return the basic stats of etcd 207 func StatsHttpHandler(w http.ResponseWriter, req *http.Request) error { 208 w.WriteHeader(http.StatusOK) 209 w.Write(etcdStore.Stats()) 210 return nil 211 } 212 213 // Get Handler 214 func GetHttpHandler(w http.ResponseWriter, req *http.Request) error { 215 key := req.URL.Path[len("/v1/keys/"):] 216 217 debugf("[recv] GET %s/v1/keys/%s [%s]", e.url, key, req.RemoteAddr) 218 219 command := &GetCommand{ 220 Key: key, 221 } 222 223 if body, err := command.Apply(r.Server); err != nil { 224 return err 225 } else { 226 body, _ := body.([]byte) 227 w.WriteHeader(http.StatusOK) 228 w.Write(body) 229 230 return nil 231 } 232 233 } 234 235 // Watch handler 236 func WatchHttpHandler(w http.ResponseWriter, req *http.Request) error { 237 key := req.URL.Path[len("/v1/watch/"):] 238 239 command := &WatchCommand{ 240 Key: key, 241 } 242 243 if req.Method == "GET" { 244 debugf("[recv] GET %s/watch/%s [%s]", e.url, key, req.RemoteAddr) 245 command.SinceIndex = 0 246 247 } else if req.Method == "POST" { 248 // watch from a specific index 249 250 debugf("[recv] POST %s/watch/%s [%s]", e.url, key, req.RemoteAddr) 251 content := req.FormValue("index") 252 253 sinceIndex, err := strconv.ParseUint(string(content), 10, 64) 254 if err != nil { 255 return etcdErr.NewError(203, "Watch From Index") 256 } 257 command.SinceIndex = sinceIndex 258 259 } else { 260 w.WriteHeader(http.StatusMethodNotAllowed) 261 return nil 262 } 263 264 if body, err := command.Apply(r.Server); err != nil { 265 return etcdErr.NewError(500, key) 266 } else { 267 w.WriteHeader(http.StatusOK) 268 269 body, _ := body.([]byte) 270 w.Write(body) 271 return nil 272 } 273 274 } 275 276 // TestHandler 277 func TestHttpHandler(w http.ResponseWriter, req *http.Request) { 278 testType := req.URL.Path[len("/test/"):] 279 280 if testType == "speed" { 281 directSet() 282 w.WriteHeader(http.StatusOK) 283 w.Write([]byte("speed test success")) 284 return 285 } 286 287 w.WriteHeader(http.StatusBadRequest) 288 }