fusenapi/proxyserver/main.go

169 lines
4.0 KiB
Go

package main
import (
"fmt"
"io"
"log"
"net"
"net/http"
"net/http/httputil"
"net/url"
"strings"
"time"
)
var Backends []*Backend
func main() {
rootDir := "../server" // Change this to your root directory
vueBuild := "/home/eson/workspace/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...))
}
// Define the static file server that serves the Vue dist folder
fs := http.FileServer(http.Dir(vueBuild))
// Define a handler function for the Vue static assets
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
var prefix string
idx := strings.Index(r.URL.Path[1:], "/")
if idx == -1 {
prefix = r.URL.Path
} else {
prefix = r.URL.Path[:idx]
}
// log.Println(r.URL.Path, prefix)
if _, ok := allRoutes[prefix]; ok {
proxy := httputil.NewSingleHostReverseProxy(apiURL)
proxy.ServeHTTP(w, r)
} else {
// fs.ServeHTTP(w, r)
if strings.HasPrefix(prefix, "/type") {
http.ServeFile(w, r, vueBuild+"/index.html")
} else if strings.HasPrefix(prefix, "/dt-") {
http.ServeFile(w, r, vueBuild+"/index.html")
} else {
fs.ServeHTTP(w, r)
}
}
})
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
}
}
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) {
targetURL, err := url.Parse(httpAddress + r.URL.Path)
if err != nil {
http.Error(w, "Error parsing target URL", http.StatusInternalServerError)
return
}
// 解析目标URL时已经包含了查询参数
targetURL.RawQuery = r.URL.RawQuery
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
}