Commit 6ed3d545 authored by Jeromy's avatar Jeromy

switch over to protobuf rpc

parent 13a46da4
package floodsub package floodsub
import ( import (
"bufio"
"encoding/base64"
"encoding/json"
"fmt" "fmt"
"sync" "sync"
"sync/atomic"
"time" "time"
peer "github.com/ipfs/go-libp2p-peer" pb "github.com/whyrusleeping/go-floodsub/pb"
logging "github.com/ipfs/go-log"
host "github.com/libp2p/go-libp2p/p2p/host" ggio "github.com/gogo/protobuf/io"
inet "github.com/libp2p/go-libp2p/p2p/net" proto "github.com/gogo/protobuf/proto"
protocol "github.com/libp2p/go-libp2p/p2p/protocol" logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log"
peer "gx/ipfs/QmWtbQU15LaB5B1JC2F7TV9P4K88vD3PpA4AJrwfCjhML8/go-libp2p-peer"
host "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/host"
inet "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/net"
protocol "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/protocol"
) )
var messageCount uint64
const ID = protocol.ID("/floodsub/1.0.0") const ID = protocol.ID("/floodsub/1.0.0")
const ( var (
AddSubMessageType = "sub" AddSubMessageType = "sub"
UnsubMessageType = "unsub" UnsubMessageType = "unsub"
PubMessageType = "pub" PubMessageType = "pub"
...@@ -44,49 +48,15 @@ type PubSub struct { ...@@ -44,49 +48,15 @@ type PubSub struct {
} }
type Message struct { type Message struct {
From peer.ID *pb.Message
Data []byte
Timestamp uint64
Topic string
}
func (m *Message) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]interface{}{
"from": base64.RawStdEncoding.EncodeToString([]byte(m.From)),
"data": m.Data,
"timestamp": m.Timestamp,
"topic": m.Topic,
})
} }
func (m *Message) UnmarshalJSON(data []byte) error { func (m *Message) GetFrom() peer.ID {
mp := struct { return peer.ID(m.Message.GetFrom())
Data []byte
Timestamp uint64
Topic string
From string
}{}
err := json.Unmarshal(data, &mp)
if err != nil {
return err
}
pid, err := base64.RawStdEncoding.DecodeString(mp.From)
if err != nil {
return err
}
m.Data = mp.Data
m.Timestamp = mp.Timestamp
m.Topic = mp.Topic
m.From = peer.ID(pid)
return nil
} }
type RPC struct { type RPC struct {
Type string pb.RPC
Msg *Message
Topics []string
// unexported on purpose, not sending this over the wire // unexported on purpose, not sending this over the wire
from peer.ID from peer.ID
...@@ -98,16 +68,15 @@ func NewFloodSub(h host.Host) *PubSub { ...@@ -98,16 +68,15 @@ func NewFloodSub(h host.Host) *PubSub {
incoming: make(chan *RPC, 32), incoming: make(chan *RPC, 32),
outgoing: make(chan *RPC), outgoing: make(chan *RPC),
newPeers: make(chan inet.Stream), newPeers: make(chan inet.Stream),
peerDead: make(chan peer.ID),
addSub: make(chan *addSub),
myTopics: make(map[string]chan *Message), myTopics: make(map[string]chan *Message),
topics: make(map[string]map[peer.ID]struct{}), topics: make(map[string]map[peer.ID]struct{}),
peers: make(map[peer.ID]chan *RPC), peers: make(map[peer.ID]chan *RPC),
lastMsg: make(map[peer.ID]uint64), lastMsg: make(map[peer.ID]uint64),
peerDead: make(chan peer.ID),
addSub: make(chan *addSub),
} }
h.SetStreamHandler(ID, ps.handleNewStream) h.SetStreamHandler(ID, ps.handleNewStream)
h.Network().Notify(ps) h.Network().Notify(ps)
go ps.processLoop() go ps.processLoop()
...@@ -120,21 +89,19 @@ func (p *PubSub) getHelloPacket() *RPC { ...@@ -120,21 +89,19 @@ func (p *PubSub) getHelloPacket() *RPC {
for t, _ := range p.myTopics { for t, _ := range p.myTopics {
rpc.Topics = append(rpc.Topics, t) rpc.Topics = append(rpc.Topics, t)
} }
rpc.Type = AddSubMessageType rpc.Type = &AddSubMessageType
return &rpc return &rpc
} }
func (p *PubSub) handleNewStream(s inet.Stream) { func (p *PubSub) handleNewStream(s inet.Stream) {
defer s.Close() defer s.Close()
scan := bufio.NewScanner(s) r := ggio.NewDelimitedReader(s, 1<<20)
for scan.Scan() { for {
rpc := new(RPC) rpc := new(RPC)
err := r.ReadMsg(&rpc.RPC)
err := json.Unmarshal(scan.Bytes(), rpc)
if err != nil { if err != nil {
log.Errorf("error reading rpc from %s: %s", s.Conn().RemotePeer(), err) log.Errorf("error reading rpc from %s: %s", s.Conn().RemotePeer(), err)
log.Error("data: ", scan.Text())
// TODO: cleanup of some sort // TODO: cleanup of some sort
return return
} }
...@@ -146,12 +113,16 @@ func (p *PubSub) handleNewStream(s inet.Stream) { ...@@ -146,12 +113,16 @@ func (p *PubSub) handleNewStream(s inet.Stream) {
func (p *PubSub) handleSendingMessages(s inet.Stream, in <-chan *RPC) { func (p *PubSub) handleSendingMessages(s inet.Stream, in <-chan *RPC) {
var dead bool var dead bool
wc := ggio.NewDelimitedWriter(s)
defer wc.Close()
for rpc := range in { for rpc := range in {
if dead { if dead {
continue continue
} }
err := writeRPC(s, rpc) atomic.AddUint64(&messageCount, 1)
err := wc.WriteMsg(&rpc.RPC)
if err != nil { if err != nil {
log.Errorf("writing message to %s: %s", s.Conn().RemotePeer(), err) log.Errorf("writing message to %s: %s", s.Conn().RemotePeer(), err)
dead = true dead = true
...@@ -192,7 +163,7 @@ func (p *PubSub) processLoop() { ...@@ -192,7 +163,7 @@ func (p *PubSub) processLoop() {
log.Error("handling RPC: ", err) log.Error("handling RPC: ", err)
} }
case rpc := <-p.outgoing: case rpc := <-p.outgoing:
switch rpc.Type { switch rpc.GetType() {
case AddSubMessageType, UnsubMessageType: case AddSubMessageType, UnsubMessageType:
for _, mch := range p.peers { for _, mch := range p.peers {
mch <- rpc mch <- rpc
...@@ -215,7 +186,9 @@ func (p *PubSub) processLoop() { ...@@ -215,7 +186,9 @@ func (p *PubSub) processLoop() {
func (p *PubSub) handleSubscriptionChange(sub *addSub) { func (p *PubSub) handleSubscriptionChange(sub *addSub) {
ch, ok := p.myTopics[sub.topic] ch, ok := p.myTopics[sub.topic]
out := &RPC{ out := &RPC{
Topics: []string{sub.topic}, RPC: pb.RPC{
Topics: []string{sub.topic},
},
} }
if sub.cancel { if sub.cancel {
...@@ -225,7 +198,7 @@ func (p *PubSub) handleSubscriptionChange(sub *addSub) { ...@@ -225,7 +198,7 @@ func (p *PubSub) handleSubscriptionChange(sub *addSub) {
close(ch) close(ch)
delete(p.myTopics, sub.topic) delete(p.myTopics, sub.topic)
out.Type = UnsubMessageType out.Type = &UnsubMessageType
} else { } else {
if ok { if ok {
// we don't allow multiple subs per topic at this point // we don't allow multiple subs per topic at this point
...@@ -236,7 +209,7 @@ func (p *PubSub) handleSubscriptionChange(sub *addSub) { ...@@ -236,7 +209,7 @@ func (p *PubSub) handleSubscriptionChange(sub *addSub) {
resp := make(chan *Message, 16) resp := make(chan *Message, 16)
p.myTopics[sub.topic] = resp p.myTopics[sub.topic] = resp
sub.resp <- resp sub.resp <- resp
out.Type = AddSubMessageType out.Type = &AddSubMessageType
} }
go func() { go func() {
...@@ -245,16 +218,16 @@ func (p *PubSub) handleSubscriptionChange(sub *addSub) { ...@@ -245,16 +218,16 @@ func (p *PubSub) handleSubscriptionChange(sub *addSub) {
} }
func (p *PubSub) recvMessage(rpc *RPC) error { func (p *PubSub) recvMessage(rpc *RPC) error {
subch, ok := p.myTopics[rpc.Msg.Topic] subch, ok := p.myTopics[rpc.Msg.GetTopic()]
if ok { if ok {
//fmt.Println("writing out to subscriber!") //fmt.Println("writing out to subscriber!")
subch <- rpc.Msg subch <- &Message{rpc.Msg}
} }
return nil return nil
} }
func (p *PubSub) handleIncomingRPC(rpc *RPC) error { func (p *PubSub) handleIncomingRPC(rpc *RPC) error {
switch rpc.Type { switch rpc.GetType() {
case AddSubMessageType: case AddSubMessageType:
for _, t := range rpc.Topics { for _, t := range rpc.Topics {
tmap, ok := p.topics[t] tmap, ok := p.topics[t]
...@@ -278,19 +251,22 @@ func (p *PubSub) handleIncomingRPC(rpc *RPC) error { ...@@ -278,19 +251,22 @@ func (p *PubSub) handleIncomingRPC(rpc *RPC) error {
return fmt.Errorf("nil pub message") return fmt.Errorf("nil pub message")
} }
msg := &Message{rpc.Msg}
// Note: Obviously this is an incredibly insecure way of // Note: Obviously this is an incredibly insecure way of
// filtering out "messages we've already seen". But it works for a // filtering out "messages we've already seen". But it works for a
// cool demo, so i'm not gonna waste time thinking about it any more // cool demo, so i'm not gonna waste time thinking about it any more
if p.lastMsg[rpc.Msg.From] >= rpc.Msg.Timestamp { if p.lastMsg[msg.GetFrom()] >= msg.GetSeqno() {
//log.Error("skipping 'old' message") //log.Error("skipping 'old' message")
return nil return nil
} }
if rpc.Msg.From == p.host.ID() { if msg.GetFrom() == p.host.ID() {
log.Error("skipping message from self")
return nil return nil
} }
p.lastMsg[rpc.Msg.From] = rpc.Msg.Timestamp p.lastMsg[msg.GetFrom()] = msg.GetSeqno()
if err := p.recvMessage(rpc); err != nil { if err := p.recvMessage(rpc); err != nil {
log.Error("error receiving message: ", err) log.Error("error receiving message: ", err)
...@@ -305,13 +281,13 @@ func (p *PubSub) handleIncomingRPC(rpc *RPC) error { ...@@ -305,13 +281,13 @@ func (p *PubSub) handleIncomingRPC(rpc *RPC) error {
} }
func (p *PubSub) publishMessage(rpc *RPC) error { func (p *PubSub) publishMessage(rpc *RPC) error {
tmap, ok := p.topics[rpc.Msg.Topic] tmap, ok := p.topics[rpc.Msg.GetTopic()]
if !ok { if !ok {
return nil return nil
} }
for pid, _ := range tmap { for pid, _ := range tmap {
if pid == rpc.from || pid == rpc.Msg.From { if pid == rpc.from || pid == peer.ID(rpc.Msg.GetFrom()) {
continue continue
} }
...@@ -355,25 +331,17 @@ func (p *PubSub) Unsub(topic string) { ...@@ -355,25 +331,17 @@ func (p *PubSub) Unsub(topic string) {
} }
func (p *PubSub) Publish(topic string, data []byte) error { func (p *PubSub) Publish(topic string, data []byte) error {
seqno := uint64(time.Now().UnixNano())
p.outgoing <- &RPC{ p.outgoing <- &RPC{
Msg: &Message{ RPC: pb.RPC{
Data: data, Msg: &pb.Message{
Topic: topic, Data: data,
From: p.host.ID(), Topic: &topic,
Timestamp: uint64(time.Now().UnixNano()), From: proto.String(string(p.host.ID())),
Seqno: &seqno,
},
Type: &PubMessageType,
}, },
Type: PubMessageType,
} }
return nil return nil
} }
func writeRPC(s inet.Stream, rpc *RPC) error {
data, err := json.Marshal(rpc)
if err != nil {
return err
}
data = append(data, '\n')
_, err = s.Write(data)
return err
}
...@@ -8,8 +8,8 @@ import ( ...@@ -8,8 +8,8 @@ import (
"testing" "testing"
"time" "time"
host "github.com/libp2p/go-libp2p/p2p/host" host "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/host"
netutil "github.com/libp2p/go-libp2p/p2p/test/util" netutil "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/test/util"
) )
func getNetHosts(t *testing.T, n int) []host.Host { func getNetHosts(t *testing.T, n int) []host.Host {
...@@ -94,4 +94,6 @@ func TestBasicFloodsub(t *testing.T) { ...@@ -94,4 +94,6 @@ func TestBasicFloodsub(t *testing.T) {
} }
} }
} }
fmt.Println("Total Sent Messages: ", messageCount)
} }
...@@ -3,8 +3,8 @@ package floodsub ...@@ -3,8 +3,8 @@ package floodsub
import ( import (
"context" "context"
ma "github.com/jbenet/go-multiaddr" ma "gx/ipfs/QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd/go-multiaddr"
inet "github.com/libp2p/go-libp2p/p2p/net" inet "gx/ipfs/Qmf4ETeAWXuThBfWwonVyFqGFSgTWepUDEr1txcctvpTXS/go-libp2p/p2p/net"
) )
var _ inet.Notifiee = (*PubSub)(nil) var _ inet.Notifiee = (*PubSub)(nil)
......
// Code generated by protoc-gen-gogo.
// source: rpc.proto
// DO NOT EDIT!
/*
Package floodsub_pb is a generated protocol buffer package.
It is generated from these files:
rpc.proto
It has these top-level messages:
RPC
Message
*/
package floodsub_pb
import proto "github.com/gogo/protobuf/proto"
import fmt "fmt"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
type RPC struct {
Type *string `protobuf:"bytes,1,opt,name=type" json:"type,omitempty"`
Topics []string `protobuf:"bytes,2,rep,name=topics" json:"topics,omitempty"`
Msg *Message `protobuf:"bytes,3,opt,name=msg" json:"msg,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *RPC) Reset() { *m = RPC{} }
func (m *RPC) String() string { return proto.CompactTextString(m) }
func (*RPC) ProtoMessage() {}
func (*RPC) Descriptor() ([]byte, []int) { return fileDescriptorRpc, []int{0} }
func (m *RPC) GetType() string {
if m != nil && m.Type != nil {
return *m.Type
}
return ""
}
func (m *RPC) GetTopics() []string {
if m != nil {
return m.Topics
}
return nil
}
func (m *RPC) GetMsg() *Message {
if m != nil {
return m.Msg
}
return nil
}
type Message struct {
From *string `protobuf:"bytes,1,opt,name=from" json:"from,omitempty"`
Data []byte `protobuf:"bytes,2,opt,name=data" json:"data,omitempty"`
Seqno *uint64 `protobuf:"varint,3,opt,name=seqno" json:"seqno,omitempty"`
Topic *string `protobuf:"bytes,4,opt,name=topic" json:"topic,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *Message) Reset() { *m = Message{} }
func (m *Message) String() string { return proto.CompactTextString(m) }
func (*Message) ProtoMessage() {}
func (*Message) Descriptor() ([]byte, []int) { return fileDescriptorRpc, []int{1} }
func (m *Message) GetFrom() string {
if m != nil && m.From != nil {
return *m.From
}
return ""
}
func (m *Message) GetData() []byte {
if m != nil {
return m.Data
}
return nil
}
func (m *Message) GetSeqno() uint64 {
if m != nil && m.Seqno != nil {
return *m.Seqno
}
return 0
}
func (m *Message) GetTopic() string {
if m != nil && m.Topic != nil {
return *m.Topic
}
return ""
}
func init() {
proto.RegisterType((*RPC)(nil), "floodsub.pb.RPC")
proto.RegisterType((*Message)(nil), "floodsub.pb.Message")
}
func init() { proto.RegisterFile("rpc.proto", fileDescriptorRpc) }
var fileDescriptorRpc = []byte{
// 158 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0x2c, 0x2a, 0x48, 0xd6,
0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x4e, 0xcb, 0xc9, 0xcf, 0x4f, 0x29, 0x2e, 0x4d, 0xd2,
0x2b, 0x48, 0x52, 0x72, 0xe3, 0x62, 0x0e, 0x0a, 0x70, 0x16, 0xe2, 0xe1, 0x62, 0x29, 0xa9, 0x2c,
0x48, 0x95, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x14, 0xe2, 0xe3, 0x62, 0x2b, 0xc9, 0x2f, 0xc8, 0x4c,
0x2e, 0x96, 0x60, 0x52, 0x60, 0xd6, 0xe0, 0x14, 0x52, 0xe4, 0x62, 0xce, 0x2d, 0x4e, 0x97, 0x60,
0x56, 0x60, 0xd4, 0xe0, 0x36, 0x12, 0xd1, 0x43, 0xd2, 0xaf, 0xe7, 0x9b, 0x5a, 0x5c, 0x9c, 0x98,
0x9e, 0xaa, 0xe4, 0xcc, 0xc5, 0x0e, 0x65, 0x82, 0xcc, 0x4a, 0x2b, 0xca, 0xcf, 0x85, 0x9a, 0xc5,
0xc3, 0xc5, 0x92, 0x92, 0x58, 0x92, 0x28, 0xc1, 0xa4, 0xc0, 0xa8, 0xc1, 0x23, 0xc4, 0xcb, 0xc5,
0x5a, 0x9c, 0x5a, 0x98, 0x97, 0x0f, 0x36, 0x8b, 0x05, 0xc4, 0x05, 0x5b, 0x24, 0xc1, 0x02, 0x52,
0x0b, 0x08, 0x00, 0x00, 0xff, 0xff, 0x08, 0xfe, 0xc0, 0xc1, 0xa5, 0x00, 0x00, 0x00,
}
package floodsub.pb;
message RPC {
optional string type = 1;
repeated string topics = 2;
optional Message msg = 3;
}
message Message {
optional string from = 1;
optional bytes data = 2;
optional uint64 seqno = 3;
optional string topic = 4;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment