feat(traffic): expose smart tcp status counters
- commit
- 6dc660313517f3066556ecb58a6ca9783385a8d5
- author
- brice · · 2026-06-30 13:28 UTC
- parent
- 3515791
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
4 files · +118 -33
M internal/adapters/dto/traffic.go +43 -8
| @@ -23,6 +23,7 @@ type TrafficEntryPointStatus struct { | |||
| 23 | 23 | TotalErrors int64 `json:"total_errors"` | |
| 24 | 24 | BytesIn int64 `json:"bytes_in"` | |
| 25 | 25 | BytesOut int64 `json:"bytes_out"` | |
| 26 | + | SmartTCP SmartTCPCounters `json:"smart_tcp"` | |
| 26 | 27 | } | |
| 27 | 28 | ||
| 28 | 29 | type TrafficRouterStatus struct { | |
| @@ -54,13 +55,29 @@ type TrafficBackendStatus struct { | |||
| 54 | 55 | } | |
| 55 | 56 | ||
| 56 | 57 | type TrafficCounters struct { | |
| 57 | - | ActiveTCPConnections int64 `json:"active_tcp_connections"` | |
| 58 | - | ActiveUDPSessions int64 `json:"active_udp_sessions"` | |
| 59 | - | TotalAccepted int64 `json:"total_accepted"` | |
| 60 | - | TotalRefused int64 `json:"total_refused"` | |
| 61 | - | TotalErrors int64 `json:"total_errors"` | |
| 62 | - | BytesIn int64 `json:"bytes_in"` | |
| 63 | - | BytesOut int64 `json:"bytes_out"` | |
| 58 | + | ActiveTCPConnections int64 `json:"active_tcp_connections"` | |
| 59 | + | ActiveUDPSessions int64 `json:"active_udp_sessions"` | |
| 60 | + | TotalAccepted int64 `json:"total_accepted"` | |
| 61 | + | TotalRefused int64 `json:"total_refused"` | |
| 62 | + | TotalErrors int64 `json:"total_errors"` | |
| 63 | + | BytesIn int64 `json:"bytes_in"` | |
| 64 | + | BytesOut int64 `json:"bytes_out"` | |
| 65 | + | SmartTCP SmartTCPCounters `json:"smart_tcp"` | |
| 66 | + | } | |
| 67 | + | ||
| 68 | + | type SmartTCPCounters struct { | |
| 69 | + | HTTPAccepted int64 `json:"http_accepted"` | |
| 70 | + | H2CAccepted int64 `json:"h2c_accepted"` | |
| 71 | + | HTTPSFallbackAccepted int64 `json:"https_fallback_accepted"` | |
| 72 | + | TLSPassthroughAccepted int64 `json:"tls_passthrough_accepted"` | |
| 73 | + | RawFallbackAccepted int64 `json:"raw_fallback_accepted"` | |
| 74 | + | EntrypointCIDRRefused int64 `json:"entrypoint_cidr_refused"` | |
| 75 | + | RawFallbackCIDRRefused int64 `json:"raw_fallback_cidr_refused"` | |
| 76 | + | PROXYRefused int64 `json:"proxy_refused"` | |
| 77 | + | UnknownNoFallbackRefused int64 `json:"unknown_no_fallback_refused"` | |
| 78 | + | MalformedRejected int64 `json:"malformed_rejected"` | |
| 79 | + | SniffTimeout int64 `json:"sniff_timeout"` | |
| 80 | + | ClientHelloTooLarge int64 `json:"client_hello_too_large"` | |
| 64 | 81 | } | |
| 65 | 82 | ||
| 66 | 83 | func TrafficStatusFromDomain(status domain.TrafficStatus) TrafficStatusResponse { | |
| @@ -81,7 +98,7 @@ func trafficEntryPointsFromDomain(values []domain.EntryPointStatus) []TrafficEnt | |||
| 81 | 98 | Name: value.Name, Address: value.Address, Protocol: value.Protocol, Active: value.Active, | |
| 82 | 99 | ActiveTCPConnections: value.ActiveTCPConnections, ActiveUDPSessions: value.ActiveUDPSessions, | |
| 83 | 100 | TotalAccepted: value.TotalAccepted, TotalRefused: value.TotalRefused, TotalErrors: value.TotalErrors, | |
| 84 | - | BytesIn: value.BytesIn, BytesOut: value.BytesOut, | |
| 101 | + | BytesIn: value.BytesIn, BytesOut: value.BytesOut, SmartTCP: smartTCPCountersFromDomain(value.SmartTCP), | |
| 85 | 102 | }) | |
| 86 | 103 | } | |
| 87 | 104 | return out | |
| @@ -125,5 +142,23 @@ func trafficCountersFromDomain(value domain.TrafficCounters) TrafficCounters { | |||
| 125 | 142 | TotalErrors: value.TotalErrors, | |
| 126 | 143 | BytesIn: value.BytesIn, | |
| 127 | 144 | BytesOut: value.BytesOut, | |
| 145 | + | SmartTCP: smartTCPCountersFromDomain(value.SmartTCP), | |
| 146 | + | } | |
| 147 | + | } | |
| 148 | + | ||
| 149 | + | func smartTCPCountersFromDomain(value domain.SmartTCPCounters) SmartTCPCounters { | |
| 150 | + | return SmartTCPCounters{ | |
| 151 | + | HTTPAccepted: value.HTTPAccepted, | |
| 152 | + | H2CAccepted: value.H2CAccepted, | |
| 153 | + | HTTPSFallbackAccepted: value.HTTPSFallbackAccepted, | |
| 154 | + | TLSPassthroughAccepted: value.TLSPassthroughAccepted, | |
| 155 | + | RawFallbackAccepted: value.RawFallbackAccepted, | |
| 156 | + | EntrypointCIDRRefused: value.EntrypointCIDRRefused, | |
| 157 | + | RawFallbackCIDRRefused: value.RawFallbackCIDRRefused, | |
| 158 | + | PROXYRefused: value.PROXYRefused, | |
| 159 | + | UnknownNoFallbackRefused: value.UnknownNoFallbackRefused, | |
| 160 | + | MalformedRejected: value.MalformedRejected, | |
| 161 | + | SniffTimeout: value.SniffTimeout, | |
| 162 | + | ClientHelloTooLarge: value.ClientHelloTooLarge, | |
| 128 | 163 | } | |
| 129 | 164 | } | |
| @@ -23,6 +23,7 @@ type TrafficEntryPointStatus struct { | |||||
| 23 | TotalErrors int64 `json:"total_errors"` | 23 | TotalErrors int64 `json:"total_errors"` | ||
| 24 | BytesIn int64 `json:"bytes_in"` | 24 | BytesIn int64 `json:"bytes_in"` | ||
| 25 | BytesOut int64 `json:"bytes_out"` | 25 | BytesOut int64 `json:"bytes_out"` | ||
| 26 | + | SmartTCP SmartTCPCounters `json:"smart_tcp"` | |||
| 26 | } | 27 | } | ||
| 27 | 28 | ||||
| 28 | type TrafficRouterStatus struct { | 29 | type TrafficRouterStatus struct { | ||
| @@ -54,13 +55,29 @@ type TrafficBackendStatus struct { | |||||
| 54 | } | 55 | } | ||
| 55 | 56 | ||||
| 56 | type TrafficCounters struct { | 57 | type TrafficCounters struct { | ||
| 57 | - | ActiveTCPConnections int64 `json:"active_tcp_connections"` | 58 | + | ActiveTCPConnections int64 `json:"active_tcp_connections"` |
| 58 | - | ActiveUDPSessions int64 `json:"active_udp_sessions"` | 59 | + | ActiveUDPSessions int64 `json:"active_udp_sessions"` |
| 59 | - | TotalAccepted int64 `json:"total_accepted"` | 60 | + | TotalAccepted int64 `json:"total_accepted"` |
| 60 | - | TotalRefused int64 `json:"total_refused"` | 61 | + | TotalRefused int64 `json:"total_refused"` |
| 61 | - | TotalErrors int64 `json:"total_errors"` | 62 | + | TotalErrors int64 `json:"total_errors"` |
| 62 | - | BytesIn int64 `json:"bytes_in"` | 63 | + | BytesIn int64 `json:"bytes_in"` |
| 63 | - | BytesOut int64 `json:"bytes_out"` | 64 | + | BytesOut int64 `json:"bytes_out"` |
| 65 | + | SmartTCP SmartTCPCounters `json:"smart_tcp"` | |||
| 66 | + | } | |||
| 67 | + | ||||
| 68 | + | type SmartTCPCounters struct { | |||
| 69 | + | HTTPAccepted int64 `json:"http_accepted"` | |||
| 70 | + | H2CAccepted int64 `json:"h2c_accepted"` | |||
| 71 | + | HTTPSFallbackAccepted int64 `json:"https_fallback_accepted"` | |||
| 72 | + | TLSPassthroughAccepted int64 `json:"tls_passthrough_accepted"` | |||
| 73 | + | RawFallbackAccepted int64 `json:"raw_fallback_accepted"` | |||
| 74 | + | EntrypointCIDRRefused int64 `json:"entrypoint_cidr_refused"` | |||
| 75 | + | RawFallbackCIDRRefused int64 `json:"raw_fallback_cidr_refused"` | |||
| 76 | + | PROXYRefused int64 `json:"proxy_refused"` | |||
| 77 | + | UnknownNoFallbackRefused int64 `json:"unknown_no_fallback_refused"` | |||
| 78 | + | MalformedRejected int64 `json:"malformed_rejected"` | |||
| 79 | + | SniffTimeout int64 `json:"sniff_timeout"` | |||
| 80 | + | ClientHelloTooLarge int64 `json:"client_hello_too_large"` | |||
| 64 | } | 81 | } | ||
| 65 | 82 | ||||
| 66 | func TrafficStatusFromDomain(status domain.TrafficStatus) TrafficStatusResponse { | 83 | func TrafficStatusFromDomain(status domain.TrafficStatus) TrafficStatusResponse { | ||
| @@ -81,7 +98,7 @@ func trafficEntryPointsFromDomain(values []domain.EntryPointStatus) []TrafficEnt | |||||
| 81 | Name: value.Name, Address: value.Address, Protocol: value.Protocol, Active: value.Active, | 98 | Name: value.Name, Address: value.Address, Protocol: value.Protocol, Active: value.Active, | ||
| 82 | ActiveTCPConnections: value.ActiveTCPConnections, ActiveUDPSessions: value.ActiveUDPSessions, | 99 | ActiveTCPConnections: value.ActiveTCPConnections, ActiveUDPSessions: value.ActiveUDPSessions, | ||
| 83 | TotalAccepted: value.TotalAccepted, TotalRefused: value.TotalRefused, TotalErrors: value.TotalErrors, | 100 | TotalAccepted: value.TotalAccepted, TotalRefused: value.TotalRefused, TotalErrors: value.TotalErrors, | ||
| 84 | - | BytesIn: value.BytesIn, BytesOut: value.BytesOut, | 101 | + | BytesIn: value.BytesIn, BytesOut: value.BytesOut, SmartTCP: smartTCPCountersFromDomain(value.SmartTCP), |
| 85 | }) | 102 | }) | ||
| 86 | } | 103 | } | ||
| 87 | return out | 104 | return out | ||
| @@ -125,5 +142,23 @@ func trafficCountersFromDomain(value domain.TrafficCounters) TrafficCounters { | |||||
| 125 | TotalErrors: value.TotalErrors, | 142 | TotalErrors: value.TotalErrors, | ||
| 126 | BytesIn: value.BytesIn, | 143 | BytesIn: value.BytesIn, | ||
| 127 | BytesOut: value.BytesOut, | 144 | BytesOut: value.BytesOut, | ||
| 145 | + | SmartTCP: smartTCPCountersFromDomain(value.SmartTCP), | |||
| 146 | + | } | |||
| 147 | + | } | |||
| 148 | + | ||||
| 149 | + | func smartTCPCountersFromDomain(value domain.SmartTCPCounters) SmartTCPCounters { | |||
| 150 | + | return SmartTCPCounters{ | |||
| 151 | + | HTTPAccepted: value.HTTPAccepted, | |||
| 152 | + | H2CAccepted: value.H2CAccepted, | |||
| 153 | + | HTTPSFallbackAccepted: value.HTTPSFallbackAccepted, | |||
| 154 | + | TLSPassthroughAccepted: value.TLSPassthroughAccepted, | |||
| 155 | + | RawFallbackAccepted: value.RawFallbackAccepted, | |||
| 156 | + | EntrypointCIDRRefused: value.EntrypointCIDRRefused, | |||
| 157 | + | RawFallbackCIDRRefused: value.RawFallbackCIDRRefused, | |||
| 158 | + | PROXYRefused: value.PROXYRefused, | |||
| 159 | + | UnknownNoFallbackRefused: value.UnknownNoFallbackRefused, | |||
| 160 | + | MalformedRejected: value.MalformedRejected, | |||
| 161 | + | SniffTimeout: value.SniffTimeout, | |||
| 162 | + | ClientHelloTooLarge: value.ClientHelloTooLarge, | |||
| 128 | } | 163 | } | ||
| 129 | } | 164 | } | ||
M internal/adapters/in/cli/traffic_status.go +62 -20
| @@ -46,48 +46,90 @@ func renderTrafficStatus(out io.Writer, status *dto.TrafficStatusResponse, jsonO | |||
| 46 | 46 | if jsonOut { | |
| 47 | 47 | return writeJSON(out, status) | |
| 48 | 48 | } | |
| 49 | + | if err := renderTrafficStatusHeader(out, status); err != nil { | |
| 50 | + | return err | |
| 51 | + | } | |
| 52 | + | if err := renderTrafficEntryPoints(out, status.EntryPoints); err != nil { | |
| 53 | + | return err | |
| 54 | + | } | |
| 55 | + | if err := renderTrafficRouters(out, status.Routers); err != nil { | |
| 56 | + | return err | |
| 57 | + | } | |
| 58 | + | if err := renderTrafficServices(out, status.Services); err != nil { | |
| 59 | + | return err | |
| 60 | + | } | |
| 61 | + | return renderTrafficTotals(out, status.Counters) | |
| 62 | + | } | |
| 63 | + | ||
| 64 | + | func renderTrafficStatusHeader(out io.Writer, status *dto.TrafficStatusResponse) error { | |
| 49 | 65 | if err := cliWriteLine(out, cliRenderTitle("Traffic Status")); err != nil { | |
| 50 | 66 | return err | |
| 51 | 67 | } | |
| 52 | 68 | if err := cliWriteLine(out, cliRenderMeta("Reload", status.LastReloadStatus)); err != nil { | |
| 53 | 69 | return err | |
| 54 | 70 | } | |
| 55 | - | if status.LastReloadError != "" { | |
| 56 | - | if err := cliWriteLine(out, cliRenderWarning("Last reload error: "+status.LastReloadError)); err != nil { | |
| 57 | - | return err | |
| 58 | - | } | |
| 71 | + | if status.LastReloadError == "" { | |
| 72 | + | return nil | |
| 59 | 73 | } | |
| 74 | + | return cliWriteLine(out, cliRenderWarning("Last reload error: "+status.LastReloadError)) | |
| 75 | + | } | |
| 76 | + | ||
| 77 | + | func renderTrafficEntryPoints(out io.Writer, entries []dto.TrafficEntryPointStatus) error { | |
| 60 | 78 | if err := cliWriteLine(out, ""); err != nil { | |
| 61 | 79 | return err | |
| 62 | 80 | } | |
| 63 | 81 | if err := cliWriteLine(out, "Entrypoints:"); err != nil { | |
| 64 | 82 | return err | |
| 65 | 83 | } | |
| 66 | - | if len(status.EntryPoints) == 0 { | |
| 67 | - | if err := cliWriteLine(out, cliRenderMuted(" none")); err != nil { | |
| 84 | + | if len(entries) == 0 { | |
| 85 | + | return cliWriteLine(out, cliRenderMuted(" none")) | |
| 86 | + | } | |
| 87 | + | for _, entry := range entries { | |
| 88 | + | if err := renderTrafficEntryPoint(out, entry); err != nil { | |
| 68 | 89 | return err | |
| 69 | 90 | } | |
| 70 | - | } else { | |
| 71 | - | for _, entry := range status.EntryPoints { | |
| 72 | - | if err := cliWritef(out, " %s %s %s active=%t tcp=%d udp=%d accepted=%d refused=%d errors=%d\n", | |
| 73 | - | entry.Name, entry.Protocol, entry.Address, entry.Active, entry.ActiveTCPConnections, entry.ActiveUDPSessions, | |
| 74 | - | entry.TotalAccepted, entry.TotalRefused, entry.TotalErrors); err != nil { | |
| 75 | - | return err | |
| 76 | - | } | |
| 77 | - | } | |
| 78 | 91 | } | |
| 79 | - | if err := renderTrafficRouters(out, status.Routers); err != nil { | |
| 92 | + | return nil | |
| 93 | + | } | |
| 94 | + | ||
| 95 | + | func renderTrafficEntryPoint(out io.Writer, entry dto.TrafficEntryPointStatus) error { | |
| 96 | + | if err := cliWritef(out, " %s %s %s active=%t tcp=%d udp=%d accepted=%d refused=%d errors=%d\n", | |
| 97 | + | entry.Name, entry.Protocol, entry.Address, entry.Active, entry.ActiveTCPConnections, entry.ActiveUDPSessions, | |
| 98 | + | entry.TotalAccepted, entry.TotalRefused, entry.TotalErrors); err != nil { | |
| 80 | 99 | return err | |
| 81 | 100 | } | |
| 82 | - | if err := renderTrafficServices(out, status.Services); err != nil { | |
| 83 | - | return err | |
| 101 | + | if hasSmartTCPCounters(entry.SmartTCP) { | |
| 102 | + | return renderSmartTCPCounters(out, " smart_tcp", entry.SmartTCP) | |
| 84 | 103 | } | |
| 104 | + | return nil | |
| 105 | + | } | |
| 106 | + | ||
| 107 | + | func renderTrafficTotals(out io.Writer, counters dto.TrafficCounters) error { | |
| 85 | 108 | if err := cliWriteLine(out, ""); err != nil { | |
| 86 | 109 | return err | |
| 87 | 110 | } | |
| 88 | - | return cliWritef(out, "Totals: tcp=%d udp=%d accepted=%d refused=%d errors=%d bytes_in=%d bytes_out=%d\n", | |
| 89 | - | status.Counters.ActiveTCPConnections, status.Counters.ActiveUDPSessions, status.Counters.TotalAccepted, | |
| 90 | - | status.Counters.TotalRefused, status.Counters.TotalErrors, status.Counters.BytesIn, status.Counters.BytesOut) | |
| 111 | + | if err := cliWritef(out, "Totals: tcp=%d udp=%d accepted=%d refused=%d errors=%d bytes_in=%d bytes_out=%d\n", | |
| 112 | + | counters.ActiveTCPConnections, counters.ActiveUDPSessions, counters.TotalAccepted, | |
| 113 | + | counters.TotalRefused, counters.TotalErrors, counters.BytesIn, counters.BytesOut); err != nil { | |
| 114 | + | return err | |
| 115 | + | } | |
| 116 | + | if hasSmartTCPCounters(counters.SmartTCP) { | |
| 117 | + | return renderSmartTCPCounters(out, "Smart TCP totals", counters.SmartTCP) | |
| 118 | + | } | |
| 119 | + | return nil | |
| 120 | + | } | |
| 121 | + | ||
| 122 | + | func hasSmartTCPCounters(c dto.SmartTCPCounters) bool { | |
| 123 | + | return c.HTTPAccepted != 0 || c.H2CAccepted != 0 || c.HTTPSFallbackAccepted != 0 || c.TLSPassthroughAccepted != 0 || | |
| 124 | + | c.RawFallbackAccepted != 0 || c.EntrypointCIDRRefused != 0 || c.RawFallbackCIDRRefused != 0 || c.PROXYRefused != 0 || | |
| 125 | + | c.UnknownNoFallbackRefused != 0 || c.MalformedRejected != 0 || c.SniffTimeout != 0 || c.ClientHelloTooLarge != 0 | |
| 126 | + | } | |
| 127 | + | ||
| 128 | + | func renderSmartTCPCounters(out io.Writer, label string, c dto.SmartTCPCounters) error { | |
| 129 | + | return cliWritef(out, "%s: http_accepted=%d h2c_accepted=%d https_fallback_accepted=%d tls_passthrough_accepted=%d raw_fallback_accepted=%d entrypoint_cidr_refused=%d raw_fallback_cidr_refused=%d proxy_refused=%d unknown_no_fallback_refused=%d malformed_rejected=%d sniff_timeout=%d client_hello_too_large=%d\n", | |
| 130 | + | label, c.HTTPAccepted, c.H2CAccepted, c.HTTPSFallbackAccepted, c.TLSPassthroughAccepted, c.RawFallbackAccepted, | |
| 131 | + | c.EntrypointCIDRRefused, c.RawFallbackCIDRRefused, c.PROXYRefused, c.UnknownNoFallbackRefused, c.MalformedRejected, | |
| 132 | + | c.SniffTimeout, c.ClientHelloTooLarge) | |
| 91 | 133 | } | |
| 92 | 134 | ||
| 93 | 135 | func renderTrafficRouters(out io.Writer, routers []dto.TrafficRouterStatus) error { | |
| @@ -46,48 +46,90 @@ func renderTrafficStatus(out io.Writer, status *dto.TrafficStatusResponse, jsonO | |||||
| 46 | if jsonOut { | 46 | if jsonOut { | ||
| 47 | return writeJSON(out, status) | 47 | return writeJSON(out, status) | ||
| 48 | } | 48 | } | ||
| 49 | + | if err := renderTrafficStatusHeader(out, status); err != nil { | |||
| 50 | + | return err | |||
| 51 | + | } | |||
| 52 | + | if err := renderTrafficEntryPoints(out, status.EntryPoints); err != nil { | |||
| 53 | + | return err | |||
| 54 | + | } | |||
| 55 | + | if err := renderTrafficRouters(out, status.Routers); err != nil { | |||
| 56 | + | return err | |||
| 57 | + | } | |||
| 58 | + | if err := renderTrafficServices(out, status.Services); err != nil { | |||
| 59 | + | return err | |||
| 60 | + | } | |||
| 61 | + | return renderTrafficTotals(out, status.Counters) | |||
| 62 | + | } | |||
| 63 | + | ||||
| 64 | + | func renderTrafficStatusHeader(out io.Writer, status *dto.TrafficStatusResponse) error { | |||
| 49 | if err := cliWriteLine(out, cliRenderTitle("Traffic Status")); err != nil { | 65 | if err := cliWriteLine(out, cliRenderTitle("Traffic Status")); err != nil { | ||
| 50 | return err | 66 | return err | ||
| 51 | } | 67 | } | ||
| 52 | if err := cliWriteLine(out, cliRenderMeta("Reload", status.LastReloadStatus)); err != nil { | 68 | if err := cliWriteLine(out, cliRenderMeta("Reload", status.LastReloadStatus)); err != nil { | ||
| 53 | return err | 69 | return err | ||
| 54 | } | 70 | } | ||
| 55 | - | if status.LastReloadError != "" { | 71 | + | if status.LastReloadError == "" { |
| 56 | - | if err := cliWriteLine(out, cliRenderWarning("Last reload error: "+status.LastReloadError)); err != nil { | 72 | + | return nil |
| 57 | - | return err | |||
| 58 | - | } | |||
| 59 | } | 73 | } | ||
| 74 | + | return cliWriteLine(out, cliRenderWarning("Last reload error: "+status.LastReloadError)) | |||
| 75 | + | } | |||
| 76 | + | ||||
| 77 | + | func renderTrafficEntryPoints(out io.Writer, entries []dto.TrafficEntryPointStatus) error { | |||
| 60 | if err := cliWriteLine(out, ""); err != nil { | 78 | if err := cliWriteLine(out, ""); err != nil { | ||
| 61 | return err | 79 | return err | ||
| 62 | } | 80 | } | ||
| 63 | if err := cliWriteLine(out, "Entrypoints:"); err != nil { | 81 | if err := cliWriteLine(out, "Entrypoints:"); err != nil { | ||
| 64 | return err | 82 | return err | ||
| 65 | } | 83 | } | ||
| 66 | - | if len(status.EntryPoints) == 0 { | 84 | + | if len(entries) == 0 { |
| 67 | - | if err := cliWriteLine(out, cliRenderMuted(" none")); err != nil { | 85 | + | return cliWriteLine(out, cliRenderMuted(" none")) |
| 86 | + | } | |||
| 87 | + | for _, entry := range entries { | |||
| 88 | + | if err := renderTrafficEntryPoint(out, entry); err != nil { | |||
| 68 | return err | 89 | return err | ||
| 69 | } | 90 | } | ||
| 70 | - | } else { | |||
| 71 | - | for _, entry := range status.EntryPoints { | |||
| 72 | - | if err := cliWritef(out, " %s %s %s active=%t tcp=%d udp=%d accepted=%d refused=%d errors=%d\n", | |||
| 73 | - | entry.Name, entry.Protocol, entry.Address, entry.Active, entry.ActiveTCPConnections, entry.ActiveUDPSessions, | |||
| 74 | - | entry.TotalAccepted, entry.TotalRefused, entry.TotalErrors); err != nil { | |||
| 75 | - | return err | |||
| 76 | - | } | |||
| 77 | - | } | |||
| 78 | } | 91 | } | ||
| 79 | - | if err := renderTrafficRouters(out, status.Routers); err != nil { | 92 | + | return nil |
| 93 | + | } | |||
| 94 | + | ||||
| 95 | + | func renderTrafficEntryPoint(out io.Writer, entry dto.TrafficEntryPointStatus) error { | |||
| 96 | + | if err := cliWritef(out, " %s %s %s active=%t tcp=%d udp=%d accepted=%d refused=%d errors=%d\n", | |||
| 97 | + | entry.Name, entry.Protocol, entry.Address, entry.Active, entry.ActiveTCPConnections, entry.ActiveUDPSessions, | |||
| 98 | + | entry.TotalAccepted, entry.TotalRefused, entry.TotalErrors); err != nil { | |||
| 80 | return err | 99 | return err | ||
| 81 | } | 100 | } | ||
| 82 | - | if err := renderTrafficServices(out, status.Services); err != nil { | 101 | + | if hasSmartTCPCounters(entry.SmartTCP) { |
| 83 | - | return err | 102 | + | return renderSmartTCPCounters(out, " smart_tcp", entry.SmartTCP) |
| 84 | } | 103 | } | ||
| 104 | + | return nil | |||
| 105 | + | } | |||
| 106 | + | ||||
| 107 | + | func renderTrafficTotals(out io.Writer, counters dto.TrafficCounters) error { | |||
| 85 | if err := cliWriteLine(out, ""); err != nil { | 108 | if err := cliWriteLine(out, ""); err != nil { | ||
| 86 | return err | 109 | return err | ||
| 87 | } | 110 | } | ||
| 88 | - | return cliWritef(out, "Totals: tcp=%d udp=%d accepted=%d refused=%d errors=%d bytes_in=%d bytes_out=%d\n", | 111 | + | if err := cliWritef(out, "Totals: tcp=%d udp=%d accepted=%d refused=%d errors=%d bytes_in=%d bytes_out=%d\n", |
| 89 | - | status.Counters.ActiveTCPConnections, status.Counters.ActiveUDPSessions, status.Counters.TotalAccepted, | 112 | + | counters.ActiveTCPConnections, counters.ActiveUDPSessions, counters.TotalAccepted, |
| 90 | - | status.Counters.TotalRefused, status.Counters.TotalErrors, status.Counters.BytesIn, status.Counters.BytesOut) | 113 | + | counters.TotalRefused, counters.TotalErrors, counters.BytesIn, counters.BytesOut); err != nil { |
| 114 | + | return err | |||
| 115 | + | } | |||
| 116 | + | if hasSmartTCPCounters(counters.SmartTCP) { | |||
| 117 | + | return renderSmartTCPCounters(out, "Smart TCP totals", counters.SmartTCP) | |||
| 118 | + | } | |||
| 119 | + | return nil | |||
| 120 | + | } | |||
| 121 | + | ||||
| 122 | + | func hasSmartTCPCounters(c dto.SmartTCPCounters) bool { | |||
| 123 | + | return c.HTTPAccepted != 0 || c.H2CAccepted != 0 || c.HTTPSFallbackAccepted != 0 || c.TLSPassthroughAccepted != 0 || | |||
| 124 | + | c.RawFallbackAccepted != 0 || c.EntrypointCIDRRefused != 0 || c.RawFallbackCIDRRefused != 0 || c.PROXYRefused != 0 || | |||
| 125 | + | c.UnknownNoFallbackRefused != 0 || c.MalformedRejected != 0 || c.SniffTimeout != 0 || c.ClientHelloTooLarge != 0 | |||
| 126 | + | } | |||
| 127 | + | ||||
| 128 | + | func renderSmartTCPCounters(out io.Writer, label string, c dto.SmartTCPCounters) error { | |||
| 129 | + | return cliWritef(out, "%s: http_accepted=%d h2c_accepted=%d https_fallback_accepted=%d tls_passthrough_accepted=%d raw_fallback_accepted=%d entrypoint_cidr_refused=%d raw_fallback_cidr_refused=%d proxy_refused=%d unknown_no_fallback_refused=%d malformed_rejected=%d sniff_timeout=%d client_hello_too_large=%d\n", | |||
| 130 | + | label, c.HTTPAccepted, c.H2CAccepted, c.HTTPSFallbackAccepted, c.TLSPassthroughAccepted, c.RawFallbackAccepted, | |||
| 131 | + | c.EntrypointCIDRRefused, c.RawFallbackCIDRRefused, c.PROXYRefused, c.UnknownNoFallbackRefused, c.MalformedRejected, | |||
| 132 | + | c.SniffTimeout, c.ClientHelloTooLarge) | |||
| 91 | } | 133 | } | ||
| 92 | 134 | ||||
| 93 | func renderTrafficRouters(out io.Writer, routers []dto.TrafficRouterStatus) error { | 135 | func renderTrafficRouters(out io.Writer, routers []dto.TrafficRouterStatus) error { | ||
M internal/adapters/in/cli/traffic_test.go +7 -2
| @@ -31,6 +31,7 @@ func TestTrafficStatusHumanOutput(t *testing.T) { | |||
| 31 | 31 | EntryPoints: []dto.TrafficEntryPointStatus{{ | |
| 32 | 32 | Name: "postgres", Address: "127.0.0.1:5432", Protocol: domain.EntryPointProtocolTCP, | |
| 33 | 33 | Active: true, ActiveTCPConnections: 1, TotalAccepted: 2, BytesIn: 10, BytesOut: 20, | |
| 34 | + | SmartTCP: dto.SmartTCPCounters{HTTPAccepted: 1, PROXYRefused: 1}, | |
| 34 | 35 | }}, | |
| 35 | 36 | Routers: []dto.TrafficRouterStatus{{ | |
| 36 | 37 | Name: "pg-router", EntryPoint: "postgres", Protocol: domain.RouterProtocolTCP, | |
| @@ -40,7 +41,7 @@ func TestTrafficStatusHumanOutput(t *testing.T) { | |||
| 40 | 41 | Name: "network_service:postgres:db", Active: true, | |
| 41 | 42 | Backends: []dto.TrafficBackendStatus{{Name: "postgres:db", Host: "127.0.0.1", Port: 5432, Protocol: domain.NetworkProtocolTCP, Active: true}}, | |
| 42 | 43 | }}, | |
| 43 | - | Counters: dto.TrafficCounters{ActiveTCPConnections: 1, TotalAccepted: 2, BytesIn: 10, BytesOut: 20}, | |
| 44 | + | Counters: dto.TrafficCounters{ActiveTCPConnections: 1, TotalAccepted: 2, BytesIn: 10, BytesOut: 20, SmartTCP: dto.SmartTCPCounters{HTTPAccepted: 1, PROXYRefused: 1}}, | |
| 44 | 45 | } | |
| 45 | 46 | var buf bytes.Buffer | |
| 46 | 47 | require.NoError(t, renderTrafficStatus(&buf, status, false)) | |
| @@ -52,14 +53,18 @@ func TestTrafficStatusHumanOutput(t *testing.T) { | |||
| 52 | 53 | assert.Contains(t, output, "network_service:postgres:db") | |
| 53 | 54 | assert.Contains(t, output, "tcp://127.0.0.1:5432") | |
| 54 | 55 | assert.Contains(t, output, "accepted=2") | |
| 56 | + | assert.Contains(t, output, "smart_tcp") | |
| 57 | + | assert.Contains(t, output, "http_accepted=1") | |
| 58 | + | assert.Contains(t, output, "proxy_refused=1") | |
| 55 | 59 | } | |
| 56 | 60 | ||
| 57 | 61 | func TestTrafficStatusJSONOutput(t *testing.T) { | |
| 58 | - | status := &dto.TrafficStatusResponse{LastReloadStatus: "ok", Counters: dto.TrafficCounters{ActiveUDPSessions: 3}} | |
| 62 | + | status := &dto.TrafficStatusResponse{LastReloadStatus: "ok", Counters: dto.TrafficCounters{ActiveUDPSessions: 3, SmartTCP: dto.SmartTCPCounters{SniffTimeout: 1}}} | |
| 59 | 63 | var buf bytes.Buffer | |
| 60 | 64 | require.NoError(t, renderTrafficStatus(&buf, status, true)) | |
| 61 | 65 | var got dto.TrafficStatusResponse | |
| 62 | 66 | require.NoError(t, json.Unmarshal(buf.Bytes(), &got)) | |
| 63 | 67 | assert.Equal(t, "ok", got.LastReloadStatus) | |
| 64 | 68 | assert.Equal(t, int64(3), got.Counters.ActiveUDPSessions) | |
| 69 | + | assert.Equal(t, int64(1), got.Counters.SmartTCP.SniffTimeout) | |
| 65 | 70 | } | |
| @@ -31,6 +31,7 @@ func TestTrafficStatusHumanOutput(t *testing.T) { | |||||
| 31 | EntryPoints: []dto.TrafficEntryPointStatus{{ | 31 | EntryPoints: []dto.TrafficEntryPointStatus{{ | ||
| 32 | Name: "postgres", Address: "127.0.0.1:5432", Protocol: domain.EntryPointProtocolTCP, | 32 | Name: "postgres", Address: "127.0.0.1:5432", Protocol: domain.EntryPointProtocolTCP, | ||
| 33 | Active: true, ActiveTCPConnections: 1, TotalAccepted: 2, BytesIn: 10, BytesOut: 20, | 33 | Active: true, ActiveTCPConnections: 1, TotalAccepted: 2, BytesIn: 10, BytesOut: 20, | ||
| 34 | + | SmartTCP: dto.SmartTCPCounters{HTTPAccepted: 1, PROXYRefused: 1}, | |||
| 34 | }}, | 35 | }}, | ||
| 35 | Routers: []dto.TrafficRouterStatus{{ | 36 | Routers: []dto.TrafficRouterStatus{{ | ||
| 36 | Name: "pg-router", EntryPoint: "postgres", Protocol: domain.RouterProtocolTCP, | 37 | Name: "pg-router", EntryPoint: "postgres", Protocol: domain.RouterProtocolTCP, | ||
| @@ -40,7 +41,7 @@ func TestTrafficStatusHumanOutput(t *testing.T) { | |||||
| 40 | Name: "network_service:postgres:db", Active: true, | 41 | Name: "network_service:postgres:db", Active: true, | ||
| 41 | Backends: []dto.TrafficBackendStatus{{Name: "postgres:db", Host: "127.0.0.1", Port: 5432, Protocol: domain.NetworkProtocolTCP, Active: true}}, | 42 | Backends: []dto.TrafficBackendStatus{{Name: "postgres:db", Host: "127.0.0.1", Port: 5432, Protocol: domain.NetworkProtocolTCP, Active: true}}, | ||
| 42 | }}, | 43 | }}, | ||
| 43 | - | Counters: dto.TrafficCounters{ActiveTCPConnections: 1, TotalAccepted: 2, BytesIn: 10, BytesOut: 20}, | 44 | + | Counters: dto.TrafficCounters{ActiveTCPConnections: 1, TotalAccepted: 2, BytesIn: 10, BytesOut: 20, SmartTCP: dto.SmartTCPCounters{HTTPAccepted: 1, PROXYRefused: 1}}, |
| 44 | } | 45 | } | ||
| 45 | var buf bytes.Buffer | 46 | var buf bytes.Buffer | ||
| 46 | require.NoError(t, renderTrafficStatus(&buf, status, false)) | 47 | require.NoError(t, renderTrafficStatus(&buf, status, false)) | ||
| @@ -52,14 +53,18 @@ func TestTrafficStatusHumanOutput(t *testing.T) { | |||||
| 52 | assert.Contains(t, output, "network_service:postgres:db") | 53 | assert.Contains(t, output, "network_service:postgres:db") | ||
| 53 | assert.Contains(t, output, "tcp://127.0.0.1:5432") | 54 | assert.Contains(t, output, "tcp://127.0.0.1:5432") | ||
| 54 | assert.Contains(t, output, "accepted=2") | 55 | assert.Contains(t, output, "accepted=2") | ||
| 56 | + | assert.Contains(t, output, "smart_tcp") | |||
| 57 | + | assert.Contains(t, output, "http_accepted=1") | |||
| 58 | + | assert.Contains(t, output, "proxy_refused=1") | |||
| 55 | } | 59 | } | ||
| 56 | 60 | ||||
| 57 | func TestTrafficStatusJSONOutput(t *testing.T) { | 61 | func TestTrafficStatusJSONOutput(t *testing.T) { | ||
| 58 | - | status := &dto.TrafficStatusResponse{LastReloadStatus: "ok", Counters: dto.TrafficCounters{ActiveUDPSessions: 3}} | 62 | + | status := &dto.TrafficStatusResponse{LastReloadStatus: "ok", Counters: dto.TrafficCounters{ActiveUDPSessions: 3, SmartTCP: dto.SmartTCPCounters{SniffTimeout: 1}}} |
| 59 | var buf bytes.Buffer | 63 | var buf bytes.Buffer | ||
| 60 | require.NoError(t, renderTrafficStatus(&buf, status, true)) | 64 | require.NoError(t, renderTrafficStatus(&buf, status, true)) | ||
| 61 | var got dto.TrafficStatusResponse | 65 | var got dto.TrafficStatusResponse | ||
| 62 | require.NoError(t, json.Unmarshal(buf.Bytes(), &got)) | 66 | require.NoError(t, json.Unmarshal(buf.Bytes(), &got)) | ||
| 63 | assert.Equal(t, "ok", got.LastReloadStatus) | 67 | assert.Equal(t, "ok", got.LastReloadStatus) | ||
| 64 | assert.Equal(t, int64(3), got.Counters.ActiveUDPSessions) | 68 | assert.Equal(t, int64(3), got.Counters.ActiveUDPSessions) | ||
| 69 | + | assert.Equal(t, int64(1), got.Counters.SmartTCP.SniffTimeout) | |||
| 65 | } | 70 | } | ||
M internal/adapters/in/http/admin/handler_traffic_test.go +6 -3
| @@ -18,10 +18,11 @@ func TestHandler_TrafficStatus(t *testing.T) { | |||
| 18 | 18 | trafficSvc.EXPECT().Status().Return(domain.TrafficStatus{ | |
| 19 | 19 | LastReloadStatus: "ok", | |
| 20 | 20 | EntryPoints: []domain.EntryPointStatus{{ | |
| 21 | - | Name: "postgres", Address: "127.0.0.1:5432", Protocol: domain.EntryPointProtocolTCP, | |
| 21 | + | Name: "edge", Address: "127.0.0.1:8443", Protocol: domain.EntryPointProtocolSmartTCP, | |
| 22 | 22 | Active: true, ActiveTCPConnections: 2, TotalAccepted: 3, BytesIn: 11, BytesOut: 22, | |
| 23 | + | SmartTCP: domain.SmartTCPCounters{HTTPAccepted: 1, PROXYRefused: 1}, | |
| 23 | 24 | }}, | |
| 24 | - | Counters: domain.TrafficCounters{ActiveTCPConnections: 2, TotalAccepted: 3, BytesIn: 11, BytesOut: 22}, | |
| 25 | + | Counters: domain.TrafficCounters{ActiveTCPConnections: 2, TotalAccepted: 3, BytesIn: 11, BytesOut: 22, SmartTCP: domain.SmartTCPCounters{HTTPAccepted: 1, PROXYRefused: 1}}, | |
| 25 | 26 | }) | |
| 26 | 27 | handler := newTestHandler(t, func(d *HandlerDeps) { | |
| 27 | 28 | d.TrafficSvc = trafficSvc | |
| @@ -37,9 +38,11 @@ func TestHandler_TrafficStatus(t *testing.T) { | |||
| 37 | 38 | require.NoError(t, json.NewDecoder(resp.Body).Decode(&body)) | |
| 38 | 39 | assert.Equal(t, "ok", body.LastReloadStatus) | |
| 39 | 40 | require.Len(t, body.EntryPoints, 1) | |
| 40 | - | assert.Equal(t, "postgres", body.EntryPoints[0].Name) | |
| 41 | + | assert.Equal(t, "edge", body.EntryPoints[0].Name) | |
| 41 | 42 | assert.Equal(t, int64(2), body.Counters.ActiveTCPConnections) | |
| 42 | 43 | assert.Equal(t, int64(22), body.Counters.BytesOut) | |
| 44 | + | assert.Equal(t, int64(1), body.EntryPoints[0].SmartTCP.HTTPAccepted) | |
| 45 | + | assert.Equal(t, int64(1), body.Counters.SmartTCP.PROXYRefused) | |
| 43 | 46 | } | |
| 44 | 47 | ||
| 45 | 48 | func TestHandler_TrafficStatusRequiresStatusRead(t *testing.T) { | |
| @@ -18,10 +18,11 @@ func TestHandler_TrafficStatus(t *testing.T) { | |||||
| 18 | trafficSvc.EXPECT().Status().Return(domain.TrafficStatus{ | 18 | trafficSvc.EXPECT().Status().Return(domain.TrafficStatus{ | ||
| 19 | LastReloadStatus: "ok", | 19 | LastReloadStatus: "ok", | ||
| 20 | EntryPoints: []domain.EntryPointStatus{{ | 20 | EntryPoints: []domain.EntryPointStatus{{ | ||
| 21 | - | Name: "postgres", Address: "127.0.0.1:5432", Protocol: domain.EntryPointProtocolTCP, | 21 | + | Name: "edge", Address: "127.0.0.1:8443", Protocol: domain.EntryPointProtocolSmartTCP, |
| 22 | Active: true, ActiveTCPConnections: 2, TotalAccepted: 3, BytesIn: 11, BytesOut: 22, | 22 | Active: true, ActiveTCPConnections: 2, TotalAccepted: 3, BytesIn: 11, BytesOut: 22, | ||
| 23 | + | SmartTCP: domain.SmartTCPCounters{HTTPAccepted: 1, PROXYRefused: 1}, | |||
| 23 | }}, | 24 | }}, | ||
| 24 | - | Counters: domain.TrafficCounters{ActiveTCPConnections: 2, TotalAccepted: 3, BytesIn: 11, BytesOut: 22}, | 25 | + | Counters: domain.TrafficCounters{ActiveTCPConnections: 2, TotalAccepted: 3, BytesIn: 11, BytesOut: 22, SmartTCP: domain.SmartTCPCounters{HTTPAccepted: 1, PROXYRefused: 1}}, |
| 25 | }) | 26 | }) | ||
| 26 | handler := newTestHandler(t, func(d *HandlerDeps) { | 27 | handler := newTestHandler(t, func(d *HandlerDeps) { | ||
| 27 | d.TrafficSvc = trafficSvc | 28 | d.TrafficSvc = trafficSvc | ||
| @@ -37,9 +38,11 @@ func TestHandler_TrafficStatus(t *testing.T) { | |||||
| 37 | require.NoError(t, json.NewDecoder(resp.Body).Decode(&body)) | 38 | require.NoError(t, json.NewDecoder(resp.Body).Decode(&body)) | ||
| 38 | assert.Equal(t, "ok", body.LastReloadStatus) | 39 | assert.Equal(t, "ok", body.LastReloadStatus) | ||
| 39 | require.Len(t, body.EntryPoints, 1) | 40 | require.Len(t, body.EntryPoints, 1) | ||
| 40 | - | assert.Equal(t, "postgres", body.EntryPoints[0].Name) | 41 | + | assert.Equal(t, "edge", body.EntryPoints[0].Name) |
| 41 | assert.Equal(t, int64(2), body.Counters.ActiveTCPConnections) | 42 | assert.Equal(t, int64(2), body.Counters.ActiveTCPConnections) | ||
| 42 | assert.Equal(t, int64(22), body.Counters.BytesOut) | 43 | assert.Equal(t, int64(22), body.Counters.BytesOut) | ||
| 44 | + | assert.Equal(t, int64(1), body.EntryPoints[0].SmartTCP.HTTPAccepted) | |||
| 45 | + | assert.Equal(t, int64(1), body.Counters.SmartTCP.PROXYRefused) | |||
| 43 | } | 46 | } | ||
| 44 | 47 | ||||
| 45 | func TestHandler_TrafficStatusRequiresStatusRead(t *testing.T) { | 48 | func TestHandler_TrafficStatusRequiresStatusRead(t *testing.T) { | ||