diff --git a/cmd/main.go b/cmd/main.go
index 320212d..8a50ef1 100755
--- a/cmd/main.go
+++ b/cmd/main.go
@@ -116,10 +116,6 @@ func main() {
printJSON(trace)
}
- if common.IsDebug {
- printJSON(docker.GetRegisteredNamespaces())
- }
-
cfg.StartProxyProviders()
cfg.WatchChanges()
diff --git a/docs/docker.md b/docs/docker.md
index fe14cba..e113992 100644
--- a/docs/docker.md
+++ b/docs/docker.md
@@ -6,6 +6,7 @@
- [Docker compose guide](#docker-compose-guide)
- [Table of content](#table-of-content)
+ - [Suggestions](#suggestions)
- [Additional setup](#additional-setup)
- [Labels](#labels)
- [Syntax](#syntax)
@@ -16,6 +17,30 @@
- [Docker compose examples](#docker-compose-examples)
- [Services URLs for above examples](#services-urls-for-above-examples)
+## Suggestions
+
+In order for labels to work correctly in `compose.yml`:
+
+1. `key: value` mapping is suggested for label, instead of `- key=value`
+2. you need to add `|` in the end for multiline strings.
+
+Example
+
+```yaml
+services:
+ app:
+ ...
+ container_name: app
+ labels:
+ proxy.app.middlewares.modify_request.set_headers: |
+ X-Custom-Header1: value1, value2
+ X-Custom-Header2: value3
+ proxy.app.middlewares.modify_request.hide_headers: |
+ X-Custom-Header4
+ X-Custom-Header5
+ X-Custom-Header6
+```
+
## Additional setup
1. Enable HTTPs _(optional)_
@@ -89,7 +114,7 @@
| `port` | proxy port **(http/s)** | first port returned from docker | number in range of `1 - 65535` |
| `port` | proxy port **(tcp/udp)** | `0:first_port` | `x:y`
- **x**: port for `go-proxy` to listen on.
**x** can be 0, which means listen on a random port - **y**: port or [_service name_](../src/common/constants.go#L55) of target container
|
| `no_tls_verify` | whether skip tls verify **(https only)** | `false` | boolean |
-| `path_patterns` | proxy path patterns **(http/s only)**
only requests that matched a pattern will be proxied | `/` **(proxy all requests)** | yaml style list[1](#list-example) of ([path patterns](https://pkg.go.dev/net/http#hdr-Patterns-ServeMux)) |
+| `path_patterns` | proxy path patterns **(http/s only)**
only requests that matched a pattern will be proxied | `/` **(proxy all requests)** | list[1](#list-example) of ([path patterns](https://pkg.go.dev/net/http#hdr-Patterns-ServeMux)) |
[🔼Back to top](#table-of-content)
@@ -132,11 +157,11 @@ services:
...
labels:
proxy.nginx.path_patterns: | # remember to add the '|'
- - GET /
- - POST /auth
+ GET /
+ POST /auth
proxy.nginx.middlewares.modify_request.hide_headers: | # remember to add the '|'
- - X-Custom-Header1
- - X-Custom-Header2
+ X-Custom-Header1
+ X-Custom-Header2
```
Include file
@@ -145,8 +170,8 @@ Include file
service_a:
host: service_a.internal
path_patterns:
- - GET /
- - POST /auth
+ GET /
+ POST /auth
middlewares:
modify_request:
hide_headers:
diff --git a/docs/middlewares.md b/docs/middlewares.md
index 64615a2..0241708 100644
--- a/docs/middlewares.md
+++ b/docs/middlewares.md
@@ -119,9 +119,9 @@ Check https://nginx.org/en/docs/http/ngx_http_realip_module.html for explainatio
# docker labels
proxy.app1.middlewares.real_ip.header: X-Real-IP
proxy.app1.middlewares.real_ip.from: |
- - 127.0.0.1
- - 192.168.0.0/16
- - 10.0.0.0/8
+ 127.0.0.1
+ 192.168.0.0/16
+ 10.0.0.0/8
proxy.app1.middlewares.real_ip.recursive: true
# include file
@@ -177,8 +177,8 @@ app1:
```yaml
# docker labels
proxy.app1.middlewares.cidr_whitelist.allow: |
- - 10.0.0.0/8
- - 192.168.0.0/16
+ 10.0.0.0/8
+ 192.168.0.0/16
# optional (default: 403)
proxy.app1.middlewares.cidr_whitelist.status_code: 403
# optional (default: "IP not allowed")
@@ -270,8 +270,8 @@ location / {
```yaml
# docker labels
proxy.app1.middlewares.modify_request.hide_headers: |
- - X-Custom-Header1
- - X-Custom-Header2
+ X-Custom-Header1
+ X-Custom-Header2
# include file
app1:
@@ -339,11 +339,11 @@ Fields:
proxy.app1.middlewares.forward_auth.address: https://auth.example.com
proxy.app1.middlewares.forward_auth.trust_forward_header: true
proxy.app1.middlewares.forward_auth.auth_response_headers: |
- - X-Auth-Token
- - X-Auth-User
+ X-Auth-Token
+ X-Auth-User
proxy.app1.middlewares.forward_auth.add_auth_cookies_to_response: |
- - uid
- - session_id
+ uid
+ session_id
# include file
app1:
@@ -421,17 +421,17 @@ services:
proxy.#1.middlewares.forward_auth.address: https://your_authentik_forward_address
proxy.#1.middlewares.forward_auth.trustForwardHeader: true
proxy.#1.middlewares.forward_auth.authResponseHeaders: |
- - X-authentik-username
- - X-authentik-groups
- - X-authentik-email
- - X-authentik-name
- - X-authentik-uid
- - X-authentik-jwt
- - X-authentik-meta-jwks
- - X-authentik-meta-outpost
- - X-authentik-meta-provider
- - X-authentik-meta-app
- - X-authentik-meta-version
+ X-authentik-username
+ X-authentik-groups
+ X-authentik-email
+ X-authentik-name
+ X-authentik-uid
+ X-authentik-jwt
+ X-authentik-meta-jwks
+ X-authentik-meta-outpost
+ X-authentik-meta-provider
+ X-authentik-meta-app
+ X-authentik-meta-version
restart: unless-stopped
```
diff --git a/internal/api/handler.go b/internal/api/handler.go
index af76837..271c40d 100644
--- a/internal/api/handler.go
+++ b/internal/api/handler.go
@@ -40,10 +40,13 @@ func NewHandler(cfg *config.Config) http.Handler {
// allow only requests to API server with host matching common.APIHTTPAddr
func checkHost(f http.HandlerFunc) http.HandlerFunc {
+ if common.IsDebug {
+ return f
+ }
return func(w http.ResponseWriter, r *http.Request) {
if r.Host != common.APIHTTPAddr {
Logger.Warnf("invalid request to API server with host: %s, expect %s", r.Host, common.APIHTTPAddr)
- w.WriteHeader(http.StatusNotFound)
+ w.WriteHeader(http.StatusForbidden)
w.Write([]byte("invalid request"))
return
}
diff --git a/internal/common/env.go b/internal/common/env.go
index ddfe935..5c5c3ba 100644
--- a/internal/common/env.go
+++ b/internal/common/env.go
@@ -2,8 +2,10 @@ package common
import (
"fmt"
+ "log"
"net"
"os"
+ "strconv"
"strings"
"github.com/sirupsen/logrus"
@@ -35,14 +37,11 @@ func GetEnvBool(key string, defaultValue bool) bool {
if !ok || value == "" {
return defaultValue
}
- switch strings.ToLower(value) {
- case "true", "yes", "1":
- return true
- case "false", "no", "0":
- return false
- default:
- return defaultValue
+ b, err := strconv.ParseBool(value)
+ if err != nil {
+ log.Fatalf("Invalid boolean value: %s", value)
}
+ return b
}
func GetEnv(key, defaultValue string) string {
diff --git a/internal/docker/label.go b/internal/docker/label.go
index 8326622..63e072f 100644
--- a/internal/docker/label.go
+++ b/internal/docker/label.go
@@ -6,7 +6,6 @@ import (
E "github.com/yusing/go-proxy/internal/error"
U "github.com/yusing/go-proxy/internal/utils"
- F "github.com/yusing/go-proxy/internal/utils/functional"
)
/*
@@ -23,8 +22,6 @@ type (
Value any
}
NestedLabelMap map[string]U.SerializedObject
- ValueParser func(string) (any, E.NestedError)
- ValueParserMap map[string]ValueParser
)
func (l *Label) String() string {
@@ -107,45 +104,5 @@ func ParseLabel(label string, value string) (*Label, E.NestedError) {
l.Value = nestedLabel
}
- // find if namespace has value parser
- pm, ok := valueParserMap.Load(U.ToLowerNoSnake(l.Namespace))
- if !ok {
- return l, nil
- }
- // find if attribute has value parser
- p, ok := pm[U.ToLowerNoSnake(l.Attribute)]
- if !ok {
- return l, nil
- }
- // try to parse value
- v, err := p(value)
- if err.HasError() {
- return nil, err.Subject(label)
- }
- l.Value = v
return l, nil
}
-
-func RegisterNamespace(namespace string, pm ValueParserMap) {
- pmCleaned := make(ValueParserMap, len(pm))
- for k, v := range pm {
- pmCleaned[U.ToLowerNoSnake(k)] = v
- }
- valueParserMap.Store(U.ToLowerNoSnake(namespace), pmCleaned)
-}
-
-func GetRegisteredNamespaces() map[string][]string {
- r := make(map[string][]string)
-
- valueParserMap.RangeAll(func(ns string, vpm ValueParserMap) {
- r[ns] = make([]string, 0, len(vpm))
- for attr := range vpm {
- r[ns] = append(r[ns], attr)
- }
- })
-
- return r
-}
-
-// namespace:target.attribute -> func(string) (any, error)
-var valueParserMap = F.NewMapOf[string, ValueParserMap]()
diff --git a/internal/docker/label_parser.go b/internal/docker/label_parser.go
deleted file mode 100644
index b166216..0000000
--- a/internal/docker/label_parser.go
+++ /dev/null
@@ -1,87 +0,0 @@
-package docker
-
-import (
- "strconv"
- "strings"
-
- E "github.com/yusing/go-proxy/internal/error"
- "gopkg.in/yaml.v3"
-)
-
-const (
- NSProxy = "proxy"
- ProxyAttributePathPatterns = "path_patterns"
- ProxyAttributeNoTLSVerify = "no_tls_verify"
- ProxyAttributeMiddlewares = "middlewares"
-)
-
-var _ = func() int {
- RegisterNamespace(NSProxy, ValueParserMap{
- ProxyAttributePathPatterns: YamlStringListParser,
- ProxyAttributeNoTLSVerify: BoolParser,
- })
- return 0
-}()
-
-func YamlStringListParser(value string) (any, E.NestedError) {
- /*
- - foo
- - bar
- - baz
- */
- value = strings.TrimSpace(value)
- if value == "" {
- return []string{}, nil
- }
- var data []string
- err := E.From(yaml.Unmarshal([]byte(value), &data))
- return data, err
-}
-
-func YamlLikeMappingParser(allowDuplicate bool) func(string) (any, E.NestedError) {
- return func(value string) (any, E.NestedError) {
- /*
- foo: bar
- boo: baz
- */
- value = strings.TrimSpace(value)
- lines := strings.Split(value, "\n")
- h := make(map[string]string)
- for _, line := range lines {
- parts := strings.SplitN(line, ":", 2)
- if len(parts) != 2 {
- return nil, E.Invalid("syntax", line).With("too many colons")
- }
- key := strings.TrimSpace(parts[0])
- val := strings.TrimSpace(parts[1])
- if existing, ok := h[key]; ok {
- if !allowDuplicate {
- return nil, E.Duplicated("key", key)
- }
- h[key] = existing + ", " + val
- } else {
- h[key] = val
- }
- }
- return h, nil
- }
-}
-
-func BoolParser(value string) (any, E.NestedError) {
- switch strings.ToLower(value) {
- case "true", "yes", "1":
- return true, nil
- case "false", "no", "0":
- return false, nil
- default:
- return nil, E.Invalid("boolean value", value)
- }
-}
-
-func IntParser(value string) (any, E.NestedError) {
- i, err := strconv.Atoi(value)
- if err != nil {
- return 0, E.Invalid("integer value", value)
- }
- return i, nil
-}
diff --git a/internal/docker/label_parser_test.go b/internal/docker/label_parser_test.go
deleted file mode 100644
index 1fd9ce8..0000000
--- a/internal/docker/label_parser_test.go
+++ /dev/null
@@ -1,106 +0,0 @@
-package docker
-
-import (
- "fmt"
- "testing"
-
- E "github.com/yusing/go-proxy/internal/error"
- . "github.com/yusing/go-proxy/internal/utils/testing"
-)
-
-func makeLabel(namespace string, alias string, field string) string {
- return fmt.Sprintf("%s.%s.%s", namespace, alias, field)
-}
-
-func TestParseLabel(t *testing.T) {
- alias := "foo"
- field := "ip"
- v := "bar"
- pl, err := ParseLabel(makeLabel(NSHomePage, alias, field), v)
- ExpectNoError(t, err.Error())
- ExpectEqual(t, pl.Namespace, NSHomePage)
- ExpectEqual(t, pl.Target, alias)
- ExpectEqual(t, pl.Attribute, field)
- ExpectEqual(t, pl.Value.(string), v)
-}
-
-func TestStringProxyLabel(t *testing.T) {
- v := "bar"
- pl, err := ParseLabel(makeLabel(NSProxy, "foo", "ip"), v)
- ExpectNoError(t, err.Error())
- ExpectEqual(t, pl.Value.(string), v)
-}
-
-func TestBoolProxyLabelValid(t *testing.T) {
- tests := map[string]bool{
- "true": true,
- "TRUE": true,
- "yes": true,
- "1": true,
- "false": false,
- "FALSE": false,
- "no": false,
- "0": false,
- }
-
- for k, v := range tests {
- pl, err := ParseLabel(makeLabel(NSProxy, "foo", ProxyAttributeNoTLSVerify), k)
- ExpectNoError(t, err.Error())
- ExpectEqual(t, pl.Value.(bool), v)
- }
-}
-
-func TestBoolProxyLabelInvalid(t *testing.T) {
- _, err := ParseLabel(makeLabel(NSProxy, "foo", ProxyAttributeNoTLSVerify), "invalid")
- if !err.Is(E.ErrInvalid) {
- t.Errorf("Expected err InvalidProxyLabel, got %s", err.Error())
- }
-}
-
-// func TestSetHeaderProxyLabelValid(t *testing.T) {
-// v := `
-// X-Custom-Header1: foo, bar
-// X-Custom-Header1: baz
-// X-Custom-Header2: boo`
-// v = strings.TrimPrefix(v, "\n")
-// h := map[string]string{
-// "X-Custom-Header1": "foo, bar, baz",
-// "X-Custom-Header2": "boo",
-// }
-
-// pl, err := ParseLabel(makeLabel(NSProxy, "foo", ProxyAttributeSetHeaders), v)
-// ExpectNoError(t, err.Error())
-// hGot := ExpectType[map[string]string](t, pl.Value)
-// ExpectFalse(t, hGot == nil)
-// ExpectDeepEqual(t, h, hGot)
-// }
-
-// func TestSetHeaderProxyLabelInvalid(t *testing.T) {
-// tests := []string{
-// "X-Custom-Header1 = bar",
-// "X-Custom-Header1",
-// "- X-Custom-Header1",
-// }
-
-// for _, v := range tests {
-// _, err := ParseLabel(makeLabel(NSProxy, "foo", ProxyAttributeSetHeaders), v)
-// if !err.Is(E.ErrInvalid) {
-// t.Errorf("Expected invalid err for %q, got %s", v, err.Error())
-// }
-// }
-// }
-
-// func TestHideHeadersProxyLabel(t *testing.T) {
-// v := `
-// - X-Custom-Header1
-// - X-Custom-Header2
-// - X-Custom-Header3
-// `
-// v = strings.TrimPrefix(v, "\n")
-// pl, err := ParseLabel(makeLabel(NSProxy, "foo", ProxyAttributeHideHeaders), v)
-// ExpectNoError(t, err.Error())
-// sGot := ExpectType[[]string](t, pl.Value)
-// sWant := []string{"X-Custom-Header1", "X-Custom-Header2", "X-Custom-Header3"}
-// ExpectFalse(t, sGot == nil)
-// ExpectDeepEqual(t, sGot, sWant)
-// }
diff --git a/internal/docker/label_test.go b/internal/docker/label_test.go
index f8fe7aa..c2588be 100644
--- a/internal/docker/label_test.go
+++ b/internal/docker/label_test.go
@@ -8,11 +8,15 @@ import (
. "github.com/yusing/go-proxy/internal/utils/testing"
)
+func makeLabel(ns, name, attr string) string {
+ return fmt.Sprintf("%s.%s.%s", ns, name, attr)
+}
+
func TestNestedLabel(t *testing.T) {
mName := "middleware1"
mAttr := "prop1"
v := "value1"
- pl, err := ParseLabel(makeLabel(NSProxy, "foo", fmt.Sprintf("%s.%s.%s", ProxyAttributeMiddlewares, mName, mAttr)), v)
+ pl, err := ParseLabel(makeLabel(NSProxy, "foo", makeLabel("middlewares", mName, mAttr)), v)
ExpectNoError(t, err.Error())
sGot := ExpectType[*Label](t, pl.Value)
ExpectFalse(t, sGot == nil)
@@ -27,7 +31,7 @@ func TestApplyNestedLabel(t *testing.T) {
mName := "middleware1"
mAttr := "prop1"
v := "value1"
- pl, err := ParseLabel(makeLabel(NSProxy, "foo", fmt.Sprintf("%s.%s.%s", ProxyAttributeMiddlewares, mName, mAttr)), v)
+ pl, err := ParseLabel(makeLabel(NSProxy, "foo", makeLabel("middlewares", mName, mAttr)), v)
ExpectNoError(t, err.Error())
err = ApplyLabel(entry, pl)
ExpectNoError(t, err.Error())
@@ -51,7 +55,7 @@ func TestApplyNestedLabelExisting(t *testing.T) {
entry.Middlewares[mName] = make(U.SerializedObject)
entry.Middlewares[mName][checkAttr] = checkV
- pl, err := ParseLabel(makeLabel(NSProxy, "foo", fmt.Sprintf("%s.%s.%s", ProxyAttributeMiddlewares, mName, mAttr)), v)
+ pl, err := ParseLabel(makeLabel(NSProxy, "foo", makeLabel("middlewares", mName, mAttr)), v)
ExpectNoError(t, err.Error())
err = ApplyLabel(entry, pl)
ExpectNoError(t, err.Error())
@@ -76,7 +80,7 @@ func TestApplyNestedLabelNoAttr(t *testing.T) {
entry.Middlewares = make(NestedLabelMap)
entry.Middlewares[mName] = make(U.SerializedObject)
- pl, err := ParseLabel(makeLabel(NSProxy, "foo", fmt.Sprintf("%s.%s", ProxyAttributeMiddlewares, mName)), v)
+ pl, err := ParseLabel(makeLabel(NSProxy, "foo", fmt.Sprintf("%s.%s", "middlewares", mName)), v)
ExpectNoError(t, err.Error())
err = ApplyLabel(entry, pl)
ExpectNoError(t, err.Error())
diff --git a/internal/docker/labels.go b/internal/docker/labels.go
index 444db36..8c78e79 100644
--- a/internal/docker/labels.go
+++ b/internal/docker/labels.go
@@ -3,6 +3,8 @@ package docker
const (
WildcardAlias = "*"
+ NSProxy = "proxy"
+
LabelAliases = NSProxy + ".aliases"
LabelExclude = NSProxy + ".exclude"
LabelIdleTimeout = NSProxy + ".idle_timeout"
diff --git a/internal/net/http/middleware/cidr_whitelist.go b/internal/net/http/middleware/cidr_whitelist.go
index 2d6e324..3a5cfe5 100644
--- a/internal/net/http/middleware/cidr_whitelist.go
+++ b/internal/net/http/middleware/cidr_whitelist.go
@@ -4,7 +4,6 @@ import (
"net"
"net/http"
- D "github.com/yusing/go-proxy/internal/docker"
E "github.com/yusing/go-proxy/internal/error"
"github.com/yusing/go-proxy/internal/types"
F "github.com/yusing/go-proxy/internal/utils/functional"
@@ -24,13 +23,7 @@ type cidrWhitelistOpts struct {
}
var CIDRWhiteList = &cidrWhitelist{
- m: &Middleware{
- labelParserMap: D.ValueParserMap{
- "allow": D.YamlStringListParser,
- "statusCode": D.IntParser,
- },
- withOptions: NewCIDRWhitelist,
- },
+ m: &Middleware{withOptions: NewCIDRWhitelist},
}
var cidrWhitelistDefaults = func() *cidrWhitelistOpts {
diff --git a/internal/net/http/middleware/cloudflare_real_ip.go b/internal/net/http/middleware/cloudflare_real_ip.go
index 96101f9..cd7f64c 100644
--- a/internal/net/http/middleware/cloudflare_real_ip.go
+++ b/internal/net/http/middleware/cloudflare_real_ip.go
@@ -30,9 +30,7 @@ var (
)
var CloudflareRealIP = &realIP{
- m: &Middleware{
- withOptions: NewCloudflareRealIP,
- },
+ m: &Middleware{withOptions: NewCloudflareRealIP},
}
func NewCloudflareRealIP(_ OptionsRaw) (*Middleware, E.NestedError) {
diff --git a/internal/net/http/middleware/forward_auth.go b/internal/net/http/middleware/forward_auth.go
index ca9e906..dbb89f9 100644
--- a/internal/net/http/middleware/forward_auth.go
+++ b/internal/net/http/middleware/forward_auth.go
@@ -13,7 +13,6 @@ import (
"strings"
"time"
- D "github.com/yusing/go-proxy/internal/docker"
E "github.com/yusing/go-proxy/internal/error"
gpHTTP "github.com/yusing/go-proxy/internal/net/http"
)
@@ -33,17 +32,9 @@ type (
}
)
-var ForwardAuth = func() *forwardAuth {
- fa := new(forwardAuth)
- fa.m = new(Middleware)
- fa.m.labelParserMap = D.ValueParserMap{
- "trust_forward_header": D.BoolParser,
- "auth_response_headers": D.YamlStringListParser,
- "add_auth_cookies_to_response": D.YamlStringListParser,
- }
- fa.m.withOptions = NewForwardAuthfunc
- return fa
-}()
+var ForwardAuth = &forwardAuth{
+ m: &Middleware{withOptions: NewForwardAuthfunc},
+}
func NewForwardAuthfunc(optsRaw OptionsRaw) (*Middleware, E.NestedError) {
faWithOpts := new(forwardAuth)
diff --git a/internal/net/http/middleware/middleware.go b/internal/net/http/middleware/middleware.go
index 3908c96..1058f1c 100644
--- a/internal/net/http/middleware/middleware.go
+++ b/internal/net/http/middleware/middleware.go
@@ -5,7 +5,6 @@ import (
"errors"
"net/http"
- D "github.com/yusing/go-proxy/internal/docker"
E "github.com/yusing/go-proxy/internal/error"
gpHTTP "github.com/yusing/go-proxy/internal/net/http"
U "github.com/yusing/go-proxy/internal/utils"
@@ -36,9 +35,8 @@ type (
before BeforeFunc // runs before ReverseProxy.ServeHTTP
modifyResponse ModifyResponseFunc // runs after ReverseProxy.ModifyResponse
- withOptions CloneWithOptFunc
- labelParserMap D.ValueParserMap
- impl any
+ withOptions CloneWithOptFunc
+ impl any
parent *Middleware
children []*Middleware
@@ -92,7 +90,7 @@ func (m *Middleware) WithOptionsClone(optsRaw OptionsRaw) (*Middleware, E.Nested
m.name,
m.before,
m.modifyResponse,
- nil, nil,
+ nil,
m.impl,
m.parent,
m.children,
diff --git a/internal/net/http/middleware/middlewares.go b/internal/net/http/middleware/middlewares.go
index aebec82..60a1a84 100644
--- a/internal/net/http/middleware/middlewares.go
+++ b/internal/net/http/middleware/middlewares.go
@@ -8,7 +8,6 @@ import (
"github.com/sirupsen/logrus"
"github.com/yusing/go-proxy/internal/common"
- D "github.com/yusing/go-proxy/internal/docker"
E "github.com/yusing/go-proxy/internal/error"
U "github.com/yusing/go-proxy/internal/utils"
)
@@ -42,11 +41,6 @@ func init() {
names := make(map[*Middleware][]string)
for name, m := range middlewares {
names[m] = append(names[m], http.CanonicalHeaderKey(name))
- // register middleware name to docker label parsr
- // in order to parse middleware_name.option=value into correct type
- if m.labelParserMap != nil {
- D.RegisterNamespace(name, m.labelParserMap)
- }
}
for m, names := range names {
if len(names) > 1 {
diff --git a/internal/net/http/middleware/modify_request.go b/internal/net/http/middleware/modify_request.go
index 69febf9..261d4f0 100644
--- a/internal/net/http/middleware/modify_request.go
+++ b/internal/net/http/middleware/modify_request.go
@@ -2,7 +2,6 @@ package middleware
import (
"github.com/yusing/go-proxy/internal/common"
- D "github.com/yusing/go-proxy/internal/docker"
E "github.com/yusing/go-proxy/internal/error"
)
@@ -19,17 +18,9 @@ type (
}
)
-var ModifyRequest = func() *modifyRequest {
- mr := new(modifyRequest)
- mr.m = new(Middleware)
- mr.m.labelParserMap = D.ValueParserMap{
- "set_headers": D.YamlLikeMappingParser(true),
- "add_headers": D.YamlLikeMappingParser(true),
- "hide_headers": D.YamlStringListParser,
- }
- mr.m.withOptions = NewModifyRequest
- return mr
-}()
+var ModifyRequest = &modifyRequest{
+ m: &Middleware{withOptions: NewModifyRequest},
+}
func NewModifyRequest(optsRaw OptionsRaw) (*Middleware, E.NestedError) {
mr := new(modifyRequest)
diff --git a/internal/net/http/middleware/modify_response.go b/internal/net/http/middleware/modify_response.go
index ecf7b0a..dd2ad24 100644
--- a/internal/net/http/middleware/modify_response.go
+++ b/internal/net/http/middleware/modify_response.go
@@ -4,7 +4,6 @@ import (
"net/http"
"github.com/yusing/go-proxy/internal/common"
- D "github.com/yusing/go-proxy/internal/docker"
E "github.com/yusing/go-proxy/internal/error"
)
@@ -21,17 +20,9 @@ type (
}
)
-var ModifyResponse = func() (mr *modifyResponse) {
- mr = new(modifyResponse)
- mr.m = new(Middleware)
- mr.m.labelParserMap = D.ValueParserMap{
- "set_headers": D.YamlLikeMappingParser(true),
- "add_headers": D.YamlLikeMappingParser(true),
- "hide_headers": D.YamlStringListParser,
- }
- mr.m.withOptions = NewModifyResponse
- return
-}()
+var ModifyResponse = &modifyResponse{
+ m: &Middleware{withOptions: NewModifyResponse},
+}
func NewModifyResponse(optsRaw OptionsRaw) (*Middleware, E.NestedError) {
mr := new(modifyResponse)
diff --git a/internal/net/http/middleware/real_ip.go b/internal/net/http/middleware/real_ip.go
index 75bfb82..ff5691f 100644
--- a/internal/net/http/middleware/real_ip.go
+++ b/internal/net/http/middleware/real_ip.go
@@ -3,7 +3,6 @@ package middleware
import (
"net"
- D "github.com/yusing/go-proxy/internal/docker"
E "github.com/yusing/go-proxy/internal/error"
"github.com/yusing/go-proxy/internal/types"
)
@@ -32,13 +31,7 @@ type realIPOpts struct {
}
var RealIP = &realIP{
- m: &Middleware{
- labelParserMap: D.ValueParserMap{
- "from": D.YamlStringListParser,
- "recursive": D.BoolParser,
- },
- withOptions: NewRealIP,
- },
+ m: &Middleware{withOptions: NewRealIP},
}
var realIPOptsDefault = func() *realIPOpts {
diff --git a/internal/utils/serialization.go b/internal/utils/serialization.go
index fb0f240..b9a7f32 100644
--- a/internal/utils/serialization.go
+++ b/internal/utils/serialization.go
@@ -5,7 +5,9 @@ import (
"encoding/json"
"fmt"
"reflect"
+ "strconv"
"strings"
+ "unicode"
"github.com/santhosh-tekuri/jsonschema"
E "github.com/yusing/go-proxy/internal/error"
@@ -210,17 +212,16 @@ func Convert(src reflect.Value, dst reflect.Value) E.NestedError {
switch {
case srcT.AssignableTo(dstT):
dst.Set(src)
+ return nil
case srcT.ConvertibleTo(dstT):
dst.Set(src.Convert(dstT))
+ return nil
case srcT.Kind() == reflect.Map:
obj, ok := src.Interface().(SerializedObject)
if !ok {
return E.TypeMismatch[SerializedObject](src.Interface())
}
- err := Deserialize(obj, dst.Addr().Interface())
- if err != nil {
- return err
- }
+ return Deserialize(obj, dst.Addr().Interface())
case srcT.Kind() == reflect.Slice:
if dstT.Kind() != reflect.Slice {
return E.TypeError("slice", srcT, dstT)
@@ -237,33 +238,114 @@ func Convert(src reflect.Value, dst reflect.Value) E.NestedError {
i++
}
dst.Set(newSlice)
- default:
- var converter Converter
- var ok bool
- // check if (*T).Convertor is implemented
- if converter, ok = dst.Addr().Interface().(Converter); !ok {
- // check if (T).Convertor is implemented
- converter, ok = dst.Interface().(Converter)
- if !ok {
- return E.TypeError("conversion", srcT, dstT)
- }
- }
-
- converted, err := converter.ConvertFrom(src.Interface())
- if err != nil {
+ return nil
+ case src.Kind() == reflect.String:
+ if convertible, err := ConvertString(src.String(), dst); convertible {
return err
}
- c := reflect.ValueOf(converted)
- if c.Kind() == reflect.Ptr {
- c = c.Elem()
- }
- dst.Set(c)
- return nil
}
+ var converter Converter
+ var ok bool
+ // check if (*T).Convertor is implemented
+ if converter, ok = dst.Addr().Interface().(Converter); !ok {
+ // check if (T).Convertor is implemented
+ converter, ok = dst.Interface().(Converter)
+ if !ok {
+ return E.TypeError("conversion", srcT, dstT)
+ }
+ }
+
+ converted, err := converter.ConvertFrom(src.Interface())
+ if err != nil {
+ return err
+ }
+ c := reflect.ValueOf(converted)
+ if c.Kind() == reflect.Ptr {
+ c = c.Elem()
+ }
+ dst.Set(c)
return nil
}
+func ConvertString(src string, dst reflect.Value) (convertible bool, convErr E.NestedError) {
+ convertible = true
+ // primitive types / simple types
+ switch dst.Kind() {
+ case reflect.Bool:
+ b, err := strconv.ParseBool(src)
+ if err != nil {
+ convErr = E.Invalid("boolean", src)
+ return
+ }
+ dst.Set(reflect.ValueOf(b))
+ return
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ i, err := strconv.ParseInt(src, 10, 64)
+ if err != nil {
+ convErr = E.Invalid("int", src)
+ return
+ }
+ dst.Set(reflect.ValueOf(i))
+ return
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ i, err := strconv.ParseUint(src, 10, 64)
+ if err != nil {
+ convErr = E.Invalid("uint", src)
+ return
+ }
+ dst.Set(reflect.ValueOf(i))
+ return
+ }
+ // yaml like
+ lines := strings.Split(strings.TrimSpace(src), "\n")
+ for i := range lines {
+ lines[i] = strings.TrimSpace(lines[i])
+ }
+ var tmp any
+ switch dst.Kind() {
+ case reflect.Slice:
+ // one liner is comma seperated list
+ if len(lines) == 0 {
+ dst.Set(reflect.ValueOf(CommaSeperatedList(src)))
+ return
+ }
+ sl := make([]string, 0, len(lines))
+ for _, line := range lines {
+ line = strings.TrimLeftFunc(line, func(r rune) bool {
+ return r == '-' || unicode.IsSpace(r)
+ })
+ if line == "" {
+ continue
+ }
+ sl = append(sl, line)
+ }
+ tmp = sl
+ case reflect.Map:
+ m := make(map[string]string, len(lines))
+ for i, line := range lines {
+ parts := strings.Split(line, ":")
+ if len(parts) < 2 {
+ convErr = E.Invalid("map", "missing colon").Subjectf("line#%d", i+1).With(line)
+ return
+ }
+ if len(parts) > 2 {
+ convErr = E.Invalid("map", "too many colons").Subjectf("line#%d", i+1).With(line)
+ return
+ }
+ k := strings.TrimSpace(parts[0])
+ v := strings.TrimSpace(parts[1])
+ m[k] = v
+ }
+ tmp = m
+ }
+ if tmp == nil {
+ convertible = false
+ return
+ }
+ return true, Convert(reflect.ValueOf(tmp), dst)
+}
+
func DeserializeJson(j map[string]string, target any) E.NestedError {
data, err := E.Check(json.Marshal(j))
if err != nil {
diff --git a/internal/utils/string.go b/internal/utils/string.go
index 5938d85..ccca361 100644
--- a/internal/utils/string.go
+++ b/internal/utils/string.go
@@ -4,6 +4,8 @@ import (
"net/url"
"strconv"
"strings"
+
+ E "github.com/yusing/go-proxy/internal/error"
)
func CommaSeperatedList(s string) []string {
@@ -14,6 +16,10 @@ func CommaSeperatedList(s string) []string {
return res
}
+func IntParser(value string) (int, E.NestedError) {
+ return E.Check(strconv.Atoi(value))
+}
+
func ExtractPort(fullURL string) (int, error) {
url, err := url.Parse(fullURL)
if err != nil {