// getVersion returns the Versions of the given member via its // peerURLs. Returns the last error if it fails to get the version. func getVersion(m *membership.Member, rt http.RoundTripper) (*version.Versions, error) { cc := &http.Client{ Transport: rt, } var ( err error resp *http.Response ) for _, u := range m.PeerURLs { resp, err = cc.Get(u + "/version") if err != nil { plog.Warningf("failed to reach the peerURL(%s) of member %s (%v)", u, m.ID, err) continue } // etcd 2.0 does not have version endpoint on peer url. if resp.StatusCode == http.StatusNotFound { httputil.GracefulClose(resp) return &version.Versions{ Server: "2.0.0", Cluster: "2.0.0", }, nil } var b []byte b, err = ioutil.ReadAll(resp.Body) resp.Body.Close() if err != nil { plog.Warningf("failed to read out the response body from the peerURL(%s) of member %s (%v)", u, m.ID, err) continue } var vers version.Versions if err = json.Unmarshal(b, &vers); err != nil { plog.Warningf("failed to unmarshal the response body got from the peerURL(%s) of member %s (%v)", u, m.ID, err) continue } return &vers, nil } return nil, err }
// post posts the given request. // It returns nil when request is sent out and processed successfully. func (s *snapshotSender) post(req *http.Request) (err error) { cancel := httputil.RequestCanceler(req) type responseAndError struct { resp *http.Response body []byte err error } result := make(chan responseAndError, 1) go func() { resp, err := s.tr.pipelineRt.RoundTrip(req) if err != nil { result <- responseAndError{resp, nil, err} return } // close the response body when timeouts. // prevents from reading the body forever when the other side dies right after // successfully receives the request body. time.AfterFunc(snapResponseReadTimeout, func() { httputil.GracefulClose(resp) }) body, err := ioutil.ReadAll(resp.Body) result <- responseAndError{resp, body, err} }() select { case <-s.stopc: cancel() return errStopped case r := <-result: if r.err != nil { return r.err } return checkPostResponse(r.resp, r.body, req, s.to) } }
func (cr *streamReader) dial(t streamType) (io.ReadCloser, error) { u := cr.picker.pick() uu := u uu.Path = path.Join(t.endpoint(), cr.local.String()) req, err := http.NewRequest("GET", uu.String(), nil) if err != nil { cr.picker.unreachable(u) return nil, fmt.Errorf("failed to make http request to %s (%v)", u, err) } req.Header.Set("X-Server-From", cr.local.String()) req.Header.Set("X-Server-Version", version.Version) req.Header.Set("X-Min-Cluster-Version", version.MinClusterVersion) req.Header.Set("X-Etcd-Cluster-ID", cr.cid.String()) req.Header.Set("X-Raft-To", cr.remote.String()) setPeerURLsHeader(req, cr.tr.URLs) cr.mu.Lock() select { case <-cr.stopc: cr.mu.Unlock() return nil, fmt.Errorf("stream reader is stopped") default: } cr.cancel = httputil.RequestCanceler(cr.tr.streamRt, req) cr.mu.Unlock() resp, err := cr.tr.streamRt.RoundTrip(req) if err != nil { cr.picker.unreachable(u) return nil, err } rv := serverVersion(resp.Header) lv := semver.Must(semver.NewVersion(version.Version)) if compareMajorMinorVersion(rv, lv) == -1 && !checkStreamSupport(rv, t) { httputil.GracefulClose(resp) cr.picker.unreachable(u) return nil, errUnsupportedStreamType } switch resp.StatusCode { case http.StatusGone: httputil.GracefulClose(resp) cr.picker.unreachable(u) err := fmt.Errorf("the member has been permanently removed from the cluster") select { case cr.errorc <- err: default: } return nil, err case http.StatusOK: return resp.Body, nil case http.StatusNotFound: httputil.GracefulClose(resp) cr.picker.unreachable(u) return nil, fmt.Errorf("remote member %s could not recognize local member", cr.remote) case http.StatusPreconditionFailed: b, err := ioutil.ReadAll(resp.Body) if err != nil { cr.picker.unreachable(u) return nil, err } httputil.GracefulClose(resp) cr.picker.unreachable(u) switch strings.TrimSuffix(string(b), "\n") { case errIncompatibleVersion.Error(): plog.Errorf("request sent was ignored by peer %s (server version incompatible)", cr.remote) return nil, errIncompatibleVersion case errClusterIDMismatch.Error(): plog.Errorf("request sent was ignored (cluster ID mismatch: remote[%s]=%s, local=%s)", cr.remote, resp.Header.Get("X-Etcd-Cluster-ID"), cr.cid) return nil, errClusterIDMismatch default: return nil, fmt.Errorf("unhandled error %q when precondition failed", string(b)) } default: httputil.GracefulClose(resp) cr.picker.unreachable(u) return nil, fmt.Errorf("unhandled http status %d", resp.StatusCode) } }
func readResponse(resp *http.Response) (b []byte, err error) { b, err = ioutil.ReadAll(resp.Body) httputil.GracefulClose(resp) return }