go-hep.org/x/hep@v0.38.1/xrootd/internal/mux/mux.go (about) 1 // Copyright ©2018 The go-hep Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 /* 6 Package mux implements the multiplexer that manages access to and writes data to the channels 7 by corresponding StreamID from xrootd protocol specification. 8 9 Example of usage: 10 11 mux := New() 12 defer m.Close() 13 14 // Claim channel for response retrieving. 15 id, channel, err := m.Claim() 16 if err != nil { 17 // handle error. 18 } 19 20 // Send a request to the server using id as a streamID. 21 22 go func() { 23 // Read response from the server. 24 // ... 25 26 // Send response to the awaiting caller using streamID from the server. 27 err := m.SendData(streamID, want) 28 if err != nil { 29 // handle error. 30 } 31 } 32 33 34 // Fetch response. 35 response := <-channel 36 */ 37 package mux // import "go-hep.org/x/hep/xrootd/internal/mux" 38 39 import ( 40 "encoding/binary" 41 "errors" 42 "fmt" 43 "math" 44 "strconv" 45 "strings" 46 "sync" 47 48 "go-hep.org/x/hep/xrootd/xrdproto" 49 ) 50 51 // ServerResponse contains slice of bytes Data representing data from 52 // XRootD server response (see XRootD protocol specification) and 53 // Err representing error received from server or occurred 54 // during response decoding. 55 type ServerResponse struct { 56 Data []byte 57 Err error 58 Redirection *Redirection 59 } 60 61 // Redirection represents the redirection request from the server. 62 // It contains address addr to which client must connect, 63 // opaque data that must be delivered to the new server as 64 // opaque information added to the file name, and token that 65 // must be delivered to the new server as part of login request. 66 type Redirection struct { 67 // Addr is the server address to which client must connect in the format "host:port". 68 Addr string 69 70 // Opaque is the data that must be delivered to the new server as 71 // opaque information added to the file name 72 Opaque string 73 74 // Token is the data that must be delivered to the new server as 75 // part of the login request. 76 Token string 77 } 78 79 // ParseRedirection parses the Redirection from the XRootD redirect response format. 80 // See http://xrootd.org/doc/dev45/XRdv310.pdf, p. 33 for details. 81 func ParseRedirection(raw []byte) (*Redirection, error) { 82 port := binary.BigEndian.Uint32(raw) 83 parts := strings.Split(string(raw[4:]), "?") 84 if len(parts) == 0 { 85 return nil, fmt.Errorf("xrootd: could not parse redirect url %q", string(raw)) 86 } 87 88 var opaque, token string 89 if len(parts) > 1 { 90 opaque = parts[1] 91 } 92 if len(parts) > 2 { 93 token = parts[2] 94 } 95 addr := parts[0] + ":" + strconv.Itoa(int(port)) 96 return &Redirection{Addr: addr, Opaque: opaque, Token: token}, nil 97 } 98 99 type dataSendChan chan<- ServerResponse 100 type DataRecvChan <-chan ServerResponse 101 102 const streamIDPartSize = math.MaxUint8 103 const streamIDPoolSize = streamIDPartSize * streamIDPartSize 104 105 // Mux manages channels by their ids. 106 // Basically, it's a map[StreamID] chan<-ServerResponse 107 // with methods to claim, free and pass data to a specific channel by id. 108 type Mux struct { 109 mu sync.Mutex 110 dataWaiters map[xrdproto.StreamID]dataSendChan 111 freeIDs chan uint16 112 quit chan struct{} 113 closed bool 114 } 115 116 // New creates a new Mux. 117 func New() *Mux { 118 const freeIDsBufferSize = 32 // 32 is completely arbitrary ATM and should be refined based on real use cases. 119 120 m := Mux{ 121 dataWaiters: make(map[xrdproto.StreamID]dataSendChan), 122 freeIDs: make(chan uint16, freeIDsBufferSize), 123 quit: make(chan struct{}), 124 } 125 126 go func() { 127 var i uint16 = 0 128 for { 129 select { 130 case m.freeIDs <- i: 131 i = (i + 1) % streamIDPoolSize 132 case <-m.quit: 133 close(m.freeIDs) 134 return 135 } 136 } 137 }() 138 139 return &m 140 } 141 142 // Close closes the Mux. 143 func (m *Mux) Close() { 144 m.mu.Lock() 145 if m.closed { 146 m.mu.Unlock() 147 return 148 } 149 m.closed = true 150 m.mu.Unlock() 151 close(m.quit) 152 153 response := ServerResponse{Err: errors.New("xrootd: close was called before response was fully received")} 154 for streamID := range m.dataWaiters { 155 _ = m.SendData(streamID, response) 156 m.Unclaim(streamID) 157 } 158 } 159 160 // Claim searches for unclaimed id and returns corresponding channel. 161 func (m *Mux) Claim() (xrdproto.StreamID, DataRecvChan, error) { 162 ch := make(chan ServerResponse) 163 164 for { 165 id := <-m.freeIDs 166 streamId := xrdproto.StreamID{byte(id >> 8), byte(id)} 167 168 m.mu.Lock() 169 if m.closed { 170 m.mu.Unlock() 171 return xrdproto.StreamID{}, nil, errors.New("mux: Claim was called on closed Mux") 172 } 173 if _, claimed := m.dataWaiters[streamId]; claimed { // Skip id if it was already claimed manually via ClaimWithID 174 m.mu.Unlock() 175 continue 176 } 177 178 m.dataWaiters[streamId] = ch 179 m.mu.Unlock() 180 return streamId, ch, nil 181 } 182 } 183 184 // ClaimWithID checks if id is unclaimed and returns the corresponding channel in case of success. 185 func (m *Mux) ClaimWithID(id xrdproto.StreamID) (DataRecvChan, error) { 186 m.mu.Lock() 187 defer m.mu.Unlock() 188 if m.closed { 189 return nil, errors.New("mux: ClaimWithID was called on closed Mux") 190 } 191 ch := make(chan ServerResponse) 192 193 if _, claimed := m.dataWaiters[id]; claimed { 194 return nil, fmt.Errorf("mux: channel with id %v is already claimed", id) 195 } 196 197 m.dataWaiters[id] = ch 198 199 return ch, nil 200 } 201 202 // Unclaim marks channel with specified id as unclaimed. 203 func (m *Mux) Unclaim(id xrdproto.StreamID) { 204 m.mu.Lock() 205 defer m.mu.Unlock() 206 207 if _, ok := m.dataWaiters[id]; ok { 208 close(m.dataWaiters[id]) 209 delete(m.dataWaiters, id) 210 } 211 } 212 213 // SendData sends data to channel with specific id. 214 func (m *Mux) SendData(id xrdproto.StreamID, data ServerResponse) error { 215 m.mu.Lock() 216 defer m.mu.Unlock() 217 218 if _, ok := m.dataWaiters[id]; !ok { 219 return fmt.Errorf("mux: cannot find data waiter for id %v", id) 220 } 221 222 m.dataWaiters[id] <- data 223 224 return nil 225 }