From bb61c131c1c7d1cddf42f81d0e5e0015357a2907 Mon Sep 17 00:00:00 2001 From: x1unix Date: Thu, 10 Mar 2022 01:11:12 +0100 Subject: [PATCH 1/2] server: serve 404 status code if page not found --- cmd/playground/main.go | 13 +++++++- pkg/langserver/spa.go | 70 ++++++++++++++++++++++++++++++++++-------- 2 files changed, 69 insertions(+), 14 deletions(-) diff --git a/cmd/playground/main.go b/cmd/playground/main.go index a8accd1c..1b5ee2ff 100644 --- a/cmd/playground/main.go +++ b/cmd/playground/main.go @@ -103,9 +103,20 @@ func start(goRoot string, args appArgs) error { r := mux.NewRouter() pg := goplay.NewClient(args.playgroundUrl, goplay.DefaultUserAgent, 15*time.Second) + + // API routes langserver.New(Version, pg, packages, compiler.NewBuildService(zap.S(), store)). Mount(r.PathPrefix("/api").Subrouter()) - r.PathPrefix("/").Handler(langserver.SpaFileServer("./public")) + + // Web UI routes + indexHandler := langserver.NewIndexFileServer("./public") + spaHandler := langserver.NewSpaFileServer("./public") + r.Path("/"). + Handler(indexHandler) + r.Path("/snippet/{snippetID:[A-Za-z0-9_-]+}"). + Handler(indexHandler) + r.PathPrefix("/"). + Handler(spaHandler) var handler http.Handler if args.debug { diff --git a/pkg/langserver/spa.go b/pkg/langserver/spa.go index c35a7d14..13962ba3 100644 --- a/pkg/langserver/spa.go +++ b/pkg/langserver/spa.go @@ -8,16 +8,45 @@ import ( "strings" ) -// Advanced static server -type spaFileServer struct { +const ( + IndexFileName = "index.html" + NotFoundFileName = "404.html" +) + +type httpStatusInterceptor struct { + http.ResponseWriter + desiredStatus int +} + +func (i httpStatusInterceptor) WriteHeader(_ int) { + i.ResponseWriter.WriteHeader(i.desiredStatus) +} + +type IndexFileServer struct { + indexFilePath string +} + +// NewIndexFileServer returns handler which serves index.html page from root. +func NewIndexFileServer(root http.Dir) *IndexFileServer { + return &IndexFileServer{ + indexFilePath: filepath.Join(string(root), IndexFileName), + } +} + +func (fs IndexFileServer) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + http.ServeFile(rw, r, fs.indexFilePath) +} + +// SpaFileServer is a wrapper around http.FileServer for serving SPA contents. +type SpaFileServer struct { root http.Dir - NotFoundHandler func(http.ResponseWriter, *http.Request) + NotFoundHandler http.Handler } // ServeHTTP implements http.Handler -func (fs *spaFileServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { +func (fs *SpaFileServer) ServeHTTP(rw http.ResponseWriter, r *http.Request) { if containsDotDot(r.URL.Path) { - Errorf(http.StatusNotFound, "Not Found").WriteResponse(w) + Errorf(http.StatusNotFound, "Not Found").WriteResponse(rw) return } @@ -39,16 +68,14 @@ func (fs *spaFileServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { name := path.Join(dir, filepath.FromSlash(upath)) //check if file exists - f, err := os.Open(name) - if err != nil { + if _, err := os.Stat(name); err != nil { if os.IsNotExist(err) { - http.ServeFile(w, r, string(fs.root)+"/index.html") + fs.NotFoundHandler.ServeHTTP(rw, r) return } } - defer f.Close() - http.ServeFile(w, r, name) + http.ServeFile(rw, r, name) } func containsDotDot(v string) bool { @@ -65,7 +92,24 @@ func containsDotDot(v string) bool { func isSlashRune(r rune) bool { return r == '/' || r == '\\' } -// SpaFileServer returns SPA handler -func SpaFileServer(root http.Dir) http.Handler { - return &spaFileServer{root: root} +// NewSpaFileServer returns SPA handler +func NewSpaFileServer(root http.Dir) *SpaFileServer { + notFoundHandler := NewFileServerWithStatus(filepath.Join(string(root), NotFoundFileName), http.StatusNotFound) + return &SpaFileServer{ + NotFoundHandler: notFoundHandler, + root: root, + } +} + +// NewFileServerWithStatus returns http.Handler which serves specified file with desired HTTP status +func NewFileServerWithStatus(name string, code int) http.HandlerFunc { + return func(rw http.ResponseWriter, r *http.Request) { + ServeFileWithStatus(rw, r, name, code) + } +} + +// ServeFileWithStatus serves file in HTTP response with specified HTTP status. +func ServeFileWithStatus(rw http.ResponseWriter, r *http.Request, name string, code int) { + interceptor := httpStatusInterceptor{desiredStatus: code, ResponseWriter: rw} + http.ServeFile(interceptor, r, name) } From f8028dc8e971472a564050f4378b8f9d31168481 Mon Sep 17 00:00:00 2001 From: x1unix Date: Thu, 10 Mar 2022 02:16:57 +0100 Subject: [PATCH 2/2] web: add 404 page template --- web/public/404.html | 203 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 web/public/404.html diff --git a/web/public/404.html b/web/public/404.html new file mode 100644 index 00000000..684f831a --- /dev/null +++ b/web/public/404.html @@ -0,0 +1,203 @@ + + + + + 🤔 404 Not Found! + + + +
+
+

404!

+

Page Not Found

+
+

+ Requested page does not exist or was deleted. +

+

+ That's all we know 🤷 +

+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + \ No newline at end of file