mirror of
https://github.com/yusing/godoxy.git
synced 2026-03-29 13:21:55 +02:00
restructured the project to comply community guideline, for others check release note
This commit is contained in:
6
internal/proxy/fields/alias.go
Normal file
6
internal/proxy/fields/alias.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package fields
|
||||
|
||||
type (
|
||||
Alias string
|
||||
NewAlias = Alias
|
||||
)
|
||||
19
internal/proxy/fields/headers.go
Normal file
19
internal/proxy/fields/headers.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package fields
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
)
|
||||
|
||||
func ValidateHTTPHeaders(headers map[string]string) (http.Header, E.NestedError) {
|
||||
h := make(http.Header)
|
||||
for k, v := range headers {
|
||||
vSplit := strings.Split(v, ",")
|
||||
for _, header := range vSplit {
|
||||
h.Add(k, strings.TrimSpace(header))
|
||||
}
|
||||
}
|
||||
return h, nil
|
||||
}
|
||||
12
internal/proxy/fields/host.go
Normal file
12
internal/proxy/fields/host.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package fields
|
||||
|
||||
import (
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
)
|
||||
|
||||
type Host string
|
||||
type Subdomain = Alias
|
||||
|
||||
func ValidateHost[String ~string](s String) (Host, E.NestedError) {
|
||||
return Host(s), nil
|
||||
}
|
||||
24
internal/proxy/fields/path_mode.go
Normal file
24
internal/proxy/fields/path_mode.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package fields
|
||||
|
||||
import (
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
)
|
||||
|
||||
type PathMode string
|
||||
|
||||
func NewPathMode(pm string) (PathMode, E.NestedError) {
|
||||
switch pm {
|
||||
case "", "forward":
|
||||
return PathMode(pm), nil
|
||||
default:
|
||||
return "", E.Invalid("path mode", pm)
|
||||
}
|
||||
}
|
||||
|
||||
func (p PathMode) IsRemove() bool {
|
||||
return p == ""
|
||||
}
|
||||
|
||||
func (p PathMode) IsForward() bool {
|
||||
return p == "forward"
|
||||
}
|
||||
37
internal/proxy/fields/path_pattern.go
Normal file
37
internal/proxy/fields/path_pattern.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package fields
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
)
|
||||
|
||||
type PathPattern string
|
||||
type PathPatterns = []PathPattern
|
||||
|
||||
func NewPathPattern(s string) (PathPattern, E.NestedError) {
|
||||
if len(s) == 0 {
|
||||
return "", E.Invalid("path", "must not be empty")
|
||||
}
|
||||
if !pathPattern.MatchString(s) {
|
||||
return "", E.Invalid("path pattern", s)
|
||||
}
|
||||
return PathPattern(s), nil
|
||||
}
|
||||
|
||||
func ValidatePathPatterns(s []string) (PathPatterns, E.NestedError) {
|
||||
if len(s) == 0 {
|
||||
return []PathPattern{"/"}, nil
|
||||
}
|
||||
pp := make(PathPatterns, len(s))
|
||||
for i, v := range s {
|
||||
if pattern, err := NewPathPattern(v); err.HasError() {
|
||||
return nil, err
|
||||
} else {
|
||||
pp[i] = pattern
|
||||
}
|
||||
}
|
||||
return pp, nil
|
||||
}
|
||||
|
||||
var pathPattern = regexp.MustCompile(`^(/[-\w./]*({\$\})?|((GET|POST|DELETE|PUT|HEAD|OPTION) /[-\w./]*({\$\})?))$`)
|
||||
47
internal/proxy/fields/path_pattern_test.go
Normal file
47
internal/proxy/fields/path_pattern_test.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package fields
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
U "github.com/yusing/go-proxy/internal/utils/testing"
|
||||
)
|
||||
|
||||
var validPatterns = []string{
|
||||
"/",
|
||||
"/index.html",
|
||||
"/somepage/",
|
||||
"/drive/abc.mp4",
|
||||
"/{$}",
|
||||
"/some-page/{$}",
|
||||
"GET /",
|
||||
"GET /static/{$}",
|
||||
"GET /drive/abc.mp4",
|
||||
"GET /drive/abc.mp4/{$}",
|
||||
"POST /auth",
|
||||
"DELETE /user/",
|
||||
"PUT /storage/id/",
|
||||
}
|
||||
|
||||
var invalidPatterns = []string{
|
||||
"/$",
|
||||
"/{$}{$}",
|
||||
"/{$}/{$}",
|
||||
"/index.html$",
|
||||
"get /",
|
||||
"GET/",
|
||||
"GET /$",
|
||||
"GET /drive/{$}/abc.mp4/",
|
||||
"OPTION /config/{$}/abc.conf/{$}",
|
||||
}
|
||||
|
||||
func TestPathPatternRegex(t *testing.T) {
|
||||
for _, pattern := range validPatterns {
|
||||
_, err := NewPathPattern(pattern)
|
||||
U.ExpectNoError(t, err.Error())
|
||||
}
|
||||
for _, pattern := range invalidPatterns {
|
||||
_, err := NewPathPattern(pattern)
|
||||
U.ExpectError2(t, pattern, E.ErrInvalid, err.Error())
|
||||
}
|
||||
}
|
||||
41
internal/proxy/fields/port.go
Normal file
41
internal/proxy/fields/port.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package fields
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
)
|
||||
|
||||
type Port int
|
||||
|
||||
func ValidatePort[String ~string](v String) (Port, E.NestedError) {
|
||||
p, err := strconv.Atoi(string(v))
|
||||
if err != nil {
|
||||
return ErrPort, E.Invalid("port number", v).With(err)
|
||||
}
|
||||
return ValidatePortInt(p)
|
||||
}
|
||||
|
||||
func ValidatePortInt[Int int | uint16](v Int) (Port, E.NestedError) {
|
||||
p := Port(v)
|
||||
if !p.inBound() {
|
||||
return ErrPort, E.OutOfRange("port", p)
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (p Port) inBound() bool {
|
||||
return p >= MinPort && p <= MaxPort
|
||||
}
|
||||
|
||||
func (p Port) String() string {
|
||||
return strconv.Itoa(int(p))
|
||||
}
|
||||
|
||||
const (
|
||||
MinPort = 0
|
||||
MaxPort = 65535
|
||||
ErrPort = Port(-1)
|
||||
NoPort = Port(-1)
|
||||
ZeroPort = Port(0)
|
||||
)
|
||||
21
internal/proxy/fields/scheme.go
Normal file
21
internal/proxy/fields/scheme.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package fields
|
||||
|
||||
import (
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
)
|
||||
|
||||
type Scheme string
|
||||
|
||||
func NewScheme[String ~string](s String) (Scheme, E.NestedError) {
|
||||
switch s {
|
||||
case "http", "https", "tcp", "udp":
|
||||
return Scheme(s), nil
|
||||
}
|
||||
return "", E.Invalid("scheme", s)
|
||||
}
|
||||
|
||||
func (s Scheme) IsHTTP() bool { return s == "http" }
|
||||
func (s Scheme) IsHTTPS() bool { return s == "https" }
|
||||
func (s Scheme) IsTCP() bool { return s == "tcp" }
|
||||
func (s Scheme) IsUDP() bool { return s == "udp" }
|
||||
func (s Scheme) IsStream() bool { return s.IsTCP() || s.IsUDP() }
|
||||
17
internal/proxy/fields/signal.go
Normal file
17
internal/proxy/fields/signal.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package fields
|
||||
|
||||
import (
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
)
|
||||
|
||||
type Signal string
|
||||
|
||||
func ValidateSignal(s string) (Signal, E.NestedError) {
|
||||
switch s {
|
||||
case "", "SIGINT", "SIGTERM", "SIGHUP", "SIGQUIT",
|
||||
"INT", "TERM", "HUP", "QUIT":
|
||||
return Signal(s), nil
|
||||
}
|
||||
|
||||
return "", E.Invalid("signal", s)
|
||||
}
|
||||
23
internal/proxy/fields/stop_method.go
Normal file
23
internal/proxy/fields/stop_method.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package fields
|
||||
|
||||
import (
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
)
|
||||
|
||||
type StopMethod string
|
||||
|
||||
const (
|
||||
StopMethodPause StopMethod = "pause"
|
||||
StopMethodStop StopMethod = "stop"
|
||||
StopMethodKill StopMethod = "kill"
|
||||
)
|
||||
|
||||
func ValidateStopMethod(s string) (StopMethod, E.NestedError) {
|
||||
sm := StopMethod(s)
|
||||
switch sm {
|
||||
case StopMethodPause, StopMethodStop, StopMethodKill:
|
||||
return sm, nil
|
||||
default:
|
||||
return "", E.Invalid("stop_method", sm)
|
||||
}
|
||||
}
|
||||
56
internal/proxy/fields/stream_port.go
Normal file
56
internal/proxy/fields/stream_port.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package fields
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
)
|
||||
|
||||
type StreamPort struct {
|
||||
ListeningPort Port `json:"listening"`
|
||||
ProxyPort Port `json:"proxy"`
|
||||
}
|
||||
|
||||
func ValidateStreamPort(p string) (StreamPort, E.NestedError) {
|
||||
split := strings.Split(p, ":")
|
||||
|
||||
switch len(split) {
|
||||
case 1:
|
||||
split = []string{"0", split[0]}
|
||||
case 2:
|
||||
break
|
||||
default:
|
||||
return ErrStreamPort, E.Invalid("stream port", p).With("too many colons")
|
||||
}
|
||||
|
||||
listeningPort, err := ValidatePort(split[0])
|
||||
if err != nil {
|
||||
return ErrStreamPort, err.Subject("listening port")
|
||||
}
|
||||
|
||||
proxyPort, err := ValidatePort(split[1])
|
||||
|
||||
if err.Is(E.ErrOutOfRange) {
|
||||
return ErrStreamPort, err.Subject("proxy port")
|
||||
} else if proxyPort == 0 {
|
||||
return ErrStreamPort, E.Invalid("proxy port", p)
|
||||
} else if err != nil {
|
||||
proxyPort, err = parseNameToPort(split[1])
|
||||
if err != nil {
|
||||
return ErrStreamPort, E.Invalid("proxy port", proxyPort)
|
||||
}
|
||||
}
|
||||
|
||||
return StreamPort{listeningPort, proxyPort}, nil
|
||||
}
|
||||
|
||||
func parseNameToPort(name string) (Port, E.NestedError) {
|
||||
port, ok := common.ServiceNamePortMapTCP[name]
|
||||
if !ok {
|
||||
return ErrPort, E.Invalid("service", name)
|
||||
}
|
||||
return Port(port), nil
|
||||
}
|
||||
|
||||
var ErrStreamPort = StreamPort{ErrPort, ErrPort}
|
||||
48
internal/proxy/fields/stream_port_test.go
Normal file
48
internal/proxy/fields/stream_port_test.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package fields
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
. "github.com/yusing/go-proxy/internal/utils/testing"
|
||||
)
|
||||
|
||||
var validPorts = []string{
|
||||
"1234:5678",
|
||||
"0:2345",
|
||||
"2345",
|
||||
"1234:postgres",
|
||||
}
|
||||
|
||||
var invalidPorts = []string{
|
||||
"",
|
||||
"123:",
|
||||
"0:",
|
||||
":1234",
|
||||
"1234:1234:1234",
|
||||
"qwerty",
|
||||
"asdfgh:asdfgh",
|
||||
"1234:asdfgh",
|
||||
}
|
||||
|
||||
var outOfRangePorts = []string{
|
||||
"-1:1234",
|
||||
"1234:-1",
|
||||
"65536",
|
||||
"0:65536",
|
||||
}
|
||||
|
||||
func TestStreamPort(t *testing.T) {
|
||||
for _, port := range validPorts {
|
||||
_, err := ValidateStreamPort(port)
|
||||
ExpectNoError(t, err.Error())
|
||||
}
|
||||
for _, port := range invalidPorts {
|
||||
_, err := ValidateStreamPort(port)
|
||||
ExpectError2(t, port, E.ErrInvalid, err.Error())
|
||||
}
|
||||
for _, port := range outOfRangePorts {
|
||||
_, err := ValidateStreamPort(port)
|
||||
ExpectError2(t, port, E.ErrOutOfRange, err.Error())
|
||||
}
|
||||
}
|
||||
43
internal/proxy/fields/stream_scheme.go
Normal file
43
internal/proxy/fields/stream_scheme.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package fields
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
)
|
||||
|
||||
type StreamScheme struct {
|
||||
ListeningScheme Scheme `json:"listening"`
|
||||
ProxyScheme Scheme `json:"proxy"`
|
||||
}
|
||||
|
||||
func ValidateStreamScheme(s string) (ss *StreamScheme, err E.NestedError) {
|
||||
ss = &StreamScheme{}
|
||||
parts := strings.Split(s, ":")
|
||||
if len(parts) == 1 {
|
||||
parts = []string{s, s}
|
||||
} else if len(parts) != 2 {
|
||||
return nil, E.Invalid("stream scheme", s)
|
||||
}
|
||||
ss.ListeningScheme, err = NewScheme(parts[0])
|
||||
if err.HasError() {
|
||||
return nil, err
|
||||
}
|
||||
ss.ProxyScheme, err = NewScheme(parts[1])
|
||||
if err.HasError() {
|
||||
return nil, err
|
||||
}
|
||||
return ss, nil
|
||||
}
|
||||
|
||||
func (s StreamScheme) String() string {
|
||||
return fmt.Sprintf("%s:%s", s.ListeningScheme, s.ProxyScheme)
|
||||
}
|
||||
|
||||
// IsCoherent checks if the ListeningScheme and ProxyScheme of the StreamScheme are equal.
|
||||
//
|
||||
// It returns a boolean value indicating whether the ListeningScheme and ProxyScheme are equal.
|
||||
func (s StreamScheme) IsCoherent() bool {
|
||||
return s.ListeningScheme == s.ProxyScheme
|
||||
}
|
||||
18
internal/proxy/fields/timeout.go
Normal file
18
internal/proxy/fields/timeout.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package fields
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
)
|
||||
|
||||
func ValidateDurationPostitive(value string) (time.Duration, E.NestedError) {
|
||||
d, err := time.ParseDuration(value)
|
||||
if err != nil {
|
||||
return 0, E.Invalid("duration", value)
|
||||
}
|
||||
if d < 0 {
|
||||
return 0, E.Invalid("duration", "negative value")
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
Reference in New Issue
Block a user