15 "www.bamsoftware.com/git/champa.git/amp"
16 "www.bamsoftware.com/git/champa.git/armor"
19 // cacheBreaker returns a random byte slice of fixed length.
20 func cacheBreaker() []byte {
21 buf
:= make([]byte, 12)
22 _
, err
:= rand
.Read(buf
)
29 func exchangeAMP(ctx context
.Context
, serverURL
, cacheURL
*url
.URL
, front
string, p
[]byte) (io
.ReadCloser
, error
) {
30 // Append a cache buster and the encoded p to the path of serverURL.
31 u
:= serverURL
.ResolveReference(&url
.URL
{
32 // Use strings.Join, rather than path.Join, in order to retain a
33 // closing slash when p is empty.
34 Path
: strings
.Join([]string{
35 // "0" is the client–server protocol version indicator.
36 "0" + base64
.RawURLEncoding
.EncodeToString(cacheBreaker()),
37 base64
.RawURLEncoding
.EncodeToString(p
),
41 // Proxy through an AMP cache, if requested.
44 u
, err
= amp
.CacheURL(u
, cacheURL
, "c")
50 req
, err
:= http
.NewRequest("GET", u
.String(), nil)
54 req
= req
.WithContext(ctx
)
56 // Do domain fronting, if requested.
58 _
, port
, err
:= net
.SplitHostPort(req
.URL
.Host
)
60 req
.URL
.Host
= net
.JoinHostPort(front
, port
)
66 req
.Header
.Set("User-Agent", "") // Disable default "Go-http-client/1.1".
68 resp
, err
:= http
.DefaultTransport
.RoundTrip(req
)
72 if resp
.StatusCode
!= http
.StatusOK
{
74 return nil, fmt
.Errorf("server returned status %v", resp
.Status
)
76 if _
, err
:= resp
.Location(); err
== nil {
77 // The Google AMP Cache can return a "silent redirect" with
78 // status 200, a Location header set, and a JavaScript redirect
79 // in the body. The redirect points directly at the origin
80 // server for the request (bypassing the AMP cache). We do not
81 // follow redirects nor execute JavaScript, but in any case we
82 // cannot extract information from this response and it's better
83 // to treat it as a poll error, rather than an EOF when given to
84 // the AMP armor decoder.
86 // Such a response looks like this (header slightly excerpted):
89 // Cache-Control: private
90 // Content-Type: text/html; charset=UTF-8
91 // Location: https://example.com/champa/...
93 // X-Silent-Redirect: true
96 // <meta http-equiv="content-type" content="text/html;charset=utf-8">
97 // <TITLE>Redirecting</TITLE>
98 // <META HTTP-EQUIV="refresh" content="0; url=https://example.com/champa/...">
100 // <BODY onLoad="location.replace('https://example.com/champa/...'+document.location.hash)">
103 return nil, fmt
.Errorf("server returned a Location header")
106 dec
, err
:= armor
.NewDecoder(bufio
.NewReader(resp
.Body
))
112 // The caller should read from the decoder (which reads from the
113 // response body), but close the actual response body when done.