...

Source file src/go.etcd.io/etcd/server/v3/etcdserver/api/etcdhttp/base.go

Documentation: go.etcd.io/etcd/server/v3/etcdserver/api/etcdhttp

     1  // Copyright 2015 The etcd Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package etcdhttp
    16  
    17  import (
    18  	"encoding/json"
    19  	"expvar"
    20  	"fmt"
    21  	"net/http"
    22  
    23  	"go.etcd.io/etcd/api/v3/version"
    24  	"go.etcd.io/etcd/server/v3/etcdserver"
    25  	"go.etcd.io/etcd/server/v3/etcdserver/api"
    26  	"go.etcd.io/etcd/server/v3/etcdserver/api/v2error"
    27  	"go.etcd.io/etcd/server/v3/etcdserver/api/v2http/httptypes"
    28  	"go.uber.org/zap"
    29  )
    30  
    31  const (
    32  	configPath  = "/config"
    33  	varsPath    = "/debug/vars"
    34  	versionPath = "/version"
    35  )
    36  
    37  // HandleBasic adds handlers to a mux for serving JSON etcd client requests
    38  // that do not access the v2 store.
    39  func HandleBasic(lg *zap.Logger, mux *http.ServeMux, server etcdserver.ServerPeer) {
    40  	mux.HandleFunc(varsPath, serveVars)
    41  	mux.HandleFunc(versionPath, versionHandler(server.Cluster(), serveVersion))
    42  }
    43  
    44  func versionHandler(c api.Cluster, fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
    45  	return func(w http.ResponseWriter, r *http.Request) {
    46  		v := c.Version()
    47  		if v != nil {
    48  			fn(w, r, v.String())
    49  		} else {
    50  			fn(w, r, "not_decided")
    51  		}
    52  	}
    53  }
    54  
    55  func serveVersion(w http.ResponseWriter, r *http.Request, clusterV string) {
    56  	if !allowMethod(w, r, "GET") {
    57  		return
    58  	}
    59  	vs := version.Versions{
    60  		Server:  version.Version,
    61  		Cluster: clusterV,
    62  	}
    63  
    64  	w.Header().Set("Content-Type", "application/json")
    65  	b, err := json.Marshal(&vs)
    66  	if err != nil {
    67  		panic(fmt.Sprintf("cannot marshal versions to json (%v)", err))
    68  	}
    69  	w.Write(b)
    70  }
    71  
    72  func serveVars(w http.ResponseWriter, r *http.Request) {
    73  	if !allowMethod(w, r, "GET") {
    74  		return
    75  	}
    76  
    77  	w.Header().Set("Content-Type", "application/json; charset=utf-8")
    78  	fmt.Fprintf(w, "{\n")
    79  	first := true
    80  	expvar.Do(func(kv expvar.KeyValue) {
    81  		if !first {
    82  			fmt.Fprintf(w, ",\n")
    83  		}
    84  		first = false
    85  		fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
    86  	})
    87  	fmt.Fprintf(w, "\n}\n")
    88  }
    89  
    90  func allowMethod(w http.ResponseWriter, r *http.Request, m string) bool {
    91  	if m == r.Method {
    92  		return true
    93  	}
    94  	w.Header().Set("Allow", m)
    95  	http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
    96  	return false
    97  }
    98  
    99  // WriteError logs and writes the given Error to the ResponseWriter
   100  // If Error is an etcdErr, it is rendered to the ResponseWriter
   101  // Otherwise, it is assumed to be a StatusInternalServerError
   102  func WriteError(lg *zap.Logger, w http.ResponseWriter, r *http.Request, err error) {
   103  	if err == nil {
   104  		return
   105  	}
   106  	switch e := err.(type) {
   107  	case *v2error.Error:
   108  		e.WriteTo(w)
   109  
   110  	case *httptypes.HTTPError:
   111  		if et := e.WriteTo(w); et != nil {
   112  			if lg != nil {
   113  				lg.Debug(
   114  					"failed to write v2 HTTP error",
   115  					zap.String("remote-addr", r.RemoteAddr),
   116  					zap.String("internal-server-error", e.Error()),
   117  					zap.Error(et),
   118  				)
   119  			}
   120  		}
   121  
   122  	default:
   123  		switch err {
   124  		case etcdserver.ErrTimeoutDueToLeaderFail, etcdserver.ErrTimeoutDueToConnectionLost, etcdserver.ErrNotEnoughStartedMembers,
   125  			etcdserver.ErrUnhealthy:
   126  			if lg != nil {
   127  				lg.Warn(
   128  					"v2 response error",
   129  					zap.String("remote-addr", r.RemoteAddr),
   130  					zap.String("internal-server-error", err.Error()),
   131  				)
   132  			}
   133  
   134  		default:
   135  			if lg != nil {
   136  				lg.Warn(
   137  					"unexpected v2 response error",
   138  					zap.String("remote-addr", r.RemoteAddr),
   139  					zap.String("internal-server-error", err.Error()),
   140  				)
   141  			}
   142  		}
   143  
   144  		herr := httptypes.NewHTTPError(http.StatusInternalServerError, "Internal Server Error")
   145  		if et := herr.WriteTo(w); et != nil {
   146  			if lg != nil {
   147  				lg.Debug(
   148  					"failed to write v2 HTTP error",
   149  					zap.String("remote-addr", r.RemoteAddr),
   150  					zap.String("internal-server-error", err.Error()),
   151  					zap.Error(et),
   152  				)
   153  			}
   154  		}
   155  	}
   156  }
   157  

View as plain text