func Fetch( sfo *state.StateFetchOptions, efo ExtraFetchOptions, opts FetchOptions, ) (*ClientResponse, error) { for { var headers = map[string]string{} if !efo.NoExtraHeaders { headers["Content-Type"] = "application/json" } if len(efo.Headers) > 0 { for k, v := range efo.Headers { headers[k] = v } } if efo.Session != nil { sess, err := efo.Session.GetCurrentSession() if err != nil { return nil, err } headers["Authorization"] = fmt.Sprintf("Bearer %v", *sess.Token) } req, err := http.NewRequest(opts.Method, opts.URL, opts.Body) if err != nil { return nil, err } for k, v := range headers { req.Header.Set(k, v) } resp, err := FetchHttpClient.Do(req) if err != nil { return nil, err } if slices.Contains([]int{408, 502, 503, 504}, resp.StatusCode) { return nil, ErrServerMaintenance } retryAfterStr := resp.Header.Get("Retry-After") if retryAfterStr != "" { // NOTE: We use milliseconds here even though the API *currently* returns seconds // to make it easier to change in the future retryAfter, err := strconv.ParseFloat(retryAfterStr, 64) if err != nil { retryAfter = 3000 } else { retryAfter *= 1000 } if efo.OnRatelimit != nil { efo.OnRatelimit(opts, retryAfter, err, sfo, efo.Session) } // Wait for the time specified by the server if !efo.NoWait { time.Sleep(time.Duration(retryAfter) * time.Second) if _, err := opts.Body.Seek(0, io.SeekStart); err != nil { return nil, err } if efo.OnRatelimit != nil { efo.OnRatelimit(opts, 0, err, sfo, efo.Session) } continue } } return NewClientResponse(resp), nil } }