Initial commit
This commit is contained in:
167
utils/client.go
Normal file
167
utils/client.go
Normal file
@@ -0,0 +1,167 @@
|
||||
// Package utils provides utilities that are needed for the functioning of Kerma
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"kerma/helpers"
|
||||
"kerma/models"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/docker/go/canonical/json"
|
||||
)
|
||||
|
||||
/*
|
||||
A Client represents a Kerma client with the following properties:
|
||||
- State - the current state of the application
|
||||
- Handler - a TCPHandler that is used to handle requests to and from remote peers
|
||||
- Logger
|
||||
*/
|
||||
type Client struct {
|
||||
State *models.State
|
||||
Handler TCPHandler
|
||||
Logger helpers.Logger
|
||||
}
|
||||
|
||||
/*
|
||||
Construct creates a new client with the specified state and handler objects
|
||||
*/
|
||||
func (c *Client) Construct(state *models.State, h TCPHandler) {
|
||||
c.State = state
|
||||
c.Handler = h
|
||||
}
|
||||
|
||||
/*
|
||||
InitHandshake initiates a handshake on the specified connection
|
||||
*/
|
||||
func (c *Client) InitHandshake(peerName string) {
|
||||
var resp models.Hello
|
||||
resp.Construct()
|
||||
respJSON, _ := json.MarshalCanonical(resp)
|
||||
|
||||
c.Handler.Output(respJSON)
|
||||
c.Handler.Conn.SetDeadline(time.Now().Add(time.Duration(c.State.Config.ConnTimeout) * time.Second))
|
||||
in, err := bufio.NewReader(c.Handler.Conn).ReadBytes('\n')
|
||||
if err != nil {
|
||||
c.Logger.Error(err.Error())
|
||||
if errors.Is(err, net.ErrClosed) {
|
||||
c.State.RemovePeerByName(peerName)
|
||||
} else {
|
||||
c.Handler.Fail("Failed reading message")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
msg, err := c.Handler.Input(in)
|
||||
if err != nil {
|
||||
c.Logger.Error(err.Error())
|
||||
c.Handler.Fail("Failed parsing message")
|
||||
return
|
||||
}
|
||||
|
||||
msgJSON, err := json.MarshalCanonical(msg)
|
||||
if err != nil {
|
||||
c.Logger.Error("Failed parsing message " + err.Error())
|
||||
c.Handler.Fail("Failed parsing message")
|
||||
return
|
||||
}
|
||||
|
||||
if msgType, ok := msg["type"]; ok {
|
||||
if msgType == "hello" {
|
||||
var helloReq models.Hello
|
||||
err := helloReq.UnmarshalJSON(msgJSON)
|
||||
|
||||
if err == nil {
|
||||
c.Logger.Info("Constructing handshake with " + c.Handler.GetRemotePeer())
|
||||
c.State.CompleteHandshake(peerName)
|
||||
} else {
|
||||
c.Handler.Fail("Hello response invalid")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
c.Handler.Fail("Response type not expected " + msgType.(string))
|
||||
return
|
||||
}
|
||||
} else {
|
||||
c.Handler.Fail("Response type not specified")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
DiscoverPeers requests remote peers from a known peer
|
||||
*/
|
||||
func (c *Client) DiscoverPeers(peerName string) {
|
||||
if c.State.CheckForHandshake(peerName) {
|
||||
var req models.Generic
|
||||
req.BuildPeerRequest()
|
||||
reqJSON, _ := req.MarshalJson()
|
||||
c.Handler.Output(reqJSON)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
DiscoverChainTip requests chaintip from a known peer
|
||||
*/
|
||||
func (c *Client) DiscoverChainTip(peerName string) {
|
||||
if c.State.CheckForHandshake(peerName) {
|
||||
var req models.Generic
|
||||
req.BuildChainTipRequest()
|
||||
reqJSON, _ := req.MarshalJson()
|
||||
c.Handler.Output(reqJSON)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
DiscoverMempool requests mempool from a known peer
|
||||
*/
|
||||
func (c *Client) DiscoverMempool(peerName string) {
|
||||
if c.State.CheckForHandshake(peerName) {
|
||||
var req models.Generic
|
||||
req.BuildMempoolRequest()
|
||||
reqJSON, _ := req.MarshalJson()
|
||||
c.Handler.Output(reqJSON)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
DiscoverObject requests remote objects from a known peer
|
||||
*/
|
||||
func (c *Client) DiscoverObject(peerName string, objectID string) {
|
||||
if c.State.CheckForHandshake(peerName) {
|
||||
var req models.ObjectWrapper
|
||||
req.BuildObjectRequest(objectID)
|
||||
reqJSON, _ := req.MarshalJson()
|
||||
c.Handler.Output(reqJSON)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
GossipBlocks sends information about local blocks to peer
|
||||
*/
|
||||
func (c *Client) GossipBlocks(peerName string) {
|
||||
if c.State.CheckForHandshake(peerName) {
|
||||
for _, block := range c.State.Chain {
|
||||
var req models.ObjectWrapper
|
||||
bid := block.GetID()
|
||||
req.BuildGossipObject(bid)
|
||||
reqJSON, _ := req.MarshalJson()
|
||||
c.Handler.Output(reqJSON)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
GossipTransactions sends information about local transactions to peer
|
||||
*/
|
||||
func (c *Client) GossipTransactions(peerName string) {
|
||||
if c.State.CheckForHandshake(peerName) {
|
||||
for txid := range c.State.Transactions {
|
||||
var req models.ObjectWrapper
|
||||
req.BuildGossipObject(txid)
|
||||
reqJSON, _ := req.MarshalJson()
|
||||
c.Handler.Output(reqJSON)
|
||||
}
|
||||
}
|
||||
}
|
89
utils/handler.go
Normal file
89
utils/handler.go
Normal file
@@ -0,0 +1,89 @@
|
||||
// Package utils provides utilities that are needed for the functioning of Kerma
|
||||
package utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"kerma/helpers"
|
||||
"kerma/models"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/docker/go/canonical/json"
|
||||
)
|
||||
|
||||
/*
|
||||
A TCPHandler is a struct used for communicating with other Kerma nodes/clients
|
||||
It consists of:
|
||||
- Conn - a connection for sending and receiving requests
|
||||
- Logger
|
||||
- Config - the configuration of the application
|
||||
*/
|
||||
type TCPHandler struct {
|
||||
Conn net.Conn
|
||||
Logger helpers.Logger
|
||||
Config helpers.Config
|
||||
}
|
||||
|
||||
/*
|
||||
Construct creates a new handler on the specified connection
|
||||
*/
|
||||
func (h *TCPHandler) Construct(conn net.Conn) {
|
||||
h.Conn = conn
|
||||
h.Config.Construct()
|
||||
}
|
||||
|
||||
/*
|
||||
Output handles outgoing data in a canonical JSON format
|
||||
*/
|
||||
func (h *TCPHandler) Output(respJSON []byte) {
|
||||
h.Logger.Info("OUT: " + h.GetRemotePeer() + " " + string(respJSON))
|
||||
respJSON = append(respJSON, '\n')
|
||||
h.Conn.Write(respJSON)
|
||||
}
|
||||
|
||||
/*
|
||||
Error handles outgoing errors in a canonical JSON format
|
||||
*/
|
||||
func (h *TCPHandler) Error(errorMsg string) {
|
||||
var resp models.Error
|
||||
resp.Construct(errorMsg)
|
||||
respJSON, _ := resp.MarshalJson()
|
||||
h.Output(respJSON)
|
||||
}
|
||||
|
||||
/*
|
||||
Fail handles outgoing errors in a canonical JSON format and terminates the connection
|
||||
*/
|
||||
func (h *TCPHandler) Fail(errorMsg string) {
|
||||
h.Error(errorMsg)
|
||||
h.Conn.Close()
|
||||
}
|
||||
|
||||
/*
|
||||
Input handles input data in a canonical JSON format
|
||||
*/
|
||||
func (h *TCPHandler) Input(in []byte) (map[string](interface{}), error) {
|
||||
var msg map[string](interface{})
|
||||
|
||||
err := json.Unmarshal(in, &msg)
|
||||
if err != nil {
|
||||
h.Logger.Error("Failed decoding message " + err.Error())
|
||||
return nil, errors.New("failed decoding message")
|
||||
}
|
||||
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
/*
|
||||
GetRemotePeer returns tha address of the remote peer as a string
|
||||
*/
|
||||
func (h *TCPHandler) GetRemotePeer() string {
|
||||
return h.Conn.RemoteAddr().String()
|
||||
}
|
||||
|
||||
/*
|
||||
GetLocalPeer returns the address + port combination of the local peer as a string
|
||||
*/
|
||||
func (h *TCPHandler) GetLocalPeer() string {
|
||||
return h.Config.IPAddress + ":" + strconv.Itoa(h.Config.Port)
|
||||
}
|
352
utils/server.go
Normal file
352
utils/server.go
Normal file
@@ -0,0 +1,352 @@
|
||||
// Package utils provides utilities that are needed for the functioning of Kerma
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"kerma/helpers"
|
||||
"kerma/models"
|
||||
"time"
|
||||
|
||||
"github.com/docker/go/canonical/json"
|
||||
)
|
||||
|
||||
/*
|
||||
A Server represents a Kerma server with the following properties:
|
||||
- State - the current state of the application
|
||||
- Handler - a TCPHandler that is used to handle requests to and from remote peers
|
||||
- Logger
|
||||
- Client - a new client that uses the same state and handler as this server
|
||||
*/
|
||||
type Server struct {
|
||||
State *models.State
|
||||
Handler TCPHandler
|
||||
Logger helpers.Logger
|
||||
Client Client
|
||||
}
|
||||
|
||||
/*
|
||||
Construct creates a new server with the specified state and handler objects
|
||||
*/
|
||||
func (s *Server) Construct(state *models.State, h TCPHandler) {
|
||||
s.State = state
|
||||
s.Handler = h
|
||||
s.Client.Construct(s.State, s.Handler)
|
||||
}
|
||||
|
||||
/*
|
||||
Handle is responsible for handling requests from other Kerma nodes/clients
|
||||
*/
|
||||
func (s *Server) Handle() {
|
||||
// Decode message received from reader, fail on error
|
||||
|
||||
scanner := bufio.NewScanner(s.Handler.Conn)
|
||||
|
||||
for scanner.Scan() {
|
||||
msg, err := s.Handler.Input(scanner.Bytes())
|
||||
if err != nil {
|
||||
s.Handler.Fail(err.Error())
|
||||
}
|
||||
|
||||
// Parse received message, fail on error
|
||||
msgJSON, err := json.MarshalCanonical(msg)
|
||||
if err != nil {
|
||||
s.Logger.Error("Failed parsing message " + err.Error())
|
||||
s.Handler.Fail("Failed parsing message")
|
||||
}
|
||||
|
||||
s.Logger.Info("IN: " + s.Handler.GetRemotePeer() + " " + string(msgJSON))
|
||||
|
||||
remotePeer := s.Handler.GetRemotePeer()
|
||||
|
||||
// check message type, proceed accordingly
|
||||
if msgType, ok := msg["type"]; ok {
|
||||
switch msgType {
|
||||
case "error":
|
||||
break
|
||||
case "hello":
|
||||
// check if peer has already handshaked
|
||||
if !s.State.CheckForHandshake(remotePeer) {
|
||||
// check if hello request is valid
|
||||
var helloReq models.Hello
|
||||
err := helloReq.UnmarshalJSON(msgJSON)
|
||||
|
||||
if err == nil {
|
||||
// finish handshake
|
||||
s.Logger.Info("Constructing handshake with " + remotePeer)
|
||||
var resp models.Hello
|
||||
resp.Construct()
|
||||
respJSON, _ := resp.MarshalJson()
|
||||
s.Handler.Output(respJSON)
|
||||
s.State.CompleteHandshake(remotePeer)
|
||||
s.Client.DiscoverPeers(remotePeer)
|
||||
} else {
|
||||
s.Handler.Fail("Hello request invalid")
|
||||
}
|
||||
}
|
||||
break
|
||||
case "getpeers":
|
||||
err := s.terminateIfNoHandshake(remotePeer)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
// create list of known peers, send to requester
|
||||
resp := s.State.FetchPeerListResponse()
|
||||
resp.Peers = append(resp.Peers, s.Handler.GetLocalPeer())
|
||||
respJSON, _ := resp.MarshalJson()
|
||||
s.Handler.Output(respJSON)
|
||||
break
|
||||
case "peers":
|
||||
err := s.terminateIfNoHandshake(remotePeer)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
var peers models.PeerListResponse
|
||||
json.Unmarshal(msgJSON, &peers)
|
||||
s.State.ParsePeerListResponse(&peers)
|
||||
break
|
||||
case "ihaveobject":
|
||||
err := s.terminateIfNoHandshake(remotePeer)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
var obj models.ObjectWrapper
|
||||
err = obj.UnmarshalJSON(msgJSON)
|
||||
if err != nil {
|
||||
s.Handler.Fail("Could not parse request")
|
||||
break
|
||||
}
|
||||
|
||||
if s.State.Transactions[obj.ObjectID] == nil {
|
||||
resp := models.ObjectWrapper{
|
||||
Type: "getobject",
|
||||
ObjectID: obj.ObjectID,
|
||||
}
|
||||
|
||||
respJSON, _ := resp.MarshalJson()
|
||||
s.Handler.Output(respJSON)
|
||||
}
|
||||
case "getobject":
|
||||
err = s.terminateIfNoHandshake(remotePeer)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
var obj models.ObjectWrapper
|
||||
err := obj.UnmarshalJSON(msgJSON)
|
||||
if err != nil {
|
||||
s.Handler.Fail("Could not parse request")
|
||||
break
|
||||
}
|
||||
|
||||
transaction := s.State.GetTransaction(obj.ObjectID)
|
||||
if transaction != nil {
|
||||
s.Logger.Debug("Found transaction: " + obj.ObjectID)
|
||||
resp := json.RawMessage(`{"type":"object","object":` + transaction.String() + `}`)
|
||||
|
||||
respJSON, _ := resp.MarshalJSON()
|
||||
s.Handler.Output(respJSON)
|
||||
}
|
||||
|
||||
block := s.State.GetBlock(obj.ObjectID)
|
||||
if block != nil {
|
||||
s.Logger.Debug("Found block: " + obj.ObjectID)
|
||||
resp := json.RawMessage(`{"type":"object","object":` + block.String() + `}`)
|
||||
|
||||
respJSON, _ := resp.MarshalJSON()
|
||||
s.Handler.Output(respJSON)
|
||||
} else {
|
||||
s.Handler.Error("Could not find object " + obj.ObjectID)
|
||||
}
|
||||
break
|
||||
case "object":
|
||||
err = s.terminateIfNoHandshake(remotePeer)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
var object models.Object
|
||||
|
||||
err := object.UnmarshalJSON(msgJSON)
|
||||
if err != nil {
|
||||
s.Logger.Debug("Could not unmarshal: " + err.Error())
|
||||
s.Handler.Fail("Object request invalid")
|
||||
break
|
||||
}
|
||||
|
||||
val, err := object.GetObjectValue()
|
||||
if err != nil {
|
||||
s.Logger.Error("Could not parse object: " + err.Error())
|
||||
s.Handler.Fail("Could not parse object request")
|
||||
break
|
||||
}
|
||||
|
||||
switch val.GetType() {
|
||||
case "transaction":
|
||||
err := s.State.AppendTransaction(val.GetEntity().(*models.Transaction))
|
||||
if err != nil {
|
||||
s.Logger.Debug("Failed appending transaction: " + err.Error())
|
||||
s.Handler.Fail("Error appending transaction")
|
||||
break
|
||||
}
|
||||
|
||||
break
|
||||
case "block":
|
||||
block := val.(*models.Block)
|
||||
|
||||
go s.appendBlockToChain(block)
|
||||
break
|
||||
default:
|
||||
s.Logger.Debug("Something went wrong with " + string(msgJSON))
|
||||
s.Handler.Fail("Could not parse object request")
|
||||
}
|
||||
break
|
||||
case "getchaintip":
|
||||
err := s.terminateIfNoHandshake(remotePeer)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
res := s.State.GetChainTip()
|
||||
|
||||
var chaintip models.Chaintip
|
||||
chaintip.Construct(res.GetID())
|
||||
respJSON, _ := chaintip.MarshalJson()
|
||||
s.Handler.Output(respJSON)
|
||||
break
|
||||
case "chaintip":
|
||||
err := s.terminateIfNoHandshake(remotePeer)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
var chaintip models.Chaintip
|
||||
|
||||
err = chaintip.UnmarshalJSON(msgJSON)
|
||||
if err != nil {
|
||||
s.Logger.Debug("Could not unmarshal: " + err.Error())
|
||||
s.Handler.Fail("Object request invalid")
|
||||
break
|
||||
}
|
||||
|
||||
res := s.State.GetChainTip()
|
||||
if res.GetID() != chaintip.BlockID {
|
||||
s.Client.DiscoverObject(remotePeer, chaintip.BlockID)
|
||||
}
|
||||
break
|
||||
case "getmempool":
|
||||
err := s.terminateIfNoHandshake(remotePeer)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
res := s.State.GetMempoolTransactionIDs()
|
||||
|
||||
var mempool models.Mempool
|
||||
mempool.Construct(res)
|
||||
respJSON, _ := mempool.MarshalJson()
|
||||
s.Handler.Output(respJSON)
|
||||
break
|
||||
case "mempool":
|
||||
err := s.terminateIfNoHandshake(remotePeer)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
var mempool models.Mempool
|
||||
|
||||
err = mempool.UnmarshalJSON(msgJSON)
|
||||
if err != nil {
|
||||
s.Logger.Debug("Could not unmarshal: " + err.Error())
|
||||
s.Handler.Fail("Object request invalid")
|
||||
break
|
||||
}
|
||||
|
||||
for _, txid := range mempool.Txids {
|
||||
res := s.State.GetTransaction(txid)
|
||||
if res == nil {
|
||||
s.Client.DiscoverObject(remotePeer, txid)
|
||||
}
|
||||
}
|
||||
break
|
||||
|
||||
default:
|
||||
s.Handler.Fail("Request type not supported " + msgType.(string))
|
||||
}
|
||||
} else {
|
||||
s.Handler.Fail("Request type not specified")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) terminateIfNoHandshake(remotePeer string) error {
|
||||
if !s.State.CheckForHandshake(remotePeer) {
|
||||
s.Handler.Fail("Handshake not completed")
|
||||
return errors.New("handshake not completed with peer " + remotePeer)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) appendBlockToChain(block *models.Block) {
|
||||
dontAppend := false
|
||||
missingTx, err := s.State.GetMissingTransactionsInBlock(block)
|
||||
if err != nil {
|
||||
s.Logger.Debug("Failed appending block: " + err.Error())
|
||||
s.Handler.Fail("Error appending block")
|
||||
return
|
||||
}
|
||||
|
||||
if len(missingTx) != 0 {
|
||||
for _, txid := range missingTx {
|
||||
s.Client.DiscoverObject(s.Handler.GetRemotePeer(), txid)
|
||||
waited := 0
|
||||
for s.State.GetTransaction(txid) == nil {
|
||||
s.Logger.Debug("Waiting for transactions " + txid)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
waited += 500
|
||||
if waited*int(time.Millisecond) >= s.State.Config.ConnTimeout*int(time.Second) {
|
||||
s.Handler.Fail("Did not receive requested transactions in time, failed appending block")
|
||||
dontAppend = true
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s.getMissingBlocks(block)
|
||||
|
||||
if !dontAppend {
|
||||
err = s.State.AppendToChain(block)
|
||||
if err != nil {
|
||||
s.Logger.Debug("Failed appending block: " + err.Error())
|
||||
s.Handler.Fail("Error appending block")
|
||||
}
|
||||
|
||||
s.State.DumpBlockStore()
|
||||
for _, peer := range s.State.PeerList {
|
||||
if peer.Active {
|
||||
s.Client.GossipBlocks(peer.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) getMissingBlocks(block *models.Block) {
|
||||
blockId := *block.Previd
|
||||
if s.State.GetBlock(blockId) != nil {
|
||||
return
|
||||
}
|
||||
|
||||
s.Logger.Debug("Block missing from chain: " + blockId)
|
||||
for _, peer := range s.State.PeerList {
|
||||
s.Client.DiscoverObject(peer.Name, blockId)
|
||||
}
|
||||
|
||||
for s.State.GetBlock(blockId) == nil {
|
||||
s.Logger.Debug("Waiting for block " + blockId)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
}
|
38
utils/tests/client_test.go
Normal file
38
utils/tests/client_test.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"kerma/helpers"
|
||||
"kerma/models"
|
||||
"kerma/utils"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestClient(t *testing.T) {
|
||||
host := "127.0.0.1:1338"
|
||||
net.Listen("tcp", host)
|
||||
conn, _ := net.Dial("tcp", host)
|
||||
|
||||
defer conn.Close()
|
||||
|
||||
var config helpers.Config
|
||||
config.Construct()
|
||||
|
||||
var handler utils.TCPHandler
|
||||
handler.Construct(conn)
|
||||
|
||||
var state models.State
|
||||
state.Construct()
|
||||
|
||||
Convey("Given a new client", t, func() {
|
||||
var client utils.Client
|
||||
client.Construct(&state, handler)
|
||||
|
||||
Convey("When Construct is called, a new object is returned", func() {
|
||||
So(client.State, ShouldResemble, &state)
|
||||
So(client.Handler, ShouldResemble, handler)
|
||||
})
|
||||
})
|
||||
}
|
51
utils/tests/handler_test.go
Normal file
51
utils/tests/handler_test.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"kerma/helpers"
|
||||
"kerma/utils"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestHandler(t *testing.T) {
|
||||
host := "127.0.0.1:1337"
|
||||
net.Listen("tcp", host)
|
||||
conn, _ := net.Dial("tcp", host)
|
||||
scanner := bufio.NewScanner(conn)
|
||||
|
||||
var config helpers.Config
|
||||
config.Construct()
|
||||
|
||||
Convey("Given a new handler", t, func() {
|
||||
var handler utils.TCPHandler
|
||||
handler.Construct(conn)
|
||||
|
||||
Convey("When Construct is called, a new object is returned", func() {
|
||||
So(handler.Conn, ShouldResemble, conn)
|
||||
So(handler.Config, ShouldResemble, config)
|
||||
})
|
||||
|
||||
Convey("When Output is called, a JSON string should be returned on the connection", func() {
|
||||
jsonString := `{"foo": "bar"}`
|
||||
handler.Output([]byte(jsonString))
|
||||
resp := string(scanner.Bytes())
|
||||
|
||||
So(resp, ShouldEqual, resp)
|
||||
})
|
||||
|
||||
Convey("When Input is called, a JSON string and no error should be obtained from the connection", func() {
|
||||
jsonString := `{"foo": "bar"}`
|
||||
resp, err := handler.Input([]byte(jsonString))
|
||||
|
||||
So(resp, ShouldEqual, resp)
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("When GetRemotePeer is called, the local connection should be returned", func() {
|
||||
So(handler.GetRemotePeer(), ShouldEqual, host)
|
||||
})
|
||||
})
|
||||
}
|
Reference in New Issue
Block a user