gddo-server: parse host and url from request correctly Most browsers send requests to http://godoc.org/net/http as GET /net/http HTTP/1.1 Host: godoc.org instead of as expected in current tests GET https://godoc.org/net/http HTTP/1.1 Host: godoc.org This causes http.Request.URL to have empty strings for the "Scheme" and "Host" fields (see https://golang.org/src/net/http/request.go#L120 and RFC 7230 Section 5.3). As a result, server logs show most gddoEvent structs as having an empty "Host" field and a relative path for the URL. This CL enables the parsing of these fields from the other fields of the http.Request struct. Change-Id: Iab923a0d9a70f32f36b6e4a15019afc648699796 Reviewed-on: https://go-review.googlesource.com/c/gddo/+/236143 Reviewed-by: Julie Qiu <julie@golang.org> Reviewed-by: Jonathan Amsterdam <jba@google.com>
diff --git a/gddo-server/main.go b/gddo-server/main.go index d316344..9ffb342 100644 --- a/gddo-server/main.go +++ b/gddo-server/main.go
@@ -1074,11 +1074,20 @@ } func newGDDOEvent(r *http.Request, latency time.Duration, isRobot bool) *gddoEvent { + targetURL := url.URL{ + Scheme: "https", + Host: r.URL.Host, + Path: r.URL.Path, + RawQuery: r.URL.RawQuery, + } + if targetURL.Host == "" && r.Host != "" { + targetURL.Host = r.Host + } pkgGoDevURL := url.URL{Scheme: "https", Host: pkgGoDevHost} return &gddoEvent{ - Host: r.URL.Host, + Host: targetURL.Host, Path: r.URL.Path, - URL: r.URL.String(), + URL: targetURL.String(), Header: r.Header, RedirectHost: pkgGoDevURL.String(), Latency: latency,
diff --git a/gddo-server/main_test.go b/gddo-server/main_test.go index 1f29fa7..28f53a4 100644 --- a/gddo-server/main_test.go +++ b/gddo-server/main_test.go
@@ -7,9 +7,11 @@ package main import ( + "bufio" "net/http" "net/http/httptest" "net/url" + "strings" "testing" "github.com/google/go-cmp/cmp" @@ -306,3 +308,140 @@ }) } } + +func TestNewGDDOEventFromRequests(t *testing.T) { + for _, test := range []struct { + name string + requestURI string + host string + want *gddoEvent + }{ + { + name: "absolute index path", + requestURI: "https://godoc.org", + host: "godoc.org", + want: &gddoEvent{ + Host: "godoc.org", + Path: "", + URL: "https://godoc.org", + }, + }, + { + name: "absolute index path with trailing slash", + requestURI: "https://godoc.org/", + host: "godoc.org", + want: &gddoEvent{ + Host: "godoc.org", + Path: "/", + URL: "https://godoc.org/", + }, + }, + { + name: "relative index path", + requestURI: "/", + host: "godoc.org", + want: &gddoEvent{ + Host: "godoc.org", + Path: "/", + URL: "https://godoc.org/", + }, + }, + { + name: "absolute about path", + requestURI: "https://godoc.org/-/about", + host: "godoc.org", + want: &gddoEvent{ + Host: "godoc.org", + Path: "/-/about", + URL: "https://godoc.org/-/about", + }, + }, + { + name: "relative about path", + requestURI: "/-/about", + host: "godoc.org", + want: &gddoEvent{ + Host: "godoc.org", + Path: "/-/about", + URL: "https://godoc.org/-/about", + }, + }, + { + name: "absolute package path", + requestURI: "https://godoc.org/net/http", + host: "godoc.org", + want: &gddoEvent{ + Host: "godoc.org", + Path: "/net/http", + URL: "https://godoc.org/net/http", + }, + }, + { + name: "relative package path", + requestURI: "/net/http", + host: "godoc.org", + want: &gddoEvent{ + Host: "godoc.org", + Path: "/net/http", + URL: "https://godoc.org/net/http", + }, + }, + { + name: "absolute path with query parameters", + requestURI: "https://godoc.org/net/http?q=test", + host: "godoc.org", + want: &gddoEvent{ + Host: "godoc.org", + Path: "/net/http", + URL: "https://godoc.org/net/http?q=test", + }, + }, + { + name: "relative path with query parameters", + requestURI: "/net/http?q=test", + host: "godoc.org", + want: &gddoEvent{ + Host: "godoc.org", + Path: "/net/http", + URL: "https://godoc.org/net/http?q=test", + }, + }, + { + name: "absolute api path", + requestURI: "https://api.godoc.org/imports/net/http", + host: "api.godoc.org", + want: &gddoEvent{ + Host: "api.godoc.org", + Path: "/imports/net/http", + URL: "https://api.godoc.org/imports/net/http", + }, + }, + { + name: "relative api path", + requestURI: "/imports/net/http", + host: "api.godoc.org", + want: &gddoEvent{ + Host: "api.godoc.org", + Path: "/imports/net/http", + URL: "https://api.godoc.org/imports/net/http", + }, + }, + } { + t.Run(test.name, func(t *testing.T) { + want := test.want + want.Latency = 100 + want.RedirectHost = "https://" + pkgGoDevHost + want.Header = http.Header{} + want.IsRobot = false + requestLine := "GET " + test.requestURI + " HTTP/1.1\r\nHost: " + test.host + "\r\n\r\n" + req, err := http.ReadRequest(bufio.NewReader(strings.NewReader(requestLine))) + if err != nil { + t.Fatal("invalid NewRequest arguments; " + err.Error()) + } + got := newGDDOEvent(req, want.Latency, want.IsRobot) + if diff := cmp.Diff(want, got); diff != "" { + t.Fatalf("mismatch (-want +got):\n%s", diff) + } + }) + } +}