Skip to content

Commit 75b1e8d

Browse files
committed
Refactor file server implementation
So that it doesn't require introducing a `Replace` function. The new implementation is more robust and properly handles cases where the file path is deep and differs from the mount path.
1 parent 8aed3dd commit 75b1e8d

File tree

7 files changed

+72
-130
lines changed

7 files changed

+72
-130
lines changed

http/codegen/server.go

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package codegen
22

33
import (
44
"fmt"
5+
"path"
56
"path/filepath"
67
"reflect"
78
"strings"
@@ -32,16 +33,16 @@ func ServerFiles(genpkg string, root *expr.RootExpr) []*codegen.File {
3233
func serverFile(genpkg string, svc *expr.HTTPServiceExpr) *codegen.File {
3334
data := HTTPServices.Get(svc.Name())
3435
svcName := data.Service.PathName
35-
path := filepath.Join(codegen.Gendir, "http", svcName, "server", "server.go")
36+
fpath := filepath.Join(codegen.Gendir, "http", svcName, "server", "server.go")
3637
title := fmt.Sprintf("%s HTTP server", svc.Name())
3738
funcs := map[string]any{
38-
"join": strings.Join,
39-
"hasWebSocket": hasWebSocket,
40-
"isWebSocketEndpoint": isWebSocketEndpoint,
41-
"viewedServerBody": viewedServerBody,
42-
"mustDecodeRequest": mustDecodeRequest,
43-
"addLeadingSlash": addLeadingSlash,
44-
"removeTrailingIndexHTML": removeTrailingIndexHTML,
39+
"join": strings.Join,
40+
"hasWebSocket": hasWebSocket,
41+
"isWebSocketEndpoint": isWebSocketEndpoint,
42+
"viewedServerBody": viewedServerBody,
43+
"mustDecodeRequest": mustDecodeRequest,
44+
"addLeadingSlash": addLeadingSlash,
45+
"dir": path.Dir,
4546
}
4647
imports := []*codegen.ImportSpec{
4748
{Path: "bufio"},
@@ -86,11 +87,14 @@ func serverFile(genpkg string, svc *expr.HTTPServiceExpr) *codegen.File {
8687
sections = append(sections, &codegen.SectionTemplate{Name: "server-handler", Source: readTemplate("server_handler"), Data: e})
8788
sections = append(sections, &codegen.SectionTemplate{Name: "server-handler-init", Source: readTemplate("server_handler_init"), FuncMap: funcs, Data: e})
8889
}
90+
if len(data.FileServers) > 0 {
91+
sections = append(sections, &codegen.SectionTemplate{Name: "append-fs", Source: readTemplate("append_fs"), FuncMap: funcs, Data: data})
92+
}
8993
for _, s := range data.FileServers {
9094
sections = append(sections, &codegen.SectionTemplate{Name: "server-files", Source: readTemplate("file_server"), FuncMap: funcs, Data: s})
9195
}
9296

93-
return &codegen.File{Path: path, SectionTemplates: sections}
97+
return &codegen.File{Path: fpath, SectionTemplates: sections}
9498
}
9599

96100
// serverEncodeDecodeFile returns the file defining the HTTP server encoding and
@@ -252,13 +256,6 @@ func addLeadingSlash(s string) string {
252256
return "/" + s
253257
}
254258

255-
func removeTrailingIndexHTML(s string) string {
256-
if strings.HasSuffix(s, "/index.html") {
257-
return strings.TrimSuffix(s, "index.html")
258-
}
259-
return s
260-
}
261-
262259
func mapQueryDecodeData(dt expr.DataType, varName string, inc int) map[string]any {
263260
return map[string]any{
264261
"Type": dt,
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// appendFS is a custom implementation of fs.FS that appends a specified prefix
2+
// to the file paths before delegating the Open call to the underlying fs.FS.
3+
type appendFS struct {
4+
prefix string
5+
fs http.FileSystem
6+
}
7+
8+
// Open opens the named file, appending the prefix to the file path before
9+
// passing it to the underlying fs.FS.
10+
func (s appendFS) Open(name string) (http.File, error) {
11+
return s.fs.Open(path.Join(s.prefix, name))
12+
}
13+
14+
// appendPrefix returns a new fs.FS that appends the specified prefix to file paths
15+
// before delegating to the provided embed.FS.
16+
func appendPrefix(fsys http.FileSystem, prefix string) http.FileSystem {
17+
return appendFS{prefix: prefix, fs: fsys}
18+
}

http/codegen/templates/server_init.go.tpl

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ func {{ .ServerInit }}(
2828
if {{ .ArgName }} == nil {
2929
{{ .ArgName }} = http.Dir(".")
3030
}
31+
{{- $prefix := addLeadingSlash .FilePath }}
32+
{{- if not .IsDir }}
33+
{{- $prefix = dir $prefix }}
34+
{{- end }}
35+
{{- if ne $prefix "/" }}
36+
{{ .ArgName }} = appendPrefix({{ .ArgName }}, "{{ $prefix }}")
37+
{{- end }}
3138
{{- end }}
3239
return &{{ .ServerStruct }}{
3340
Mounts: []*{{ .MountPointStruct }}{
@@ -39,7 +46,7 @@ func {{ .ServerInit }}(
3946
{{- range .FileServers }}
4047
{{- $filepath := .FilePath }}
4148
{{- range .RequestPaths }}
42-
{"{{ $filepath }}", "GET", "{{ . }}"},
49+
{"Serve {{ $filepath }}", "GET", "{{ . }}"},
4350
{{- end }}
4451
{{- end }}
4552
},

http/codegen/templates/server_mount.go.tpl

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,21 @@ func {{ .MountServer }}(mux goahttp.Muxer, h *{{ .ServerStruct }}) {
88
{{ .MountHandler }}(mux, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
99
http.Redirect(w, r, "{{ .Redirect.URL }}", {{ .Redirect.StatusCode }})
1010
}))
11-
{{- else if .IsDir }}
12-
{{- $filepath := addLeadingSlash (removeTrailingIndexHTML .FilePath) }}
13-
{{ .MountHandler }}(mux, {{ range .RequestPaths }}{{if ne . $filepath }}goahttp.Replace("{{ . }}", "{{ $filepath }}", {{ end }}{{ end }}h.{{ .VarName }}){{ range .RequestPaths }}{{ if ne . $filepath }}){{ end}}{{ end }}
1411
{{- else }}
15-
{{- $filepath := addLeadingSlash (removeTrailingIndexHTML .FilePath) }}
16-
{{ .MountHandler }}(mux, {{ range .RequestPaths }}{{if ne . $filepath }}goahttp.Replace("", "{{ $filepath }}", {{ end }}{{ end }}h.{{ .VarName }}){{ range .RequestPaths }}{{ if ne . $filepath }}){{ end}}{{ end }}
12+
{{- $mountHandler := .MountHandler }}
13+
{{- $varName := .VarName }}
14+
{{- $isDir := .IsDir }}
15+
{{- range .RequestPaths }}
16+
{{- $stripped := addLeadingSlash . }}
17+
{{- if not $isDir }}
18+
{{- $stripped = (dir $stripped) }}
19+
{{- end }}
20+
{{- if eq $stripped "/" }}
21+
{{ $mountHandler }}(mux, h.{{ $varName }})
22+
{{- else }}
23+
{{ $mountHandler }}(mux, http.StripPrefix("{{ $stripped }}", h.{{ $varName }}))
24+
{{- end }}
25+
{{- end }}
1726
{{- end }}
1827
{{- end }}
1928
}

http/codegen/testdata/server_init_functions.go

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -69,17 +69,20 @@ func New(
6969
if fileSystemPathToFile1JSON == nil {
7070
fileSystemPathToFile1JSON = http.Dir(".")
7171
}
72+
fileSystemPathToFile1JSON = appendPrefix(fileSystemPathToFile1JSON, "/path/to")
7273
if fileSystemPathToFile2JSON == nil {
7374
fileSystemPathToFile2JSON = http.Dir(".")
7475
}
76+
fileSystemPathToFile2JSON = appendPrefix(fileSystemPathToFile2JSON, "/path/to")
7577
if fileSystemPathToFile3JSON == nil {
7678
fileSystemPathToFile3JSON = http.Dir(".")
7779
}
80+
fileSystemPathToFile3JSON = appendPrefix(fileSystemPathToFile3JSON, "/path/to")
7881
return &Server{
7982
Mounts: []*MountPoint{
80-
{"/path/to/file1.json", "GET", "/server_file_server/file1.json"},
81-
{"/path/to/file2.json", "GET", "/server_file_server/file2.json"},
82-
{"/path/to/file3.json", "GET", "/server_file_server/file3.json"},
83+
{"Serve /path/to/file1.json", "GET", "/server_file_server/file1.json"},
84+
{"Serve /path/to/file2.json", "GET", "/server_file_server/file2.json"},
85+
{"Serve /path/to/file3.json", "GET", "/server_file_server/file3.json"},
8386
},
8487
PathToFile1JSON: http.FileServer(fileSystemPathToFile1JSON),
8588
PathToFile2JSON: http.FileServer(fileSystemPathToFile2JSON),
@@ -107,15 +110,17 @@ func New(
107110
if fileSystemPathToFile1JSON == nil {
108111
fileSystemPathToFile1JSON = http.Dir(".")
109112
}
113+
fileSystemPathToFile1JSON = appendPrefix(fileSystemPathToFile1JSON, "/path/to")
110114
if fileSystemPathToFile2JSON == nil {
111115
fileSystemPathToFile2JSON = http.Dir(".")
112116
}
117+
fileSystemPathToFile2JSON = appendPrefix(fileSystemPathToFile2JSON, "/path/to")
113118
return &Server{
114119
Mounts: []*MountPoint{
115120
{"MethodMixed1", "GET", "/resources1/{id}"},
116121
{"MethodMixed2", "GET", "/resources2/{id}"},
117-
{"/path/to/file1.json", "GET", "/file1.json"},
118-
{"/path/to/file2.json", "GET", "/file2.json"},
122+
{"Serve /path/to/file1.json", "GET", "/file1.json"},
123+
{"Serve /path/to/file2.json", "GET", "/file2.json"},
119124
},
120125
MethodMixed1: NewMethodMixed1Handler(e.MethodMixed1, mux, decoder, encoder, errhandler, formatter),
121126
MethodMixed2: NewMethodMixed2Handler(e.MethodMixed2, mux, decoder, encoder, errhandler, formatter),
@@ -179,10 +184,10 @@ func New(
179184

180185
var ServerMultipleFilesConstructorCode = `// Mount configures the mux to serve the ServiceFileServer endpoints.
181186
func Mount(mux goahttp.Muxer, h *Server) {
182-
MountPathToFileJSON(mux, goahttp.Replace("", "/path/to/file.json", h.PathToFileJSON))
183-
MountPathToFileJSON2(mux, goahttp.Replace("", "/path/to/file.json", h.PathToFileJSON2))
187+
MountPathToFileJSON(mux, h.PathToFileJSON)
188+
MountPathToFileJSON2(mux, h.PathToFileJSON2)
184189
MountFileJSON(mux, h.FileJSON)
185-
MountPathToFolder(mux, goahttp.Replace("/", "/path/to/folder", h.PathToFolder))
190+
MountPathToFolder(mux, h.PathToFolder)
186191
}
187192
188193
// Mount configures the mux to serve the ServiceFileServer endpoints.
@@ -193,10 +198,10 @@ func (s *Server) Mount(mux goahttp.Muxer) {
193198

194199
var ServerMultipleFilesWithPrefixPathConstructorCode = `// Mount configures the mux to serve the ServiceFileServer endpoints.
195200
func Mount(mux goahttp.Muxer, h *Server) {
196-
MountPathToFileJSON(mux, goahttp.Replace("", "/path/to/file.json", h.PathToFileJSON))
197-
MountPathToFileJSON2(mux, goahttp.Replace("", "/path/to/file.json", h.PathToFileJSON2))
198-
MountFileJSON(mux, goahttp.Replace("", "/file.json", h.FileJSON))
199-
MountPathToFolder(mux, goahttp.Replace("/server_file_server", "/path/to/folder", h.PathToFolder))
201+
MountPathToFileJSON(mux, http.StripPrefix("/server_file_server", h.PathToFileJSON))
202+
MountPathToFileJSON2(mux, h.PathToFileJSON2)
203+
MountFileJSON(mux, http.StripPrefix("/server_file_server", h.FileJSON))
204+
MountPathToFolder(mux, http.StripPrefix("/server_file_server", h.PathToFolder))
200205
}
201206
202207
// Mount configures the mux to serve the ServiceFileServer endpoints.
@@ -210,9 +215,9 @@ func Mount(mux goahttp.Muxer, h *Server) {
210215
MountPathToFileJSON(mux, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
211216
http.Redirect(w, r, "/redirect/dest", http.StatusMovedPermanently)
212217
}))
213-
MountPathToFileJSON2(mux, goahttp.Replace("", "/path/to/file.json", h.PathToFileJSON2))
218+
MountPathToFileJSON2(mux, h.PathToFileJSON2)
214219
MountFileJSON(mux, h.FileJSON)
215-
MountPathToFolder(mux, goahttp.Replace("/", "/path/to/folder", h.PathToFolder))
220+
MountPathToFolder(mux, h.PathToFolder)
216221
}
217222
218223
// Mount configures the mux to serve the ServiceFileServer endpoints.

http/server.go

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ package http
22

33
import (
44
"net/http"
5-
"net/url"
6-
"strings"
75
)
86

97
type (
@@ -38,32 +36,3 @@ func (s Servers) Mount(mux Muxer) {
3836
m.Mount(mux)
3937
}
4038
}
41-
42-
// Replace returns a handler that serves HTTP requests by replacing the
43-
// request URL's Path (and RawPath if set) and invoking the handler h.
44-
// The logic is the same as the standard http package StripPrefix function.
45-
func Replace(old, nw string, h http.Handler) http.Handler {
46-
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
47-
var p, rp string
48-
if old != "" {
49-
p = strings.Replace(r.URL.Path, old, nw, 1)
50-
rp = strings.Replace(r.URL.RawPath, old, nw, 1)
51-
} else {
52-
p = nw
53-
if r.URL.RawPath != "" {
54-
rp = nw
55-
}
56-
}
57-
if p != r.URL.Path && (r.URL.RawPath == "" || rp != r.URL.RawPath) {
58-
r2 := new(http.Request)
59-
*r2 = *r
60-
r2.URL = new(url.URL)
61-
*r2.URL = *r.URL
62-
r2.URL.Path = p
63-
r2.URL.RawPath = rp
64-
h.ServeHTTP(w, r2)
65-
} else {
66-
http.NotFound(w, r)
67-
}
68-
})
69-
}

http/server_test.go

Lines changed: 0 additions & 63 deletions
This file was deleted.

0 commit comments

Comments
 (0)