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  }