Repository: goadesign/goa
Pull request #2861 (2021-05-31 05:58:10)

Bump golang.org/x/tools from 0.1.1 to 0.1.2
Bumps [golang.org/x/tools](https://github.com/golang/tools) from 0.1.1 to 0.1.2.
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/golang/tools/commit/c3e30ffe9275c0e89ef0d38049bad16987bd7fb2"><code>c3e30ff</code></a> gopls/internal/hooks: add Staticcheck's quickfix category of checks</li>
<li><a href="https://github.com/golang/tools/commit/917abfb0e7d0299b5b87d8f4e7316da6e130cf9c"><code>917abfb</code></a> gopls: propagate Staticcheck's diagnostic severities</li>
<li><a href="https://github.com/golang/tools/commit/b95d58a81bf7fb3460d69cebc0d6991eddc77376"><code>b95d58a</code></a> gopls/internal/hooks: update for Staticcheck 2021.1</li>
<li><a href="https://github.com/golang/tools/commit/874765c9672aef97f1f818f9c554f71e93452a2c"><code>874765c</code></a> internal/testenv: update linux-arm to linux-arm-scaleway</li>
<li><a href="https://github.com/golang/tools/commit/98f8936a45ac344cdeb67b2e4008b5ca34947441"><code>98f8936</code></a> go/internal/gcimporter: cleanup testenv functions</li>
<li><a href="https://github.com/golang/tools/commit/71e666b5c4b60e3a0d87098a50faa2a1dd558a08"><code>71e666b</code></a> internal/lsp: don't crash on an empty import path</li>
<li><a href="https://github.com/golang/tools/commit/d5449c00d2e9158aa8f4e5a0899dc417552f3a6b"><code>d5449c0</code></a> internal/lsp: fix mod file codelens</li>
<li><a href="https://github.com/golang/tools/commit/88a9bccae53277c35424a9ae8238f8cff6105722"><code>88a9bcc</code></a> internal/lsp/protocol: update code generated for LSP</li>
<li><a href="https://github.com/golang/tools/commit/e64a37c1d51946a16e95e98ff87b7b77555bd9d7"><code>e64a37c</code></a> lsp/completion: don't offer literal candidates requiring conversion</li>
<li><a href="https://github.com/golang/tools/commit/4061312594fc4c553ddda40109133239c66553b2"><code>4061312</code></a> internal/lsp: add list_known_packages and add_import commands</li>
<li>Additional commits viewable in <a href="https://github.com/golang/tools/compare/v0.1.1...v0.1.2">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=golang.org/x/tools&package-manager=go_modules&previous-version=0.1.1&new-version=0.1.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


Patch:
diff --git a/go.mod b/go.mod
index 3ee7c9af..d57b6326 100644
--- a/go.mod
+++ b/go.mod
@@ -18,7 +18,7 @@ require (
 	github.com/zach-klippenstein/goregen v0.0.0-20160303162051-795b5e3961ea
 	golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect
 	golang.org/x/sys v0.0.0-20210514084401-e8d321eab015 // indirect
-	golang.org/x/tools v0.1.1
+	golang.org/x/tools v0.1.2
 	google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384 // indirect
 	google.golang.org/grpc v1.38.0
 	google.golang.org/protobuf v1.26.0
diff --git a/go.sum b/go.sum
index 0737645e..cd052d31 100644
--- a/go.sum
+++ b/go.sum
@@ -373,8 +373,8 @@ golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgw
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
-golang.org/x/tools v0.1.1 h1:wGiQel/hW0NnEkJUk8lbzkX2gFJU6PFxf1v5OlCfuOs=
-golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.2 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA=
+golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/http/codegen/server.go b/http/codegen/server.go
index 0eef29d1..32680b21 100644
--- a/http/codegen/server.go
+++ b/http/codegen/server.go
@@ -35,12 +35,13 @@ func serverFile(genpkg string, svc *expr.HTTPServiceExpr) *codegen.File {
 	path := filepath.Join(codegen.Gendir, "http", svcName, "server", "server.go")
 	title := fmt.Sprintf("%s HTTP server", svc.Name())
 	funcs := map[string]interface{}{
-		"join":                func(ss []string, s string) string { return strings.Join(ss, s) },
-		"hasWebSocket":        hasWebSocket,
-		"isWebSocketEndpoint": isWebSocketEndpoint,
-		"viewedServerBody":    viewedServerBody,
-		"mustDecodeRequest":   mustDecodeRequest,
-		"addLeadingSlash":     addLeadingSlash,
+		"join":                    func(ss []string, s string) string { return strings.Join(ss, s) },
+		"hasWebSocket":            hasWebSocket,
+		"isWebSocketEndpoint":     isWebSocketEndpoint,
+		"viewedServerBody":        viewedServerBody,
+		"mustDecodeRequest":       mustDecodeRequest,
+		"addLeadingSlash":         addLeadingSlash,
+		"removeTrailingIndexHTML": removeTrailingIndexHTML,
 	}
 	sections := []*codegen.SectionTemplate{
 		codegen.Header(title, "server", []*codegen.ImportSpec{
@@ -246,6 +247,13 @@ func addLeadingSlash(s string) string {
 	return "/" + s
 }
 
+func removeTrailingIndexHTML(s string) string {
+	if strings.HasSuffix(s, "/index.html") {
+		return strings.TrimSuffix(s, "index.html")
+	}
+	return s
+}
+
 func mapQueryDecodeData(dt expr.DataType, varName string, inc int) map[string]interface{} {
 	return map[string]interface{}{
 		"Type":      dt,
@@ -368,9 +376,12 @@ func {{ .MountServer }}(mux goahttp.Muxer, h *{{ .ServerStruct }}) {
 	{{ .MountHandler }}(mux, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 			http.Redirect(w, r, "{{ .Redirect.URL }}", {{ .Redirect.StatusCode }})
 		}))
-	 	{{- else }}
-			{{- $filepath := addLeadingSlash .FilePath }}
-	{{ .MountHandler }}(mux, {{ range .RequestPaths }}{{if ne . $filepath }}goahttp.ReplacePrefix("{{ . }}", "{{ $filepath }}", {{ end }}{{ end }}h.{{ .VarName }}){{ range .RequestPaths }}{{ if ne . $filepath }}){{ end}}{{ end }}
+	 	{{- else if .IsDir }}
+			{{- $filepath := addLeadingSlash (removeTrailingIndexHTML .FilePath) }}
+	{{ .MountHandler }}(mux, {{ range .RequestPaths }}{{if ne . $filepath }}goahttp.Replace("{{ . }}", "{{ $filepath }}", {{ end }}{{ end }}h.{{ .VarName }}){{ range .RequestPaths }}{{ if ne . $filepath }}){{ end}}{{ end }}
+		{{- else }}
+			{{- $filepath := addLeadingSlash (removeTrailingIndexHTML .FilePath) }}
+	{{ .MountHandler }}(mux, {{ range .RequestPaths }}{{if ne . $filepath }}goahttp.Replace("", "{{ $filepath }}", {{ end }}{{ end }}h.{{ .VarName }}){{ range .RequestPaths }}{{ if ne . $filepath }}){{ end}}{{ end }}
 		{{- end }}
 	{{- end }}
 }
diff --git a/http/server.go b/http/server.go
index 4b34b6aa..e0453f85 100644
--- a/http/server.go
+++ b/http/server.go
@@ -24,14 +24,21 @@ func (s Servers) Use(m func(http.Handler) http.Handler) {
 	}
 }
 
-// ReplacePrefix returns a handler that serves HTTP requests by replacing the
-// prefix from the request URL's Path (and RawPath if set) and invoking the
-// handler h. The logic is the same as the standard http package StripPrefix
-// function.
-func ReplacePrefix(old, nw string, h http.Handler) http.Handler {
+// Replace returns a handler that serves HTTP requests by replacing the
+// request URL's Path (and RawPath if set) and invoking the handler h.
+// The logic is the same as the standard http package StripPrefix function.
+func Replace(old, nw string, h http.Handler) http.Handler {
 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		p := strings.Replace(r.URL.Path, old, nw, 1)
-		rp := strings.Replace(r.URL.RawPath, old, nw, 1)
+		var p, rp string
+		if old != "" {
+			p = strings.Replace(r.URL.Path, old, nw, 1)
+			rp = strings.Replace(r.URL.RawPath, old, nw, 1)
+		} else {
+			p = nw
+			if r.URL.RawPath != "" {
+				rp = nw
+			}
+		}
 		if p != r.URL.Path && (r.URL.RawPath == "" || rp != r.URL.RawPath) {
 			r2 := new(http.Request)
 			*r2 = *r


Test patch:
diff --git a/http/codegen/testdata/server_init_functions.go b/http/codegen/testdata/server_init_functions.go
index a36b4856..d2730e03 100644
--- a/http/codegen/testdata/server_init_functions.go
+++ b/http/codegen/testdata/server_init_functions.go
@@ -179,19 +179,19 @@ func New(
 
 var ServerMultipleFilesConstructorCode = `// Mount configures the mux to serve the ServiceFileServer endpoints.
 func Mount(mux goahttp.Muxer, h *Server) {
-	MountPathToFileJSON(mux, goahttp.ReplacePrefix("/file.json", "/path/to/file.json", h.PathToFileJSON))
-	MountPathToFileJSON2(mux, goahttp.ReplacePrefix("/", "/path/to/file.json", h.PathToFileJSON2))
+	MountPathToFileJSON(mux, goahttp.Replace("", "/path/to/file.json", h.PathToFileJSON))
+	MountPathToFileJSON2(mux, goahttp.Replace("", "/path/to/file.json", h.PathToFileJSON2))
 	MountFileJSON(mux, h.FileJSON)
-	MountPathToFolder(mux, goahttp.ReplacePrefix("/", "/path/to/folder", h.PathToFolder))
+	MountPathToFolder(mux, goahttp.Replace("/", "/path/to/folder", h.PathToFolder))
 }
 `
 
 var ServerMultipleFilesWithPrefixPathConstructorCode = `// Mount configures the mux to serve the ServiceFileServer endpoints.
 func Mount(mux goahttp.Muxer, h *Server) {
-	MountPathToFileJSON(mux, goahttp.ReplacePrefix("/server_file_server/file.json", "/path/to/file.json", h.PathToFileJSON))
-	MountPathToFileJSON2(mux, goahttp.ReplacePrefix("/server_file_server", "/path/to/file.json", h.PathToFileJSON2))
-	MountFileJSON(mux, goahttp.ReplacePrefix("/server_file_server/file.json", "/file.json", h.FileJSON))
-	MountPathToFolder(mux, goahttp.ReplacePrefix("/server_file_server", "/path/to/folder", h.PathToFolder))
+	MountPathToFileJSON(mux, goahttp.Replace("", "/path/to/file.json", h.PathToFileJSON))
+	MountPathToFileJSON2(mux, goahttp.Replace("", "/path/to/file.json", h.PathToFileJSON2))
+	MountFileJSON(mux, goahttp.Replace("", "/file.json", h.FileJSON))
+	MountPathToFolder(mux, goahttp.Replace("/server_file_server", "/path/to/folder", h.PathToFolder))
 }
 `
 
@@ -200,9 +200,9 @@ func Mount(mux goahttp.Muxer, h *Server) {
 	MountPathToFileJSON(mux, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		http.Redirect(w, r, "/redirect/dest", http.StatusMovedPermanently)
 	}))
-	MountPathToFileJSON2(mux, goahttp.ReplacePrefix("/", "/path/to/file.json", h.PathToFileJSON2))
+	MountPathToFileJSON2(mux, goahttp.Replace("", "/path/to/file.json", h.PathToFileJSON2))
 	MountFileJSON(mux, h.FileJSON)
-	MountPathToFolder(mux, goahttp.ReplacePrefix("/", "/path/to/folder", h.PathToFolder))
+	MountPathToFolder(mux, goahttp.Replace("/", "/path/to/folder", h.PathToFolder))
 }
 `
 
diff --git a/http/server_test.go b/http/server_test.go
new file mode 100644
index 00000000..dacf99cf
--- /dev/null
+++ b/http/server_test.go
@@ -0,0 +1,63 @@
+package http
+
+import (
+	"net/http"
+	"net/http/httptest"
+	"testing"
+)
+
+func TestReplace(t *testing.T) {
+	var (
+		h = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+			w.Header().Set("X-Path", r.URL.Path)
+			w.Header().Set("X-RawPath", r.URL.RawPath)
+		})
+	)
+	cases := []struct {
+		old     string
+		nw      string
+		reqPath string
+		path    string // If empty we want a 404.
+		rawPath string
+	}{
+		{"/foo/bar", "", "/foo/bar/qux", "/qux", ""},
+		{"/foo/bar", "", "/foo/bar%2Fqux", "/qux", "%2Fqux"},
+		{"/foo/bar", "", "/foo%2Fbar/qux", "", ""}, // Escaped prefix does not match.
+		{"/foo/bar", "", "/bar", "", ""},           // No prefix match.
+		{"/foo/bar", "/baz", "/foo/bar/qux", "/baz/qux", ""},
+		{"/foo/bar", "/baz", "/foo/bar%2Fqux", "/baz/qux", "/baz%2Fqux"},
+		{"/foo/bar", "/baz", "/foo%2Fbar/qux", "", ""}, // Escaped prefix does not match.
+		{"/foo/bar", "/baz", "/bar", "", ""},           // No prefix match.
+		{"", "/baz/baz/baz", "/foo/bar/qux", "/baz/baz/baz", ""},
+		{"", "/baz/baz/baz", "/foo/bar%2Fqux", "/baz/baz/baz", "/baz/baz/baz"},
+		{"", "/baz/baz/baz", "/foo%2Fbar/qux", "/baz/baz/baz", "/baz/baz/baz"},
+		{"", "/baz/baz/baz", "/bar", "/baz/baz/baz", ""},
+	}
+	for _, tc := range cases {
+		t.Run(tc.reqPath, func(t *testing.T) {
+			ts := httptest.NewServer(Replace(tc.old, tc.nw, h))
+			defer ts.Close()
+			c := ts.Client()
+			res, err := c.Get(ts.URL + tc.reqPath)
+			if err != nil {
+				t.Fatal(err)
+			}
+			res.Body.Close()
+			if tc.path == "" {
+				if res.StatusCode != http.StatusNotFound {
+					t.Errorf("got %q, want 404 Not Found", res.Status)
+				}
+				return
+			}
+			if res.StatusCode != http.StatusOK {
+				t.Fatalf("got %q, want 200 OK", res.Status)
+			}
+			if g, w := res.Header.Get("X-Path"), tc.path; g != w {
+				t.Errorf("got Path %q, want %q", g, w)
+			}
+			if g, w := res.Header.Get("X-RawPath"), tc.rawPath; g != w {
+				t.Errorf("got RawPath %q, want %q", g, w)
+			}
+		})
+	}
+}