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 }