fusenapi/proxyserver/main.go

205 lines
5.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"fmt"
"io"
"log"
"net"
"net/http"
"net/http/httputil"
"net/url"
"strings"
"sync"
"time"
)
// Backend结构体
var Backends []*Backend
// 设置跨域请求
func SetCors(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
w.Header().Set("Access-Control-Allow-Headers", "*")
w.Header().Set("Access-Control-Expose-Headers", "*")
w.Header().Set("Access-Control-Allow-Credentials", "true")
// 如果请求方法为 OPTIONS直接返回 200 状态码
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
return
}
}
// 存储路径的并发安全的Map
var pathdict sync.Map = sync.Map{}
func main() {
// 将静态资源路径存储到pathdict
pathdict.Store("/css", true)
pathdict.Store("/fonts", true)
pathdict.Store("/img", true)
pathdict.Store("/js", true)
pathdict.Store("/svg", true)
pathdict.Store("/favicon.ico", true)
rootDir := "../server" // 更改为你的根目录
vueBuild := "/opt/fusenpack-vue-created"
apiURL, err := url.Parse("http://localhost:9900")
if err != nil {
panic(err)
}
mux := http.NewServeMux()
// 获取并解析服务信息
results := GetZeroInfo(rootDir)
var allRoutes map[string]bool = make(map[string]bool)
for _, result := range results {
fmt.Printf("FolderName: %s, Host: %s, Port: %d, PrefixRoute: %v\n", result.FolderName, result.Host, result.Port, result.PrefixRoute)
var routes []string
for k := range result.PrefixRoute {
routes = append(routes, k)
allRoutes[k] = true
}
// 根据获取的服务信息创建后端服务
Backends = append(Backends,
NewBackend(mux,
fmt.Sprintf("http://%s:%d", result.Host, result.Port),
routes...))
}
// 定义用于服务Vue dist文件夹的静态文件服务器
fs := http.FileServer(http.Dir(vueBuild))
indexHtmlPath := vueBuild + "/index.html"
mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.HasPrefix(r.URL.Path, "/api/") {
// 对/api开头的请求进行反向代理
proxy := httputil.NewSingleHostReverseProxy(apiURL)
proxy.ServeHTTP(w, r)
return
} else {
// 根据请求路径判断是服务静态文件或者是返回index.html
idx := strings.Index(r.URL.Path[1:], "/")
var prefix string
if idx != -1 {
prefix = r.URL.Path[:idx+1]
} else {
prefix = r.URL.Path
}
if _, ok := pathdict.Load(prefix); ok {
fs.ServeHTTP(w, r)
} else {
http.ServeFile(w, r, indexHtmlPath)
}
}
}))
ServerAddress := ":9900"
log.Println("listen on ", ServerAddress)
log.Fatal(http.ListenAndServe(ServerAddress, mux))
}
// 后端服务的类型
type Backend struct {
HttpAddress string
Client *http.Client
Handler http.HandlerFunc
}
func NewBackend(mux *http.ServeMux, httpAddress string, muxPaths ...string) *Backend {
// 如果路径最后没有以'/'结尾,则添加'/'
for i, muxPath := range muxPaths {
if muxPath[len(muxPath)-1] != '/' {
muxPath = muxPath + "/"
muxPaths[i] = muxPath
}
}
// 创建HTTP客户端设置相关的超时参数和连接数限制
client := &http.Client{
Transport: &http.Transport{
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
MaxIdleConnsPerHost: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
},
}
// 创建后端服务对象,包含地址和客户端
backend := &Backend{
HttpAddress: httpAddress,
Client: client,
}
// 创建处理请求的函数
handleRequest := func(w http.ResponseWriter, r *http.Request) {
// 解析目标URL包含了查询参数
targetURL, err := url.Parse(httpAddress + r.URL.String())
if err != nil {
http.Error(w, "Error parsing target URL", http.StatusInternalServerError)
return
}
// 创建新的请求
proxyReq, err := http.NewRequest(r.Method, targetURL.String(), r.Body)
if err != nil {
http.Error(w, "Error creating proxy request", http.StatusInternalServerError)
return
}
// 复制原始请求的 Header
for key, values := range r.Header {
for _, value := range values {
proxyReq.Header.Add(key, value)
}
}
// 设置 Content-Length 和 Content-Type
proxyReq.ContentLength = r.ContentLength
proxyReq.Header.Set("Content-Type", r.Header.Get("Content-Type"))
// 发送请求
resp, err := backend.Client.Do(proxyReq)
if err != nil {
http.Error(w, "Error sending proxy request", http.StatusInternalServerError)
return
}
defer resp.Body.Close()
// 复制目标服务器的响应 Header
for key, values := range resp.Header {
for _, value := range values {
w.Header().Add(key, value)
}
}
// 转发目标服务器的响应状态码和主体
w.WriteHeader(resp.StatusCode)
_, err = io.Copy(w, resp.Body)
if err != nil {
http.Error(w, "Error copying proxy response", http.StatusInternalServerError)
return
}
}
// 为每个路径注册处理函数
for _, muxPath := range muxPaths {
mux.HandleFunc(muxPath, handleRequest)
}
// 返回后端服务对象
return backend
}