mirror of
https://github.com/TeaOSLab/EdgeNode.git
synced 2025-11-03 15:00:26 +08:00
实现Web静态文件分发
This commit is contained in:
2
build/configs/.gitignore
vendored
2
build/configs/.gitignore
vendored
@@ -1,2 +1,2 @@
|
||||
node.yaml
|
||||
node.json
|
||||
api.yaml
|
||||
@@ -1 +0,0 @@
|
||||
{"id":"db4ab72647d3d2b9c8d9b57b4f4bf47b","isOn":true,"servers":[{"id":3,"type":"httpProxy","isOn":true,"name":"我的服务","description":"","serverNames":[],"http":{"isOn":true,"listen":[{"protocol":"http","host":"127.0.0.1","portRange":"9991"}]},"https":null,"tcp":null,"tls":null,"unix":null,"udp":null,"web":{"id":35,"isOn":true,"locations":[{"id":7,"isOn":true,"pattern":"/hello","name":"","web":{"id":36,"isOn":true,"locations":null,"locationRefs":null,"gzipRef":null,"gzip":null,"charset":null,"shutdown":{"isPrior":false,"isOn":false,"url":"","status":0},"pages":null,"redirectToHttps":{"isPrior":false,"isOn":false,"status":307,"host":"www","port":11111},"root":"","indexes":null,"maxRequestBodySize":"","accessLog":null,"statRef":null,"cacheRef":null,"firewallRef":null,"websocketRef":null,"websocket":null,"requestHeaderPolicyRef":{"isPrior":false,"isOn":true,"headerPolicyId":23},"requestHeaderPolicy":{"id":23,"name":"","isOn":true,"description":"","addHeaderRefs":null,"addHeaders":null,"addTrailerRefs":null,"addTrailers":null,"setHeaderRefs":null,"setHeaders":null,"replaceHeaderRefs":null,"replaceHeaders":null,"deleteHeaders":["Cache-Control","Pragma"],"expires":null},"responseHeaderPolicyRef":{"isPrior":false,"isOn":true,"headerPolicyId":24},"responseHeaderPolicy":{"id":24,"name":"","isOn":true,"description":"","addHeaderRefs":null,"addHeaders":null,"addTrailerRefs":null,"addTrailers":null,"setHeaderRefs":null,"setHeaders":null,"replaceHeaderRefs":null,"replaceHeaders":null,"deleteHeaders":null,"expires":null},"filterRefs":null,"filterPolicies":null},"urlPrefix":"","description":"","reverseProxyRef":null,"reverseProxy":null,"isBreak":false,"children":null,"condGroups":null}],"locationRefs":[{"isOn":true,"locationId":7,"children":null}],"gzipRef":null,"gzip":null,"charset":null,"shutdown":{"isPrior":false,"isOn":false,"url":"hello.html","status":0},"pages":[{"id":14,"isOn":true,"status":["404"],"url":"pages/404.html","newStatus":0}],"redirectToHttps":{"isPrior":false,"isOn":false,"status":307,"host":"","port":0},"root":"","indexes":null,"maxRequestBodySize":"","accessLog":null,"statRef":null,"cacheRef":null,"firewallRef":null,"websocketRef":null,"websocket":null,"requestHeaderPolicyRef":{"isPrior":false,"isOn":true,"headerPolicyId":21},"requestHeaderPolicy":{"id":21,"name":"","isOn":true,"description":"","addHeaderRefs":null,"addHeaders":null,"addTrailerRefs":null,"addTrailers":null,"setHeaderRefs":[{"isOn":true,"headerId":30}],"setHeaders":[{"id":30,"isOn":true,"name":"From","value":"Edge","status":{"always":true,"codes":null}}],"replaceHeaderRefs":null,"replaceHeaders":null,"deleteHeaders":["Cache-Control","Cookie"],"expires":null},"responseHeaderPolicyRef":{"isPrior":false,"isOn":true,"headerPolicyId":22},"responseHeaderPolicy":{"id":22,"name":"","isOn":true,"description":"","addHeaderRefs":null,"addHeaders":null,"addTrailerRefs":null,"addTrailers":null,"setHeaderRefs":[{"isOn":true,"headerId":28},{"isOn":true,"headerId":29}],"setHeaders":[{"id":28,"isOn":true,"name":"Server","value":"Edge","status":{"always":true,"codes":null}},{"id":29,"isOn":true,"name":"Hello","value":"World","status":{"always":true,"codes":null}}],"replaceHeaderRefs":null,"replaceHeaders":null,"deleteHeaders":["Name"],"expires":null},"filterRefs":null,"filterPolicies":null},"reverseProxyRef":{"isPrior":false,"isOn":true,"reverseProxyId":20},"reverseProxy":{"id":20,"isOn":false,"primaryOrigins":null,"primaryOriginRefs":null,"backupOrigins":null,"backupOriginRefs":null,"scheduling":null}}],"version":114,"name":"认证啊","globalConfig":null}
|
||||
6
go.mod
6
go.mod
@@ -7,15 +7,11 @@ replace github.com/TeaOSLab/EdgeCommon => ../EdgeCommon
|
||||
require (
|
||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
|
||||
github.com/TeaOSLab/EdgeCommon v0.0.0-00010101000000-000000000000
|
||||
github.com/dchest/siphash v1.2.1
|
||||
github.com/go-ole/go-ole v1.2.4 // indirect
|
||||
github.com/go-redis/redis v6.15.8+incompatible // indirect
|
||||
github.com/go-yaml/yaml v2.1.0+incompatible
|
||||
github.com/iwind/TeaGo v0.0.0-20200923021120-f5d76441fe9e
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||
github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7 // indirect
|
||||
github.com/shirou/gopsutil v2.20.7+incompatible
|
||||
github.com/stretchr/testify v1.6.1 // indirect
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7
|
||||
google.golang.org/grpc v1.32.0
|
||||
)
|
||||
|
||||
15
go.sum
15
go.sum
@@ -14,6 +14,8 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dchest/siphash v1.2.1 h1:4cLinnzVJDKxTCl9B01807Yiy+W7ZzVHj/KIroQRvT4=
|
||||
github.com/dchest/siphash v1.2.1/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200624174652-8d2f3be8b2d9/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
@@ -24,7 +26,6 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
|
||||
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
|
||||
github.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
||||
github.com/go-redis/redis/v8 v8.0.0-beta.7/go.mod h1:FGJAWDWFht1sQ4qxyJHZZbVyvnVcKQN0E3u5/5lRz+g=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o=
|
||||
@@ -51,17 +52,10 @@ github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/iwind/TeaGo v0.0.0-20200727075925-7e7e67b44f2d h1:V7HA0wUOdmZbXJTVpiUEvSD4ARKHwMLMmiCccfkqf24=
|
||||
github.com/iwind/TeaGo v0.0.0-20200727075925-7e7e67b44f2d/go.mod h1:zjM7k+b+Jthhf0T0fKwuF0iy4TWb5SsU1gmKR2l+OmE=
|
||||
github.com/iwind/TeaGo v0.0.0-20200822074248-b1cf7248c98a h1:VaWcMNOzHHT1y8MeTA2fWhG6GEfAdy6CwF2tW+KiY5Y=
|
||||
github.com/iwind/TeaGo v0.0.0-20200822074248-b1cf7248c98a/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
|
||||
github.com/iwind/TeaGo v0.0.0-20200910072805-729cffe36729 h1:/v0WhSFVeNay/dA5zU9iCBXlgVDfxnztuanlauXE0gM=
|
||||
github.com/iwind/TeaGo v0.0.0-20200910072805-729cffe36729/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
|
||||
github.com/iwind/TeaGo v0.0.0-20200923021120-f5d76441fe9e h1:/xn7wUvlwaoA5IkdBUctv2OQbJSZ0/Dw8qRJmn55sJk=
|
||||
github.com/iwind/TeaGo v0.0.0-20200923021120-f5d76441fe9e/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
|
||||
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
@@ -85,12 +79,8 @@ github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/opentracing/opentracing-go v1.1.1-0.20190913142402-a7454ce5950e/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.12.0/go.mod h1:fUqqXB5vEgVCZ131L+9say31RAri6aF6KDViawhxKK8=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7 h1:xoIK0ctDddBMnc74udxJYBqlo9Ylnsp1waqjLsnef20=
|
||||
github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/shirou/gopsutil v2.20.7+incompatible h1:Ymv4OD12d6zm+2yONe39VSmp2XooJe8za7ngOLW/o/w=
|
||||
github.com/shirou/gopsutil v2.20.7+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
@@ -101,7 +91,6 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
go.opentelemetry.io/otel v0.7.0/go.mod h1:aZMyHG5TqDOXEgH2tyLiXSUKly1jT3yqE9PmrzIeCdo=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -70,7 +71,7 @@ func (this *HTTPRequest) Do() {
|
||||
// 配置
|
||||
err := this.configureWeb(this.Server.Web, true, 0)
|
||||
if err != nil {
|
||||
this.writeInternalServerError()
|
||||
this.write500()
|
||||
this.doEnd()
|
||||
return
|
||||
}
|
||||
@@ -106,8 +107,26 @@ func (this *HTTPRequest) doBegin() {
|
||||
return
|
||||
}
|
||||
|
||||
// Origin
|
||||
// root
|
||||
// TODO 从本地文件中读取
|
||||
// TODO 增加stripPrefix
|
||||
// TODO 增加URLEncode的处理方式
|
||||
// TODO ROOT支持变量
|
||||
if this.web.Root != nil && this.web.Root.IsOn {
|
||||
// 如果处理成功,则终止请求的处理
|
||||
if this.doRoot() {
|
||||
return
|
||||
}
|
||||
|
||||
// 如果明确设置了终止,则也会自动终止
|
||||
if this.web.Root.IsBreak {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Reverse
|
||||
// TODO
|
||||
logs.Println("reverse proxy")
|
||||
|
||||
// WebSocket
|
||||
// TODO
|
||||
@@ -118,14 +137,8 @@ func (this *HTTPRequest) doBegin() {
|
||||
// Server Event Sent
|
||||
// TODO 实现Location的AutoFlush
|
||||
|
||||
// root
|
||||
// TODO 从本地文件中读取
|
||||
// TODO 增加root优先级:High:优先从Root读取,Low:优先从反向代理等条件中读取
|
||||
// TODO 增加stripPrefix
|
||||
// TODO 增加URLEncode的处理方式
|
||||
|
||||
// 返回404页面
|
||||
this.writeNotFoundError()
|
||||
this.write404()
|
||||
}
|
||||
|
||||
// 结束调用
|
||||
@@ -187,6 +200,16 @@ func (this *HTTPRequest) configureWeb(web *serverconfigs.HTTPWebConfig, isTop bo
|
||||
this.web.ResponseHeaderPolicy = web.ResponseHeaderPolicy
|
||||
}
|
||||
|
||||
// root
|
||||
if web.Root != nil && (web.Root.IsPrior || isTop) {
|
||||
this.web.Root = web.Root
|
||||
}
|
||||
|
||||
// charset
|
||||
if web.Charset != nil && (web.Charset.IsPrior || isTop) {
|
||||
this.web.Charset = web.Charset
|
||||
}
|
||||
|
||||
// locations
|
||||
if len(web.LocationRefs) > 0 {
|
||||
var resultLocation *serverconfigs.HTTPLocationConfig
|
||||
@@ -270,8 +293,8 @@ func (this *HTTPRequest) Format(source string) string {
|
||||
return filename
|
||||
}
|
||||
|
||||
if len(this.web.Root) > 0 {
|
||||
return filepath.Clean(this.web.Root + this.requestPath())
|
||||
if this.web.Root != nil && this.web.Root.IsOn {
|
||||
return filepath.Clean(this.web.Root.Dir + this.requestPath())
|
||||
}
|
||||
|
||||
return ""
|
||||
@@ -322,7 +345,10 @@ func (this *HTTPRequest) Format(source string) string {
|
||||
case "hostname":
|
||||
return HOSTNAME
|
||||
case "documentRoot":
|
||||
return this.web.Root
|
||||
if this.web.Root != nil {
|
||||
return this.web.Root.Dir
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
dotIndex := strings.Index(varName, ".")
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (this *HTTPRequest) writeNotFoundError() {
|
||||
func (this *HTTPRequest) write404() {
|
||||
if this.doPage(http.StatusNotFound) {
|
||||
return
|
||||
}
|
||||
@@ -17,7 +17,7 @@ func (this *HTTPRequest) writeNotFoundError() {
|
||||
_, _ = this.writer.Write([]byte(msg))
|
||||
}
|
||||
|
||||
func (this *HTTPRequest) writeInternalServerError() {
|
||||
func (this *HTTPRequest) write500() {
|
||||
statusCode := http.StatusInternalServerError
|
||||
if this.doPage(statusCode) {
|
||||
return
|
||||
|
||||
278
internal/nodes/http_request_root.go
Normal file
278
internal/nodes/http_request_root.go
Normal file
@@ -0,0 +1,278 @@
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/dchest/siphash"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"io"
|
||||
"mime"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 文本mime-type列表
|
||||
var textMimeMap = map[string]bool{
|
||||
"application/atom+xml": true,
|
||||
"application/javascript": true,
|
||||
"application/x-javascript": true,
|
||||
"application/json": true,
|
||||
"application/rss+xml": true,
|
||||
"application/x-web-app-manifest+json": true,
|
||||
"application/xhtml+xml": true,
|
||||
"application/xml": true,
|
||||
"image/svg+xml": true,
|
||||
"text/css": true,
|
||||
"text/plain": true,
|
||||
"text/javascript": true,
|
||||
"text/xml": true,
|
||||
"text/html": true,
|
||||
"text/xhtml": true,
|
||||
"text/sgml": true,
|
||||
}
|
||||
|
||||
// 调用本地静态资源
|
||||
// 如果返回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
|
||||
}
|
||||
|
||||
rootDir := this.web.Root.Dir
|
||||
if !filepath.IsAbs(rootDir) {
|
||||
rootDir = Tea.Root + Tea.DS + rootDir
|
||||
}
|
||||
|
||||
requestPath := this.uri
|
||||
|
||||
questionMarkIndex := strings.Index(this.uri, "?")
|
||||
if questionMarkIndex > -1 {
|
||||
requestPath = this.uri[:questionMarkIndex]
|
||||
}
|
||||
|
||||
// 去掉其中的奇怪的路径
|
||||
requestPath = strings.Replace(requestPath, "..\\", "", -1)
|
||||
|
||||
// 进行URL Decode
|
||||
if this.web.Root.DecodePath {
|
||||
p, err := url.QueryUnescape(requestPath)
|
||||
if err == nil {
|
||||
requestPath = p
|
||||
} else {
|
||||
logs.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// 去掉前缀
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
filename := strings.Replace(requestPath, "/", Tea.DS, -1)
|
||||
filePath := ""
|
||||
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 {
|
||||
if os.IsNotExist(err) {
|
||||
if this.web.Root.IsBreak {
|
||||
this.write404()
|
||||
return true
|
||||
}
|
||||
return
|
||||
} else {
|
||||
this.write500()
|
||||
logs.Error(err)
|
||||
this.addError(err)
|
||||
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.write500()
|
||||
logs.Error(err)
|
||||
this.addError(err)
|
||||
return true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
stat = indexStat
|
||||
}
|
||||
}
|
||||
|
||||
// 响应header
|
||||
respHeader := this.writer.Header()
|
||||
|
||||
// mime type
|
||||
if !(this.web.ResponseHeaderPolicy != nil && this.web.ResponseHeaderPolicy.IsOn && this.web.ResponseHeaderPolicy.ContainsHeader("CONTENT-TYPE")) {
|
||||
ext := filepath.Ext(requestPath)
|
||||
if len(ext) > 0 {
|
||||
mimeType := mime.TypeByExtension(ext)
|
||||
if len(mimeType) > 0 {
|
||||
if _, found := textMimeMap[mimeType]; found {
|
||||
if this.web.Charset != nil && this.web.Charset.IsOn && len(this.web.Charset.Charset) > 0 {
|
||||
charset := this.web.Charset.Charset
|
||||
|
||||
// 去掉里面的charset设置
|
||||
index := strings.Index(mimeType, "charset=")
|
||||
if index > 0 {
|
||||
respHeader.Set("Content-Type", mimeType[:index+len("charset=")]+charset)
|
||||
} else {
|
||||
respHeader.Set("Content-Type", mimeType+"; charset="+charset)
|
||||
}
|
||||
} else {
|
||||
respHeader.Set("Content-Type", mimeType)
|
||||
}
|
||||
} else {
|
||||
respHeader.Set("Content-Type", mimeType)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// length
|
||||
fileSize := stat.Size()
|
||||
respHeader.Set("Content-Length", strconv.FormatInt(fileSize, 10))
|
||||
|
||||
// 支持 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
|
||||
eTag := "\"et" + fmt.Sprintf("%0x", siphash.Hash(0, 0, []byte(filename+strconv.FormatInt(stat.ModTime().UnixNano(), 10)+strconv.FormatInt(stat.Size(), 10)))) + "\""
|
||||
if len(respHeader.Get("ETag")) == 0 {
|
||||
respHeader.Set("ETag", eTag)
|
||||
}
|
||||
|
||||
// proxy callback
|
||||
// TODO
|
||||
|
||||
// 支持 If-None-Match
|
||||
if this.requestHeader("If-None-Match") == eTag {
|
||||
// 自定义Header
|
||||
this.processResponseHeaders(http.StatusNotModified)
|
||||
this.writer.WriteHeader(http.StatusNotModified)
|
||||
return true
|
||||
}
|
||||
|
||||
// 支持 If-Modified-Since
|
||||
if this.requestHeader("If-Modified-Since") == modifiedTime {
|
||||
// 自定义Header
|
||||
this.processResponseHeaders(http.StatusNotModified)
|
||||
this.writer.WriteHeader(http.StatusNotModified)
|
||||
return true
|
||||
}
|
||||
|
||||
// 自定义Header
|
||||
this.processResponseHeaders(http.StatusOK)
|
||||
|
||||
reader, err := os.OpenFile(filePath, os.O_RDONLY, 0444)
|
||||
if err != nil {
|
||||
this.write500()
|
||||
logs.Error(err)
|
||||
this.addError(err)
|
||||
return true
|
||||
}
|
||||
|
||||
this.writer.Prepare(fileSize)
|
||||
|
||||
pool := this.bytePool(fileSize)
|
||||
buf := pool.Get()
|
||||
_, err = io.CopyBuffer(this.writer, reader, buf)
|
||||
pool.Put(buf)
|
||||
|
||||
// 不使用defer,以便于加快速度
|
||||
_ = reader.Close()
|
||||
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
return true
|
||||
}
|
||||
|
||||
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 {
|
||||
logs.Error(err)
|
||||
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
|
||||
}
|
||||
@@ -36,7 +36,7 @@ func (this *HTTPRequest) doURL(method string, url string, host string, statusCod
|
||||
if err != nil {
|
||||
logs.Error(errors.New(req.URL.String() + ": " + err.Error()))
|
||||
this.addError(err)
|
||||
this.writeInternalServerError()
|
||||
this.write500()
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
|
||||
Reference in New Issue
Block a user