go.nanomsg.org/mangos/v3@v3.4.3-0.20240217232803-46464076f1f5/transport/tcp/tcp.go (about)

     1  // Copyright 2020 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 tcp implements the TCP transport for mangos. To enable it simply
    16  // import it.
    17  package tcp
    18  
    19  import (
    20  	"context"
    21  	"net"
    22  	"sync"
    23  	"time"
    24  
    25  	"go.nanomsg.org/mangos/v3"
    26  	"go.nanomsg.org/mangos/v3/transport"
    27  )
    28  
    29  const (
    30  	// Transport is a transport.Transport for TCP.
    31  	Transport = tcpTran(0)
    32  )
    33  
    34  func init() {
    35  	transport.RegisterTransport(Transport)
    36  }
    37  
    38  type dialer struct {
    39  	addr        string
    40  	proto       transport.ProtocolInfo
    41  	hs          transport.Handshaker
    42  	maxRecvSize int
    43  	d           net.Dialer
    44  	lock        sync.Mutex
    45  }
    46  
    47  func (d *dialer) Dial() (_ transport.Pipe, err error) {
    48  	conn, err := d.d.Dial("tcp", d.addr)
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  
    53  	p := transport.NewConnPipe(conn, d.proto)
    54  	d.lock.Lock()
    55  	p.SetOption(mangos.OptionMaxRecvSize, d.maxRecvSize)
    56  	d.lock.Unlock()
    57  	d.hs.Start(p)
    58  	return d.hs.Wait()
    59  }
    60  
    61  func (d *dialer) SetOption(n string, v interface{}) error {
    62  	d.lock.Lock()
    63  	defer d.lock.Unlock()
    64  
    65  	switch n {
    66  	case mangos.OptionMaxRecvSize:
    67  		if b, ok := v.(int); ok {
    68  			d.maxRecvSize = b
    69  			return nil
    70  		}
    71  		return mangos.ErrBadValue
    72  	case mangos.OptionKeepAliveTime:
    73  		if b, ok := v.(time.Duration); ok {
    74  			d.d.KeepAlive = b
    75  			return nil
    76  		}
    77  		return mangos.ErrBadValue
    78  
    79  	// The following options exist *only* for compatibility reasons.
    80  	// Remove them from new code.
    81  
    82  	case mangos.OptionKeepAlive:
    83  		if b, ok := v.(bool); ok {
    84  			if b {
    85  				d.d.KeepAlive = 0 // Enable (default time)
    86  			} else {
    87  				d.d.KeepAlive = -1 // Disable
    88  			}
    89  			return nil
    90  		}
    91  		return mangos.ErrBadValue
    92  
    93  	case mangos.OptionNoDelay:
    94  		if _, ok := v.(bool); ok {
    95  			return nil
    96  		}
    97  		return mangos.ErrBadValue
    98  	}
    99  	return mangos.ErrBadOption
   100  }
   101  
   102  func (d *dialer) GetOption(n string) (interface{}, error) {
   103  	d.lock.Lock()
   104  	defer d.lock.Unlock()
   105  
   106  	switch n {
   107  	case mangos.OptionMaxRecvSize:
   108  		return d.maxRecvSize, nil
   109  	case mangos.OptionKeepAliveTime:
   110  		return d.d.KeepAlive, nil
   111  	case mangos.OptionNoDelay:
   112  		return true, nil
   113  	case mangos.OptionKeepAlive:
   114  		if d.d.KeepAlive >= 0 {
   115  			return true, nil
   116  		}
   117  		return false, nil
   118  
   119  	}
   120  	return nil, mangos.ErrBadOption
   121  }
   122  
   123  type listener struct {
   124  	addr        string
   125  	bound       net.Addr
   126  	proto       transport.ProtocolInfo
   127  	l           net.Listener
   128  	lc          net.ListenConfig
   129  	maxRecvSize int
   130  	handshaker  transport.Handshaker
   131  	closeq      chan struct{}
   132  	once        sync.Once
   133  	lock        sync.Mutex
   134  }
   135  
   136  func (l *listener) Accept() (transport.Pipe, error) {
   137  
   138  	if l.l == nil {
   139  		return nil, mangos.ErrClosed
   140  	}
   141  	return l.handshaker.Wait()
   142  }
   143  
   144  func (l *listener) Listen() (err error) {
   145  	select {
   146  	case <-l.closeq:
   147  		return mangos.ErrClosed
   148  	default:
   149  	}
   150  	l.l, err = l.lc.Listen(context.Background(), "tcp", l.addr)
   151  	if err != nil {
   152  		return
   153  	}
   154  	l.bound = l.l.Addr()
   155  	go func() {
   156  		for {
   157  			conn, err := l.l.Accept()
   158  			if err != nil {
   159  				select {
   160  				case <-l.closeq:
   161  					return
   162  				default:
   163  					// We probably should be checking
   164  					// if this is a temporary error.
   165  					// If we run out of files we will
   166  					// spin hard here.
   167  					time.Sleep(time.Millisecond)
   168  					continue
   169  				}
   170  			}
   171  			p := transport.NewConnPipe(conn, l.proto)
   172  			l.lock.Lock()
   173  			p.SetOption(mangos.OptionMaxRecvSize, l.maxRecvSize)
   174  			l.lock.Unlock()
   175  			l.handshaker.Start(p)
   176  		}
   177  	}()
   178  	return
   179  }
   180  
   181  func (l *listener) Address() string {
   182  	if b := l.bound; b != nil {
   183  		return "tcp://" + b.String()
   184  	}
   185  	return "tcp://" + l.addr
   186  }
   187  
   188  func (l *listener) Close() error {
   189  	l.once.Do(func() {
   190  		close(l.closeq)
   191  		if l.l != nil {
   192  			_ = l.l.Close()
   193  		}
   194  		l.handshaker.Close()
   195  	})
   196  	return nil
   197  }
   198  
   199  func (l *listener) SetOption(n string, v interface{}) error {
   200  	l.lock.Lock()
   201  	defer l.lock.Unlock()
   202  
   203  	switch n {
   204  	case mangos.OptionMaxRecvSize:
   205  		if b, ok := v.(int); ok {
   206  			l.maxRecvSize = b
   207  			return nil
   208  		}
   209  		return mangos.ErrBadValue
   210  	case mangos.OptionKeepAliveTime:
   211  		if b, ok := v.(time.Duration); ok {
   212  			l.lc.KeepAlive = b
   213  			return nil
   214  		}
   215  		return mangos.ErrBadValue
   216  
   217  	// The following options exist *only* for compatibility reasons.
   218  	// Remove them from new code.
   219  
   220  	case mangos.OptionKeepAlive:
   221  		if b, ok := v.(bool); ok {
   222  			if b {
   223  				l.lc.KeepAlive = 0 // Enable (default time)
   224  			} else {
   225  				l.lc.KeepAlive = -1 // Disable
   226  			}
   227  			return nil
   228  		}
   229  		return mangos.ErrBadValue
   230  
   231  	case mangos.OptionNoDelay:
   232  		if _, ok := v.(bool); ok {
   233  			return nil
   234  		}
   235  		return mangos.ErrBadValue
   236  	}
   237  	return mangos.ErrBadOption
   238  }
   239  
   240  func (l *listener) GetOption(n string) (interface{}, error) {
   241  	l.lock.Lock()
   242  	defer l.lock.Unlock()
   243  
   244  	switch n {
   245  	case mangos.OptionMaxRecvSize:
   246  		return l.maxRecvSize, nil
   247  	case mangos.OptionKeepAliveTime:
   248  		return l.lc.KeepAlive, nil
   249  	case mangos.OptionNoDelay:
   250  		return true, nil
   251  	case mangos.OptionKeepAlive:
   252  		if l.lc.KeepAlive >= 0 {
   253  			return true, nil
   254  		}
   255  		return false, nil
   256  	}
   257  	return nil, mangos.ErrBadOption
   258  }
   259  
   260  type tcpTran int
   261  
   262  func (t tcpTran) Scheme() string {
   263  	return "tcp"
   264  }
   265  
   266  func (t tcpTran) NewDialer(addr string, sock mangos.Socket) (transport.Dialer, error) {
   267  	var err error
   268  	if addr, err = transport.StripScheme(t, addr); err != nil {
   269  		return nil, err
   270  	}
   271  
   272  	// check to ensure the provided addr resolves correctly.
   273  	if _, err = transport.ResolveTCPAddr(addr); err != nil {
   274  		return nil, err
   275  	}
   276  
   277  	d := &dialer{
   278  		addr:  addr,
   279  		proto: sock.Info(),
   280  		hs:    transport.NewConnHandshaker(),
   281  	}
   282  
   283  	return d, nil
   284  }
   285  
   286  func (t tcpTran) NewListener(addr string, sock mangos.Socket) (transport.Listener, error) {
   287  	var err error
   288  	l := &listener{
   289  		proto:  sock.Info(),
   290  		closeq: make(chan struct{}),
   291  	}
   292  
   293  	if addr, err = transport.StripScheme(t, addr); err != nil {
   294  		return nil, err
   295  	}
   296  
   297  	l.addr = addr
   298  
   299  	l.handshaker = transport.NewConnHandshaker()
   300  	return l, nil
   301  }