2024-10-27 06:10:27 +01:00
package pkg
/ *
Copyright 2024 Jonas Kaninda
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
import (
"fmt"
2024-11-14 00:26:21 +01:00
"github.com/jkaninda/goma-gateway/internal/middleware"
2024-11-04 08:48:38 +01:00
"github.com/jkaninda/goma-gateway/pkg/logger"
2024-10-27 06:10:27 +01:00
"net/http"
"net/http/httputil"
"net/url"
2024-11-08 22:58:09 +01:00
"slices"
2024-10-27 06:10:27 +01:00
"strings"
2024-11-09 10:59:17 +01:00
"sync/atomic"
2024-10-27 06:10:27 +01:00
)
// ProxyHandler proxies requests to the backend
func ( proxyRoute ProxyRoute ) ProxyHandler ( ) http . HandlerFunc {
return func ( w http . ResponseWriter , r * http . Request ) {
2024-11-10 17:06:58 +01:00
logger . Info ( "%s %s %s %s" , r . Method , getRealIP ( r ) , r . URL . Path , r . UserAgent ( ) )
2024-11-10 19:58:53 +01:00
logger . Trace ( "Request params: %s" , r . URL . RawQuery )
logger . Trace ( "Request Headers: %s" , r . Header )
2024-11-08 22:58:09 +01:00
// Check Method if is allowed
if len ( proxyRoute . methods ) > 0 {
if ! slices . Contains ( proxyRoute . methods , r . Method ) {
logger . Error ( "%s Method is not allowed" , r . Method )
2024-11-14 00:26:21 +01:00
middleware . RespondWithError ( w , http . StatusMethodNotAllowed , fmt . Sprintf ( "%d %s method is not allowed" , http . StatusMethodNotAllowed , r . Method ) , proxyRoute . ErrorInterceptor )
2024-11-08 22:58:09 +01:00
return
}
}
2024-10-27 06:10:27 +01:00
// Set CORS headers from the cors config
//Update Cors Headers
for k , v := range proxyRoute . cors . Headers {
w . Header ( ) . Set ( k , v )
}
2024-10-29 10:35:31 +01:00
if allowedOrigin ( proxyRoute . cors . Origins , r . Header . Get ( "Origin" ) ) {
// Handle preflight requests (OPTIONS)
if r . Method == "OPTIONS" {
w . Header ( ) . Set ( accessControlAllowOrigin , r . Header . Get ( "Origin" ) )
w . WriteHeader ( http . StatusNoContent )
return
} else {
w . Header ( ) . Set ( accessControlAllowOrigin , r . Header . Get ( "Origin" ) )
2024-10-27 06:10:27 +01:00
}
}
// Parse the target backend URL
targetURL , err := url . Parse ( proxyRoute . destination )
if err != nil {
logger . Error ( "Error parsing backend URL: %s" , err )
2024-11-14 00:26:21 +01:00
middleware . RespondWithError ( w , http . StatusInternalServerError , http . StatusText ( http . StatusInternalServerError ) , proxyRoute . ErrorInterceptor )
2024-10-27 06:10:27 +01:00
return
}
2024-11-12 17:38:55 +01:00
r . Header . Set ( "X-Forwarded-Host" , r . Header . Get ( "Host" ) )
r . Header . Set ( "X-Forwarded-For" , getRealIP ( r ) )
r . Header . Set ( "X-Real-IP" , getRealIP ( r ) )
2024-10-27 06:10:27 +01:00
// Update the headers to allow for SSL redirection
2024-11-12 18:14:50 +01:00
if proxyRoute . disableHostFording {
2024-10-27 06:10:27 +01:00
r . URL . Scheme = targetURL . Scheme
2024-11-12 17:17:22 +01:00
r . Host = targetURL . Host
2024-10-27 06:10:27 +01:00
}
2024-11-09 10:59:17 +01:00
backendURL , _ := url . Parse ( proxyRoute . destination )
if len ( proxyRoute . backends ) > 0 {
// Select the next backend URL
backendURL = getNextBackend ( proxyRoute . backends )
}
2024-10-27 06:10:27 +01:00
// Create proxy
2024-11-09 10:59:17 +01:00
proxy := httputil . NewSingleHostReverseProxy ( backendURL )
2024-10-27 06:10:27 +01:00
// Rewrite
if proxyRoute . path != "" && proxyRoute . rewrite != "" {
// Rewrite the path
if strings . HasPrefix ( r . URL . Path , fmt . Sprintf ( "%s/" , proxyRoute . path ) ) {
r . URL . Path = strings . Replace ( r . URL . Path , fmt . Sprintf ( "%s/" , proxyRoute . path ) , proxyRoute . rewrite , 1 )
}
}
2024-11-02 11:55:37 +01:00
w . Header ( ) . Set ( "Proxied-By" , gatewayName ) //Set Server name
2024-11-09 05:45:49 +01:00
w . Header ( ) . Del ( "Server" ) // Remove the Server header
2024-10-27 06:10:27 +01:00
// Custom error handler for proxy errors
proxy . ErrorHandler = ProxyErrorHandler
proxy . ServeHTTP ( w , r )
}
}
2024-11-09 10:59:17 +01:00
// getNextBackend selects the next backend in a round-robin fashion
func getNextBackend ( backendURLs [ ] string ) * url . URL {
idx := atomic . AddUint32 ( & counter , 1 ) % uint32 ( len ( backendURLs ) )
backendURL , _ := url . Parse ( backendURLs [ idx ] )
return backendURL
}