Skip to content

Commit 71a19e1

Browse files
committed
Service + request
1 parent e81dc87 commit 71a19e1

File tree

7 files changed

+551
-0
lines changed

7 files changed

+551
-0
lines changed

net/service/Makefile

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
all: request.pb.go
3+
4+
request.pb.go: request.proto
5+
protoc --gogo_out=. --proto_path=../../../../../:/usr/local/opt/protobuf/include:. $<
6+
7+
clean:
8+
rm request.pb.go

net/service/request.go

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

net/service/request.pb.go

+50
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

net/service/request.proto

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package service;
2+
3+
message PBRequest {
4+
required bytes Data = 1;
5+
optional bytes Tag = 3;
6+
}

net/service/request_test.go

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package service
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
)
7+
8+
func TestMarshaling(t *testing.T) {
9+
10+
test := func(d1 []byte, rid1 RequestID) {
11+
d2, err := wrapData(d1, rid1)
12+
if err != nil {
13+
t.Error(err)
14+
}
15+
16+
d3, rid2, err := unwrapData(d2)
17+
if err != nil {
18+
t.Error(err)
19+
}
20+
21+
d4, err := wrapData(d3, rid1)
22+
if err != nil {
23+
t.Error(err)
24+
}
25+
26+
if !bytes.Equal(rid2, rid1) {
27+
t.Error("RequestID fail")
28+
}
29+
30+
if !bytes.Equal(d1, d3) {
31+
t.Error("unmarshalled data should be the same")
32+
}
33+
34+
if !bytes.Equal(d2, d4) {
35+
t.Error("marshalled data should be the same")
36+
}
37+
}
38+
39+
test([]byte("foo"), []byte{1, 2, 3, 4})
40+
test([]byte("bar"), nil)
41+
}

0 commit comments

Comments
 (0)