66 "net/http/httputil"
77 "net/url"
88 "strings"
9+ "sync"
910 "time"
1011
1112 "github.com/mirkobrombin/goup/internal/config"
@@ -20,15 +21,16 @@ func createHandler(conf config.SiteConfig, logger *log.Logger, identifier string
2021
2122 if conf .ProxyPass != "" {
2223 // Set up reverse proxy handler if ProxyPass is set.
23- proxyURL , err := url . Parse (conf .ProxyPass )
24+ proxy , err := getSharedReverseProxy (conf .ProxyPass )
2425 if err != nil {
2526 return nil , fmt .Errorf ("invalid proxy URL: %v" , err )
2627 }
27- proxy := httputil . NewSingleHostReverseProxy ( proxyURL )
28+
2829 handler = http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
2930 addCustomHeaders (w , conf .CustomHeaders )
3031 proxy .ServeHTTP (w , r )
3132 })
33+
3234 } else {
3335 // Serve static files from the root directory.
3436 fs := http .FileServer (http .Dir (conf .RootDirectory ))
@@ -52,7 +54,7 @@ func createHandler(conf config.SiteConfig, logger *log.Logger, identifier string
5254 if reqTimeout == 0 {
5355 reqTimeout = 60 // Default to 60 seconds
5456 }
55- timeout := time .Duration (conf . RequestTimeout ) * time .Second
57+ timeout := time .Duration (reqTimeout ) * time .Second
5658 siteMwManager .Use (middleware .TimeoutMiddleware (timeout ))
5759
5860 // Add logging middleware last to ensure it wraps the entire request
@@ -70,11 +72,60 @@ func addCustomHeaders(w http.ResponseWriter, headers map[string]string) {
7072 w .Header ().Set (key , value )
7173 }
7274
73- // Expose custom headers to the client.
74- exposeHeaders := []string {}
75+ exposeHeaders := make ([]string , 0 , len (headers ))
7576 for key := range headers {
7677 exposeHeaders = append (exposeHeaders , key )
7778 }
7879
7980 w .Header ().Set ("Access-Control-Expose-Headers" , strings .Join (exposeHeaders , ", " ))
8081}
82+
83+ var (
84+ sharedProxyMap = make (map [string ]* httputil.ReverseProxy )
85+ sharedProxyMapMu sync.Mutex
86+ defaultTransport = & http.Transport {}
87+
88+ globalBytePool = & byteSlicePool {
89+ pool : sync.Pool {
90+ New : func () interface {} {
91+ return make ([]byte , 32 * 1024 )
92+ },
93+ },
94+ }
95+ )
96+
97+ type byteSlicePool struct {
98+ pool sync.Pool
99+ }
100+
101+ func (b * byteSlicePool ) Get () []byte {
102+ return b .pool .Get ().([]byte )
103+ }
104+
105+ func (b * byteSlicePool ) Put (buf []byte ) {
106+ if cap (buf ) == 32 * 1024 {
107+ b .pool .Put (buf [:32 * 1024 ])
108+ }
109+ }
110+
111+ // getSharedReverseProxy returns a shared ReverseProxy for the given backend URL.
112+ func getSharedReverseProxy (rawURL string ) (* httputil.ReverseProxy , error ) {
113+ sharedProxyMapMu .Lock ()
114+ defer sharedProxyMapMu .Unlock ()
115+
116+ if rp , ok := sharedProxyMap [rawURL ]; ok {
117+ return rp , nil
118+ }
119+
120+ parsedURL , err := url .Parse (rawURL )
121+ if err != nil {
122+ return nil , err
123+ }
124+
125+ rp := httputil .NewSingleHostReverseProxy (parsedURL )
126+ rp .Transport = defaultTransport
127+ rp .BufferPool = globalBytePool
128+
129+ sharedProxyMap [rawURL ] = rp
130+ return rp , nil
131+ }
0 commit comments