服务支持fastcgi;路径规则支持匹配后缀

This commit is contained in:
GoEdgeLab
2021-05-10 21:13:18 +08:00
parent a5b97d370b
commit 93cdb73381
4 changed files with 230 additions and 4 deletions

1
go.mod
View File

@@ -13,6 +13,7 @@ require (
github.com/go-yaml/yaml v2.1.0+incompatible
github.com/golang/protobuf v1.4.2
github.com/iwind/TeaGo v0.0.0-20201020081413-7cf62d6f420f
github.com/iwind/gofcgi v0.0.0-20210506081859-17498ab3e9d7
github.com/lionsoul2014/ip2region v2.2.0-release+incompatible
github.com/mssola/user_agent v0.5.2
github.com/shirou/gopsutil v2.20.9+incompatible

2
go.sum
View File

@@ -58,6 +58,8 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
github.com/iwind/TeaGo v0.0.0-20200923021120-f5d76441fe9e/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
github.com/iwind/TeaGo v0.0.0-20201020081413-7cf62d6f420f h1:6Ws2H+eorfVUoMO2jta6A9nIdh8oi5/5LXo/LkAxR+E=
github.com/iwind/TeaGo v0.0.0-20201020081413-7cf62d6f420f/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
github.com/iwind/gofcgi v0.0.0-20210506081859-17498ab3e9d7 h1:apv23QzWNmv0D76gB3+u/5kf0F/Yw4W8h489CWUZtss=
github.com/iwind/gofcgi v0.0.0-20210506081859-17498ab3e9d7/go.mod h1:JtbX20untAjUVjZs1ZBtq80f5rJWvwtQNRL6EnuYRnY=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=

View File

@@ -191,6 +191,13 @@ func (this *HTTPRequest) doBegin() {
}
}
// Fastcgi
if this.web.FastcgiRef != nil && this.web.FastcgiRef.IsOn && len(this.web.FastcgiList) > 0 {
if this.doFastcgi() {
return
}
}
// root
if this.web.Root != nil && this.web.Root.IsOn {
// 如果处理成功,则终止请求的处理
@@ -210,9 +217,6 @@ func (this *HTTPRequest) doBegin() {
return
}
// Fastcgi
// TODO
// 返回404页面
this.write404()
}
@@ -229,7 +233,7 @@ func (this *HTTPRequest) doEnd() {
}
}
// 原始的请求URI
// RawURI 原始的请求URI
func (this *HTTPRequest) RawURI() string {
return this.rawURI
}
@@ -332,6 +336,12 @@ func (this *HTTPRequest) configureWeb(web *serverconfigs.HTTPWebConfig, isTop bo
this.web.StatRef = web.StatRef
}
// fastcgi
if web.FastcgiRef != nil && (web.FastcgiRef.IsPrior || isTop) {
this.web.FastcgiRef = web.FastcgiRef
this.web.FastcgiList = web.FastcgiList
}
// 重写规则
if len(web.RewriteRefs) > 0 {
for index, ref := range web.RewriteRefs {

View File

@@ -0,0 +1,213 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package nodes
import (
"errors"
"fmt"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
"github.com/iwind/TeaGo/types"
"github.com/iwind/gofcgi/pkg"
"io"
"net"
"net/url"
"path/filepath"
"strings"
)
func (this *HTTPRequest) doFastcgi() (shouldStop bool) {
fastcgiList := []*serverconfigs.HTTPFastcgiConfig{}
for _, fastcgi := range this.web.FastcgiList {
if !fastcgi.IsOn {
continue
}
fastcgiList = append(fastcgiList, fastcgi)
}
if len(fastcgiList) == 0 {
return false
}
shouldStop = true
fastcgi := fastcgiList[rands.Int(0, len(fastcgiList)-1)]
env := fastcgi.FilterParams()
if !env.Has("DOCUMENT_ROOT") {
env["DOCUMENT_ROOT"] = ""
}
if !env.Has("REMOTE_ADDR") {
env["REMOTE_ADDR"] = this.requestRemoteAddr()
}
if !env.Has("QUERY_STRING") {
u, err := url.ParseRequestURI(this.uri)
if err == nil {
env["QUERY_STRING"] = u.RawQuery
} else {
env["QUERY_STRING"] = this.RawReq.URL.RawQuery
}
}
if !env.Has("SERVER_NAME") {
env["SERVER_NAME"] = this.Host
}
if !env.Has("REQUEST_URI") {
env["REQUEST_URI"] = this.uri
}
if !env.Has("HOST") {
env["HOST"] = this.Host
}
if len(this.ServerAddr) > 0 {
if !env.Has("SERVER_ADDR") {
env["SERVER_ADDR"] = this.ServerAddr
}
if !env.Has("SERVER_PORT") {
_, port, err := net.SplitHostPort(this.ServerAddr)
if err == nil {
env["SERVER_PORT"] = port
}
}
}
// 连接池配置
poolSize := fastcgi.PoolSize
if poolSize <= 0 {
poolSize = 32
}
client, err := pkg.SharedPool(fastcgi.Network(), fastcgi.RealAddress(), uint(poolSize)).Client()
if err != nil {
this.write500(err)
return
}
// 请求相关
if !env.Has("REQUEST_METHOD") {
env["REQUEST_METHOD"] = this.RawReq.Method
}
if !env.Has("CONTENT_LENGTH") {
env["CONTENT_LENGTH"] = fmt.Sprintf("%d", this.RawReq.ContentLength)
}
if !env.Has("CONTENT_TYPE") {
env["CONTENT_TYPE"] = this.RawReq.Header.Get("Content-Type")
}
if !env.Has("SERVER_SOFTWARE") {
env["SERVER_SOFTWARE"] = teaconst.ProductName + "/v" + teaconst.Version
}
// 处理SCRIPT_FILENAME
scriptPath := env.GetString("SCRIPT_FILENAME")
if len(scriptPath) > 0 && (strings.Index(scriptPath, "/") < 0 && strings.Index(scriptPath, "\\") < 0) {
env["SCRIPT_FILENAME"] = env.GetString("DOCUMENT_ROOT") + Tea.DS + scriptPath
}
scriptFilename := filepath.Base(this.RawReq.URL.Path)
// PATH_INFO
pathInfoReg := fastcgi.PathInfoRegexp()
pathInfo := ""
if pathInfoReg != nil {
matches := pathInfoReg.FindStringSubmatch(this.RawReq.URL.Path)
countMatches := len(matches)
if countMatches == 1 {
pathInfo = matches[0]
} else if countMatches == 2 {
pathInfo = matches[1]
} else if countMatches > 2 {
scriptFilename = matches[1]
pathInfo = matches[2]
}
if !env.Has("PATH_INFO") {
env["PATH_INFO"] = pathInfo
}
}
this.addVarMapping(map[string]string{
"fastcgi.documentRoot": env.GetString("DOCUMENT_ROOT"),
"fastcgi.filename": scriptFilename,
"fastcgi.pathInfo": pathInfo,
})
params := map[string]string{}
for key, value := range env {
params[key] = this.Format(types.String(value))
}
this.processRequestHeaders(this.RawReq.Header)
for k, v := range this.RawReq.Header {
if k == "Connection" {
continue
}
for _, subV := range v {
params["HTTP_"+strings.ToUpper(strings.Replace(k, "-", "_", -1))] = subV
}
}
host, found := params["HTTP_HOST"]
if !found || len(host) == 0 {
params["HTTP_HOST"] = this.Host
}
fcgiReq := pkg.NewRequest()
fcgiReq.SetTimeout(fastcgi.ReadTimeoutDuration())
fcgiReq.SetParams(params)
fcgiReq.SetBody(this.RawReq.Body, uint32(this.requestLength()))
resp, stderr, err := client.Call(fcgiReq)
if err != nil {
this.write500(err)
return
}
if len(stderr) > 0 {
err := errors.New("Fastcgi Error: " + strings.TrimSpace(string(stderr)) + " script: " + maps.NewMap(params).GetString("SCRIPT_FILENAME"))
this.write500(err)
return
}
defer func() {
_ = resp.Body.Close()
}()
// 设置Charset
// TODO 这里应该可以设置文本类型的列表,以及是否强制覆盖所有文本类型的字符集
if this.web.Charset != nil && this.web.Charset.IsOn && len(this.web.Charset.Charset) > 0 {
contentTypes, ok := resp.Header["Content-Type"]
if ok && len(contentTypes) > 0 {
contentType := contentTypes[0]
if _, found := textMimeMap[contentType]; found {
resp.Header["Content-Type"][0] = contentType + "; charset=" + this.web.Charset.Charset
}
}
}
// 响应Header
this.writer.AddHeaders(resp.Header)
this.processResponseHeaders(resp.StatusCode)
// 准备
this.writer.Prepare(resp.ContentLength)
// 设置响应代码
this.writer.WriteHeader(resp.StatusCode)
// 输出到客户端
pool := this.bytePool(resp.ContentLength)
buf := pool.Get()
_, err = io.CopyBuffer(this.writer, resp.Body, buf)
pool.Put(buf)
err1 := resp.Body.Close()
if err1 != nil {
remotelogs.Error("REQUEST_REVERSE_PROXY", err1.Error())
}
if err != nil && err != io.EOF {
remotelogs.Error("REQUEST_REVERSE_PROXY", err.Error())
this.addError(err)
}
return
}