github.com/gdamore/mangos@v1.4.0/protocol/rep/rep.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 rep implements the REP protocol, which is the response side of 16 // the request/response pattern. (REQ is the request.) 17 package rep 18 19 import ( 20 "encoding/binary" 21 "sync" 22 "time" 23 24 "nanomsg.org/go-mangos" 25 ) 26 27 type repEp struct { 28 q chan *mangos.Message 29 ep mangos.Endpoint 30 sock mangos.ProtocolSocket 31 w mangos.Waiter 32 r *rep 33 } 34 35 type rep struct { 36 sock mangos.ProtocolSocket 37 eps map[uint32]*repEp 38 backtracebuf []byte 39 backtrace []byte 40 backtraceL sync.Mutex 41 raw bool 42 ttl int 43 w mangos.Waiter 44 45 sync.Mutex 46 } 47 48 func (r *rep) Init(sock mangos.ProtocolSocket) { 49 r.sock = sock 50 r.eps = make(map[uint32]*repEp) 51 r.backtracebuf = make([]byte, 64) 52 r.ttl = 8 // default specified in the RFC 53 r.w.Init() 54 r.sock.SetSendError(mangos.ErrProtoState) 55 r.w.Add() 56 go r.sender() 57 } 58 59 func (r *rep) Shutdown(expire time.Time) { 60 61 r.w.WaitAbsTimeout(expire) 62 63 r.Lock() 64 peers := r.eps 65 r.eps = make(map[uint32]*repEp) 66 r.Unlock() 67 68 for id, peer := range peers { 69 delete(peers, id) 70 mangos.DrainChannel(peer.q, expire) 71 close(peer.q) 72 } 73 } 74 75 func (pe *repEp) sender() { 76 for { 77 m := <-pe.q 78 if m == nil { 79 break 80 } 81 82 if pe.ep.SendMsg(m) != nil { 83 m.Free() 84 break 85 } 86 } 87 } 88 89 func (r *rep) receiver(ep mangos.Endpoint) { 90 91 rq := r.sock.RecvChannel() 92 cq := r.sock.CloseChannel() 93 94 for { 95 96 m := ep.RecvMsg() 97 if m == nil { 98 return 99 } 100 101 v := ep.GetID() 102 m.Header = append(m.Header, 103 byte(v>>24), byte(v>>16), byte(v>>8), byte(v)) 104 105 hops := 0 106 // Move backtrace from body to header. 107 for { 108 if hops >= r.ttl { 109 m.Free() // ErrTooManyHops 110 return 111 } 112 hops++ 113 if len(m.Body) < 4 { 114 m.Free() // ErrGarbled 115 return 116 } 117 m.Header = append(m.Header, m.Body[:4]...) 118 m.Body = m.Body[4:] 119 // Check for high order bit set (0x80000000, big endian) 120 if m.Header[len(m.Header)-4]&0x80 != 0 { 121 break 122 } 123 } 124 125 select { 126 case rq <- m: 127 case <-cq: 128 m.Free() 129 return 130 } 131 } 132 } 133 134 func (r *rep) sender() { 135 defer r.w.Done() 136 cq := r.sock.CloseChannel() 137 sq := r.sock.SendChannel() 138 139 for { 140 var m *mangos.Message 141 142 select { 143 case m = <-sq: 144 if m == nil { 145 sq = r.sock.SendChannel() 146 continue 147 } 148 case <-cq: 149 return 150 } 151 152 // Lop off the 32-bit peer/pipe ID. If absent, drop. 153 if len(m.Header) < 4 { 154 m.Free() 155 continue 156 } 157 id := binary.BigEndian.Uint32(m.Header) 158 m.Header = m.Header[4:] 159 r.Lock() 160 pe := r.eps[id] 161 if pe == nil { 162 r.Unlock() 163 m.Free() 164 continue 165 } 166 167 select { 168 case pe.q <- m: 169 default: 170 // If our queue is full, we have no choice but to 171 // throw it on the floor. This shouldn't happen, 172 // since each partner should be running synchronously. 173 // Devices are a different situation, and this could 174 // lead to lossy behavior there. Initiators will 175 // resend if this happens. Devices need to have deep 176 // enough queues and be fast enough to avoid this. 177 m.Free() 178 } 179 r.Unlock() 180 } 181 } 182 183 func (*rep) Number() uint16 { 184 return mangos.ProtoRep 185 } 186 187 func (*rep) PeerNumber() uint16 { 188 return mangos.ProtoReq 189 } 190 191 func (*rep) Name() string { 192 return "rep" 193 } 194 195 func (*rep) PeerName() string { 196 return "req" 197 } 198 199 func (r *rep) AddEndpoint(ep mangos.Endpoint) { 200 pe := &repEp{ep: ep, r: r, q: make(chan *mangos.Message, 2)} 201 pe.w.Init() 202 r.Lock() 203 r.eps[ep.GetID()] = pe 204 r.Unlock() 205 go r.receiver(ep) 206 go pe.sender() 207 } 208 209 func (r *rep) RemoveEndpoint(ep mangos.Endpoint) { 210 id := ep.GetID() 211 212 r.Lock() 213 pe := r.eps[id] 214 delete(r.eps, id) 215 216 if pe != nil { 217 close(pe.q) 218 } 219 r.Unlock() 220 } 221 222 // We save the backtrace from this message. This means that if the app calls 223 // Recv before calling Send, the saved backtrace will be lost. This is how 224 // the application discards / cancels a request to which it declines to reply. 225 // This is only done in cooked mode. 226 func (r *rep) RecvHook(m *mangos.Message) bool { 227 if r.raw { 228 return true 229 } 230 r.sock.SetSendError(nil) 231 r.backtraceL.Lock() 232 r.backtrace = append(r.backtracebuf[0:0], m.Header...) 233 r.backtraceL.Unlock() 234 m.Header = nil 235 return true 236 } 237 238 func (r *rep) SendHook(m *mangos.Message) bool { 239 // Store our saved backtrace. Note that if none was previously stored, 240 // there is no one to reply to, and we drop the message. We only 241 // do this in cooked mode. 242 if r.raw { 243 return true 244 } 245 r.sock.SetSendError(mangos.ErrProtoState) 246 r.backtraceL.Lock() 247 m.Header = append(m.Header[0:0], r.backtrace...) 248 r.backtrace = nil 249 r.backtraceL.Unlock() 250 if m.Header == nil { 251 return false 252 } 253 return true 254 } 255 256 func (r *rep) SetOption(name string, v interface{}) error { 257 var ok bool 258 switch name { 259 case mangos.OptionRaw: 260 if r.raw, ok = v.(bool); !ok { 261 return mangos.ErrBadValue 262 } 263 if r.raw { 264 r.sock.SetSendError(nil) 265 } else { 266 r.sock.SetSendError(mangos.ErrProtoState) 267 } 268 return nil 269 case mangos.OptionTTL: 270 if ttl, ok := v.(int); !ok { 271 return mangos.ErrBadValue 272 } else if ttl < 1 || ttl > 255 { 273 return mangos.ErrBadValue 274 } else { 275 r.ttl = ttl 276 } 277 return nil 278 default: 279 return mangos.ErrBadOption 280 } 281 } 282 283 func (r *rep) GetOption(name string) (interface{}, error) { 284 switch name { 285 case mangos.OptionRaw: 286 return r.raw, nil 287 case mangos.OptionTTL: 288 return r.ttl, nil 289 default: 290 return nil, mangos.ErrBadOption 291 } 292 } 293 294 // NewSocket allocates a new Socket using the REP protocol. 295 func NewSocket() (mangos.Socket, error) { 296 return mangos.MakeSocket(&rep{}), nil 297 }