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