// 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
}