diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..919ce1f --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..a55e7a1 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..639900d --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..2574df2 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/spa-server.iml b/.idea/spa-server.iml new file mode 100644 index 0000000..25ed3f6 --- /dev/null +++ b/.idea/spa-server.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 403f7ca..ae197c2 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,21 @@ spa-server Simple static file server for single-page application +This fork introduces a bunch of configuration and code cleanup. You can specify a custom listening hostname and port, +as well as custom base directory for the files. It is as simple as: + +```sh +PORT=3000 HOST=127.0.0.1 BASE_DIRECTORY=/home/ubuntu/application spa-server +``` + +Please build the binary yourself. + +```sh +# Assuming you already have Go +go build . +./spa-server +``` + ```sh $ tree . diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..13cf7ad --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module spa-server + +go 1.20 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e69de29 diff --git a/main.go b/main.go index 1116cd2..19e9535 100644 --- a/main.go +++ b/main.go @@ -1,61 +1,94 @@ package main import ( - "flag" - "fmt" + "context" + "errors" "log" "mime" + "net" "net/http" "os" + "os/signal" + "path" "path/filepath" + "time" ) +var baseDirectory string + func handler(w http.ResponseWriter, r *http.Request) { - path := "." + r.URL.Path - fmt.Print(r.URL.Path) - file, err := os.Stat(path) + filePath := path.Join(baseDirectory, r.URL.Path) + file, err := os.Stat(filePath) if err == nil && !file.IsDir() { // file exists - gz := path + ".gz" + gz := filePath + ".gz" file, err := os.Stat(gz) - t := mime.TypeByExtension(filepath.Ext(path)) + t := mime.TypeByExtension(filepath.Ext(filePath)) if err == nil && !file.IsDir() && t != "" { - fmt.Println(" => " + gz) w.Header().Add("Content-Encoding", "gzip") w.Header().Add("Content-Type", t) http.ServeFile(w, r, gz) } else { - fmt.Println(" => " + path) - http.ServeFile(w, r, path) + http.ServeFile(w, r, filePath) } } else { // file does not exist - index := "index.html" + index := path.Join(baseDirectory, "index.html") file, err := os.Stat(index) if err == nil && !file.IsDir() { // index.html exists - fmt.Println(" => " + index) http.ServeFile(w, r, index) } else { // index.html does not exist - fmt.Println(" => NotFound") http.NotFound(w, r) } } } func main() { - - port := ":5050" - flag.Parse() - if flag.NArg() != 0 { - port = ":" + flag.Arg(0) + listeningPort, ok := os.LookupEnv("PORT") + if !ok { + listeningPort = "5050" } - fmt.Println("spa-server starting on localhost" + port) - http.HandleFunc("/", handler) - err := http.ListenAndServe(port, nil) - if err != nil { - log.Fatal("spa-server: ", err) + listeningHost, ok := os.LookupEnv("HOST") + if !ok { + listeningHost = "127.0.0.1" + } + + baseDirectory, ok = os.LookupEnv("BASE_DIRECTORY") + if !ok { + baseDirectory = "." + } + + router := http.NewServeMux() + router.HandleFunc("/", handler) + + server := &http.Server{ + Addr: net.JoinHostPort(listeningHost, listeningPort), + Handler: router, + } + + exitSignal := make(chan os.Signal, 1) + signal.Notify(exitSignal, os.Interrupt) + + go func() { + <-exitSignal + + log.Println("Received shutdown signal, shutting down") + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + err := server.Shutdown(ctx) + if err != nil { + log.Printf("Error occured during shutting down HTTP server: %s", err.Error()) + } + }() + + log.Printf("HTTP server listening on %s", server.Addr) + + err := server.ListenAndServe() + if err != nil && !errors.Is(err, http.ErrServerClosed) { + log.Fatalf("Error occured during listening to HTTP server: %s", err.Error()) } }