静态文件分发也支持压缩、WebP转换

This commit is contained in:
GoEdgeLab
2022-06-19 11:39:21 +08:00
parent 562db39b28
commit 111982ab15
2 changed files with 144 additions and 98 deletions

View File

@@ -51,7 +51,7 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
return true return true
} }
rootDir := this.web.Root.Dir var rootDir = this.web.Root.Dir
if this.web.Root.HasVariables() { if this.web.Root.HasVariables() {
rootDir = this.Format(rootDir) rootDir = this.Format(rootDir)
} }
@@ -59,9 +59,9 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
rootDir = Tea.Root + Tea.DS + rootDir rootDir = Tea.Root + Tea.DS + rootDir
} }
requestPath := this.uri var requestPath = this.uri
questionMarkIndex := strings.Index(this.uri, "?") var questionMarkIndex = strings.Index(this.uri, "?")
if questionMarkIndex > -1 { if questionMarkIndex > -1 {
requestPath = this.uri[:questionMarkIndex] requestPath = this.uri[:questionMarkIndex]
} }
@@ -75,7 +75,9 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
if err == nil { if err == nil {
requestPath = p requestPath = p
} else { } else {
logs.Error(err) if !this.canIgnore(err) {
logs.Error(err)
}
} }
} }
@@ -92,8 +94,8 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
} }
} }
filename := strings.Replace(requestPath, "/", Tea.DS, -1) var filename = strings.Replace(requestPath, "/", Tea.DS, -1)
filePath := "" var filePath = ""
if len(filename) > 0 && filename[0:1] == Tea.DS { if len(filename) > 0 && filename[0:1] == Tea.DS {
filePath = rootDir + filename filePath = rootDir + filename
} else { } else {
@@ -113,7 +115,9 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
return return
} else { } else {
this.write50x(err, http.StatusInternalServerError, true) this.write50x(err, http.StatusInternalServerError, true)
logs.Error(err) if !this.canIgnore(err) {
logs.Error(err)
}
return true return true
} }
} }
@@ -142,7 +146,9 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
return return
} else { } else {
this.write50x(err, http.StatusInternalServerError, true) this.write50x(err, http.StatusInternalServerError, true)
logs.Error(err) if !this.canIgnore(err) {
logs.Error(err)
}
return true return true
} }
} }
@@ -152,24 +158,24 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
} }
// 响应header // 响应header
respHeader := this.writer.Header() var respHeader = this.writer.Header()
// mime type // mime type
contentType := "" var contentType = ""
if this.web.ResponseHeaderPolicy == nil || !this.web.ResponseHeaderPolicy.IsOn || !this.web.ResponseHeaderPolicy.ContainsHeader("CONTENT-TYPE") { if this.web.ResponseHeaderPolicy == nil || !this.web.ResponseHeaderPolicy.IsOn || !this.web.ResponseHeaderPolicy.ContainsHeader("CONTENT-TYPE") {
ext := filepath.Ext(filePath) var ext = filepath.Ext(filePath)
if len(ext) > 0 { if len(ext) > 0 {
mimeType := mime.TypeByExtension(ext) mimeType := mime.TypeByExtension(ext)
if len(mimeType) > 0 { if len(mimeType) > 0 {
semicolonIndex := strings.Index(mimeType, ";") var semicolonIndex = strings.Index(mimeType, ";")
mimeTypeKey := mimeType var mimeTypeKey = mimeType
if semicolonIndex > 0 { if semicolonIndex > 0 {
mimeTypeKey = mimeType[:semicolonIndex] mimeTypeKey = mimeType[:semicolonIndex]
} }
if _, found := textMimeMap[mimeTypeKey]; found { if _, found := textMimeMap[mimeTypeKey]; found {
if this.web.Charset != nil && this.web.Charset.IsOn && len(this.web.Charset.Charset) > 0 { if this.web.Charset != nil && this.web.Charset.IsOn && len(this.web.Charset.Charset) > 0 {
charset := this.web.Charset.Charset var charset = this.web.Charset.Charset
if this.web.Charset.IsUpper { if this.web.Charset.IsUpper {
charset = strings.ToUpper(charset) charset = strings.ToUpper(charset)
} }
@@ -197,7 +203,7 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
} }
// 支持 ETag // 支持 ETag
eTag := "\"e" + fmt.Sprintf("%0x", xxhash.Sum64String(filename+strconv.FormatInt(stat.ModTime().UnixNano(), 10)+strconv.FormatInt(stat.Size(), 10))) + "\"" var eTag = "\"e" + fmt.Sprintf("%0x", xxhash.Sum64String(filename+strconv.FormatInt(stat.ModTime().UnixNano(), 10)+strconv.FormatInt(stat.Size(), 10))) + "\""
if len(respHeader.Get("ETag")) == 0 { if len(respHeader.Get("ETag")) == 0 {
respHeader.Set("ETag", eTag) respHeader.Set("ETag", eTag)
} }
@@ -227,7 +233,7 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
// 支持Range // 支持Range
respHeader.Set("Accept-Ranges", "bytes") respHeader.Set("Accept-Ranges", "bytes")
ifRangeHeaders, ok := this.RawReq.Header["If-Range"] ifRangeHeaders, ok := this.RawReq.Header["If-Range"]
supportRange := true var supportRange = true
if ok { if ok {
supportRange = false supportRange = false
for _, v := range ifRangeHeaders { for _, v := range ifRangeHeaders {
@@ -244,7 +250,7 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
// 支持Range // 支持Range
var ranges = []rangeutils.Range{} var ranges = []rangeutils.Range{}
if supportRange { if supportRange {
contentRange := this.RawReq.Header.Get("Range") var contentRange = this.RawReq.Header.Get("Range")
if len(contentRange) > 0 { if len(contentRange) > 0 {
if fileSize == 0 { if fileSize == 0 {
this.processResponseHeaders(http.StatusRequestedRangeNotSatisfiable) this.processResponseHeaders(http.StatusRequestedRangeNotSatisfiable)
@@ -277,7 +283,7 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
respHeader.Set("Content-Length", strconv.FormatInt(fileSize, 10)) respHeader.Set("Content-Length", strconv.FormatInt(fileSize, 10))
} }
reader, err := os.OpenFile(filePath, os.O_RDONLY, 0444) fileReader, err := os.OpenFile(filePath, os.O_RDONLY, 0444)
if err != nil { if err != nil {
this.write50x(err, http.StatusInternalServerError, true) this.write50x(err, http.StatusInternalServerError, true)
return true return true
@@ -291,12 +297,16 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
this.cacheRef = nil // 不支持缓存 this.cacheRef = nil // 不支持缓存
} }
this.writer.Prepare(nil, fileSize, http.StatusOK, true) var resp = &http.Response{
ContentLength: fileSize,
Body: fileReader,
StatusCode: http.StatusOK,
}
this.writer.Prepare(resp, fileSize, http.StatusOK, true)
pool := this.bytePool(fileSize) var pool = this.bytePool(fileSize)
buf := pool.Get() var buf = pool.Get()
defer func() { defer func() {
_ = reader.Close()
pool.Put(buf) pool.Put(buf)
}() }()
@@ -304,12 +314,14 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
respHeader.Set("Content-Range", ranges[0].ComposeContentRangeHeader(types.String(fileSize))) respHeader.Set("Content-Range", ranges[0].ComposeContentRangeHeader(types.String(fileSize)))
this.writer.WriteHeader(http.StatusPartialContent) this.writer.WriteHeader(http.StatusPartialContent)
ok, err := httpRequestReadRange(reader, buf, ranges[0].Start(), ranges[0].End(), func(buf []byte, n int) error { ok, err := httpRequestReadRange(resp.Body, buf, ranges[0].Start(), ranges[0].End(), func(buf []byte, n int) error {
_, err := this.writer.Write(buf[:n]) _, err := this.writer.Write(buf[:n])
return err return err
}) })
if err != nil { if err != nil {
logs.Error(err) if !this.canIgnore(err) {
logs.Error(err)
}
return true return true
} }
if !ok { if !ok {
@@ -318,7 +330,7 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
return true return true
} }
} else if len(ranges) > 1 { } else if len(ranges) > 1 {
boundary := httpRequestGenBoundary() var boundary = httpRequestGenBoundary()
respHeader.Set("Content-Type", "multipart/byteranges; boundary="+boundary) respHeader.Set("Content-Type", "multipart/byteranges; boundary="+boundary)
this.writer.WriteHeader(http.StatusPartialContent) this.writer.WriteHeader(http.StatusPartialContent)
@@ -330,30 +342,38 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
_, err = this.writer.WriteString("\r\n--" + boundary + "\r\n") _, err = this.writer.WriteString("\r\n--" + boundary + "\r\n")
} }
if err != nil { if err != nil {
logs.Error(err) if !this.canIgnore(err) {
logs.Error(err)
}
return true return true
} }
_, err = this.writer.WriteString("Content-Range: " + r.ComposeContentRangeHeader(types.String(fileSize)) + "\r\n") _, err = this.writer.WriteString("Content-Range: " + r.ComposeContentRangeHeader(types.String(fileSize)) + "\r\n")
if err != nil { if err != nil {
logs.Error(err) if !this.canIgnore(err) {
logs.Error(err)
}
return true return true
} }
if len(contentType) > 0 { if len(contentType) > 0 {
_, err = this.writer.WriteString("Content-Type: " + contentType + "\r\n\r\n") _, err = this.writer.WriteString("Content-Type: " + contentType + "\r\n\r\n")
if err != nil { if err != nil {
logs.Error(err) if !this.canIgnore(err) {
logs.Error(err)
}
return true return true
} }
} }
ok, err := httpRequestReadRange(reader, buf, r.Start(), r.End(), func(buf []byte, n int) error { ok, err := httpRequestReadRange(resp.Body, buf, r.Start(), r.End(), func(buf []byte, n int) error {
_, err := this.writer.Write(buf[:n]) _, err := this.writer.Write(buf[:n])
return err return err
}) })
if err != nil { if err != nil {
logs.Error(err) if !this.canIgnore(err) {
logs.Error(err)
}
return true return true
} }
if !ok { if !ok {
@@ -365,14 +385,17 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
_, err = this.writer.WriteString("\r\n--" + boundary + "--\r\n") _, err = this.writer.WriteString("\r\n--" + boundary + "--\r\n")
if err != nil { if err != nil {
logs.Error(err) if !this.canIgnore(err) {
logs.Error(err)
}
return true return true
} }
} else { } else {
_, err = io.CopyBuffer(this.writer, reader, buf) _, err = io.CopyBuffer(this.writer, resp.Body, buf)
if err != nil { if err != nil {
logs.Error(err) if !this.canIgnore(err) {
logs.Error(err)
}
return true return true
} }
} }
@@ -400,7 +423,9 @@ func (this *HTTPRequest) findIndexFile(dir string) (filename string, stat os.Fil
if strings.Contains(index, "*") { if strings.Contains(index, "*") {
indexFiles, err := filepath.Glob(dir + Tea.DS + index) indexFiles, err := filepath.Glob(dir + Tea.DS + index)
if err != nil { if err != nil {
logs.Error(err) if !this.canIgnore(err) {
logs.Error(err)
}
this.addError(err) this.addError(err)
continue continue
} }

View File

@@ -449,7 +449,9 @@ func (this *HTTPWriter) PrepareCache(resp *http.Response, size int64) {
this.rawReader = cacheReader this.rawReader = cacheReader
cacheReader.OnFail(func(err error) { cacheReader.OnFail(func(err error) {
_ = this.cacheWriter.Discard() if this.cacheWriter != nil {
_ = this.cacheWriter.Discard()
}
this.cacheWriter = nil this.cacheWriter = nil
}) })
cacheReader.OnEOF(func() { cacheReader.OnEOF(func() {
@@ -860,6 +862,70 @@ func (this *HTTPWriter) SetOk() {
// Close 关闭 // Close 关闭
func (this *HTTPWriter) Close() { func (this *HTTPWriter) Close() {
this.finishWebP()
this.finishRequest()
this.finishCache()
this.finishCompression()
// 统计
if this.sentBodyBytes == 0 {
this.sentBodyBytes = this.counterWriter.TotalBytes()
}
}
// Hijack Hijack
func (this *HTTPWriter) Hijack() (conn net.Conn, buf *bufio.ReadWriter, err error) {
hijack, ok := this.rawWriter.(http.Hijacker)
if ok {
return hijack.Hijack()
}
return
}
// Flush Flush
func (this *HTTPWriter) Flush() {
flusher, ok := this.rawWriter.(http.Flusher)
if ok {
flusher.Flush()
}
}
// DelayRead 是否延迟读取Reader
func (this *HTTPWriter) DelayRead() bool {
return this.delayRead
}
// 计算stale时长
func (this *HTTPWriter) calculateStaleLife() int {
var staleLife = 600 // TODO 可以在缓存策略里设置此时间
var staleConfig = this.req.web.Cache.Stale
if staleConfig != nil && staleConfig.IsOn {
// 从Header中读取stale-if-error
var isDefinedInHeader = false
if staleConfig.SupportStaleIfErrorHeader {
var cacheControl = this.GetHeader("Cache-Control")
var pieces = strings.Split(cacheControl, ",")
for _, piece := range pieces {
var eqIndex = strings.Index(piece, "=")
if eqIndex > 0 && strings.TrimSpace(piece[:eqIndex]) == "stale-if-error" {
// 这里预示着如果stale-if-error=0可以关闭stale功能
staleLife = types.Int(strings.TrimSpace(piece[eqIndex+1:]))
isDefinedInHeader = true
break
}
}
}
// 自定义
if !isDefinedInHeader && staleConfig.Life != nil {
staleLife = types.Int(staleConfig.Life.Duration().Seconds())
}
}
return staleLife
}
// 结束WebP
func (this *HTTPWriter) finishWebP() {
// 处理WebP // 处理WebP
if this.webpIsEncoding { if this.webpIsEncoding {
var webpCacheWriter caches.Writer var webpCacheWriter caches.Writer
@@ -920,6 +986,7 @@ func (this *HTTPWriter) Close() {
} }
if err != nil { if err != nil {
// 发生了错误终止处理
return return
} }
@@ -949,7 +1016,7 @@ func (this *HTTPWriter) Close() {
//webpConfig.SetLossless(1) //webpConfig.SetLossless(1)
webpConfig.SetQuality(f) webpConfig.SetQuality(f)
timeline := 0 var timeline = 0
for i, img := range gifImage.Image { for i, img := range gifImage.Image {
err = anim.AddFrame(img, timeline, webpConfig) err = anim.AddFrame(img, timeline, webpConfig)
@@ -989,15 +1056,10 @@ func (this *HTTPWriter) Close() {
} }
} }
} }
}
if this.writer != nil { // 结束缓存相关处理
_ = this.writer.Close() func (this *HTTPWriter) finishCache() {
}
if this.rawReader != nil {
_ = this.rawReader.Close()
}
// 缓存 // 缓存
if this.cacheWriter != nil { if this.cacheWriter != nil {
if this.isOk && this.cacheIsFinished { if this.isOk && this.cacheIsFinished {
@@ -1055,7 +1117,10 @@ func (this *HTTPWriter) Close() {
} }
} }
} }
}
// 结束压缩相关处理
func (this *HTTPWriter) finishCompression() {
if this.compressionCacheWriter != nil { if this.compressionCacheWriter != nil {
if this.isOk { if this.isOk {
err := this.compressionCacheWriter.Close() err := this.compressionCacheWriter.Close()
@@ -1076,59 +1141,15 @@ func (this *HTTPWriter) Close() {
_ = this.compressionCacheWriter.Discard() _ = this.compressionCacheWriter.Discard()
} }
} }
}
if this.sentBodyBytes == 0 { // 最终关闭
this.sentBodyBytes = this.counterWriter.TotalBytes() func (this *HTTPWriter) finishRequest() {
if this.writer != nil {
_ = this.writer.Close()
}
if this.rawReader != nil {
_ = this.rawReader.Close()
} }
} }
// Hijack Hijack
func (this *HTTPWriter) Hijack() (conn net.Conn, buf *bufio.ReadWriter, err error) {
hijack, ok := this.rawWriter.(http.Hijacker)
if ok {
return hijack.Hijack()
}
return
}
// Flush Flush
func (this *HTTPWriter) Flush() {
flusher, ok := this.rawWriter.(http.Flusher)
if ok {
flusher.Flush()
}
}
// DelayRead 是否延迟读取Reader
func (this *HTTPWriter) DelayRead() bool {
return this.delayRead
}
// 计算stale时长
func (this *HTTPWriter) calculateStaleLife() int {
var staleLife = 600 // TODO 可以在缓存策略里设置此时间
var staleConfig = this.req.web.Cache.Stale
if staleConfig != nil && staleConfig.IsOn {
// 从Header中读取stale-if-error
var isDefinedInHeader = false
if staleConfig.SupportStaleIfErrorHeader {
var cacheControl = this.GetHeader("Cache-Control")
var pieces = strings.Split(cacheControl, ",")
for _, piece := range pieces {
var eqIndex = strings.Index(piece, "=")
if eqIndex > 0 && strings.TrimSpace(piece[:eqIndex]) == "stale-if-error" {
// 这里预示着如果stale-if-error=0可以关闭stale功能
staleLife = types.Int(strings.TrimSpace(piece[eqIndex+1:]))
isDefinedInHeader = true
break
}
}
}
// 自定义
if !isDefinedInHeader && staleConfig.Life != nil {
staleLife = types.Int(staleConfig.Life.Duration().Seconds())
}
}
return staleLife
}