internal/middleware: delete GodocURL

Delete the GodocURL middleware since it is no longer needed.

Change-Id: I24b717e4d060ed19320e778afae6803b5404e49f
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/286512
Reviewed-by: Jamal Carvalho <jamal@golang.org>
Trust: Julie Qiu <julie@golang.org>
This commit is contained in:
Julie Qiu 2021-01-25 17:46:13 -05:00 коммит произвёл Jonathan Amsterdam
Родитель 4da3fcf7c7
Коммит 34b331ad78
6 изменённых файлов: 15 добавлений и 286 удалений

Просмотреть файл

@ -191,7 +191,6 @@ func main() {
middleware.RequestLog(cmdconfig.Logger(ctx, cfg, "frontend-log")),
middleware.AcceptRequests(http.MethodGet, http.MethodPost, http.MethodHead), // accept only GETs, POSTs and HEADs
middleware.Quota(cfg.Quota, cacheClient),
middleware.GodocURL(), // potentially redirects so should be early in chain
middleware.RedirectedFrom(),
middleware.SecureHeaders(!*disableCSP), // must come before any caching for nonces to work
middleware.Experiment(experimenter),

Просмотреть файл

@ -6,7 +6,6 @@
{{define "main_content"}}
<div class="Container">
<a class="GodocButton" href="{{.GodocURL}}">Back to godoc.org</a>
<div class="SearchResults">
<h1 class="SearchResults-header">Results for “{{.Query}}”</h1>
<div class="SearchResults-help"><a href="/search-help">Search help</a></div>

Просмотреть файл

@ -243,9 +243,6 @@ type basePage struct {
// Experiments contains the experiments currently active.
Experiments *experiment.Set
// GodocURL is the URL of the corresponding page on godoc.org (if applicable).
GodocURL string
// DevMode indicates whether the server is running in development mode.
DevMode bool
@ -285,7 +282,6 @@ func (s *Server) newBasePage(r *http.Request, title string) basePage {
HTMLTitle: title,
Query: searchQuery(r),
Experiments: experiment.FromContext(r.Context()),
GodocURL: middleware.GodocURLPlaceholder,
DevMode: s.devMode,
AppVersionLabel: s.appVersionLabel,
GoogleTagManagerID: s.googleTagManagerID,

Просмотреть файл

@ -1,119 +0,0 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package middleware
import (
"bytes"
"net/http"
"net/url"
"golang.org/x/pkgsite/internal/log"
)
// GodocURLPlaceholder should be used as the value for any godoc.org URL in rendered
// content. It is substituted for the actual godoc.org URL value by the GodocURL middleware.
const GodocURLPlaceholder = "$$GODISCOVERY_GODOCURL$$"
// GodocURL adds a corresponding godoc.org URL value to the rendered page
// if the request is due to godoc.org automatically redirecting a user.
// The value is empty otherwise.
func GodocURL() Middleware {
// In order to reliably know that a request is coming to pkg.go.dev from
// godoc.org, we look for a utm_source GET parameter set to 'godoc'.
// If we see this, we set a temporary cookie and redirect to the
// pkg.go.dev URL with the utm_source param stripped (so that it doesnt
// remain in all our URLs coming from godoc.org). If this temporary cookie
// is seen, a non-empty value for the “Back to godoc.org” link is set.
// The existence of this value will be used to determine whether to show the
// button in the UI.
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
const tmpCookieName = "tmp-from-godoc"
// If the user is redirected from godoc.org, the requests URL will have
// utm_source=godoc.
if r.FormValue("utm_source") == "godoc" {
http.SetCookie(w, &http.Cookie{
Name: tmpCookieName,
Value: "1",
SameSite: http.SameSiteLaxMode, // request can originate from another domain via redirect
})
// Redirect to the same URL only without the utm_source parameter.
u := r.URL
q := u.Query()
q.Del("utm_source")
u.RawQuery = q.Encode()
http.Redirect(w, r, u.String(), http.StatusFound)
return
}
godocURL := godoc(r.URL)
if _, err := r.Cookie(tmpCookieName); err == http.ErrNoCookie {
// Cookie isnt set, indicating user is not coming from godoc.org.
godocURL = ""
} else {
http.SetCookie(w, &http.Cookie{
Name: tmpCookieName,
MaxAge: -1,
})
}
crw := &capturingResponseWriter{ResponseWriter: w}
h.ServeHTTP(crw, r)
body := crw.bytes()
body = bytes.ReplaceAll(body, []byte(GodocURLPlaceholder), []byte(godocURL))
if _, err := w.Write(body); err != nil {
log.Errorf(r.Context(), "GodocURL, writing: %v", err)
}
})
}
}
// godoc takes a Discovery URL and returns the corresponding godoc.org equivalent.
func godoc(u *url.URL) string {
result := &url.URL{Scheme: "https", Host: "godoc.org"}
switch u.Path {
case "/std":
result.Path = "/-/go"
case "/about":
result.Path = "/-/about"
case "/search":
result.Path = "/"
result.RawQuery = u.RawQuery
default:
{
result.Path = u.Path
switch u.Query().Get("tab") {
case "imports":
result.RawQuery = "imports"
case "importedby":
result.RawQuery = "importers"
case "subdirectories":
result.Fragment = "pkg-subdirectories"
}
}
}
q := result.Query()
q.Add("utm_source", "backtogodoc")
result.RawQuery = q.Encode()
return result.String()
}
// capturingResponseWriter is an http.ResponseWriter that captures
// the body for later processing.
type capturingResponseWriter struct {
http.ResponseWriter
buf bytes.Buffer
}
func (c *capturingResponseWriter) Write(b []byte) (int, error) {
return c.buf.Write(b)
}
func (c *capturingResponseWriter) bytes() []byte {
return c.buf.Bytes()
}

Просмотреть файл

@ -1,161 +0,0 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package middleware
import (
"bytes"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"testing"
)
func TestGodocURL(t *testing.T) {
mw := GodocURL()
mwh := mw(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
body := []byte(`<a href="$$GODISCOVERY_GODOCURL$$">godoc</a>`)
if _, err := w.Write(body); err != nil {
t.Fatalf("w.Write(%q) = %v", body, err)
}
}))
testCases := []struct {
desc string
// Request values
path string
cookies map[string]string
// Response values
code int
body []byte
headers map[string]string
}{
{
desc: "Unaffected request",
path: "/cloud.google.com/go/storage",
code: http.StatusOK,
body: []byte(`<a href="">godoc</a>`),
},
{
desc: "Strip utm_source, set temporary cookie, and redirect",
path: "/cloud.google.com/go/storage?utm_source=godoc",
code: http.StatusFound,
headers: map[string]string{
"Location": "/cloud.google.com/go/storage",
"Set-Cookie": "tmp-from-godoc=1; SameSite=Lax",
},
},
{
desc: "Delete temporary cookie; godoc URL should be set",
path: "/cloud.google.com/go/storage",
cookies: map[string]string{
"tmp-from-godoc": "1",
},
code: http.StatusOK,
body: []byte(`<a href="https://godoc.org/cloud.google.com/go/storage?utm_source=backtogodoc">godoc</a>`),
headers: map[string]string{
"Set-Cookie": "tmp-from-godoc=; Max-Age=0",
},
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
req := httptest.NewRequest("GET", test.path, nil)
for k, v := range test.cookies {
req.AddCookie(&http.Cookie{
Name: k,
Value: v,
})
}
w := httptest.NewRecorder()
mwh.ServeHTTP(w, req)
resp := w.Result()
defer resp.Body.Close()
if got, want := resp.StatusCode, test.code; got != want {
t.Errorf("Status code = %d; want %d", got, want)
}
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatalf("ioutil.ReadAll(resp.Body) = %v", err)
}
if got, want := body, test.body; !bytes.Equal(got, want) {
t.Errorf("Response body = %q; want %q", got, want)
}
}
for k, v := range test.headers {
if _, ok := resp.Header[k]; !ok {
t.Errorf("%q not present in response headers", k)
continue
}
if got, want := resp.Header.Get(k), v; got != want {
t.Errorf("Response header mismatch for %q: got %q; want %q", k, got, want)
}
}
})
}
}
func TestGodoc(t *testing.T) {
testCases := []struct {
from, to string
}{
{
from: "https://pkg.go.dev/cloud.google.com/go/storage",
to: "https://godoc.org/cloud.google.com/go/storage?utm_source=backtogodoc",
},
{
from: "https://pkg.go.dev/cloud.google.com/go/storage?tab=overview",
to: "https://godoc.org/cloud.google.com/go/storage?utm_source=backtogodoc",
},
{
from: "https://pkg.go.dev/cloud.google.com/go/storage?tab=versions",
to: "https://godoc.org/cloud.google.com/go/storage?utm_source=backtogodoc",
},
{
from: "https://pkg.go.dev/cloud.google.com/go/storage?tab=licenses",
to: "https://godoc.org/cloud.google.com/go/storage?utm_source=backtogodoc",
},
{
from: "https://pkg.go.dev/cloud.google.com/go/storage?tab=subdirectories",
to: "https://godoc.org/cloud.google.com/go/storage?utm_source=backtogodoc#pkg-subdirectories",
},
{
from: "https://pkg.go.dev/cloud.google.com/go/storage?tab=imports",
to: "https://godoc.org/cloud.google.com/go/storage?imports=&utm_source=backtogodoc",
},
{
from: "https://pkg.go.dev/cloud.google.com/go/storage?tab=importedby",
to: "https://godoc.org/cloud.google.com/go/storage?importers=&utm_source=backtogodoc",
},
{
from: "https://pkg.go.dev/std?tab=packages",
to: "https://godoc.org/-/go?utm_source=backtogodoc",
},
{
from: "https://pkg.go.dev/search?q=foo",
to: "https://godoc.org/?q=foo&utm_source=backtogodoc",
},
{
from: "https://pkg.go.dev/about",
to: "https://godoc.org/-/about?utm_source=backtogodoc",
},
}
for _, test := range testCases {
u, err := url.Parse(test.from)
if err != nil {
t.Errorf("url.Parse(%q): %v", test.from, err)
continue
}
to := godoc(u)
if got, want := to, test.to; got != want {
t.Errorf("godocURL(%q) = %q; want %q", u, got, want)
}
}
}

Просмотреть файл

@ -85,3 +85,18 @@ func LatestVersions(getLatest latestFunc) Middleware {
})
}
}
// capturingResponseWriter is an http.ResponseWriter that captures
// the body for later processing.
type capturingResponseWriter struct {
http.ResponseWriter
buf bytes.Buffer
}
func (c *capturingResponseWriter) Write(b []byte) (int, error) {
return c.buf.Write(b)
}
func (c *capturingResponseWriter) bytes() []byte {
return c.buf.Bytes()
}