fusenapi/proxyserver/main.go
2023-07-11 18:48:22 +08:00

188 lines
4.7 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"
"time"
)
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
}
}
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))
indexHtmlPath := vueBuild + "/index.html"
mux.Handle("/", http.HandlerFunc(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, indexHtmlPath)
} else if strings.HasPrefix(prefix, "/dt-") {
http.ServeFile(w, r, indexHtmlPath)
} 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
}
hasBridge := strings.Contains(r.Header.Get("Access-Control-Request-Headers"), "bridge")
// 解析目标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)
}
}
if hasBridge {
w.Header().Add("Access-Control-Allow-Headers", "Bridge")
}
// 转发目标服务器的响应状态码和主体
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
}