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