196 lines
6.2 KiB
Go
196 lines
6.2 KiB
Go
|
package cloudflare
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"net/url"
|
||
|
"strconv"
|
||
|
|
||
|
"github.com/pkg/errors"
|
||
|
)
|
||
|
|
||
|
// RateLimit is a policy than can be applied to limit traffic within a customer domain
|
||
|
type RateLimit struct {
|
||
|
ID string `json:"id,omitempty"`
|
||
|
Disabled bool `json:"disabled,omitempty"`
|
||
|
Description string `json:"description,omitempty"`
|
||
|
Match RateLimitTrafficMatcher `json:"match"`
|
||
|
Bypass []RateLimitKeyValue `json:"bypass,omitempty"`
|
||
|
Threshold int `json:"threshold"`
|
||
|
Period int `json:"period"`
|
||
|
Action RateLimitAction `json:"action"`
|
||
|
}
|
||
|
|
||
|
// RateLimitTrafficMatcher contains the rules that will be used to apply a rate limit to traffic
|
||
|
type RateLimitTrafficMatcher struct {
|
||
|
Request RateLimitRequestMatcher `json:"request"`
|
||
|
Response RateLimitResponseMatcher `json:"response"`
|
||
|
}
|
||
|
|
||
|
// RateLimitRequestMatcher contains the matching rules pertaining to requests
|
||
|
type RateLimitRequestMatcher struct {
|
||
|
Methods []string `json:"methods,omitempty"`
|
||
|
Schemes []string `json:"schemes,omitempty"`
|
||
|
URLPattern string `json:"url,omitempty"`
|
||
|
}
|
||
|
|
||
|
// RateLimitResponseMatcher contains the matching rules pertaining to responses
|
||
|
type RateLimitResponseMatcher struct {
|
||
|
Statuses []int `json:"status,omitempty"`
|
||
|
OriginTraffic *bool `json:"origin_traffic,omitempty"` // api defaults to true so we need an explicit empty value
|
||
|
}
|
||
|
|
||
|
// RateLimitKeyValue is k-v formatted as expected in the rate limit description
|
||
|
type RateLimitKeyValue struct {
|
||
|
Name string `json:"name"`
|
||
|
Value string `json:"value"`
|
||
|
}
|
||
|
|
||
|
// RateLimitAction is the action that will be taken when the rate limit threshold is reached
|
||
|
type RateLimitAction struct {
|
||
|
Mode string `json:"mode"`
|
||
|
Timeout int `json:"timeout"`
|
||
|
Response *RateLimitActionResponse `json:"response"`
|
||
|
}
|
||
|
|
||
|
// RateLimitActionResponse is the response that will be returned when rate limit action is triggered
|
||
|
type RateLimitActionResponse struct {
|
||
|
ContentType string `json:"content_type"`
|
||
|
Body string `json:"body"`
|
||
|
}
|
||
|
|
||
|
type rateLimitResponse struct {
|
||
|
Response
|
||
|
Result RateLimit `json:"result"`
|
||
|
}
|
||
|
|
||
|
type rateLimitListResponse struct {
|
||
|
Response
|
||
|
Result []RateLimit `json:"result"`
|
||
|
ResultInfo ResultInfo `json:"result_info"`
|
||
|
}
|
||
|
|
||
|
// CreateRateLimit creates a new rate limit for a zone.
|
||
|
//
|
||
|
// API reference: https://api.cloudflare.com/#rate-limits-for-a-zone-create-a-ratelimit
|
||
|
func (api *API) CreateRateLimit(zoneID string, limit RateLimit) (RateLimit, error) {
|
||
|
uri := "/zones/" + zoneID + "/rate_limits"
|
||
|
res, err := api.makeRequest("POST", uri, limit)
|
||
|
if err != nil {
|
||
|
return RateLimit{}, errors.Wrap(err, errMakeRequestError)
|
||
|
}
|
||
|
var r rateLimitResponse
|
||
|
if err := json.Unmarshal(res, &r); err != nil {
|
||
|
return RateLimit{}, errors.Wrap(err, errUnmarshalError)
|
||
|
}
|
||
|
return r.Result, nil
|
||
|
}
|
||
|
|
||
|
// ListRateLimits returns Rate Limits for a zone, paginated according to the provided options
|
||
|
//
|
||
|
// API reference: https://api.cloudflare.com/#rate-limits-for-a-zone-list-rate-limits
|
||
|
func (api *API) ListRateLimits(zoneID string, pageOpts PaginationOptions) ([]RateLimit, ResultInfo, error) {
|
||
|
v := url.Values{}
|
||
|
if pageOpts.PerPage > 0 {
|
||
|
v.Set("per_page", strconv.Itoa(pageOpts.PerPage))
|
||
|
}
|
||
|
if pageOpts.Page > 0 {
|
||
|
v.Set("page", strconv.Itoa(pageOpts.Page))
|
||
|
}
|
||
|
|
||
|
uri := "/zones/" + zoneID + "/rate_limits"
|
||
|
if len(v) > 0 {
|
||
|
uri = uri + "?" + v.Encode()
|
||
|
}
|
||
|
|
||
|
res, err := api.makeRequest("GET", uri, nil)
|
||
|
if err != nil {
|
||
|
return []RateLimit{}, ResultInfo{}, errors.Wrap(err, errMakeRequestError)
|
||
|
}
|
||
|
|
||
|
var r rateLimitListResponse
|
||
|
err = json.Unmarshal(res, &r)
|
||
|
if err != nil {
|
||
|
return []RateLimit{}, ResultInfo{}, errors.Wrap(err, errUnmarshalError)
|
||
|
}
|
||
|
return r.Result, r.ResultInfo, nil
|
||
|
}
|
||
|
|
||
|
// ListAllRateLimits returns all Rate Limits for a zone.
|
||
|
//
|
||
|
// API reference: https://api.cloudflare.com/#rate-limits-for-a-zone-list-rate-limits
|
||
|
func (api *API) ListAllRateLimits(zoneID string) ([]RateLimit, error) {
|
||
|
pageOpts := PaginationOptions{
|
||
|
PerPage: 100, // this is the max page size allowed
|
||
|
Page: 1,
|
||
|
}
|
||
|
|
||
|
allRateLimits := make([]RateLimit, 0)
|
||
|
for {
|
||
|
rateLimits, resultInfo, err := api.ListRateLimits(zoneID, pageOpts)
|
||
|
if err != nil {
|
||
|
return []RateLimit{}, err
|
||
|
}
|
||
|
allRateLimits = append(allRateLimits, rateLimits...)
|
||
|
// total pages is not returned on this call
|
||
|
// if number of records is less than the max, this must be the last page
|
||
|
// in case TotalCount % PerPage = 0, the last request will return an empty list
|
||
|
if resultInfo.Count < resultInfo.PerPage {
|
||
|
break
|
||
|
}
|
||
|
// continue with the next page
|
||
|
pageOpts.Page = pageOpts.Page + 1
|
||
|
}
|
||
|
|
||
|
return allRateLimits, nil
|
||
|
}
|
||
|
|
||
|
// RateLimit fetches detail about one Rate Limit for a zone.
|
||
|
//
|
||
|
// API reference: https://api.cloudflare.com/#rate-limits-for-a-zone-rate-limit-details
|
||
|
func (api *API) RateLimit(zoneID, limitID string) (RateLimit, error) {
|
||
|
uri := "/zones/" + zoneID + "/rate_limits/" + limitID
|
||
|
res, err := api.makeRequest("GET", uri, nil)
|
||
|
if err != nil {
|
||
|
return RateLimit{}, errors.Wrap(err, errMakeRequestError)
|
||
|
}
|
||
|
var r rateLimitResponse
|
||
|
err = json.Unmarshal(res, &r)
|
||
|
if err != nil {
|
||
|
return RateLimit{}, errors.Wrap(err, errUnmarshalError)
|
||
|
}
|
||
|
return r.Result, nil
|
||
|
}
|
||
|
|
||
|
// UpdateRateLimit lets you replace a Rate Limit for a zone.
|
||
|
//
|
||
|
// API reference: https://api.cloudflare.com/#rate-limits-for-a-zone-update-rate-limit
|
||
|
func (api *API) UpdateRateLimit(zoneID, limitID string, limit RateLimit) (RateLimit, error) {
|
||
|
uri := "/zones/" + zoneID + "/rate_limits/" + limitID
|
||
|
res, err := api.makeRequest("PUT", uri, limit)
|
||
|
if err != nil {
|
||
|
return RateLimit{}, errors.Wrap(err, errMakeRequestError)
|
||
|
}
|
||
|
var r rateLimitResponse
|
||
|
if err := json.Unmarshal(res, &r); err != nil {
|
||
|
return RateLimit{}, errors.Wrap(err, errUnmarshalError)
|
||
|
}
|
||
|
return r.Result, nil
|
||
|
}
|
||
|
|
||
|
// DeleteRateLimit deletes a Rate Limit for a zone.
|
||
|
//
|
||
|
// API reference: https://api.cloudflare.com/#rate-limits-for-a-zone-delete-rate-limit
|
||
|
func (api *API) DeleteRateLimit(zoneID, limitID string) error {
|
||
|
uri := "/zones/" + zoneID + "/rate_limits/" + limitID
|
||
|
res, err := api.makeRequest("DELETE", uri, nil)
|
||
|
if err != nil {
|
||
|
return errors.Wrap(err, errMakeRequestError)
|
||
|
}
|
||
|
var r rateLimitResponse
|
||
|
err = json.Unmarshal(res, &r)
|
||
|
if err != nil {
|
||
|
return errors.Wrap(err, errUnmarshalError)
|
||
|
}
|
||
|
return nil
|
||
|
}
|