refactor: move types into a single file for each package
This commit is contained in:
160
pkg/config.go
160
pkg/config.go
@@ -16,7 +16,6 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/jkaninda/goma-gateway/internal/logger"
|
"github.com/jkaninda/goma-gateway/internal/logger"
|
||||||
"github.com/jkaninda/goma-gateway/util"
|
"github.com/jkaninda/goma-gateway/util"
|
||||||
@@ -27,165 +26,6 @@ import (
|
|||||||
|
|
||||||
var cfg *Gateway
|
var cfg *Gateway
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
file string
|
|
||||||
}
|
|
||||||
type BasicRuleMiddleware struct {
|
|
||||||
Username string `yaml:"username"`
|
|
||||||
Password string `yaml:"password"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Cors struct {
|
|
||||||
// Cors Allowed origins,
|
|
||||||
//e.g:
|
|
||||||
//
|
|
||||||
// - http://localhost:80
|
|
||||||
//
|
|
||||||
// - https://example.com
|
|
||||||
Origins []string `yaml:"origins"`
|
|
||||||
//
|
|
||||||
//e.g:
|
|
||||||
//
|
|
||||||
//Access-Control-Allow-Origin: '*'
|
|
||||||
//
|
|
||||||
// Access-Control-Allow-Methods: 'GET, POST, PUT, DELETE, OPTIONS'
|
|
||||||
//
|
|
||||||
// Access-Control-Allow-Cors: 'Content-Type, Authorization'
|
|
||||||
Headers map[string]string `yaml:"headers"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// JWTRuleMiddleware authentication using HTTP GET method
|
|
||||||
//
|
|
||||||
// JWTRuleMiddleware contains the authentication details
|
|
||||||
type JWTRuleMiddleware struct {
|
|
||||||
// URL contains the authentication URL, it supports HTTP GET method only.
|
|
||||||
URL string `yaml:"url"`
|
|
||||||
// RequiredHeaders , contains required before sending request to the backend.
|
|
||||||
RequiredHeaders []string `yaml:"requiredHeaders"`
|
|
||||||
// Headers Add header to the backend from Authentication request's header, depending on your requirements.
|
|
||||||
// Key is Http's response header Key, and value is the backend Request's header Key.
|
|
||||||
// In case you want to get headers from Authentication service and inject them to backend request's headers.
|
|
||||||
Headers map[string]string `yaml:"headers"`
|
|
||||||
// Params same as Headers, contains the request params.
|
|
||||||
//
|
|
||||||
// Gets authentication headers from authentication request and inject them as request params to the backend.
|
|
||||||
//
|
|
||||||
// Key is Http's response header Key, and value is the backend Request's request param Key.
|
|
||||||
//
|
|
||||||
// In case you want to get headers from Authentication service and inject them to next request's params.
|
|
||||||
//
|
|
||||||
//e.g: Header X-Auth-UserId to query userId
|
|
||||||
Params map[string]string `yaml:"params"`
|
|
||||||
}
|
|
||||||
type RateLimiter struct {
|
|
||||||
// ipBased, tokenBased
|
|
||||||
Type string `yaml:"type"`
|
|
||||||
Rate float64 `yaml:"rate"`
|
|
||||||
Rule int `yaml:"rule"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type AccessRuleMiddleware struct {
|
|
||||||
ResponseCode int `yaml:"responseCode"` // HTTP Response code
|
|
||||||
}
|
|
||||||
|
|
||||||
// Middleware defined the route middleware
|
|
||||||
type Middleware struct {
|
|
||||||
//Path contains the name of middleware and must be unique
|
|
||||||
Name string `yaml:"name"`
|
|
||||||
// Type contains authentication types
|
|
||||||
//
|
|
||||||
// basic, jwt, auth0, rateLimit, access
|
|
||||||
Type string `yaml:"type"` // Middleware type [basic, jwt, auth0, rateLimit, access]
|
|
||||||
Paths []string `yaml:"paths"` // Protected paths
|
|
||||||
// Rule contains rule type of
|
|
||||||
Rule interface{} `yaml:"rule"` // Middleware rule
|
|
||||||
}
|
|
||||||
type MiddlewareName struct {
|
|
||||||
name string `yaml:"name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Route defines gateway route
|
|
||||||
type Route struct {
|
|
||||||
// Name defines route name
|
|
||||||
Name string `yaml:"name"`
|
|
||||||
//Host Domain/host based request routing
|
|
||||||
Host string `yaml:"host"`
|
|
||||||
// Path defines route path
|
|
||||||
Path string `yaml:"path"`
|
|
||||||
// Rewrite rewrites route path to desired path
|
|
||||||
//
|
|
||||||
// E.g. /cart to / => It will rewrite /cart path to /
|
|
||||||
Rewrite string `yaml:"rewrite"`
|
|
||||||
// Destination Defines backend URL
|
|
||||||
Destination string `yaml:"destination"`
|
|
||||||
// Cors contains the route cors headers
|
|
||||||
Cors Cors `yaml:"cors"`
|
|
||||||
// DisableHeaderXForward Disable X-forwarded header.
|
|
||||||
//
|
|
||||||
// [X-Forwarded-Host, X-Forwarded-For, Host, Scheme ]
|
|
||||||
//
|
|
||||||
// It will not match the backend route
|
|
||||||
DisableHeaderXForward bool `yaml:"disableHeaderXForward"`
|
|
||||||
// HealthCheck Defines the backend is health check PATH
|
|
||||||
HealthCheck string `yaml:"healthCheck"`
|
|
||||||
// InterceptErrors intercepts backend errors based on the status codes
|
|
||||||
//
|
|
||||||
// Eg: [ 403, 405, 500 ]
|
|
||||||
InterceptErrors []int `yaml:"interceptErrors"`
|
|
||||||
// Middlewares Defines route middleware from Middleware names
|
|
||||||
Middlewares []string `yaml:"middlewares"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gateway contains Goma Proxy Gateway's configs
|
|
||||||
type Gateway struct {
|
|
||||||
// ListenAddr Defines the server listenAddr
|
|
||||||
//
|
|
||||||
//e.g: localhost:8080
|
|
||||||
ListenAddr string `yaml:"listenAddr" env:"GOMA_LISTEN_ADDR, overwrite"`
|
|
||||||
// WriteTimeout defines proxy write timeout
|
|
||||||
WriteTimeout int `yaml:"writeTimeout" env:"GOMA_WRITE_TIMEOUT, overwrite"`
|
|
||||||
// ReadTimeout defines proxy read timeout
|
|
||||||
ReadTimeout int `yaml:"readTimeout" env:"GOMA_READ_TIMEOUT, overwrite"`
|
|
||||||
// IdleTimeout defines proxy idle timeout
|
|
||||||
IdleTimeout int `yaml:"idleTimeout" env:"GOMA_IDLE_TIMEOUT, overwrite"`
|
|
||||||
// RateLimiter Defines number of request peer minute
|
|
||||||
RateLimiter int `yaml:"rateLimiter" env:"GOMA_RATE_LIMITER, overwrite"`
|
|
||||||
AccessLog string `yaml:"accessLog" env:"GOMA_ACCESS_LOG, overwrite"`
|
|
||||||
ErrorLog string `yaml:"errorLog" env:"GOMA_ERROR_LOG=, overwrite"`
|
|
||||||
// DisableHealthCheckStatus enable and disable routes health check
|
|
||||||
DisableHealthCheckStatus bool `yaml:"disableHealthCheckStatus"`
|
|
||||||
// DisableRouteHealthCheckError allows enabling and disabling backend healthcheck errors
|
|
||||||
DisableRouteHealthCheckError bool `yaml:"disableRouteHealthCheckError"`
|
|
||||||
//Disable allows enabling and disabling displaying routes on start
|
|
||||||
DisableDisplayRouteOnStart bool `yaml:"disableDisplayRouteOnStart"`
|
|
||||||
// DisableKeepAlive allows enabling and disabling KeepALive server
|
|
||||||
DisableKeepAlive bool `yaml:"disableKeepAlive"`
|
|
||||||
// InterceptErrors holds the status codes to intercept the error from backend
|
|
||||||
InterceptErrors []int `yaml:"interceptErrors"`
|
|
||||||
// Cors holds proxy global cors
|
|
||||||
Cors Cors `yaml:"cors"`
|
|
||||||
// Routes holds proxy routes
|
|
||||||
Routes []Route `yaml:"routes"`
|
|
||||||
}
|
|
||||||
type GatewayConfig struct {
|
|
||||||
// GatewayConfig holds Gateway config
|
|
||||||
GatewayConfig Gateway `yaml:"gateway"`
|
|
||||||
// Middlewares holds proxy middlewares
|
|
||||||
Middlewares []Middleware `yaml:"middlewares"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrorResponse represents the structure of the JSON error response
|
|
||||||
type ErrorResponse struct {
|
|
||||||
Success bool `json:"success"`
|
|
||||||
Code int `json:"code"`
|
|
||||||
Message string `json:"message"`
|
|
||||||
}
|
|
||||||
type GatewayServer struct {
|
|
||||||
ctx context.Context
|
|
||||||
gateway Gateway
|
|
||||||
middlewares []Middleware
|
|
||||||
}
|
|
||||||
|
|
||||||
// Config reads config file and returns Gateway
|
// Config reads config file and returns Gateway
|
||||||
func (GatewayServer) Config(configFile string) (*GatewayServer, error) {
|
func (GatewayServer) Config(configFile string) (*GatewayServer, error) {
|
||||||
if util.FileExists(configFile) {
|
if util.FileExists(configFile) {
|
||||||
|
|||||||
@@ -23,22 +23,6 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HealthCheckRoute struct {
|
|
||||||
DisableRouteHealthCheckError bool
|
|
||||||
Routes []Route
|
|
||||||
}
|
|
||||||
|
|
||||||
// HealthCheckResponse represents the health check response structure
|
|
||||||
type HealthCheckResponse struct {
|
|
||||||
Status string `json:"status"`
|
|
||||||
Routes []HealthCheckRouteResponse `json:"routes"`
|
|
||||||
}
|
|
||||||
type HealthCheckRouteResponse struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Status string `json:"status"`
|
|
||||||
Error string `json:"error"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func HealthCheck(healthURL string) error {
|
func HealthCheck(healthURL string) error {
|
||||||
healthCheckURL, err := url.Parse(healthURL)
|
healthCheckURL, err := url.Parse(healthURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package pkg
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -18,14 +17,6 @@ func getMiddleware(rules []string, middlewares []Middleware) (Middleware, error)
|
|||||||
return Middleware{}, errors.New("middleware not found with name: [" + strings.Join(rules, ";") + "]")
|
return Middleware{}, errors.New("middleware not found with name: [" + strings.Join(rules, ";") + "]")
|
||||||
}
|
}
|
||||||
|
|
||||||
type RoutePath struct {
|
|
||||||
route Route
|
|
||||||
path string
|
|
||||||
rules []string
|
|
||||||
middlewares []Middleware
|
|
||||||
router *mux.Router
|
|
||||||
}
|
|
||||||
|
|
||||||
func doesExist(tyName string) bool {
|
func doesExist(tyName string) bool {
|
||||||
middlewareList := []string{BasicAuth, JWTAuth, AccessMiddleware}
|
middlewareList := []string{BasicAuth, JWTAuth, AccessMiddleware}
|
||||||
if slices.Contains(middlewareList, tyName) {
|
if slices.Contains(middlewareList, tyName) {
|
||||||
|
|||||||
@@ -24,18 +24,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
// InterceptErrors contains backend status code errors to intercept
|
|
||||||
type InterceptErrors struct {
|
|
||||||
Errors []int
|
|
||||||
}
|
|
||||||
|
|
||||||
// responseRecorder intercepts the response body and status code
|
|
||||||
type responseRecorder struct {
|
|
||||||
http.ResponseWriter
|
|
||||||
statusCode int
|
|
||||||
body *bytes.Buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
func newResponseRecorder(w http.ResponseWriter) *responseRecorder {
|
func newResponseRecorder(w http.ResponseWriter) *responseRecorder {
|
||||||
return &responseRecorder{
|
return &responseRecorder{
|
||||||
ResponseWriter: w,
|
ResponseWriter: w,
|
||||||
@@ -59,7 +47,7 @@ func (intercept InterceptErrors) ErrorInterceptor(next http.Handler) http.Handle
|
|||||||
next.ServeHTTP(rec, r)
|
next.ServeHTTP(rec, r)
|
||||||
if canIntercept(rec.statusCode, intercept.Errors) {
|
if canIntercept(rec.statusCode, intercept.Errors) {
|
||||||
logger.Error("Backend error")
|
logger.Error("Backend error")
|
||||||
logger.Error("An error occurred in the backend, %d", rec.statusCode)
|
logger.Error("An error occurred from the backend with the status code: %d", rec.statusCode)
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.WriteHeader(rec.statusCode)
|
w.WriteHeader(rec.statusCode)
|
||||||
err := json.NewEncoder(w).Encode(ProxyResponseError{
|
err := json.NewEncoder(w).Encode(ProxyResponseError{
|
||||||
|
|||||||
@@ -23,78 +23,8 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// RateLimiter defines rate limit properties.
|
|
||||||
type RateLimiter struct {
|
|
||||||
Requests int
|
|
||||||
Window time.Duration
|
|
||||||
ClientMap map[string]*Client
|
|
||||||
mu sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// Client stores request count and window expiration for each client.
|
|
||||||
type Client struct {
|
|
||||||
RequestCount int
|
|
||||||
ExpiresAt time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewRateLimiterWindow creates a new RateLimiter.
|
|
||||||
func NewRateLimiterWindow(requests int, window time.Duration) *RateLimiter {
|
|
||||||
return &RateLimiter{
|
|
||||||
Requests: requests,
|
|
||||||
Window: window,
|
|
||||||
ClientMap: make(map[string]*Client),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TokenRateLimiter stores tokenRate limit
|
|
||||||
type TokenRateLimiter struct {
|
|
||||||
tokens int
|
|
||||||
maxTokens int
|
|
||||||
refillRate time.Duration
|
|
||||||
lastRefill time.Time
|
|
||||||
mu sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProxyResponseError represents the structure of the JSON error response
|
|
||||||
type ProxyResponseError struct {
|
|
||||||
Success bool `json:"success"`
|
|
||||||
Code int `json:"code"`
|
|
||||||
Message string `json:"message"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// JwtAuth stores JWT configuration
|
|
||||||
type JwtAuth struct {
|
|
||||||
AuthURL string
|
|
||||||
RequiredHeaders []string
|
|
||||||
Headers map[string]string
|
|
||||||
Params map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
// AuthenticationMiddleware Define struct
|
|
||||||
type AuthenticationMiddleware struct {
|
|
||||||
AuthURL string
|
|
||||||
RequiredHeaders []string
|
|
||||||
Headers map[string]string
|
|
||||||
Params map[string]string
|
|
||||||
}
|
|
||||||
type AccessListMiddleware struct {
|
|
||||||
Path string
|
|
||||||
Destination string
|
|
||||||
List []string
|
|
||||||
}
|
|
||||||
|
|
||||||
// AuthBasic contains Basic auth configuration
|
|
||||||
type AuthBasic struct {
|
|
||||||
Username string
|
|
||||||
Password string
|
|
||||||
Headers map[string]string
|
|
||||||
Params map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
// AuthMiddleware authenticate the client using JWT
|
// AuthMiddleware authenticate the client using JWT
|
||||||
//
|
//
|
||||||
// authorization based on the result of backend's response and continue the request when the client is authorized
|
// authorization based on the result of backend's response and continue the request when the client is authorized
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ func (rl *TokenRateLimiter) RateLimitMiddleware() mux.MiddlewareFunc {
|
|||||||
err := json.NewEncoder(w).Encode(ProxyResponseError{
|
err := json.NewEncoder(w).Encode(ProxyResponseError{
|
||||||
Success: false,
|
Success: false,
|
||||||
Code: http.StatusTooManyRequests,
|
Code: http.StatusTooManyRequests,
|
||||||
Message: "Too many requests. Please try again later.",
|
Message: "Too many requests, API rate limit exceeded. Please try again later.",
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@@ -66,13 +66,13 @@ func (rl *RateLimiter) RateLimitMiddleware() mux.MiddlewareFunc {
|
|||||||
rl.mu.Unlock()
|
rl.mu.Unlock()
|
||||||
|
|
||||||
if client.RequestCount > rl.Requests {
|
if client.RequestCount > rl.Requests {
|
||||||
logger.Error("Too many request from IP: %s %s %s", clientID, r.URL, r.UserAgent())
|
logger.Error("Too many requests from IP: %s %s %s", clientID, r.URL, r.UserAgent())
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.WriteHeader(http.StatusTooManyRequests)
|
w.WriteHeader(http.StatusTooManyRequests)
|
||||||
err := json.NewEncoder(w).Encode(ProxyResponseError{
|
err := json.NewEncoder(w).Encode(ProxyResponseError{
|
||||||
Success: false,
|
Success: false,
|
||||||
Code: http.StatusTooManyRequests,
|
Code: http.StatusTooManyRequests,
|
||||||
Message: "Too many requests. Please try again later.",
|
Message: "Too many requests, API rate limit exceeded. Please try again later.",
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
105
pkg/middleware/types.go
Normal file
105
pkg/middleware/types.go
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RateLimiter defines rate limit properties.
|
||||||
|
type RateLimiter struct {
|
||||||
|
Requests int
|
||||||
|
Window time.Duration
|
||||||
|
ClientMap map[string]*Client
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client stores request count and window expiration for each client.
|
||||||
|
type Client struct {
|
||||||
|
RequestCount int
|
||||||
|
ExpiresAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRateLimiterWindow creates a new RateLimiter.
|
||||||
|
func NewRateLimiterWindow(requests int, window time.Duration) *RateLimiter {
|
||||||
|
return &RateLimiter{
|
||||||
|
Requests: requests,
|
||||||
|
Window: window,
|
||||||
|
ClientMap: make(map[string]*Client),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TokenRateLimiter stores tokenRate limit
|
||||||
|
type TokenRateLimiter struct {
|
||||||
|
tokens int
|
||||||
|
maxTokens int
|
||||||
|
refillRate time.Duration
|
||||||
|
lastRefill time.Time
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProxyResponseError represents the structure of the JSON error response
|
||||||
|
type ProxyResponseError struct {
|
||||||
|
Success bool `json:"success"`
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// JwtAuth stores JWT configuration
|
||||||
|
type JwtAuth struct {
|
||||||
|
AuthURL string
|
||||||
|
RequiredHeaders []string
|
||||||
|
Headers map[string]string
|
||||||
|
Params map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthenticationMiddleware Define struct
|
||||||
|
type AuthenticationMiddleware struct {
|
||||||
|
AuthURL string
|
||||||
|
RequiredHeaders []string
|
||||||
|
Headers map[string]string
|
||||||
|
Params map[string]string
|
||||||
|
}
|
||||||
|
type AccessListMiddleware struct {
|
||||||
|
Path string
|
||||||
|
Destination string
|
||||||
|
List []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthBasic contains Basic auth configuration
|
||||||
|
type AuthBasic struct {
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
Headers map[string]string
|
||||||
|
Params map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// InterceptErrors contains backend status code errors to intercept
|
||||||
|
type InterceptErrors struct {
|
||||||
|
Errors []int
|
||||||
|
}
|
||||||
|
|
||||||
|
// responseRecorder intercepts the response body and status code
|
||||||
|
type responseRecorder struct {
|
||||||
|
http.ResponseWriter
|
||||||
|
statusCode int
|
||||||
|
body *bytes.Buffer
|
||||||
|
}
|
||||||
15
pkg/proxy.go
15
pkg/proxy.go
@@ -25,19 +25,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ProxyRoute struct {
|
|
||||||
path string
|
|
||||||
rewrite string
|
|
||||||
destination string
|
|
||||||
cors Cors
|
|
||||||
disableXForward bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProxyHandler proxies requests to the backend
|
// ProxyHandler proxies requests to the backend
|
||||||
func (proxyRoute ProxyRoute) ProxyHandler() http.HandlerFunc {
|
func (proxyRoute ProxyRoute) ProxyHandler() http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
realIP := getRealIP(r)
|
logger.Info("%s %s %s %s", r.Method, getRealIP(r), r.URL, r.UserAgent())
|
||||||
logger.Info("%s %s %s %s", r.Method, realIP, r.URL, r.UserAgent())
|
|
||||||
// Set CORS headers from the cors config
|
// Set CORS headers from the cors config
|
||||||
//Update Cors Headers
|
//Update Cors Headers
|
||||||
for k, v := range proxyRoute.cors.Headers {
|
for k, v := range proxyRoute.cors.Headers {
|
||||||
@@ -75,8 +66,8 @@ func (proxyRoute ProxyRoute) ProxyHandler() http.HandlerFunc {
|
|||||||
r.URL.Host = targetURL.Host
|
r.URL.Host = targetURL.Host
|
||||||
r.URL.Scheme = targetURL.Scheme
|
r.URL.Scheme = targetURL.Scheme
|
||||||
r.Header.Set("X-Forwarded-Host", r.Header.Get("Host"))
|
r.Header.Set("X-Forwarded-Host", r.Header.Get("Host"))
|
||||||
r.Header.Set("X-Forwarded-For", realIP)
|
r.Header.Set("X-Forwarded-For", getRealIP(r))
|
||||||
r.Header.Set("X-Real-IP", realIP)
|
r.Header.Set("X-Real-IP", getRealIP(r))
|
||||||
r.Host = targetURL.Host
|
r.Host = targetURL.Host
|
||||||
}
|
}
|
||||||
// Create proxy
|
// Create proxy
|
||||||
|
|||||||
214
pkg/types.go
Normal file
214
pkg/types.go
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pkg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
file string
|
||||||
|
}
|
||||||
|
type BasicRuleMiddleware struct {
|
||||||
|
Username string `yaml:"username"`
|
||||||
|
Password string `yaml:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Cors struct {
|
||||||
|
// Cors Allowed origins,
|
||||||
|
//e.g:
|
||||||
|
//
|
||||||
|
// - http://localhost:80
|
||||||
|
//
|
||||||
|
// - https://example.com
|
||||||
|
Origins []string `yaml:"origins"`
|
||||||
|
//
|
||||||
|
//e.g:
|
||||||
|
//
|
||||||
|
//Access-Control-Allow-Origin: '*'
|
||||||
|
//
|
||||||
|
// Access-Control-Allow-Methods: 'GET, POST, PUT, DELETE, OPTIONS'
|
||||||
|
//
|
||||||
|
// Access-Control-Allow-Cors: 'Content-Type, Authorization'
|
||||||
|
Headers map[string]string `yaml:"headers"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// JWTRuleMiddleware authentication using HTTP GET method
|
||||||
|
//
|
||||||
|
// JWTRuleMiddleware contains the authentication details
|
||||||
|
type JWTRuleMiddleware struct {
|
||||||
|
// URL contains the authentication URL, it supports HTTP GET method only.
|
||||||
|
URL string `yaml:"url"`
|
||||||
|
// RequiredHeaders , contains required before sending request to the backend.
|
||||||
|
RequiredHeaders []string `yaml:"requiredHeaders"`
|
||||||
|
// Headers Add header to the backend from Authentication request's header, depending on your requirements.
|
||||||
|
// Key is Http's response header Key, and value is the backend Request's header Key.
|
||||||
|
// In case you want to get headers from Authentication service and inject them to backend request's headers.
|
||||||
|
Headers map[string]string `yaml:"headers"`
|
||||||
|
// Params same as Headers, contains the request params.
|
||||||
|
//
|
||||||
|
// Gets authentication headers from authentication request and inject them as request params to the backend.
|
||||||
|
//
|
||||||
|
// Key is Http's response header Key, and value is the backend Request's request param Key.
|
||||||
|
//
|
||||||
|
// In case you want to get headers from Authentication service and inject them to next request's params.
|
||||||
|
//
|
||||||
|
//e.g: Header X-Auth-UserId to query userId
|
||||||
|
Params map[string]string `yaml:"params"`
|
||||||
|
}
|
||||||
|
type RateLimiter struct {
|
||||||
|
// ipBased, tokenBased
|
||||||
|
Type string `yaml:"type"`
|
||||||
|
Rate float64 `yaml:"rate"`
|
||||||
|
Rule int `yaml:"rule"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccessRuleMiddleware struct {
|
||||||
|
ResponseCode int `yaml:"responseCode"` // HTTP Response code
|
||||||
|
}
|
||||||
|
|
||||||
|
// Middleware defined the route middleware
|
||||||
|
type Middleware struct {
|
||||||
|
//Path contains the name of middleware and must be unique
|
||||||
|
Name string `yaml:"name"`
|
||||||
|
// Type contains authentication types
|
||||||
|
//
|
||||||
|
// basic, jwt, auth0, rateLimit, access
|
||||||
|
Type string `yaml:"type"` // Middleware type [basic, jwt, auth0, rateLimit, access]
|
||||||
|
Paths []string `yaml:"paths"` // Protected paths
|
||||||
|
// Rule contains rule type of
|
||||||
|
Rule interface{} `yaml:"rule"` // Middleware rule
|
||||||
|
}
|
||||||
|
type MiddlewareName struct {
|
||||||
|
name string `yaml:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Route defines gateway route
|
||||||
|
type Route struct {
|
||||||
|
// Name defines route name
|
||||||
|
Name string `yaml:"name"`
|
||||||
|
//Host Domain/host based request routing
|
||||||
|
Host string `yaml:"host"`
|
||||||
|
// Path defines route path
|
||||||
|
Path string `yaml:"path"`
|
||||||
|
// Rewrite rewrites route path to desired path
|
||||||
|
//
|
||||||
|
// E.g. /cart to / => It will rewrite /cart path to /
|
||||||
|
Rewrite string `yaml:"rewrite"`
|
||||||
|
// Destination Defines backend URL
|
||||||
|
Destination string `yaml:"destination"`
|
||||||
|
// Cors contains the route cors headers
|
||||||
|
Cors Cors `yaml:"cors"`
|
||||||
|
// DisableHeaderXForward Disable X-forwarded header.
|
||||||
|
//
|
||||||
|
// [X-Forwarded-Host, X-Forwarded-For, Host, Scheme ]
|
||||||
|
//
|
||||||
|
// It will not match the backend route
|
||||||
|
DisableHeaderXForward bool `yaml:"disableHeaderXForward"`
|
||||||
|
// HealthCheck Defines the backend is health check PATH
|
||||||
|
HealthCheck string `yaml:"healthCheck"`
|
||||||
|
// InterceptErrors intercepts backend errors based on the status codes
|
||||||
|
//
|
||||||
|
// Eg: [ 403, 405, 500 ]
|
||||||
|
InterceptErrors []int `yaml:"interceptErrors"`
|
||||||
|
// Middlewares Defines route middleware from Middleware names
|
||||||
|
Middlewares []string `yaml:"middlewares"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gateway contains Goma Proxy Gateway's configs
|
||||||
|
type Gateway struct {
|
||||||
|
// ListenAddr Defines the server listenAddr
|
||||||
|
//
|
||||||
|
//e.g: localhost:8080
|
||||||
|
ListenAddr string `yaml:"listenAddr" env:"GOMA_LISTEN_ADDR, overwrite"`
|
||||||
|
// WriteTimeout defines proxy write timeout
|
||||||
|
WriteTimeout int `yaml:"writeTimeout" env:"GOMA_WRITE_TIMEOUT, overwrite"`
|
||||||
|
// ReadTimeout defines proxy read timeout
|
||||||
|
ReadTimeout int `yaml:"readTimeout" env:"GOMA_READ_TIMEOUT, overwrite"`
|
||||||
|
// IdleTimeout defines proxy idle timeout
|
||||||
|
IdleTimeout int `yaml:"idleTimeout" env:"GOMA_IDLE_TIMEOUT, overwrite"`
|
||||||
|
// RateLimiter Defines number of request peer minute
|
||||||
|
RateLimiter int `yaml:"rateLimiter" env:"GOMA_RATE_LIMITER, overwrite"`
|
||||||
|
AccessLog string `yaml:"accessLog" env:"GOMA_ACCESS_LOG, overwrite"`
|
||||||
|
ErrorLog string `yaml:"errorLog" env:"GOMA_ERROR_LOG=, overwrite"`
|
||||||
|
// DisableHealthCheckStatus enable and disable routes health check
|
||||||
|
DisableHealthCheckStatus bool `yaml:"disableHealthCheckStatus"`
|
||||||
|
// DisableRouteHealthCheckError allows enabling and disabling backend healthcheck errors
|
||||||
|
DisableRouteHealthCheckError bool `yaml:"disableRouteHealthCheckError"`
|
||||||
|
//Disable allows enabling and disabling displaying routes on start
|
||||||
|
DisableDisplayRouteOnStart bool `yaml:"disableDisplayRouteOnStart"`
|
||||||
|
// DisableKeepAlive allows enabling and disabling KeepALive server
|
||||||
|
DisableKeepAlive bool `yaml:"disableKeepAlive"`
|
||||||
|
// InterceptErrors holds the status codes to intercept the error from backend
|
||||||
|
InterceptErrors []int `yaml:"interceptErrors"`
|
||||||
|
// Cors holds proxy global cors
|
||||||
|
Cors Cors `yaml:"cors"`
|
||||||
|
// Routes holds proxy routes
|
||||||
|
Routes []Route `yaml:"routes"`
|
||||||
|
}
|
||||||
|
type GatewayConfig struct {
|
||||||
|
// GatewayConfig holds Gateway config
|
||||||
|
GatewayConfig Gateway `yaml:"gateway"`
|
||||||
|
// Middlewares holds proxy middlewares
|
||||||
|
Middlewares []Middleware `yaml:"middlewares"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorResponse represents the structure of the JSON error response
|
||||||
|
type ErrorResponse struct {
|
||||||
|
Success bool `json:"success"`
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
type GatewayServer struct {
|
||||||
|
ctx context.Context
|
||||||
|
gateway Gateway
|
||||||
|
middlewares []Middleware
|
||||||
|
}
|
||||||
|
type ProxyRoute struct {
|
||||||
|
path string
|
||||||
|
rewrite string
|
||||||
|
destination string
|
||||||
|
cors Cors
|
||||||
|
disableXForward bool
|
||||||
|
}
|
||||||
|
type RoutePath struct {
|
||||||
|
route Route
|
||||||
|
path string
|
||||||
|
rules []string
|
||||||
|
middlewares []Middleware
|
||||||
|
router *mux.Router
|
||||||
|
}
|
||||||
|
|
||||||
|
type HealthCheckRoute struct {
|
||||||
|
DisableRouteHealthCheckError bool
|
||||||
|
Routes []Route
|
||||||
|
}
|
||||||
|
|
||||||
|
// HealthCheckResponse represents the health check response structure
|
||||||
|
type HealthCheckResponse struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
Routes []HealthCheckRouteResponse `json:"routes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// HealthCheckRouteResponse represents the health check response for a route
|
||||||
|
type HealthCheckRouteResponse struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Error string `json:"error"`
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user