From ffa7c9fdf5901d45aafdd96ebb7b72f30374b421 Mon Sep 17 00:00:00 2001 From: Philip Gough Date: Thu, 21 Feb 2019 09:00:26 +0000 Subject: [PATCH] client: Expose rate limit extensions in response --- client/auth_rep.go | 2 +- client/authz.go | 4 ++-- client/client.go | 35 +++++++++++++++++++++++++++++++++-- client/report.go | 2 +- client/types.go | 10 ++++++---- 5 files changed, 43 insertions(+), 10 deletions(-) diff --git a/client/auth_rep.go b/client/auth_rep.go index 2eb9a05..1f07bf2 100644 --- a/client/auth_rep.go +++ b/client/auth_rep.go @@ -51,7 +51,7 @@ func (client *ThreeScaleClient) authRep(values url.Values, extensions map[string } req.URL.RawQuery = values.Encode() - resp, err = client.doHttpReq(req) + resp, err = client.doHttpReq(req, extensions) if err != nil { return resp, fmt.Errorf("error calling 3Scale API - %s", err.Error()) } diff --git a/client/authz.go b/client/authz.go index d2aa016..ac802c4 100644 --- a/client/authz.go +++ b/client/authz.go @@ -23,7 +23,7 @@ func (client *ThreeScaleClient) Authorize(appId string, serviceToken string, ser values.Add("service_id", serviceId) req.URL.RawQuery = values.Encode() - authRepRes, err := client.doHttpReq(req) + authRepRes, err := client.doHttpReq(req, extensions) if err != nil { return authRepResp, fmt.Errorf("error calling 3Scale API - %s", err.Error()) } @@ -45,7 +45,7 @@ func (client *ThreeScaleClient) AuthorizeKey(userKey string, serviceToken string values.Add("service_id", serviceId) req.URL.RawQuery = values.Encode() - resp, err = client.doHttpReq(req) + resp, err = client.doHttpReq(req, extensions) if err != nil { return resp, fmt.Errorf("error calling 3Scale API - %s", err.Error()) } diff --git a/client/client.go b/client/client.go index fa947c1..703b1de 100644 --- a/client/client.go +++ b/client/client.go @@ -14,6 +14,7 @@ import ( const ( defaultBackendUrl = "https://su1.3scale.net:443" queryTag = "query" + limitExtensions = "limit_headers" ) var httpReqError = errors.New("error building http request") @@ -52,7 +53,7 @@ func NewThreeScale(backEnd *Backend, httpClient *http.Client) *ThreeScaleClient } // GetPeer - a utility method that returns the remote hostname of the client -func (client *ThreeScaleClient) GetPeer() string { +func (client *ThreeScaleClient) GetPeer() string { return client.backend.host } @@ -93,7 +94,7 @@ func encodeExtensions(extensions map[string]string) string { } // Call 3scale backend with the provided HTTP request -func (client *ThreeScaleClient) doHttpReq(req *http.Request) (ApiResponse, error) { +func (client *ThreeScaleClient) doHttpReq(req *http.Request, ext map[string]string) (ApiResponse, error) { var authRepRes ApiResponse resp, err := client.httpClient.Do(req) @@ -108,10 +109,40 @@ func (client *ThreeScaleClient) doHttpReq(req *http.Request) (ApiResponse, error if err != nil { return authRepRes, err } + + if ext != nil { + if _, ok := ext[limitExtensions]; ok { + if limitRem := resp.Header.Get("3scale-limit-remaining"); limitRem != "" { + remainingLimit, _ := strconv.Atoi(limitRem) + authRepRes.limitRemaining = &remainingLimit + } + + if limReset := resp.Header.Get("3scale-limit-reset"); limReset != "" { + resetLimit, _ := strconv.Atoi(limReset) + authRepRes.limitReset = &resetLimit + } + } + } + authRepRes.StatusCode = resp.StatusCode return authRepRes, nil } +// GetLimitRemaining - An integer stating the amount of hits left for the full combination of metrics authorized in this call +// before the rate limiting logic would start denying authorizations for the current period. +// A negative integer value means there is no limit in the amount of hits. +// Nil value will indicate the extension has not been used. +func (r ApiResponse) GetLimitRemaining() *int { + return r.limitRemaining +} + +// GetLimitReset - An integer stating the amount of seconds left for the current limiting period to elapse. +// A negative integer value means there is no limit in time. +// Nil value will indicate the extension has not been used. +func (r ApiResponse) GetLimitReset() *int { + return r.limitReset +} + // Add a metric to list of metrics to be reported // Returns error if provided value is non-positive and entry will be ignored func (m Metrics) Add(name string, value int) error { diff --git a/client/report.go b/client/report.go index 1052782..f79c5e4 100644 --- a/client/report.go +++ b/client/report.go @@ -44,7 +44,7 @@ func (client *ThreeScaleClient) report(values url.Values, extensions map[string] } req.URL.RawQuery = values.Encode() - resp, err = client.doHttpReq(req) + resp, err = client.doHttpReq(req, extensions) if err != nil { return resp, fmt.Errorf("error calling 3Scale API - %s", err.Error()) } diff --git a/client/types.go b/client/types.go index d499868..2c74826 100644 --- a/client/types.go +++ b/client/types.go @@ -12,9 +12,11 @@ const providerKey = "provider_key" // ApiResponse - formatted response to client type ApiResponse struct { - Reason string - Success bool - StatusCode int + Reason string + Success bool + StatusCode int + limitRemaining *int + limitReset *int } // ApiResponseXML - response from backend API @@ -80,7 +82,7 @@ type TokenAuth struct { Value string } -func (auth *TokenAuth) SetURLValues(values *url.Values) (error) { +func (auth *TokenAuth) SetURLValues(values *url.Values) error { switch auth.Type { case serviceToken: