From 26fe2843e8a1201f2dc0d2ec3ce0edc8726d353f Mon Sep 17 00:00:00 2001 From: Diogo Sousa Date: Wed, 5 Apr 2023 17:21:20 +0100 Subject: [PATCH] Added support for `http_response_compress` phase and `compress_response` action. --- .changelog/1261.txt | 7 +++ rulesets.go | 105 ++++++++++++++++++++++++-------------------- rulesets_test.go | 80 +++++++++++++++++++++++++++++++++ 3 files changed, 145 insertions(+), 47 deletions(-) create mode 100644 .changelog/1261.txt diff --git a/.changelog/1261.txt b/.changelog/1261.txt new file mode 100644 index 00000000000..efa0a01bad2 --- /dev/null +++ b/.changelog/1261.txt @@ -0,0 +1,7 @@ +```release-note:enhancement +rulesets: add support for the `http_response_compression` phase +``` + +```release-note:enhancement +rulesets: add support for the `compress_response` action +``` diff --git a/rulesets.go b/rulesets.go index 29aa2dfd608..8110d0206a1 100644 --- a/rulesets.go +++ b/rulesets.go @@ -35,6 +35,7 @@ const ( RulesetPhaseHTTPResponseFirewallManaged RulesetPhase = "http_response_firewall_managed" RulesetPhaseHTTPResponseHeadersTransform RulesetPhase = "http_response_headers_transform" RulesetPhaseHTTPResponseHeadersTransformManaged RulesetPhase = "http_response_headers_transform_managed" + RulesetPhaseHTTPResponseCompression RulesetPhase = "http_response_compression" RulesetPhaseMagicTransit RulesetPhase = "magic_transit" RulesetPhaseRateLimit RulesetPhase = "http_ratelimit" RulesetPhaseSuperBotFightMode RulesetPhase = "http_request_sbfm" @@ -58,6 +59,7 @@ const ( RulesetRuleActionSetConfig RulesetRuleAction = "set_config" RulesetRuleActionServeError RulesetRuleAction = "serve_error" RulesetRuleActionSkip RulesetRuleAction = "skip" + RulesetRuleActionCompressResponse RulesetRuleAction = "compress_response" RulesetActionParameterProductBIC RulesetActionParameterProduct = "bic" RulesetActionParameterProductHOT RulesetActionParameterProduct = "hot" @@ -106,6 +108,7 @@ func RulesetPhaseValues() []string { string(RulesetPhaseHTTPResponseFirewallManaged), string(RulesetPhaseHTTPResponseHeadersTransform), string(RulesetPhaseHTTPResponseHeadersTransformManaged), + string(RulesetPhaseHTTPResponseCompression), string(RulesetPhaseMagicTransit), string(RulesetPhaseRateLimit), string(RulesetPhaseSuperBotFightMode), @@ -135,6 +138,7 @@ func RulesetRuleActionValues() []string { string(RulesetRuleActionSetConfig), string(RulesetRuleActionServeError), string(RulesetRuleActionSkip), + string(RulesetRuleActionCompressResponse), } } @@ -205,53 +209,54 @@ type RulesetActionParametersLogCustomField struct { // RulesetRuleActionParameters specifies the action parameters for a Ruleset // rule. type RulesetRuleActionParameters struct { - ID string `json:"id,omitempty"` - Ruleset string `json:"ruleset,omitempty"` - Rulesets []string `json:"rulesets,omitempty"` - Rules map[string][]string `json:"rules,omitempty"` - Increment int `json:"increment,omitempty"` - URI *RulesetRuleActionParametersURI `json:"uri,omitempty"` - Headers map[string]RulesetRuleActionParametersHTTPHeader `json:"headers,omitempty"` - Products []string `json:"products,omitempty"` - Phases []string `json:"phases,omitempty"` - Overrides *RulesetRuleActionParametersOverrides `json:"overrides,omitempty"` - MatchedData *RulesetRuleActionParametersMatchedData `json:"matched_data,omitempty"` - Version *string `json:"version,omitempty"` - Response *RulesetRuleActionParametersBlockResponse `json:"response,omitempty"` - HostHeader string `json:"host_header,omitempty"` - Origin *RulesetRuleActionParametersOrigin `json:"origin,omitempty"` - SNI *RulesetRuleActionParametersSni `json:"sni,omitempty"` - RequestFields []RulesetActionParametersLogCustomField `json:"request_fields,omitempty"` - ResponseFields []RulesetActionParametersLogCustomField `json:"response_fields,omitempty"` - CookieFields []RulesetActionParametersLogCustomField `json:"cookie_fields,omitempty"` - Cache *bool `json:"cache,omitempty"` - EdgeTTL *RulesetRuleActionParametersEdgeTTL `json:"edge_ttl,omitempty"` - BrowserTTL *RulesetRuleActionParametersBrowserTTL `json:"browser_ttl,omitempty"` - ServeStale *RulesetRuleActionParametersServeStale `json:"serve_stale,omitempty"` - Content string `json:"content,omitempty"` - ContentType string `json:"content_type,omitempty"` - StatusCode uint16 `json:"status_code,omitempty"` - RespectStrongETags *bool `json:"respect_strong_etags,omitempty"` - CacheKey *RulesetRuleActionParametersCacheKey `json:"cache_key,omitempty"` - OriginErrorPagePassthru *bool `json:"origin_error_page_passthru,omitempty"` - FromList *RulesetRuleActionParametersFromList `json:"from_list,omitempty"` - FromValue *RulesetRuleActionParametersFromValue `json:"from_value,omitempty"` - AutomaticHTTPSRewrites *bool `json:"automatic_https_rewrites,omitempty"` - AutoMinify *RulesetRuleActionParametersAutoMinify `json:"autominify,omitempty"` - BrowserIntegrityCheck *bool `json:"bic,omitempty"` - DisableApps *bool `json:"disable_apps,omitempty"` - DisableZaraz *bool `json:"disable_zaraz,omitempty"` - DisableRailgun *bool `json:"disable_railgun,omitempty"` - EmailObfuscation *bool `json:"email_obfuscation,omitempty"` - Mirage *bool `json:"mirage,omitempty"` - OpportunisticEncryption *bool `json:"opportunistic_encryption,omitempty"` - Polish *Polish `json:"polish,omitempty"` - RocketLoader *bool `json:"rocket_loader,omitempty"` - SecurityLevel *SecurityLevel `json:"security_level,omitempty"` - ServerSideExcludes *bool `json:"server_side_excludes,omitempty"` - SSL *SSL `json:"ssl,omitempty"` - SXG *bool `json:"sxg,omitempty"` - HotLinkProtection *bool `json:"hotlink_protection,omitempty"` + ID string `json:"id,omitempty"` + Ruleset string `json:"ruleset,omitempty"` + Rulesets []string `json:"rulesets,omitempty"` + Rules map[string][]string `json:"rules,omitempty"` + Increment int `json:"increment,omitempty"` + URI *RulesetRuleActionParametersURI `json:"uri,omitempty"` + Headers map[string]RulesetRuleActionParametersHTTPHeader `json:"headers,omitempty"` + Products []string `json:"products,omitempty"` + Phases []string `json:"phases,omitempty"` + Overrides *RulesetRuleActionParametersOverrides `json:"overrides,omitempty"` + MatchedData *RulesetRuleActionParametersMatchedData `json:"matched_data,omitempty"` + Version *string `json:"version,omitempty"` + Response *RulesetRuleActionParametersBlockResponse `json:"response,omitempty"` + HostHeader string `json:"host_header,omitempty"` + Origin *RulesetRuleActionParametersOrigin `json:"origin,omitempty"` + SNI *RulesetRuleActionParametersSni `json:"sni,omitempty"` + RequestFields []RulesetActionParametersLogCustomField `json:"request_fields,omitempty"` + ResponseFields []RulesetActionParametersLogCustomField `json:"response_fields,omitempty"` + CookieFields []RulesetActionParametersLogCustomField `json:"cookie_fields,omitempty"` + Cache *bool `json:"cache,omitempty"` + EdgeTTL *RulesetRuleActionParametersEdgeTTL `json:"edge_ttl,omitempty"` + BrowserTTL *RulesetRuleActionParametersBrowserTTL `json:"browser_ttl,omitempty"` + ServeStale *RulesetRuleActionParametersServeStale `json:"serve_stale,omitempty"` + Content string `json:"content,omitempty"` + ContentType string `json:"content_type,omitempty"` + StatusCode uint16 `json:"status_code,omitempty"` + RespectStrongETags *bool `json:"respect_strong_etags,omitempty"` + CacheKey *RulesetRuleActionParametersCacheKey `json:"cache_key,omitempty"` + OriginErrorPagePassthru *bool `json:"origin_error_page_passthru,omitempty"` + FromList *RulesetRuleActionParametersFromList `json:"from_list,omitempty"` + FromValue *RulesetRuleActionParametersFromValue `json:"from_value,omitempty"` + AutomaticHTTPSRewrites *bool `json:"automatic_https_rewrites,omitempty"` + AutoMinify *RulesetRuleActionParametersAutoMinify `json:"autominify,omitempty"` + BrowserIntegrityCheck *bool `json:"bic,omitempty"` + DisableApps *bool `json:"disable_apps,omitempty"` + DisableZaraz *bool `json:"disable_zaraz,omitempty"` + DisableRailgun *bool `json:"disable_railgun,omitempty"` + EmailObfuscation *bool `json:"email_obfuscation,omitempty"` + Mirage *bool `json:"mirage,omitempty"` + OpportunisticEncryption *bool `json:"opportunistic_encryption,omitempty"` + Polish *Polish `json:"polish,omitempty"` + RocketLoader *bool `json:"rocket_loader,omitempty"` + SecurityLevel *SecurityLevel `json:"security_level,omitempty"` + ServerSideExcludes *bool `json:"server_side_excludes,omitempty"` + SSL *SSL `json:"ssl,omitempty"` + SXG *bool `json:"sxg,omitempty"` + HotLinkProtection *bool `json:"hotlink_protection,omitempty"` + Algorithms []RulesetRuleActionParametersCompressionAlgorithm `json:"algorithms,omitempty"` } // RulesetRuleActionParametersFromList holds the FromList struct for @@ -451,6 +456,12 @@ type RulesetRuleActionParametersSni struct { Value string `json:"value"` } +// RulesetRuleActionParametersCompressionAlgorithm defines a compression +// algorithm for the compress_response action. +type RulesetRuleActionParametersCompressionAlgorithm struct { + Name string `json:"name"` +} + type Polish int const ( diff --git a/rulesets_test.go b/rulesets_test.go index 32a0774c728..c126c723948 100644 --- a/rulesets_test.go +++ b/rulesets_test.go @@ -554,6 +554,86 @@ func TestGetRuleset_RedirectFromValue(t *testing.T) { } } +func TestGetRuleset_CompressResponse(t *testing.T) { + setup() + defer teardown() + + handler := func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method) + w.Header().Set("content-type", "application/json") + fmt.Fprint(w, `{ + "result": { + "id": "70339d97bdb34195bbf054b1ebe81f76", + "name": "Cloudflare compress response ruleset", + "description": "This ruleset provides response compression rules", + "kind": "zone", + "version": "1", + "rules": [ + { + "id": "78723a9e0c7c4c6dbec5684cb766231d", + "version": "1", + "action": "compress_response", + "action_parameters": { + "algorithms": [ { "name": "brotli" }, { "name": "default" } ] + }, + "description": "Compress response rule", + "last_updated": "2020-12-18T09:28:09.655749Z", + "ref": "272936dc447b41fe976255ff6b768ec0", + "enabled": true + } + ], + "last_updated": "2020-12-18T09:28:09.655749Z", + "phase": "http_response_compression" + }, + "success": true, + "errors": [], + "messages": [] + }`) + } + + mux.HandleFunc("/accounts/"+testAccountID+"/rulesets/b232b534beea4e00a21dcbb7a8a545e9", handler) + mux.HandleFunc("/zones/"+testZoneID+"/rulesets/b232b534beea4e00a21dcbb7a8a545e9", handler) + + lastUpdated, _ := time.Parse(time.RFC3339, "2020-12-18T09:28:09.655749Z") + + rules := []RulesetRule{{ + ID: "78723a9e0c7c4c6dbec5684cb766231d", + Version: StringPtr("1"), + Action: string(RulesetRuleActionCompressResponse), + ActionParameters: &RulesetRuleActionParameters{ + Algorithms: []RulesetRuleActionParametersCompressionAlgorithm{ + {Name: "brotli"}, + {Name: "default"}, + }, + }, + Description: "Compress response rule", + LastUpdated: &lastUpdated, + Ref: "272936dc447b41fe976255ff6b768ec0", + Enabled: BoolPtr(true), + }} + + want := Ruleset{ + ID: "70339d97bdb34195bbf054b1ebe81f76", + Name: "Cloudflare compress response ruleset", + Description: "This ruleset provides response compression rules", + Kind: string(RulesetKindZone), + Version: StringPtr("1"), + LastUpdated: &lastUpdated, + Phase: string(RulesetPhaseHTTPResponseCompression), + Rules: rules, + } + + zoneActual, err := client.GetZoneRuleset(context.Background(), testZoneID, "b232b534beea4e00a21dcbb7a8a545e9") + if assert.NoError(t, err) { + assert.Equal(t, want, zoneActual) + } + + accountActual, err := client.GetAccountRuleset(context.Background(), testAccountID, "b232b534beea4e00a21dcbb7a8a545e9") + if assert.NoError(t, err) { + assert.Equal(t, want, accountActual) + } +} + func TestCreateRuleset(t *testing.T) { setup() defer teardown()