mirror of
https://github.com/fatedier/frp.git
synced 2025-06-15 07:08:22 +00:00
Compare commits
6 Commits
accb71f318
...
6d93f13e1a
Author | SHA1 | Date | |
---|---|---|---|
|
6d93f13e1a | ||
|
1e8db66743 | ||
|
e0dd947e6a | ||
|
f62cd91f09 | ||
|
b499412aee | ||
|
cda2cb151e |
@ -1,3 +1,7 @@
|
|||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Support metadatas and annotations in frpc proxy commands.
|
* Support metadatas and annotations in frpc proxy commands.
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Properly release resources in service.Close() to prevent resource leaks when used as a library.
|
||||||
|
2
go.mod
2
go.mod
@ -5,7 +5,7 @@ go 1.22.0
|
|||||||
require (
|
require (
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
|
||||||
github.com/coreos/go-oidc/v3 v3.10.0
|
github.com/coreos/go-oidc/v3 v3.10.0
|
||||||
github.com/fatedier/golib v0.5.0
|
github.com/fatedier/golib v0.5.1
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/gorilla/mux v1.8.1
|
github.com/gorilla/mux v1.8.1
|
||||||
github.com/gorilla/websocket v1.5.0
|
github.com/gorilla/websocket v1.5.0
|
||||||
|
4
go.sum
4
go.sum
@ -21,8 +21,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/fatedier/golib v0.5.0 h1:hNcH7hgfIFqVWbP+YojCCAj4eO94pPf4dEF8lmq2jWs=
|
github.com/fatedier/golib v0.5.1 h1:hcKAnaw5mdI/1KWRGejxR+i1Hn/NvbY5UsMKDr7o13M=
|
||||||
github.com/fatedier/golib v0.5.0/go.mod h1:W6kIYkIFxHsTzbgqg5piCxIiDo4LzwgTY6R5W8l9NFQ=
|
github.com/fatedier/golib v0.5.1/go.mod h1:W6kIYkIFxHsTzbgqg5piCxIiDo4LzwgTY6R5W8l9NFQ=
|
||||||
github.com/fatedier/yamux v0.0.0-20230628132301-7aca4898904d h1:ynk1ra0RUqDWQfvFi5KtMiSobkVQ3cNc0ODb8CfIETo=
|
github.com/fatedier/yamux v0.0.0-20230628132301-7aca4898904d h1:ynk1ra0RUqDWQfvFi5KtMiSobkVQ3cNc0ODb8CfIETo=
|
||||||
github.com/fatedier/yamux v0.0.0-20230628132301-7aca4898904d/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
github.com/fatedier/yamux v0.0.0-20230628132301-7aca4898904d/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||||
github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U=
|
github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U=
|
||||||
|
@ -51,7 +51,7 @@ func NewAuthVerifier(cfg v1.AuthServerConfig) (authVerifier Verifier) {
|
|||||||
authVerifier = NewTokenAuth(cfg.AdditionalScopes, cfg.Token)
|
authVerifier = NewTokenAuth(cfg.AdditionalScopes, cfg.Token)
|
||||||
case v1.AuthMethodOIDC:
|
case v1.AuthMethodOIDC:
|
||||||
tokenVerifier := NewTokenVerifier(cfg.OIDC)
|
tokenVerifier := NewTokenVerifier(cfg.OIDC)
|
||||||
authVerifier = NewOidcAuthVerifier(cfg.AdditionalScopes, tokenVerifier)
|
authVerifier = NewOidcAuthVerifier(cfg.AdditionalScopes, tokenVerifier, cfg.OIDC.AllowedClaims)
|
||||||
}
|
}
|
||||||
return authVerifier
|
return authVerifier
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,12 @@ package auth
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"slices"
|
"slices"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/coreos/go-oidc/v3/oidc"
|
"github.com/coreos/go-oidc/v3/oidc"
|
||||||
"golang.org/x/oauth2/clientcredentials"
|
"golang.org/x/oauth2/clientcredentials"
|
||||||
@ -30,6 +34,10 @@ type OidcAuthProvider struct {
|
|||||||
additionalAuthScopes []v1.AuthScope
|
additionalAuthScopes []v1.AuthScope
|
||||||
|
|
||||||
tokenGenerator *clientcredentials.Config
|
tokenGenerator *clientcredentials.Config
|
||||||
|
|
||||||
|
// rawToken is used to specify a raw JWT token for authentication.
|
||||||
|
// If rawToken is not empty, it will be used directly instead of generating a new token.
|
||||||
|
rawToken string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewOidcAuthSetter(additionalAuthScopes []v1.AuthScope, cfg v1.AuthOIDCClientConfig) *OidcAuthProvider {
|
func NewOidcAuthSetter(additionalAuthScopes []v1.AuthScope, cfg v1.AuthOIDCClientConfig) *OidcAuthProvider {
|
||||||
@ -53,10 +61,17 @@ func NewOidcAuthSetter(additionalAuthScopes []v1.AuthScope, cfg v1.AuthOIDCClien
|
|||||||
return &OidcAuthProvider{
|
return &OidcAuthProvider{
|
||||||
additionalAuthScopes: additionalAuthScopes,
|
additionalAuthScopes: additionalAuthScopes,
|
||||||
tokenGenerator: tokenGenerator,
|
tokenGenerator: tokenGenerator,
|
||||||
|
rawToken: cfg.RawToken,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *OidcAuthProvider) generateAccessToken() (accessToken string, err error) {
|
func (auth *OidcAuthProvider) generateAccessToken() (accessToken string, err error) {
|
||||||
|
// If a raw token is provided, use it directly.
|
||||||
|
if auth.rawToken != "" {
|
||||||
|
return auth.rawToken, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, generate a new token using the client credentials flow.
|
||||||
tokenObj, err := auth.tokenGenerator.Token(context.Background())
|
tokenObj, err := auth.tokenGenerator.Token(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("couldn't generate OIDC token for login: %v", err)
|
return "", fmt.Errorf("couldn't generate OIDC token for login: %v", err)
|
||||||
@ -96,6 +111,9 @@ type OidcAuthConsumer struct {
|
|||||||
|
|
||||||
verifier TokenVerifier
|
verifier TokenVerifier
|
||||||
subjectsFromLogin []string
|
subjectsFromLogin []string
|
||||||
|
|
||||||
|
// allowedClaims specifies a map of allowed claims for the OIDC token.
|
||||||
|
allowedClaims map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTokenVerifier(cfg v1.AuthOIDCServerConfig) TokenVerifier {
|
func NewTokenVerifier(cfg v1.AuthOIDCServerConfig) TokenVerifier {
|
||||||
@ -112,15 +130,60 @@ func NewTokenVerifier(cfg v1.AuthOIDCServerConfig) TokenVerifier {
|
|||||||
return provider.Verifier(&verifierConf)
|
return provider.Verifier(&verifierConf)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewOidcAuthVerifier(additionalAuthScopes []v1.AuthScope, verifier TokenVerifier) *OidcAuthConsumer {
|
func NewOidcAuthVerifier(additionalAuthScopes []v1.AuthScope, verifier TokenVerifier, allowedClaims map[string]string) *OidcAuthConsumer {
|
||||||
return &OidcAuthConsumer{
|
return &OidcAuthConsumer{
|
||||||
additionalAuthScopes: additionalAuthScopes,
|
additionalAuthScopes: additionalAuthScopes,
|
||||||
verifier: verifier,
|
verifier: verifier,
|
||||||
subjectsFromLogin: []string{},
|
subjectsFromLogin: []string{},
|
||||||
|
allowedClaims: allowedClaims,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *OidcAuthConsumer) VerifyLogin(loginMsg *msg.Login) (err error) {
|
func (auth *OidcAuthConsumer) VerifyLogin(loginMsg *msg.Login) (err error) {
|
||||||
|
// Verify allowed claims if configured.
|
||||||
|
if len(auth.allowedClaims) > 0 {
|
||||||
|
// Decode token without verifying signature.
|
||||||
|
parts := strings.Split(loginMsg.PrivilegeKey, ".")
|
||||||
|
if len(parts) != 3 {
|
||||||
|
return fmt.Errorf("invalid OIDC token format")
|
||||||
|
}
|
||||||
|
|
||||||
|
payload, err := base64.RawURLEncoding.DecodeString(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid OIDC token: failed to decode payload: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var claims map[string]any
|
||||||
|
if err := json.Unmarshal(payload, &claims); err != nil {
|
||||||
|
return fmt.Errorf("invalid OIDC token: failed to unmarshal payload: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over allowed claims and attempt to verify.
|
||||||
|
for claimName, expectedValue := range auth.allowedClaims {
|
||||||
|
claimValue, ok := claims[claimName]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("OIDC token missing required claim: %s", claimName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strClaimValue, ok := claimValue.(string); ok {
|
||||||
|
if strClaimValue != expectedValue {
|
||||||
|
return fmt.Errorf("OIDC token claim '%s' value [%s] does not match expected value [%s]", claimName, strClaimValue, expectedValue)
|
||||||
|
}
|
||||||
|
} else if intClaimValue, ok := claimValue.(int); ok {
|
||||||
|
expectedIntValue, err := strconv.Atoi(expectedValue)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("OIDC token claim '%s' is number, expected value [%s] not parseable", claimName, expectedValue)
|
||||||
|
}
|
||||||
|
if intClaimValue != expectedIntValue {
|
||||||
|
return fmt.Errorf("OIDC token claim '%s' value [%d] does not match expected value [%d]", claimName, intClaimValue, expectedIntValue)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("claim %s is of unsupported type", claimName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If claim verification passes, proceed with standard verification.
|
||||||
token, err := auth.verifier.Verify(context.Background(), loginMsg.PrivilegeKey)
|
token, err := auth.verifier.Verify(context.Background(), loginMsg.PrivilegeKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid OIDC token in login: %v", err)
|
return fmt.Errorf("invalid OIDC token in login: %v", err)
|
||||||
|
@ -23,7 +23,7 @@ func (m *mockTokenVerifier) Verify(ctx context.Context, subject string) (*oidc.I
|
|||||||
|
|
||||||
func TestPingWithEmptySubjectFromLoginFails(t *testing.T) {
|
func TestPingWithEmptySubjectFromLoginFails(t *testing.T) {
|
||||||
r := require.New(t)
|
r := require.New(t)
|
||||||
consumer := auth.NewOidcAuthVerifier([]v1.AuthScope{v1.AuthScopeHeartBeats}, &mockTokenVerifier{})
|
consumer := auth.NewOidcAuthVerifier([]v1.AuthScope{v1.AuthScopeHeartBeats}, &mockTokenVerifier{}, map[string]string{})
|
||||||
err := consumer.VerifyPing(&msg.Ping{
|
err := consumer.VerifyPing(&msg.Ping{
|
||||||
PrivilegeKey: "ping-without-login",
|
PrivilegeKey: "ping-without-login",
|
||||||
Timestamp: time.Now().UnixMilli(),
|
Timestamp: time.Now().UnixMilli(),
|
||||||
@ -34,7 +34,7 @@ func TestPingWithEmptySubjectFromLoginFails(t *testing.T) {
|
|||||||
|
|
||||||
func TestPingAfterLoginWithNewSubjectSucceeds(t *testing.T) {
|
func TestPingAfterLoginWithNewSubjectSucceeds(t *testing.T) {
|
||||||
r := require.New(t)
|
r := require.New(t)
|
||||||
consumer := auth.NewOidcAuthVerifier([]v1.AuthScope{v1.AuthScopeHeartBeats}, &mockTokenVerifier{})
|
consumer := auth.NewOidcAuthVerifier([]v1.AuthScope{v1.AuthScopeHeartBeats}, &mockTokenVerifier{}, map[string]string{})
|
||||||
err := consumer.VerifyLogin(&msg.Login{
|
err := consumer.VerifyLogin(&msg.Login{
|
||||||
PrivilegeKey: "ping-after-login",
|
PrivilegeKey: "ping-after-login",
|
||||||
})
|
})
|
||||||
@ -49,7 +49,7 @@ func TestPingAfterLoginWithNewSubjectSucceeds(t *testing.T) {
|
|||||||
|
|
||||||
func TestPingAfterLoginWithDifferentSubjectFails(t *testing.T) {
|
func TestPingAfterLoginWithDifferentSubjectFails(t *testing.T) {
|
||||||
r := require.New(t)
|
r := require.New(t)
|
||||||
consumer := auth.NewOidcAuthVerifier([]v1.AuthScope{v1.AuthScopeHeartBeats}, &mockTokenVerifier{})
|
consumer := auth.NewOidcAuthVerifier([]v1.AuthScope{v1.AuthScopeHeartBeats}, &mockTokenVerifier{}, map[string]string{})
|
||||||
err := consumer.VerifyLogin(&msg.Login{
|
err := consumer.VerifyLogin(&msg.Login{
|
||||||
PrivilegeKey: "login-with-first-subject",
|
PrivilegeKey: "login-with-first-subject",
|
||||||
})
|
})
|
||||||
|
@ -203,4 +203,7 @@ type AuthOIDCClientConfig struct {
|
|||||||
// AdditionalEndpointParams specifies additional parameters to be sent
|
// AdditionalEndpointParams specifies additional parameters to be sent
|
||||||
// this field will be transfer to map[string][]string in OIDC token generator.
|
// this field will be transfer to map[string][]string in OIDC token generator.
|
||||||
AdditionalEndpointParams map[string]string `json:"additionalEndpointParams,omitempty"`
|
AdditionalEndpointParams map[string]string `json:"additionalEndpointParams,omitempty"`
|
||||||
|
// RawToken specifies a raw JWT token to use for authentication, bypassing
|
||||||
|
// the OIDC flow.
|
||||||
|
RawToken string `json:"rawToken,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -147,6 +147,15 @@ type AuthOIDCServerConfig struct {
|
|||||||
// SkipIssuerCheck specifies whether to skip checking if the OIDC token's
|
// SkipIssuerCheck specifies whether to skip checking if the OIDC token's
|
||||||
// issuer claim matches the issuer specified in OidcIssuer.
|
// issuer claim matches the issuer specified in OidcIssuer.
|
||||||
SkipIssuerCheck bool `json:"skipIssuerCheck,omitempty"`
|
SkipIssuerCheck bool `json:"skipIssuerCheck,omitempty"`
|
||||||
|
// AllowedClaims specifies a map of allowed claims for the OIDC token.
|
||||||
|
AllowedClaims map[string]string `json:"allowedClaims,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *AuthOIDCServerConfig) Complete() {
|
||||||
|
// Ensure AllowedClaims is at least an empty map and not nil
|
||||||
|
if c.AllowedClaims == nil {
|
||||||
|
c.AllowedClaims = map[string]string{}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServerTransportConfig struct {
|
type ServerTransportConfig struct {
|
||||||
|
@ -112,6 +112,10 @@ func (g *Gateway) Run() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *Gateway) Close() error {
|
||||||
|
return g.ln.Close()
|
||||||
|
}
|
||||||
|
|
||||||
func (g *Gateway) handleConn(conn net.Conn) {
|
func (g *Gateway) handleConn(conn net.Conn) {
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
|
@ -100,6 +100,10 @@ func (v *Muxer) SetRewriteHostFunc(f hostRewriteFunc) *Muxer {
|
|||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *Muxer) Close() error {
|
||||||
|
return v.listener.Close()
|
||||||
|
}
|
||||||
|
|
||||||
type ChooseEndpointFunc func() (string, error)
|
type ChooseEndpointFunc func() (string, error)
|
||||||
|
|
||||||
type CreateConnFunc func(remoteAddr string) (net.Conn, error)
|
type CreateConnFunc func(remoteAddr string) (net.Conn, error)
|
||||||
|
@ -59,3 +59,13 @@ type ResourceController struct {
|
|||||||
// All server manager plugin
|
// All server manager plugin
|
||||||
PluginManager *plugin.Manager
|
PluginManager *plugin.Manager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rc *ResourceController) Close() error {
|
||||||
|
if rc.VhostHTTPSMuxer != nil {
|
||||||
|
rc.VhostHTTPSMuxer.Close()
|
||||||
|
}
|
||||||
|
if rc.TCPMuxHTTPConnectMuxer != nil {
|
||||||
|
rc.TCPMuxHTTPConnectMuxer.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -77,7 +77,7 @@ type Service struct {
|
|||||||
muxer *mux.Mux
|
muxer *mux.Mux
|
||||||
|
|
||||||
// Accept connections from client
|
// Accept connections from client
|
||||||
muxListener net.Listener
|
listener net.Listener
|
||||||
|
|
||||||
// Accept connections using kcp
|
// Accept connections using kcp
|
||||||
kcpListener net.Listener
|
kcpListener net.Listener
|
||||||
@ -125,11 +125,6 @@ type Service struct {
|
|||||||
ctx context.Context
|
ctx context.Context
|
||||||
// call cancel to stop service
|
// call cancel to stop service
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
|
|
||||||
// Track listeners so they can be closed manually
|
|
||||||
vhostHTTPSListener net.Listener
|
|
||||||
tcpmuxHTTPConnectListener net.Listener
|
|
||||||
tcpListener net.Listener
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(cfg *v1.ServerConfig) (*Service, error) {
|
func NewService(cfg *v1.ServerConfig) (*Service, error) {
|
||||||
@ -185,8 +180,6 @@ func NewService(cfg *v1.ServerConfig) (*Service, error) {
|
|||||||
return nil, fmt.Errorf("create server listener error, %v", err)
|
return nil, fmt.Errorf("create server listener error, %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save listener so it can be closed in svr.Close()
|
|
||||||
svr.tcpmuxHTTPConnectListener = l
|
|
||||||
svr.rc.TCPMuxHTTPConnectMuxer, err = tcpmux.NewHTTPConnectTCPMuxer(l, cfg.TCPMuxPassthrough, vhostReadWriteTimeout)
|
svr.rc.TCPMuxHTTPConnectMuxer, err = tcpmux.NewHTTPConnectTCPMuxer(l, cfg.TCPMuxPassthrough, vhostReadWriteTimeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("create vhost tcpMuxer error, %v", err)
|
return nil, fmt.Errorf("create vhost tcpMuxer error, %v", err)
|
||||||
@ -233,16 +226,14 @@ func NewService(cfg *v1.ServerConfig) (*Service, error) {
|
|||||||
return nil, fmt.Errorf("create server listener error, %v", err)
|
return nil, fmt.Errorf("create server listener error, %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save listener so it can be closed in svr.Close()
|
|
||||||
svr.tcpListener = ln
|
|
||||||
|
|
||||||
svr.muxer = mux.NewMux(ln)
|
svr.muxer = mux.NewMux(ln)
|
||||||
svr.muxer.SetKeepAlive(time.Duration(cfg.Transport.TCPKeepAlive) * time.Second)
|
svr.muxer.SetKeepAlive(time.Duration(cfg.Transport.TCPKeepAlive) * time.Second)
|
||||||
go func() {
|
go func() {
|
||||||
_ = svr.muxer.Serve()
|
_ = svr.muxer.Serve()
|
||||||
}()
|
}()
|
||||||
ln = svr.muxer.DefaultListener()
|
ln = svr.muxer.DefaultListener()
|
||||||
svr.muxListener = ln
|
|
||||||
|
svr.listener = ln
|
||||||
log.Infof("frps tcp listen on %s", address)
|
log.Infof("frps tcp listen on %s", address)
|
||||||
|
|
||||||
// Listen for accepting connections from client using kcp protocol.
|
// Listen for accepting connections from client using kcp protocol.
|
||||||
@ -327,8 +318,7 @@ func NewService(cfg *v1.ServerConfig) (*Service, error) {
|
|||||||
}
|
}
|
||||||
log.Infof("https service listen on %s", address)
|
log.Infof("https service listen on %s", address)
|
||||||
}
|
}
|
||||||
// Save listener so it can be closed in svr.Close()
|
|
||||||
svr.vhostHTTPSListener = l
|
|
||||||
svr.rc.VhostHTTPSMuxer, err = vhost.NewHTTPSMuxer(l, vhostReadWriteTimeout)
|
svr.rc.VhostHTTPSMuxer, err = vhost.NewHTTPSMuxer(l, vhostReadWriteTimeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("create vhost httpsMuxer error, %v", err)
|
return nil, fmt.Errorf("create vhost httpsMuxer error, %v", err)
|
||||||
@ -384,11 +374,11 @@ func (svr *Service) Run(ctx context.Context) {
|
|||||||
go svr.sshTunnelGateway.Run()
|
go svr.sshTunnelGateway.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
svr.HandleListener(svr.muxListener, false)
|
svr.HandleListener(svr.listener, false)
|
||||||
|
|
||||||
<-svr.ctx.Done()
|
<-svr.ctx.Done()
|
||||||
// service context may not be canceled by svr.Close(), we should call it here to release resources
|
// service context may not be canceled by svr.Close(), we should call it here to release resources
|
||||||
if svr.muxListener != nil {
|
if svr.listener != nil {
|
||||||
svr.Close()
|
svr.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -396,40 +386,30 @@ func (svr *Service) Run(ctx context.Context) {
|
|||||||
func (svr *Service) Close() error {
|
func (svr *Service) Close() error {
|
||||||
if svr.kcpListener != nil {
|
if svr.kcpListener != nil {
|
||||||
svr.kcpListener.Close()
|
svr.kcpListener.Close()
|
||||||
svr.kcpListener = nil
|
|
||||||
}
|
}
|
||||||
if svr.quicListener != nil {
|
if svr.quicListener != nil {
|
||||||
svr.quicListener.Close()
|
svr.quicListener.Close()
|
||||||
svr.quicListener = nil
|
|
||||||
}
|
}
|
||||||
if svr.websocketListener != nil {
|
if svr.websocketListener != nil {
|
||||||
svr.websocketListener.Close()
|
svr.websocketListener.Close()
|
||||||
svr.websocketListener = nil
|
|
||||||
}
|
}
|
||||||
if svr.tlsListener != nil {
|
if svr.tlsListener != nil {
|
||||||
svr.tlsListener.Close()
|
svr.tlsListener.Close()
|
||||||
svr.tlsConfig = nil
|
|
||||||
}
|
}
|
||||||
if svr.muxListener != nil {
|
if svr.sshTunnelListener != nil {
|
||||||
svr.muxListener.Close()
|
svr.sshTunnelListener.Close()
|
||||||
svr.muxListener = nil
|
|
||||||
}
|
}
|
||||||
if svr.vhostHTTPSListener != nil {
|
if svr.listener != nil {
|
||||||
svr.vhostHTTPSListener.Close()
|
svr.listener.Close()
|
||||||
svr.vhostHTTPSListener = nil
|
|
||||||
}
|
|
||||||
if svr.tcpmuxHTTPConnectListener != nil {
|
|
||||||
svr.tcpmuxHTTPConnectListener.Close()
|
|
||||||
svr.tcpmuxHTTPConnectListener = nil
|
|
||||||
}
|
}
|
||||||
if svr.webServer != nil {
|
if svr.webServer != nil {
|
||||||
svr.webServer.Close()
|
svr.webServer.Close()
|
||||||
svr.webServer = nil
|
|
||||||
}
|
}
|
||||||
if svr.tcpListener != nil {
|
if svr.sshTunnelGateway != nil {
|
||||||
svr.tcpListener.Close()
|
svr.sshTunnelGateway.Close()
|
||||||
svr.tcpListener = nil
|
|
||||||
}
|
}
|
||||||
|
svr.rc.Close()
|
||||||
|
svr.muxer.Close()
|
||||||
svr.ctlManager.Close()
|
svr.ctlManager.Close()
|
||||||
if svr.cancel != nil {
|
if svr.cancel != nil {
|
||||||
svr.cancel()
|
svr.cancel()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user