github.com/zignig/go-ipfs@v0.0.0-20141111235910-c9e5fdf55a52/net/service/request.go (about)

     1  package service
     2  
     3  import (
     4  	crand "crypto/rand"
     5  
     6  	msg "github.com/jbenet/go-ipfs/net/message"
     7  	pb "github.com/jbenet/go-ipfs/net/service/internal/pb"
     8  	peer "github.com/jbenet/go-ipfs/peer"
     9  
    10  	proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto"
    11  )
    12  
    13  const (
    14  	// IDSize is the size of the ID in bytes.
    15  	IDSize int = 4
    16  )
    17  
    18  // RequestID is a field that identifies request-response flows.
    19  type RequestID []byte
    20  
    21  // Request turns a RequestID into a Request (unsetting first bit)
    22  func (r RequestID) Request() RequestID {
    23  	if r == nil {
    24  		return nil
    25  	}
    26  	r2 := make([]byte, len(r))
    27  	copy(r2, r)
    28  	r2[0] = r[0] & 0x7F // unset first bit for request
    29  	return RequestID(r2)
    30  }
    31  
    32  // Response turns a RequestID into a Response (setting first bit)
    33  func (r RequestID) Response() RequestID {
    34  	if r == nil {
    35  		return nil
    36  	}
    37  	r2 := make([]byte, len(r))
    38  	copy(r2, r)
    39  	r2[0] = r[0] | 0x80 // set first bit for response
    40  	return RequestID(r2)
    41  }
    42  
    43  // IsRequest returns whether a RequestID identifies a request
    44  func (r RequestID) IsRequest() bool {
    45  	if r == nil {
    46  		return false
    47  	}
    48  	return !r.IsResponse()
    49  }
    50  
    51  // IsResponse returns whether a RequestID identifies a response
    52  func (r RequestID) IsResponse() bool {
    53  	if r == nil {
    54  		return false
    55  	}
    56  	return bool(r[0]&0x80 == 0x80)
    57  }
    58  
    59  // RandomRequestID creates and returns a new random request ID
    60  func RandomRequestID() (RequestID, error) {
    61  	buf := make([]byte, IDSize)
    62  	_, err := crand.Read(buf)
    63  	return RequestID(buf).Request(), err
    64  }
    65  
    66  // RequestMap is a map of Requests. the key = (peer.ID concat RequestID).
    67  type RequestMap map[string]*Request
    68  
    69  // Request objects are used to multiplex request-response flows.
    70  type Request struct {
    71  
    72  	// ID is the RequestID identifying this Request-Response Flow.
    73  	ID RequestID
    74  
    75  	// PeerID identifies the peer from whom to expect the response.
    76  	PeerID peer.ID
    77  
    78  	// Response is the channel of incoming responses.
    79  	Response chan msg.NetMessage
    80  }
    81  
    82  // NewRequest creates a request for given peer.ID
    83  func NewRequest(pid peer.ID) (*Request, error) {
    84  	id, err := RandomRequestID()
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  
    89  	return &Request{
    90  		ID:       id,
    91  		PeerID:   pid,
    92  		Response: make(chan msg.NetMessage, 1),
    93  	}, nil
    94  }
    95  
    96  // Key returns the RequestKey for this request. Use with maps.
    97  func (r *Request) Key() string {
    98  	return RequestKey(r.PeerID, r.ID)
    99  }
   100  
   101  // RequestKey is the peer.ID concatenated with the RequestID. Use with maps.
   102  func RequestKey(pid peer.ID, rid RequestID) string {
   103  	return string(pid) + string(rid.Request()[:])
   104  }
   105  
   106  func wrapData(data []byte, rid RequestID) ([]byte, error) {
   107  	// Marshal
   108  	pbm := new(pb.PBRequest)
   109  	pbm.Data = data
   110  	pbm.Tag = rid
   111  	b, err := proto.Marshal(pbm)
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  
   116  	return b, nil
   117  }
   118  
   119  func unwrapData(data []byte) ([]byte, RequestID, error) {
   120  	// Unmarshal
   121  	pbm := new(pb.PBRequest)
   122  	err := proto.Unmarshal(data, pbm)
   123  	if err != nil {
   124  		return nil, nil, err
   125  	}
   126  
   127  	return pbm.GetData(), pbm.GetTag(), nil
   128  }