fix: align channel copy and gateway connection state
This commit is contained in:
@@ -1,24 +1,18 @@
|
||||
package control
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"cmpp-platform/gateway/internal/inbound"
|
||||
"cmpp-platform/gateway/internal/queue"
|
||||
"cmpp-platform/gateway/internal/upstream"
|
||||
|
||||
cmpp "github.com/bigwhite/gocmpp"
|
||||
)
|
||||
|
||||
const defaultConnectTimeout = 5 * time.Second
|
||||
|
||||
type DialFunc func(context.Context, ConnectChannelCommand) error
|
||||
type ConnectFunc func(context.Context, ConnectChannelCommand) (ConnectionStateCallback, error)
|
||||
|
||||
type ConnectChannelCommand struct {
|
||||
SchemaVersion string `json:"schemaVersion"`
|
||||
@@ -59,7 +53,7 @@ type ConnectionStateCallback struct {
|
||||
type Server struct {
|
||||
APIBaseURL string
|
||||
HTTPClient *http.Client
|
||||
Dial DialFunc
|
||||
Connect ConnectFunc
|
||||
Upstream *upstream.Manager
|
||||
RecoveryCandidates func(context.Context) ([]inbound.DownstreamPresence, error)
|
||||
RecoveryStatuses func(context.Context) ([]inbound.DownstreamRecoveryStatus, error)
|
||||
@@ -74,12 +68,12 @@ func Register(mux *http.ServeMux, server Server) {
|
||||
if server.HTTPClient == nil {
|
||||
server.HTTPClient = &http.Client{Timeout: 10 * time.Second}
|
||||
}
|
||||
if server.Dial == nil {
|
||||
server.Dial = DialCMPP
|
||||
}
|
||||
if server.Upstream == nil {
|
||||
server.Upstream = &upstream.Manager{APIBaseURL: server.APIBaseURL, HTTPClient: server.HTTPClient}
|
||||
}
|
||||
if server.Connect == nil {
|
||||
server.Connect = server.connectChannel
|
||||
}
|
||||
mux.HandleFunc("/connections/connect", server.handleConnectChannel)
|
||||
mux.HandleFunc("/upstream/submit", server.handleUpstreamSubmit)
|
||||
mux.HandleFunc("/downstream/receipt", server.handleDownstreamReceipt)
|
||||
@@ -105,26 +99,9 @@ func (s Server) handleConnectChannel(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
status := ConnectionStateCallback{
|
||||
ChannelID: command.ChannelID,
|
||||
ConnectionID: command.ConnectionID,
|
||||
DesiredConnections: desiredConnections(command.DesiredConnections),
|
||||
}
|
||||
if err := s.Dial(r.Context(), command); err != nil {
|
||||
status.Status = "failed"
|
||||
status.CurrentConnections = 0
|
||||
status.LastDisconnectedAt = time.Now().UTC().Format(time.RFC3339Nano)
|
||||
status.LastError = err.Error()
|
||||
} else {
|
||||
now := time.Now().UTC().Format(time.RFC3339Nano)
|
||||
status.Status = "connected"
|
||||
status.CurrentConnections = status.DesiredConnections
|
||||
status.LastConnectedAt = now
|
||||
status.LastHeartbeatAt = now
|
||||
}
|
||||
|
||||
if err := s.postConnectionState(r.Context(), status); err != nil {
|
||||
http.Error(w, fmt.Sprintf("failed to callback api: %v", err), http.StatusBadGateway)
|
||||
status, err := s.Connect(r.Context(), command)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("failed to establish upstream pool: %v", err), http.StatusBadGateway)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -258,55 +235,43 @@ func (s Server) handleDownstreamRecoveryOverview(w http.ResponseWriter, r *http.
|
||||
})
|
||||
}
|
||||
|
||||
func DialCMPP(ctx context.Context, command ConnectChannelCommand) error {
|
||||
ctx, cancel := context.WithTimeout(ctx, defaultConnectTimeout)
|
||||
defer cancel()
|
||||
|
||||
version := cmpp.V30
|
||||
if strings.HasPrefix(command.Channel.CMPPVersion, "2") {
|
||||
version = cmpp.V20
|
||||
}
|
||||
|
||||
client := cmpp.NewClient(version)
|
||||
defer client.Disconnect()
|
||||
|
||||
done := make(chan error, 1)
|
||||
go func() {
|
||||
addr := fmt.Sprintf("%s:%d", command.Channel.GatewayHost, command.Channel.GatewayPort)
|
||||
done <- client.Connect(addr, command.Channel.Account, command.Channel.PasswordCipher, defaultConnectTimeout)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case err := <-done:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (s Server) postConnectionState(ctx context.Context, state ConnectionStateCallback) error {
|
||||
apiBaseURL := strings.TrimRight(s.APIBaseURL, "/")
|
||||
if apiBaseURL == "" {
|
||||
apiBaseURL = "http://127.0.0.1:3000/api"
|
||||
}
|
||||
payload, err := json.Marshal(state)
|
||||
func (s Server) connectChannel(ctx context.Context, command ConnectChannelCommand) (ConnectionStateCallback, error) {
|
||||
state, err := s.Upstream.ConnectChannel(ctx, queue.ConnectChannelCommand{
|
||||
SchemaVersion: command.SchemaVersion,
|
||||
MessageType: queue.MessageTypeConnectChannel,
|
||||
TraceID: command.TraceID,
|
||||
ChannelID: command.ChannelID,
|
||||
ConnectionID: command.ConnectionID,
|
||||
CreatedAt: time.Now().UTC(),
|
||||
Reason: command.Reason,
|
||||
DesiredConnections: command.DesiredConnections,
|
||||
Channel: queue.ConnectChannelConfig{
|
||||
Code: command.Channel.Code,
|
||||
Name: command.Channel.Name,
|
||||
GatewayHost: command.Channel.GatewayHost,
|
||||
GatewayPort: command.Channel.GatewayPort,
|
||||
Account: command.Channel.Account,
|
||||
PasswordCipher: command.Channel.PasswordCipher,
|
||||
SrcID: command.Channel.SrcID,
|
||||
CMPPVersion: command.Channel.CMPPVersion,
|
||||
RateLimitPerSecond: command.Channel.RateLimitPerSecond,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
return ConnectionStateCallback{}, err
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, apiBaseURL+"/admin/gateway/connections", bytes.NewReader(payload))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
resp, err := s.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
return fmt.Errorf("api returned %s", resp.Status)
|
||||
}
|
||||
return nil
|
||||
return ConnectionStateCallback{
|
||||
ChannelID: state.ChannelID,
|
||||
ConnectionID: state.ConnectionID,
|
||||
Status: state.Status,
|
||||
DesiredConnections: state.DesiredConnections,
|
||||
CurrentConnections: state.CurrentConnections,
|
||||
LastConnectedAt: state.LastConnectedAt,
|
||||
LastDisconnectedAt: state.LastDisconnectedAt,
|
||||
LastHeartbeatAt: state.LastHeartbeatAt,
|
||||
ReconnectCount: state.ReconnectCount,
|
||||
LastError: state.LastError,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func validateConnectChannelCommand(command ConnectChannelCommand) error {
|
||||
|
||||
Reference in New Issue
Block a user