cmd/golangorg: remove duplicate list of repo names from the /x/ handler

Also, add tests.

This also removes the golang.org/x/codereview redirect because
code.google.com and our Mercurial repository are long dead.

Updates golang/go#36047

Change-Id: I0aacbe5a963b0edeea2dd2f1b2e2ad6a2f1f7319
Reviewed-on: https://go-review.googlesource.com/c/website/+/210744
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
diff --git a/cmd/golangorg/regtest_test.go b/cmd/golangorg/regtest_test.go
index c6a5e11..cf8a491 100644
--- a/cmd/golangorg/regtest_test.go
+++ b/cmd/golangorg/regtest_test.go
@@ -118,6 +118,11 @@
 			Substring:   "", // just check it is a 2xx.
 			NoAnalytics: true,
 		},
+		{
+			Path:        "/x/net",
+			Substring:   `<meta name="go-import" content="golang.org/x/net git https://go.googlesource.com/net">`,
+			NoAnalytics: true,
+		},
 	}
 
 	for _, tc := range substringTests {
diff --git a/cmd/golangorg/x.go b/cmd/golangorg/x.go
index 539316f..6aae7d6 100644
--- a/cmd/golangorg/x.go
+++ b/cmd/golangorg/x.go
@@ -13,68 +13,39 @@
 	"log"
 	"net/http"
 	"strings"
+
+	"golang.org/x/build/repos"
 )
 
 const xPrefix = "/x/"
 
-type xRepo struct {
-	URL, VCS string
-}
-
-var xMap = map[string]xRepo{
-	"codereview": {"https://code.google.com/p/go.codereview", "hg"}, // Not included at https://golang.org/pkg/#subrepo.
-
-	"arch":       {"https://go.googlesource.com/arch", "git"}, // Not included at https://golang.org/pkg/#subrepo.
-	"benchmarks": {"https://go.googlesource.com/benchmarks", "git"},
-	"blog":       {"https://go.googlesource.com/blog", "git"},
-	"build":      {"https://go.googlesource.com/build", "git"},
-	"crypto":     {"https://go.googlesource.com/crypto", "git"},
-	"debug":      {"https://go.googlesource.com/debug", "git"},
-	"exp":        {"https://go.googlesource.com/exp", "git"},
-	"image":      {"https://go.googlesource.com/image", "git"},
-	"lint":       {"https://go.googlesource.com/lint", "git"}, // Not included at https://golang.org/pkg/#subrepo.
-	"mobile":     {"https://go.googlesource.com/mobile", "git"},
-	"mod":        {"https://go.googlesource.com/mod", "git"},
-	"net":        {"https://go.googlesource.com/net", "git"},
-	"oauth2":     {"https://go.googlesource.com/oauth2", "git"}, // Not included at https://golang.org/pkg/#subrepo.
-	"perf":       {"https://go.googlesource.com/perf", "git"},
-	"playground": {"https://go.googlesource.com/playground", "git"}, // Not included at https://golang.org/pkg/#subrepo.
-	"review":     {"https://go.googlesource.com/review", "git"},
-	"sync":       {"https://go.googlesource.com/sync", "git"},
-	"sys":        {"https://go.googlesource.com/sys", "git"},
-	"talks":      {"https://go.googlesource.com/talks", "git"}, // Not included at https://golang.org/pkg/#subrepo.
-	"term":       {"https://go.googlesource.com/term", "git"},  // Not included at https://golang.org/pkg/#subrepo.
-	"text":       {"https://go.googlesource.com/text", "git"},
-	"time":       {"https://go.googlesource.com/time", "git"},
-	"tools":      {"https://go.googlesource.com/tools", "git"},
-	"tour":       {"https://go.googlesource.com/tour", "git"},
-	"vgo":        {"https://go.googlesource.com/vgo", "git"},     // Not included at https://golang.org/pkg/#subrepo.
-	"website":    {"https://go.googlesource.com/website", "git"}, // Not included at https://golang.org/pkg/#subrepo.
-	"xerrors":    {"https://go.googlesource.com/xerrors", "git"}, // Not included at https://golang.org/pkg/#subrepo.
-}
-
 func init() {
 	http.HandleFunc(xPrefix, xHandler)
 }
 
 func xHandler(w http.ResponseWriter, r *http.Request) {
-	head, tail := strings.TrimPrefix(r.URL.Path, xPrefix), ""
-	if i := strings.Index(head, "/"); i != -1 {
-		head, tail = head[:i], head[i:]
-	}
-	if head == "" {
+	if !strings.HasPrefix(r.URL.Path, xPrefix) {
+		// Shouldn't happen if handler is registered correctly.
 		http.Redirect(w, r, "https://godoc.org/-/subrepo", http.StatusTemporaryRedirect)
 		return
 	}
-	repo, ok := xMap[head]
-	if !ok {
+	proj, suffix := strings.TrimPrefix(r.URL.Path, xPrefix), ""
+	if i := strings.Index(proj, "/"); i != -1 {
+		proj, suffix = proj[:i], proj[i:]
+	}
+	if proj == "" {
+		http.Redirect(w, r, "https://godoc.org/-/subrepo", http.StatusTemporaryRedirect)
+		return
+	}
+	repo, ok := repos.ByGerritProject[proj]
+	if !ok || !strings.HasPrefix(repo.ImportPath, "golang.org/x/") {
 		http.NotFound(w, r)
 		return
 	}
 	data := struct {
-		Prefix, Head, Tail string
-		Repo               xRepo
-	}{xPrefix, head, tail, repo}
+		Proj   string // Gerrit project ("net", "sys", etc)
+		Suffix string // optional "/path" for requests like /x/PROJ/path
+	}{proj, suffix}
 	if err := xTemplate.Execute(w, data); err != nil {
 		log.Println("xHandler:", err)
 	}
@@ -84,12 +55,12 @@
 <html>
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
-<meta name="go-import" content="golang.org{{.Prefix}}{{.Head}} {{.Repo.VCS}} {{.Repo.URL}}">
-<meta name="go-source" content="golang.org{{.Prefix}}{{.Head}} https://github.com/golang/{{.Head}}/ https://github.com/golang/{{.Head}}/tree/master{/dir} https://github.com/golang/{{.Head}}/blob/master{/dir}/{file}#L{line}">
-<meta http-equiv="refresh" content="0; url=https://godoc.org/golang.org{{.Prefix}}{{.Head}}{{.Tail}}">
+<meta name="go-import" content="golang.org/x/{{.Proj}} git https://go.googlesource.com/{{.Proj}}">
+<meta name="go-source" content="golang.org/x/{{.Proj}} https://github.com/golang/{{.Proj}}/ https://github.com/golang/{{.Proj}}/tree/master{/dir} https://github.com/golang/{{.Proj}}/blob/master{/dir}/{file}#L{line}">
+<meta http-equiv="refresh" content="0; url=https://godoc.org/golang.org/x/{{.Proj}}{{.Suffix}}">
 </head>
 <body>
-Nothing to see here; <a href="https://godoc.org/golang.org{{.Prefix}}{{.Head}}{{.Tail}}">move along</a>.
+Nothing to see here; <a href="https://godoc.org/golang.org/x/{{.Proj}}{{.Suffix}}">move along</a>.
 </body>
 </html>
 `))
diff --git a/cmd/golangorg/x_test.go b/cmd/golangorg/x_test.go
new file mode 100644
index 0000000..28982c3
--- /dev/null
+++ b/cmd/golangorg/x_test.go
@@ -0,0 +1,92 @@
+// Copyright 2019 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 main
+
+import (
+	"net/http/httptest"
+	"strings"
+	"testing"
+)
+
+func TestXHandler(t *testing.T) {
+	type check func(t *testing.T, rec *httptest.ResponseRecorder)
+	status := func(v int) check {
+		return func(t *testing.T, rec *httptest.ResponseRecorder) {
+			t.Helper()
+			if rec.Code != v {
+				t.Errorf("response status = %v; want %v", rec.Code, v)
+			}
+		}
+	}
+	substr := func(s string) check {
+		return func(t *testing.T, rec *httptest.ResponseRecorder) {
+			t.Helper()
+			if !strings.Contains(rec.Body.String(), s) {
+				t.Errorf("missing expected substring %q in value: %#q", s, rec.Body)
+			}
+		}
+	}
+	hasHeader := func(k, v string) check {
+		return func(t *testing.T, rec *httptest.ResponseRecorder) {
+			t.Helper()
+			if got := rec.HeaderMap.Get(k); got != v {
+				t.Errorf("header[%q] = %q; want %q", k, got, v)
+			}
+		}
+	}
+
+	tests := []struct {
+		name   string
+		path   string
+		checks []check
+	}{
+		{
+			name: "net",
+			path: "/x/net",
+			checks: []check{
+				status(200),
+				substr(`<meta name="go-import" content="golang.org/x/net git https://go.googlesource.com/net">`),
+				substr(`http-equiv="refresh" content="0; url=https://godoc.org/golang.org/x/net">`),
+			},
+		},
+		{
+			name: "net-suffix",
+			path: "/x/net/suffix",
+			checks: []check{
+				status(200),
+				substr(`<meta name="go-import" content="golang.org/x/net git https://go.googlesource.com/net">`),
+				substr(`http-equiv="refresh" content="0; url=https://godoc.org/golang.org/x/net/suffix">`),
+			},
+		},
+		{
+			name:   "notexist",
+			path:   "/x/notexist",
+			checks: []check{status(404)},
+		},
+		{
+			name: "empty",
+			path: "/x/",
+			checks: []check{
+				status(307),
+				hasHeader("Location", "https://godoc.org/-/subrepo"),
+			},
+		},
+		{
+			name:   "invalid",
+			path:   "/x/In%20Valid,X",
+			checks: []check{status(404)},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			req := httptest.NewRequest("GET", tt.path, nil)
+			rec := httptest.NewRecorder()
+			xHandler(rec, req)
+			for _, check := range tt.checks {
+				check(t, rec)
+			}
+		})
+	}
+}
diff --git a/go.mod b/go.mod
index f1f3f5a..aefe183 100644
--- a/go.mod
+++ b/go.mod
@@ -6,6 +6,7 @@
 	cloud.google.com/go v0.38.0
 	github.com/gomodule/redigo v2.0.0+incompatible
 	go.opencensus.io v0.22.0 // indirect
+	golang.org/x/build v0.0.0-20191213161705-41fffb13b6ef
 	golang.org/x/net v0.0.0-20190620200207-3b0461eec859
 	golang.org/x/tools v0.0.0-20190930201159-7c411dea38b0
 	google.golang.org/api v0.9.0 // indirect
diff --git a/go.sum b/go.sum
index 5a5d064..e0b1248 100644
--- a/go.sum
+++ b/go.sum
@@ -5,7 +5,13 @@
 cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
 github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
+github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
+github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@@ -21,20 +27,32 @@
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.2-0.20191028172631-481baca67f93 h1:VvBteXw2zOXEgm0o3PgONTWf+bhUGsCaiNn3pbkU9LA=
+github.com/google/go-cmp v0.3.2-0.20191028172631-481baca67f93/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
+github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
 github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
 github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
 github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
+github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
 github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
 github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
 go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
 go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
+go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
+golang.org/x/build v0.0.0-20191213161705-41fffb13b6ef h1:vxq/R6+rmxSS0Dpy3fgjNUIa3RMHLyN1HLFnCVSSaPc=
+golang.org/x/build v0.0.0-20191213161705-41fffb13b6ef/go.mod h1:ZnlK0tLKZc2fY0bQwgXiLO1V9KYILhZHGJZGCecl/1Q=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@@ -48,15 +66,19 @@
 golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190502183928-7f726cade0ab/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c h1:uOCk1iQW6Vc18bnC13MfzScl+wdKBmM9Y9kU7Z83/lw=
 golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
@@ -67,7 +89,9 @@
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b h1:ag/x1USPSsqHud38I9BAC88qdNLDHHtQ4mlgQIZPPNA=
 golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -75,6 +99,7 @@
 golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -101,6 +126,8 @@
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU=
 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a h1:LJwr7TCTghdatWv40WobzlKXc9c4s8oGa7QKJUtHhWA=