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 }