github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/network/transport/memory/memory.go (about) 1 // Licensed under the Apache License, Version 2.0 (the "License"); 2 // you may not use this file except in compliance with the License. 3 // You may obtain a copy of the License at 4 // 5 // https://www.apache.org/licenses/LICENSE-2.0 6 // 7 // Unless required by applicable law or agreed to in writing, software 8 // distributed under the License is distributed on an "AS IS" BASIS, 9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 // See the License for the specific language governing permissions and 11 // limitations under the License. 12 // 13 // Original source: github.com/micro/go-micro/v3/network/transport/memory/memory.go 14 15 // Package memory is an in-memory transport 16 package memory 17 18 import ( 19 "context" 20 "errors" 21 "fmt" 22 "math/rand" 23 "net" 24 "sync" 25 "time" 26 27 "github.com/tickoalcantara12/micro/v3/service/network/transport" 28 maddr "github.com/tickoalcantara12/micro/v3/util/addr" 29 mnet "github.com/tickoalcantara12/micro/v3/util/net" 30 ) 31 32 type memorySocket struct { 33 recv chan *transport.Message 34 send chan *transport.Message 35 // sock exit 36 exit chan bool 37 // listener exit 38 lexit chan bool 39 40 local string 41 remote string 42 43 // for send/recv transport.Timeout 44 timeout time.Duration 45 ctx context.Context 46 sync.RWMutex 47 } 48 49 type memoryClient struct { 50 *memorySocket 51 opts transport.DialOptions 52 } 53 54 type memoryListener struct { 55 addr string 56 exit chan bool 57 conn chan *memorySocket 58 lopts transport.ListenOptions 59 topts transport.Options 60 sync.RWMutex 61 ctx context.Context 62 } 63 64 type memoryTransport struct { 65 opts transport.Options 66 sync.RWMutex 67 listeners map[string]*memoryListener 68 } 69 70 func (ms *memorySocket) Recv(m *transport.Message) error { 71 ms.RLock() 72 defer ms.RUnlock() 73 74 ctx := ms.ctx 75 if ms.timeout > 0 { 76 var cancel context.CancelFunc 77 ctx, cancel = context.WithTimeout(ms.ctx, ms.timeout) 78 defer cancel() 79 } 80 81 select { 82 case <-ctx.Done(): 83 return ctx.Err() 84 case <-ms.exit: 85 return errors.New("connection closed") 86 case <-ms.lexit: 87 return errors.New("server connection closed") 88 case cm := <-ms.recv: 89 *m = *cm 90 } 91 return nil 92 } 93 94 func (ms *memorySocket) Local() string { 95 return ms.local 96 } 97 98 func (ms *memorySocket) Remote() string { 99 return ms.remote 100 } 101 102 func (ms *memorySocket) Send(m *transport.Message) error { 103 ms.RLock() 104 defer ms.RUnlock() 105 106 ctx := ms.ctx 107 if ms.timeout > 0 { 108 var cancel context.CancelFunc 109 ctx, cancel = context.WithTimeout(ms.ctx, ms.timeout) 110 defer cancel() 111 } 112 113 select { 114 case <-ctx.Done(): 115 return ctx.Err() 116 case <-ms.exit: 117 return errors.New("connection closed") 118 case <-ms.lexit: 119 return errors.New("server connection closed") 120 case ms.send <- m: 121 } 122 return nil 123 } 124 125 func (ms *memorySocket) Close() error { 126 ms.Lock() 127 defer ms.Unlock() 128 select { 129 case <-ms.exit: 130 return nil 131 default: 132 close(ms.exit) 133 } 134 return nil 135 } 136 137 func (m *memoryListener) Addr() string { 138 return m.addr 139 } 140 141 func (m *memoryListener) Close() error { 142 m.Lock() 143 defer m.Unlock() 144 select { 145 case <-m.exit: 146 return nil 147 default: 148 close(m.exit) 149 } 150 return nil 151 } 152 153 func (m *memoryListener) Accept(fn func(transport.Socket)) error { 154 for { 155 select { 156 case <-m.exit: 157 return nil 158 case c := <-m.conn: 159 go fn(&memorySocket{ 160 lexit: c.lexit, 161 exit: c.exit, 162 send: c.recv, 163 recv: c.send, 164 local: c.Remote(), 165 remote: c.Local(), 166 timeout: m.topts.Timeout, 167 ctx: m.topts.Context, 168 }) 169 } 170 } 171 } 172 173 func (m *memoryTransport) Dial(addr string, opts ...transport.DialOption) (transport.Client, error) { 174 m.RLock() 175 defer m.RUnlock() 176 177 listener, ok := m.listeners[addr] 178 if !ok { 179 return nil, errors.New("could not dial " + addr) 180 } 181 182 var options transport.DialOptions 183 for _, o := range opts { 184 o(&options) 185 } 186 187 client := &memoryClient{ 188 &memorySocket{ 189 send: make(chan *transport.Message), 190 recv: make(chan *transport.Message), 191 exit: make(chan bool), 192 lexit: listener.exit, 193 local: addr, 194 remote: addr, 195 timeout: m.opts.Timeout, 196 ctx: m.opts.Context, 197 }, 198 options, 199 } 200 201 // pseudo connect 202 select { 203 case <-listener.exit: 204 return nil, errors.New("connection error") 205 case listener.conn <- client.memorySocket: 206 } 207 208 return client, nil 209 } 210 211 func (m *memoryTransport) Listen(addr string, opts ...transport.ListenOption) (transport.Listener, error) { 212 m.Lock() 213 defer m.Unlock() 214 215 var options transport.ListenOptions 216 for _, o := range opts { 217 o(&options) 218 } 219 220 host, port, err := net.SplitHostPort(addr) 221 if err != nil { 222 return nil, err 223 } 224 225 addr, err = maddr.Extract(host) 226 if err != nil { 227 return nil, err 228 } 229 230 // if zero port then randomly assign one 231 if len(port) > 0 && port == "0" { 232 i := rand.Intn(20000) 233 port = fmt.Sprintf("%d", 10000+i) 234 } 235 236 // set addr with port 237 addr = mnet.HostPort(addr, port) 238 239 if _, ok := m.listeners[addr]; ok { 240 return nil, errors.New("already listening on " + addr) 241 } 242 243 listener := &memoryListener{ 244 lopts: options, 245 topts: m.opts, 246 addr: addr, 247 conn: make(chan *memorySocket), 248 exit: make(chan bool), 249 ctx: m.opts.Context, 250 } 251 252 m.listeners[addr] = listener 253 254 return listener, nil 255 } 256 257 func (m *memoryTransport) Init(opts ...transport.Option) error { 258 for _, o := range opts { 259 o(&m.opts) 260 } 261 return nil 262 } 263 264 func (m *memoryTransport) Options() transport.Options { 265 return m.opts 266 } 267 268 func (m *memoryTransport) String() string { 269 return "memory" 270 } 271 272 func NewTransport(opts ...transport.Option) transport.Transport { 273 var options transport.Options 274 275 rand.Seed(time.Now().UnixNano()) 276 277 for _, o := range opts { 278 o(&options) 279 } 280 281 if options.Context == nil { 282 options.Context = context.Background() 283 } 284 285 return &memoryTransport{ 286 opts: options, 287 listeners: make(map[string]*memoryListener), 288 } 289 }