This commit is contained in:
Zoltán Papp
2024-07-29 21:53:07 +02:00
parent 12f472c58c
commit 5be33d668b
19 changed files with 204 additions and 23 deletions
+8 -1
View File
@@ -18,6 +18,7 @@ const (
bufferSize = 8820
)
// Peer represents a peer connection
type Peer struct {
log *log.Entry
idS string
@@ -27,6 +28,7 @@ type Peer struct {
store *Store
}
// NewPeer creates a new Peer instance and prepare custom logging
func NewPeer(id []byte, conn net.Conn, store *Store) *Peer {
stringID := messages.HashIDToString(id)
return &Peer{
@@ -38,6 +40,9 @@ func NewPeer(id []byte, conn net.Conn, store *Store) *Peer {
}
}
// Work reads data from the connection
// It manages the protocol (healthcheck, transport, close). Read the message and determine the message type and handle
// the message accordingly.
func (p *Peer) Work() {
ctx, cancel := context.WithCancel(context.Background())
hc := healthcheck.NewSender(ctx)
@@ -75,13 +80,14 @@ func (p *Peer) Work() {
}
// Write writes data to the connection
// it has been called by the remote peer
func (p *Peer) Write(b []byte) (int, error) {
p.connMu.RLock()
defer p.connMu.RUnlock()
return p.conn.Write(b)
}
// CloseGracefully closes the connection with the peer gracefully. Send a close message to the client and close the
// connection.
func (p *Peer) CloseGracefully(ctx context.Context) {
p.connMu.Lock()
_, err := p.writeWithTimeout(ctx, messages.MarshalCloseMsg())
@@ -97,6 +103,7 @@ func (p *Peer) CloseGracefully(ctx context.Context) {
defer p.connMu.Unlock()
}
// String returns the peer ID
func (p *Peer) String() string {
return p.idS
}
+31 -6
View File
@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"net"
"net/url"
"sync"
log "github.com/sirupsen/logrus"
@@ -14,6 +15,7 @@ import (
"github.com/netbirdio/netbird/relay/metrics"
)
// Relay represents the relay server
type Relay struct {
metrics *metrics.Metrics
validator auth.Validator
@@ -25,6 +27,21 @@ type Relay struct {
closeMu sync.RWMutex
}
// NewRelay creates a new Relay instance
//
// Parameters:
// meter: An instance of metric.Meter from the go.opentelemetry.io/otel/metric package. It is used to create and manage
// metrics for the relay server.
// exposedAddress: A string representing the address that the relay server is exposed on. The client will use this
// address as the relay server's instance URL.
// tlsSupport: A boolean indicating whether the relay server supports TLS (Transport Layer Security) or not. The
// instance URL depends on this value.
// validator: An instance of auth.Validator from the auth package. It is used to validate the authentication of the
// peers.
//
// Returns:
// A pointer to a Relay instance and an error. If the Relay instance is successfully created, the error is nil.
// Otherwise, the error contains the details of what went wrong.
func NewRelay(meter metric.Meter, exposedAddress string, tlsSupport bool, validator auth.Validator) (*Relay, error) {
m, err := metrics.NewMetrics(meter)
if err != nil {
@@ -42,10 +59,15 @@ func NewRelay(meter metric.Meter, exposedAddress string, tlsSupport bool, valida
} else {
r.instanceURL = fmt.Sprintf("rel://%s", exposedAddress)
}
_, err = url.ParseRequestURI(r.instanceURL)
if err != nil {
return nil, fmt.Errorf("invalid exposed address: %v", err)
}
return r, nil
}
// Accept start to handle a new peer connection
func (r *Relay) Accept(conn net.Conn) {
r.closeMu.RLock()
defer r.closeMu.RUnlock()
@@ -53,7 +75,7 @@ func (r *Relay) Accept(conn net.Conn) {
return
}
peerID, err := r.handShake(conn)
peerID, err := r.handshake(conn)
if err != nil {
log.Errorf("failed to handshake with %s: %s", conn.RemoteAddr(), err)
cErr := conn.Close()
@@ -75,6 +97,8 @@ func (r *Relay) Accept(conn net.Conn) {
}()
}
// Close closes the relay server
// It closes the connection with all peers in gracefully and stops accepting new connections.
func (r *Relay) Close(ctx context.Context) {
log.Infof("close connection with all peers")
r.closeMu.Lock()
@@ -91,7 +115,12 @@ func (r *Relay) Close(ctx context.Context) {
r.closeMu.Unlock()
}
func (r *Relay) handShake(conn net.Conn) ([]byte, error) {
// InstanceURL returns the instance URL of the relay server
func (r *Relay) InstanceURL() string {
return r.instanceURL
}
func (r *Relay) handshake(conn net.Conn) ([]byte, error) {
buf := make([]byte, messages.MaxHandshakeSize)
n, err := conn.Read(buf)
if err != nil {
@@ -127,7 +156,3 @@ func (r *Relay) handShake(conn net.Conn) ([]byte, error) {
}
return peerID, nil
}
func (r *Relay) InstanceURL() string {
return r.instanceURL
}
+15
View File
@@ -13,16 +13,27 @@ import (
"github.com/netbirdio/netbird/relay/server/listener/ws"
)
// ListenerConfig is the configuration for the listener.
// Address: the address to bind the listener to. It could be an address behind a reverse proxy.
// TLSConfig: the TLS configuration for the listener.
type ListenerConfig struct {
Address string
TLSConfig *tls.Config
}
// Server is the main entry point for the relay server.
// It is the gate between the WebSocket listener and the Relay server logic.
// In a new HTTP connection, the server will accept the connection and pass it to the Relay server via the Accept method.
type Server struct {
relay *Relay
wSListener listener.Listener
}
// NewServer creates a new relay server instance.
// meter: the OpenTelemetry meter
// exposedAddress: this address will be used as the instance URL. It should be a domain:port format.
// tlsSupport: if true, the server will support TLS
// authValidator: the auth validator to use for the server
func NewServer(meter metric.Meter, exposedAddress string, tlsSupport bool, authValidator auth.Validator) (*Server, error) {
relay, err := NewRelay(meter, exposedAddress, tlsSupport, authValidator)
if err != nil {
@@ -33,6 +44,7 @@ func NewServer(meter metric.Meter, exposedAddress string, tlsSupport bool, authV
}, nil
}
// Listen starts the relay server.
func (r *Server) Listen(cfg ListenerConfig) error {
r.wSListener = &ws.Listener{
Address: cfg.Address,
@@ -47,6 +59,8 @@ func (r *Server) Listen(cfg ListenerConfig) error {
return wslErr
}
// Close stops the relay server. If there are active connections, they will be closed gracefully. In case of a timeout,
// the connections will be forcefully closed.
func (r *Server) Close() (err error) {
// stop service new connections
if r.wSListener != nil {
@@ -60,6 +74,7 @@ func (r *Server) Close() (err error) {
return
}
// InstanceURL returns the instance URL of the relay server.
func (r *Server) InstanceURL() string {
return r.relay.instanceURL
}
+8
View File
@@ -4,29 +4,36 @@ import (
"sync"
)
// Store is a thread-safe store of peers
// It is used to store the peers that are connected to the relay server
type Store struct {
peers map[string]*Peer // consider to use [32]byte as key. The Peer(id string) would be faster
peersLock sync.RWMutex
}
// NewStore creates a new Store instance
func NewStore() *Store {
return &Store{
peers: make(map[string]*Peer),
}
}
// AddPeer adds a peer to the store
// It distinguishes the peers by their ID
func (s *Store) AddPeer(peer *Peer) {
s.peersLock.Lock()
defer s.peersLock.Unlock()
s.peers[peer.String()] = peer
}
// DeletePeer deletes a peer from the store
func (s *Store) DeletePeer(peer *Peer) {
s.peersLock.Lock()
defer s.peersLock.Unlock()
delete(s.peers, peer.String())
}
// Peer returns a peer by its ID
func (s *Store) Peer(id string) (*Peer, bool) {
s.peersLock.RLock()
defer s.peersLock.RUnlock()
@@ -35,6 +42,7 @@ func (s *Store) Peer(id string) (*Peer, bool) {
return p, ok
}
// Peers returns all the peers in the store
func (s *Store) Peers() []*Peer {
s.peersLock.RLock()
defer s.peersLock.RUnlock()