思路:
- 理解fastcgi协议
- php-fmp开启监听127.0.0.1:9000,此时它是fastcgi的服务端
- golang实现的web服务器监听用户请求
- web服务器接收到用户的request请求后,把服务器信息和用户的request请求打包
- 把打包好的信息发送到fastcgi服务端(就是请求127.0.0.1:9000),此时web服务器相对来说是fastcgi的客户端
- web服务器把fastcgi服务器返回的信息重新打包,返回给用户
实现主要代码
fastcgi 客户端golang的实现包可以上网找,我自己用的是 github.com/tomasen/fcgi_client
// 设置环境变量
func getEnv(r *http.Request, c config.Conf) map[string]string {
remoteAddr := strings.Split(r.RemoteAddr, ":")
serverKey := c.ServerIndex[r.Host]
config := c.Server[serverKey]
scriptName := "/"+config.Index
scriptFileName := config.Root+"/"+config.Index+r.URL.Path
if path.Ext(r.URL.Path) == ".php" {
scriptFileName = config.Root + r.URL.Path
scriptName = r.URL.Path
}
if strings.Contains(r.URL.Path, ".php") {
scriptFileName = config.Root+r.URL.Path
}
env := make(map[string]string)
// 设置request 的 header,这里如果没有设置,没办法把cookie带回来
for header, values := range r.Header {
env["HTTP_" + strings.Replace(strings.ToUpper(header), "-", "_", -1)] = values[0]
}
env["SERVER_SOFTWARE"] = "go php server/1.0"
env["REMOTE_ADDR"] = remoteAddr[0]
env["REMOTE_PORT"] = remoteAddr[1]
env["SERVER_PORT"] = config.Listen
env["SERVER_ADDR"] = config.Addr
env["DOCUMENT_ROOT"] = config.Root
env["SCRIPT_FILENAME"] = scriptFileName
env["SERVER_NAME"] = r.Host
env["SCRIPT_NAME"] = scriptName // 脚本相对于根目录下完整路径
env["QUERY_STRING"] = r.URL.Query().Encode()
env["REQUEST_URI"] = r.URL.RequestURI()
return env
}
// 设置响应信息
func setResponse(w http.ResponseWriter, resp *http.Response){
for _, c := range resp.Cookies() {
http.SetCookie(w, c)
}
for k, v := range resp.Header {
if k == "Set-Cookie" {
continue
}
headerValue := ""
for _, s := range v {
headerValue = headerValue + s
}
w.Header().Set(k, headerValue)
}
if resp.StatusCode > 0 {
w.WriteHeader(resp.StatusCode)
}
w.Header().Set("Connection", "keep-alive")
w.Header().Set("Server", "go_php_server")
content, err := ioutil.ReadAll(resp.Body)
common.LogErr(err)
io.WriteString(w, string(content))
}