mirror of
https://github.com/yusing/godoxy.git
synced 2025-07-25 21:34:02 +02:00
v0.5: (BREAKING) new syntax for set_headers and hide_headers, updated label parser, error.Nil().String() will now return 'nil', better readme
This commit is contained in:
parent
5be8659a99
commit
2e7ba51521
9 changed files with 197 additions and 176 deletions
|
@ -1,4 +1,4 @@
|
||||||
FROM golang:1.22.6-alpine AS builder
|
FROM golang:1.23.1-alpine AS builder
|
||||||
COPY src /src
|
COPY src /src
|
||||||
ENV GOCACHE=/root/.cache/go-build
|
ENV GOCACHE=/root/.cache/go-build
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
|
|
22
README.md
22
README.md
|
@ -9,7 +9,7 @@ A [lightweight](docs/benchmark_result.md), easy-to-use, and efficient reverse pr
|
||||||
- [go-proxy](#go-proxy)
|
- [go-proxy](#go-proxy)
|
||||||
- [Key Points](#key-points)
|
- [Key Points](#key-points)
|
||||||
- [Getting Started](#getting-started)
|
- [Getting Started](#getting-started)
|
||||||
- [Commands](#commands)
|
- [Commands line arguments](#commands-line-arguments)
|
||||||
- [Environment variables](#environment-variables)
|
- [Environment variables](#environment-variables)
|
||||||
- [Use JSON Schema in VSCode](#use-json-schema-in-vscode)
|
- [Use JSON Schema in VSCode](#use-json-schema-in-vscode)
|
||||||
- [Config File](#config-file)
|
- [Config File](#config-file)
|
||||||
|
@ -45,20 +45,22 @@ A [lightweight](docs/benchmark_result.md), easy-to-use, and efficient reverse pr
|
||||||
|
|
||||||
[🔼Back to top](#table-of-content)
|
[🔼Back to top](#table-of-content)
|
||||||
|
|
||||||
### Commands
|
### Commands line arguments
|
||||||
|
|
||||||
- `go-proxy` start proxy server
|
| Argument | Description |
|
||||||
- `go-proxy validate` validate config and exit
|
| ---------- | -------------------------------- |
|
||||||
- `go-proxy reload` trigger a force reload of config
|
| empty | start proxy server |
|
||||||
|
| `validate` | validate config and exit |
|
||||||
|
| `reload` | trigger a force reload of config |
|
||||||
|
|
||||||
**For docker containers, run `docker exec -it go-proxy /app/go-proxy <command>`**
|
**run with `docker exec <container_name> /app/go-proxy <command>`**
|
||||||
|
|
||||||
### Environment variables
|
### Environment variables
|
||||||
|
|
||||||
Booleans:
|
| Environment Variable | Description | Default | Values |
|
||||||
|
| ------------------------------ | ------------------------- | ------- | ------- |
|
||||||
- `GOPROXY_DEBUG` enable debug behaviors
|
| `GOPROXY_NO_SCHEMA_VALIDATION` | disable schema validation | `false` | boolean |
|
||||||
- `GOPROXY_NO_SCHEMA_VALIDATION`: disable schema validation **(useful for testing new DNS Challenge providers)**
|
| `GOPROXY_DEBUG` | enable debug behaviors | `false` | boolean |
|
||||||
|
|
||||||
### Use JSON Schema in VSCode
|
### Use JSON Schema in VSCode
|
||||||
|
|
||||||
|
|
207
docs/docker.md
207
docs/docker.md
|
@ -1,126 +1,161 @@
|
||||||
# Docker container guide
|
# Docker compose guide
|
||||||
|
|
||||||
## Table of content
|
## Table of content
|
||||||
|
|
||||||
<!-- TOC -->
|
<!-- TOC -->
|
||||||
|
|
||||||
- [Docker container guide](#docker-container-guide)
|
- [Docker compose guide](#docker-compose-guide)
|
||||||
- [Table of content](#table-of-content)
|
- [Table of content](#table-of-content)
|
||||||
- [Setup](#setup)
|
- [Setup](#setup)
|
||||||
- [Labels](#labels)
|
- [Labels](#labels)
|
||||||
|
- [Syntax](#syntax)
|
||||||
|
- [Fields](#fields)
|
||||||
|
- [Key-value mapping example](#key-value-mapping-example)
|
||||||
|
- [List example](#list-example)
|
||||||
- [Troubleshooting](#troubleshooting)
|
- [Troubleshooting](#troubleshooting)
|
||||||
- [Docker compose examples](#docker-compose-examples)
|
- [Docker compose examples](#docker-compose-examples)
|
||||||
- [Local docker provider in bridge network](#local-docker-provider-in-bridge-network)
|
|
||||||
- [Proxy setup](#proxy-setup)
|
|
||||||
- [Services URLs for above examples](#services-urls-for-above-examples)
|
- [Services URLs for above examples](#services-urls-for-above-examples)
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
1. Install `wget` if not already
|
1. Install `wget` if not already
|
||||||
|
|
||||||
2. Run setup script
|
- Ubuntu based: `sudo apt install -y wget`
|
||||||
|
- Fedora based: `sudo yum install -y wget`
|
||||||
|
- Arch based: `sudo pacman -Sy wget`
|
||||||
|
|
||||||
`bash <(wget -qO- https://github.com/yusing/go-proxy/raw/main/setup-docker.sh)`
|
2. Run setup script
|
||||||
|
|
||||||
What it does:
|
`bash <(wget -qO- https://github.com/yusing/go-proxy/raw/main/setup-docker.sh)`
|
||||||
|
|
||||||
- Create required directories
|
It will setup folder structure and required config files
|
||||||
- Setup `config.yml` and `compose.yml`
|
|
||||||
|
|
||||||
3. Verify folder structure and then `cd go-proxy`
|
3. Verify folder structure and then `cd go-proxy`
|
||||||
|
|
||||||
```plain
|
```plain
|
||||||
go-proxy
|
go-proxy
|
||||||
├── certs
|
├── certs
|
||||||
├── compose.yml
|
├── compose.yml
|
||||||
└── config
|
└── config
|
||||||
├── config.yml
|
├── config.yml
|
||||||
└── providers.yml
|
└── providers.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Enable HTTPs _(optional)_
|
4. Enable HTTPs _(optional)_
|
||||||
|
|
||||||
- To use autocert feature
|
Mount a folder (to store obtained certs) or (containing existing cert)
|
||||||
|
|
||||||
- completing `autocert` section in `config/config.yml`
|
```yaml
|
||||||
- mount `certs/` to `/app/certs` to store obtained certs
|
services:
|
||||||
|
go-proxy:
|
||||||
|
...
|
||||||
|
volumes:
|
||||||
|
- ./certs:/app/certs
|
||||||
|
```
|
||||||
|
|
||||||
- To use existing certificate
|
To use **autocert**, complete that section in `config.yml`, e.g.
|
||||||
|
|
||||||
mount your wildcard (`*.y.z`) SSL cert
|
```yaml
|
||||||
|
autocert:
|
||||||
|
email: john.doe@x.y.z # ACME Email
|
||||||
|
domains: # a list of domains for cert registration
|
||||||
|
- x.y.z
|
||||||
|
provider: cloudflare
|
||||||
|
options:
|
||||||
|
- auth_token: c1234565789-abcdefghijklmnopqrst # your zone API token
|
||||||
|
```
|
||||||
|
|
||||||
- cert / chain / fullchain -> `/app/certs/cert.crt`
|
To use **existing certificate**, set path for cert and key in `config.yml`, e.g.
|
||||||
- private key -> `/app/certs/priv.key`
|
|
||||||
|
|
||||||
5. Modify `compose.yml` fit your needs
|
```yaml
|
||||||
|
autocert:
|
||||||
|
cert_path: /app/certs/cert.crt
|
||||||
|
key_path: /app/certs/priv.key
|
||||||
|
```
|
||||||
|
|
||||||
Add networks to make sure it is in the same network with other containers, or make sure `proxy.<alias>.host` is reachable
|
5. Modify `compose.yml` to fit your needs
|
||||||
|
|
||||||
6. Run `docker compose up -d` to start the container
|
6. Run `docker compose up -d` to start the container
|
||||||
|
|
||||||
7. Start editing config files in `http://<ip>:8080`
|
7. Navigate to Web panel `http://gp.yourdomain.com` and edit proxy config
|
||||||
|
|
||||||
[🔼Back to top](#table-of-content)
|
[🔼Back to top](#table-of-content)
|
||||||
|
|
||||||
## Labels
|
## Labels
|
||||||
|
|
||||||
- `proxy.aliases`: comma separated aliases for subdomain matching
|
### Syntax
|
||||||
|
|
||||||
- default: container name
|
| Label | Description |
|
||||||
|
| ----------------------- | -------------------------------------------------------- |
|
||||||
|
| `proxy.aliases` | comma separated aliases for subdomain and label matching |
|
||||||
|
| `proxy.<alias>.<field>` | set field for specific alias |
|
||||||
|
| `proxy.*.<field>` | set field for all aliases |
|
||||||
|
|
||||||
- `proxy.*.<field>`: wildcard label for all aliases
|
### Fields
|
||||||
|
|
||||||
_Labels below should have a **`proxy.<alias>.`** prefix._
|
| Field | Description | Default | Allowed Values / Syntax |
|
||||||
|
| --------------------- | ---------------------------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| `scheme` | proxy protocol | <ul><li>`http` for numeric port</li><li>`tcp` for `x:y` port</li></ul> | `http`, `https`, `tcp`, `udp` |
|
||||||
|
| `host` | proxy host | `container_name` | IP address, hostname |
|
||||||
|
| `port` | proxy port **(http/s)** | first port in `ports:` | number in range of `0 - 65535` |
|
||||||
|
| `port` **(required)** | proxy port **(tcp/udp)** | N/A | `x:y` <br><ul><li>x: port for `go-proxy` to listen on</li><li>y: port or [_service name_](../src/common/constants.go#L55) of target container</li></ul> |
|
||||||
|
| `no_tls_verify` | whether skip tls verify **(https only)** | `false` | boolean |
|
||||||
|
| `path` | proxy path | empty | **(http/s only)** string |
|
||||||
|
| `path_mode` | path handling **(http/s only)** | empty | empty, `forward` |
|
||||||
|
| `set_headers` | header to set **(http/s only)** | empty | yaml style key-value mapping[<sup>1</sup>](#1-key-value-mapping-example) |
|
||||||
|
| `hide_headers` | header to hide **(http/s only)** | empty | yaml style list[<sup>2</sup>](#2-list-example) |
|
||||||
|
|
||||||
_i.e. `proxy.nginx.scheme: http`_
|
#### Key-value mapping example
|
||||||
|
|
||||||
- `scheme`: proxy protocol
|
Docker Compose
|
||||||
- default:
|
|
||||||
- if `port` is like `x:y`: `tcp`
|
|
||||||
- if `port` is a number: `http`
|
|
||||||
- allowed: `http`, `https`, `tcp`, `udp`
|
|
||||||
- `host`: proxy host
|
|
||||||
- default: `container_name`
|
|
||||||
- allowed: IP address, hostname
|
|
||||||
- `port`: proxy port
|
|
||||||
- default: first port in `ports:`
|
|
||||||
- `http(s)`: number in range og `0 - 65535`
|
|
||||||
- `tcp`, `udp`: `x:y`
|
|
||||||
- `x`: port for `go-proxy` to listen on
|
|
||||||
- `y`: port, or _service name_ of target container
|
|
||||||
see [constants.go:14 for _service names_](../src/common/constants.go#L74)
|
|
||||||
- `no_tls_verify`: whether skip tls verify when scheme is https
|
|
||||||
- default: `false`
|
|
||||||
- `path`: proxy path _(http(s) proxy only)_
|
|
||||||
- default: empty
|
|
||||||
- `path_mode`: mode for path handling
|
|
||||||
|
|
||||||
- default: empty
|
```yaml
|
||||||
- allowed: empty, `forward`
|
services:
|
||||||
|
nginx:
|
||||||
|
...
|
||||||
|
labels:
|
||||||
|
# values from duplicated header keys will be combined
|
||||||
|
proxy.nginx.set_headers: | # remember to add the '|'
|
||||||
|
X-Custom-Header1: value1, value2
|
||||||
|
X-Custom-Header2: value3
|
||||||
|
X-Custom-Header2: value4
|
||||||
|
# X-Custom-Header2 will be "value3, value4"
|
||||||
|
```
|
||||||
|
|
||||||
- `empty`: remove path prefix from URL when proxying
|
File Provider
|
||||||
1. apps.y.z/webdav -> webdav:80
|
|
||||||
2. apps.y.z./webdav/path/to/file -> webdav:80/path/to/file
|
|
||||||
- `forward`: path remain unchanged
|
|
||||||
1. apps.y.z/webdav -> webdav:80/webdav
|
|
||||||
2. apps.y.z./webdav/path/to/file -> webdav:80/webdav/path/to/file
|
|
||||||
|
|
||||||
- `set_headers`: a list of header to set, (key:value, one by line)
|
```yaml
|
||||||
|
service_a:
|
||||||
|
host: service_a.internal
|
||||||
|
set_headers:
|
||||||
|
# do not duplicate header keys, as it is not allowed in YAML
|
||||||
|
X-Custom-Header1: value1, value2
|
||||||
|
X-Custom-Header2: value3
|
||||||
|
```
|
||||||
|
|
||||||
Duplicated keys will be treated as multiple-value headers
|
#### List example
|
||||||
|
|
||||||
```yaml
|
Docker Compose
|
||||||
labels:
|
|
||||||
proxy.app.set_headers: |
|
|
||||||
X-Custom-Header1: value1
|
|
||||||
X-Custom-Header1: value2
|
|
||||||
X-Custom-Header2: value2
|
|
||||||
```
|
|
||||||
|
|
||||||
- `hide_headers`: comma seperated list of headers to hide
|
```yaml
|
||||||
|
services:
|
||||||
|
nginx:
|
||||||
|
...
|
||||||
|
labels:
|
||||||
|
proxy.nginx.hide_headers: | # remember to add the '|'
|
||||||
|
- X-Custom-Header1
|
||||||
|
- X-Custom-Header2
|
||||||
|
```
|
||||||
|
|
||||||
- `load_balance`: enable load balance
|
File Provider
|
||||||
- allowed: `1`, `true`
|
|
||||||
|
```yaml
|
||||||
|
service_a:
|
||||||
|
host: service_a.internal
|
||||||
|
hide_headers:
|
||||||
|
- X-Custom-Header1
|
||||||
|
- X-Custom-Header2
|
||||||
|
```
|
||||||
|
|
||||||
[🔼Back to top](#table-of-content)
|
[🔼Back to top](#table-of-content)
|
||||||
|
|
||||||
|
@ -146,8 +181,6 @@ _i.e. `proxy.nginx.scheme: http`_
|
||||||
|
|
||||||
## Docker compose examples
|
## Docker compose examples
|
||||||
|
|
||||||
### Local docker provider in bridge network
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
volumes:
|
volumes:
|
||||||
adg-work:
|
adg-work:
|
||||||
|
@ -220,24 +253,6 @@ services:
|
||||||
|
|
||||||
[🔼Back to top](#table-of-content)
|
[🔼Back to top](#table-of-content)
|
||||||
|
|
||||||
#### Proxy setup
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
go-proxy:
|
|
||||||
image: ghcr.io/yusing/go-proxy
|
|
||||||
container_name: go-proxy
|
|
||||||
restart: always
|
|
||||||
network_mode: host
|
|
||||||
volumes:
|
|
||||||
- ./config:/app/config
|
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
||||||
labels:
|
|
||||||
- proxy.aliases=gp
|
|
||||||
- proxy.gp.port=8080
|
|
||||||
```
|
|
||||||
|
|
||||||
[🔼Back to top](#table-of-content)
|
|
||||||
|
|
||||||
### Services URLs for above examples
|
### Services URLs for above examples
|
||||||
|
|
||||||
- `gp.yourdomain.com`: go-proxy web panel
|
- `gp.yourdomain.com`: go-proxy web panel
|
||||||
|
|
4
go.work
4
go.work
|
@ -1,5 +1,5 @@
|
||||||
go 1.22.0
|
go 1.22
|
||||||
|
|
||||||
toolchain go1.22.6
|
toolchain go1.23.1
|
||||||
|
|
||||||
use ./src
|
use ./src
|
||||||
|
|
|
@ -5,8 +5,15 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
E "github.com/yusing/go-proxy/error"
|
E "github.com/yusing/go-proxy/error"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func yamlParser[T any](value string) (any, E.NestedError) {
|
||||||
|
var data T
|
||||||
|
err := E.From(yaml.Unmarshal([]byte(value), &data))
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
|
||||||
func setHeadersParser(value string) (any, E.NestedError) {
|
func setHeadersParser(value string) (any, E.NestedError) {
|
||||||
value = strings.TrimSpace(value)
|
value = strings.TrimSpace(value)
|
||||||
lines := strings.Split(value, "\n")
|
lines := strings.Split(value, "\n")
|
||||||
|
@ -17,8 +24,10 @@ func setHeadersParser(value string) (any, E.NestedError) {
|
||||||
return nil, E.Invalid("set header statement", line)
|
return nil, E.Invalid("set header statement", line)
|
||||||
}
|
}
|
||||||
key := strings.TrimSpace(parts[0])
|
key := strings.TrimSpace(parts[0])
|
||||||
val := strings.TrimSpace(parts[1])
|
vals := strings.Split(parts[1], ",")
|
||||||
h.Add(key, val)
|
for i := range vals {
|
||||||
|
h.Add(key, strings.TrimSpace(vals[i]))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return h, E.Nil()
|
return h, E.Nil()
|
||||||
}
|
}
|
||||||
|
@ -48,7 +57,7 @@ var _ = func() int {
|
||||||
RegisterNamespace(NSProxy, ValueParserMap{
|
RegisterNamespace(NSProxy, ValueParserMap{
|
||||||
"aliases": commaSepParser,
|
"aliases": commaSepParser,
|
||||||
"set_headers": setHeadersParser,
|
"set_headers": setHeadersParser,
|
||||||
"hide_headers": commaSepParser,
|
"hide_headers": yamlParser[[]string],
|
||||||
"no_tls_verify": boolParser,
|
"no_tls_verify": boolParser,
|
||||||
})
|
})
|
||||||
return 0
|
return 0
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
E "github.com/yusing/go-proxy/error"
|
E "github.com/yusing/go-proxy/error"
|
||||||
|
@ -33,27 +34,17 @@ func TestHomePageLabel(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStringProxyLabel(t *testing.T) {
|
func TestStringProxyLabel(t *testing.T) {
|
||||||
alias := "foo"
|
|
||||||
field := "ip"
|
|
||||||
v := "bar"
|
v := "bar"
|
||||||
pl, err := ParseLabel(makeLabel(NSProxy, alias, field), v)
|
pl, err := ParseLabel(makeLabel(NSProxy, "foo", "ip"), v)
|
||||||
if err.IsNotNil() {
|
if err.IsNotNil() {
|
||||||
t.Errorf("expected err=nil, got %s", err.Error())
|
t.Errorf("expected err=nil, got %s", err.Error())
|
||||||
}
|
}
|
||||||
if pl.Target != alias {
|
|
||||||
t.Errorf("expected alias=%s, got %s", alias, pl.Target)
|
|
||||||
}
|
|
||||||
if pl.Attribute != field {
|
|
||||||
t.Errorf("expected field=%s, got %s", field, pl.Target)
|
|
||||||
}
|
|
||||||
if pl.Value != v {
|
if pl.Value != v {
|
||||||
t.Errorf("expected value=%q, got %s", v, pl.Value)
|
t.Errorf("expected value=%q, got %s", v, pl.Value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBoolProxyLabelValid(t *testing.T) {
|
func TestBoolProxyLabelValid(t *testing.T) {
|
||||||
alias := "foo"
|
|
||||||
field := "no_tls_verify"
|
|
||||||
tests := map[string]bool{
|
tests := map[string]bool{
|
||||||
"true": true,
|
"true": true,
|
||||||
"TRUE": true,
|
"TRUE": true,
|
||||||
|
@ -66,16 +57,10 @@ func TestBoolProxyLabelValid(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range tests {
|
for k, v := range tests {
|
||||||
pl, err := ParseLabel(makeLabel(NSProxy, alias, field), k)
|
pl, err := ParseLabel(makeLabel(NSProxy, "foo", "no_tls_verify"), k)
|
||||||
if err.IsNotNil() {
|
if err.IsNotNil() {
|
||||||
t.Errorf("expected err=nil, got %s", err.Error())
|
t.Errorf("expected err=nil, got %s", err.Error())
|
||||||
}
|
}
|
||||||
if pl.Target != alias {
|
|
||||||
t.Errorf("expected alias=%s, got %s", alias, pl.Target)
|
|
||||||
}
|
|
||||||
if pl.Attribute != field {
|
|
||||||
t.Errorf("expected field=%s, got %s", field, pl.Attribute)
|
|
||||||
}
|
|
||||||
if pl.Value != v {
|
if pl.Value != v {
|
||||||
t.Errorf("expected value=%v, got %v", v, pl.Value)
|
t.Errorf("expected value=%v, got %v", v, pl.Value)
|
||||||
}
|
}
|
||||||
|
@ -87,33 +72,26 @@ func TestBoolProxyLabelInvalid(t *testing.T) {
|
||||||
field := "no_tls_verify"
|
field := "no_tls_verify"
|
||||||
_, err := ParseLabel(makeLabel(NSProxy, alias, field), "invalid")
|
_, err := ParseLabel(makeLabel(NSProxy, alias, field), "invalid")
|
||||||
if !err.Is(E.ErrInvalid) {
|
if !err.Is(E.ErrInvalid) {
|
||||||
t.Errorf("expected err InvalidProxyLabel, got %v", reflect.TypeOf(err))
|
t.Errorf("expected err InvalidProxyLabel, got %s", err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHeaderProxyLabelValid(t *testing.T) {
|
func TestSetHeaderProxyLabelValid(t *testing.T) {
|
||||||
alias := "foo"
|
|
||||||
field := "set_headers"
|
|
||||||
v := `
|
v := `
|
||||||
X-Custom-Header1: foo
|
X-Custom-Header1: foo, bar
|
||||||
X-Custom-Header1: bar
|
X-Custom-Header1: baz
|
||||||
X-Custom-Header2: baz
|
X-Custom-Header2: boo`
|
||||||
`
|
v = strings.TrimPrefix(v, "\n")
|
||||||
h := make(http.Header, 0)
|
h := make(http.Header, 0)
|
||||||
h.Set("X-Custom-Header1", "foo")
|
h.Set("X-Custom-Header1", "foo")
|
||||||
h.Add("X-Custom-Header1", "bar")
|
h.Add("X-Custom-Header1", "bar")
|
||||||
h.Set("X-Custom-Header2", "baz")
|
h.Add("X-Custom-Header1", "baz")
|
||||||
|
h.Set("X-Custom-Header2", "boo")
|
||||||
|
|
||||||
pl, err := ParseLabel(makeLabel(NSProxy, alias, field), v)
|
pl, err := ParseLabel(makeLabel(NSProxy, "foo", "set_headers"), v)
|
||||||
if err.IsNotNil() {
|
if err.IsNotNil() {
|
||||||
t.Errorf("expected err=nil, got %s", err.Error())
|
t.Errorf("expected err=nil, got %s", err.Error())
|
||||||
}
|
}
|
||||||
if pl.Target != alias {
|
|
||||||
t.Errorf("expected alias=%s, got %s", alias, pl.Target)
|
|
||||||
}
|
|
||||||
if pl.Attribute != field {
|
|
||||||
t.Errorf("expected field=%s, got %s", field, pl.Attribute)
|
|
||||||
}
|
|
||||||
hGot, ok := pl.Value.(http.Header)
|
hGot, ok := pl.Value.(http.Header)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Error("value is not http.Header")
|
t.Error("value is not http.Header")
|
||||||
|
@ -127,40 +105,52 @@ func TestHeaderProxyLabelValid(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHeaderProxyLabelInvalid(t *testing.T) {
|
func TestSetHeaderProxyLabelInvalid(t *testing.T) {
|
||||||
alias := "foo"
|
|
||||||
field := "set_headers"
|
|
||||||
tests := []string{
|
tests := []string{
|
||||||
"X-Custom-Header1 = bar",
|
"X-Custom-Header1 = bar",
|
||||||
"X-Custom-Header1",
|
"X-Custom-Header1",
|
||||||
|
"- X-Custom-Header1",
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, v := range tests {
|
for _, v := range tests {
|
||||||
_, err := ParseLabel(makeLabel(NSProxy, alias, field), v)
|
_, err := ParseLabel(makeLabel(NSProxy, "foo", "set_headers"), v)
|
||||||
if !err.Is(E.ErrInvalid) {
|
if !err.Is(E.ErrInvalid) {
|
||||||
t.Errorf("expected err InvalidProxyLabel for %q, got %v", v, err)
|
t.Errorf("expected invalid err for %q, got %s", v, err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCommaSepProxyLabelSingle(t *testing.T) {
|
func TestHideHeadersProxyLabel(t *testing.T) {
|
||||||
alias := "foo"
|
v := `
|
||||||
field := "hide_headers"
|
- X-Custom-Header1
|
||||||
v := "X-Custom-Header1"
|
- X-Custom-Header2
|
||||||
pl, err := ParseLabel(makeLabel(NSProxy, alias, field), v)
|
- X-Custom-Header3
|
||||||
|
`
|
||||||
|
v = strings.TrimPrefix(v, "\n")
|
||||||
|
pl, err := ParseLabel(makeLabel(NSProxy, "foo", "hide_headers"), v)
|
||||||
if err.IsNotNil() {
|
if err.IsNotNil() {
|
||||||
t.Errorf("expected err=nil, got %s", err.Error())
|
t.Errorf("expected err=nil, got %s", err.Error())
|
||||||
}
|
}
|
||||||
if pl.Target != alias {
|
sGot, ok := pl.Value.([]string)
|
||||||
t.Errorf("expected alias=%s, got %s", alias, pl.Target)
|
sWant := []string{"X-Custom-Header1", "X-Custom-Header2", "X-Custom-Header3"}
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("value is not []string, but %T", pl.Value)
|
||||||
}
|
}
|
||||||
if pl.Attribute != field {
|
if !reflect.DeepEqual(sGot, sWant) {
|
||||||
t.Errorf("expected field=%s, got %s", field, pl.Attribute)
|
t.Errorf("expected %q, got %q", sWant, sGot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommaSepProxyLabelSingle(t *testing.T) {
|
||||||
|
v := "a"
|
||||||
|
pl, err := ParseLabel("proxy.aliases", v)
|
||||||
|
if err.IsNotNil() {
|
||||||
|
t.Errorf("expected err=nil, got %s", err.Error())
|
||||||
}
|
}
|
||||||
sGot, ok := pl.Value.([]string)
|
sGot, ok := pl.Value.([]string)
|
||||||
sWant := []string{"X-Custom-Header1"}
|
sWant := []string{"a"}
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Error("value is not []string")
|
t.Errorf("value is not []string, but %T", pl.Value)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(sGot, sWant) {
|
if !reflect.DeepEqual(sGot, sWant) {
|
||||||
t.Errorf("expected %q, got %q", sWant, sGot)
|
t.Errorf("expected %q, got %q", sWant, sGot)
|
||||||
|
@ -168,23 +158,15 @@ func TestCommaSepProxyLabelSingle(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCommaSepProxyLabelMulti(t *testing.T) {
|
func TestCommaSepProxyLabelMulti(t *testing.T) {
|
||||||
alias := "foo"
|
|
||||||
field := "hide_headers"
|
|
||||||
v := "X-Custom-Header1, X-Custom-Header2,X-Custom-Header3"
|
v := "X-Custom-Header1, X-Custom-Header2,X-Custom-Header3"
|
||||||
pl, err := ParseLabel(makeLabel(NSProxy, alias, field), v)
|
pl, err := ParseLabel("proxy.aliases", v)
|
||||||
if err.IsNotNil() {
|
if err.IsNotNil() {
|
||||||
t.Errorf("expected err=nil, got %s", err.Error())
|
t.Errorf("expected err=nil, got %s", err.Error())
|
||||||
}
|
}
|
||||||
if pl.Target != alias {
|
|
||||||
t.Errorf("expected alias=%s, got %s", alias, pl.Target)
|
|
||||||
}
|
|
||||||
if pl.Attribute != field {
|
|
||||||
t.Errorf("expected field=%s, got %s", field, pl.Attribute)
|
|
||||||
}
|
|
||||||
sGot, ok := pl.Value.([]string)
|
sGot, ok := pl.Value.([]string)
|
||||||
sWant := []string{"X-Custom-Header1", "X-Custom-Header2", "X-Custom-Header3"}
|
sWant := []string{"X-Custom-Header1", "X-Custom-Header2", "X-Custom-Header3"}
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Error("value is not []string")
|
t.Errorf("value is not []string, but %T", pl.Value)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(sGot, sWant) {
|
if !reflect.DeepEqual(sGot, sWant) {
|
||||||
t.Errorf("expected %q, got %q", sWant, sGot)
|
t.Errorf("expected %q, got %q", sWant, sGot)
|
|
@ -139,9 +139,12 @@ func (ne *NestedError) writeToSB(sb *strings.Builder, level int, prefix string)
|
||||||
ne.writeIndents(sb, level)
|
ne.writeIndents(sb, level)
|
||||||
sb.WriteString(prefix)
|
sb.WriteString(prefix)
|
||||||
|
|
||||||
if ne.err != nil {
|
if ne.IsNil() {
|
||||||
sb.WriteString(ne.err.Error())
|
sb.WriteString("nil")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sb.WriteString(ne.err.Error())
|
||||||
if ne.subject != "" {
|
if ne.subject != "" {
|
||||||
if ne.err != nil {
|
if ne.err != nil {
|
||||||
sb.WriteString(fmt.Sprintf(" for %q", ne.subject))
|
sb.WriteString(fmt.Sprintf(" for %q", ne.subject))
|
||||||
|
|
|
@ -19,6 +19,16 @@ func TestErrorIs(t *testing.T) {
|
||||||
|
|
||||||
AssertEq(t, Invalid("foo", "bar").Is(ErrInvalid), true)
|
AssertEq(t, Invalid("foo", "bar").Is(ErrInvalid), true)
|
||||||
AssertEq(t, Invalid("foo", "bar").Is(ErrFailure), false)
|
AssertEq(t, Invalid("foo", "bar").Is(ErrFailure), false)
|
||||||
|
|
||||||
|
AssertEq(t, Nil().Is(nil), true)
|
||||||
|
AssertEq(t, Nil().Is(ErrInvalid), false)
|
||||||
|
AssertEq(t, Invalid("foo", "bar").Is(nil), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNil(t *testing.T) {
|
||||||
|
AssertEq(t, Nil().IsNil(), true)
|
||||||
|
AssertEq(t, Nil().IsNotNil(), false)
|
||||||
|
AssertEq(t, Nil().Error(), "nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestErrorSimple(t *testing.T) {
|
func TestErrorSimple(t *testing.T) {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
module github.com/yusing/go-proxy
|
module github.com/yusing/go-proxy
|
||||||
|
|
||||||
go 1.22.0
|
go 1.22
|
||||||
|
|
||||||
toolchain go1.22.6
|
toolchain go1.23.1
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/docker/cli v27.2.1+incompatible
|
github.com/docker/cli v27.2.1+incompatible
|
||||||
|
|
Loading…
Add table
Reference in a new issue