github.com/decred/dcrlnd@v0.7.6/internal/testutils/ipc.go (about)

     1  package testutils
     2  
     3  import (
     4  	"bufio"
     5  	"encoding/binary"
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  )
    10  
    11  // ipcPipePair holds both ends of an IPC pipe used to communicate with dcrd.
    12  type ipcPipePair struct {
    13  	r *os.File
    14  	w *os.File
    15  
    16  	// Whether to close the R and/or W ends.
    17  	closeR, closeW bool
    18  }
    19  
    20  // close closes the required ends of the pipe and returns the first error.
    21  func (p ipcPipePair) close() error {
    22  	var errR, errW error
    23  	if p.closeR {
    24  		errR = p.r.Close()
    25  	}
    26  	if p.closeW {
    27  		errW = p.w.Close()
    28  	}
    29  	if errR == nil {
    30  		return errW
    31  	}
    32  	return errR
    33  }
    34  
    35  // newIPCPipePair creates a new IPC pipe pair.
    36  func newIPCPipePair(closeR, closeW bool) (ipcPipePair, error) {
    37  	r, w, err := os.Pipe()
    38  	if err != nil {
    39  		return ipcPipePair{}, err
    40  	}
    41  	return ipcPipePair{r: r, w: w, closeR: closeR, closeW: closeW}, nil
    42  }
    43  
    44  // pipeMessage is a generic interface for dcrd pipe messages.
    45  type pipeMessage interface{}
    46  
    47  // boundJSONRPCListenAddrEvent is a pipeMessage that tracks the json RPC
    48  // address of the underlying dcrwallet instance.
    49  type boundJSONRPCListenAddrEvent string
    50  
    51  // boundGRPCListenAddrEvent is a pipeMessage that tracks the RPC address of the
    52  // underlying dcrwallet instance.
    53  type boundGRPCListenAddrEvent string
    54  
    55  // nextIPCMessage returns the next dcrd IPC message read from the passed
    56  // reading-end pipe.
    57  //
    58  // For unknown messages, this returns an empty pipeMessage instead of an error.
    59  func nextIPCMessage(r io.Reader) (pipeMessage, error) {
    60  	var emptyMsg pipeMessage
    61  	const protocolVersion = 1
    62  
    63  	// Bufferize reads from the underlying file.
    64  	r = bufio.NewReader(r)
    65  
    66  	// Decode the header.
    67  	var bProto [1]byte
    68  	var bLenType [1]byte
    69  	var bType [255]byte
    70  	var bLenPay [4]byte
    71  
    72  	// Enforce the protocol version.
    73  	if _, err := io.ReadFull(r, bProto[:]); err != nil {
    74  		return emptyMsg, fmt.Errorf("unable to read protocol: %v", err)
    75  	}
    76  	gotProtoVersion := bProto[0]
    77  	if gotProtoVersion != protocolVersion {
    78  		return emptyMsg, fmt.Errorf("protocol version mismatch: %d != %d",
    79  			gotProtoVersion, protocolVersion)
    80  	}
    81  
    82  	// Decode rest of header.
    83  	if _, err := io.ReadFull(r, bLenType[:]); err != nil {
    84  		return emptyMsg, fmt.Errorf("unable to read type length: %v", err)
    85  	}
    86  	lenType := bLenType[0]
    87  	if _, err := io.ReadFull(r, bType[:lenType]); err != nil {
    88  		return emptyMsg, fmt.Errorf("unable to read type: %v", err)
    89  	}
    90  	if _, err := io.ReadFull(r, bLenPay[:]); err != nil {
    91  		return emptyMsg, fmt.Errorf("unable to read payload length: %v", err)
    92  	}
    93  
    94  	// The existing IPC messages are small, so reading the entire message
    95  	// in an in-memory buffer is feasible today.
    96  	lenPay := binary.LittleEndian.Uint32(bLenPay[:])
    97  	payload := make([]byte, lenPay)
    98  	if _, err := io.ReadFull(r, payload); err != nil {
    99  		return emptyMsg, fmt.Errorf("unable to read payload: %v", err)
   100  	}
   101  
   102  	// Decode the payload based on the type.
   103  	typ := string(bType[:lenType])
   104  	switch typ {
   105  	case "jsonrpclistener":
   106  		return boundJSONRPCListenAddrEvent(string(payload)), nil
   107  	case "grpclistener":
   108  		return boundGRPCListenAddrEvent(string(payload)), nil
   109  	default:
   110  		// Other message types are unsupported but don't cause a read
   111  		// error.
   112  		return emptyMsg, nil
   113  	}
   114  }