...

Source file src/github.com/launchdarkly/go-server-sdk/v6/internal/datasource/requestor.go

Documentation: github.com/launchdarkly/go-server-sdk/v6/internal/datasource

     1  package datasource
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"net/http"
     7  
     8  	"github.com/launchdarkly/go-sdk-common/v3/ldlog"
     9  	"github.com/launchdarkly/go-server-sdk/v6/internal/endpoints"
    10  	"github.com/launchdarkly/go-server-sdk/v6/subsystems"
    11  	"github.com/launchdarkly/go-server-sdk/v6/subsystems/ldstoretypes"
    12  
    13  	"github.com/launchdarkly/go-jsonstream/v3/jreader"
    14  
    15  	"github.com/gregjones/httpcache"
    16  	"golang.org/x/exp/maps"
    17  )
    18  
    19  // requestor is the interface implemented by requestorImpl, used for testing purposes
    20  type requestor interface {
    21  	requestAll() (data []ldstoretypes.Collection, cached bool, err error)
    22  }
    23  
    24  // requestorImpl is the internal implementation of getting flag/segment data from the LD polling endpoints.
    25  type requestorImpl struct {
    26  	httpClient *http.Client
    27  	baseURI    string
    28  	headers    http.Header
    29  	loggers    ldlog.Loggers
    30  }
    31  
    32  type malformedJSONError struct {
    33  	innerError error
    34  }
    35  
    36  func (e malformedJSONError) Error() string {
    37  	return e.innerError.Error()
    38  }
    39  
    40  func newRequestorImpl(
    41  	context subsystems.ClientContext,
    42  	httpClient *http.Client,
    43  	baseURI string,
    44  ) requestor {
    45  	if httpClient == nil {
    46  		httpClient = context.GetHTTP().CreateHTTPClient()
    47  	}
    48  
    49  	modifiedClient := *httpClient
    50  	modifiedClient.Transport = &httpcache.Transport{
    51  		Cache:               httpcache.NewMemoryCache(),
    52  		MarkCachedResponses: true,
    53  		Transport:           httpClient.Transport,
    54  	}
    55  
    56  	return &requestorImpl{
    57  		httpClient: &modifiedClient,
    58  		baseURI:    baseURI,
    59  		headers:    context.GetHTTP().DefaultHeaders,
    60  		loggers:    context.GetLogging().Loggers,
    61  	}
    62  }
    63  
    64  func (r *requestorImpl) requestAll() ([]ldstoretypes.Collection, bool, error) {
    65  	if r.loggers.IsDebugEnabled() {
    66  		r.loggers.Debug("Polling LaunchDarkly for feature flag updates")
    67  	}
    68  
    69  	body, cached, err := r.makeRequest(endpoints.PollingRequestPath)
    70  	if err != nil {
    71  		return nil, false, err
    72  	}
    73  	if cached {
    74  		return nil, true, nil
    75  	}
    76  
    77  	reader := jreader.NewReader(body)
    78  	data := parseAllStoreDataFromJSONReader(&reader)
    79  	if err := reader.Error(); err != nil {
    80  		return nil, false, malformedJSONError{err}
    81  	}
    82  	return data, cached, nil
    83  }
    84  
    85  func (r *requestorImpl) makeRequest(resource string) ([]byte, bool, error) {
    86  	req, reqErr := http.NewRequest("GET", endpoints.AddPath(r.baseURI, resource), nil)
    87  	if reqErr != nil {
    88  		reqErr = fmt.Errorf(
    89  			"unable to create a poll request; this is not a network problem, most likely a bad base URI: %w",
    90  			reqErr,
    91  		)
    92  		return nil, false, reqErr
    93  	}
    94  	url := req.URL.String()
    95  	if r.headers != nil {
    96  		req.Header = maps.Clone(r.headers)
    97  	}
    98  
    99  	res, resErr := r.httpClient.Do(req)
   100  
   101  	if resErr != nil {
   102  		return nil, false, resErr
   103  	}
   104  
   105  	defer func() {
   106  		_, _ = io.ReadAll(res.Body)
   107  		_ = res.Body.Close()
   108  	}()
   109  
   110  	if err := checkForHTTPError(res.StatusCode, url); err != nil {
   111  		return nil, false, err
   112  	}
   113  
   114  	cached := res.Header.Get(httpcache.XFromCache) != ""
   115  
   116  	body, ioErr := io.ReadAll(res.Body)
   117  
   118  	if ioErr != nil {
   119  		return nil, false, ioErr // COVERAGE: there is no way to simulate this condition in unit tests
   120  	}
   121  	return body, cached, nil
   122  }
   123  

View as plain text