// Decompress and read gzipped responses
//
// NOTE: httpbin.org only sends gzip encoded data if the client indicates it
// accepts that compression method.
package main
import (
"bytes"
"context"
"fmt"
"io"
"github.com/fastly/compute-sdk-go/fsthttp"
)
// BackendName is the name of our service backend.
const Backend = "origin_0"
func main() {
fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) {
// Automatically decompress Gzip data if returned by the origin.
// NOTE: The `Content-Encoding` response header will be deleted.
r.DecompressResponseOptions.Gzip = true
// Send the request to the named backend.
resp, err := r.Send(ctx, Backend)
if err != nil {
w.WriteHeader(fsthttp.StatusBadGateway)
fmt.Fprintln(w, err)
return
}
// Modify the response body.
rd, err := transformBody(resp.Body)
if err != nil {
w.WriteHeader(fsthttp.StatusBadGateway)
fmt.Fprintln(w, err)
return
}
// Tell Fastly to compress the response if client accepts compression.
// X-Compress-Hint causes Fastly to set Content-Encoding.
// https://developer.fastly.com/reference/http/http-headers/X-Compress-Hint/
//
// NOTE: The local testing server (https://github.com/fastly/viceroy)
// currently does not implement X-Compress-Hint.
resp.Header.Add("X-Compress-Hint", "on")
// Write response to client.
w.Header().Reset(resp.Header)
w.WriteHeader(resp.StatusCode)
io.Copy(w, rd)
})
}
// transformBody partially replaces content within the backend response body.
func transformBody(body io.Reader) (io.Reader, error) {
// NOTE: It's possible to create a streaming replacer that doesn't require
// the entire body to be read and decompressed. Working with streams is the
// preferred method, but for the sake of keeping this example simple we've
// opted to read the entire body into memory.
data, err := io.ReadAll(body)
if err != nil {
return nil, err
}
find := []byte("headers")
replace := []byte("received_headers")
return bytes.NewReader(
bytes.ReplaceAll(data, find, replace),
), nil
}