moved Read/Send code to http.Connection

This commit is contained in:
Altair-sh
2026-11-04 03:32:36 +01:00
parent 59f7d873be
commit 0ecbb629ea
10 changed files with 297 additions and 274 deletions
+89
View File
@@ -0,0 +1,89 @@
package server
import (
"fmt"
"os"
"path"
"serveg/internal/html"
"serveg/internal/http"
"time"
)
func (conn *TcpConnection) serveFileOrDir(req *http.Request) *http.Response {
path, share, urlStatus := conn.srv.Conf.Share.MapUrl(req.Url)
// TODO implement authorization
_ = share
switch urlStatus {
case UrlFile:
return conn.serveFile(req, path)
case UrlDir:
return conn.serveDirListing(req, path)
case UrlNotFound:
return conn.createErrorResponse(req, false, http.StatusNotFound, path)
case UrlForbidden:
return conn.createErrorResponse(req, false, http.StatusForbidden, path)
case UrlIncorrect:
return conn.createErrorResponse(req, false, http.StatusBadRequest,
"invalid url '"+req.Url+"'")
default:
panic(fmt.Sprintf("unsupported UrlStatus: %d", int32(urlStatus)))
}
}
func (conn *TcpConnection) serveDirListing(req *http.Request, dirPath string) *http.Response {
htmlBytes, err := html.GenDirListingHtml(&conn.log, dirPath,
req.Url, conn.srv.Conf.HtmlTitle)
if err != nil {
return conn.createErrorResponse(req, false, http.StatusInternalServerError, err.Error())
}
return conn.http.CreateResponse(http.StatusOK, req, false).
WithBodyBuf(htmlBytes, http.TextHtml)
}
func (conn *TcpConnection) serveFile(req *http.Request, filePath string) *http.Response {
f, err := os.Open(filePath)
if err != nil {
return conn.createErrorResponse(req, false, http.StatusInternalServerError, err.Error())
}
var fileOwnershipTransferred bool
defer func() {
if !fileOwnershipTransferred {
_ = f.Close()
}
}()
finf, err := f.Stat()
if err != nil {
return conn.createErrorResponse(req, false, http.StatusInternalServerError, err.Error())
}
size := finf.Size()
// time is received with second precision
// if not truncate, mtime will always be > ifModifiedSince
mtime := finf.ModTime().UTC().Truncate(time.Second)
ifModifiedSince, errTime := time.Parse(http.HeaderTimeFormat, req.Headers["if-modified-since"])
notModified := errTime == nil && !mtime.After(ifModifiedSince)
if notModified {
return conn.http.CreateResponse(http.StatusNotModified, req, false)
}
res := conn.http.CreateResponse(http.StatusOK, req, false)
fileName := path.Base(filePath)
if req.Method != "HEAD" {
// file will be closed by SendResponse()
fileOwnershipTransferred = true
res.WithBodyReader(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.HeaderTimeFormat)
res.Headers["content-disposition"] = http.ContentDisposition("inline", fileName)
//TODO: accept "range" header and return StatusPartialContent
// res.Headers["accept-ranges"] = "bytes"
return res
}