github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/p2p/p2putil/message.go (about)

     1  package p2putil
     2  
     3  import (
     4  	"encoding/json"
     5  	"math/rand"
     6  	"time"
     7  
     8  	peer "github.com/libp2p/go-libp2p-core/peer"
     9  )
    10  
    11  // MsgType indicates the type of message being sent
    12  type MsgType string
    13  
    14  // String implements the Stringer interface for MsgType
    15  func (mt MsgType) String() string {
    16  	return string(mt)
    17  }
    18  
    19  // Message is a serializable/encodable object that we send & receive on a Stream.
    20  type Message struct {
    21  	Type     MsgType
    22  	ID       string
    23  	Created  time.Time
    24  	Deadline time.Time
    25  	// peer that originated this message
    26  	Initiator peer.ID
    27  	// Headers proxies the concept of HTTP headers, but with no
    28  	// mandatory fields. It's intended to be small & simple on purpose
    29  	// In the future we can upgrade this to map[string]interface{} while keeping
    30  	// backward compatibility
    31  	Headers map[string]string
    32  	// Body carries the payload of a message, if any
    33  	Body []byte
    34  	// provider is who sent this message
    35  	// not transmitted over the wire, but
    36  	// instead populated by WrapStream
    37  	provider peer.ID
    38  }
    39  
    40  // Update returns a new message with an updated body
    41  func (m Message) Update(body []byte) Message {
    42  	return Message{
    43  		Type:      m.Type,
    44  		ID:        m.ID,
    45  		Headers:   m.Headers,
    46  		Created:   m.Created,
    47  		Deadline:  m.Deadline,
    48  		Initiator: m.Initiator,
    49  		Body:      body,
    50  	}
    51  }
    52  
    53  // UpdateJSON updates a messages by JSON-encoding a body
    54  func (m Message) UpdateJSON(body interface{}) (Message, error) {
    55  	data, err := json.Marshal(body)
    56  	return m.Update(data), err
    57  }
    58  
    59  // NewMessage creates a message. provided initiator should always be the peerID
    60  // of the local node
    61  func NewMessage(initiator peer.ID, t MsgType, body []byte) Message {
    62  	return Message{
    63  		ID:        NewMessageID(),
    64  		Initiator: initiator,
    65  		Created:   time.Now(),
    66  		Deadline:  time.Now().Add(time.Minute * 2),
    67  		Type:      t,
    68  		Headers:   map[string]string{},
    69  		Body:      body,
    70  	}
    71  }
    72  
    73  // WithHeaders adds a sequence of key,value,key,value as headers
    74  func (m Message) WithHeaders(keyval ...string) Message {
    75  	headers := map[string]string{}
    76  	for i := 0; i < len(keyval)-1; i = i + 2 {
    77  		headers[keyval[i]] = keyval[i+1]
    78  	}
    79  	return Message{
    80  		ID:        m.ID,
    81  		Initiator: m.Initiator,
    82  		Type:      m.Type,
    83  		Headers:   headers,
    84  		Body:      m.Body,
    85  	}
    86  }
    87  
    88  // Header gets a header value for a given key
    89  func (m Message) Header(key string) (value string) {
    90  	if m.Headers == nil {
    91  		return ""
    92  	}
    93  	return m.Headers[key]
    94  }
    95  
    96  // NewJSONBodyMessage is a convenience wrapper for json-encoding a message
    97  func NewJSONBodyMessage(initiator peer.ID, t MsgType, body interface{}) (Message, error) {
    98  	data, err := json.Marshal(body)
    99  	return NewMessage(initiator, t, data), err
   100  }
   101  
   102  var alpharunes = []rune("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
   103  
   104  // NewMessageID generates a random message identifier
   105  // TODO - replace with UUIDs
   106  func NewMessageID() string {
   107  	b := make([]rune, 10)
   108  	for i := range b {
   109  		b[i] = alpharunes[rand.Intn(len(alpharunes))]
   110  	}
   111  	return string(b)
   112  }