mirror of
https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake.git
synced 2025-10-13 11:11:30 -04:00
Client timeouts are currently counted from when the client is matched with a proxy. Instead, count client timeouts from the moment when the request is received. Closes #40449
84 lines
2.8 KiB
Go
84 lines
2.8 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"
|
|
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/util"
|
|
)
|
|
|
|
// 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
|
|
// remote 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: util.GetClientIp(r),
|
|
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)
|
|
}
|
|
}
|