diff --git a/drivers/quqi/driver.go b/drivers/quqi/driver.go index 0fa64041..36758bd1 100644 --- a/drivers/quqi/driver.go +++ b/drivers/quqi/driver.go @@ -316,7 +316,7 @@ func (d *Quqi) Put(ctx context.Context, dstDir model.Obj, stream model.FileStrea // if the file already exists in Quqi server, there is no need to actually upload it if uploadInitResp.Data.Exist { // the file name returned by Quqi does not include the extension name - nodeName, nodeExt := uploadInitResp.Data.NodeName, rawExt(stream.GetName()) + nodeName, nodeExt := uploadInitResp.Data.NodeName, utils.Ext(stream.GetName()) if nodeExt != "" { nodeName = nodeName + "." + nodeExt } @@ -432,7 +432,7 @@ func (d *Quqi) Put(ctx context.Context, dstDir model.Obj, stream model.FileStrea return nil, err } // the file name returned by Quqi does not include the extension name - nodeName, nodeExt := uploadFinishResp.Data.NodeName, rawExt(stream.GetName()) + nodeName, nodeExt := uploadFinishResp.Data.NodeName, utils.Ext(stream.GetName()) if nodeExt != "" { nodeName = nodeName + "." + nodeExt } diff --git a/drivers/quqi/util.go b/drivers/quqi/util.go index 5ad43c4b..aa184d70 100644 --- a/drivers/quqi/util.go +++ b/drivers/quqi/util.go @@ -9,7 +9,6 @@ import ( "io" "net/http" "net/url" - stdpath "path" "strings" "time" @@ -115,16 +114,6 @@ func (d *Quqi) checkLogin() bool { return true } -// rawExt 保留扩展名大小写 -func rawExt(name string) string { - ext := stdpath.Ext(name) - if strings.HasPrefix(ext, ".") { - ext = ext[1:] - } - - return ext -} - // decryptKey 获取密码 func decryptKey(encodeKey string) []byte { // 移除非法字符 diff --git a/drivers/vtencent/util.go b/drivers/vtencent/util.go index 91db54b7..4ba72d1b 100644 --- a/drivers/vtencent/util.go +++ b/drivers/vtencent/util.go @@ -8,9 +8,7 @@ import ( "fmt" "io" "net/http" - "path" "strconv" - "strings" "github.com/alist-org/alist/v3/drivers/base" "github.com/alist-org/alist/v3/internal/driver" @@ -151,7 +149,7 @@ func (d *Vtencent) ApplyUploadUGC(signature string, stream model.FileStreamer) ( form := base.Json{ "signature": signature, "videoName": stream.GetName(), - "videoType": strings.ReplaceAll(path.Ext(stream.GetName()), ".", ""), + "videoType": utils.Ext(stream.GetName()), "videoSize": stream.GetSize(), } var resps RspApplyUploadUGC diff --git a/internal/net/request.go b/internal/net/request.go index c9ef363f..d4f9321c 100644 --- a/internal/net/request.go +++ b/internal/net/request.go @@ -619,10 +619,9 @@ type Buf struct { // NewBuf is a buffer that can have 1 read & 1 write at the same time. // when read is faster write, immediately feed data to read after written func NewBuf(ctx context.Context, maxSize int) *Buf { - d := make([]byte, 0, maxSize) return &Buf{ ctx: ctx, - buffer: bytes.NewBuffer(d), + buffer: bytes.NewBuffer(make([]byte, 0, maxSize)), size: maxSize, } } @@ -677,5 +676,5 @@ func (br *Buf) Write(p []byte) (n int, err error) { } func (br *Buf) Close() { - br.buffer.Reset() + br.buffer = nil } diff --git a/pkg/utils/path.go b/pkg/utils/path.go index c0793a3e..135f8e4e 100644 --- a/pkg/utils/path.go +++ b/pkg/utils/path.go @@ -45,7 +45,7 @@ func IsSubPath(path string, subPath string) bool { func Ext(path string) string { ext := stdpath.Ext(path) - if strings.HasPrefix(ext, ".") { + if len(ext) > 0 && ext[0] == '.' { ext = ext[1:] } return strings.ToLower(ext) diff --git a/server/common/proxy.go b/server/common/proxy.go index 00fee4b2..c14af6fa 100644 --- a/server/common/proxy.go +++ b/server/common/proxy.go @@ -1,97 +1,25 @@ package common import ( - "bytes" "context" "fmt" "io" "net/http" "net/url" "os" - "strconv" "strings" - "github.com/alist-org/alist/v3/internal/conf" + "maps" + "github.com/alist-org/alist/v3/internal/model" "github.com/alist-org/alist/v3/internal/net" - "github.com/alist-org/alist/v3/internal/setting" "github.com/alist-org/alist/v3/internal/stream" "github.com/alist-org/alist/v3/pkg/http_range" "github.com/alist-org/alist/v3/pkg/utils" - "github.com/microcosm-cc/bluemonday" log "github.com/sirupsen/logrus" - "github.com/yuin/goldmark" ) -func processMarkdown(content []byte) ([]byte, error) { - var buf bytes.Buffer - if err := goldmark.New().Convert(content, &buf); err != nil { - return nil, fmt.Errorf("markdown conversion failed: %w", err) - } - return bluemonday.UGCPolicy().SanitizeBytes(buf.Bytes()), nil -} - func Proxy(w http.ResponseWriter, r *http.Request, link *model.Link, file model.Obj) error { - - //优先处理md文件 - if utils.Ext(file.GetName()) == "md" && setting.GetBool(conf.FilterReadMeScripts) { - var markdownContent []byte - var err error - - if link.MFile != nil { - defer link.MFile.Close() - attachHeader(w, file) - markdownContent, err = io.ReadAll(link.MFile) - if err != nil { - return fmt.Errorf("failed to read markdown content: %w", err) - } - } else if link.RangeReadCloser != nil { - attachHeader(w, file) - rrc, err := link.RangeReadCloser.RangeRead(r.Context(), http_range.Range{Start: 0, Length: -1}) - if err != nil { - return err - } - defer rrc.Close() - markdownContent, err = io.ReadAll(rrc) - if err != nil { - return fmt.Errorf("failed to read markdown content: %w", err) - } - } else { - header := net.ProcessHeader(r.Header, link.Header) - res, err := net.RequestHttp(r.Context(), r.Method, header, link.URL) - if err != nil { - return err - } - defer res.Body.Close() - for h, v := range res.Header { - w.Header()[h] = v - } - w.WriteHeader(res.StatusCode) - if r.Method == http.MethodHead { - return nil - } - markdownContent, err = io.ReadAll(res.Body) - if err != nil { - return fmt.Errorf("failed to read markdown content: %w", err) - } - - } - - safeHTML, err := processMarkdown(markdownContent) - if err != nil { - return err - } - - safeHTMLReader := bytes.NewReader(safeHTML) - w.Header().Set("Content-Length", strconv.FormatInt(int64(len(safeHTML)), 10)) - w.Header().Set("Content-Type", "text/html; charset=utf-8") - _, err = utils.CopyWithBuffer(w, safeHTMLReader) - if err != nil { - return err - } - return nil - } - if link.MFile != nil { defer link.MFile.Close() attachHeader(w, file) @@ -152,9 +80,7 @@ func Proxy(w http.ResponseWriter, r *http.Request, link *model.Link, file model. } defer res.Body.Close() - for h, v := range res.Header { - w.Header()[h] = v - } + maps.Copy(w.Header(), res.Header) w.WriteHeader(res.StatusCode) if r.Method == http.MethodHead { return nil diff --git a/server/handles/down.go b/server/handles/down.go index b2f9a21b..1153881f 100644 --- a/server/handles/down.go +++ b/server/handles/down.go @@ -1,9 +1,12 @@ package handles import ( + "bytes" "fmt" "io" + "net/http" stdpath "path" + "strconv" "strings" "github.com/alist-org/alist/v3/internal/conf" @@ -15,7 +18,9 @@ import ( "github.com/alist-org/alist/v3/pkg/utils" "github.com/alist-org/alist/v3/server/common" "github.com/gin-gonic/gin" + "github.com/microcosm-cc/bluemonday" log "github.com/sirupsen/logrus" + "github.com/yuin/goldmark" ) func Down(c *gin.Context) { @@ -124,7 +129,34 @@ func localProxy(c *gin.Context, link *model.Link, file model.Obj, proxyRange boo if proxyRange { common.ProxyRange(link, file.GetSize()) } - err = common.Proxy(c.Writer, c.Request, link, file) + + //优先处理md文件 + if utils.Ext(file.GetName()) == "md" && setting.GetBool(conf.FilterReadMeScripts) { + w := c.Writer + buf := bytes.NewBuffer(make([]byte, 0, file.GetSize())) + err = common.Proxy(&proxyResponseWriter{ResponseWriter: w, Writer: buf}, c.Request, link, file) + if err == nil && buf.Len() > 0 { + if w.Status() < 200 || w.Status() > 300 { + w.Write(buf.Bytes()) + return + } + + var html bytes.Buffer + if err = goldmark.Convert(buf.Bytes(), &html); err != nil { + err = fmt.Errorf("markdown conversion failed: %w", err) + } else { + buf.Reset() + err = bluemonday.UGCPolicy().SanitizeReaderToWriter(&html, buf) + if err == nil { + w.Header().Set("Content-Length", strconv.FormatInt(int64(buf.Len()), 10)) + w.Header().Set("Content-Type", "text/html; charset=utf-8") + _, err = utils.CopyWithBuffer(c.Writer, buf) + } + } + } + } else { + err = common.Proxy(c.Writer, c.Request, link, file) + } if err != nil { common.ErrorResp(c, err, 500, true) return @@ -150,3 +182,12 @@ func canProxy(storage driver.Driver, filename string) bool { } return false } + +type proxyResponseWriter struct { + http.ResponseWriter + io.Writer +} + +func (pw *proxyResponseWriter) Write(p []byte) (int, error) { + return pw.Writer.Write(p) +}