Skip to content

Commit c9b0988

Browse files
authored
Connect code to unidirectional disconnect transforms (#914)
1 parent 599af5f commit c9b0988

File tree

5 files changed

+100
-25
lines changed

5 files changed

+100
-25
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ require (
77
github.com/FZambia/statik v0.1.2-0.20180217151304-b9f012bb2a1b
88
github.com/FZambia/tarantool v0.3.1
99
github.com/FZambia/viper-lite v0.0.0-20220110144934-1899f66c7d0e
10-
github.com/centrifugal/centrifuge v0.33.5-0.20241026095149-106d43f21426
10+
github.com/centrifugal/centrifuge v0.33.5-0.20241103135221-783166d2ec2b
1111
github.com/centrifugal/protocol v0.13.4
1212
github.com/cristalhq/jwt/v5 v5.4.0
1313
github.com/gobwas/glob v0.2.3

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
1010
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
1111
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
1212
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
13-
github.com/centrifugal/centrifuge v0.33.5-0.20241026095149-106d43f21426 h1:g5zZaCr/BybYgq8Nqrnrvqvb3jGGO/Dloil3cFGzzbg=
14-
github.com/centrifugal/centrifuge v0.33.5-0.20241026095149-106d43f21426/go.mod h1:Ck+7H3eVwoeyabKcj3L55oSunaORIOGPAIVB5xrQyGU=
13+
github.com/centrifugal/centrifuge v0.33.5-0.20241103135221-783166d2ec2b h1:Ayx2/Pn0m51Rw9NTXdc3PGLt+kF+2JIEnVr5Hu30lOs=
14+
github.com/centrifugal/centrifuge v0.33.5-0.20241103135221-783166d2ec2b/go.mod h1:yvzNn5hq/bFBpoXQwM8HbU481pAXQkyP2tzvJgFsiN8=
1515
github.com/centrifugal/protocol v0.13.4 h1:I0YxXtFNfn/ndDIZp5RkkqQcSSNH7DNPUbXKYtJXDzs=
1616
github.com/centrifugal/protocol v0.13.4/go.mod h1:7V5vI30VcoxJe4UD87xi7bOsvI0bmEhvbQuMjrFM2L4=
1717
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=

internal/proxy/proxy.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package proxy
33
import (
44
"context"
55
"encoding/json"
6+
"errors"
7+
"fmt"
68
"net"
79
"strings"
810

@@ -26,6 +28,29 @@ type HttpStatusToCodeTransform struct {
2628
ToDisconnect TransformDisconnect `mapstructure:"to_disconnect" json:"to_disconnect"`
2729
}
2830

31+
func (t *HttpStatusToCodeTransform) Validate() error {
32+
if t.StatusCode == 0 {
33+
return errors.New("no status code specified")
34+
}
35+
if t.ToDisconnect.Code == 0 && t.ToError.Code == 0 {
36+
return errors.New("no error or disconnect code set")
37+
}
38+
if t.ToDisconnect.Code > 0 && t.ToError.Code > 0 {
39+
return errors.New("only error or disconnect code can be set")
40+
}
41+
if !tools.IsASCII(t.ToDisconnect.Reason) {
42+
return errors.New("disconnect reason must be ASCII")
43+
}
44+
if !tools.IsASCII(t.ToError.Message) {
45+
return errors.New("error message must be ASCII")
46+
}
47+
const reasonOrMessageMaxLength = 123 // limit comes from WebSocket close reason length limit. See https://datatracker.ietf.org/doc/html/rfc6455.
48+
if len(t.ToDisconnect.Reason) > reasonOrMessageMaxLength {
49+
return fmt.Errorf("disconnect reason can be up to %d characters long", reasonOrMessageMaxLength)
50+
}
51+
return nil
52+
}
53+
2954
// Config for proxy.
3055
type Config struct {
3156
// Name is a unique name of proxy to reference.

internal/tools/code_translate.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,35 @@
11
package tools
22

33
import (
4+
"errors"
45
"net/http"
56

67
"github.com/centrifugal/centrifuge"
78
)
89

10+
type TransformDisconnect struct {
11+
Code uint32 `mapstructure:"code" json:"code"`
12+
Reason string `mapstructure:"reason" json:"reason"`
13+
}
14+
15+
type UniConnectCodeToDisconnectTransform struct {
16+
Code uint32 `mapstructure:"code" json:"code"`
17+
To TransformDisconnect `mapstructure:"to" json:"to"`
18+
}
19+
20+
func (t UniConnectCodeToDisconnectTransform) Validate() error {
21+
if t.Code == 0 {
22+
return errors.New("no code specified")
23+
}
24+
if t.To.Code == 0 {
25+
return errors.New("no disconnect code specified")
26+
}
27+
if !IsASCII(t.To.Reason) {
28+
return errors.New("disconnect reason must be ASCII")
29+
}
30+
return nil
31+
}
32+
933
type ConnectCodeToHTTPStatus struct {
1034
Enabled bool `mapstructure:"enabled" json:"enabled"`
1135
Transforms []ConnectCodeToHTTPStatusTransform `mapstructure:"transforms" json:"transforms"`
@@ -16,6 +40,16 @@ type ConnectCodeToHTTPStatusTransform struct {
1640
To TransformedConnectErrorHttpResponse `mapstructure:"to" json:"to"`
1741
}
1842

43+
func (t ConnectCodeToHTTPStatusTransform) Validate() error {
44+
if t.Code == 0 {
45+
return errors.New("no code specified")
46+
}
47+
if t.To.Status == 0 {
48+
return errors.New("no status_code specified")
49+
}
50+
return nil
51+
}
52+
1953
type TransformedConnectErrorHttpResponse struct {
2054
Status int `mapstructure:"status_code" json:"status_code"`
2155
Body string `mapstructure:"body" json:"body"`

main.go

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,9 @@ var defaults = map[string]any{
243243
"uni_sse": false,
244244
"uni_http_stream": false,
245245

246+
"client_connect_code_to_unidirectional_disconnect.enabled": false,
247+
"client_connect_code_to_unidirectional_disconnect.transforms": []any{},
248+
246249
"uni_sse_connect_code_to_http_response.enabled": false,
247250
"uni_sse_connect_code_to_http_response.transforms": []any{},
248251
"uni_http_stream_connect_code_to_http_response.enabled": false,
@@ -1911,6 +1914,11 @@ func granularProxiesFromConfig(v *viper.Viper) []proxy.Config {
19111914
if p.Endpoint == "" {
19121915
log.Fatal().Msgf("no endpoint set for proxy %s", p.Name)
19131916
}
1917+
for i, transform := range p.HttpStatusTransforms {
1918+
if err := transform.Validate(); err != nil {
1919+
log.Fatal().Msgf("error validating proxy_http_status_code_transforms[%d] in proxy %s: %v", i, p.Name, err)
1920+
}
1921+
}
19141922
names[p.Name] = struct{}{}
19151923
}
19161924

@@ -2075,28 +2083,9 @@ func proxyMapConfig() (*client.ProxyMap, bool) {
20752083
if v.IsSet("proxy_http_status_code_transforms") {
20762084
tools.DecodeSlice(v, &httpStatusTransforms, "proxy_http_status_code_transforms")
20772085
}
2078-
for _, transform := range httpStatusTransforms {
2079-
if transform.StatusCode == 0 {
2080-
log.Fatal().Msg("status should be set in proxy_http_status_code_transforms item")
2081-
}
2082-
if transform.ToDisconnect.Code == 0 && transform.ToError.Code == 0 {
2083-
log.Fatal().Msg("no error or disconnect code set in proxy_http_status_code_transforms item")
2084-
}
2085-
if transform.ToDisconnect.Code > 0 && transform.ToError.Code > 0 {
2086-
log.Fatal().Msg("only error or disconnect code can be set in proxy_http_status_code_transforms item, but not both")
2087-
}
2088-
if !tools.IsASCII(transform.ToDisconnect.Reason) {
2089-
log.Fatal().Msg("proxy_http_status_code_transforms item disconnect reason must be ASCII")
2090-
}
2091-
if !tools.IsASCII(transform.ToError.Message) {
2092-
log.Fatal().Msg("proxy_http_status_code_transforms item error message must be ASCII")
2093-
}
2094-
const reasonOrMessageMaxLength = 123 // limit comes from WebSocket close reason length limit. See https://datatracker.ietf.org/doc/html/rfc6455.
2095-
if len(transform.ToError.Message) > reasonOrMessageMaxLength {
2096-
log.Fatal().Msgf("proxy_http_status_code_transforms item error message can be up to %d characters long", reasonOrMessageMaxLength)
2097-
}
2098-
if len(transform.ToDisconnect.Reason) > reasonOrMessageMaxLength {
2099-
log.Fatal().Msgf("proxy_http_status_code_transforms item disconnect reason can be up to %d characters long", reasonOrMessageMaxLength)
2086+
for i, transform := range httpStatusTransforms {
2087+
if err := transform.Validate(); err != nil {
2088+
log.Fatal().Msgf("error validating proxy_http_status_code_transforms[%d]: %v", i, err)
21002089
}
21012090
}
21022091
proxyConfig.HttpStatusTransforms = httpStatusTransforms
@@ -2431,6 +2420,23 @@ func nodeConfig(version string) centrifuge.Config {
24312420
}
24322421
cfg.LogLevel = level
24332422
cfg.LogHandler = newLogHandler().handle
2423+
2424+
uniCodeTransformsEnabled := viper.GetBool("client_connect_code_to_unidirectional_disconnect.enabled")
2425+
if uniCodeTransformsEnabled {
2426+
var uniCodeToDisconnectTransforms []tools.UniConnectCodeToDisconnectTransform
2427+
if viper.IsSet("client_connect_code_to_unidirectional_disconnect.transforms") {
2428+
tools.DecodeSlice(viper.GetViper(), &uniCodeToDisconnectTransforms, "client_connect_code_to_unidirectional_disconnect.transforms")
2429+
}
2430+
uniCodeTransforms := make(map[uint32]centrifuge.Disconnect)
2431+
for _, transform := range uniCodeToDisconnectTransforms {
2432+
if err := transform.Validate(); err != nil {
2433+
log.Fatal().Msgf("error validating unidirectional code to disconnect transform: %v", err)
2434+
}
2435+
uniCodeTransforms[transform.Code] = centrifuge.Disconnect{Code: transform.To.Code, Reason: transform.To.Reason}
2436+
}
2437+
cfg.UnidirectionalCodeToDisconnect = uniCodeTransforms
2438+
}
2439+
24342440
return cfg
24352441
}
24362442

@@ -2570,6 +2576,11 @@ func uniSSEHandlerConfig() unisse.Config {
25702576
if viper.IsSet("uni_sse_connect_code_to_http_response.transforms") {
25712577
tools.DecodeSlice(viper.GetViper(), &connectCodeToHTTPStatusTransforms, "uni_sse_connect_code_to_http_response.transforms")
25722578
}
2579+
for i, transform := range connectCodeToHTTPStatusTransforms {
2580+
if err := transform.Validate(); err != nil {
2581+
log.Fatal().Msgf("error validating uni_sse_connect_code_to_http_response.transforms[%d]: %v", i, err)
2582+
}
2583+
}
25732584
return unisse.Config{
25742585
MaxRequestBodySize: viper.GetInt("uni_sse_max_request_body_size"),
25752586
PingPongConfig: getPingPongConfig(),
@@ -2586,6 +2597,11 @@ func uniStreamHandlerConfig() unihttpstream.Config {
25862597
if viper.IsSet("uni_http_stream_connect_code_to_http_response.transforms") {
25872598
tools.DecodeSlice(viper.GetViper(), &connectCodeToHTTPStatusTransforms, "uni_http_stream_connect_code_to_http_response.transforms")
25882599
}
2600+
for i, transform := range connectCodeToHTTPStatusTransforms {
2601+
if err := transform.Validate(); err != nil {
2602+
log.Fatal().Msgf("error validating uni_http_stream_connect_code_to_http_response.transforms[%d]: %v", i, err)
2603+
}
2604+
}
25892605
return unihttpstream.Config{
25902606
MaxRequestBodySize: viper.GetInt("uni_http_stream_max_request_body_size"),
25912607
PingPongConfig: getPingPongConfig(),

0 commit comments

Comments
 (0)