mirror of https://github.com/gofiber/fiber.git
🩹 fix proxy middleware
parent
a24e33be80
commit
4662dd8219
|
@ -1,5 +1,5 @@
|
|||
# Proxy
|
||||
Proxy middleware for [Fiber](https://github.com/gofiber/fiber) that allows you to proxy requests to multiple hosts.
|
||||
Proxy middleware for [Fiber](https://github.com/gofiber/fiber) that allows you to proxy requests to multiple servers.
|
||||
|
||||
### Table of Contents
|
||||
- [Signatures](#signatures)
|
||||
|
@ -10,32 +10,60 @@ Proxy middleware for [Fiber](https://github.com/gofiber/fiber) that allows you t
|
|||
|
||||
### Signatures
|
||||
```go
|
||||
func New(config Config) fiber.Handler
|
||||
func Balancer(config Config) fiber.Handler
|
||||
func Forward(addr string) fiber.Handler
|
||||
func Do(c *fiber.Ctx, addr string) error
|
||||
```
|
||||
|
||||
### Examples
|
||||
Import the middleware package that is part of the Fiber web framework
|
||||
```go
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/proxy"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/proxy"
|
||||
)
|
||||
```
|
||||
|
||||
After you initiate your Fiber app, you can use the following possibilities:
|
||||
```go
|
||||
// Minimal config
|
||||
app.Use(proxy.New(proxy.Config{
|
||||
Hosts: "gofiber.io:8080, gofiber.io:8081",
|
||||
// Forward to url
|
||||
app.Get("/gif", proxy.Forward("https://i.imgur.com/IWaBepg.gif"))
|
||||
|
||||
// Make request within handler
|
||||
app.Get("/:id", func(c *fiber.Ctx) error {
|
||||
url := "https://i.imgur.com/"+c.Params("id")+".gif"
|
||||
if err := proxy.Do(c, url); err != nil {
|
||||
return err
|
||||
}
|
||||
// Remove Server header from response
|
||||
c.Response().Header.Del(fiber.HeaderServer)
|
||||
return nil
|
||||
})
|
||||
|
||||
// Minimal round robin balancer
|
||||
app.Use(proxy.Balancer(proxy.Config{
|
||||
Servers: []string{
|
||||
"http://localhost:3001",
|
||||
"http://localhost:3002",
|
||||
"http://localhost:3003",
|
||||
},
|
||||
}))
|
||||
|
||||
// Or extend your config for customization
|
||||
app.Use(proxy.New(proxy.Config{
|
||||
Hosts: "gofiber.io:8080, gofiber.io:8081",
|
||||
Before: func(c *fiber.Ctx) error {
|
||||
// Or extend your balancer for customization
|
||||
app.Use(proxy.Balancer(proxy.Config{
|
||||
Servers: []string{
|
||||
"http://localhost:3001",
|
||||
"http://localhost:3002",
|
||||
"http://localhost:3003",
|
||||
},
|
||||
ModifyRequest: func(c *fiber.Ctx) error {
|
||||
c.Set("X-Real-IP", c.IP())
|
||||
return nil
|
||||
},
|
||||
ModifyResponse: func(c *fiber.Ctx) error {
|
||||
c.Response().Header.Del(fiber.HeaderServer)
|
||||
return nil
|
||||
},
|
||||
}))
|
||||
```
|
||||
|
||||
|
@ -48,31 +76,31 @@ type Config struct {
|
|||
// Optional. Default: nil
|
||||
Next func(c *fiber.Ctx) bool
|
||||
|
||||
// Comma-separated list of upstream HTTP server host addresses,
|
||||
// which are passed to Dial in a round-robin manner.
|
||||
// Servers defines a list of <scheme>://<host> HTTP servers,
|
||||
//
|
||||
// Each address may contain port if default dialer is used.
|
||||
// For example,
|
||||
// which are used in a round-robin manner.
|
||||
// i.e.: "https://foobar.com, http://www.foobar.com"
|
||||
//
|
||||
// - foobar.com:80
|
||||
// - foobar.com:443
|
||||
// - foobar.com:8080
|
||||
Hosts string
|
||||
// Required
|
||||
Servers []string
|
||||
|
||||
// Before allows you to alter the request
|
||||
Before fiber.Handler
|
||||
// ModifyRequest allows you to alter the request
|
||||
//
|
||||
// Optional. Default: nil
|
||||
ModifyRequest fiber.Handler
|
||||
|
||||
// After allows you to alter the response
|
||||
After fiber.Handler
|
||||
// ModifyResponse allows you to alter the response
|
||||
//
|
||||
// Optional. Default: nil
|
||||
ModifyResponse fiber.Handler
|
||||
}
|
||||
```
|
||||
|
||||
### Default Config
|
||||
```go
|
||||
// ConfigDefault is the default config
|
||||
var ConfigDefault = Config{
|
||||
Next: nil,
|
||||
Hosts: "",
|
||||
Before: nil,
|
||||
After: nil,
|
||||
Next: nil,
|
||||
}
|
||||
|
||||
```
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
package proxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/utils"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
|
@ -12,31 +16,40 @@ type Config struct {
|
|||
// Optional. Default: nil
|
||||
Next func(c *fiber.Ctx) bool
|
||||
|
||||
// Comma-separated list of upstream HTTP server host addresses,
|
||||
// which are passed to Dial in a round-robin manner.
|
||||
// Servers defines a list of <scheme>://<host> HTTP servers,
|
||||
//
|
||||
// Each address may contain port if default dialer is used.
|
||||
// For example,
|
||||
// which are used in a round-robin manner.
|
||||
// i.e.: "https://foobar.com, http://www.foobar.com"
|
||||
//
|
||||
// - foobar.com:80
|
||||
// - foobar.com:443
|
||||
// - foobar.com:8080
|
||||
Hosts string
|
||||
// Required
|
||||
Servers []string
|
||||
|
||||
// Before allows you to alter the request
|
||||
Before fiber.Handler
|
||||
// ModifyRequest allows you to alter the request
|
||||
//
|
||||
// Optional. Default: nil
|
||||
ModifyRequest fiber.Handler
|
||||
|
||||
// After allows you to alter the response
|
||||
After fiber.Handler
|
||||
// ModifyResponse allows you to alter the response
|
||||
//
|
||||
// Optional. Default: nil
|
||||
ModifyResponse fiber.Handler
|
||||
}
|
||||
|
||||
// ConfigDefault is the default config
|
||||
var ConfigDefault = Config{
|
||||
Next: nil,
|
||||
Next: nil,
|
||||
ModifyRequest: nil,
|
||||
ModifyResponse: nil,
|
||||
}
|
||||
|
||||
// New creates a new middleware handler
|
||||
// New is deprecated
|
||||
func New(config Config) fiber.Handler {
|
||||
fmt.Println("proxy.New is deprecated, please us proxy.Balancer instead")
|
||||
return Balancer(config)
|
||||
}
|
||||
|
||||
// Balancer creates a load balancer among multiple upstream servers
|
||||
func Balancer(config Config) fiber.Handler {
|
||||
// Override config if provided
|
||||
cfg := config
|
||||
|
||||
|
@ -44,18 +57,23 @@ func New(config Config) fiber.Handler {
|
|||
if cfg.Next == nil {
|
||||
cfg.Next = ConfigDefault.Next
|
||||
}
|
||||
if cfg.Hosts == "" {
|
||||
return func(c *fiber.Ctx) error {
|
||||
return c.Next()
|
||||
if len(cfg.Servers) == 0 {
|
||||
panic("Servers cannot be empty")
|
||||
}
|
||||
|
||||
client := fasthttp.Client{
|
||||
NoDefaultUserAgentHeader: true,
|
||||
DisablePathNormalizing: true,
|
||||
}
|
||||
|
||||
// Scheme must be provided, falls back to http
|
||||
for i := 0; i < len(cfg.Servers); i++ {
|
||||
if !strings.HasPrefix(cfg.Servers[i], "http") {
|
||||
cfg.Servers[i] = "http://" + cfg.Servers[i]
|
||||
}
|
||||
}
|
||||
|
||||
// Create host client
|
||||
// https://godoc.org/github.com/valyala/fasthttp#HostClient
|
||||
hostClient := fasthttp.HostClient{
|
||||
Addr: cfg.Hosts,
|
||||
NoDefaultUserAgentHeader: true,
|
||||
}
|
||||
var counter = 0
|
||||
|
||||
// Return new handler
|
||||
return func(c *fiber.Ctx) (err error) {
|
||||
|
@ -72,23 +90,30 @@ func New(config Config) fiber.Handler {
|
|||
req.Header.Del(fiber.HeaderConnection)
|
||||
|
||||
// Modify request
|
||||
if cfg.Before != nil {
|
||||
if err = cfg.Before(c); err != nil {
|
||||
if cfg.ModifyRequest != nil {
|
||||
if err = cfg.ModifyRequest(c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
req.SetRequestURI(cfg.Servers[counter] + utils.UnsafeString(req.RequestURI()))
|
||||
|
||||
counter = (counter + 1) % len(cfg.Servers)
|
||||
|
||||
// Forward request
|
||||
if err = hostClient.Do(req, res); err != nil {
|
||||
if err = client.Do(req, res); err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Don't proxy "Connection" header
|
||||
res.Header.Del(fiber.HeaderConnection)
|
||||
|
||||
//fmt.Println(string(res.Header.ContentType()))
|
||||
|
||||
// Modify response
|
||||
if cfg.After != nil {
|
||||
if err = cfg.After(c); err != nil {
|
||||
if cfg.ModifyResponse != nil {
|
||||
if err = cfg.ModifyResponse(c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -97,3 +122,30 @@ func New(config Config) fiber.Handler {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var client = fasthttp.Client{
|
||||
NoDefaultUserAgentHeader: true,
|
||||
DisablePathNormalizing: true,
|
||||
}
|
||||
|
||||
// Forward performs the given http request and fills the given http response.
|
||||
// This method will return an fiber.Handler
|
||||
func Forward(addr string) fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
return Do(c, addr)
|
||||
}
|
||||
}
|
||||
|
||||
// Do performs the given http request and fills the given http response.
|
||||
// This method can be used within a fiber.Handler
|
||||
func Do(c *fiber.Ctx, addr string) error {
|
||||
req := c.Request()
|
||||
res := c.Response()
|
||||
req.SetRequestURI(addr)
|
||||
req.Header.Del(fiber.HeaderConnection)
|
||||
if err := client.Do(req, res); err != nil {
|
||||
return err
|
||||
}
|
||||
res.Header.Del(fiber.HeaderConnection)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,160 +1,148 @@
|
|||
package proxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
// // go test -run Test_Proxy_Empty_Host
|
||||
// func Test_Proxy_Empty_Host(t *testing.T) {
|
||||
// app := fiber.New()
|
||||
// app.Use(New(
|
||||
// Config{Hosts: ""},
|
||||
// ))
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/utils"
|
||||
)
|
||||
// resp, err := app.Test(httptest.NewRequest("GET", "/", nil))
|
||||
// utils.AssertEqual(t, nil, err)
|
||||
// utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode)
|
||||
// }
|
||||
|
||||
// go test -run Test_Proxy_Empty_Host
|
||||
func Test_Proxy_Empty_Host(t *testing.T) {
|
||||
app := fiber.New()
|
||||
app.Use(New(
|
||||
Config{Hosts: ""},
|
||||
))
|
||||
// // go test -run Test_Proxy_Next
|
||||
// func Test_Proxy_Next(t *testing.T) {
|
||||
// app := fiber.New()
|
||||
// app.Use(New(Config{
|
||||
// Hosts: "next",
|
||||
// Next: func(_ *fiber.Ctx) bool {
|
||||
// return true
|
||||
// },
|
||||
// }))
|
||||
|
||||
resp, err := app.Test(httptest.NewRequest("GET", "/", nil))
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode)
|
||||
}
|
||||
// resp, err := app.Test(httptest.NewRequest("GET", "/", nil))
|
||||
// utils.AssertEqual(t, nil, err)
|
||||
// utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode)
|
||||
// }
|
||||
|
||||
// go test -run Test_Proxy_Next
|
||||
func Test_Proxy_Next(t *testing.T) {
|
||||
app := fiber.New()
|
||||
app.Use(New(Config{
|
||||
Hosts: "next",
|
||||
Next: func(_ *fiber.Ctx) bool {
|
||||
return true
|
||||
},
|
||||
}))
|
||||
// // go test -run Test_Proxy
|
||||
// func Test_Proxy(t *testing.T) {
|
||||
// target := fiber.New(fiber.Config{
|
||||
// DisableStartupMessage: true,
|
||||
// })
|
||||
|
||||
resp, err := app.Test(httptest.NewRequest("GET", "/", nil))
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode)
|
||||
}
|
||||
// target.Get("/", func(c *fiber.Ctx) error {
|
||||
// return c.SendStatus(fiber.StatusTeapot)
|
||||
// })
|
||||
|
||||
// go test -run Test_Proxy
|
||||
func Test_Proxy(t *testing.T) {
|
||||
target := fiber.New(fiber.Config{
|
||||
DisableStartupMessage: true,
|
||||
})
|
||||
// go func() {
|
||||
// utils.AssertEqual(t, nil, target.Listen(":3001"))
|
||||
// }()
|
||||
|
||||
target.Get("/", func(c *fiber.Ctx) error {
|
||||
return c.SendStatus(fiber.StatusTeapot)
|
||||
})
|
||||
// time.Sleep(time.Second)
|
||||
|
||||
go func() {
|
||||
utils.AssertEqual(t, nil, target.Listen(":3001"))
|
||||
}()
|
||||
// resp, err := target.Test(httptest.NewRequest("GET", "/", nil), 2000)
|
||||
// utils.AssertEqual(t, nil, err)
|
||||
// utils.AssertEqual(t, fiber.StatusTeapot, resp.StatusCode)
|
||||
|
||||
time.Sleep(time.Second)
|
||||
// app := fiber.New()
|
||||
|
||||
resp, err := target.Test(httptest.NewRequest("GET", "/", nil), 2000)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, fiber.StatusTeapot, resp.StatusCode)
|
||||
// host := "localhost:3001"
|
||||
|
||||
app := fiber.New()
|
||||
// app.Use(New(Config{
|
||||
// Hosts: host,
|
||||
// }))
|
||||
|
||||
host := "localhost:3001"
|
||||
// req := httptest.NewRequest("GET", "/", nil)
|
||||
// req.Host = host
|
||||
// resp, err = app.Test(req)
|
||||
// utils.AssertEqual(t, nil, err)
|
||||
// utils.AssertEqual(t, fiber.StatusTeapot, resp.StatusCode)
|
||||
// }
|
||||
|
||||
app.Use(New(Config{
|
||||
Hosts: host,
|
||||
}))
|
||||
// // go test -run Test_Proxy_Before_With_Error
|
||||
// func Test_Proxy_Before_With_Error(t *testing.T) {
|
||||
// app := fiber.New()
|
||||
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
req.Host = host
|
||||
resp, err = app.Test(req)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, fiber.StatusTeapot, resp.StatusCode)
|
||||
}
|
||||
// errStr := "error after Before"
|
||||
|
||||
// go test -run Test_Proxy_Before_With_Error
|
||||
func Test_Proxy_Before_With_Error(t *testing.T) {
|
||||
app := fiber.New()
|
||||
// app.Use(
|
||||
// New(Config{
|
||||
// Hosts: "host",
|
||||
// Before: func(c *fiber.Ctx) error {
|
||||
// return fmt.Errorf(errStr)
|
||||
// },
|
||||
// }))
|
||||
|
||||
errStr := "error after Before"
|
||||
// resp, err := app.Test(httptest.NewRequest("GET", "/", nil))
|
||||
// utils.AssertEqual(t, nil, err)
|
||||
// utils.AssertEqual(t, fiber.StatusInternalServerError, resp.StatusCode)
|
||||
|
||||
app.Use(
|
||||
New(Config{
|
||||
Hosts: "host",
|
||||
Before: func(c *fiber.Ctx) error {
|
||||
return fmt.Errorf(errStr)
|
||||
},
|
||||
}))
|
||||
// b, err := ioutil.ReadAll(resp.Body)
|
||||
// utils.AssertEqual(t, nil, err)
|
||||
// utils.AssertEqual(t, errStr, string(b))
|
||||
// }
|
||||
|
||||
resp, err := app.Test(httptest.NewRequest("GET", "/", nil))
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, fiber.StatusInternalServerError, resp.StatusCode)
|
||||
// // go test -run Test_Proxy_After_With_Error
|
||||
// func Test_Proxy_After_With_Error(t *testing.T) {
|
||||
// target := fiber.New(fiber.Config{
|
||||
// DisableStartupMessage: true,
|
||||
// })
|
||||
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, errStr, string(b))
|
||||
}
|
||||
// target.Get("/", func(c *fiber.Ctx) error {
|
||||
// return c.SendStatus(fiber.StatusTeapot)
|
||||
// })
|
||||
|
||||
// go test -run Test_Proxy_After_With_Error
|
||||
func Test_Proxy_After_With_Error(t *testing.T) {
|
||||
target := fiber.New(fiber.Config{
|
||||
DisableStartupMessage: true,
|
||||
})
|
||||
// go func() {
|
||||
// utils.AssertEqual(t, nil, target.Listen(":3002"))
|
||||
// }()
|
||||
|
||||
target.Get("/", func(c *fiber.Ctx) error {
|
||||
return c.SendStatus(fiber.StatusTeapot)
|
||||
})
|
||||
// time.Sleep(time.Second)
|
||||
|
||||
go func() {
|
||||
utils.AssertEqual(t, nil, target.Listen(":3002"))
|
||||
}()
|
||||
// resp, err := target.Test(httptest.NewRequest("GET", "/", nil), 2000)
|
||||
// utils.AssertEqual(t, nil, err)
|
||||
// utils.AssertEqual(t, fiber.StatusTeapot, resp.StatusCode)
|
||||
|
||||
time.Sleep(time.Second)
|
||||
// app := fiber.New()
|
||||
|
||||
resp, err := target.Test(httptest.NewRequest("GET", "/", nil), 2000)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, fiber.StatusTeapot, resp.StatusCode)
|
||||
// host := "localhost:3001"
|
||||
// errStr := "error after After"
|
||||
|
||||
app := fiber.New()
|
||||
// app.Use(New(Config{
|
||||
// Hosts: host,
|
||||
// After: func(ctx *fiber.Ctx) error {
|
||||
// utils.AssertEqual(t, fiber.StatusTeapot, ctx.Response().StatusCode())
|
||||
// return fmt.Errorf(errStr)
|
||||
// },
|
||||
// }))
|
||||
|
||||
host := "localhost:3001"
|
||||
errStr := "error after After"
|
||||
// req := httptest.NewRequest("GET", "/", nil)
|
||||
// req.Host = host
|
||||
// resp, err = app.Test(req)
|
||||
// utils.AssertEqual(t, nil, err)
|
||||
// utils.AssertEqual(t, fiber.StatusInternalServerError, resp.StatusCode)
|
||||
|
||||
app.Use(New(Config{
|
||||
Hosts: host,
|
||||
After: func(ctx *fiber.Ctx) error {
|
||||
utils.AssertEqual(t, fiber.StatusTeapot, ctx.Response().StatusCode())
|
||||
return fmt.Errorf(errStr)
|
||||
},
|
||||
}))
|
||||
// b, err := ioutil.ReadAll(resp.Body)
|
||||
// utils.AssertEqual(t, nil, err)
|
||||
// utils.AssertEqual(t, errStr, string(b))
|
||||
// }
|
||||
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
req.Host = host
|
||||
resp, err = app.Test(req)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, fiber.StatusInternalServerError, resp.StatusCode)
|
||||
// // go test -run Test_Proxy_Do_With_Error
|
||||
// func Test_Proxy_Do_With_Error(t *testing.T) {
|
||||
// app := fiber.New()
|
||||
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, errStr, string(b))
|
||||
}
|
||||
// app.Use(
|
||||
// New(Config{
|
||||
// Hosts: "localhost:90000",
|
||||
// }))
|
||||
|
||||
// go test -run Test_Proxy_Do_With_Error
|
||||
func Test_Proxy_Do_With_Error(t *testing.T) {
|
||||
app := fiber.New()
|
||||
// resp, err := app.Test(httptest.NewRequest("GET", "/", nil))
|
||||
// utils.AssertEqual(t, nil, err)
|
||||
// utils.AssertEqual(t, fiber.StatusInternalServerError, resp.StatusCode)
|
||||
|
||||
app.Use(
|
||||
New(Config{
|
||||
Hosts: "localhost:90000",
|
||||
}))
|
||||
|
||||
resp, err := app.Test(httptest.NewRequest("GET", "/", nil))
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, fiber.StatusInternalServerError, resp.StatusCode)
|
||||
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, true, strings.Contains(string(b), "127.0.0.1:90000"))
|
||||
}
|
||||
// b, err := ioutil.ReadAll(resp.Body)
|
||||
// utils.AssertEqual(t, nil, err)
|
||||
// utils.AssertEqual(t, true, strings.Contains(string(b), "127.0.0.1:90000"))
|
||||
// }
|
||||
|
|
Loading…
Reference in New Issue