func makeRequest(testCase plan.TestCase) ([]byte, error) { callURL, err := url.Parse(fmt.Sprintf("http://%v:8080/", testCase.Client)) if err != nil { return []byte(""), err } args := url.Values{} for k, v := range testCase.Arguments { args.Add(k, v) } callURL.RawQuery = args.Encode() ctx, _ := context.WithTimeout( context.Background(), testCase.Plan.Config.CallTimeout*time.Second) resp, err := ctxhttp.Get(ctx, nil, callURL.String()) if err != nil { return []byte(""), err } body, err := ioutil.ReadAll(resp.Body) if err != nil { return []byte(""), err } if resp.StatusCode != 200 { return []byte(""), fmt.Errorf("wanted status code 200, got %v", resp.StatusCode) } return body, nil }
// Lookup returns rectangles for the given address. Currently the only // implementation is the Google geocoding service. func Lookup(ctx context.Context, address string) ([]Rect, error) { mu.RLock() rects, ok := cache[address] mu.RUnlock() if ok { return rects, nil } rectsi, err := sf.Do(address, func() (interface{}, error) { // TODO: static data files from OpenStreetMap, Wikipedia, etc? urlStr := "https://maps.googleapis.com/maps/api/geocode/json?address=" + url.QueryEscape(address) + "&sensor=false" res, err := ctxhttp.Get(ctx, ctxutil.Client(ctx), urlStr) if err != nil { return nil, err } defer res.Body.Close() rects, err := decodeGoogleResponse(res.Body) log.Printf("Google geocode lookup (%q) = %#v, %v", address, rects, err) if err == nil { mu.Lock() cache[address] = rects mu.Unlock() } return rects, err }) if err != nil { return nil, err } return rectsi.([]Rect), nil }
// ContentWithContext is a helper to abstract the location concept (not thread safe) func (obj *Object) ContentWithContext(ctx context.Context) ([]byte, error) { if obj == nil { return nil, nil } if len(obj.Blob) > 0 || obj.Location == "" { return obj.Blob, nil } resp, err := ctxhttp.Get(ctx, nil, obj.Location) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, errors.New(resp.Status) } ct := resp.Header.Get("Content-Type") if ct != "" { obj.ContentType = ct } blob, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } obj.Blob = blob return blob, nil }
func (ds *Datasource) fetchIntervalState(ctx context.Context, interval string, n int) (State, error) { var url string if n != 0 { url = ds.baseIntervalURL(interval, n) + ".state.txt" } else { url = fmt.Sprintf("%s/replication/%s/state.txt", ds.baseURL(), interval) } resp, err := ctxhttp.Get(ctx, ds.client(), url) if err != nil { return State{}, err } defer resp.Body.Close() if resp.StatusCode != 200 { return State{}, fmt.Errorf("incorrect status code: %v", resp.StatusCode) } data, err := ioutil.ReadAll(resp.Body) if err != nil { return State{}, err } return decodeIntervalState(data, interval) }
// PluginGetURL fetches the content of a URL, which could be static or dynamic (passed in). // It only works with an api with a simple Text/Text signature. func PluginGetURL(static bool, uri string, model interface{}) Plugin { if static { if uri == "" { return nil } } return func(ctx context.Context, in interface{}) (out interface{}, err error) { inb, err := TextBytes(in) if err != nil { return nil, err } ins := string(inb) if static { ins = uri } resp, err := ctxhttp.Get(ctx, http.DefaultClient, ins) // handles context.Done() correctly if err != nil { return nil, err } byts, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err // unable to create a simple test case for this error } err = resp.Body.Close() if err != nil { return nil, err // unable to create a simple test case for this error } return TextConvert(byts, model) } }
// Login takes a Hover username and password and returns the login cookie value func Login(ctx context.Context, hc *http.Client, username, password string) (*http.Cookie, error) { v := make(url.Values) v.Set("username", username) v.Set("password", password) r, err := ctxhttp.Get(ctx, hc, fmt.Sprintf("%s/login?%s", defaultURL, v.Encode())) if err != nil { return nil, err } defer r.Body.Close() if r.StatusCode != 200 { return nil, InvalidLogin(fmt.Sprintf("login HTTP status code was %d", r.StatusCode)) } var c *http.Cookie for _, cook := range r.Cookies() { if cook.Name == "hoverauth" && cook.Value != "" { c = cook break } } if c == nil { return nil, InvalidLogin("unable to find 'hoverauth' cookie with data in response") } return c, nil }
// queryCAA sends the query request to the GPD API. If the return code is // dns.RcodeSuccess the 'Answer' section is parsed for CAA records, otherwise // an error is returned. Unlike bdns.DNSResolver.LookupCAA it will not repeat // failed queries if the context has not expired as we expect to be running // multiple queries in parallel and only need a M of N quorum (we also expect // GPD to have quite good availability) func (cdr *CAADistributedResolver) queryCAA(ctx context.Context, url string, ic *http.Client) ([]*dns.CAA, error) { apiResp, err := ctxhttp.Get(ctx, ic, url) if err != nil { return nil, err } defer func() { _ = apiResp.Body.Close() }() body, err := ioutil.ReadAll(&io.LimitedReader{R: apiResp.Body, N: 1024}) if err != nil { return nil, err } if apiResp.StatusCode != http.StatusOK { if string(body) != "" { return nil, fmt.Errorf("Unexpected HTTP status code %d, body: %s", apiResp.StatusCode, body) } return nil, fmt.Errorf("Unexpected HTTP status code %d", apiResp.StatusCode) } var respObj core.GPDNSResponse err = json.Unmarshal(body, &respObj) if err != nil { return nil, err } if respObj.Status != dns.RcodeSuccess { if respObj.Comment != "" { return nil, fmt.Errorf("Query failed with %s: %s", dns.RcodeToString[respObj.Status], respObj.Comment) } return nil, fmt.Errorf("Query failed wtih %s", dns.RcodeToString[respObj.Status]) } return parseAnswer(respObj.Answer) }
// FetchCert retrieves already issued certificate from the given url, in DER format. // It retries the request until the certificate is successfully retrieved, // context is cancelled by the caller or an error response is received. // // The returned value will also contain the CA (issuer) certificate if the bundle argument is true. // // FetchCert returns an error if the CA's response or chain was unreasonably large. // Callers are encouraged to parse the returned value to ensure the certificate is valid // and has expected features. func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]byte, error) { for { res, err := ctxhttp.Get(ctx, c.HTTPClient, url) if err != nil { return nil, err } defer res.Body.Close() if res.StatusCode == http.StatusOK { return responseCert(ctx, c.HTTPClient, res, bundle) } if res.StatusCode > 299 { return nil, responseError(res) } d, err := retryAfter(res.Header.Get("retry-after")) if err != nil { d = 3 * time.Second } select { case <-time.After(d): // retry case <-ctx.Done(): return nil, ctx.Err() } } }
func testOnGCE() bool { ctx, cancel := context.WithCancel(context.Background()) defer cancel() resc := make(chan bool, 2) // Try two strategies in parallel. // See https://github.com/GoogleCloudPlatform/gcloud-golang/issues/194 go func() { res, err := ctxhttp.Get(ctx, metaClient, "http://"+metadataIP) if err != nil { resc <- false return } defer res.Body.Close() resc <- res.Header.Get("Metadata-Flavor") == "Google" }() go func() { addrs, err := net.LookupHost("metadata.google.internal") if err != nil || len(addrs) == 0 { resc <- false return } resc <- strsContains(addrs, metadataIP) }() return <-resc }
func TestContextCancel(t *testing.T) { // server that doesn't reply before the timeout wg := sync.WaitGroup{} srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { time.Sleep(2 * time.Second) fmt.Fprint(w, r.URL.Path) wg.Done() })) defer srv.Close() tr := NewTransport(nil, RetryAll(RetryMaxRetries(1), RetryStatusInterval(500, 600)), ConstDelay(0)) c := &http.Client{ Transport: tr, } ctx, cancelFn := context.WithDeadline(context.Background(), time.Now().Add(time.Second)) defer cancelFn() wg.Add(1) res, err := ctxhttp.Get(ctx, c, srv.URL+"/test") require.Nil(t, res) assert.Equal(t, context.DeadlineExceeded, err) wg.Wait() }
// NewProvider uses the OpenID Connect discovery mechanism to construct a Provider. // // The issuer is the URL identifier for the service. For example: "https://accounts.google.com" // or "https://login.salesforce.com". func NewProvider(ctx context.Context, issuer string) (*Provider, error) { wellKnown := strings.TrimSuffix(issuer, "/") + "/.well-known/openid-configuration" resp, err := ctxhttp.Get(ctx, clientFromContext(ctx), wellKnown) if err != nil { return nil, err } body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("%s: %s", resp.Status, body) } defer resp.Body.Close() var p providerJSON if err := json.Unmarshal(body, &p); err != nil { return nil, fmt.Errorf("oidc: failed to decode provider discovery object: %v", err) } if p.Issuer != issuer { return nil, fmt.Errorf("oidc: issuer did not match the issuer returned by provider, expected %q got %q", issuer, p.Issuer) } return &Provider{ issuer: p.Issuer, authURL: p.AuthURL, tokenURL: p.TokenURL, userInfoURL: p.UserInfoURL, rawClaims: body, remoteKeySet: newRemoteKeySet(ctx, p.JWKSURL, time.Now), }, nil }
func TestContextCancelOnRetry(t *testing.T) { callCnt := int32(0) srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { cnt := atomic.AddInt32(&callCnt, 1) switch cnt { case 1: w.WriteHeader(500) default: time.Sleep(2 * time.Second) fmt.Fprint(w, r.URL.Path) } })) defer srv.Close() // cancel while waiting on retry response tr := NewTransport(nil, RetryAll(RetryMaxRetries(1), RetryStatusInterval(500, 600)), ConstDelay(0)) c := &http.Client{ Transport: tr, } ctx, cancelFn := context.WithDeadline(context.Background(), time.Now().Add(time.Second)) defer cancelFn() res, err := ctxhttp.Get(ctx, c, srv.URL+"/test") require.Nil(t, res) assert.Equal(t, context.DeadlineExceeded, err) assert.Equal(t, int32(2), atomic.LoadInt32(&callCnt)) // cancel while waiting on delay atomic.StoreInt32(&callCnt, 0) tr = NewTransport(nil, RetryAll(RetryMaxRetries(1), RetryStatusInterval(500, 600)), ConstDelay(2*time.Second)) c = &http.Client{ Transport: tr, } ctx, cancelFn = context.WithDeadline(context.Background(), time.Now().Add(time.Second)) defer cancelFn() res, err = ctxhttp.Get(ctx, c, srv.URL+"/test") require.Nil(t, res) assert.Equal(t, context.DeadlineExceeded, err) assert.Equal(t, int32(1), atomic.LoadInt32(&callCnt)) }
func NewTHttpClientWithCtx(urlstr string, ctx context.Context) (TTransport, error) { parsedURL, err := url.Parse(urlstr) if err != nil { return nil, err } response, err := ctxhttp.Get(ctx, nil, urlstr) if err != nil { return nil, err } return &THttpClient{response: response, url: parsedURL, ctx: ctx}, nil }
func (c *ArtifactStoreClient) getAPI(path string) (io.ReadCloser, *ArtifactsError) { url := c.server + path ctx, cancel := context.WithTimeout(c.ctx, c.timeout) defer cancel() if resp, err := ctxhttp.Get(ctx, nil, url); err != nil { return nil, NewRetriableError(err.Error()) } else { if resp.StatusCode != http.StatusOK { return nil, determineResponseError(resp, url, "POST") } return resp.Body, nil } }
// WaitAuthorization polls an authorization at the given URL // until it is in one of the final states, StatusValid or StatusInvalid, // or the context is done. // // It returns a non-nil Authorization only if its Status is StatusValid. // In all other cases WaitAuthorization returns an error. // If the Status is StatusInvalid, the returned error is ErrAuthorizationFailed. func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorization, error) { var count int sleep := func(v string, inc int) error { count += inc d := backoff(count, 10*time.Second) d = retryAfter(v, d) wakeup := time.NewTimer(d) defer wakeup.Stop() select { case <-ctx.Done(): return ctx.Err() case <-wakeup.C: return nil } } for { res, err := ctxhttp.Get(ctx, c.HTTPClient, url) if err != nil { return nil, err } retry := res.Header.Get("retry-after") if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted { res.Body.Close() if err := sleep(retry, 1); err != nil { return nil, err } continue } var raw wireAuthz err = json.NewDecoder(res.Body).Decode(&raw) res.Body.Close() if err != nil { if err := sleep(retry, 0); err != nil { return nil, err } continue } if raw.Status == StatusValid { return raw.authorization(url), nil } if raw.Status == StatusInvalid { return nil, ErrAuthorizationFailed } if err := sleep(retry, 0); err != nil { return nil, err } } }
func (f Fetcher) GetAll(ctx context.Context, urls []string) ([]*FetcherResponse, error) { defer metrics.MeasureSince([]string{"fn.FetchRemoteData"}, time.Now()) fetches := make([]*FetcherResponse, len(urls)) var wg sync.WaitGroup wg.Add(len(urls)) // TODO: add thruput here.. for i, urlStr := range urls { fetches[i] = &FetcherResponse{} go func(fetch *FetcherResponse) { defer wg.Done() url, err := urlx.Parse(urlStr) if err != nil { fetch.Err = err return } fetch.URL = url lg.Infof("Fetching %s", url.String()) resp, err := ctxhttp.Get(ctx, f.client(), url.String()) if err != nil { lg.Warnf("Error fetching %s because %s", url.String(), err) fetch.Err = err return } defer resp.Body.Close() fetch.Status = resp.StatusCode body, err := ioutil.ReadAll(resp.Body) if err != nil { fetch.Err = err return } fetch.Data = body fetch.Err = nil }(fetches[i]) } wg.Wait() return fetches, nil }
// GetChallenge retrieves the current status of an challenge. // // A client typically polls a challenge status using this method. func (c *Client) GetChallenge(ctx context.Context, url string) (*Challenge, error) { res, err := ctxhttp.Get(ctx, c.HTTPClient, url) if err != nil { return nil, err } defer res.Body.Close() if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted { return nil, responseError(res) } v := wireChallenge{URI: url} if err := json.NewDecoder(res.Body).Decode(&v); err != nil { return nil, fmt.Errorf("acme: invalid response: %v", err) } return v.challenge(), nil }
// GetAuthorization retrieves an authorization identified by the given URL. // // If a caller needs to poll an authorization until its status is final, // see the WaitAuthorization method. func (c *Client) GetAuthorization(ctx context.Context, url string) (*Authorization, error) { res, err := ctxhttp.Get(ctx, c.HTTPClient, url) if err != nil { return nil, err } defer res.Body.Close() if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted { return nil, responseError(res) } var v wireAuthz if err := json.NewDecoder(res.Body).Decode(&v); err != nil { return nil, fmt.Errorf("acme: invalid response: %v", err) } return v.authorization(url), nil }
func (h *HttpProbe) Probe(c ctx.Context) error { resp, err := http.Get(c, nil, h.url) if err != nil { return ErrNotReachable } else { go func() { defer resp.Body.Close() io.Copy(ioutil.Discard, resp.Body) }() if resp.StatusCode != 200 { return fmt.Errorf("%d: %s", resp.StatusCode, h.url) } else { return nil } } }
func handleAddBookmark(ctx context.Context, w http.ResponseWriter, r *http.Request) { var input struct { Url string `json:"url"` } if err := json.NewDecoder(r.Body).Decode(&input); err != nil { web.JSONErr(w, err.Error(), http.StatusBadRequest) return } resp, err := ctxhttp.Get(ctx, &crawler, input.Url) if err != nil { log.Error("cannot crawl", "url", input.Url, "error", err.Error()) web.StdJSONResp(w, http.StatusInternalServerError) return } defer resp.Body.Close() body := make([]byte, 1024*20) if n, err := resp.Body.Read(body); err != nil && err != io.EOF { log.Error("cannot read crawler response", "url", input.Url, "error", err.Error()) web.StdJSONResp(w, http.StatusInternalServerError) return } else { body = body[:n] } title := pageTitle(body) var b Bookmark err = pg.DB(ctx).Get(&b, ` INSERT INTO bookmarks (title, url, created) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING RETURNING * `, title, input.Url, time.Now()) if err != nil { log.Error("cannot create bookmark", "error", err.Error()) web.StdJSONResp(w, http.StatusInternalServerError) return } web.JSONResp(w, b, http.StatusCreated) }
// changesetReader will return a ReadCloser with the data from the changeset. // It will be gzip compressed, so the caller must decompress. // It is the caller's responsibility to call Close on the Reader when done. func (ds *Datasource) changesetReader(ctx context.Context, n ChangesetSeqNum) (io.ReadCloser, error) { url := ds.changesetURL(n) resp, err := ctxhttp.Get(ctx, ds.client(), url) if err != nil { return nil, err } if resp.StatusCode != 200 { resp.Body.Close() return nil, &UnexpectedStatusCodeError{ Code: resp.StatusCode, URL: url, } } return resp.Body, nil }
func unsealInstance(ctx context.Context, instance string) error { s := sealStatus{} r, err := ctxhttp.Get(ctx, http.DefaultClient, instance+"/v1/sys/seal-status") if err != nil { return fmt.Errorf("[%s] An error ocurred while reading seal-status: %s", instance, err) } defer r.Body.Close() if err := json.NewDecoder(r.Body).Decode(&s); err != nil { return fmt.Errorf("[%s] Unable to decode seal-status: %s", instance, err) } if s.Sealed { for _, token := range config.SealTokens { log.Printf("[%s] Vault instance is sealed (missing %d tokens), trying to unlock...", instance, s.T-s.Progress) body := bytes.NewBuffer([]byte{}) json.NewEncoder(body).Encode(map[string]interface{}{ "key": token, }) r, _ := http.NewRequest("PUT", instance+"/v1/sys/unseal", body) resp, err := ctxhttp.Do(ctx, http.DefaultClient, r) if err != nil { return fmt.Errorf("[%s] An error ocurred while doing unseal: %s", instance, err) } defer resp.Body.Close() if err := json.NewDecoder(resp.Body).Decode(&s); err != nil { return fmt.Errorf("[%s] Unable to decode seal-status: %s", instance, err) } if !s.Sealed { log.Printf("[%s] Unseal successfully finished.", instance) break } } if s.Sealed { log.Printf("[%s] Vault instance is still sealed (missing %d tokens), I don't have any more tokens.", instance, s.T-s.Progress) } } else { log.Printf("[%s] Vault instance is already unsealed.", instance) } return nil }
// Discover performs ACME server discovery using c.DirectoryURL. // // It caches successful result. So, subsequent calls will not result in // a network round-trip. This also means mutating c.DirectoryURL after successful call // of this method will have no effect. func (c *Client) Discover(ctx context.Context) (Directory, error) { c.dirMu.Lock() defer c.dirMu.Unlock() if c.dir != nil { return *c.dir, nil } dirURL := c.DirectoryURL if dirURL == "" { dirURL = LetsEncryptURL } res, err := ctxhttp.Get(ctx, c.HTTPClient, dirURL) if err != nil { return Directory{}, err } defer res.Body.Close() if res.StatusCode != http.StatusOK { return Directory{}, responseError(res) } var v struct { Reg string `json:"new-reg"` Authz string `json:"new-authz"` Cert string `json:"new-cert"` Revoke string `json:"revoke-cert"` Meta struct { Terms string `json:"terms-of-service"` Website string `json:"website"` CAA []string `json:"caa-identities"` } } if json.NewDecoder(res.Body).Decode(&v); err != nil { return Directory{}, err } c.dir = &Directory{ RegURL: v.Reg, AuthzURL: v.Authz, CertURL: v.Cert, RevokeURL: v.Revoke, Terms: v.Meta.Terms, Website: v.Meta.Website, CAA: v.Meta.CAA, } return *c.dir, nil }
func ListProxyReq() (output chan *Response) { var ( wg sync.WaitGroup root = ctx.Background() v = make(chan *Response, 1) ) go func() { defer close(v) for _, endpoint := range Endpoint { wg.Add(1) go func(ep string) { defer wg.Done() wk, abort := ctx.WithTimeout(root, DefaultTimeout) defer abort() resp, err := ctxhttp.Get(wk, nil, ep+"/proxy/list") if err != nil { v <- &Response{Host: ep, Err: err} return } defer resp.Body.Close() inn := new(bytes.Buffer) _, err = inn.ReadFrom(resp.Body) if err != nil { v <- &Response{Host: ep, Err: err} return } v <- &Response{Host: ep, Data: inn.Bytes()} }(endpoint) } wg.Wait() }() return v }
func (ds *Datasource) fetchIntervalData(ctx context.Context, url string) (*osm.Change, error) { resp, err := ctxhttp.Get(ctx, ds.client(), url) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != 200 { return nil, fmt.Errorf("incorrect status code: %v", resp.StatusCode) } gzReader, err := gzip.NewReader(resp.Body) if err != nil { return nil, err } defer gzReader.Close() change := &osm.Change{} err = xml.NewDecoder(gzReader).Decode(change) return change, err }
func (p *Plugin) get(path string, params url.Values, result interface{}) error { // Context here lets us either timeout req. or cancel it in Plugin.Close ctx, cancel := context.WithTimeout(p.context, pluginTimeout) defer cancel() resp, err := ctxhttp.Get(ctx, p.client, fmt.Sprintf("http://plugin%s?%s", path, params.Encode())) if err != nil { return err } if resp.StatusCode != http.StatusOK { return fmt.Errorf("plugin returned non-200 status code: %s", resp.Status) } defer resp.Body.Close() err = codec.NewDecoder(MaxBytesReader(resp.Body, maxResponseBytes, errResponseTooLarge), &codec.JsonHandle{}).Decode(&result) if err == errResponseTooLarge { return err } if err != nil { return fmt.Errorf("decoding error: %s", err) } return nil }
func (n *Ngrok) Start(subdomain, localAddr string) (string, error) { if n.process != nil { return "", errors.New("ngrok tunnel is already running") } if subdomain == "" { subdomain = utils.RandString(12) } p := n.cmd("-log", "stdout", "-authtoken", Test.NgrokToken, "-subdomain", subdomain, localAddr) if err := p.Start(); err != nil { return "", err } // wait for tunnel to get up tunnelHost := subdomain + ".ngrok.com" tunnelURL := "http://" + tunnelHost ctx, cancel := context.WithTimeout(context.Background(), n.StartTimeout) defer cancel() for { resp, err := ctxhttp.Get(ctx, nil, tunnelURL) if err == context.DeadlineExceeded { return "", fmt.Errorf("timed out after %s waiting for %s to be ready", n.StartTimeout, tunnelURL) } if err == nil { resp.Body.Close() // 404 means tunnel not found if resp.StatusCode != http.StatusNotFound { n.process = p return tunnelHost, nil } } time.Sleep(1 * time.Second) } }
func get(ctx context.Context, client *http.Client, url string) (gonzo.File, error) { resp, err := ctxhttp.Get(ctx, client, url) if err != nil { return nil, err } if resp.StatusCode < 200 || resp.StatusCode > 399 { return nil, fmt.Errorf("%s (%s)", resp.Status, url) } _, params, err := mime.ParseMediaType(resp.Header.Get("Content-Disposition")) name, ok := params["filename"] if !ok || err != nil { name = path.Base(url) } file := gonzo.NewFile(resp.Body, gonzo.NewFileInfo()) file.FileInfo().SetName(name) file.FileInfo().SetSize(resp.ContentLength) return file, nil }
// CurrentChangesetState returns the current state of the changeset replication. func (ds *Datasource) CurrentChangesetState(ctx context.Context) (ChangesetSeqNum, State, error) { url := ds.baseURL() + "/replication/changesets/state.yaml" resp, err := ctxhttp.Get(ctx, ds.client(), url) if err != nil { return 0, State{}, err } defer resp.Body.Close() if resp.StatusCode != 200 { return 0, State{}, &UnexpectedStatusCodeError{ Code: resp.StatusCode, URL: url, } } data, err := ioutil.ReadAll(resp.Body) if err != nil { return 0, State{}, err } s, err := decodeChangesetState(data) return ChangesetSeqNum(s.SeqNum), s, err }
// Ready returns true if the API is ready func (f *Forensiq) Ready(ctx context.Context) (bool, error) { var ( uri *url.URL b []byte err error sts = xstats.FromContext(ctx) ) { uri, err = url.Parse(f.Host) if err != nil { return false, err } uri.Path = "/ready" } { begin := time.Now() resp, err := ctxhttp.Get(ctx, f.httpClient, uri.String()) if err != nil { return false, err } defer resp.Body.Close() sts.Timing("forensiq.request_time", time.Since(begin), "request:ready", "status:"+responseStatus(ctx, resp.StatusCode), "status_code:"+strconv.Itoa(resp.StatusCode), ) b, err = ioutil.ReadAll(resp.Body) if err != nil { return false, err } } return string(b) == "1", nil }