github.com/amazechain/amc@v0.1.3/internal/sync/error.go (about)

     1  package sync
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"github.com/amazechain/amc/internal/p2p"
     8  	"github.com/amazechain/amc/internal/p2p/encoder"
     9  	p2ptypes "github.com/amazechain/amc/internal/p2p/types"
    10  	"github.com/amazechain/amc/log"
    11  
    12  	libp2pcore "github.com/libp2p/go-libp2p/core"
    13  	"github.com/libp2p/go-libp2p/core/network"
    14  )
    15  
    16  var responseCodeSuccess = byte(0x00)
    17  var responseCodeInvalidRequest = byte(0x01)
    18  var responseCodeServerError = byte(0x02)
    19  
    20  func (s *Service) generateErrorResponse(code byte, reason string) ([]byte, error) {
    21  	return createErrorResponse(code, reason, s.cfg.p2p)
    22  }
    23  
    24  // ReadStatusCode response from a RPC stream.
    25  func ReadStatusCode(stream network.Stream, encoding encoder.NetworkEncoding) (uint8, string, error) {
    26  	// Set ttfb deadline.
    27  	SetStreamReadDeadline(stream, ttfbTimeout)
    28  	b := make([]byte, 1)
    29  	_, err := stream.Read(b)
    30  	if err != nil {
    31  		return 0, "", err
    32  	}
    33  
    34  	if b[0] == responseCodeSuccess {
    35  		// Set response deadline on a successful response code.
    36  		SetStreamReadDeadline(stream, respTimeout)
    37  
    38  		return 0, "", nil
    39  	}
    40  
    41  	// Set response deadline, when reading error message.
    42  	SetStreamReadDeadline(stream, respTimeout)
    43  	msg := &p2ptypes.ErrorMessage{}
    44  	if err := encoding.DecodeWithMaxLength(stream, msg); err != nil {
    45  		return 0, "", err
    46  	}
    47  
    48  	return b[0], string(*msg), nil
    49  }
    50  
    51  func writeErrorResponseToStream(responseCode byte, reason string, stream libp2pcore.Stream, encoder p2p.EncodingProvider) {
    52  	resp, err := createErrorResponse(responseCode, reason, encoder)
    53  	if err != nil {
    54  		log.Debug("Could not generate a response error", "err", err)
    55  	} else if _, err := stream.Write(resp); err != nil {
    56  		log.Debug("Could not write to stream", "err", err)
    57  	} else {
    58  		// If sending the error message succeeded, close to send an EOF.
    59  		closeStream(stream)
    60  	}
    61  }
    62  
    63  func createErrorResponse(code byte, reason string, encoder p2p.EncodingProvider) ([]byte, error) {
    64  	buf := bytes.NewBuffer([]byte{code})
    65  	errMsg := p2ptypes.ErrorMessage(reason)
    66  	if _, err := encoder.Encoding().EncodeWithMaxLength(buf, &errMsg); err != nil {
    67  		return nil, err
    68  	}
    69  
    70  	return buf.Bytes(), nil
    71  }
    72  
    73  // reads data from the stream without applying any timeouts.
    74  func readStatusCodeNoDeadline(stream network.Stream, encoding encoder.NetworkEncoding) (uint8, string, error) {
    75  	b := make([]byte, 1)
    76  	_, err := stream.Read(b)
    77  	if err != nil {
    78  		return 0, "", err
    79  	}
    80  
    81  	if b[0] == responseCodeSuccess {
    82  		return 0, "", nil
    83  	}
    84  
    85  	msg := &p2ptypes.ErrorMessage{}
    86  	if err := encoding.DecodeWithMaxLength(stream, msg); err != nil {
    87  		return 0, "", err
    88  	}
    89  
    90  	return b[0], string(*msg), nil
    91  }
    92  
    93  // only returns true for errors that are valid (no resets or expectedEOF errors).
    94  func isValidStreamError(err error) bool {
    95  	// check the error message itself as well as libp2p doesn't currently
    96  	// return the correct error type from Close{Read,Write,}.
    97  	return err != nil && !errors.Is(err, network.ErrReset) && err.Error() != network.ErrReset.Error()
    98  }
    99  
   100  func closeStream(stream network.Stream) {
   101  	if err := stream.Close(); isValidStreamError(err) {
   102  		log.Debug(fmt.Sprintf("Could not reset stream with protocol %s", stream.Protocol()), "err", err)
   103  	}
   104  }
   105  
   106  func closeStreamAndWait(stream network.Stream) {
   107  	if err := stream.CloseWrite(); err != nil {
   108  		_err := stream.Reset()
   109  		_ = _err
   110  		if isValidStreamError(err) {
   111  			log.Debug(fmt.Sprintf("Could not reset stream with protocol %s", stream.Protocol()), "err", err)
   112  		}
   113  		return
   114  	}
   115  	// Wait for the remote side to respond.
   116  	//
   117  	// 1. On success, we expect to read an EOF (remote side received our
   118  	//    response and closed the stream.
   119  	// 2. On failure (e.g., disconnect), we expect to receive an error.
   120  	// 3. If the remote side misbehaves, we may receive data.
   121  	//
   122  	// However, regardless of what happens, we just close the stream and
   123  	// walk away. We only read to wait for a response, we close regardless.
   124  	_, _err := stream.Read([]byte{0})
   125  	_ = _err
   126  	_err = stream.Close()
   127  	_ = _err
   128  }