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  }