github.com/cyverse/go-irodsclient@v0.13.2/irods/connection/request_response.go (about) 1 package connection 2 3 import ( 4 "github.com/cyverse/go-irodsclient/irods/common" 5 "github.com/cyverse/go-irodsclient/irods/message" 6 "golang.org/x/xerrors" 7 ) 8 9 // Request is an interface for calling iRODS RPC. 10 type Request interface { 11 GetMessage() (*message.IRODSMessage, error) 12 } 13 14 // Response is an interface for response of iRODS RPC Call. 15 type Response interface { 16 FromMessage(*message.IRODSMessage) error 17 } 18 19 // CheckErrorResponse is a Response on which CheckError can be called. 20 type CheckErrorResponse interface { 21 Response 22 CheckError() error 23 } 24 25 // RequestResponsePair is a structure that wraps Request, Response, and other parameters for making iRODS RPC call. 26 type RequestResponsePair struct { 27 Request Request 28 Response Response 29 BsBuffer []byte // can be null 30 RequestCallback common.TrackerCallBack // can be null 31 ResponseCallback common.TrackerCallBack // can be null 32 Error error 33 } 34 35 // Request sends a request and expects a response. 36 // bsBuffer is optional 37 func (conn *IRODSConnection) Request(request Request, response Response, bsBuffer []byte) error { 38 return conn.RequestWithTrackerCallBack(request, response, bsBuffer, nil, nil) 39 } 40 41 // RequestWithTrackerCallBack sends a request and expects a response. 42 // bsBuffer is optional 43 func (conn *IRODSConnection) RequestWithTrackerCallBack(request Request, response Response, bsBuffer []byte, reqCallback common.TrackerCallBack, resCallback common.TrackerCallBack) error { 44 // set transaction dirty 45 conn.SetTransactionDirty(true) 46 47 requestMessage, err := conn.getRequestMessage(request, true, false) 48 if err != nil { 49 if conn.metrics != nil { 50 conn.metrics.IncreaseCounterForRequestResponseFailures(1) 51 } 52 return err 53 } 54 55 err = conn.SendMessageWithTrackerCallBack(requestMessage, reqCallback) 56 if err != nil { 57 if conn.metrics != nil { 58 conn.metrics.IncreaseCounterForRequestResponseFailures(1) 59 } 60 return xerrors.Errorf("failed to send a request message: %w", err) 61 } 62 63 // Server responds with results 64 // external bs buffer 65 responseMessage, err := conn.ReadMessageWithTrackerCallBack(bsBuffer, resCallback) 66 if err != nil { 67 if conn.metrics != nil { 68 conn.metrics.IncreaseCounterForRequestResponseFailures(1) 69 } 70 return xerrors.Errorf("failed to receive a response message: %w", err) 71 } 72 73 err = conn.getResponse(responseMessage, response, true) 74 if err != nil { 75 if conn.metrics != nil { 76 conn.metrics.IncreaseCounterForRequestResponseFailures(1) 77 } 78 return xerrors.Errorf("failed to parse response message: %w", err) 79 } 80 81 return nil 82 } 83 84 // RequestAsyncWithTrackerCallBack sends multiple requests and expects responses. 85 func (conn *IRODSConnection) RequestAsyncWithTrackerCallBack(rrChan chan RequestResponsePair) chan RequestResponsePair { 86 waitResponseChan := make(chan RequestResponsePair, 100) 87 outputPair := make(chan RequestResponsePair, 100) 88 89 var lastErr error 90 91 // sender 92 go func() { 93 for { 94 pair, ok := <-rrChan 95 if !ok { 96 // input closed 97 close(waitResponseChan) 98 break 99 } 100 101 // if errored before? skip 102 if lastErr != nil { 103 pair.Error = lastErr 104 waitResponseChan <- pair 105 continue 106 } 107 108 requestMessage, err := conn.getRequestMessage(pair.Request, true, false) 109 if err != nil { 110 if conn.metrics != nil { 111 conn.metrics.IncreaseCounterForRequestResponseFailures(1) 112 } 113 114 lastErr = err 115 pair.Error = lastErr 116 waitResponseChan <- pair 117 continue 118 } 119 120 err = conn.SendMessageWithTrackerCallBack(requestMessage, pair.RequestCallback) 121 if err != nil { 122 if conn.metrics != nil { 123 conn.metrics.IncreaseCounterForRequestResponseFailures(1) 124 } 125 126 lastErr = xerrors.Errorf("failed to send a request message: %w", err) 127 pair.Error = lastErr 128 waitResponseChan <- pair 129 continue 130 } 131 132 waitResponseChan <- pair 133 } 134 }() 135 136 // receiver 137 go func() { 138 for { 139 pair, ok := <-waitResponseChan 140 if !ok { 141 // input closed 142 close(outputPair) 143 break 144 } 145 146 // if errored before? skip 147 if lastErr != nil { 148 if pair.Error == nil { 149 pair.Error = lastErr 150 } 151 outputPair <- pair 152 continue 153 } 154 155 // Server responds with results 156 // external bs buffer 157 responseMessage, err := conn.ReadMessageWithTrackerCallBack(pair.BsBuffer, pair.ResponseCallback) 158 if err != nil { 159 if conn.metrics != nil { 160 conn.metrics.IncreaseCounterForRequestResponseFailures(1) 161 } 162 163 lastErr = xerrors.Errorf("failed to receive a response message: %w", err) 164 pair.Error = lastErr 165 outputPair <- pair 166 continue 167 } 168 169 err = conn.getResponse(responseMessage, pair.Response, true) 170 if err != nil { 171 if conn.metrics != nil { 172 conn.metrics.IncreaseCounterForRequestResponseFailures(1) 173 } 174 175 lastErr = xerrors.Errorf("failed to parse response message: %w", err) 176 pair.Error = lastErr 177 outputPair <- pair 178 continue 179 } 180 181 outputPair <- pair 182 } 183 }() 184 185 return outputPair 186 } 187 188 // RequestForPassword sends a request and expects a response. XML escape only for '&' 189 // bsBuffer is optional 190 func (conn *IRODSConnection) RequestForPassword(request Request, response Response, bsBuffer []byte) error { 191 requestMessage, err := conn.getRequestMessage(request, true, true) 192 if err != nil { 193 if conn.metrics != nil { 194 conn.metrics.IncreaseCounterForRequestResponseFailures(1) 195 } 196 return err 197 } 198 199 err = conn.SendMessage(requestMessage) 200 if err != nil { 201 if conn.metrics != nil { 202 conn.metrics.IncreaseCounterForRequestResponseFailures(1) 203 } 204 return xerrors.Errorf("failed to send a request message: %w", err) 205 } 206 207 // Server responds with results 208 // external bs buffer 209 responseMessage, err := conn.ReadMessage(bsBuffer) 210 if err != nil { 211 if conn.metrics != nil { 212 conn.metrics.IncreaseCounterForRequestResponseFailures(1) 213 } 214 return xerrors.Errorf("failed to receive a response message: %w", err) 215 } 216 217 err = conn.getResponse(responseMessage, response, true) 218 if err != nil { 219 if conn.metrics != nil { 220 conn.metrics.IncreaseCounterForRequestResponseFailures(1) 221 } 222 return xerrors.Errorf("failed to parse response message: %w", err) 223 } 224 225 return nil 226 } 227 228 // RequestWithoutResponse sends a request but does not wait for a response. 229 func (conn *IRODSConnection) RequestWithoutResponse(request Request) error { 230 requestMessage, err := conn.getRequestMessage(request, true, false) 231 if err != nil { 232 if conn.metrics != nil { 233 conn.metrics.IncreaseCounterForRequestResponseFailures(1) 234 } 235 return err 236 } 237 238 err = conn.SendMessage(requestMessage) 239 if err != nil { 240 if conn.metrics != nil { 241 conn.metrics.IncreaseCounterForRequestResponseFailures(1) 242 } 243 return xerrors.Errorf("failed to send a request message: %w", err) 244 } 245 246 return nil 247 } 248 249 // RequestWithoutResponseNoXML sends a request but does not wait for a response. 250 func (conn *IRODSConnection) RequestWithoutResponseNoXML(request Request) error { 251 requestMessage, err := conn.getRequestMessage(request, false, false) 252 if err != nil { 253 if conn.metrics != nil { 254 conn.metrics.IncreaseCounterForRequestResponseFailures(1) 255 } 256 return xerrors.Errorf("failed to make a request message: %w", err) 257 } 258 259 err = conn.SendMessage(requestMessage) 260 if err != nil { 261 if conn.metrics != nil { 262 conn.metrics.IncreaseCounterForRequestResponseFailures(1) 263 } 264 return xerrors.Errorf("failed to send a request message: %w", err) 265 } 266 267 return nil 268 } 269 270 // RequestAndCheck sends a request and expects a CheckErrorResponse, on which the error is already checked. 271 func (conn *IRODSConnection) RequestAndCheck(request Request, response CheckErrorResponse, bsBuffer []byte) error { 272 return conn.RequestAndCheckWithTrackerCallBack(request, response, bsBuffer, nil, nil) 273 } 274 275 // RequestAndCheckWithCallBack sends a request and expects a CheckErrorResponse, on which the error is already checked. 276 func (conn *IRODSConnection) RequestAndCheckWithTrackerCallBack(request Request, response CheckErrorResponse, bsBuffer []byte, reqCallback common.TrackerCallBack, resCallback common.TrackerCallBack) error { 277 if err := conn.RequestWithTrackerCallBack(request, response, bsBuffer, reqCallback, resCallback); err != nil { 278 return err 279 } 280 281 return response.CheckError() 282 } 283 284 // RequestAndCheckForPassword sends a request and expects a CheckErrorResponse, on which the error is already checked. 285 // Only escape '&' 286 func (conn *IRODSConnection) RequestAndCheckForPassword(request Request, response CheckErrorResponse, bsBuffer []byte) error { 287 if err := conn.RequestForPassword(request, response, bsBuffer); err != nil { 288 return err 289 } 290 291 return response.CheckError() 292 } 293 294 func (conn *IRODSConnection) getRequestMessage(request Request, xml bool, forPassword bool) (*message.IRODSMessage, error) { 295 requestMessage, err := request.GetMessage() 296 if err != nil { 297 return nil, xerrors.Errorf("failed to make a request message: %w", err) 298 } 299 300 if xml { 301 // translate xml.Marshal XML into irods-understandable XML (among others, replace " by ") 302 err = conn.PreprocessMessage(requestMessage, forPassword) 303 if err != nil { 304 return nil, xerrors.Errorf("failed to send preprocess message: %w", err) 305 } 306 } 307 308 return requestMessage, nil 309 } 310 311 func (conn *IRODSConnection) getResponse(responseMessage *message.IRODSMessage, response Response, xml bool) error { 312 if xml { 313 // translate irods-dialect XML into valid XML 314 err := conn.PostprocessMessage(responseMessage) 315 if err != nil { 316 return xerrors.Errorf("failed to send postprocess message: %w", err) 317 } 318 } 319 320 err := response.FromMessage(responseMessage) 321 if err != nil { 322 return xerrors.Errorf("failed to parse a response message: %w", err) 323 } 324 325 return nil 326 }