gddo-server: tee requests to pkgGoDev

At the end of a request to *.godoc.org, an additional request is made
to the teeproxy for pkg.go.dev containing data about the request.

These requests are used to collect data about godoc.org, and ensure that
all incoming traffic to godoc.org can be handled appropriately by
pkg.go.dev.

Change-Id: Ib0b92b81c2a361f5b6a1813ddc62d090edbe4ba1
Reviewed-on: https://go-review.googlesource.com/c/gddo/+/219017
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
Reviewed-by: Andrew Bonventre <andybons@golang.org>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
diff --git a/gddo-server/main.go b/gddo-server/main.go
index ea34868..4ee5f8f 100644
--- a/gddo-server/main.go
+++ b/gddo-server/main.go
@@ -987,11 +987,61 @@
 	return s, nil
 }
 
+const (
+	pkgGoDevRedirectCookie = "pkggodev-redirect"
+	pkgGoDevRedirectParam  = "redirect"
+	pkgGoDevRedirectOn     = "on"
+	pkgGoDevRedirectOff    = "off"
+	pkgGoDevHost           = "pkg.go.dev"
+	teeproxyHost           = "teeproxy-dot-go-discovery.appspot.com"
+)
+
 func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	start := time.Now()
 	s.logRequestStart(r)
 	s.root.ServeHTTP(w, r)
-	s.logRequestEnd(r, time.Since(start))
+	latency := time.Since(start)
+	s.logRequestEnd(r, latency)
+	if f, ok := w.(http.Flusher); ok {
+		f.Flush()
+	}
+	if err := makePkgGoDevRequest(r, latency); err != nil {
+		log.Printf("makePkgGoDevRequest(%q, %d) error: %v", r.URL, latency, err)
+	}
+}
+
+func makePkgGoDevRequest(r *http.Request, latency time.Duration) error {
+	event := newGDDOEvent(r, latency)
+	b, err := json.Marshal(event)
+	if err != nil {
+		return fmt.Errorf("json.Marshal(%v): %v", event, err)
+	}
+
+	teeproxyURL := url.URL{Scheme: "https", Host: teeproxyHost}
+	if _, err := http.Post(teeproxyURL.String(), jsonMIMEType, bytes.NewReader(b)); err != nil {
+		return fmt.Errorf("http.Post(%q, %q, %v): %v", teeproxyURL.String(), jsonMIMEType, event, err)
+	}
+	log.Printf("makePkgGoDevRequest: request made to %q for %+v", teeproxyURL.String(), event)
+	return nil
+}
+
+type gddoEvent struct {
+	Host         string
+	Path         string
+	URL          string
+	RedirectHost string
+	Latency      time.Duration
+}
+
+func newGDDOEvent(r *http.Request, latency time.Duration) *gddoEvent {
+	pkgGoDevURL := url.URL{Scheme: "https", Host: pkgGoDevHost}
+	return &gddoEvent{
+		Host:         r.URL.Host,
+		Path:         r.URL.Path,
+		URL:          r.URL.String(),
+		RedirectHost: pkgGoDevURL.String(),
+		Latency:      latency,
+	}
 }
 
 func (s *server) logRequestStart(req *http.Request) {
@@ -1019,14 +1069,6 @@
 	})
 }
 
-const (
-	pkgGoDevRedirectCookie = "pkggodev-redirect"
-	pkgGoDevRedirectParam  = "redirect"
-	pkgGoDevRedirectOn     = "on"
-	pkgGoDevRedirectOff    = "off"
-	pkgGoDevHost           = "pkg.go.dev"
-)
-
 var gddoToPkgGoDevRequest = map[string]string{
 	"/-/about": "/about",
 	"/-/go":    "/std",
diff --git a/gddo-server/main_test.go b/gddo-server/main_test.go
index 8dddb8e..dde193a 100644
--- a/gddo-server/main_test.go
+++ b/gddo-server/main_test.go
@@ -10,6 +10,8 @@
 	"net/http"
 	"net/http/httptest"
 	"testing"
+
+	"github.com/google/go-cmp/cmp"
 )
 
 var robotTests = []string{
@@ -111,3 +113,58 @@
 		})
 	}
 }
+
+func TestNewGDDOEvent(t *testing.T) {
+	for _, test := range []struct {
+		url  string
+		want *gddoEvent
+	}{
+		{
+			url: "https://godoc.org",
+			want: &gddoEvent{
+				Host: "godoc.org",
+				Path: "",
+			},
+		},
+		{
+			url: "https://godoc.org/-/about",
+			want: &gddoEvent{
+				Host: "godoc.org",
+				Path: "/-/about",
+			},
+		},
+		{
+			url: "https://godoc.org/?q=test",
+			want: &gddoEvent{
+				Host: "godoc.org",
+				Path: "/",
+			},
+		},
+		{
+			url: "https://godoc.org/net/http",
+			want: &gddoEvent{
+				Host: "godoc.org",
+				Path: "/net/http",
+			},
+		},
+		{
+			url: "https://api.godoc.org/imports/net/http",
+			want: &gddoEvent{
+				Host: "api.godoc.org",
+				Path: "/imports/net/http",
+			},
+		},
+	} {
+		t.Run(test.url, func(t *testing.T) {
+			r := httptest.NewRequest("GET", test.url, nil)
+			want := test.want
+			want.Latency = 100
+			want.RedirectHost = "https://" + pkgGoDevHost
+			want.URL = test.url
+			got := newGDDOEvent(r, want.Latency)
+			if diff := cmp.Diff(want, got); diff != "" {
+				t.Fatalf("mismatch (-want +got):\n%s", diff)
+			}
+		})
+	}
+}