66 "io"
77 "net"
88 "strings"
9+ "sync"
910 "time"
1011
1112 "github.com/markdingo/netstring"
@@ -27,29 +28,49 @@ func (r *SocketmapResponse) String() string {
2728 return fmt .Sprintf ("%s %s" , r .Status , r .Data )
2829}
2930
30- // SocketmapAdapter handles socketmap protocol requests
31- type SocketmapAdapter struct {
31+ // LookupServer handles Postfix socketmap protocol requests for lookups.
32+ // It implements the ConnectionHandler interface.
33+ type LookupServer struct {
3234 client UserliService
3335}
3436
35- // NewSocketmapAdapter creates a new SocketmapAdapter with the given UserliService
36- func NewSocketmapAdapter (client UserliService ) * SocketmapAdapter {
37- return & SocketmapAdapter {client : client }
37+ // NewLookupServer creates a new LookupServer with the given UserliService
38+ func NewLookupServer (client UserliService ) * LookupServer {
39+ return & LookupServer {client : client }
3840}
3941
40- // HandleConnection processes a single socketmap connection
41- // Supports persistent connections with multiple requests
42- func (s * SocketmapAdapter ) HandleConnection (conn net.Conn ) {
43- defer func () {
44- if err := conn .Close (); err != nil {
45- logger .Error ("Error closing connection" , zap .Error (err ))
46- }
47- }()
42+ // StartLookupServer starts the lookup server on the given address
43+ func StartLookupServer (ctx context.Context , wg * sync.WaitGroup , addr string , server * LookupServer ) {
44+ config := TCPServerConfig {
45+ Name : "socketmap" ,
46+ Addr : addr ,
47+ OnConnectionAcquired : func () {
48+ activeConnections .Inc ()
49+ },
50+ OnConnectionReleased : func () {
51+ activeConnections .Dec ()
52+ },
53+ OnPoolUsageChanged : func (size int ) {
54+ connectionPoolUsage .Set (float64 (size ))
55+ },
56+ }
57+
58+ StartTCPServer (ctx , wg , config , server )
59+ }
4860
61+ // HandleConnection implements ConnectionHandler interface for LookupServer.
62+ // It processes socketmap protocol requests, supporting persistent connections with multiple requests.
63+ // Note: The caller (tcpserver.go) is responsible for closing the connection.
64+ func (s * LookupServer ) HandleConnection (ctx context.Context , conn net.Conn ) {
4965 decoder := netstring .NewDecoder (conn )
5066 encoder := netstring .NewEncoder (conn )
5167
5268 for {
69+ // Check if context is cancelled
70+ if ctx .Err () != nil {
71+ return
72+ }
73+
5374 // Set read deadline for each request
5475 _ = conn .SetReadDeadline (time .Now ().Add (ReadTimeout ))
5576
@@ -80,34 +101,38 @@ func (s *SocketmapAdapter) HandleConnection(conn net.Conn) {
80101 mapName := parts [0 ]
81102 key := parts [1 ]
82103
83- logger .Debug ("Processing socketmap request" , zap .String ("map" , mapName ), zap .String ("key" , key ))
84-
85- // Create context with timeout for this request
86- ctx , cancel := context .WithTimeout (context .Background (), 5 * time .Second )
87-
88- // Route to appropriate handler based on map name
89- var response * SocketmapResponse
90- switch mapName {
91- case "alias" :
92- response = s .handleAlias (ctx , key )
93- case "domain" :
94- response = s .handleDomain (ctx , key )
95- case "mailbox" :
96- response = s .handleMailbox (ctx , key )
97- case "senders" :
98- response = s .handleSenders (ctx , key )
99- default :
100- logger .Error ("Unknown map name" , zap .String ("map" , mapName ))
101- response = & SocketmapResponse {Status : "PERM" , Data : "Unknown map name" }
102- }
104+ logger .Debug ("Processing socketmap request" ,
105+ zap .String ("map" , mapName ),
106+ zap .String ("key" , key ))
103107
104- cancel () // Always cancel context when done
108+ response := s . processRequest ( ctx , mapName , key )
105109 s .writeResponse (encoder , conn , response , now , mapName )
106110 }
107111}
108112
113+ // processRequest routes a socketmap request to the appropriate handler with a timeout context.
114+ // Using a separate method ensures defer cancel() runs after each request, preventing context leaks.
115+ func (s * LookupServer ) processRequest (ctx context.Context , mapName , key string ) * SocketmapResponse {
116+ reqCtx , cancel := context .WithTimeout (ctx , 5 * time .Second )
117+ defer cancel ()
118+
119+ switch mapName {
120+ case "alias" :
121+ return s .handleAlias (reqCtx , key )
122+ case "domain" :
123+ return s .handleDomain (reqCtx , key )
124+ case "mailbox" :
125+ return s .handleMailbox (reqCtx , key )
126+ case "senders" :
127+ return s .handleSenders (reqCtx , key )
128+ default :
129+ logger .Error ("Unknown map name" , zap .String ("map" , mapName ))
130+ return & SocketmapResponse {Status : "PERM" , Data : "Unknown map name" }
131+ }
132+ }
133+
109134// handleAlias processes alias lookup requests
110- func (s * SocketmapAdapter ) handleAlias (ctx context.Context , key string ) * SocketmapResponse {
135+ func (s * LookupServer ) handleAlias (ctx context.Context , key string ) * SocketmapResponse {
111136 aliases , err := s .client .GetAliases (ctx , key )
112137 if err != nil {
113138 logger .Error ("Error fetching aliases" , zap .String ("key" , key ), zap .Error (err ))
@@ -122,7 +147,7 @@ func (s *SocketmapAdapter) handleAlias(ctx context.Context, key string) *Socketm
122147}
123148
124149// handleDomain processes domain lookup requests
125- func (s * SocketmapAdapter ) handleDomain (ctx context.Context , key string ) * SocketmapResponse {
150+ func (s * LookupServer ) handleDomain (ctx context.Context , key string ) * SocketmapResponse {
126151 exists , err := s .client .GetDomain (ctx , key )
127152 if err != nil {
128153 logger .Error ("Error fetching domain" , zap .String ("key" , key ), zap .Error (err ))
@@ -137,7 +162,7 @@ func (s *SocketmapAdapter) handleDomain(ctx context.Context, key string) *Socket
137162}
138163
139164// handleMailbox processes mailbox lookup requests
140- func (s * SocketmapAdapter ) handleMailbox (ctx context.Context , key string ) * SocketmapResponse {
165+ func (s * LookupServer ) handleMailbox (ctx context.Context , key string ) * SocketmapResponse {
141166 exists , err := s .client .GetMailbox (ctx , key )
142167 if err != nil {
143168 logger .Error ("Error fetching mailbox" , zap .String ("key" , key ), zap .Error (err ))
@@ -152,7 +177,7 @@ func (s *SocketmapAdapter) handleMailbox(ctx context.Context, key string) *Socke
152177}
153178
154179// handleSenders processes senders lookup requests
155- func (s * SocketmapAdapter ) handleSenders (ctx context.Context , key string ) * SocketmapResponse {
180+ func (s * LookupServer ) handleSenders (ctx context.Context , key string ) * SocketmapResponse {
156181 senders , err := s .client .GetSenders (ctx , key )
157182 if err != nil {
158183 logger .Error ("Error fetching senders" , zap .String ("key" , key ), zap .Error (err ))
@@ -167,7 +192,7 @@ func (s *SocketmapAdapter) handleSenders(ctx context.Context, key string) *Socke
167192}
168193
169194// writeResponse sends a socketmap response back to the client
170- func (s * SocketmapAdapter ) writeResponse (encoder * netstring.Encoder , conn net.Conn , response * SocketmapResponse , startTime time.Time , mapName string ) {
195+ func (s * LookupServer ) writeResponse (encoder * netstring.Encoder , conn net.Conn , response * SocketmapResponse , startTime time.Time , mapName string ) {
171196 var status string
172197 switch response .Status {
173198 case "OK" :
0 commit comments