mirror of
				https://github.com/TeaOSLab/EdgeNode.git
				synced 2025-11-04 07:40:56 +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