github.com/ghodss/etcd@v0.3.1-0.20140417172404-cc329bfa55cb/server/v2/get_handler.go (about) 1 package v2 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "net/url" 8 "strconv" 9 10 etcdErr "github.com/coreos/etcd/error" 11 "github.com/coreos/etcd/log" 12 "github.com/coreos/etcd/third_party/github.com/goraft/raft" 13 "github.com/coreos/etcd/third_party/github.com/gorilla/mux" 14 ) 15 16 func GetHandler(w http.ResponseWriter, req *http.Request, s Server) error { 17 vars := mux.Vars(req) 18 key := "/" + vars["key"] 19 20 // Help client to redirect the request to the current leader 21 if req.FormValue("consistent") == "true" && s.State() != raft.Leader { 22 leader := s.Leader() 23 hostname, _ := s.ClientURL(leader) 24 25 url, err := url.Parse(hostname) 26 if err != nil { 27 log.Warn("Redirect cannot parse hostName ", hostname) 28 return err 29 } 30 url.RawQuery = req.URL.RawQuery 31 url.Path = req.URL.Path 32 33 log.Debugf("Redirect consistent get to %s", url.String()) 34 http.Redirect(w, req, url.String(), http.StatusTemporaryRedirect) 35 return nil 36 } 37 38 recursive := (req.FormValue("recursive") == "true") 39 sort := (req.FormValue("sorted") == "true") 40 waitIndex := req.FormValue("waitIndex") 41 stream := (req.FormValue("stream") == "true") 42 43 if req.FormValue("wait") == "true" { 44 return handleWatch(key, recursive, stream, waitIndex, w, req, s) 45 } 46 47 return handleGet(key, recursive, sort, w, req, s) 48 } 49 50 func handleWatch(key string, recursive, stream bool, waitIndex string, w http.ResponseWriter, req *http.Request, s Server) error { 51 // Create a command to watch from a given index (default 0). 52 var sinceIndex uint64 = 0 53 var err error 54 55 if waitIndex != "" { 56 sinceIndex, err = strconv.ParseUint(waitIndex, 10, 64) 57 if err != nil { 58 return etcdErr.NewError(etcdErr.EcodeIndexNaN, "Watch From Index", s.Store().Index()) 59 } 60 } 61 62 watcher, err := s.Store().Watch(key, recursive, stream, sinceIndex) 63 if err != nil { 64 return err 65 } 66 67 cn, _ := w.(http.CloseNotifier) 68 closeChan := cn.CloseNotify() 69 70 writeHeaders(w, s) 71 72 if stream { 73 // watcher hub will not help to remove stream watcher 74 // so we need to remove here 75 defer watcher.Remove() 76 for { 77 select { 78 case <-closeChan: 79 return nil 80 case event, ok := <-watcher.EventChan: 81 if !ok { 82 // If the channel is closed this may be an indication of 83 // that notifications are much more than we are able to 84 // send to the client in time. Then we simply end streaming. 85 return nil 86 } 87 if req.Method == "HEAD" { 88 continue 89 } 90 91 b, _ := json.Marshal(event) 92 _, err := w.Write(b) 93 if err != nil { 94 return nil 95 } 96 w.(http.Flusher).Flush() 97 } 98 } 99 } 100 101 select { 102 case <-closeChan: 103 watcher.Remove() 104 case event := <-watcher.EventChan: 105 if req.Method == "HEAD" { 106 return nil 107 } 108 b, _ := json.Marshal(event) 109 w.Write(b) 110 } 111 return nil 112 } 113 114 func handleGet(key string, recursive, sort bool, w http.ResponseWriter, req *http.Request, s Server) error { 115 event, err := s.Store().Get(key, recursive, sort) 116 if err != nil { 117 return err 118 } 119 120 if req.Method == "HEAD" { 121 return nil 122 } 123 124 writeHeaders(w, s) 125 b, _ := json.Marshal(event) 126 w.Write(b) 127 return nil 128 } 129 130 func writeHeaders(w http.ResponseWriter, s Server) { 131 w.Header().Set("Content-Type", "application/json") 132 w.Header().Add("X-Etcd-Index", fmt.Sprint(s.Store().Index())) 133 w.Header().Add("X-Raft-Index", fmt.Sprint(s.CommitIndex())) 134 w.Header().Add("X-Raft-Term", fmt.Sprint(s.Term())) 135 if url, ok := s.ClientURL(s.Leader()); ok { 136 w.Header().Set("X-Leader-Client-URL", url) 137 } 138 if url, ok := s.PeerURL(s.Leader()); ok { 139 w.Header().Set("X-Leader-Peer-URL", url) 140 } 141 w.WriteHeader(http.StatusOK) 142 }