github.com/TeaOSLab/EdgeNode@v1.3.8/internal/nodes/http_request_fastcgi.go (about) 1 // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved. 2 3 package nodes 4 5 import ( 6 "errors" 7 "fmt" 8 "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs" 9 teaconst "github.com/TeaOSLab/EdgeNode/internal/const" 10 "github.com/TeaOSLab/EdgeNode/internal/remotelogs" 11 "github.com/iwind/TeaGo/Tea" 12 "github.com/iwind/TeaGo/maps" 13 "github.com/iwind/TeaGo/rands" 14 "github.com/iwind/TeaGo/types" 15 "github.com/iwind/gofcgi/pkg/fcgi" 16 "io" 17 "net" 18 "net/http" 19 "net/url" 20 "path/filepath" 21 "strings" 22 ) 23 24 func (this *HTTPRequest) doFastcgi() (shouldStop bool) { 25 fastcgiList := []*serverconfigs.HTTPFastcgiConfig{} 26 for _, fastcgi := range this.web.FastcgiList { 27 if !fastcgi.IsOn { 28 continue 29 } 30 fastcgiList = append(fastcgiList, fastcgi) 31 } 32 if len(fastcgiList) == 0 { 33 return false 34 } 35 shouldStop = true 36 fastcgi := fastcgiList[rands.Int(0, len(fastcgiList)-1)] 37 38 env := fastcgi.FilterParams() 39 if !env.Has("DOCUMENT_ROOT") { 40 env["DOCUMENT_ROOT"] = "" 41 } 42 43 if !env.Has("REMOTE_ADDR") { 44 env["REMOTE_ADDR"] = this.requestRemoteAddr(true) 45 } 46 if !env.Has("QUERY_STRING") { 47 u, err := url.ParseRequestURI(this.uri) 48 if err == nil { 49 env["QUERY_STRING"] = u.RawQuery 50 } else { 51 env["QUERY_STRING"] = this.RawReq.URL.RawQuery 52 } 53 } 54 if !env.Has("SERVER_NAME") { 55 env["SERVER_NAME"] = this.ReqHost 56 } 57 if !env.Has("REQUEST_URI") { 58 env["REQUEST_URI"] = this.uri 59 } 60 if !env.Has("HOST") { 61 env["HOST"] = this.ReqHost 62 } 63 64 if len(this.ServerAddr) > 0 { 65 if !env.Has("SERVER_ADDR") { 66 env["SERVER_ADDR"] = this.ServerAddr 67 } 68 if !env.Has("SERVER_PORT") { 69 _, port, err := net.SplitHostPort(this.ServerAddr) 70 if err == nil { 71 env["SERVER_PORT"] = port 72 } 73 } 74 } 75 76 // 设置为持久化连接 77 var requestConn = this.RawReq.Context().Value(HTTPConnContextKey) 78 if requestConn == nil { 79 return 80 } 81 requestClientConn, ok := requestConn.(ClientConnInterface) 82 if ok { 83 requestClientConn.SetIsPersistent(true) 84 } 85 86 // 连接池配置 87 poolSize := fastcgi.PoolSize 88 if poolSize <= 0 { 89 poolSize = 32 90 } 91 92 client, err := fcgi.SharedPool(fastcgi.Network(), fastcgi.RealAddress(), uint(poolSize)).Client() 93 if err != nil { 94 this.write50x(err, http.StatusInternalServerError, "Failed to create Fastcgi pool", "Fastcgi池生成失败", false) 95 return 96 } 97 98 // 请求相关 99 if !env.Has("REQUEST_METHOD") { 100 env["REQUEST_METHOD"] = this.RawReq.Method 101 } 102 if !env.Has("CONTENT_LENGTH") { 103 env["CONTENT_LENGTH"] = fmt.Sprintf("%d", this.RawReq.ContentLength) 104 } 105 if !env.Has("CONTENT_TYPE") { 106 env["CONTENT_TYPE"] = this.RawReq.Header.Get("Content-Type") 107 } 108 if !env.Has("SERVER_SOFTWARE") { 109 env["SERVER_SOFTWARE"] = teaconst.ProductName + "/v" + teaconst.Version 110 } 111 112 // 处理SCRIPT_FILENAME 113 scriptPath := env.GetString("SCRIPT_FILENAME") 114 if len(scriptPath) > 0 && !strings.Contains(scriptPath, "/") && !strings.Contains(scriptPath, "\\") { 115 env["SCRIPT_FILENAME"] = env.GetString("DOCUMENT_ROOT") + Tea.DS + scriptPath 116 } 117 scriptFilename := filepath.Base(this.RawReq.URL.Path) 118 119 // PATH_INFO 120 pathInfoReg := fastcgi.PathInfoRegexp() 121 pathInfo := "" 122 if pathInfoReg != nil { 123 matches := pathInfoReg.FindStringSubmatch(this.RawReq.URL.Path) 124 countMatches := len(matches) 125 if countMatches == 1 { 126 pathInfo = matches[0] 127 } else if countMatches == 2 { 128 pathInfo = matches[1] 129 } else if countMatches > 2 { 130 scriptFilename = matches[1] 131 pathInfo = matches[2] 132 } 133 134 if !env.Has("PATH_INFO") { 135 env["PATH_INFO"] = pathInfo 136 } 137 } 138 139 this.addVarMapping(map[string]string{ 140 "fastcgi.documentRoot": env.GetString("DOCUMENT_ROOT"), 141 "fastcgi.filename": scriptFilename, 142 "fastcgi.pathInfo": pathInfo, 143 }) 144 145 params := map[string]string{} 146 for key, value := range env { 147 params[key] = this.Format(types.String(value)) 148 } 149 150 this.processRequestHeaders(this.RawReq.Header) 151 for k, v := range this.RawReq.Header { 152 if k == "Connection" { 153 continue 154 } 155 for _, subV := range v { 156 params["HTTP_"+strings.ToUpper(strings.Replace(k, "-", "_", -1))] = subV 157 } 158 } 159 160 host, found := params["HTTP_HOST"] 161 if !found || len(host) == 0 { 162 params["HTTP_HOST"] = this.ReqHost 163 } 164 165 fcgiReq := fcgi.NewRequest() 166 fcgiReq.SetTimeout(fastcgi.ReadTimeoutDuration()) 167 fcgiReq.SetParams(params) 168 fcgiReq.SetBody(this.RawReq.Body, uint32(this.requestLength())) 169 170 resp, stderr, err := client.Call(fcgiReq) 171 if err != nil { 172 this.write50x(err, http.StatusInternalServerError, "Failed to read Fastcgi", "读取Fastcgi失败", false) 173 return 174 } 175 176 if len(stderr) > 0 { 177 err := errors.New("Fastcgi Error: " + strings.TrimSpace(string(stderr)) + " script: " + maps.NewMap(params).GetString("SCRIPT_FILENAME")) 178 this.write50x(err, http.StatusInternalServerError, "Failed to read Fastcgi", "读取Fastcgi失败", false) 179 return 180 } 181 182 defer func() { 183 _ = resp.Body.Close() 184 }() 185 186 // 设置Charset 187 // TODO 这里应该可以设置文本类型的列表,以及是否强制覆盖所有文本类型的字符集 188 if this.web.Charset != nil && this.web.Charset.IsOn && len(this.web.Charset.Charset) > 0 { 189 contentTypes, ok := resp.Header["Content-Type"] 190 if ok && len(contentTypes) > 0 { 191 contentType := contentTypes[0] 192 if _, found := textMimeMap[contentType]; found { 193 resp.Header["Content-Type"][0] = contentType + "; charset=" + this.web.Charset.Charset 194 } 195 } 196 } 197 198 // 响应Header 199 this.writer.AddHeaders(resp.Header) 200 this.ProcessResponseHeaders(this.writer.Header(), resp.StatusCode) 201 202 // 准备 203 this.writer.Prepare(resp, resp.ContentLength, resp.StatusCode, true) 204 205 // 设置响应代码 206 this.writer.WriteHeader(resp.StatusCode) 207 208 // 输出到客户端 209 var pool = this.bytePool(resp.ContentLength) 210 var buf = pool.Get() 211 _, err = io.CopyBuffer(this.writer, resp.Body, buf.Bytes) 212 pool.Put(buf) 213 214 closeErr := resp.Body.Close() 215 if closeErr != nil { 216 remotelogs.Warn("HTTP_REQUEST_FASTCGI", closeErr.Error()) 217 } 218 219 if err != nil && err != io.EOF { 220 remotelogs.Warn("HTTP_REQUEST_FASTCGI", err.Error()) 221 this.addError(err) 222 } 223 224 // 是否成功结束 225 if err == nil && closeErr == nil { 226 this.writer.SetOk() 227 } 228 229 return 230 }