github.com/gdamore/mangos@v1.4.0/protocol/respondent/respondent.go (about)

     1  // Copyright 2018 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 respondent implements the RESPONDENT protocol.  This protocol
    16  // receives SURVEYOR requests, and responds with an answer.
    17  package respondent
    18  
    19  import (
    20  	"encoding/binary"
    21  	"sync"
    22  	"time"
    23  
    24  	"nanomsg.org/go-mangos"
    25  )
    26  
    27  type resp struct {
    28  	sock      mangos.ProtocolSocket
    29  	peers     map[uint32]*respPeer
    30  	raw       bool
    31  	ttl       int
    32  	backbuf   []byte
    33  	backtrace []byte
    34  	w         mangos.Waiter
    35  	sync.Mutex
    36  }
    37  
    38  type respPeer struct {
    39  	q  chan *mangos.Message
    40  	ep mangos.Endpoint
    41  	x  *resp
    42  }
    43  
    44  func (x *resp) Init(sock mangos.ProtocolSocket) {
    45  	x.sock = sock
    46  	x.ttl = 8
    47  	x.peers = make(map[uint32]*respPeer)
    48  	x.w.Init()
    49  	x.backbuf = make([]byte, 0, 64)
    50  	x.sock.SetSendError(mangos.ErrProtoState)
    51  	x.w.Add()
    52  	go x.sender()
    53  }
    54  
    55  func (x *resp) Shutdown(expire time.Time) {
    56  	peers := make(map[uint32]*respPeer)
    57  	x.w.WaitAbsTimeout(expire)
    58  	x.Lock()
    59  	for id, peer := range x.peers {
    60  		delete(x.peers, id)
    61  		peers[id] = peer
    62  	}
    63  	x.Unlock()
    64  
    65  	for id, peer := range peers {
    66  		delete(peers, id)
    67  		mangos.DrainChannel(peer.q, expire)
    68  		close(peer.q)
    69  	}
    70  }
    71  
    72  func (x *resp) sender() {
    73  	// This is pretty easy because we have only one peer at a time.
    74  	// If the peer goes away, we'll just drop the message on the floor.
    75  
    76  	defer x.w.Done()
    77  	cq := x.sock.CloseChannel()
    78  	sq := x.sock.SendChannel()
    79  	for {
    80  		var m *mangos.Message
    81  		select {
    82  		case m = <-sq:
    83  			if m == nil {
    84  				sq = x.sock.SendChannel()
    85  				continue
    86  			}
    87  		case <-cq:
    88  			return
    89  		}
    90  
    91  		// Lop off the 32-bit peer/pipe ID.  If absent, drop.
    92  		if len(m.Header) < 4 {
    93  			m.Free()
    94  			continue
    95  		}
    96  
    97  		id := binary.BigEndian.Uint32(m.Header)
    98  		m.Header = m.Header[4:]
    99  
   100  		x.Lock()
   101  		peer := x.peers[id]
   102  		x.Unlock()
   103  
   104  		if peer == nil {
   105  			m.Free()
   106  			continue
   107  		}
   108  
   109  		// Put it on the outbound queue
   110  		select {
   111  		case peer.q <- m:
   112  		default:
   113  			// Backpressure, drop it.
   114  			m.Free()
   115  		}
   116  	}
   117  }
   118  
   119  // When sending, we should have the survey ID in the header.
   120  func (peer *respPeer) sender() {
   121  	for {
   122  		m := <-peer.q
   123  		if m == nil {
   124  			break
   125  		}
   126  		if peer.ep.SendMsg(m) != nil {
   127  			m.Free()
   128  			break
   129  		}
   130  	}
   131  }
   132  
   133  func (x *resp) receiver(ep mangos.Endpoint) {
   134  
   135  	rq := x.sock.RecvChannel()
   136  	cq := x.sock.CloseChannel()
   137  
   138  outer:
   139  	for {
   140  		m := ep.RecvMsg()
   141  		if m == nil {
   142  			return
   143  		}
   144  
   145  		v := ep.GetID()
   146  		m.Header = append(m.Header,
   147  			byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
   148  		hops := 0
   149  
   150  		for {
   151  			if hops >= x.ttl {
   152  				m.Free() // ErrTooManyHops
   153  				continue outer
   154  			}
   155  			hops++
   156  			if len(m.Body) < 4 {
   157  				m.Free()
   158  				continue outer
   159  			}
   160  			m.Header = append(m.Header, m.Body[:4]...)
   161  			m.Body = m.Body[4:]
   162  			if m.Header[len(m.Header)-4]&0x80 != 0 {
   163  				break
   164  			}
   165  		}
   166  
   167  		select {
   168  		case rq <- m:
   169  		case <-cq:
   170  			m.Free()
   171  			return
   172  		}
   173  	}
   174  }
   175  
   176  func (x *resp) RecvHook(m *mangos.Message) bool {
   177  	if x.raw {
   178  		// Raw mode receivers get the message unadulterated.
   179  		return true
   180  	}
   181  
   182  	if len(m.Header) < 4 {
   183  		return false
   184  	}
   185  
   186  	x.Lock()
   187  	x.backbuf = x.backbuf[0:0] // avoid allocations
   188  	x.backtrace = append(x.backbuf, m.Header...)
   189  	x.Unlock()
   190  	x.sock.SetSendError(nil)
   191  	return true
   192  }
   193  
   194  func (x *resp) SendHook(m *mangos.Message) bool {
   195  	if x.raw {
   196  		// Raw mode senders expected to have prepared header already.
   197  		return true
   198  	}
   199  	x.sock.SetSendError(mangos.ErrProtoState)
   200  	x.Lock()
   201  	m.Header = append(m.Header[0:0], x.backtrace...)
   202  	x.backtrace = nil
   203  	x.Unlock()
   204  	if len(m.Header) == 0 {
   205  		return false
   206  	}
   207  	return true
   208  }
   209  
   210  func (x *resp) AddEndpoint(ep mangos.Endpoint) {
   211  	peer := &respPeer{ep: ep, x: x, q: make(chan *mangos.Message, 1)}
   212  
   213  	x.Lock()
   214  	x.peers[ep.GetID()] = peer
   215  	x.Unlock()
   216  
   217  	go x.receiver(ep)
   218  	go peer.sender()
   219  }
   220  
   221  func (x *resp) RemoveEndpoint(ep mangos.Endpoint) {
   222  	id := ep.GetID()
   223  
   224  	x.Lock()
   225  	peer := x.peers[id]
   226  	delete(x.peers, id)
   227  	x.Unlock()
   228  
   229  	if peer != nil {
   230  		close(peer.q)
   231  	}
   232  }
   233  
   234  func (*resp) Number() uint16 {
   235  	return mangos.ProtoRespondent
   236  }
   237  
   238  func (*resp) PeerNumber() uint16 {
   239  	return mangos.ProtoSurveyor
   240  }
   241  
   242  func (*resp) Name() string {
   243  	return "respondent"
   244  }
   245  
   246  func (*resp) PeerName() string {
   247  	return "surveyor"
   248  }
   249  
   250  func (x *resp) SetOption(name string, v interface{}) error {
   251  	var ok bool
   252  	switch name {
   253  	case mangos.OptionRaw:
   254  		if x.raw, ok = v.(bool); !ok {
   255  			return mangos.ErrBadValue
   256  		}
   257  		if x.raw {
   258  			x.sock.SetSendError(nil)
   259  		} else {
   260  			x.sock.SetSendError(mangos.ErrProtoState)
   261  		}
   262  		return nil
   263  	case mangos.OptionTTL:
   264  		if ttl, ok := v.(int); !ok {
   265  			return mangos.ErrBadValue
   266  		} else if ttl < 1 || ttl > 255 {
   267  			return mangos.ErrBadValue
   268  		} else {
   269  			x.ttl = ttl
   270  		}
   271  		return nil
   272  	default:
   273  		return mangos.ErrBadOption
   274  	}
   275  }
   276  
   277  func (x *resp) GetOption(name string) (interface{}, error) {
   278  	switch name {
   279  	case mangos.OptionRaw:
   280  		return x.raw, nil
   281  	case mangos.OptionTTL:
   282  		return x.ttl, nil
   283  	default:
   284  		return nil, mangos.ErrBadOption
   285  	}
   286  }
   287  
   288  // NewSocket allocates a new Socket using the RESPONDENT protocol.
   289  func NewSocket() (mangos.Socket, error) {
   290  	return mangos.MakeSocket(&resp{}), nil
   291  }