mirror of
https://github.com/yusing/godoxy.git
synced 2026-04-26 18:28:30 +02:00
feat(route): add bind address support for TCP/UDP routes
- Introduced a new `Bind` field in the route configuration to specify the address to listen on for TCP and UDP routes. - Defaulted the bind address to "0.0.0.0" if not provided. - Enhanced validation to ensure the bind address is a valid IP. - Updated stream initialization to use the correct network type (tcp4/tcp6 or udp4/udp6) based on the bind address. - Refactored stream creation functions to accept the network type as a parameter.
This commit is contained in:
@@ -4189,6 +4189,11 @@
|
|||||||
"x-nullable": false,
|
"x-nullable": false,
|
||||||
"x-omitempty": false
|
"x-omitempty": false
|
||||||
},
|
},
|
||||||
|
"bind": {
|
||||||
|
"description": "for TCP and UDP routes, bind address to listen on",
|
||||||
|
"type": "string",
|
||||||
|
"x-nullable": true
|
||||||
|
},
|
||||||
"container": {
|
"container": {
|
||||||
"description": "Docker only",
|
"description": "Docker only",
|
||||||
"allOf": [
|
"allOf": [
|
||||||
@@ -5327,6 +5332,11 @@
|
|||||||
"x-nullable": false,
|
"x-nullable": false,
|
||||||
"x-omitempty": false
|
"x-omitempty": false
|
||||||
},
|
},
|
||||||
|
"bind": {
|
||||||
|
"description": "for TCP and UDP routes, bind address to listen on",
|
||||||
|
"type": "string",
|
||||||
|
"x-nullable": true
|
||||||
|
},
|
||||||
"container": {
|
"container": {
|
||||||
"description": "Docker only",
|
"description": "Docker only",
|
||||||
"allOf": [
|
"allOf": [
|
||||||
|
|||||||
@@ -879,6 +879,10 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
alias:
|
alias:
|
||||||
type: string
|
type: string
|
||||||
|
bind:
|
||||||
|
description: for TCP and UDP routes, bind address to listen on
|
||||||
|
type: string
|
||||||
|
x-nullable: true
|
||||||
container:
|
container:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: '#/definitions/Container'
|
- $ref: '#/definitions/Container'
|
||||||
@@ -1495,6 +1499,10 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
alias:
|
alias:
|
||||||
type: string
|
type: string
|
||||||
|
bind:
|
||||||
|
description: for TCP and UDP routes, bind address to listen on
|
||||||
|
type: string
|
||||||
|
x-nullable: true
|
||||||
container:
|
container:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: '#/definitions/Container'
|
- $ref: '#/definitions/Container'
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
@@ -47,6 +48,9 @@ type (
|
|||||||
Host string `json:"host,omitempty"`
|
Host string `json:"host,omitempty"`
|
||||||
Port route.Port `json:"port"`
|
Port route.Port `json:"port"`
|
||||||
|
|
||||||
|
// for TCP and UDP routes, bind address to listen on
|
||||||
|
Bind string `json:"bind,omitempty" validate:"omitempty,ip_addr" extensions:"x-nullable"`
|
||||||
|
|
||||||
Root string `json:"root,omitempty"`
|
Root string `json:"root,omitempty"`
|
||||||
SPA bool `json:"spa,omitempty"` // Single-page app mode: serves index for non-existent paths
|
SPA bool `json:"spa,omitempty"` // Single-page app mode: serves index for non-existent paths
|
||||||
Index string `json:"index,omitempty"` // Index file to serve for single-page app mode
|
Index string `json:"index,omitempty"` // Index file to serve for single-page app mode
|
||||||
@@ -278,7 +282,28 @@ func (r *Route) validate() gperr.Error {
|
|||||||
r.ProxyURL = gperr.Collect(&errs, nettypes.ParseURL, fmt.Sprintf("%s://%s:%d", r.Scheme, r.Host, r.Port.Proxy))
|
r.ProxyURL = gperr.Collect(&errs, nettypes.ParseURL, fmt.Sprintf("%s://%s:%d", r.Scheme, r.Host, r.Port.Proxy))
|
||||||
case route.SchemeTCP, route.SchemeUDP:
|
case route.SchemeTCP, route.SchemeUDP:
|
||||||
if !r.ShouldExclude() {
|
if !r.ShouldExclude() {
|
||||||
r.LisURL = gperr.Collect(&errs, nettypes.ParseURL, fmt.Sprintf("%s://:%d", r.Scheme, r.Port.Listening))
|
if r.Bind == "" {
|
||||||
|
r.Bind = "0.0.0.0"
|
||||||
|
}
|
||||||
|
bindIP := net.ParseIP(r.Bind)
|
||||||
|
if bindIP == nil {
|
||||||
|
return gperr.Errorf("invalid bind address %s", r.Bind)
|
||||||
|
}
|
||||||
|
var scheme string
|
||||||
|
if bindIP.To4() == nil { // IPv6
|
||||||
|
if r.Scheme == route.SchemeTCP {
|
||||||
|
scheme = "tcp6"
|
||||||
|
} else {
|
||||||
|
scheme = "udp6"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if r.Scheme == route.SchemeTCP {
|
||||||
|
scheme = "tcp4"
|
||||||
|
} else {
|
||||||
|
scheme = "udp4"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.LisURL = gperr.Collect(&errs, nettypes.ParseURL, fmt.Sprintf("%s://%s:%d", scheme, r.Bind, r.Port.Listening))
|
||||||
}
|
}
|
||||||
r.ProxyURL = gperr.Collect(&errs, nettypes.ParseURL, fmt.Sprintf("%s://%s:%d", r.Scheme, r.Host, r.Port.Proxy))
|
r.ProxyURL = gperr.Collect(&errs, nettypes.ParseURL, fmt.Sprintf("%s://%s:%d", r.Scheme, r.Host, r.Port.Proxy))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
@@ -30,7 +31,7 @@ func NewStreamRoute(base *Route) (types.Route, gperr.Error) {
|
|||||||
return &StreamRoute{
|
return &StreamRoute{
|
||||||
Route: base,
|
Route: base,
|
||||||
l: log.With().
|
l: log.With().
|
||||||
Str("type", string(base.Scheme)).
|
Str("type", base.LisURL.Scheme).
|
||||||
Str("name", base.Name()).
|
Str("name", base.Name()).
|
||||||
Logger(),
|
Logger(),
|
||||||
}, nil
|
}, nil
|
||||||
@@ -99,7 +100,9 @@ func (r *StreamRoute) LocalAddr() net.Addr {
|
|||||||
|
|
||||||
func (r *StreamRoute) initStream() (nettypes.Stream, error) {
|
func (r *StreamRoute) initStream() (nettypes.Stream, error) {
|
||||||
lurl, rurl := r.LisURL, r.ProxyURL
|
lurl, rurl := r.LisURL, r.ProxyURL
|
||||||
if lurl != nil && lurl.Scheme != rurl.Scheme {
|
// lurl scheme is either tcp4/tcp6 -> tcp, udp4/udp6 -> udp
|
||||||
|
// rurl scheme does not have the trailing 4/6
|
||||||
|
if strings.TrimRight(lurl.Scheme, "46") != rurl.Scheme {
|
||||||
return nil, fmt.Errorf("incoherent scheme is not yet supported: %s != %s", lurl.Scheme, rurl.Scheme)
|
return nil, fmt.Errorf("incoherent scheme is not yet supported: %s != %s", lurl.Scheme, rurl.Scheme)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,9 +113,9 @@ func (r *StreamRoute) initStream() (nettypes.Stream, error) {
|
|||||||
|
|
||||||
switch rurl.Scheme {
|
switch rurl.Scheme {
|
||||||
case "tcp":
|
case "tcp":
|
||||||
return stream.NewTCPTCPStream(laddr, rurl.Host)
|
return stream.NewTCPTCPStream(r.LisURL.Scheme, laddr, rurl.Host)
|
||||||
case "udp":
|
case "udp":
|
||||||
return stream.NewUDPUDPStream(laddr, rurl.Host)
|
return stream.NewUDPUDPStream(r.LisURL.Scheme, laddr, rurl.Host)
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("unknown scheme: %s", rurl.Scheme)
|
return nil, fmt.Errorf("unknown scheme: %s", rurl.Scheme)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type TCPTCPStream struct {
|
type TCPTCPStream struct {
|
||||||
|
network string
|
||||||
listener net.Listener
|
listener net.Listener
|
||||||
laddr *net.TCPAddr
|
laddr *net.TCPAddr
|
||||||
dst *net.TCPAddr
|
dst *net.TCPAddr
|
||||||
@@ -24,21 +25,21 @@ type TCPTCPStream struct {
|
|||||||
closed atomic.Bool
|
closed atomic.Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTCPTCPStream(listenAddr, dstAddr string) (nettypes.Stream, error) {
|
func NewTCPTCPStream(network, listenAddr, dstAddr string) (nettypes.Stream, error) {
|
||||||
dst, err := net.ResolveTCPAddr("tcp", dstAddr)
|
dst, err := net.ResolveTCPAddr(network, dstAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
laddr, err := net.ResolveTCPAddr("tcp", listenAddr)
|
laddr, err := net.ResolveTCPAddr(network, listenAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &TCPTCPStream{laddr: laddr, dst: dst}, nil
|
return &TCPTCPStream{network: network, laddr: laddr, dst: dst}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TCPTCPStream) ListenAndServe(ctx context.Context, preDial, onRead nettypes.HookFunc) {
|
func (s *TCPTCPStream) ListenAndServe(ctx context.Context, preDial, onRead nettypes.HookFunc) {
|
||||||
var err error
|
var err error
|
||||||
s.listener, err = net.ListenTCP("tcp", s.laddr)
|
s.listener, err = net.ListenTCP(s.network, s.laddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logErr(s, err, "failed to listen")
|
logErr(s, err, "failed to listen")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type UDPUDPStream struct {
|
type UDPUDPStream struct {
|
||||||
name string
|
network string
|
||||||
listener net.PacketConn
|
listener net.PacketConn
|
||||||
|
|
||||||
laddr *net.UDPAddr
|
laddr *net.UDPAddr
|
||||||
@@ -51,16 +51,17 @@ const (
|
|||||||
|
|
||||||
var bufPool = synk.GetSizedBytesPool()
|
var bufPool = synk.GetSizedBytesPool()
|
||||||
|
|
||||||
func NewUDPUDPStream(listenAddr, dstAddr string) (nettypes.Stream, error) {
|
func NewUDPUDPStream(network, listenAddr, dstAddr string) (nettypes.Stream, error) {
|
||||||
dst, err := net.ResolveUDPAddr("udp", dstAddr)
|
dst, err := net.ResolveUDPAddr(network, dstAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
laddr, err := net.ResolveUDPAddr("udp", listenAddr)
|
laddr, err := net.ResolveUDPAddr(network, listenAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &UDPUDPStream{
|
return &UDPUDPStream{
|
||||||
|
network: network,
|
||||||
laddr: laddr,
|
laddr: laddr,
|
||||||
dst: dst,
|
dst: dst,
|
||||||
conns: make(map[string]*udpUDPConn),
|
conns: make(map[string]*udpUDPConn),
|
||||||
@@ -69,7 +70,7 @@ func NewUDPUDPStream(listenAddr, dstAddr string) (nettypes.Stream, error) {
|
|||||||
|
|
||||||
func (s *UDPUDPStream) ListenAndServe(ctx context.Context, preDial, onRead nettypes.HookFunc) {
|
func (s *UDPUDPStream) ListenAndServe(ctx context.Context, preDial, onRead nettypes.HookFunc) {
|
||||||
var err error
|
var err error
|
||||||
s.listener, err = net.ListenUDP("udp", s.laddr)
|
s.listener, err = net.ListenUDP(s.network, s.laddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logErr(s, err, "failed to listen")
|
logErr(s, err, "failed to listen")
|
||||||
return
|
return
|
||||||
@@ -114,9 +115,6 @@ func (s *UDPUDPStream) LocalAddr() net.Addr {
|
|||||||
|
|
||||||
func (s *UDPUDPStream) MarshalZerologObject(e *zerolog.Event) {
|
func (s *UDPUDPStream) MarshalZerologObject(e *zerolog.Event) {
|
||||||
e.Str("protocol", "udp-udp")
|
e.Str("protocol", "udp-udp")
|
||||||
if s.name != "" {
|
|
||||||
e.Str("name", s.name)
|
|
||||||
}
|
|
||||||
if s.dst != nil {
|
if s.dst != nil {
|
||||||
e.Str("dst", s.dst.String())
|
e.Str("dst", s.dst.String())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user