small fixes

This commit is contained in:
Altair-sh
2026-11-03 11:51:54 +01:00
parent 86dce6584b
commit 793cbcc37f
6 changed files with 34 additions and 16 deletions
+2
View File
@@ -38,6 +38,8 @@ func (conn *Connection) CreateResponse(
Status: status, Status: status,
Headers: HeadersMap{ Headers: HeadersMap{
"date": time.Now().UTC().Format(TimeFormat), "date": time.Now().UTC().Format(TimeFormat),
// without it browser may remember response and not resend it
"cache-control": "no-store",
}, },
} }
+1
View File
@@ -18,6 +18,7 @@ type Shares []*SharedLocation
type Config struct { type Config struct {
Addr string `toml:"address"` Addr string `toml:"address"`
MaxUpload int64 `toml:"max_upload"` MaxUpload int64 `toml:"max_upload"`
HtmlTitle string `toml:"html_title"`
// sorted by Url in reverse to allow this pattern: // sorted by Url in reverse to allow this pattern:
// shared/private // shared/private
// shared/ // shared/
+9 -5
View File
@@ -56,26 +56,30 @@ func (sh *SharedLocation) normalize(configDir string) error {
func (sh *SharedLocation) String() string { func (sh *SharedLocation) String() string {
var r, w string var r, w string
if sh.ReadPassword == "" { if sh.ReadPassword == "" {
r = "public" r = "pub"
} else { } else {
r = "password" r = "pas"
} }
if sh.WritePassword == "" { if sh.WritePassword == "" {
w = "no " w = "no "
} else { } else {
w = "password" w = "pas"
} }
access := "{ " + "r:" + r + ", w:" + w + " }"
url := sh.Url url := sh.Url
if url == "" { if url == "" {
url = "/" url = "/"
} }
return "share '" + url + "' r:" + r + " w:" + w + return "share " + access + " '" + url + "' -> '" + sh.Path + "'"
" -> '" + sh.Path + "'"
} }
func (sh *SharedLocation) mkdir() error { func (sh *SharedLocation) mkdir() error {
_, err := os.Stat(sh.Path)
if err == os.ErrNotExist {
return os.MkdirAll(sh.Path, 0o755) return os.MkdirAll(sh.Path, 0o755)
} }
return err
}
type UrlStatus uint32 type UrlStatus uint32
+5 -2
View File
@@ -164,8 +164,8 @@ func (conn *connection) serveFile(filePath string) *http.Response {
// if not truncate, mtime will always be > ifModifiedSince // if not truncate, mtime will always be > ifModifiedSince
mtime := finf.ModTime().UTC().Truncate(time.Second) mtime := finf.ModTime().UTC().Truncate(time.Second)
ifModifiedSince, errTimeParse := time.Parse(http.TimeFormat, conn.req.Headers["if-modified-since"]) ifModifiedSince, errTime := time.Parse(http.TimeFormat, conn.req.Headers["if-modified-since"])
notModified := errTimeParse == nil && !mtime.After(ifModifiedSince) notModified := errTime == nil && !mtime.After(ifModifiedSince)
if notModified { if notModified {
return conn.http.CreateResponse(http.StatusNotModified, conn.req, false) return conn.http.CreateResponse(http.StatusNotModified, conn.req, false)
} }
@@ -178,6 +178,9 @@ func (conn *connection) serveFile(filePath string) *http.Response {
fileOwnershipTransferred = true fileOwnershipTransferred = true
res.AttachBodyReader(f, size, http.GetMimeType(fileName)) res.AttachBodyReader(f, size, http.GetMimeType(fileName))
} }
// Tell browser to not use its own caching, remember last-modified
// On next request browser will send if-modified-since and let server check if file changed.
res.Headers["cache-control"] = "no-cache"
res.Headers["last-modified"] = mtime.Format(http.TimeFormat) res.Headers["last-modified"] = mtime.Format(http.TimeFormat)
res.Headers["content-disposition"] = http.ContentDisposition("inline", fileName) res.Headers["content-disposition"] = http.ContentDisposition("inline", fileName)
//TODO: accept "range" header and return StatusPartialContent //TODO: accept "range" header and return StatusPartialContent
+7 -6
View File
@@ -41,7 +41,8 @@ type listingRow struct {
} }
func (conn *connection) serveDirListing(dirPath string) *http.Response { func (conn *connection) serveDirListing(dirPath string) *http.Response {
htmlBytes, err := genDirListingHtml(&conn.log, dirPath, conn.req.Url) htmlBytes, err := genDirListingHtml(&conn.log, dirPath,
conn.req.Url, conn.srv.Conf.HtmlTitle)
if err != nil { if err != nil {
return conn.createErrorResponse(false, http.StatusInternalServerError, err.Error()) return conn.createErrorResponse(false, http.StatusInternalServerError, err.Error())
} }
@@ -51,7 +52,7 @@ func (conn *connection) serveDirListing(dirPath string) *http.Response {
return res return res
} }
func genDirListingHtml(logger *log.LoggerContext, dirPath, reqUrl string) ([]byte, error) { func genDirListingHtml(logger *log.LoggerContext, dirPath, reqUrl, pageTitle string) ([]byte, error) {
dirEntries, err := os.ReadDir(dirPath) dirEntries, err := os.ReadDir(dirPath)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -89,9 +90,9 @@ func genDirListingHtml(logger *log.LoggerContext, dirPath, reqUrl string) ([]byt
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>`) <title>`)
reqUrlHtmlEscaped := htmlEscape(reqUrl) sb.AddS(htmlEscape(pageTitle + " " + reqUrl))
sb.AddS(reqUrlHtmlEscaped) sb.AddS(`
sb.AddS(`</title> </title>
<style> <style>
body { font-family: sans-serif; margin: 2em; } body { font-family: sans-serif; margin: 2em; }
td { padding: .2em 1em .2em 0; } td { padding: .2em 1em .2em 0; }
@@ -99,7 +100,7 @@ func genDirListingHtml(logger *log.LoggerContext, dirPath, reqUrl string) ([]byt
</style> </style>
</head> </head>
<body>`) <body>`)
sb.AddS("<h1>").AddS(reqUrlHtmlEscaped).AddS("</h1>") sb.AddS("<h1>").AddS(htmlEscape(reqUrl)).AddS("</h1>")
sb.AddS(`<table id="listing">`) sb.AddS(`<table id="listing">`)
sb.AddS(`<tr><td><a href="`) sb.AddS(`<tr><td><a href="`)
sb.AddS(http.PercentEcodeUrl(path.Dir(reqUrl))) sb.AddS(http.PercentEcodeUrl(path.Dir(reqUrl)))
+8 -1
View File
@@ -2,6 +2,7 @@
# The file contains passwords: make it readable only by the user serveg runs as (chmod 600). # The file contains passwords: make it readable only by the user serveg runs as (chmod 600).
address = "127.0.0.1:8080" address = "127.0.0.1:8080"
max_upload = 4_294_967_296 max_upload = 4_294_967_296
html_title = "serveg"
# Each [[share]] makes a directory available under an URL prefix. # Each [[share]] makes a directory available under an URL prefix.
# Missing read_password: anyone can download. # Missing read_password: anyone can download.
@@ -12,6 +13,7 @@ max_upload = 4_294_967_296
url = "/public" # http://address/public/... url = "/public" # http://address/public/...
path = "share/public" # relative paths are relative to this config file path = "share/public" # relative paths are relative to this config file
write_password = "change-me" write_password = "change-me"
# read:public, write:password
[[share]] [[share]]
url = "/private" url = "/private"
@@ -21,4 +23,9 @@ write_password = "change-me-three"
[[share]] [[share]]
url = "/" url = "/"
path = "share/index.html" # redirect url to file path = "share/index.html" # you may add links to shared directories here
# read:public, write:no
[[share]]
url = "/favicon.ico"
path = "share/favicon.ico" # add favicon