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  }