Files
EdgeNode/internal/nodes/http_request_root.go

461 lines
11 KiB
Go
Raw Normal View History

2020-09-26 11:22:21 +08:00
package nodes
import (
"fmt"
rangeutils "github.com/TeaOSLab/EdgeNode/internal/utils/ranges"
"github.com/TeaOSLab/EdgeNode/internal/zero"
2024-04-18 18:25:33 +08:00
"github.com/cespare/xxhash/v2"
2020-09-26 11:22:21 +08:00
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/types"
2020-09-26 11:22:21 +08:00
"io"
2021-01-26 18:42:46 +08:00
"io/fs"
2020-09-26 11:22:21 +08:00
"mime"
"net/http"
"net/url"
"os"
"path/filepath"
"strconv"
"strings"
)
// 文本mime-type列表
var textMimeMap = map[string]zero.Zero{
"application/atom+xml": {},
"application/javascript": {},
"application/x-javascript": {},
"application/json": {},
"application/rss+xml": {},
"application/x-web-app-manifest+json": {},
"application/xhtml+xml": {},
"application/xml": {},
"image/svg+xml": {},
"text/css": {},
"text/plain": {},
"text/javascript": {},
"text/xml": {},
"text/html": {},
"text/xhtml": {},
"text/sgml": {},
2020-09-26 11:22:21 +08:00
}
// 调用本地静态资源
// 如果返回true则终止请求
func (this *HTTPRequest) doRoot() (isBreak bool) {
if this.web.Root == nil || !this.web.Root.IsOn {
return
}
if len(this.uri) == 0 {
this.write404()
return true
}
var rootDir = this.web.Root.Dir
2020-09-26 19:54:26 +08:00
if this.web.Root.HasVariables() {
rootDir = this.Format(rootDir)
}
2020-09-26 11:22:21 +08:00
if !filepath.IsAbs(rootDir) {
rootDir = Tea.Root + Tea.DS + rootDir
}
var requestPath = this.uri
2020-09-26 11:22:21 +08:00
var questionMarkIndex = strings.Index(this.uri, "?")
2020-09-26 11:22:21 +08:00
if questionMarkIndex > -1 {
requestPath = this.uri[:questionMarkIndex]
}
// except hidden files
if this.web.Root.ExceptHiddenFiles &&
(strings.Contains(requestPath, "/.") || strings.Contains(requestPath, "\\.")) {
this.write404()
return true
}
// except and only files
if !this.web.Root.MatchURL(this.URL()) {
this.write404()
return true
}
2020-09-26 11:22:21 +08:00
// 去掉其中的奇怪的路径
requestPath = strings.Replace(requestPath, "..\\", "", -1)
// 进行URL Decode
if this.web.Root.DecodePath {
p, err := url.QueryUnescape(requestPath)
if err == nil {
requestPath = p
} else {
if !this.canIgnore(err) {
logs.Error(err)
}
2020-09-26 11:22:21 +08:00
}
}
// 去掉前缀
stripPrefix := this.web.Root.StripPrefix
if len(stripPrefix) > 0 {
if stripPrefix[0] != '/' {
stripPrefix = "/" + stripPrefix
}
requestPath = strings.TrimPrefix(requestPath, stripPrefix)
if len(requestPath) == 0 || requestPath[0] != '/' {
requestPath = "/" + requestPath
}
}
var filename = strings.Replace(requestPath, "/", Tea.DS, -1)
2023-08-08 10:07:24 +08:00
var filePath string
2020-09-26 11:22:21 +08:00
if len(filename) > 0 && filename[0:1] == Tea.DS {
filePath = rootDir + filename
} else {
filePath = rootDir + Tea.DS + filename
}
this.filePath = filePath // 用来记录日志
stat, err := os.Stat(filePath)
if err != nil {
2021-01-26 18:42:46 +08:00
_, isPathError := err.(*fs.PathError)
if os.IsNotExist(err) || isPathError {
2020-09-26 11:22:21 +08:00
if this.web.Root.IsBreak {
this.write404()
return true
}
return
} else {
this.write50x(err, http.StatusInternalServerError, "Failed to stat the file", "查看文件统计信息失败", true)
if !this.canIgnore(err) {
logs.Error(err)
}
2020-09-26 11:22:21 +08:00
return true
}
}
if stat.IsDir() {
indexFile, indexStat := this.findIndexFile(filePath)
if len(indexFile) > 0 {
filePath += Tea.DS + indexFile
} else {
if this.web.Root.IsBreak {
this.write404()
return true
}
return
}
this.filePath = filePath
// stat again
if indexStat == nil {
stat, err = os.Stat(filePath)
if err != nil {
if os.IsNotExist(err) {
if this.web.Root.IsBreak {
this.write404()
return true
}
return
} else {
this.write50x(err, http.StatusInternalServerError, "Failed to stat the file", "查看文件统计信息失败", true)
if !this.canIgnore(err) {
logs.Error(err)
}
2020-09-26 11:22:21 +08:00
return true
}
}
} else {
stat = indexStat
}
}
// 响应header
var respHeader = this.writer.Header()
2020-09-26 11:22:21 +08:00
// mime type
var contentType = ""
2020-09-26 19:54:26 +08:00
if this.web.ResponseHeaderPolicy == nil || !this.web.ResponseHeaderPolicy.IsOn || !this.web.ResponseHeaderPolicy.ContainsHeader("CONTENT-TYPE") {
var ext = filepath.Ext(filePath)
2020-09-26 11:22:21 +08:00
if len(ext) > 0 {
mimeType := mime.TypeByExtension(ext)
if len(mimeType) > 0 {
var semicolonIndex = strings.Index(mimeType, ";")
var mimeTypeKey = mimeType
2020-09-26 19:54:26 +08:00
if semicolonIndex > 0 {
mimeTypeKey = mimeType[:semicolonIndex]
}
if _, found := textMimeMap[mimeTypeKey]; found {
2020-09-26 11:22:21 +08:00
if this.web.Charset != nil && this.web.Charset.IsOn && len(this.web.Charset.Charset) > 0 {
var charset = this.web.Charset.Charset
2020-09-26 19:54:26 +08:00
if this.web.Charset.IsUpper {
charset = strings.ToUpper(charset)
2020-09-26 11:22:21 +08:00
}
contentType = mimeTypeKey + "; charset=" + charset
2020-09-26 19:54:26 +08:00
respHeader.Set("Content-Type", mimeTypeKey+"; charset="+charset)
2020-09-26 11:22:21 +08:00
} else {
contentType = mimeType
2020-09-26 11:22:21 +08:00
respHeader.Set("Content-Type", mimeType)
}
} else {
contentType = mimeType
2020-09-26 11:22:21 +08:00
respHeader.Set("Content-Type", mimeType)
}
}
}
}
// length
var fileSize = stat.Size()
2020-09-26 11:22:21 +08:00
// 支持 Last-Modified
modifiedTime := stat.ModTime().Format("Mon, 02 Jan 2006 15:04:05 GMT")
if len(respHeader.Get("Last-Modified")) == 0 {
respHeader.Set("Last-Modified", modifiedTime)
}
// 支持 ETag
var eTag = "\"e" + fmt.Sprintf("%0x", xxhash.Sum64String(filename+strconv.FormatInt(stat.ModTime().UnixNano(), 10)+strconv.FormatInt(stat.Size(), 10))) + "\""
2020-09-26 11:22:21 +08:00
if len(respHeader.Get("ETag")) == 0 {
respHeader.Set("ETag", eTag)
}
// 调用回调
this.onRequest()
if this.writer.isFinished {
return
}
2020-09-26 11:22:21 +08:00
// 支持 If-None-Match
if this.requestHeader("If-None-Match") == eTag {
// 自定义Header
this.ProcessResponseHeaders(this.writer.Header(), http.StatusNotModified)
2020-09-26 11:22:21 +08:00
this.writer.WriteHeader(http.StatusNotModified)
return true
}
// 支持 If-Modified-Since
if this.requestHeader("If-Modified-Since") == modifiedTime {
// 自定义Header
this.ProcessResponseHeaders(this.writer.Header(), http.StatusNotModified)
2020-09-26 11:22:21 +08:00
this.writer.WriteHeader(http.StatusNotModified)
return true
}
// 支持Range
respHeader.Set("Accept-Ranges", "bytes")
ifRangeHeaders, ok := this.RawReq.Header["If-Range"]
var supportRange = true
if ok {
supportRange = false
for _, v := range ifRangeHeaders {
if v == eTag || v == modifiedTime {
supportRange = true
break
}
}
2021-01-13 12:52:38 +08:00
if !supportRange {
respHeader.Del("Accept-Ranges")
}
}
// 支持Range
var ranges = []rangeutils.Range{}
if supportRange {
var contentRange = this.RawReq.Header.Get("Range")
if len(contentRange) > 0 {
2021-01-13 12:52:38 +08:00
if fileSize == 0 {
this.ProcessResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable)
2021-01-13 12:52:38 +08:00
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
return true
}
set, ok := httpRequestParseRangeHeader(contentRange)
if !ok {
this.ProcessResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable)
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
return true
}
if len(set) > 0 {
ranges = set
for k, r := range ranges {
r2, ok := r.Convert(fileSize)
if !ok {
this.ProcessResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable)
2021-01-13 12:52:38 +08:00
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
return true
}
ranges[k] = r2
}
}
} else {
respHeader.Set("Content-Length", strconv.FormatInt(fileSize, 10))
}
} else {
respHeader.Set("Content-Length", strconv.FormatInt(fileSize, 10))
}
2020-09-26 11:22:21 +08:00
fileReader, err := os.OpenFile(filePath, os.O_RDONLY, 0444)
2020-09-26 11:22:21 +08:00
if err != nil {
this.write50x(err, http.StatusInternalServerError, "Failed to open the file", "试图打开文件失败", true)
2020-09-26 11:22:21 +08:00
return true
}
// 自定义Header
this.ProcessResponseHeaders(this.writer.Header(), http.StatusOK)
// 在Range请求中不能缓存
if len(ranges) > 0 {
this.cacheRef = nil // 不支持缓存
}
var resp = &http.Response{
ContentLength: fileSize,
Body: fileReader,
StatusCode: http.StatusOK,
}
this.writer.Prepare(resp, fileSize, http.StatusOK, true)
2020-09-26 11:22:21 +08:00
var pool = this.bytePool(fileSize)
var buf = pool.Get()
defer func() {
pool.Put(buf)
}()
if len(ranges) == 1 {
respHeader.Set("Content-Range", ranges[0].ComposeContentRangeHeader(types.String(fileSize)))
this.writer.WriteHeader(http.StatusPartialContent)
2024-04-15 09:26:00 +08:00
ok, err := httpRequestReadRange(resp.Body, buf.Bytes, ranges[0].Start(), ranges[0].End(), func(buf []byte, n int) error {
_, err := this.writer.Write(buf[:n])
return err
})
if err != nil {
if !this.canIgnore(err) {
logs.Error(err)
}
return true
}
if !ok {
this.ProcessResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable)
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
return true
}
} else if len(ranges) > 1 {
var boundary = httpRequestGenBoundary()
respHeader.Set("Content-Type", "multipart/byteranges; boundary="+boundary)
2020-09-26 11:22:21 +08:00
this.writer.WriteHeader(http.StatusPartialContent)
2020-09-26 11:22:21 +08:00
for index, r := range ranges {
if index == 0 {
_, err = this.writer.WriteString("--" + boundary + "\r\n")
} else {
_, err = this.writer.WriteString("\r\n--" + boundary + "\r\n")
}
if err != nil {
if !this.canIgnore(err) {
logs.Error(err)
}
return true
}
_, err = this.writer.WriteString("Content-Range: " + r.ComposeContentRangeHeader(types.String(fileSize)) + "\r\n")
if err != nil {
if !this.canIgnore(err) {
logs.Error(err)
}
return true
}
if len(contentType) > 0 {
_, err = this.writer.WriteString("Content-Type: " + contentType + "\r\n\r\n")
if err != nil {
if !this.canIgnore(err) {
logs.Error(err)
}
return true
}
}
2024-04-15 09:26:00 +08:00
ok, err := httpRequestReadRange(resp.Body, buf.Bytes, r.Start(), r.End(), func(buf []byte, n int) error {
_, err := this.writer.Write(buf[:n])
return err
})
if err != nil {
if !this.canIgnore(err) {
logs.Error(err)
}
return true
}
if !ok {
this.ProcessResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable)
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
return true
}
}
_, err = this.writer.WriteString("\r\n--" + boundary + "--\r\n")
if err != nil {
if !this.canIgnore(err) {
logs.Error(err)
}
return true
}
} else {
2024-04-15 09:26:00 +08:00
_, err = io.CopyBuffer(this.writer, resp.Body, buf.Bytes)
if err != nil {
if !this.canIgnore(err) {
logs.Error(err)
}
return true
}
2020-09-26 11:22:21 +08:00
}
// 设置成功
this.writer.SetOk()
2020-09-26 11:22:21 +08:00
return true
}
// 查找首页文件
func (this *HTTPRequest) findIndexFile(dir string) (filename string, stat os.FileInfo) {
if this.web.Root == nil || !this.web.Root.IsOn {
return "", nil
}
if len(this.web.Root.Indexes) == 0 {
return "", nil
}
for _, index := range this.web.Root.Indexes {
if len(index) == 0 {
continue
}
// 模糊查找
if strings.Contains(index, "*") {
indexFiles, err := filepath.Glob(dir + Tea.DS + index)
if err != nil {
if !this.canIgnore(err) {
logs.Error(err)
}
2020-09-26 11:22:21 +08:00
this.addError(err)
continue
}
if len(indexFiles) > 0 {
return filepath.Base(indexFiles[0]), nil
}
continue
}
// 精确查找
filePath := dir + Tea.DS + index
stat, err := os.Stat(filePath)
if err != nil || !stat.Mode().IsRegular() {
continue
}
return index, stat
}
return "", nil
}