go-hep.org/x/hep@v0.38.1/xrootd/xrdproto/xrdproto.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 // Package protocol contains the XRootD protocol specific types 6 // and methods to handle them, such as marshalling and unmarshalling requests. 7 package xrdproto // import "go-hep.org/x/hep/xrootd/xrdproto" 8 9 import ( 10 "encoding/binary" 11 "errors" 12 "fmt" 13 "io" 14 "strings" 15 "time" 16 17 "go-hep.org/x/hep/xrootd/internal/xrdenc" 18 ) 19 20 // Request is a XRootD request issued to a server. 21 type Request interface { 22 // ReqID uniquely identifies the type of a request. 23 ReqID() uint16 24 25 // ShouldSign indicates whether this request should be signed if security level is SignLikely. 26 // For the list of actual examples see XRootD protocol specification v. 3.1.0, p.76. 27 ShouldSign() bool 28 29 Marshaler 30 Unmarshaler 31 } 32 33 // Response is a XRootD response returned by the server 34 type Response interface { 35 RespID() uint16 36 Marshaler 37 Unmarshaler 38 } 39 40 // Marshaler is the interface implemented by a type that can marshal itself 41 // into a binary form, following the XRootD protocol. 42 // 43 // MarshalXrd encodes the receiver into a binary form and returns the result. 44 type Marshaler interface { 45 MarshalXrd(enc *xrdenc.WBuffer) error 46 } 47 48 // Unmarshaler is the interface implemented by a type that can 49 // unmarshal a binary representation of itself, following the XRootD protocol. 50 // 51 // UnmarshalXrd must be able to decode the form generated by MarshalXrd. 52 // UnmarshalXrd must copy the data if it wishes to retain the data after 53 // returning. 54 type Unmarshaler interface { 55 UnmarshalXrd(dec *xrdenc.RBuffer) error 56 } 57 58 // ResponseStatus is the status code indicating how the request completed. 59 type ResponseStatus uint16 60 61 const ( 62 // Ok indicates that request fully completed and no addition responses will be forthcoming. 63 Ok ResponseStatus = 0 64 // OkSoFar indicates that server provides partial response and client should be prepared 65 // to receive additional responses on same stream. 66 OkSoFar ResponseStatus = 4000 67 // Error indicates that an error occurred during request handling. 68 // Error code and error message are sent as part of response (see xrootd protocol specification v3.1.0, p. 27). 69 Error ResponseStatus = 4003 70 // Redirect indicates that the client must re-issue the request to another server. 71 Redirect ResponseStatus = 4004 72 // Wait indicates that the client must wait the indicated number of seconds and retry the request. 73 Wait ResponseStatus = 4005 74 ) 75 76 // WaitResponse is the response indicating that the client must wait and retry the request. 77 // See http://xrootd.org/doc/dev45/XRdv310.pdf, p. 35 for details. 78 type WaitResponse struct { 79 Duration time.Duration 80 } 81 82 // MarshalXrd implements Marshaler. 83 func (o WaitResponse) MarshalXrd(wBuffer *xrdenc.WBuffer) error { 84 wBuffer.WriteI32(int32(o.Duration.Seconds())) 85 return nil 86 } 87 88 // UnmarshalXrd implements Unmarshaler. 89 func (o *WaitResponse) UnmarshalXrd(rBuffer *xrdenc.RBuffer) error { 90 o.Duration = time.Second * time.Duration(rBuffer.ReadI32()) 91 return nil 92 } 93 94 // ServerError is the error returned by the XRootD server as part of response to the request. 95 type ServerError struct { 96 Code ServerErrorCode 97 Message string 98 } 99 100 // ServerErrorCode is the code of the error returned by the XRootD server as part of response to the request. 101 type ServerErrorCode int32 102 103 const ( 104 InvalidRequest ServerErrorCode = 3006 // InvalidRequest indicates that request is invalid. 105 IOError ServerErrorCode = 3007 // IOError indicates that an IO error has occurred on the server side. 106 NotAuthorized ServerErrorCode = 3010 // NotAuthorized indicates that user was not authorized for operation. 107 NotFound ServerErrorCode = 3011 // NotFound indicates that path was not found on the remote server. 108 ) 109 110 func (err ServerError) Error() string { 111 return fmt.Sprintf("xrootd: error %d: %s", err.Code, err.Message) 112 } 113 114 // MarshalXrd implements Marshaler. 115 func (o ServerError) MarshalXrd(wBuffer *xrdenc.WBuffer) error { 116 wBuffer.WriteI32(int32(o.Code)) 117 wBuffer.WriteBytes([]byte(o.Message)) 118 wBuffer.WriteBytes([]byte("\x00")) 119 return nil 120 } 121 122 // UnmarshalXrd implements Unmarshaler. 123 func (o *ServerError) UnmarshalXrd(rBuffer *xrdenc.RBuffer) error { 124 o.Code = ServerErrorCode(rBuffer.ReadI32()) 125 data := rBuffer.Bytes() 126 if len(data) == 0 { 127 return errors.New("xrootd: missing error message in server response") 128 } 129 o.Message = string(data[:len(data)-1]) 130 return nil 131 } 132 133 // StreamID is the binary identifier associated with a request stream. 134 type StreamID [2]byte 135 136 // ResponseHeaderLength is the length of the ResponseHeader in bytes. 137 const ResponseHeaderLength = 2 + 2 + 4 138 139 // ResponseHeader is the header that precedes all responses (see xrootd protocol specification). 140 type ResponseHeader struct { 141 StreamID StreamID 142 Status ResponseStatus 143 DataLength int32 144 } 145 146 // MarshalXrd implements xrdproto.Marshaler 147 func (o ResponseHeader) MarshalXrd(wBuffer *xrdenc.WBuffer) error { 148 wBuffer.WriteBytes(o.StreamID[:]) 149 wBuffer.WriteU16(uint16(o.Status)) 150 wBuffer.WriteI32(o.DataLength) 151 return nil 152 } 153 154 // UnmarshalXrd implements xrdproto.Unmarshaler 155 func (o *ResponseHeader) UnmarshalXrd(rBuffer *xrdenc.RBuffer) error { 156 rBuffer.ReadBytes(o.StreamID[:]) 157 o.Status = ResponseStatus(rBuffer.ReadU16()) 158 o.DataLength = rBuffer.ReadI32() 159 return nil 160 } 161 162 // Error returns an error received from the server or nil if request hasn't failed. 163 func (hdr ResponseHeader) Error(data []byte) error { 164 if hdr.Status != Error { 165 return nil 166 } 167 if len(data) < 4 { 168 return fmt.Errorf("xrootd: invalid ResponseHeader error: %w", io.ErrShortBuffer) 169 } 170 171 var serverError ServerError 172 rBuffer := xrdenc.NewRBuffer(data) 173 err := serverError.UnmarshalXrd(rBuffer) 174 if err != nil { 175 return fmt.Errorf("xrootd: error occurred during unmarshaling of a server error: %w", err) 176 } 177 178 return serverError 179 } 180 181 // RequestHeaderLength is the length of the RequestHeader in bytes. 182 const RequestHeaderLength = 2 + 2 183 184 // ResponseHeader is the header that precedes all requests (we are interested in StreamID and RequestID, actual request 185 // parameters are a part of specific request). 186 type RequestHeader struct { 187 StreamID StreamID 188 RequestID uint16 189 } 190 191 // MarshalXrd implements Marshaler. 192 func (o RequestHeader) MarshalXrd(wBuffer *xrdenc.WBuffer) error { 193 wBuffer.WriteBytes(o.StreamID[:]) 194 wBuffer.WriteU16(o.RequestID) 195 return nil 196 } 197 198 // UnmarshalXrd implements Unmarshaler. 199 func (o *RequestHeader) UnmarshalXrd(rBuffer *xrdenc.RBuffer) error { 200 rBuffer.ReadBytes(o.StreamID[:]) 201 o.RequestID = rBuffer.ReadU16() 202 return nil 203 } 204 205 // ServerType is the general server type kept for compatibility 206 // with 2.0 protocol version (see xrootd protocol specification v3.1.0, p. 5). 207 type ServerType int32 208 209 const ( 210 // LoadBalancingServer indicates whether this is a load-balancing server. 211 LoadBalancingServer ServerType = iota 212 // DataServer indicates whether this is a data server. 213 DataServer 214 ) 215 216 // FilepathRequest is a request that contains file paths. 217 // This interface is used to append opaque data to the request. 218 // Opaque data is received as part of the redirect response. 219 type FilepathRequest interface { 220 Opaque() string // Opaque returns opaque data from this request. 221 SetOpaque(opaque string) // SetOpaque sets opaque data for this request. 222 } 223 224 // PathID is the socket identifier. It may be used in read and write requests to indicate 225 // which socket should be used for a response or as a source of data. 226 type PathID byte 227 228 // DataRequest is the request that operate over 2 sockets. 229 // One socket is used for sending the request and other is used to 230 // send or receive data. 231 type DataRequest interface { 232 // PathID returns an identifier of the socket which should be used to read or write a data. 233 PathID() PathID 234 235 // SePathID sets the identifier of the socket which should be used to read or write a data. 236 SetPathID(pathID PathID) 237 238 // Direction returns the direction of the request: either reading or writing. 239 Direction() DataRequestDirection 240 241 // PathData returns the data which should be send to the data socket. 242 PathData() []byte 243 } 244 245 // DataRequestDirection is the direction of the request: either reading or writing. 246 type DataRequestDirection int 247 248 const ( 249 // DataRequestRead indicates that request has reading direction. 250 // In other words, the request obtains a data from the server. 251 DataRequestRead DataRequestDirection = iota 252 253 // DataRequestWrite indicates that request has writing direction. 254 // In other words, the request sends a data to the server. 255 DataRequestWrite 256 ) 257 258 // RequestLevel is the security requirement that the associated request is to have. 259 type RequestLevel byte 260 261 const ( 262 SignNone RequestLevel = 0 // SignNone indicates that the request need not to be signed. 263 SignLikely RequestLevel = 1 // SignLikely indicates that the request must be signed if it modifies data. 264 SignNeeded RequestLevel = 2 // SignNeeded indicates that the request mush be signed. 265 ) 266 267 // SecurityLevel is the predefined security level that specifies which requests should be signed. 268 // See specification for details: http://xrootd.org/doc/dev45/XRdv310.pdf, p. 75. 269 type SecurityLevel byte 270 271 const ( 272 // NoneLevel indicates that no request needs to be signed. 273 NoneLevel SecurityLevel = 0 274 // Compatible indicates that only potentially destructive requests need to be signed. 275 Compatible SecurityLevel = 1 276 // Standard indicates that potentially destructive requests 277 // as well as certain non-destructive requests need to be signed. 278 Standard SecurityLevel = 2 279 // Intense indicates that request that may reveal metadata or modify data need to be signed. 280 Intense SecurityLevel = 3 281 // Pedantic indicates that all requests need to be signed. 282 Pedantic SecurityLevel = 4 283 ) 284 285 // SecurityOverrideLength is the length of SecurityOverride in bytes. 286 const SecurityOverrideLength = 2 287 288 // SecurityOverride is an alteration needed to the specified predefined security level. 289 // It consists of the request index and the security requirement the associated request should have. 290 // Request index is calculated as: 291 // 292 // (request code) - (request code of Auth request) 293 // 294 // according to xrootd protocol specification. 295 type SecurityOverride struct { 296 RequestIndex byte 297 RequestLevel RequestLevel 298 } 299 300 // MarshalXrd implements xrdproto.Marshaler 301 func (o SecurityOverride) MarshalXrd(enc *xrdenc.WBuffer) error { 302 enc.WriteU8(o.RequestIndex) 303 enc.WriteU8(byte(o.RequestLevel)) 304 return nil 305 } 306 307 // UnmarshalXrd implements xrdproto.Unmarshaler 308 func (o *SecurityOverride) UnmarshalXrd(dec *xrdenc.RBuffer) error { 309 o.RequestIndex = dec.ReadU8() 310 o.RequestLevel = RequestLevel(dec.ReadU8()) 311 return nil 312 } 313 314 // SetOpaque sets opaque data part in the provided path. 315 func SetOpaque(path *string, opaque string) { 316 pos := strings.LastIndex(*path, "?") 317 if pos != -1 { 318 *path = (*path)[:pos] 319 } 320 *path = *path + "?" + opaque 321 } 322 323 // Opaque returns opaque data from provided path. 324 func Opaque(path string) string { 325 pos := strings.LastIndex(path, "?") 326 return path[pos+1:] 327 } 328 329 // ReadRequest reads a XRootD request from r. 330 // ReadRequest returns entire payload of the request including header. 331 // ReadRequest requires serialization since multiple ReadFull calls are made. 332 func ReadRequest(r io.Reader) ([]byte, error) { 333 // 16 is for the request options and 4 is for the data length 334 const requestSize = RequestHeaderLength + 16 + 4 335 request := make([]byte, requestSize) 336 if _, err := io.ReadFull(r, request); err != nil { 337 return nil, err 338 } 339 340 dataLength := binary.BigEndian.Uint32(request[RequestHeaderLength+16:]) 341 if dataLength == 0 { 342 return request, nil 343 } 344 345 data := make([]byte, dataLength) 346 if _, err := io.ReadFull(r, data); err != nil { 347 return nil, err 348 } 349 350 return append(request, data...), nil 351 } 352 353 // WriteResponse writes a XRootD response resp to the w. 354 // The response is directed to the stream with id equal to the streamID. 355 // The status is sent as part of response header. 356 // WriteResponse writes all data to the w as single Write call, so no 357 // serialization is required. 358 func WriteResponse(w io.Writer, streamID StreamID, status ResponseStatus, resp Marshaler) error { 359 var respWBuffer xrdenc.WBuffer 360 if resp != nil { 361 if err := resp.MarshalXrd(&respWBuffer); err != nil { 362 return err 363 } 364 } 365 366 header := ResponseHeader{ 367 StreamID: streamID, 368 Status: status, 369 DataLength: int32(len(respWBuffer.Bytes())), 370 } 371 372 var headerWBuffer xrdenc.WBuffer 373 if err := header.MarshalXrd(&headerWBuffer); err != nil { 374 return err 375 } 376 377 response := append(headerWBuffer.Bytes(), respWBuffer.Bytes()...) 378 if _, err := w.Write(response); err != nil { 379 return err 380 } 381 return nil 382 } 383 384 // ReadResponse reads a XRootD response from r. 385 // ReadResponse returns the response header and the response body. 386 // ReadResponse requires serialization since multiple ReadFull calls are made. 387 func ReadResponse(r io.Reader) (ResponseHeader, []byte, error) { 388 var header ResponseHeader 389 data, err := ReadResponseWithReuse(r, make([]byte, ResponseHeaderLength), &header) 390 return header, data, err 391 } 392 393 // ReadResponseWithReuse reads a XRootD response from r. A response header is read into headerBytes and 394 // unmarshaled to header for the reusing reasons. 395 // ReadResponseWithReuse returns the response body. 396 // ReadResponseWithReuse requires serialization since multiple ReadFull calls are made. 397 func ReadResponseWithReuse(r io.Reader, headerBytes []byte, header *ResponseHeader) ([]byte, error) { 398 if _, err := io.ReadFull(r, headerBytes); err != nil { 399 return nil, err 400 } 401 rBuffer := xrdenc.NewRBuffer(headerBytes) 402 if err := header.UnmarshalXrd(rBuffer); err != nil { 403 return nil, err 404 } 405 if header.DataLength == 0 { 406 return nil, nil 407 } 408 var data = make([]byte, header.DataLength) 409 if _, err := io.ReadFull(r, data); err != nil { 410 return nil, err 411 } 412 return data, nil 413 }