github.com/soulinfo/etcd@v0.1.2-0.20130922053317-3a0a8c89e859/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  		// tell the client where is the leader
   171  		path := req.URL.Path
   172  
   173  		var url string
   174  
   175  		if etcd {
   176  			etcdAddr, _ := nameToEtcdURL(leader)
   177  			url = etcdAddr + path
   178  		} else {
   179  			raftAddr, _ := nameToRaftURL(leader)
   180  			url = raftAddr + path
   181  		}
   182  
   183  		debugf("Redirect to %s", url)
   184  
   185  		http.Redirect(w, req, url, http.StatusTemporaryRedirect)
   186  		return nil
   187  	}
   188  	return etcdErr.NewError(300, "")
   189  }
   190  
   191  //--------------------------------------
   192  // State non-sensitive handlers
   193  // will not dispatch to leader
   194  // TODO: add sensitive version for these
   195  // command?
   196  //--------------------------------------
   197  
   198  // Handler to return the current leader's raft address
   199  func LeaderHttpHandler(w http.ResponseWriter, req *http.Request) error {
   200  	leader := r.Leader()
   201  
   202  	if leader != "" {
   203  		w.WriteHeader(http.StatusOK)
   204  		raftURL, _ := nameToRaftURL(leader)
   205  		w.Write([]byte(raftURL))
   206  		return nil
   207  	} else {
   208  		return etcdErr.NewError(301, "")
   209  	}
   210  }
   211  
   212  // Handler to return all the known machines in the current cluster
   213  func MachinesHttpHandler(w http.ResponseWriter, req *http.Request) error {
   214  	machines := getMachines(nameToEtcdURL)
   215  
   216  	w.WriteHeader(http.StatusOK)
   217  	w.Write([]byte(strings.Join(machines, ", ")))
   218  	return nil
   219  }
   220  
   221  // Handler to return the current version of etcd
   222  func VersionHttpHandler(w http.ResponseWriter, req *http.Request) error {
   223  	w.WriteHeader(http.StatusOK)
   224  	fmt.Fprintf(w, "etcd %s", releaseVersion)
   225  	return nil
   226  }
   227  
   228  // Handler to return the basic stats of etcd
   229  func StatsHttpHandler(w http.ResponseWriter, req *http.Request) error {
   230  	w.WriteHeader(http.StatusOK)
   231  	w.Write(etcdStore.Stats())
   232  	w.Write(r.Stats())
   233  	return nil
   234  }
   235  
   236  // Get Handler
   237  func GetHttpHandler(w http.ResponseWriter, req *http.Request) error {
   238  	key := req.URL.Path[len("/v1/keys/"):]
   239  
   240  	debugf("[recv] GET %s/v1/keys/%s [%s]", e.url, key, req.RemoteAddr)
   241  
   242  	command := &GetCommand{
   243  		Key: key,
   244  	}
   245  
   246  	if body, err := command.Apply(r.Server); err != nil {
   247  		return err
   248  	} else {
   249  		body, _ := body.([]byte)
   250  		w.WriteHeader(http.StatusOK)
   251  		w.Write(body)
   252  
   253  		return nil
   254  	}
   255  
   256  }
   257  
   258  // Watch handler
   259  func WatchHttpHandler(w http.ResponseWriter, req *http.Request) error {
   260  	key := req.URL.Path[len("/v1/watch/"):]
   261  
   262  	command := &WatchCommand{
   263  		Key: key,
   264  	}
   265  
   266  	if req.Method == "GET" {
   267  		debugf("[recv] GET %s/watch/%s [%s]", e.url, key, req.RemoteAddr)
   268  		command.SinceIndex = 0
   269  
   270  	} else if req.Method == "POST" {
   271  		// watch from a specific index
   272  
   273  		debugf("[recv] POST %s/watch/%s [%s]", e.url, key, req.RemoteAddr)
   274  		content := req.FormValue("index")
   275  
   276  		sinceIndex, err := strconv.ParseUint(string(content), 10, 64)
   277  		if err != nil {
   278  			return etcdErr.NewError(203, "Watch From Index")
   279  		}
   280  		command.SinceIndex = sinceIndex
   281  
   282  	} else {
   283  		w.WriteHeader(http.StatusMethodNotAllowed)
   284  		return nil
   285  	}
   286  
   287  	if body, err := command.Apply(r.Server); err != nil {
   288  		return etcdErr.NewError(500, key)
   289  	} else {
   290  		w.WriteHeader(http.StatusOK)
   291  
   292  		body, _ := body.([]byte)
   293  		w.Write(body)
   294  		return nil
   295  	}
   296  
   297  }
   298  
   299  // TestHandler
   300  func TestHttpHandler(w http.ResponseWriter, req *http.Request) {
   301  	testType := req.URL.Path[len("/test/"):]
   302  
   303  	if testType == "speed" {
   304  		directSet()
   305  		w.WriteHeader(http.StatusOK)
   306  		w.Write([]byte("speed test success"))
   307  		return
   308  	}
   309  
   310  	w.WriteHeader(http.StatusBadRequest)
   311  }