small fixes
This commit is contained in:
@@ -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",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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/
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
Reference in New Issue
Block a user