mirror of
https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake.git
synced 2025-10-13 11:11:30 -04:00
The remote address for AMP cache rendezvous is always geolocated to the AMP cache server address. For more accurate metrics on where this rendezvous method is used and working, we can pull the remote address directly from the client SDP sent in the poll request.
83 lines
2.7 KiB
Go
83 lines
2.7 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/amp"
|
|
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/messages"
|
|
)
|
|
|
|
// ampClientOffers is the AMP-speaking endpoint for client poll messages,
|
|
// intended for access via an AMP cache. In contrast to the other clientOffers,
|
|
// the client's encoded poll message is stored in the URL path rather than the
|
|
// HTTP request body (because an AMP cache does not support POST), and the
|
|
// encoded client poll response is sent back as AMP-armored HTML.
|
|
func ampClientOffers(i *IPC, w http.ResponseWriter, r *http.Request) {
|
|
ctx, cancel := context.WithTimeout(r.Context(), ClientTimeout*time.Second)
|
|
defer cancel()
|
|
|
|
// The encoded client poll message immediately follows the /amp/client/
|
|
// path prefix, so this function unfortunately needs to be aware of and
|
|
// remove its own routing prefix.
|
|
path := strings.TrimPrefix(r.URL.Path, "/amp/client/")
|
|
if path == r.URL.Path {
|
|
// The path didn't start with the expected prefix. This probably
|
|
// indicates an internal bug.
|
|
log.Println("ampClientOffers: unexpected prefix in path")
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
var encPollReq []byte
|
|
var response []byte
|
|
var err error
|
|
|
|
encPollReq, err = amp.DecodePath(path)
|
|
if err == nil {
|
|
arg := messages.Arg{
|
|
Body: encPollReq,
|
|
RemoteAddr: "",
|
|
RendezvousMethod: messages.RendezvousAmpCache,
|
|
Context: ctx,
|
|
}
|
|
err = i.ClientOffers(arg, &response)
|
|
} else {
|
|
response, err = (&messages.ClientPollResponse{
|
|
Error: "cannot decode URL path",
|
|
}).EncodePollResponse()
|
|
}
|
|
|
|
if err != nil {
|
|
// We couldn't even construct a JSON object containing an error
|
|
// message :( Nothing to do but signal an error at the HTTP
|
|
// layer. The AMP cache will translate this 500 status into a
|
|
// 404 status.
|
|
// https://amp.dev/documentation/guides-and-tutorials/learn/amp-caches-and-cors/amp-cache-urls/#redirect-%26-error-handling
|
|
log.Printf("ampClientOffers: %v", err)
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "text/html")
|
|
// Attempt to hint to an AMP cache not to waste resources caching this
|
|
// document. "The Google AMP Cache considers any document fresh for at
|
|
// least 15 seconds."
|
|
// https://developers.google.com/amp/cache/overview#google-amp-cache-updates
|
|
w.Header().Set("Cache-Control", "max-age=15")
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
enc, err := amp.NewArmorEncoder(w)
|
|
if err != nil {
|
|
log.Printf("amp.NewArmorEncoder: %v", err)
|
|
return
|
|
}
|
|
defer enc.Close()
|
|
|
|
if _, err := enc.Write(response); err != nil {
|
|
log.Printf("ampClientOffers: unable to write answer: %v", err)
|
|
}
|
|
}
|