go.nanomsg.org/mangos/v3@v3.4.3-0.20240217232803-46464076f1f5/internal/core/socket.go (about) 1 // Copyright 2019 The Mangos Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use file except in compliance with the License. 5 // You may obtain a copy of the license at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package core 16 17 import ( 18 "strings" 19 "sync" 20 "time" 21 22 "go.nanomsg.org/mangos/v3" 23 "go.nanomsg.org/mangos/v3/transport" 24 ) 25 26 // Message is just a local alias for mangos.Message 27 type Message = mangos.Message 28 29 // defaultMaxRxSize is the default maximum Rx size 30 const defaultMaxRxSize = 1024 * 1024 31 32 const defaultReconnMinTime = time.Millisecond * 100 33 34 const defaultReconnMaxTime = time.Duration(0) 35 36 // socket is the meaty part of the core information. 37 type socket struct { 38 proto mangos.ProtocolBase 39 40 sync.Mutex 41 42 closed bool // true if Socket was closed at API level 43 reconnMinTime time.Duration // reconnect time after error or disconnect 44 reconnMaxTime time.Duration // max reconnect interval 45 maxRxSize int // max recv size 46 dialAsynch bool // asynchronous dialing? 47 48 listeners []*listener 49 dialers []*dialer 50 pipes pipeList 51 pipehook mangos.PipeEventHook 52 } 53 54 type context struct { 55 mangos.ProtocolContext 56 } 57 58 func (s *socket) addPipe(tp transport.Pipe, d *dialer, l *listener) { 59 p := newPipe(tp, s, d, l) 60 61 s.Lock() 62 ph := s.pipehook 63 s.Unlock() 64 65 // Add to the list of pipes for the socket; this also reserves an ID 66 // for it. 67 s.pipes.Add(p) 68 69 if ph != nil { 70 ph(mangos.PipeEventAttaching, p) 71 } 72 73 p.lock.Lock() 74 if p.closing { 75 p.lock.Lock() 76 return 77 } 78 if s.proto.AddPipe(p) != nil { 79 p.lock.Unlock() 80 s.pipes.Remove(p) 81 go p.close() 82 return 83 } 84 p.added = true 85 p.lock.Unlock() 86 87 if p.d != nil { 88 // This call resets the redial time in the dialer. Its 89 // kind of ugly that we have the socket doing this, but 90 // the scope is narrow, and it works. 91 go p.d.pipeConnected() 92 } 93 if ph != nil { 94 ph(mangos.PipeEventAttached, p) 95 } 96 } 97 98 func (s *socket) remPipe(p *pipe) { 99 100 s.proto.RemovePipe(p) 101 102 s.Lock() 103 ph := s.pipehook 104 s.Unlock() 105 s.pipes.Remove(p) 106 go func() { 107 if ph != nil { 108 ph(mangos.PipeEventDetached, p) 109 } 110 // Don't free the pipe ID until the callback is run, to 111 // ensure no use-after-free of the ID itself. 112 pipeIDs.Free(p.id) 113 }() 114 } 115 116 func newSocket(proto mangos.ProtocolBase) *socket { 117 s := &socket{ 118 proto: proto, 119 reconnMinTime: defaultReconnMinTime, 120 reconnMaxTime: defaultReconnMaxTime, 121 maxRxSize: defaultMaxRxSize, 122 } 123 return s 124 } 125 126 // MakeSocket is intended for use by Protocol implementations. The intention 127 // is that they can wrap this to provide a "proto.NewSocket()" implementation. 128 func MakeSocket(proto mangos.ProtocolBase) mangos.Socket { 129 return newSocket(proto) 130 } 131 132 func (s *socket) Close() error { 133 134 s.Lock() 135 listeners := s.listeners 136 dialers := s.dialers 137 138 s.listeners = nil 139 s.dialers = nil 140 s.closed = true // ensure we don't add new listeners or dialers 141 s.Unlock() 142 143 for _, l := range listeners { 144 _ = l.Close() 145 } 146 for _, d := range dialers { 147 _ = d.Close() 148 } 149 150 err := s.proto.Close() 151 s.pipes.CloseAll() 152 return err 153 } 154 155 func (ctx context) Send(b []byte) error { 156 msg := mangos.NewMessage(len(b)) 157 msg.Body = append(msg.Body, b...) 158 return ctx.SendMsg(msg) 159 } 160 func (ctx context) Recv() ([]byte, error) { 161 msg, err := ctx.RecvMsg() 162 if err != nil { 163 return nil, err 164 } 165 b := make([]byte, 0, len(msg.Body)) 166 b = append(b, msg.Body...) 167 msg.Free() 168 return b, nil 169 } 170 171 func (s *socket) OpenContext() (mangos.Context, error) { 172 c, err := s.proto.OpenContext() 173 if err != nil { 174 return nil, err 175 } 176 return &context{c}, nil 177 } 178 179 func (s *socket) SendMsg(msg *Message) error { 180 return s.proto.SendMsg(msg) 181 } 182 183 func (s *socket) Send(b []byte) error { 184 msg := mangos.NewMessage(len(b)) 185 msg.Body = append(msg.Body, b...) 186 return s.SendMsg(msg) 187 } 188 189 func (s *socket) RecvMsg() (*Message, error) { 190 return s.proto.RecvMsg() 191 } 192 193 func (s *socket) Recv() ([]byte, error) { 194 msg, err := s.RecvMsg() 195 if err != nil { 196 return nil, err 197 } 198 b := make([]byte, 0, len(msg.Body)) 199 b = append(b, msg.Body...) 200 msg.Free() 201 return b, nil 202 } 203 204 func (s *socket) getTransport(addr string) transport.Transport { 205 var i int 206 207 if i = strings.Index(addr, "://"); i < 0 { 208 return nil 209 } 210 scheme := addr[:i] 211 212 return transport.GetTransport(scheme) 213 } 214 215 func (s *socket) DialOptions(addr string, opts map[string]interface{}) error { 216 217 d, err := s.NewDialer(addr, opts) 218 if err != nil { 219 return err 220 } 221 return d.Dial() 222 } 223 224 func (s *socket) Dial(addr string) error { 225 return s.DialOptions(addr, nil) 226 } 227 228 func (s *socket) NewDialer(addr string, options map[string]interface{}) (mangos.Dialer, error) { 229 t := s.getTransport(addr) 230 if t == nil { 231 return nil, mangos.ErrBadTran 232 } 233 td, err := t.NewDialer(addr, s) 234 if err != nil { 235 return nil, err 236 } 237 d := &dialer{ 238 d: td, 239 s: s, 240 reconnMinTime: s.reconnMinTime, 241 reconnMaxTime: s.reconnMaxTime, 242 asynch: s.dialAsynch, 243 addr: addr, 244 } 245 for n, v := range options { 246 switch n { 247 case mangos.OptionReconnectTime: 248 fallthrough 249 case mangos.OptionMaxReconnectTime: 250 fallthrough 251 case mangos.OptionDialAsynch: 252 if err := d.SetOption(n, v); err != nil { 253 return nil, err 254 } 255 default: 256 if err = td.SetOption(n, v); err != nil { 257 return nil, err 258 } 259 } 260 } 261 if _, ok := options[mangos.OptionMaxRecvSize]; !ok { 262 err = td.SetOption(mangos.OptionMaxRecvSize, s.maxRxSize) 263 if err != nil && err != mangos.ErrBadOption { 264 return nil, err 265 } 266 } 267 268 s.Lock() 269 if s.closed { 270 s.Unlock() 271 _ = d.Close() 272 return nil, mangos.ErrClosed 273 } 274 s.dialers = append(s.dialers, d) 275 s.Unlock() 276 return d, nil 277 } 278 279 func (s *socket) ListenOptions(addr string, options map[string]interface{}) error { 280 l, err := s.NewListener(addr, options) 281 if err != nil { 282 return err 283 } 284 return l.Listen() 285 } 286 287 func (s *socket) Listen(addr string) error { 288 return s.ListenOptions(addr, nil) 289 } 290 291 func (s *socket) NewListener(addr string, options map[string]interface{}) (mangos.Listener, error) { 292 // This function sets up a goroutine to accept inbound connections. 293 // The accepted connection will be added to a list of accepted 294 // connections. The Listener just needs to listen continuously, 295 // as we assume that we want to continue to receive inbound 296 // connections without limit. 297 t := s.getTransport(addr) 298 if t == nil { 299 return nil, mangos.ErrBadTran 300 } 301 tl, err := t.NewListener(addr, s) 302 if err != nil { 303 return nil, err 304 } 305 for n, v := range options { 306 if err = tl.SetOption(n, v); err != nil { 307 _ = tl.Close() 308 return nil, err 309 } 310 } 311 if _, ok := options[mangos.OptionMaxRecvSize]; !ok { 312 err = tl.SetOption(mangos.OptionMaxRecvSize, s.maxRxSize) 313 if err != nil && err != mangos.ErrBadOption { 314 return nil, err 315 } 316 } 317 l := &listener{ 318 l: tl, 319 s: s, 320 addr: addr, 321 } 322 s.Lock() 323 if s.closed { 324 s.Unlock() 325 _ = l.Close() 326 return nil, mangos.ErrClosed 327 } 328 s.listeners = append(s.listeners, l) 329 s.Unlock() 330 331 return l, nil 332 } 333 334 func (s *socket) SetOption(name string, value interface{}) error { 335 if err := s.proto.SetOption(name, value); err != mangos.ErrBadOption { 336 return err 337 } 338 339 s.Lock() 340 defer s.Unlock() 341 342 switch name { 343 case mangos.OptionMaxRecvSize: 344 if v, ok := value.(int); ok && v >= 0 { 345 s.maxRxSize = v 346 } else { 347 return mangos.ErrBadValue 348 } 349 case mangos.OptionReconnectTime: 350 if v, ok := value.(time.Duration); ok { 351 s.reconnMinTime = v 352 } else { 353 return mangos.ErrBadValue 354 } 355 case mangos.OptionMaxReconnectTime: 356 if v, ok := value.(time.Duration); ok { 357 s.reconnMaxTime = v 358 } else { 359 return mangos.ErrBadValue 360 } 361 case mangos.OptionDialAsynch: 362 if v, ok := value.(bool); ok { 363 s.dialAsynch = v 364 } else { 365 return mangos.ErrBadValue 366 } 367 default: 368 return mangos.ErrBadOption 369 } 370 for _, d := range s.dialers { 371 _ = d.SetOption(name, value) 372 } 373 for _, l := range s.listeners { 374 _ = l.SetOption(name, value) 375 } 376 return nil 377 } 378 379 func (s *socket) GetOption(name string) (interface{}, error) { 380 if val, err := s.proto.GetOption(name); err != mangos.ErrBadOption { 381 return val, err 382 } 383 384 s.Lock() 385 defer s.Unlock() 386 387 switch name { 388 case mangos.OptionMaxRecvSize: 389 return s.maxRxSize, nil 390 case mangos.OptionReconnectTime: 391 return s.reconnMinTime, nil 392 case mangos.OptionMaxReconnectTime: 393 return s.reconnMaxTime, nil 394 case mangos.OptionDialAsynch: 395 return s.dialAsynch, nil 396 } 397 return nil, mangos.ErrBadOption 398 } 399 400 func (s *socket) Info() mangos.ProtocolInfo { 401 return s.proto.Info() 402 } 403 404 func (s *socket) SetPipeEventHook(newhook mangos.PipeEventHook) mangos.PipeEventHook { 405 s.Lock() 406 oldhook := s.pipehook 407 s.pipehook = newhook 408 s.Unlock() 409 return oldhook 410 }