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  }