Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue with Purging Cache for API Responses in cloudflare-go library #1350

Closed
2 tasks done
shinngohamatani opened this issue Aug 2, 2023 · 2 comments · Fixed by #1360
Closed
2 tasks done

Issue with Purging Cache for API Responses in cloudflare-go library #1350

shinngohamatani opened this issue Aug 2, 2023 · 2 comments · Fixed by #1360
Milestone

Comments

@shinngohamatani
Copy link

shinngohamatani commented Aug 2, 2023

Confirmation

  • My issue isn't already found on the issue tracker.
  • I have replicated my issue using the latest version of the library and it is still present.

cloudflare-go version

v0.73.0

Go environment

❯ go env                                       
GO111MODULE="on"
GOARCH="amd64"

Expected output

I'm utilizing the Cloudflare v4API, specifically the Zone > Purge Cached Content API. I believe there's a files entry in the POST body during this operation. In this context, the files don't represent static files, but APIs whose responses are application/json.

Let's take an API endpoint like /api/v1/users for example. Suppose we only want to fetch users updated after a specific time, we might use a URL such as /api/v1/users?size=20&updated_at=2023-08-02T19%3A30%3A00%2B09%3A00. While this might not seem typical for an API, suppose we want to purge the cache for this /api/v1/users?size=20&updated_at=2023-08-02T19%3A30%3A00%2B09%3A00 endpoint.

In this case, we make a POST request to /client/v4/sonze/{identifier}/purge_cache, and I assume that the body should look something like this:

{
    "files": [
        "https://cdn.example.com/api/v1/users?size=20&updated_at=2023-08-02T19%3A30%3A00%2B09%3A00"
    ]
}

In Go, it can be described as follows:

	api, _ := cloudflare.NewWithAPIToken("***")
	_, _ = api.PurgeCacheContext(context.Background(), "***", cloudflare.PurgeCacheRequest{
		Files: []string{"https://cdn.example.com/api/v1/users?size=20&updated_at=2023-08-02T19%3A30%3A00%2B09%3A00"},
	})

However, when I actually try to execute this, json.Marshal seems to be performed, which causes the '&' to be escaped, preventing the cache from being purged successfully.

{
    "files": [
        "https://cdn.example.com/api/v1/users?size=20\u0026updated_at=2023-08-02T19%3A30%3A00%2B09%3A00"
    ]
}

I have a feeling that a modification to the library may be needed, but there's also the possibility that I'm using it incorrectly. If that's the case, could you please enlighten me with the correct usage in detail?

Actual output

When trying to purge the cache using the Cloudflare Go library, I've been encountering an issue. When trying to purge cache for an API endpoint with URL parameters, the '&' character in the URL is being escaped when converted into JSON. This escaping is preventing the cache from being successfully purged.

Code demonstrating the issue

	api, _ := cloudflare.NewWithAPIToken("***")
	_, _ = api.PurgeCacheContext(context.Background(), "***", cloudflare.PurgeCacheRequest{
		Files: []string{"https://cdn.example.com/api/v1/users?size=20&updated_at=2023-08-02T19%3A30%3A00%2B09%3A00"},
	})

Steps to reproduce

above

References

No response

@jacobbednarz
Copy link
Member

thanks for the thorough issue!

i'm able to confirm this is a doing of stdlib handling of HTML characters in Marshal method - https://github.com/golang/go/blob/master/src/encoding/json/encode.go#L162. example: https://go.dev/play/p/sYrB0pXsduD

unfortunately, the only option is to side step the Marshal interface completely and implement your own custom method for handling the encoding via Encode. in our case, something like this would work.

+func (t *PurgeCacheRequest) JSON() ([]byte, error) {
+       buffer := &bytes.Buffer{}
+       encoder := json.NewEncoder(buffer)
+       encoder.SetEscapeHTML(false)
+       err := encoder.Encode(t)
+       return buffer.Bytes(), err
+}
+
 // PurgeCacheRequest represents the request format made to the purge endpoint.
 type PurgeCacheRequest struct {
        Everything bool `json:"purge_everything,omitempty"`
@@ -672,7 +681,8 @@ func (api *API) PurgeCache(ctx context.Context, zoneID string, pcr PurgeCacheReq
 // API reference: https://api.cloudflare.com/#zone-purge-individual-files-by-url-and-cache-tags
 func (api *API) PurgeCacheContext(ctx context.Context, zoneID string, pcr PurgeCacheRequest) (PurgeCacheResponse, error) {
        uri := fmt.Sprintf("/zones/%s/purge_cache", zoneID)
-       res, err := api.makeRequestContext(ctx, http.MethodPost, uri, pcr)
+       payload, _ := pcr.JSON()
+       res, err := api.makeRequestContext(ctx, http.MethodPost, uri, payload)
        if err != nil {
                return PurgeCacheResponse{}, err
        }

it's not a great solution though because we would have to do this on every instance where we may expect HTML characters to be in JSON payloads.

i might have a look at if we have any comparable JSON libraries that don't implement this and we can swap it out globally.

jacobbednarz added a commit that referenced this issue Aug 7, 2023
Ensures the exact cache key will match that which we have stored.

Closes #1350
jacobbednarz added a commit that referenced this issue Aug 7, 2023
Ensures the exact cache key will match that which we have stored.

Closes #1350
jacobbednarz added a commit that referenced this issue Aug 7, 2023
Ensures the exact cache key will match that which we have stored.

Closes #1350
@github-actions github-actions bot added this to the v0.75.0 milestone Aug 7, 2023
@github-actions
Copy link
Contributor

This functionality has been released in v0.75.0.

For further feature requests or bug reports with this functionality, please create a new GitHub issue following the template. Thank you!

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 15, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants