From 39ce592d342dad7f0dafaa8ee14434accc961925 Mon Sep 17 00:00:00 2001 From: Francesco <110564842+playeruan@users.noreply.github.com> Date: Sun, 3 Aug 2025 21:26:29 +0200 Subject: [PATCH 1/3] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b902b66..f7e3d1b 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ miniws (minimalist web sever) is a very simple web server written in golang. its ## how to configure in your config folder you will find `ipfilter.conf` and `useragentfilter.conf` -both files use the same format: specify `allow|deny` in the first row to tell miniws to treat the file as a whitelist or a blacklist, then specify one ip/user-agent per row. +both files use the same format: specify `allow|deny` in the first row to tell miniws to treat the file as a whitelist or a blacklist, then specify one ip/user-agent per line. ## logging From 455390499d3a691bc9f1eca8a47a2233848f32c1 Mon Sep 17 00:00:00 2001 From: Francesco <110564842+playeruan@users.noreply.github.com> Date: Sun, 3 Aug 2025 21:26:52 +0200 Subject: [PATCH 2/3] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f7e3d1b..aa61d4e 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ miniws (minimalist web sever) is a very simple web server written in golang. its ## how to configure in your config folder you will find `ipfilter.conf` and `useragentfilter.conf` -both files use the same format: specify `allow|deny` in the first row to tell miniws to treat the file as a whitelist or a blacklist, then specify one ip/user-agent per line. +both files use the same format: specify `allow|deny` in the first line to tell miniws to treat the file as a whitelist or a blacklist, then specify one ip/user-agent per line. ## logging From 615f93e788871bd1b454c9af92381c7cbde5e17a Mon Sep 17 00:00:00 2001 From: Solar Date: Wed, 6 Aug 2025 15:01:23 +0200 Subject: [PATCH 3/3] use http.ServeContent, which allows for caching if file hasn't been modified (what I wanted), and other features --- Dockerfile | 8 -------- miniws/webserver.go | 38 +++++++++++++++++++++++++++++--------- 2 files changed, 29 insertions(+), 17 deletions(-) delete mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index a7dc50e..0000000 --- a/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -FROM fedora:43 -SHELL [ "/bin/bash", "-c" ] -COPY build/miniws /bin/miniws -RUN mkdir -p "/data/{logs,config}" -RUN ls "/data" -EXPOSE 8040/tcp -ENTRYPOINT [ "/bin/miniws", "--port", "8040", "--logs-folder", "/data/logs", \ - "--config-folder", "/data/config" ] \ No newline at end of file diff --git a/miniws/webserver.go b/miniws/webserver.go index 7edda33..987c4be 100644 --- a/miniws/webserver.go +++ b/miniws/webserver.go @@ -128,19 +128,34 @@ func (ws *WebServer) isUserAgentValid(userAgent string) bool { } } -func (ws *WebServer) fetchFileContents(filepath string) ([]byte, error) { +// fetchFile is a safe wrapper for os.OpenFile, which sanitizes the +// provided filepath, and, if a folder is passed, it looks for an +// index.html to fetch. +// +// IMPORTANT: remember to close the file after use!!!! fetchFile doesn't +// do it for you for obvious reasons +func (ws *WebServer) fetchFile(filepath string) (*os.File, error) { + return os.OpenFile(ws._cleanFilepath(filepath), os.O_RDONLY, 0) +} + +func (ws *WebServer) fetchStat(filepath string) (os.FileInfo, error) { + clean_filepath := ws._cleanFilepath(filepath) + return os.Stat(clean_filepath) +} + +func (ws *WebServer) _cleanFilepath(filepath string) string { if filepath == "/" { filepath = "." } fileinfo, err := os.Stat(filepath) if err != nil { - return nil, err + ws.logger.logError(err.Error()) + return "" } if fileinfo.IsDir() { filepath += "/index.html" } - return os.ReadFile(filepath) - + return filepath } func (ws *WebServer) get(writer http.ResponseWriter, req *http.Request) { @@ -164,19 +179,24 @@ func (ws *WebServer) get(writer http.ResponseWriter, req *http.Request) { return } - fetchedData, fetchErr := ws.fetchFileContents(ensureSlashSuffix(ws.wwwFolder) + strings.TrimPrefix(req.URL.Path, "/")) + fileToFetch := ensureSlashSuffix(ws.wwwFolder) + strings.TrimPrefix(req.URL.Path, "/") + fetchedFile, fetchErr := ws.fetchFile(fileToFetch) + fetchedFileStat, _ := fetchedFile.Stat() + fetchedStat, _ := ws.fetchStat(fileToFetch) - sentBytes := 0 + sentBytes := int64(0) if ws.logger.logIfError(fetchErr) { respStatusCode = http.StatusNotFound writer.WriteHeader(respStatusCode) } else { + http.ServeContent(writer, req, fileToFetch, fetchedStat.ModTime(), fetchedFile) writer.Header().Add("Content-Type", mime.TypeByExtension(filepath.Ext(req.URL.Path))) - sentBytesCount, _ := writer.Write(fetchedData) - sentBytes = sentBytesCount + sentBytes = fetchedFileStat.Size() + fetchedFile.Close() } + // this thing writes to the log using the NCSA Combined Log Format ws.logger.logAccess( strings.Split(req.RemoteAddr, ":")[0], //remote address "-", //identifier (can't get) @@ -184,7 +204,7 @@ func (ws *WebServer) get(writer http.ResponseWriter, req *http.Request) { time.Now().Format("02/Jan/2006:15:04:05 -0700"), //timestamp req.Method+" "+req.URL.Path+" "+getHttpVersionString(req.ProtoMajor, req.ProtoMinor), //HTTP version strconv.Itoa(respStatusCode), //response code - strconv.Itoa(sentBytes), //# of sent bytes + strconv.Itoa(int(sentBytes)), //# of sent bytes req.Referer(), //Referer req.UserAgent(), //User Agent )