<commit_before>// Copyright (c) 2014-2015 Bitmark Inc.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package bilateralrpc

import (
	"container/list"
	"errors"
	"fmt"
	svrpc "github.com/bitmark-inc/bilateralrpc/rpc"
	"github.com/bitmark-inc/logger"
	zmq "github.com/pebbe/zmq4"
	"reflect"
	"syscall"
	"time"
)

// limits
const (
	tickTime       = 50 * time.Millisecond
	reactorRunTime = 20 * time.Millisecond

	channelRequests = 0  // number of channel requests to handle each cycle (0 =>all)
	channelBuffers  = 50 // size of RPC channel buffers
)

// sent to all connections value for Call
var SendToAll = []string{}

// type to hold the client/server data
type Bilateral struct {
	// to stop and wait for shutdown
	shutdownAll     chan bool
	rpcShutdown     chan bool
	reactorShutdown chan bool

	// record all connections
	connections map[string]*connect

	// for RPC registration
	server *svrpc.Server

	// this servers identifier
	encrypted   bool
	serverName  string
	networkName string

	// map RPC IDs to their channels
	rpcReturns map[uint32]*rpcClientRequestData

	// communication
	listenSocket   *zmq.Socket
	outgoingSocket *zmq.Socket

	// RPC queues
	// for outgoing RPC
	rpcClientRequestChannel  chan interface{}
	rpcClientResponseChannel chan interface{}

	// for incoming RPC
	rpcServerCallChannel chan rpcServerCallData
	rpcServerBackChannel chan interface{}

	// timeout queue
	timeoutQueue list.List
}

// log channel for debugging
var log *logger.L

// set up the connection (can set plain or encrypted)
// Note that in encrypted mode the public key functions as the server name
// and that the netwokName must match on both sides for connection to be established
//
// for plaintext call as:  NewPlaintext(network_name, server_name)
//
// for encrypted call as:  NewEncrypted(network_name, public_key, private_key)
//      with values from:  pub, priv, err := NewKeypair()
func NewPlaintext(networkName string, serverName string) *Bilateral {
	return createBilateral(networkName, serverName)
}

func NewEncrypted(networkName string, publicKey string, privateKey string) *Bilateral {
	return createBilateral(networkName, publicKey, privateKey)
}

func createBilateral(networkName string, configuration ...string) *Bilateral {

	n := len(configuration)
	if n < 1 || n > 2 {
		panic("NewBilateral must have one or two arguments")
	}

	if nil == log {
		log = logger.New("bilateral")
	}

	twoway := new(Bilateral)

	// initialise global data
	twoway.serverName = configuration[0]
	twoway.networkName = networkName

	twoway.server = svrpc.NewServer()
	twoway.connections = make(map[string]*connect)
	twoway.rpcReturns = make(map[uint32]*rpcClientRequestData)

	// for stopping threads
	twoway.shutdownAll = make(chan bool)

	// RPC buffered streams
	twoway.rpcClientRequestChannel = make(chan interface{}, channelBuffers)
	twoway.rpcClientResponseChannel = make(chan interface{}, channelBuffers)
	twoway.rpcServerCallChannel = make(chan rpcServerCallData, channelBuffers)
	twoway.rpcServerBackChannel = make(chan interface{}, channelBuffers)

	log.Infof("server name: %q", twoway.serverName)

	var err error

	twoway.encrypted = false

	switch n {
	case 1: // plaintext connection
		twoway.encrypted = false

		// this is our server side
		twoway.listenSocket, err = createSocket(plainSocket, twoway.serverName, "")
		if nil != err {
			panic(fmt.Sprintf("listen socket: error: %v", err))
		}

		// prepare socket to connect to to multiple upstreams
		twoway.outgoingSocket, err = createSocket(plainSocket, twoway.serverName, "")
		if nil != err {
			panic(fmt.Sprintf("outgoing socket: error: %v", err))
		}

	case 2:
		twoway.encrypted = true

		// this is our server side
		twoway.listenSocket, err = createSocket(serverSocket, twoway.serverName, configuration[1])
		if nil != err {
			panic(fmt.Sprintf("listen socket: error: %v", err))
		}

		// prepare socket to connect to to multiple upstreams
		twoway.outgoingSocket, err = createSocket(clientSocket, twoway.serverName, configuration[1])
		if nil != err {
			panic(fmt.Sprintf("outgoing socket: error: %v", err))
		}
	}

	// set up the reactor to handle all sockets
	reactor := zmq.NewReactor()

	// incoming from downstream clients
	reactor.AddSocket(twoway.listenSocket, zmq.POLLIN,
		func(e zmq.State) error { return twoway.listenHandler() })

	// incoming from upstream servers
	reactor.AddSocket(twoway.outgoingSocket, zmq.POLLIN,
		func(e zmq.State) error { return twoway.replyHandler() })

	reactor.AddChannel(twoway.rpcClientRequestChannel, channelRequests,
		func(item interface{}) error { return twoway.rpcClientRequestHandler(item) })

	reactor.AddChannel(twoway.rpcClientResponseChannel, channelRequests,
		func(item interface{}) error { return twoway.rpcClientResponseHandler(item) })

	reactor.AddChannel(twoway.rpcServerBackChannel, channelRequests,
		func(item interface{}) error { return twoway.rpcBackHandler(item) })

	// start an event loop
	reactor.AddChannelTime(time.Tick(tickTime), 1,
		func(i interface{}) error { return twoway.sender() })

	// background RPC test routines
	go twoway.rpcProcedure()

	// start event loop
	go func() {
		twoway.reactorShutdown = make(chan bool)
		defer close(twoway.reactorShutdown)
		log.Info("reactor start")
		for {
			// note: re-run reactor if its internal poll fails with "interrupted system call"
			err := reactor.Run(reactorRunTime)
			if nil == err {
				break // normal termination
			} else if zmq.Errno(syscall.EINTR) != zmq.AsErrno(err) {
				//log.Errorf("reactor failed: error: %v", err)
				break
			}
			log.Errorf("reactor retry: error: %v", err)
		}
		log.Info("reactor stopped")
	}()

	return twoway
}

// shutdown all connections, terminate all background processing
func (twoway *Bilateral) Close() {
	log.Info("closing…")

	// disconnect all outgoing connections
	for to := range twoway.connections {
		twoway.DisconnectFrom(to)
	}

	// shutdown background processing
	close(twoway.shutdownAll)

	// close sockets so reactor will stop
	twoway.listenSocket.Close()
	twoway.outgoingSocket.Close()

	log.Info("Close: waiting…")

	// wait for final shutdown
	<-twoway.rpcShutdown
	<-twoway.reactorShutdown

	// close any remaining channels
	close(twoway.rpcClientRequestChannel)
	close(twoway.rpcClientResponseChannel)
	close(twoway.rpcServerCallChannel)
	close(twoway.rpcServerBackChannel)

	// terminate all outstanding calls
	for _, request := range twoway.rpcReturns {
		twoway.finishRequest(request)
	}

	// clear the timeout queue
	for e := twoway.timeoutQueue.Front(); nil != e; e = e.Prev() {
		twoway.timeoutQueue.Remove(e)
	}
	log.Info("Close: complete")
}

// listen for incomming requests
func (twoway *Bilateral) ListenOn(address string) error {
	log.Infof("set listen on: %s", address)
	return twoway.listenSocket.Bind(address)
}

// connect to a remote
func (twoway *Bilateral) ConnectTo(name string, address string) error {
	// do not make a loopback connection
	if twoway.serverName == name {
		return errors.New("loopback forbidden")
	}

	if _, exists := twoway.connections[name]; exists {
		return errors.New("duplicate connection")
	}

	log.Infof("connect to: %s @ %s", name, address)

	twoway.connections[name] = &connect{
		timestamp: time.Now().Add(FIRST_START_TIME),
		name:      name,
		address:   address,
		state:     stateWaiting,
		joinCount: 0,
		backoff:   0,
		outgoing:  true,
		tick:      0,
	}

	return nil
}

// disconnect from a remote
func (twoway *Bilateral) DisconnectFrom(name string) error {
	_, exists := twoway.connections[name]
	if !exists {
		return errors.New("no existing connection")
	}
	log.Infof("disconnect from: %s", name)
	delete(twoway.connections, name)
	return nil
}

// count active connections
func (twoway *Bilateral) ConnectionCount() int {
	count := 0
	for _, c := range twoway.connections {
		if stateConnected == c.state || stateCheck == c.state || stateServing == c.state {
			count += 1
		}
	}
	return count
}

// register a callback object
func (twoway *Bilateral) Register(receiver interface{}) error {
	return twoway.server.Register(receiver)
}

// get a list of acive peers names
func (twoway *Bilateral) ActiveConnections() []string {
	active := make([]string, 0, len(twoway.connections))
	for name, c := range twoway.connections {
		if stateConnected == c.state || stateCheck == c.state || stateServing == c.state {
			active = append(active, name)
		}
	}
	return active
}

// rpc call routine and receive reply, waiting if necessary
// to can be set to SendToAll to do a broadcast
func (twoway *Bilateral) Call(to []string, method string, args interface{}, results interface{}, optionalTimeout ...time.Duration) error {

	if nil != to && 0 == len(to) {
		to = nil
	}

	timeout := DEFAULT_CALL_TIMEOUT
	if len(optionalTimeout) == 1 {
		timeout = optionalTimeout[0]
	} else if len(optionalTimeout) == 1 {
		return errors.New("too many timeout values")
	}
	if timeout < MINIMUM_CALL_TIMEOUT {
		return errors.New("timeout is too short")
	}

	select {
	case <-twoway.shutdownAll:
		return errors.New("RPC system shutting down")
	default:
	}

	id := nonce()

	ptr := reflect.TypeOf(results)
	if reflect.Ptr != ptr.Kind() && !reflect.ValueOf(results).IsNil() && reflect.Slice != ptr.Elem().Kind() {
		return errors.New("must be a non-nil pointer to slice")
	}

	theSlice := ptr.Elem()

	// ptr to slice of 's' - some kind of struct
	theStruct := theSlice.Elem()
	if reflect.Struct != theStruct.Kind() || 3 != theStruct.NumField() {
		return errors.New("must be a struct with three fields")
	}

	fromField, hasFrom := theStruct.FieldByName("From")
	_, hasReply := theStruct.FieldByName("Reply")
	errField, hasErr := theStruct.FieldByName("Err")

	if !hasFrom || !hasReply || !hasErr {
		return errors.New("must be a struct with From, Reply and Err fields")
	}

	if reflect.String != fromField.Type.Kind() {
		return errors.New("struct From field must be string")
	}

	if reflect.Interface != errField.Type.Kind() {
		return errors.New("struct Err field must be error")
	}

	done := make(chan *rpcClientRequestData)

	log.Infof("send for method: %s", method)
	twoway.rpcClientRequestChannel <- &rpcClientRequestData{
		id:         id,
		to:         to,
		timeout:    timeout,
		method:     method,
		args:       args,
		structType: theStruct,
		done:       done,
		reply:      nil, // added later
		count:      0,   // added later
	}

	// do retries
	var request *rpcClientRequestData
loop:
	for retry := 0; retry < DEFAULT_RETRIES; retry += 1 {
		log.Infof("wait for result for method: %s", method)
		// the processed request will be returned here
		request = <-done
		if nil != request.reply && len(request.reply) > 0 {
			break loop
		}
		twoway.rpcClientRequestChannel <- request
		log.Infof("timeout method: %s : %v", method, request)
	}

	log.Debugf("request for method: %s : %v", method, request)

	if nil == request.reply || len(request.reply) == 0 {
		return errors.New("no results")
	}

	received := reflect.MakeSlice(theSlice, 0, len(to))
	for r := range request.reply {
		received = reflect.Append(received, r.Elem())
	}
	reflect.ValueOf(results).Elem().Set(received)
	log.Debugf("Result for method: %s : %v", method, results)

	return nil
}

// rpc cast routine - no reply will be sent by remote
// to can be set to SendToAll to do a broadcast
func (twoway *Bilateral) Cast(to []string, method string, args interface{}) error {

	if nil != to && 0 == len(to) {
		to = nil
	}

	select {
	case <-twoway.shutdownAll:
		return errors.New("RPC system shutting down")
	default:
	}

	id := nonce()
	twoway.rpcClientRequestChannel <- &rpcClientRequestData{
		id:         id,
		to:         to,
		timeout:    DEFAULT_CALL_TIMEOUT, // not used
		method:     method,
		args:       args,
		structType: nil,
		done:       nil,
		reply:      nil, // added later
		count:      0,   // added later
	}

	return nil
}
<commit_msg>set reply to nil if the request got timeout<commit_after>// Copyright (c) 2014-2015 Bitmark Inc.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package bilateralrpc

import (
	"container/list"
	"errors"
	"fmt"
	svrpc "github.com/bitmark-inc/bilateralrpc/rpc"
	"github.com/bitmark-inc/logger"
	zmq "github.com/pebbe/zmq4"
	"reflect"
	"syscall"
	"time"
)

// limits
const (
	tickTime       = 50 * time.Millisecond
	reactorRunTime = 20 * time.Millisecond

	channelRequests = 0  // number of channel requests to handle each cycle (0 =>all)
	channelBuffers  = 50 // size of RPC channel buffers
)

// sent to all connections value for Call
var SendToAll = []string{}

// type to hold the client/server data
type Bilateral struct {
	// to stop and wait for shutdown
	shutdownAll     chan bool
	rpcShutdown     chan bool
	reactorShutdown chan bool

	// record all connections
	connections map[string]*connect

	// for RPC registration
	server *svrpc.Server

	// this servers identifier
	encrypted   bool
	serverName  string
	networkName string

	// map RPC IDs to their channels
	rpcReturns map[uint32]*rpcClientRequestData

	// communication
	listenSocket   *zmq.Socket
	outgoingSocket *zmq.Socket

	// RPC queues
	// for outgoing RPC
	rpcClientRequestChannel  chan interface{}
	rpcClientResponseChannel chan interface{}

	// for incoming RPC
	rpcServerCallChannel chan rpcServerCallData
	rpcServerBackChannel chan interface{}

	// timeout queue
	timeoutQueue list.List
}

// log channel for debugging
var log *logger.L

// set up the connection (can set plain or encrypted)
// Note that in encrypted mode the public key functions as the server name
// and that the netwokName must match on both sides for connection to be established
//
// for plaintext call as:  NewPlaintext(network_name, server_name)
//
// for encrypted call as:  NewEncrypted(network_name, public_key, private_key)
//      with values from:  pub, priv, err := NewKeypair()
func NewPlaintext(networkName string, serverName string) *Bilateral {
	return createBilateral(networkName, serverName)
}

func NewEncrypted(networkName string, publicKey string, privateKey string) *Bilateral {
	return createBilateral(networkName, publicKey, privateKey)
}

func createBilateral(networkName string, configuration ...string) *Bilateral {

	n := len(configuration)
	if n < 1 || n > 2 {
		panic("NewBilateral must have one or two arguments")
	}

	if nil == log {
		log = logger.New("bilateral")
	}

	twoway := new(Bilateral)

	// initialise global data
	twoway.serverName = configuration[0]
	twoway.networkName = networkName

	twoway.server = svrpc.NewServer()
	twoway.connections = make(map[string]*connect)
	twoway.rpcReturns = make(map[uint32]*rpcClientRequestData)

	// for stopping threads
	twoway.shutdownAll = make(chan bool)

	// RPC buffered streams
	twoway.rpcClientRequestChannel = make(chan interface{}, channelBuffers)
	twoway.rpcClientResponseChannel = make(chan interface{}, channelBuffers)
	twoway.rpcServerCallChannel = make(chan rpcServerCallData, channelBuffers)
	twoway.rpcServerBackChannel = make(chan interface{}, channelBuffers)

	log.Infof("server name: %q", twoway.serverName)

	var err error

	twoway.encrypted = false

	switch n {
	case 1: // plaintext connection
		twoway.encrypted = false

		// this is our server side
		twoway.listenSocket, err = createSocket(plainSocket, twoway.serverName, "")
		if nil != err {
			panic(fmt.Sprintf("listen socket: error: %v", err))
		}

		// prepare socket to connect to to multiple upstreams
		twoway.outgoingSocket, err = createSocket(plainSocket, twoway.serverName, "")
		if nil != err {
			panic(fmt.Sprintf("outgoing socket: error: %v", err))
		}

	case 2:
		twoway.encrypted = true

		// this is our server side
		twoway.listenSocket, err = createSocket(serverSocket, twoway.serverName, configuration[1])
		if nil != err {
			panic(fmt.Sprintf("listen socket: error: %v", err))
		}

		// prepare socket to connect to to multiple upstreams
		twoway.outgoingSocket, err = createSocket(clientSocket, twoway.serverName, configuration[1])
		if nil != err {
			panic(fmt.Sprintf("outgoing socket: error: %v", err))
		}
	}

	// set up the reactor to handle all sockets
	reactor := zmq.NewReactor()

	// incoming from downstream clients
	reactor.AddSocket(twoway.listenSocket, zmq.POLLIN,
		func(e zmq.State) error { return twoway.listenHandler() })

	// incoming from upstream servers
	reactor.AddSocket(twoway.outgoingSocket, zmq.POLLIN,
		func(e zmq.State) error { return twoway.replyHandler() })

	reactor.AddChannel(twoway.rpcClientRequestChannel, channelRequests,
		func(item interface{}) error { return twoway.rpcClientRequestHandler(item) })

	reactor.AddChannel(twoway.rpcClientResponseChannel, channelRequests,
		func(item interface{}) error { return twoway.rpcClientResponseHandler(item) })

	reactor.AddChannel(twoway.rpcServerBackChannel, channelRequests,
		func(item interface{}) error { return twoway.rpcBackHandler(item) })

	// start an event loop
	reactor.AddChannelTime(time.Tick(tickTime), 1,
		func(i interface{}) error { return twoway.sender() })

	// background RPC test routines
	go twoway.rpcProcedure()

	// start event loop
	go func() {
		twoway.reactorShutdown = make(chan bool)
		defer close(twoway.reactorShutdown)
		log.Info("reactor start")
		for {
			// note: re-run reactor if its internal poll fails with "interrupted system call"
			err := reactor.Run(reactorRunTime)
			if nil == err {
				break // normal termination
			} else if zmq.Errno(syscall.EINTR) != zmq.AsErrno(err) {
				//log.Errorf("reactor failed: error: %v", err)
				break
			}
			log.Errorf("reactor retry: error: %v", err)
		}
		log.Info("reactor stopped")
	}()

	return twoway
}

// shutdown all connections, terminate all background processing
func (twoway *Bilateral) Close() {
	log.Info("closing…")

	// disconnect all outgoing connections
	for to := range twoway.connections {
		twoway.DisconnectFrom(to)
	}

	// shutdown background processing
	close(twoway.shutdownAll)

	// close sockets so reactor will stop
	twoway.listenSocket.Close()
	twoway.outgoingSocket.Close()

	log.Info("Close: waiting…")

	// wait for final shutdown
	<-twoway.rpcShutdown
	<-twoway.reactorShutdown

	// close any remaining channels
	close(twoway.rpcClientRequestChannel)
	close(twoway.rpcClientResponseChannel)
	close(twoway.rpcServerCallChannel)
	close(twoway.rpcServerBackChannel)

	// terminate all outstanding calls
	for _, request := range twoway.rpcReturns {
		twoway.finishRequest(request)
	}

	// clear the timeout queue
	for e := twoway.timeoutQueue.Front(); nil != e; e = e.Prev() {
		twoway.timeoutQueue.Remove(e)
	}
	log.Info("Close: complete")
}

// listen for incomming requests
func (twoway *Bilateral) ListenOn(address string) error {
	log.Infof("set listen on: %s", address)
	return twoway.listenSocket.Bind(address)
}

// connect to a remote
func (twoway *Bilateral) ConnectTo(name string, address string) error {
	// do not make a loopback connection
	if twoway.serverName == name {
		return errors.New("loopback forbidden")
	}

	if _, exists := twoway.connections[name]; exists {
		return errors.New("duplicate connection")
	}

	log.Infof("connect to: %s @ %s", name, address)

	twoway.connections[name] = &connect{
		timestamp: time.Now().Add(FIRST_START_TIME),
		name:      name,
		address:   address,
		state:     stateWaiting,
		joinCount: 0,
		backoff:   0,
		outgoing:  true,
		tick:      0,
	}

	return nil
}

// disconnect from a remote
func (twoway *Bilateral) DisconnectFrom(name string) error {
	_, exists := twoway.connections[name]
	if !exists {
		return errors.New("no existing connection")
	}
	log.Infof("disconnect from: %s", name)
	delete(twoway.connections, name)
	return nil
}

// count active connections
func (twoway *Bilateral) ConnectionCount() int {
	count := 0
	for _, c := range twoway.connections {
		if stateConnected == c.state || stateCheck == c.state || stateServing == c.state {
			count += 1
		}
	}
	return count
}

// register a callback object
func (twoway *Bilateral) Register(receiver interface{}) error {
	return twoway.server.Register(receiver)
}

// get a list of acive peers names
func (twoway *Bilateral) ActiveConnections() []string {
	active := make([]string, 0, len(twoway.connections))
	for name, c := range twoway.connections {
		if stateConnected == c.state || stateCheck == c.state || stateServing == c.state {
			active = append(active, name)
		}
	}
	return active
}

// rpc call routine and receive reply, waiting if necessary
// to can be set to SendToAll to do a broadcast
func (twoway *Bilateral) Call(to []string, method string, args interface{}, results interface{}, optionalTimeout ...time.Duration) error {

	if nil != to && 0 == len(to) {
		to = nil
	}

	timeout := DEFAULT_CALL_TIMEOUT
	if len(optionalTimeout) == 1 {
		timeout = optionalTimeout[0]
	} else if len(optionalTimeout) == 1 {
		return errors.New("too many timeout values")
	}
	if timeout < MINIMUM_CALL_TIMEOUT {
		return errors.New("timeout is too short")
	}

	select {
	case <-twoway.shutdownAll:
		return errors.New("RPC system shutting down")
	default:
	}

	id := nonce()

	ptr := reflect.TypeOf(results)
	if reflect.Ptr != ptr.Kind() && !reflect.ValueOf(results).IsNil() && reflect.Slice != ptr.Elem().Kind() {
		return errors.New("must be a non-nil pointer to slice")
	}

	theSlice := ptr.Elem()

	// ptr to slice of 's' - some kind of struct
	theStruct := theSlice.Elem()
	if reflect.Struct != theStruct.Kind() || 3 != theStruct.NumField() {
		return errors.New("must be a struct with three fields")
	}

	fromField, hasFrom := theStruct.FieldByName("From")
	_, hasReply := theStruct.FieldByName("Reply")
	errField, hasErr := theStruct.FieldByName("Err")

	if !hasFrom || !hasReply || !hasErr {
		return errors.New("must be a struct with From, Reply and Err fields")
	}

	if reflect.String != fromField.Type.Kind() {
		return errors.New("struct From field must be string")
	}

	if reflect.Interface != errField.Type.Kind() {
		return errors.New("struct Err field must be error")
	}

	done := make(chan *rpcClientRequestData)

	log.Infof("send for method: %s", method)
	twoway.rpcClientRequestChannel <- &rpcClientRequestData{
		id:         id,
		to:         to,
		timeout:    timeout,
		method:     method,
		args:       args,
		structType: theStruct,
		done:       done,
		reply:      nil, // added later
		count:      0,   // added later
	}

	// do retries
	var request *rpcClientRequestData
loop:
	for retry := 0; retry < DEFAULT_RETRIES; retry += 1 {
		log.Infof("wait for result for method: %s", method)
		// the processed request will be returned here
		request = <-done
		if nil != request.reply && len(request.reply) > 0 {
			break loop
		}
		request.reply = nil
		twoway.rpcClientRequestChannel <- request
		log.Infof("timeout method: %s : %v", method, request)
	}

	log.Debugf("request for method: %s : %v", method, request)

	if nil == request.reply || len(request.reply) == 0 {
		return errors.New("no results")
	}

	received := reflect.MakeSlice(theSlice, 0, len(to))
	for r := range request.reply {
		received = reflect.Append(received, r.Elem())
	}
	reflect.ValueOf(results).Elem().Set(received)
	log.Debugf("Result for method: %s : %v", method, results)

	return nil
}

// rpc cast routine - no reply will be sent by remote
// to can be set to SendToAll to do a broadcast
func (twoway *Bilateral) Cast(to []string, method string, args interface{}) error {

	if nil != to && 0 == len(to) {
		to = nil
	}

	select {
	case <-twoway.shutdownAll:
		return errors.New("RPC system shutting down")
	default:
	}

	id := nonce()
	twoway.rpcClientRequestChannel <- &rpcClientRequestData{
		id:         id,
		to:         to,
		timeout:    DEFAULT_CALL_TIMEOUT, // not used
		method:     method,
		args:       args,
		structType: nil,
		done:       nil,
		reply:      nil, // added later
		count:      0,   // added later
	}

	return nil
}
<|endoftext|>


<commit_before>package simpleamqp

import (
	"log"
	"strconv"
	"time"

	"github.com/streadway/amqp"
)

type messageToPublish struct {
	routingKey string
	message    []byte
	expiration string
}

type AMQPPublisher interface {
	Publish(string, []byte)
	PublishWithTTL(string, []byte, int)
}

type AmqpPublisher struct {
	brokerURI      string
	exchange       string
	outputMessages chan messageToPublish
}

func NewAmqpPublisher(brokerURI, exchange string) *AmqpPublisher {
	publisher := AmqpPublisher{
		brokerURI:      brokerURI,
		exchange:       exchange,
		outputMessages: make(chan messageToPublish, 1024),
	}

	go func() {
		for {
			err := publisher.publishLoop()
			log.Println("Error", err)
			log.Println("Waiting", timeToReconnect, "to reconnect")
			time.Sleep(timeToReconnect)
		}
	}()
	return &publisher
}

func (publisher *AmqpPublisher) Publish(routingKey string, message []byte) {
	messageToPublish := messageToPublish{routingKey: routingKey, message: message}
	publisher.queueMessageToPublish(messageToPublish)
}

func (publisher *AmqpPublisher) PublishWithTTL(routingKey string, message []byte, ttl int) {
	publisher.queueMessageToPublish(messageToPublish{routingKey: routingKey, message: message, expiration: strconv.Itoa(ttl)})
}

// Queue the message to be published and return inmediatly
// The message will be published to the AmqpPublisher exchange using the given routingKey
// If the message can't be queued (because the channel is full) a log is printed and the message is discarded
func (publisher *AmqpPublisher) queueMessageToPublish(messageToPublish messageToPublish) {
	timeoutTimer := time.NewTimer(5 * time.Second)
	defer timeoutTimer.Stop()
	afterTimeout := timeoutTimer.C

	select {
	case publisher.outputMessages <- messageToPublish:
	case <-afterTimeout:
		log.Println("Publish channel full", messageToPublish)
	}
}

func (publisher *AmqpPublisher) publish(channel *amqp.Channel, messageToPublish messageToPublish) error {
	err := channel.Publish(
		publisher.exchange,
		messageToPublish.routingKey,
		false,
		false,
		amqp.Publishing{
			Headers:         amqp.Table{},
			ContentType:     "application/json",
			ContentEncoding: "",
			Body:            messageToPublish.message,
			DeliveryMode:    amqp.Transient,
			Priority:        0,
			Expiration:      messageToPublish.expiration,
		})
	return err

}

func (publisher *AmqpPublisher) publishLoop() error {
	conn, ch := Setup(publisher.brokerURI)
	defer conn.Close()
	defer ch.Close()

	exchangeDeclare(ch, publisher.exchange)
	for {
		messageToPublish := <-publisher.outputMessages
		err := publisher.publish(ch, messageToPublish)
		if err != nil {
			log.Println("Error publishing message:", err)
			return err
		}
		log.Println("Published", messageToPublish.routingKey, string(messageToPublish.message))
	}
}
<commit_msg>Refactor<commit_after>package simpleamqp

import (
	"log"
	"strconv"
	"time"

	"github.com/streadway/amqp"
)

type messageToPublish struct {
	routingKey string
	message    []byte
	expiration string
}

type AMQPPublisher interface {
	Publish(string, []byte)
	PublishWithTTL(string, []byte, int)
}

type AmqpPublisher struct {
	brokerURI      string
	exchange       string
	outputMessages chan messageToPublish
}

func NewAmqpPublisher(brokerURI, exchange string) *AmqpPublisher {
	publisher := AmqpPublisher{
		brokerURI:      brokerURI,
		exchange:       exchange,
		outputMessages: make(chan messageToPublish, 1024),
	}

	go func() {
		for {
			err := publisher.publishLoop()
			log.Println("Error", err)
			log.Println("Waiting", timeToReconnect, "to reconnect")
			time.Sleep(timeToReconnect)
		}
	}()
	return &publisher
}

func (publisher *AmqpPublisher) Publish(routingKey string, message []byte) {
	publisher.queueMessageToPublish(messageToPublish{routingKey: routingKey, message: message})
}

func (publisher *AmqpPublisher) PublishWithTTL(routingKey string, message []byte, ttl int) {
	publisher.queueMessageToPublish(messageToPublish{routingKey: routingKey, message: message, expiration: strconv.Itoa(ttl)})
}

// Queue the message to be published and return inmediatly
// The message will be published to the AmqpPublisher exchange using the given routingKey
// If the message can't be queued (because the channel is full) a log is printed and the message is discarded
func (publisher *AmqpPublisher) queueMessageToPublish(messageToPublish messageToPublish) {
	timeoutTimer := time.NewTimer(5 * time.Second)
	defer timeoutTimer.Stop()
	afterTimeout := timeoutTimer.C

	select {
	case publisher.outputMessages <- messageToPublish:
	case <-afterTimeout:
		log.Println("Publish channel full", messageToPublish)
	}
}

func (publisher *AmqpPublisher) publish(channel *amqp.Channel, messageToPublish messageToPublish) error {
	err := channel.Publish(
		publisher.exchange,
		messageToPublish.routingKey,
		false,
		false,
		amqp.Publishing{
			Headers:         amqp.Table{},
			ContentType:     "application/json",
			ContentEncoding: "",
			Body:            messageToPublish.message,
			DeliveryMode:    amqp.Transient,
			Priority:        0,
			Expiration:      messageToPublish.expiration,
		})
	return err

}

func (publisher *AmqpPublisher) publishLoop() error {
	conn, ch := Setup(publisher.brokerURI)
	defer conn.Close()
	defer ch.Close()

	exchangeDeclare(ch, publisher.exchange)
	for {
		messageToPublish := <-publisher.outputMessages
		err := publisher.publish(ch, messageToPublish)
		if err != nil {
			log.Println("Error publishing message:", err)
			return err
		}
		log.Println("Published", messageToPublish.routingKey, string(messageToPublish.message))
	}
}
<|endoftext|>


<commit_before>package inlet_http

import (
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/go-martini/martini"
	"github.com/gogap/errors"
	"github.com/gogap/logs"
	"github.com/gogap/spirit"
)

const (
	REQUEST_TIMEOUT = 30 * time.Second
)

const (
	CTX_HTTP_COOKIES = "CTX_HTTP_COOKIES"
	CTX_HTTP_HEADERS = "CTX_HTTP_HEADERS"

	CMD_HTTP_HEADERS_SET = "CMD_HTTP_HEADERS_SET"
	CMD_HTTP_COOKIES_SET = "CMD_HTTP_COOKIES_SET"
)

type NameValue struct {
	Name  string `json:"name"`
	Value string `json:"value"`
}

type InletHTTPRequestPayloadHook func(r *http.Request, graphName string, body []byte, payload *spirit.Payload) (err error)
type InletHTTPResponseHandler func(payloads map[string]spirit.Payload, errs map[string]error, w http.ResponseWriter, r *http.Request)
type InletHTTPErrorResponseHandler func(err error, w http.ResponseWriter, r *http.Request)
type InletHTTPRequestDecoder func(body []byte) (map[string]interface{}, error)

type option func(*InletHTTP)

type InletHTTP struct {
	config         Config
	requester      Requester
	graphProvider  GraphProvider
	respHandler    InletHTTPResponseHandler
	errRespHandler InletHTTPErrorResponseHandler
	requestDecoder InletHTTPRequestDecoder
	payloadHook    InletHTTPRequestPayloadHook
	timeout        time.Duration
	timeoutHeader  string
}

func (p *InletHTTP) Option(opts ...option) {
	for _, opt := range opts {
		opt(p)
	}
}

func SetRequestPayloadHook(hook InletHTTPRequestPayloadHook) option {
	return func(f *InletHTTP) {
		f.payloadHook = hook
	}
}

func SetRequestDecoder(decoder InletHTTPRequestDecoder) option {
	return func(f *InletHTTP) {
		f.requestDecoder = decoder
	}
}

func SetRequester(requester Requester) option {
	return func(f *InletHTTP) {
		f.requester = requester
	}
}

func SetHTTPConfig(httpConfig Config) option {
	return func(f *InletHTTP) {
		f.config = httpConfig
	}
}

func SetGraphProvider(graphProvider GraphProvider) option {
	return func(f *InletHTTP) {
		f.graphProvider = graphProvider
	}
}

func SetResponseHandler(handler InletHTTPResponseHandler) option {
	return func(f *InletHTTP) {
		f.respHandler = handler
	}
}

func SetErrorResponseHandler(handler InletHTTPErrorResponseHandler) option {
	return func(f *InletHTTP) {
		f.errRespHandler = handler
	}
}

func SetTimeout(millisecond int64) option {
	return func(f *InletHTTP) {
		f.timeout = time.Duration(millisecond)
	}
}

func SetTimeoutHeader(header string) option {
	return func(f *InletHTTP) {
		f.timeoutHeader = strings.TrimSpace(header)
	}
}

func NewInletHTTP(opts ...option) *InletHTTP {
	inletHTTP := new(InletHTTP)
	inletHTTP.Option(opts...)

	if inletHTTP.requester == nil {
		inletHTTP.requester = NewClassicRequester()
	}

	return inletHTTP
}

func (p *InletHTTP) Requester() Requester {
	return p.requester
}

func (p *InletHTTP) Run(handlers ...martini.Handler) {
	if p.graphProvider == nil {
		panic("graph provider is nil")
	}

	if p.requester == nil {
		panic("requester is nil")
	}

	if p.respHandler == nil {
		panic("response handler is nil")
	}

	if p.errRespHandler == nil {
		panic("error response handler is nil")
	}

	if p.requestDecoder == nil {
		panic("request encoder is nil")
	}

	if p.config.Timeout > 0 {
		p.timeout = time.Millisecond * time.Duration(p.config.Timeout)
		p.requester.SetTimeout(p.timeout)
	} else {
		p.timeout = REQUEST_TIMEOUT
	}

	m := martini.Classic()

	if handlers != nil {
		for _, handler := range handlers {
			m.Use(handler)
		}
	}

	m.Use(p.handler)

	if p.config.Address != "" {
		m.RunOnAddr(p.config.Address)
	} else {
		m.Run()
	}
}

func (p *InletHTTP) handler(w http.ResponseWriter, r *http.Request) {
	var err error
	var binBody []byte
	if binBody, err = ioutil.ReadAll(r.Body); err != nil {
		err = ERR_READ_HTTP_BODY_FAILED.New(errors.Params{"err": err})
		logs.Error(err)
		p.errRespHandler(err, w, r)
		return
	}

	var mapContent map[string]interface{}

	if mapContent, err = p.requestDecoder(binBody); err != nil {
		err = ERR_UNMARSHAL_HTTP_BODY_FAILED.New(errors.Params{"err": err})
		logs.Error(err)
		p.errRespHandler(err, w, r)
		return
	}

	var graphs map[string]spirit.MessageGraph
	if graphs, err = p.graphProvider.GetGraph(r, binBody); err != nil {
		logs.Error(err)
		p.errRespHandler(err, w, r)
		return
	}

	cookies := map[string]string{}
	reqCookies := r.Cookies()
	if reqCookies != nil && len(reqCookies) > 0 {
		for _, cookie := range reqCookies {
			cookies[cookie.Name] = cookie.Value
		}
	}

	payloads := map[string]*spirit.Payload{}

	for graphName, _ := range graphs {

		payload := new(spirit.Payload)
		payload.SetContent(mapContent)
		payload.SetContext(CTX_HTTP_COOKIES, cookies)

		if p.payloadHook != nil {
			if e := p.payloadHook(r, graphName, binBody, payload); e != nil {
				p.errRespHandler(e, w, r)
				return
			}
		}

		payloads[graphName] = payload
	}

	responseChan := make(chan spirit.Payload)
	errorChan := make(chan error)

	defer close(responseChan)
	defer close(errorChan)

	timeout := p.timeout

	if p.timeoutHeader != "" {
		if strTimeout := r.Header.Get(p.timeoutHeader); strTimeout != "" {
			if intTimeout, e := strconv.Atoi(strTimeout); e != nil {
				e = ERR_REQUEST_TIMEOUT_VALUE_FORMAT_WRONG.New(errors.Params{"value": strTimeout})
				logs.Warn(e)
			} else {
				timeout = time.Duration(intTimeout) * time.Millisecond
			}
		}
	}

	sendPayloadFunc := func(requester Requester, graphName string, graph spirit.MessageGraph, payload spirit.Payload, responseChan chan spirit.Payload, errorChan chan error, timeout time.Duration) {
		defer func() {
			if err := recover(); err != nil {
				return
			}
		}()

		respChan := make(chan spirit.Payload)
		errChan := make(chan error)
		defer close(respChan)
		defer close(errChan)

		msgId, err := requester.Request(graph, payload, respChan, errChan)
		if err != nil {
			errChan <- err
			return
		}

		defer requester.OnMessageProcessed(msgId)

		select {
		case respPayload := <-respChan:
			{
				responseChan <- respPayload
			}
		case respErr := <-errChan:
			{
				errorChan <- respErr
			}
		case <-time.After(time.Duration(timeout)):
			{
				err = ERR_REQUEST_TIMEOUT.New(errors.Params{"graphName": graphName, "msgId": msgId})
				errorChan <- err
			}
		}
	}

	for graphName, payload := range payloads {
		graph, _ := graphs[graphName]

		go sendPayloadFunc(p.requester, graphName, graph, *payload, responseChan, errorChan, timeout)
	}

	respPayloads := map[string]spirit.Payload{}
	errs := map[string]error{}

	for graphName, _ := range payloads {
		select {
		case respPayload := <-responseChan:
			{
				respPayloads[graphName] = respPayload
			}
		case respErr := <-errorChan:
			{
				errs[graphName] = respErr
			}
		case <-time.After(time.Duration(timeout) + time.Second):
			{
				err = ERR_REQUEST_TIMEOUT.New(errors.Params{"graphName": graphName})
				errs[graphName] = err
			}
		}
	}

	for _, respPayload := range respPayloads {
		p.writeCookiesAndHeaders(respPayload, w, r)
	}

	p.respHandler(respPayloads, errs, w, r)

	return
}

func (p *InletHTTP) writeCookiesAndHeaders(payload spirit.Payload, w http.ResponseWriter, r *http.Request) {
	var err error
	// Cookies
	cmdCookiesSize := payload.GetCommandValueSize(CMD_HTTP_COOKIES_SET)
	cmdCookies := make([]interface{}, cmdCookiesSize)
	for i := 0; i < cmdCookiesSize; i++ {
		cookie := new(http.Cookie)
		cmdCookies[i] = cookie
	}

	if err = payload.GetCommandObjectArray(CMD_HTTP_COOKIES_SET, cmdCookies); err != nil {
		err = ERR_PARSE_COMMAND_TO_OBJECT_FAILED.New(errors.Params{"cmd": CMD_HTTP_COOKIES_SET, "err": err})
		p.errRespHandler(err, w, r)
		return
	}

	for _, cookie := range cmdCookies {
		if c, ok := cookie.(*http.Cookie); ok {
			c.Domain = p.config.Domain
			c.Path = "/"
			http.SetCookie(w, c)
		} else {
			err = ERR_PARSE_COMMAND_TO_OBJECT_FAILED.New(errors.Params{"cmd": CMD_HTTP_COOKIES_SET, "err": "object could not parser to cookies"})
			logs.Error(err)
			p.errRespHandler(err, w, r)
			return
		}
	}

	cmdHeadersSize := payload.GetCommandValueSize(CMD_HTTP_HEADERS_SET)
	cmdHeaders := make([]interface{}, cmdHeadersSize)
	for i := 0; i < cmdHeadersSize; i++ {
		header := new(NameValue)
		cmdHeaders[i] = header
	}

	if err = payload.GetCommandObjectArray(CMD_HTTP_HEADERS_SET, cmdHeaders); err != nil {
		err = ERR_PARSE_COMMAND_TO_OBJECT_FAILED.New(errors.Params{"cmd": CMD_HTTP_HEADERS_SET, "err": err})
		logs.Error(err)
		p.errRespHandler(err, w, r)
		return
	}

	for _, header := range cmdHeaders {
		if nv, ok := header.(*NameValue); ok {
			w.Header().Add(nv.Name, nv.Value)
		} else {
			err = ERR_PARSE_COMMAND_TO_OBJECT_FAILED.New(errors.Params{"cmd": CMD_HTTP_HEADERS_SET, "err": "object could not parser to headers"})
			logs.Error(err)
			p.errRespHandler(err, w, r)
			return
		}
	}
}

func (p *InletHTTP) CallBack(payload *spirit.Payload) (result interface{}, err error) {
	p.OnMessageResponse(payload)
	return nil, nil
}

func (p *InletHTTP) Error(payload *spirit.Payload) (result interface{}, err error) {
	p.OnMessageResponse(payload)
	return nil, nil
}

func (p *InletHTTP) OnMessageResponse(payload *spirit.Payload) {
	p.requester.OnMessageReceived(*payload)
}
<commit_msg>fix multi call response sequence issue<commit_after>package inlet_http

import (
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/go-martini/martini"
	"github.com/gogap/errors"
	"github.com/gogap/logs"
	"github.com/gogap/spirit"
)

type GraphResponse struct {
	GraphName   string
	RespPayload spirit.Payload
	Error       error
}

const (
	REQUEST_TIMEOUT = 30 * time.Second
)

const (
	CTX_HTTP_COOKIES = "CTX_HTTP_COOKIES"
	CTX_HTTP_HEADERS = "CTX_HTTP_HEADERS"

	CMD_HTTP_HEADERS_SET = "CMD_HTTP_HEADERS_SET"
	CMD_HTTP_COOKIES_SET = "CMD_HTTP_COOKIES_SET"
)

type NameValue struct {
	Name  string `json:"name"`
	Value string `json:"value"`
}

type InletHTTPRequestPayloadHook func(r *http.Request, graphName string, body []byte, payload *spirit.Payload) (err error)
type InletHTTPResponseHandler func(graphsResponse map[string]GraphResponse, w http.ResponseWriter, r *http.Request)
type InletHTTPErrorResponseHandler func(err error, w http.ResponseWriter, r *http.Request)
type InletHTTPRequestDecoder func(body []byte) (map[string]interface{}, error)

type option func(*InletHTTP)

type InletHTTP struct {
	config         Config
	requester      Requester
	graphProvider  GraphProvider
	respHandler    InletHTTPResponseHandler
	errRespHandler InletHTTPErrorResponseHandler
	requestDecoder InletHTTPRequestDecoder
	payloadHook    InletHTTPRequestPayloadHook
	timeout        time.Duration
	timeoutHeader  string
}

func (p *InletHTTP) Option(opts ...option) {
	for _, opt := range opts {
		opt(p)
	}
}

func SetRequestPayloadHook(hook InletHTTPRequestPayloadHook) option {
	return func(f *InletHTTP) {
		f.payloadHook = hook
	}
}

func SetRequestDecoder(decoder InletHTTPRequestDecoder) option {
	return func(f *InletHTTP) {
		f.requestDecoder = decoder
	}
}

func SetRequester(requester Requester) option {
	return func(f *InletHTTP) {
		f.requester = requester
	}
}

func SetHTTPConfig(httpConfig Config) option {
	return func(f *InletHTTP) {
		f.config = httpConfig
	}
}

func SetGraphProvider(graphProvider GraphProvider) option {
	return func(f *InletHTTP) {
		f.graphProvider = graphProvider
	}
}

func SetResponseHandler(handler InletHTTPResponseHandler) option {
	return func(f *InletHTTP) {
		f.respHandler = handler
	}
}

func SetErrorResponseHandler(handler InletHTTPErrorResponseHandler) option {
	return func(f *InletHTTP) {
		f.errRespHandler = handler
	}
}

func SetTimeout(millisecond int64) option {
	return func(f *InletHTTP) {
		f.timeout = time.Duration(millisecond)
	}
}

func SetTimeoutHeader(header string) option {
	return func(f *InletHTTP) {
		f.timeoutHeader = strings.TrimSpace(header)
	}
}

func NewInletHTTP(opts ...option) *InletHTTP {
	inletHTTP := new(InletHTTP)
	inletHTTP.Option(opts...)

	if inletHTTP.requester == nil {
		inletHTTP.requester = NewClassicRequester()
	}

	return inletHTTP
}

func (p *InletHTTP) Requester() Requester {
	return p.requester
}

func (p *InletHTTP) Run(handlers ...martini.Handler) {
	if p.graphProvider == nil {
		panic("graph provider is nil")
	}

	if p.requester == nil {
		panic("requester is nil")
	}

	if p.respHandler == nil {
		panic("response handler is nil")
	}

	if p.errRespHandler == nil {
		panic("error response handler is nil")
	}

	if p.requestDecoder == nil {
		panic("request encoder is nil")
	}

	if p.config.Timeout > 0 {
		p.timeout = time.Millisecond * time.Duration(p.config.Timeout)
		p.requester.SetTimeout(p.timeout)
	} else {
		p.timeout = REQUEST_TIMEOUT
	}

	m := martini.Classic()

	if handlers != nil {
		for _, handler := range handlers {
			m.Use(handler)
		}
	}

	m.Use(p.handler)

	if p.config.Address != "" {
		m.RunOnAddr(p.config.Address)
	} else {
		m.Run()
	}
}

func (p *InletHTTP) handler(w http.ResponseWriter, r *http.Request) {
	var err error
	var binBody []byte
	if binBody, err = ioutil.ReadAll(r.Body); err != nil {
		err = ERR_READ_HTTP_BODY_FAILED.New(errors.Params{"err": err})
		logs.Error(err)
		p.errRespHandler(err, w, r)
		return
	}

	var mapContent map[string]interface{}

	if mapContent, err = p.requestDecoder(binBody); err != nil {
		err = ERR_UNMARSHAL_HTTP_BODY_FAILED.New(errors.Params{"err": err})
		logs.Error(err)
		p.errRespHandler(err, w, r)
		return
	}

	var graphs map[string]spirit.MessageGraph
	if graphs, err = p.graphProvider.GetGraph(r, binBody); err != nil {
		logs.Error(err)
		p.errRespHandler(err, w, r)
		return
	}

	cookies := map[string]string{}
	reqCookies := r.Cookies()
	if reqCookies != nil && len(reqCookies) > 0 {
		for _, cookie := range reqCookies {
			cookies[cookie.Name] = cookie.Value
		}
	}

	payloads := map[string]*spirit.Payload{}

	for graphName, _ := range graphs {

		payload := new(spirit.Payload)
		payload.SetContent(mapContent)
		payload.SetContext(CTX_HTTP_COOKIES, cookies)

		if p.payloadHook != nil {
			if e := p.payloadHook(r, graphName, binBody, payload); e != nil {
				p.errRespHandler(e, w, r)
				return
			}
		}

		payloads[graphName] = payload
	}

	responseChan := make(chan GraphResponse)

	defer close(responseChan)

	timeout := p.timeout

	if p.timeoutHeader != "" {
		if strTimeout := r.Header.Get(p.timeoutHeader); strTimeout != "" {
			if intTimeout, e := strconv.Atoi(strTimeout); e != nil {
				e = ERR_REQUEST_TIMEOUT_VALUE_FORMAT_WRONG.New(errors.Params{"value": strTimeout})
				logs.Warn(e)
			} else {
				timeout = time.Duration(intTimeout) * time.Millisecond
			}
		}
	}

	sendPayloadFunc := func(requester Requester, graphName string, graph spirit.MessageGraph, payload spirit.Payload, responseChan chan GraphResponse, timeout time.Duration) {
		defer func() {
			if err := recover(); err != nil {
				return
			}
		}()

		respChan := make(chan spirit.Payload)
		errChan := make(chan error)
		defer close(respChan)
		defer close(errChan)

		resp := GraphResponse{GraphName: graphName}

		msgId, err := requester.Request(graph, payload, respChan, errChan)
		if err != nil {
			resp.Error = err
			return
		}

		defer requester.OnMessageProcessed(msgId)

		select {
		case resp.RespPayload = <-respChan:
		case resp.Error = <-errChan:
		case <-time.After(time.Duration(timeout)):
			{
				resp.Error = ERR_REQUEST_TIMEOUT.New(errors.Params{"graphName": graphName, "msgId": msgId})
			}
		}
		responseChan <- resp
	}

	for graphName, payload := range payloads {
		graph, _ := graphs[graphName]

		go sendPayloadFunc(p.requester, graphName, graph, *payload, responseChan, timeout)
	}

	graphsResponse := map[string]GraphResponse{}

	lenGraph := len(graphs)
	for i := 0; i < lenGraph; i++ {
		select {
		case resp := <-responseChan:
			{
				graphsResponse[resp.GraphName] = resp
			}
		case <-time.After(time.Duration(timeout) + time.Second):
			{
				continue
			}
		}
	}

	for graphName, _ := range graphs {
		if _, exist := graphsResponse[graphName]; !exist {
			err := ERR_REQUEST_TIMEOUT.New(errors.Params{"graphName": graphName})
			resp := GraphResponse{
				GraphName: graphName,
				Error:     err,
			}
			graphsResponse[graphName] = resp
		}
	}

	for _, graphResponse := range graphsResponse {
		p.writeCookiesAndHeaders(graphResponse.RespPayload, w, r)
	}

	p.respHandler(graphsResponse, w, r)

	return
}

func (p *InletHTTP) writeCookiesAndHeaders(payload spirit.Payload, w http.ResponseWriter, r *http.Request) {
	var err error
	// Cookies
	cmdCookiesSize := payload.GetCommandValueSize(CMD_HTTP_COOKIES_SET)
	cmdCookies := make([]interface{}, cmdCookiesSize)
	for i := 0; i < cmdCookiesSize; i++ {
		cookie := new(http.Cookie)
		cmdCookies[i] = cookie
	}

	if err = payload.GetCommandObjectArray(CMD_HTTP_COOKIES_SET, cmdCookies); err != nil {
		err = ERR_PARSE_COMMAND_TO_OBJECT_FAILED.New(errors.Params{"cmd": CMD_HTTP_COOKIES_SET, "err": err})
		p.errRespHandler(err, w, r)
		return
	}

	for _, cookie := range cmdCookies {
		if c, ok := cookie.(*http.Cookie); ok {
			c.Domain = p.config.Domain
			c.Path = "/"
			http.SetCookie(w, c)
		} else {
			err = ERR_PARSE_COMMAND_TO_OBJECT_FAILED.New(errors.Params{"cmd": CMD_HTTP_COOKIES_SET, "err": "object could not parser to cookies"})
			logs.Error(err)
			p.errRespHandler(err, w, r)
			return
		}
	}

	cmdHeadersSize := payload.GetCommandValueSize(CMD_HTTP_HEADERS_SET)
	cmdHeaders := make([]interface{}, cmdHeadersSize)
	for i := 0; i < cmdHeadersSize; i++ {
		header := new(NameValue)
		cmdHeaders[i] = header
	}

	if err = payload.GetCommandObjectArray(CMD_HTTP_HEADERS_SET, cmdHeaders); err != nil {
		err = ERR_PARSE_COMMAND_TO_OBJECT_FAILED.New(errors.Params{"cmd": CMD_HTTP_HEADERS_SET, "err": err})
		logs.Error(err)
		p.errRespHandler(err, w, r)
		return
	}

	for _, header := range cmdHeaders {
		if nv, ok := header.(*NameValue); ok {
			w.Header().Add(nv.Name, nv.Value)
		} else {
			err = ERR_PARSE_COMMAND_TO_OBJECT_FAILED.New(errors.Params{"cmd": CMD_HTTP_HEADERS_SET, "err": "object could not parser to headers"})
			logs.Error(err)
			p.errRespHandler(err, w, r)
			return
		}
	}
}

func (p *InletHTTP) CallBack(payload *spirit.Payload) (result interface{}, err error) {
	p.OnMessageResponse(payload)
	return nil, nil
}

func (p *InletHTTP) Error(payload *spirit.Payload) (result interface{}, err error) {
	p.OnMessageResponse(payload)
	return nil, nil
}

func (p *InletHTTP) OnMessageResponse(payload *spirit.Payload) {
	p.requester.OnMessageReceived(*payload)
}
<|endoftext|>


<commit_before>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package i2c allows users to read from an write to a slave I2C device.
package i2c // import "golang.org/x/exp/io/i2c"

import (
	"golang.org/x/exp/io/i2c/driver"
)

// Device represents an I2C device. Devices must be closed once
// they are no longer in use.
type Device struct {
	conn driver.Conn
}

// TOOD(jbd): Do we need higher level I2C packet writers and readers?

// Read reads at most len(buf) number of bytes from the device. n represents
// the total number of bytes read.
func (d *Device) Read(buf []byte) (n int, err error) {
	return d.conn.Read(buf)
}

// Write writes the given byte buffer to the device. n represents the total
// number of bytes written.
func (d *Device) Write(buf []byte) (n int, err error) {
	return d.conn.Write(buf)
}

// Close closes the device and releases the underlying sources.
// All devices must be closed once they are no longer in use.
func (d *Device) Close() error {
	return d.conn.Close()
}

// Open opens an I2C device with the given I2C address on the specified bus.
func Open(o driver.Opener, bus, addr int) (*Device, error) {
	conn, err := o.Open(bus, addr)
	if err != nil {
		return nil, err
	}
	return &Device{conn: conn}, nil
}
<commit_msg>io/i2c: use devfs if no other Opener is specified<commit_after>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package i2c allows users to read from an write to a slave I2C device.
package i2c // import "golang.org/x/exp/io/i2c"

import (
	"golang.org/x/exp/io/i2c/driver"
)

// Device represents an I2C device. Devices must be closed once
// they are no longer in use.
type Device struct {
	conn driver.Conn
}

// TOOD(jbd): Do we need higher level I2C packet writers and readers?

// Read reads at most len(buf) number of bytes from the device. n represents
// the total number of bytes read.
func (d *Device) Read(buf []byte) (n int, err error) {
	return d.conn.Read(buf)
}

// Write writes the given byte buffer to the device. n represents the total
// number of bytes written.
func (d *Device) Write(buf []byte) (n int, err error) {
	return d.conn.Write(buf)
}

// Close closes the device and releases the underlying sources.
// All devices must be closed once they are no longer in use.
func (d *Device) Close() error {
	return d.conn.Close()
}

// Open opens an I2C device with the given I2C address on the specified bus.
func Open(o driver.Opener, bus, addr int) (*Device, error) {
	if o == nil {
		o = &DevFS{}
	}
	conn, err := o.Open(bus, addr)
	if err != nil {
		return nil, err
	}
	return &Device{conn: conn}, nil
}
<|endoftext|>


<commit_before>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package crypto collects common cryptographic constants.
package crypto

import (
	"hash"
	"io"
	"strconv"
)

// Hash identifies a cryptographic hash function that is implemented in another
// package.
type Hash uint

// HashFunc simply returns the value of h so that Hash implements SignerOpts.
func (h Hash) HashFunc() Hash {
	return h
}

const (
	MD4       Hash = 1 + iota // import code.google.com/p/go.crypto/md4
	MD5                       // import crypto/md5
	SHA1                      // import crypto/sha1
	SHA224                    // import crypto/sha256
	SHA256                    // import crypto/sha256
	SHA384                    // import crypto/sha512
	SHA512                    // import crypto/sha512
	MD5SHA1                   // no implementation; MD5+SHA1 used for TLS RSA
	RIPEMD160                 // import code.google.com/p/go.crypto/ripemd160
	maxHash
)

var digestSizes = []uint8{
	MD4:       16,
	MD5:       16,
	SHA1:      20,
	SHA224:    28,
	SHA256:    32,
	SHA384:    48,
	SHA512:    64,
	MD5SHA1:   36,
	RIPEMD160: 20,
}

// Size returns the length, in bytes, of a digest resulting from the given hash
// function. It doesn't require that the hash function in question be linked
// into the program.
func (h Hash) Size() int {
	if h > 0 && h < maxHash {
		return int(digestSizes[h])
	}
	panic("crypto: Size of unknown hash function")
}

var hashes = make([]func() hash.Hash, maxHash)

// New returns a new hash.Hash calculating the given hash function. New panics
// if the hash function is not linked into the binary.
func (h Hash) New() hash.Hash {
	if h > 0 && h < maxHash {
		f := hashes[h]
		if f != nil {
			return f()
		}
	}
	panic("crypto: requested hash function #" + strconv.Itoa(int(h)) + " is unavailable")
}

// Available reports whether the given hash function is linked into the binary.
func (h Hash) Available() bool {
	return h < maxHash && hashes[h] != nil
}

// RegisterHash registers a function that returns a new instance of the given
// hash function. This is intended to be called from the init function in
// packages that implement hash functions.
func RegisterHash(h Hash, f func() hash.Hash) {
	if h >= maxHash {
		panic("crypto: RegisterHash of unknown hash function")
	}
	hashes[h] = f
}

// PublicKey represents a public key using an unspecified algorithm.
type PublicKey interface{}

// PrivateKey represents a private key using an unspecified algorithm.
type PrivateKey interface{}

// Signer is an interface for an opaque private key that can be used for
// signing operations. For example, an RSA key kept in a hardware module.
type Signer interface {
	// Public returns the public key corresponding to the opaque,
	// private key.
	Public() PublicKey

	// Sign signs msg with the private key, possibly using entropy from
	// rand. For an RSA key, the resulting signature should be either a
	// PKCS#1 v1.5 or PSS signature (as indicated by opts). For an (EC)DSA
	// key, it should be a DER-serialised, ASN.1 signature structure.
	//
	// Hash implements the SignerOpts interface and, in most cases, one can
	// simply pass in the hash function used as opts. Sign may also attempt
	// to type assert opts to other types in order to obtain algorithm
	// specific values. See the documentation in each package for details.
	Sign(rand io.Reader, msg []byte, opts SignerOpts) (signature []byte, err error)
}

// SignerOpts contains options for signing with a Signer.
type SignerOpts interface {
	// HashFunc returns an identifier for the hash function used to produce
	// the message passed to Signer.Sign, or else zero to indicate that no
	// hashing was done.
	HashFunc() Hash
}
<commit_msg>crypto: Add SHA3 functions in go.crypto/sha3 to the Hash enum.<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package crypto collects common cryptographic constants.
package crypto

import (
	"hash"
	"io"
	"strconv"
)

// Hash identifies a cryptographic hash function that is implemented in another
// package.
type Hash uint

// HashFunc simply returns the value of h so that Hash implements SignerOpts.
func (h Hash) HashFunc() Hash {
	return h
}

const (
	MD4       Hash = 1 + iota // import code.google.com/p/go.crypto/md4
	MD5                       // import crypto/md5
	SHA1                      // import crypto/sha1
	SHA224                    // import crypto/sha256
	SHA256                    // import crypto/sha256
	SHA384                    // import crypto/sha512
	SHA512                    // import crypto/sha512
	MD5SHA1                   // no implementation; MD5+SHA1 used for TLS RSA
	RIPEMD160                 // import code.google.com/p/go.crypto/ripemd160
	SHA3_224                  // import code.google.com/p/go.crypto/sha3
	SHA3_256                  // import code.google.com/p/go.crypto/sha3
	SHA3_384                  // import code.google.com/p/go.crypto/sha3
	SHA3_512                  // import code.google.com/p/go.crypto/sha3
	maxHash
)

var digestSizes = []uint8{
	MD4:       16,
	MD5:       16,
	SHA1:      20,
	SHA224:    28,
	SHA256:    32,
	SHA384:    48,
	SHA512:    64,
	SHA3_224:  28,
	SHA3_256:  32,
	SHA3_384:  48,
	SHA3_512:  64,
	MD5SHA1:   36,
	RIPEMD160: 20,
}

// Size returns the length, in bytes, of a digest resulting from the given hash
// function. It doesn't require that the hash function in question be linked
// into the program.
func (h Hash) Size() int {
	if h > 0 && h < maxHash {
		return int(digestSizes[h])
	}
	panic("crypto: Size of unknown hash function")
}

var hashes = make([]func() hash.Hash, maxHash)

// New returns a new hash.Hash calculating the given hash function. New panics
// if the hash function is not linked into the binary.
func (h Hash) New() hash.Hash {
	if h > 0 && h < maxHash {
		f := hashes[h]
		if f != nil {
			return f()
		}
	}
	panic("crypto: requested hash function #" + strconv.Itoa(int(h)) + " is unavailable")
}

// Available reports whether the given hash function is linked into the binary.
func (h Hash) Available() bool {
	return h < maxHash && hashes[h] != nil
}

// RegisterHash registers a function that returns a new instance of the given
// hash function. This is intended to be called from the init function in
// packages that implement hash functions.
func RegisterHash(h Hash, f func() hash.Hash) {
	if h >= maxHash {
		panic("crypto: RegisterHash of unknown hash function")
	}
	hashes[h] = f
}

// PublicKey represents a public key using an unspecified algorithm.
type PublicKey interface{}

// PrivateKey represents a private key using an unspecified algorithm.
type PrivateKey interface{}

// Signer is an interface for an opaque private key that can be used for
// signing operations. For example, an RSA key kept in a hardware module.
type Signer interface {
	// Public returns the public key corresponding to the opaque,
	// private key.
	Public() PublicKey

	// Sign signs msg with the private key, possibly using entropy from
	// rand. For an RSA key, the resulting signature should be either a
	// PKCS#1 v1.5 or PSS signature (as indicated by opts). For an (EC)DSA
	// key, it should be a DER-serialised, ASN.1 signature structure.
	//
	// Hash implements the SignerOpts interface and, in most cases, one can
	// simply pass in the hash function used as opts. Sign may also attempt
	// to type assert opts to other types in order to obtain algorithm
	// specific values. See the documentation in each package for details.
	Sign(rand io.Reader, msg []byte, opts SignerOpts) (signature []byte, err error)
}

// SignerOpts contains options for signing with a Signer.
type SignerOpts interface {
	// HashFunc returns an identifier for the hash function used to produce
	// the message passed to Signer.Sign, or else zero to indicate that no
	// hashing was done.
	HashFunc() Hash
}
<|endoftext|>


<commit_before>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package net

import (
	"runtime"
	"testing"
	"time"
)

func newLocalListener(t *testing.T) Listener {
	ln, err := Listen("tcp", "127.0.0.1:0")
	if err != nil {
		ln, err = Listen("tcp6", "[::1]:0")
	}
	if err != nil {
		t.Fatal(err)
	}
	return ln
}

func TestDialTimeout(t *testing.T) {
	ln := newLocalListener(t)
	defer ln.Close()

	errc := make(chan error)

	const SOMAXCONN = 0x80 // copied from syscall, but not always available
	const numConns = SOMAXCONN + 10

	// TODO(bradfitz): It's hard to test this in a portable
	// way. This is unforunate, but works for now.
	switch runtime.GOOS {
	case "linux":
		// The kernel will start accepting TCP connections before userspace
		// gets a chance to not accept them, so fire off a bunch to fill up
		// the kernel's backlog.  Then we test we get a failure after that.
		for i := 0; i < numConns; i++ {
			go func() {
				_, err := DialTimeout("tcp", ln.Addr().String(), 200*time.Millisecond)
				errc <- err
			}()
		}
	case "darwin":
		// At least OS X 10.7 seems to accept any number of
		// connections, ignoring listen's backlog, so resort
		// to connecting to a hopefully-dead 127/8 address.
		go func() {
			_, err := DialTimeout("tcp", "127.0.71.111:80", 200*time.Millisecond)
			errc <- err
		}()
	default:
		// TODO(bradfitz): this probably doesn't work on
		// Windows? SOMAXCONN is huge there.  I'm not sure how
		// listen works there.
		// OpenBSD may have a reject route to 10/8.
		// FreeBSD likely works, but is untested.
		t.Logf("skipping test on %q; untested.", runtime.GOOS)
		return
	}

	connected := 0
	for {
		select {
		case <-time.After(15 * time.Second):
			t.Fatal("too slow")
		case err := <-errc:
			if err == nil {
				connected++
				if connected == numConns {
					t.Fatal("all connections connected; expected some to time out")
				}
			} else {
				terr, ok := err.(timeout)
				if !ok {
					t.Fatalf("got error %q; want error with timeout interface", err)
				}
				if !terr.Timeout() {
					t.Fatalf("got error %q; not a timeout", err)
				}
				// Pass. We saw a timeout error.
				return
			}
		}
	}
}
<commit_msg>net: run TestDialTimeout on windows<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package net

import (
	"runtime"
	"testing"
	"time"
)

func newLocalListener(t *testing.T) Listener {
	ln, err := Listen("tcp", "127.0.0.1:0")
	if err != nil {
		ln, err = Listen("tcp6", "[::1]:0")
	}
	if err != nil {
		t.Fatal(err)
	}
	return ln
}

func TestDialTimeout(t *testing.T) {
	ln := newLocalListener(t)
	defer ln.Close()

	errc := make(chan error)

	const SOMAXCONN = 0x80 // copied from syscall, but not always available
	const numConns = SOMAXCONN + 10

	// TODO(bradfitz): It's hard to test this in a portable
	// way. This is unforunate, but works for now.
	switch runtime.GOOS {
	case "linux":
		// The kernel will start accepting TCP connections before userspace
		// gets a chance to not accept them, so fire off a bunch to fill up
		// the kernel's backlog.  Then we test we get a failure after that.
		for i := 0; i < numConns; i++ {
			go func() {
				_, err := DialTimeout("tcp", ln.Addr().String(), 200*time.Millisecond)
				errc <- err
			}()
		}
	case "darwin", "windows":
		// At least OS X 10.7 seems to accept any number of
		// connections, ignoring listen's backlog, so resort
		// to connecting to a hopefully-dead 127/8 address.
		// Same for windows.
		go func() {
			_, err := DialTimeout("tcp", "127.0.71.111:80", 200*time.Millisecond)
			errc <- err
		}()
	default:
		// TODO(bradfitz):
		// OpenBSD may have a reject route to 10/8.
		// FreeBSD likely works, but is untested.
		t.Logf("skipping test on %q; untested.", runtime.GOOS)
		return
	}

	connected := 0
	for {
		select {
		case <-time.After(15 * time.Second):
			t.Fatal("too slow")
		case err := <-errc:
			if err == nil {
				connected++
				if connected == numConns {
					t.Fatal("all connections connected; expected some to time out")
				}
			} else {
				terr, ok := err.(timeout)
				if !ok {
					t.Fatalf("got error %q; want error with timeout interface", err)
				}
				if !terr.Timeout() {
					t.Fatalf("got error %q; not a timeout", err)
				}
				// Pass. We saw a timeout error.
				return
			}
		}
	}
}
<|endoftext|>


<commit_before>// RTLAMR - An rtl-sdr receiver for smart meters operating in the 900MHz ISM band.
// Copyright (C) 2014 Douglas Hall
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package r900

import (
	"bytes"
	"fmt"
	"math"
	"strconv"

	"github.com/bemasher/rtlamr/decode"
	"github.com/bemasher/rtlamr/parse"
	"github.com/bemasher/rtlamr/r900/gf"
)

const (
	PayloadSymbols = 42
)

func NewPacketConfig(symbolLength int) (cfg decode.PacketConfig) {
	cfg.DataRate = 32768

	cfg.CenterFreq = 912380000

	cfg.SymbolLength = symbolLength
	cfg.SymbolLength2 = cfg.SymbolLength << 1

	cfg.SampleRate = cfg.DataRate * cfg.SymbolLength

	cfg.PreambleSymbols = 32
	cfg.PacketSymbols = 116

	cfg.PreambleLength = cfg.PreambleSymbols * cfg.SymbolLength2
	cfg.PacketLength = cfg.PacketSymbols * cfg.SymbolLength2

	cfg.BlockSize = decode.NextPowerOf2(cfg.PreambleLength)
	cfg.BlockSize2 = cfg.BlockSize << 1

	cfg.BufferLength = cfg.PacketLength + cfg.BlockSize

	cfg.Preamble = "00000000000000001110010101100100"

	return
}

type Parser struct {
	decode.Decoder
	field *gf.Field
	rsBuf [31]byte

	csum      []float64
	filtered  [][3]float64
	quantized []byte
}

func NewParser(symbolLength int, fastMag bool) (p Parser) {
	p.Decoder = decode.NewDecoder(NewPacketConfig(symbolLength), fastMag)

	// GF of order 32, polynomial 37, generator 2.
	p.field = gf.NewField(32, 37, 2)

	p.csum = make([]float64, p.Decoder.Cfg.BufferLength+1)
	p.filtered = make([][3]float64, p.Decoder.Cfg.BufferLength)
	p.quantized = make([]byte, p.Decoder.Cfg.BufferLength)

	return
}

func (p Parser) Dec() decode.Decoder {
	return p.Decoder
}

func (p Parser) Cfg() decode.PacketConfig {
	return p.Decoder.Cfg
}

// Perform matched filtering.
func (p Parser) Filter() {
	// This function computes the convolution of each symbol kernel with the
	// signal. The naive approach requires for each symbol to calculate the
	// summation of samples between a pair of indices.

	// 0 |--------|
	// 1   |--------|
	// 2     |--------|
	// 3       |--------|

	// To avoid redundant calculations we compute the cumulative sum of the
	// signal. This reduces each summation to the difference between the two
	// indices of the cumulative sum.

	var sum float64
	for idx, v := range p.Decoder.Signal {
		sum += v
		p.csum[idx+1] = sum
	}

	// There are six symbols, composed of three base symbols and their bitwise
	// inversions. Compute the convolution of each base symbol with the
	// signal.

	// 1100 -> 0011
	// 1010 -> 0101
	// 1001 -> 0110

	// This is basically unreadable because of a lot of algebraic
	// simplification but is necessary for efficiency.
	for idx := 0; idx < p.Decoder.Cfg.BufferLength-p.Decoder.Cfg.SymbolLength*4; idx++ {
		c0 := p.csum[idx]
		c1 := p.csum[idx+p.Decoder.Cfg.SymbolLength] * 2
		c2 := p.csum[idx+p.Decoder.Cfg.SymbolLength*2] * 2
		c3 := p.csum[idx+p.Decoder.Cfg.SymbolLength*3] * 2
		c4 := p.csum[idx+p.Decoder.Cfg.SymbolLength*4]

		p.filtered[idx][0] = c2 - c4 - c0           // 1100
		p.filtered[idx][1] = c1 - c2 + c3 - c4 - c0 // 1010
		p.filtered[idx][2] = c1 - c3 + c4 - c0      // 1001
	}
}

// Determine the symbol that exists at each sample of the signal.
func (p Parser) Quantize() {
	// 0 0011, 3 1100
	// 1 0101, 4 1010
	// 2 0110, 5 1001

	for idx, vec := range p.filtered {
		argmax := byte(0)
		max := math.Abs(vec[0])

		// If v1 is larger than v0, update max and argmax.
		if v1 := math.Abs(vec[1]); v1 > max {
			max = v1
			argmax = 1
		}

		// If v2 is larger than the greater of v1 or v0, update max and argmax.
		if v2 := math.Abs(vec[2]); v2 > max {
			max = v2
			argmax = 2
		}

		// Set the output symbol index.
		p.quantized[idx] = argmax

		// If the sign is negative, jump to the index of the inverted symbol.
		if vec[argmax] > 0 {
			p.quantized[idx] += 3
		}
	}
}

// Given a list of indices the preamble exists at, decode and parse a message.
func (p Parser) Parse(indices []int) (msgs []parse.Message) {
	p.Filter()
	p.Quantize()

	preambleLength := p.Decoder.Cfg.PreambleLength
	symbolLength := p.Decoder.Cfg.SymbolLength

	symbols := make([]byte, 21)
	zeros := make([]byte, 5)

	seen := make(map[string]bool)

	for _, preambleIdx := range indices {
		if preambleIdx > p.Decoder.Cfg.BlockSize {
			break
		}

		payloadIdx := preambleIdx + preambleLength
		var digits string
		for idx := 0; idx < PayloadSymbols*4*p.Decoder.Cfg.SymbolLength; idx += symbolLength * 4 {
			qIdx := payloadIdx + idx

			digits += strconv.Itoa(int(p.quantized[qIdx]))
		}

		var (
			bits      string
			badSymbol bool
		)
		for idx := 0; idx < len(digits); idx += 2 {
			symbol, _ := strconv.ParseInt(digits[idx:idx+2], 6, 32)
			if symbol > 31 {
				badSymbol = true
				break
			}
			symbols[idx>>1] = byte(symbol)
			bits += fmt.Sprintf("%05b", symbol)
		}

		if badSymbol || seen[bits] {
			continue
		}

		seen[bits] = true

		copy(p.rsBuf[:], symbols[:16])
		copy(p.rsBuf[26:], symbols[16:])
		syndromes := p.field.Syndrome(p.rsBuf[:], 5, 29)

		if !bytes.Equal(zeros, syndromes) {
			continue
		}

		id, _ := strconv.ParseUint(bits[:32], 2, 32)
		unkn1, _ := strconv.ParseUint(bits[32:40], 2, 8)
		unkn2, _ := strconv.ParseUint(bits[40:48], 2, 8)
		consumption, _ := strconv.ParseUint(bits[48:72], 2, 24)
		unkn3, _ := strconv.ParseUint(bits[72:74], 2, 2)
		unkn4, _ := strconv.ParseUint(bits[74:80], 2, 6)

		var r900 R900

		r900.ID = uint32(id)
		r900.Unkn1 = uint8(unkn1)
		r900.Unkn2 = uint8(unkn2)
		r900.Consumption = uint32(consumption)
		r900.Unkn3 = uint8(unkn3)
		r900.Unkn4 = uint8(unkn4)

		msgs = append(msgs, r900)
	}

	return
}

type R900 struct {
	ID          uint32 `xml:",attr"` // 32 bits
	Unkn1       uint8  `xml:",attr"` // 8 bits
	Unkn2       uint8  `xml:",attr"` // 8 bits
	Consumption uint32 `xml:",attr"` // 24 bits
	Unkn3       uint8  `xml:",attr"` // 2 bits
	Unkn4       uint8  `xml:",attr"` // 6 bits
}

func (r900 R900) MsgType() string {
	return "R900"
}

func (r900 R900) MeterID() uint32 {
	return r900.ID
}

func (r900 R900) MeterType() uint8 {
	return r900.Unkn1
}

func (r900 R900) String() string {
	return fmt.Sprintf("{ID:%10d Unkn1:0x%02X Unkn2:0x%02X Consumption:%8d Unkn3:0x%02X Unkn4:0x%02X}",
		r900.ID,
		r900.Unkn1,
		r900.Unkn2,
		r900.Consumption,
		r900.Unkn3,
		r900.Unkn4,
	)
}

func (r900 R900) Record() (r []string) {
	r = append(r, strconv.FormatUint(uint64(r900.ID), 10))
	r = append(r, strconv.FormatUint(uint64(r900.Unkn1), 10))
	r = append(r, strconv.FormatUint(uint64(r900.Unkn2), 10))
	r = append(r, strconv.FormatUint(uint64(r900.Consumption), 10))
	r = append(r, strconv.FormatUint(uint64(r900.Unkn3), 10))
	r = append(r, strconv.FormatUint(uint64(r900.Unkn4), 10))

	return
}
<commit_msg>r900: decode leak, fraud, and backflow status flags<commit_after>// RTLAMR - An rtl-sdr receiver for smart meters operating in the 900MHz ISM band.
// Copyright (C) 2014 Douglas Hall
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package r900

import (
	"bytes"
	"fmt"
	"math"
	"strconv"

	"github.com/bemasher/rtlamr/decode"
	"github.com/bemasher/rtlamr/parse"
	"github.com/bemasher/rtlamr/r900/gf"
)

const (
	PayloadSymbols = 42
)

func NewPacketConfig(symbolLength int) (cfg decode.PacketConfig) {
	cfg.DataRate = 32768

	cfg.CenterFreq = 912380000

	cfg.SymbolLength = symbolLength
	cfg.SymbolLength2 = cfg.SymbolLength << 1

	cfg.SampleRate = cfg.DataRate * cfg.SymbolLength

	cfg.PreambleSymbols = 32
	cfg.PacketSymbols = 116

	cfg.PreambleLength = cfg.PreambleSymbols * cfg.SymbolLength2
	cfg.PacketLength = cfg.PacketSymbols * cfg.SymbolLength2

	cfg.BlockSize = decode.NextPowerOf2(cfg.PreambleLength)
	cfg.BlockSize2 = cfg.BlockSize << 1

	cfg.BufferLength = cfg.PacketLength + cfg.BlockSize

	cfg.Preamble = "00000000000000001110010101100100"

	return
}

type Parser struct {
	decode.Decoder
	field *gf.Field
	rsBuf [31]byte

	csum      []float64
	filtered  [][3]float64
	quantized []byte
}

func NewParser(symbolLength int, fastMag bool) (p Parser) {
	p.Decoder = decode.NewDecoder(NewPacketConfig(symbolLength), fastMag)

	// GF of order 32, polynomial 37, generator 2.
	p.field = gf.NewField(32, 37, 2)

	p.csum = make([]float64, p.Decoder.Cfg.BufferLength+1)
	p.filtered = make([][3]float64, p.Decoder.Cfg.BufferLength)
	p.quantized = make([]byte, p.Decoder.Cfg.BufferLength)

	return
}

func (p Parser) Dec() decode.Decoder {
	return p.Decoder
}

func (p Parser) Cfg() decode.PacketConfig {
	return p.Decoder.Cfg
}

// Perform matched filtering.
func (p Parser) Filter() {
	// This function computes the convolution of each symbol kernel with the
	// signal. The naive approach requires for each symbol to calculate the
	// summation of samples between a pair of indices.

	// 0 |--------|
	// 1   |--------|
	// 2     |--------|
	// 3       |--------|

	// To avoid redundant calculations we compute the cumulative sum of the
	// signal. This reduces each summation to the difference between the two
	// indices of the cumulative sum.

	var sum float64
	for idx, v := range p.Decoder.Signal {
		sum += v
		p.csum[idx+1] = sum
	}

	// There are six symbols, composed of three base symbols and their bitwise
	// inversions. Compute the convolution of each base symbol with the
	// signal.

	// 1100 -> 0011
	// 1010 -> 0101
	// 1001 -> 0110

	// This is basically unreadable because of a lot of algebraic
	// simplification but is necessary for efficiency.
	for idx := 0; idx < p.Decoder.Cfg.BufferLength-p.Decoder.Cfg.SymbolLength*4; idx++ {
		c0 := p.csum[idx]
		c1 := p.csum[idx+p.Decoder.Cfg.SymbolLength] * 2
		c2 := p.csum[idx+p.Decoder.Cfg.SymbolLength*2] * 2
		c3 := p.csum[idx+p.Decoder.Cfg.SymbolLength*3] * 2
		c4 := p.csum[idx+p.Decoder.Cfg.SymbolLength*4]

		p.filtered[idx][0] = c2 - c4 - c0           // 1100
		p.filtered[idx][1] = c1 - c2 + c3 - c4 - c0 // 1010
		p.filtered[idx][2] = c1 - c3 + c4 - c0      // 1001
	}
}

// Determine the symbol that exists at each sample of the signal.
func (p Parser) Quantize() {
	// 0 0011, 3 1100
	// 1 0101, 4 1010
	// 2 0110, 5 1001

	for idx, vec := range p.filtered {
		argmax := byte(0)
		max := math.Abs(vec[0])

		// If v1 is larger than v0, update max and argmax.
		if v1 := math.Abs(vec[1]); v1 > max {
			max = v1
			argmax = 1
		}

		// If v2 is larger than the greater of v1 or v0, update max and argmax.
		if v2 := math.Abs(vec[2]); v2 > max {
			max = v2
			argmax = 2
		}

		// Set the output symbol index.
		p.quantized[idx] = argmax

		// If the sign is negative, jump to the index of the inverted symbol.
		if vec[argmax] > 0 {
			p.quantized[idx] += 3
		}
	}
}

// Given a list of indices the preamble exists at, decode and parse a message.
func (p Parser) Parse(indices []int) (msgs []parse.Message) {
	p.Filter()
	p.Quantize()

	preambleLength := p.Decoder.Cfg.PreambleLength
	symbolLength := p.Decoder.Cfg.SymbolLength

	symbols := make([]byte, 21)
	zeros := make([]byte, 5)

	seen := make(map[string]bool)

	for _, preambleIdx := range indices {
		if preambleIdx > p.Decoder.Cfg.BlockSize {
			break
		}

		payloadIdx := preambleIdx + preambleLength
		var digits string
		for idx := 0; idx < PayloadSymbols*4*p.Decoder.Cfg.SymbolLength; idx += symbolLength * 4 {
			qIdx := payloadIdx + idx

			digits += strconv.Itoa(int(p.quantized[qIdx]))
		}

		var (
			bits      string
			badSymbol bool
		)
		for idx := 0; idx < len(digits); idx += 2 {
			symbol, _ := strconv.ParseInt(digits[idx:idx+2], 6, 32)
			if symbol > 31 {
				badSymbol = true
				break
			}
			symbols[idx>>1] = byte(symbol)
			bits += fmt.Sprintf("%05b", symbol)
		}

		if badSymbol || seen[bits] {
			continue
		}

		seen[bits] = true

		copy(p.rsBuf[:], symbols[:16])
		copy(p.rsBuf[26:], symbols[16:])
		syndromes := p.field.Syndrome(p.rsBuf[:], 5, 29)

		if !bytes.Equal(zeros, syndromes) {
			continue
		}

		id, _ := strconv.ParseUint(bits[:32], 2, 32)
		unkn1, _ := strconv.ParseUint(bits[32:40], 2, 8)
		nouse, _ := strconv.ParseUint(bits[40:46], 2, 6)
		backflow, _ := strconv.ParseUint(bits[46:48], 2, 2)
		consumption, _ := strconv.ParseUint(bits[48:72], 2, 24)
		unkn3, _ := strconv.ParseUint(bits[72:74], 2, 2)
		leak, _ := strconv.ParseUint(bits[74:78], 2, 4)
		leaknow, _ := strconv.ParseUint(bits[78:80], 2, 2)

		var r900 R900

		r900.ID = uint32(id)
		r900.Unkn1 = uint8(unkn1)
		r900.NoUse = uint8(nouse)
		r900.BackFlow = uint8(backflow)
		r900.Consumption = uint32(consumption)
		r900.Unkn3 = uint8(unkn3)
		r900.Leak = uint8(leak)
		r900.LeakNow = uint8(leaknow)

		msgs = append(msgs, r900)
	}

	return
}

type R900 struct {
	ID          uint32 `xml:",attr"` // 32 bits
	Unkn1       uint8  `xml:",attr"` // 8 bits
	NoUse       uint8  `xml:",attr"` // 6 bits, day bins of no use
	BackFlow    uint8  `xml:",attr"` // 2 bits, backflow past 35d hi/lo
	Consumption uint32 `xml:",attr"` // 24 bits
	Unkn3       uint8  `xml:",attr"` // 2 bits
	Leak        uint8  `xml:",attr"` // 4 bits, day bins of leak
	LeakNow     uint8  `xml:",attr"` // 2 bits, leak past 24h hi/lo

}

func (r900 R900) MsgType() string {
	return "R900"
}

func (r900 R900) MeterID() uint32 {
	return r900.ID
}

func (r900 R900) MeterType() uint8 {
	return r900.Unkn1
}

func (r900 R900) String() string {
	return fmt.Sprintf("{ID:%10d Unkn1:0x%02X NoUse:%2d BackFlow:%1d Consumption:%8d Unkn3:0x%02X Leak:%2d LeakNow:%1d}",
		r900.ID,
		r900.Unkn1,
		r900.NoUse,
		r900.BackFlow,
		r900.Consumption,
		r900.Unkn3,
		r900.Leak,
		r900.LeakNow,
	)
}

func (r900 R900) Record() (r []string) {
	r = append(r, strconv.FormatUint(uint64(r900.ID), 10))
	r = append(r, strconv.FormatUint(uint64(r900.Unkn1), 10))
	r = append(r, strconv.FormatUint(uint64(r900.NoUse), 10))
	r = append(r, strconv.FormatUint(uint64(r900.BackFlow), 10))
	r = append(r, strconv.FormatUint(uint64(r900.Consumption), 10))
	r = append(r, strconv.FormatUint(uint64(r900.Unkn3), 10))
	r = append(r, strconv.FormatUint(uint64(r900.Leak), 10))
	r = append(r, strconv.FormatUint(uint64(r900.LeakNow), 10))

	return
}
<|endoftext|>


<commit_before>package raft

import (
	"errors"
	"sort"
)

const none = -1

type messageType int

const (
	msgHup messageType = iota
	msgBeat
	msgProp
	msgApp
	msgAppResp
	msgVote
	msgVoteResp
)

var mtmap = [...]string{
	msgHup:      "msgHup",
	msgBeat:     "msgBeat",
	msgProp:     "msgProp",
	msgApp:      "msgApp",
	msgAppResp:  "msgAppResp",
	msgVote:     "msgVote",
	msgVoteResp: "msgVoteResp",
}

func (mt messageType) String() string {
	return mtmap[int(mt)]
}

var errNoLeader = errors.New("no leader")

const (
	stateFollower stateType = iota
	stateCandidate
	stateLeader
)

type stateType int

var stmap = [...]string{
	stateFollower:  "stateFollower",
	stateCandidate: "stateCandidate",
	stateLeader:    "stateLeader",
}

func (st stateType) String() string {
	return stmap[int(st)]
}

type Message struct {
	Type     messageType
	To       int
	From     int
	Term     int
	LogTerm  int
	Index    int
	PrevTerm int
	Entries  []Entry
	Commit   int
}

type index struct {
	match, next int
}

func (in *index) update(n int) {
	in.match = n
	in.next = n + 1
}

func (in *index) decr() {
	if in.next--; in.next < 1 {
		in.next = 1
	}
}

type stateMachine struct {
	id int

	// the term we are participating in at any time
	term int

	// who we voted for in term
	vote int

	// the log
	log *log

	ins map[int]*index

	state stateType

	votes map[int]bool

	msgs []Message

	// the leader id
	lead int

	// pending reconfiguration
	pendingConf bool
}

func newStateMachine(id int, peers []int) *stateMachine {
	sm := &stateMachine{id: id, log: newLog(), ins: make(map[int]*index)}
	for p := range peers {
		sm.ins[p] = &index{}
	}
	sm.reset()
	return sm
}

func (sm *stateMachine) canStep(m Message) bool {
	if m.Type == msgProp {
		return sm.lead != none
	}
	return true
}

func (sm *stateMachine) poll(id int, v bool) (granted int) {
	if _, ok := sm.votes[id]; !ok {
		sm.votes[id] = v
	}
	for _, vv := range sm.votes {
		if vv {
			granted++
		}
	}
	return granted
}

// send persists state to stable storage and then sends to its mailbox.
func (sm *stateMachine) send(m Message) {
	m.From = sm.id
	m.Term = sm.term
	sm.msgs = append(sm.msgs, m)
}

// sendAppend sends RRPC, with entries to the given peer.
func (sm *stateMachine) sendAppend(to int) {
	in := sm.ins[to]
	m := Message{}
	m.Type = msgApp
	m.To = to
	m.Index = in.next - 1
	m.LogTerm = sm.log.term(in.next - 1)
	m.Entries = sm.log.entries(in.next)
	m.Commit = sm.log.committed
	sm.send(m)
}

// bcastAppend sends RRPC, with entries to all peers that are not up-to-date according to sm.mis.
func (sm *stateMachine) bcastAppend() {
	for i := range sm.ins {
		if i == sm.id {
			continue
		}
		sm.sendAppend(i)
	}
}

func (sm *stateMachine) maybeCommit() bool {
	// TODO(bmizerany): optimize.. Currently naive
	mis := make([]int, len(sm.ins))
	for i := range mis {
		mis[i] = sm.ins[i].match
	}
	sort.Sort(sort.Reverse(sort.IntSlice(mis)))
	mci := mis[sm.q()-1]

	return sm.log.maybeCommit(mci, sm.term)
}

// nextEnts returns the appliable entries and updates the applied index
func (sm *stateMachine) nextEnts() (ents []Entry) {
	return sm.log.nextEnts()
}

func (sm *stateMachine) reset() {
	sm.lead = none
	sm.vote = none
	sm.votes = make(map[int]bool)
	for i := range sm.ins {
		sm.ins[i] = &index{next: sm.log.lastIndex() + 1}
		if i == sm.id {
			sm.ins[i].match = sm.log.lastIndex()
		}
	}
}

func (sm *stateMachine) q() int {
	return len(sm.ins)/2 + 1
}

func (sm *stateMachine) becomeFollower(term, lead int) {
	sm.reset()
	sm.term = term
	sm.lead = lead
	sm.state = stateFollower
	sm.pendingConf = false
}

func (sm *stateMachine) becomeCandidate() {
	// TODO(xiangli) remove the panic when the raft implementation is stable
	if sm.state == stateLeader {
		panic("invalid transition [leader -> candidate]")
	}
	sm.reset()
	sm.term++
	sm.vote = sm.id
	sm.state = stateCandidate
}

func (sm *stateMachine) becomeLeader() {
	// TODO(xiangli) remove the panic when the raft implementation is stable
	if sm.state == stateFollower {
		panic("invalid transition [follower -> leader]")
	}
	sm.reset()
	sm.lead = sm.id
	sm.state = stateLeader

	for _, e := range sm.log.ents[sm.log.committed:] {
		if e.isConfig() {
			sm.pendingConf = true
		}
	}
}

func (sm *stateMachine) Msgs() []Message {
	msgs := sm.msgs
	sm.msgs = make([]Message, 0)

	return msgs
}

func (sm *stateMachine) Step(m Message) (ok bool) {
	switch m.Type {
	case msgHup:
		sm.becomeCandidate()
		if sm.q() == sm.poll(sm.id, true) {
			sm.becomeLeader()
			return true
		}
		for i := range sm.ins {
			if i == sm.id {
				continue
			}
			lasti := sm.log.lastIndex()
			sm.send(Message{To: i, Type: msgVote, Index: lasti, LogTerm: sm.log.term(lasti)})
		}
		return true
	case msgBeat:
		if sm.state != stateLeader {
			return true
		}
		sm.bcastAppend()
		return
	case msgProp:
		if len(m.Entries) != 1 {
			panic("unexpected length(entries) of a msgProp")
		}

		switch sm.lead {
		case sm.id:
			e := m.Entries[0]
			if e.isConfig() {
				if sm.pendingConf {
					return false
				}
				sm.pendingConf = true
			}
			e.Term = sm.term

			sm.log.append(sm.log.lastIndex(), e)
			sm.ins[sm.id].update(sm.log.lastIndex())
			sm.maybeCommit()
			sm.bcastAppend()
		case none:
			// msgProp given without leader
			return false
		default:
			m.To = sm.lead
			sm.send(m)
		}
		return true
	}

	switch {
	case m.Term > sm.term:
		sm.becomeFollower(m.Term, m.From)
	case m.Term < sm.term:
		// ignore
		return true
	}

	handleAppendEntries := func() {
		if sm.log.maybeAppend(m.Index, m.LogTerm, m.Commit, m.Entries...) {
			sm.send(Message{To: m.From, Type: msgAppResp, Index: sm.log.lastIndex()})
		} else {
			sm.send(Message{To: m.From, Type: msgAppResp, Index: -1})
		}
	}

	switch sm.state {
	case stateLeader:
		switch m.Type {
		case msgAppResp:
			if m.Index < 0 {
				sm.ins[m.From].decr()
				sm.sendAppend(m.From)
			} else {
				sm.ins[m.From].update(m.Index)
				if sm.maybeCommit() {
					sm.bcastAppend()
				}
			}
		case msgVote:
			sm.send(Message{To: m.From, Type: msgVoteResp, Index: -1})
		}
	case stateCandidate:
		switch m.Type {
		case msgApp:
			sm.becomeFollower(sm.term, m.From)
			handleAppendEntries()
		case msgVote:
			sm.send(Message{To: m.From, Type: msgVoteResp, Index: -1})
		case msgVoteResp:
			gr := sm.poll(m.From, m.Index >= 0)
			switch sm.q() {
			case gr:
				sm.becomeLeader()
				sm.bcastAppend()
			case len(sm.votes) - gr:
				sm.becomeFollower(sm.term, none)
			}
		}
	case stateFollower:
		switch m.Type {
		case msgApp:
			handleAppendEntries()
		case msgVote:
			if (sm.vote == none || sm.vote == m.From) && sm.log.isUpToDate(m.Index, m.LogTerm) {
				sm.vote = m.From
				sm.send(Message{To: m.From, Type: msgVoteResp, Index: sm.log.lastIndex()})
			} else {
				sm.send(Message{To: m.From, Type: msgVoteResp, Index: -1})
			}
		}
	}
	return true
}

func (sm *stateMachine) addNode(id int) {
	sm.ins[id] = &index{next: sm.log.lastIndex() + 1}
	sm.pendingConf = false
}

func (sm *stateMachine) removeNode(id int) {
	delete(sm.ins, id)
	sm.pendingConf = false
}
<commit_msg>raft: break Step into pieces<commit_after>package raft

import (
	"errors"
	"sort"
)

const none = -1

type messageType int

const (
	msgHup messageType = iota
	msgBeat
	msgProp
	msgApp
	msgAppResp
	msgVote
	msgVoteResp
)

var mtmap = [...]string{
	msgHup:      "msgHup",
	msgBeat:     "msgBeat",
	msgProp:     "msgProp",
	msgApp:      "msgApp",
	msgAppResp:  "msgAppResp",
	msgVote:     "msgVote",
	msgVoteResp: "msgVoteResp",
}

func (mt messageType) String() string {
	return mtmap[int(mt)]
}

var errNoLeader = errors.New("no leader")

const (
	stateFollower stateType = iota
	stateCandidate
	stateLeader
)

type stateType int

var stmap = [...]string{
	stateFollower:  "stateFollower",
	stateCandidate: "stateCandidate",
	stateLeader:    "stateLeader",
}

var stepmap = [...]stepFunc{
	stateFollower:  stepFollower,
	stateCandidate: stepCandidate,
	stateLeader:    stepLeader,
}

func (st stateType) String() string {
	return stmap[int(st)]
}

type Message struct {
	Type     messageType
	To       int
	From     int
	Term     int
	LogTerm  int
	Index    int
	PrevTerm int
	Entries  []Entry
	Commit   int
}

type index struct {
	match, next int
}

func (in *index) update(n int) {
	in.match = n
	in.next = n + 1
}

func (in *index) decr() {
	if in.next--; in.next < 1 {
		in.next = 1
	}
}

type stateMachine struct {
	id int

	// the term we are participating in at any time
	term int

	// who we voted for in term
	vote int

	// the log
	log *log

	ins map[int]*index

	state stateType

	votes map[int]bool

	msgs []Message

	// the leader id
	lead int

	// pending reconfiguration
	pendingConf bool
}

func newStateMachine(id int, peers []int) *stateMachine {
	sm := &stateMachine{id: id, log: newLog(), ins: make(map[int]*index)}
	for p := range peers {
		sm.ins[p] = &index{}
	}
	sm.reset()
	return sm
}

func (sm *stateMachine) canStep(m Message) bool {
	if m.Type == msgProp {
		return sm.lead != none
	}
	return true
}

func (sm *stateMachine) poll(id int, v bool) (granted int) {
	if _, ok := sm.votes[id]; !ok {
		sm.votes[id] = v
	}
	for _, vv := range sm.votes {
		if vv {
			granted++
		}
	}
	return granted
}

// send persists state to stable storage and then sends to its mailbox.
func (sm *stateMachine) send(m Message) {
	m.From = sm.id
	m.Term = sm.term
	sm.msgs = append(sm.msgs, m)
}

// sendAppend sends RRPC, with entries to the given peer.
func (sm *stateMachine) sendAppend(to int) {
	in := sm.ins[to]
	m := Message{}
	m.Type = msgApp
	m.To = to
	m.Index = in.next - 1
	m.LogTerm = sm.log.term(in.next - 1)
	m.Entries = sm.log.entries(in.next)
	m.Commit = sm.log.committed
	sm.send(m)
}

// bcastAppend sends RRPC, with entries to all peers that are not up-to-date according to sm.mis.
func (sm *stateMachine) bcastAppend() {
	for i := range sm.ins {
		if i == sm.id {
			continue
		}
		sm.sendAppend(i)
	}
}

func (sm *stateMachine) maybeCommit() bool {
	// TODO(bmizerany): optimize.. Currently naive
	mis := make([]int, len(sm.ins))
	for i := range mis {
		mis[i] = sm.ins[i].match
	}
	sort.Sort(sort.Reverse(sort.IntSlice(mis)))
	mci := mis[sm.q()-1]

	return sm.log.maybeCommit(mci, sm.term)
}

// nextEnts returns the appliable entries and updates the applied index
func (sm *stateMachine) nextEnts() (ents []Entry) {
	return sm.log.nextEnts()
}

func (sm *stateMachine) reset() {
	sm.lead = none
	sm.vote = none
	sm.votes = make(map[int]bool)
	for i := range sm.ins {
		sm.ins[i] = &index{next: sm.log.lastIndex() + 1}
		if i == sm.id {
			sm.ins[i].match = sm.log.lastIndex()
		}
	}
}

func (sm *stateMachine) q() int {
	return len(sm.ins)/2 + 1
}

func (sm *stateMachine) becomeFollower(term, lead int) {
	sm.reset()
	sm.term = term
	sm.lead = lead
	sm.state = stateFollower
	sm.pendingConf = false
}

func (sm *stateMachine) becomeCandidate() {
	// TODO(xiangli) remove the panic when the raft implementation is stable
	if sm.state == stateLeader {
		panic("invalid transition [leader -> candidate]")
	}
	sm.reset()
	sm.term++
	sm.vote = sm.id
	sm.state = stateCandidate
}

func (sm *stateMachine) becomeLeader() {
	// TODO(xiangli) remove the panic when the raft implementation is stable
	if sm.state == stateFollower {
		panic("invalid transition [follower -> leader]")
	}
	sm.reset()
	sm.lead = sm.id
	sm.state = stateLeader

	for _, e := range sm.log.ents[sm.log.committed:] {
		if e.isConfig() {
			sm.pendingConf = true
		}
	}
}

func (sm *stateMachine) Msgs() []Message {
	msgs := sm.msgs
	sm.msgs = make([]Message, 0)

	return msgs
}

func (sm *stateMachine) Step(m Message) (ok bool) {
	if m.Type == msgHup {
		sm.becomeCandidate()
		if sm.q() == sm.poll(sm.id, true) {
			sm.becomeLeader()
			return true
		}
		for i := range sm.ins {
			if i == sm.id {
				continue
			}
			lasti := sm.log.lastIndex()
			sm.send(Message{To: i, Type: msgVote, Index: lasti, LogTerm: sm.log.term(lasti)})
		}
		return true
	}

	switch {
	case m.Term == 0:
		// local message
	case m.Term > sm.term:
		sm.becomeFollower(m.Term, m.From)
	case m.Term < sm.term:
		// ignore
		return true
	}

	return stepmap[sm.state](sm, m)
}

func (sm *stateMachine) handleAppendEntries(m Message) {
	if sm.log.maybeAppend(m.Index, m.LogTerm, m.Commit, m.Entries...) {
		sm.send(Message{To: m.From, Type: msgAppResp, Index: sm.log.lastIndex()})
	} else {
		sm.send(Message{To: m.From, Type: msgAppResp, Index: -1})
	}
}

func (sm *stateMachine) addNode(id int) {
	sm.ins[id] = &index{next: sm.log.lastIndex() + 1}
	sm.pendingConf = false
}

func (sm *stateMachine) removeNode(id int) {
	delete(sm.ins, id)
	sm.pendingConf = false
}

type stepFunc func(sm *stateMachine, m Message) bool

func stepLeader(sm *stateMachine, m Message) bool {
	switch m.Type {
	case msgBeat:
		sm.bcastAppend()
	case msgProp:
		if len(m.Entries) != 1 {
			panic("unexpected length(entries) of a msgProp")
		}
		e := m.Entries[0]
		if e.isConfig() {
			if sm.pendingConf {
				return false
			}
			sm.pendingConf = true
		}
		e.Term = sm.term

		sm.log.append(sm.log.lastIndex(), e)
		sm.ins[sm.id].update(sm.log.lastIndex())
		sm.maybeCommit()
		sm.bcastAppend()
	case msgAppResp:
		if m.Index < 0 {
			sm.ins[m.From].decr()
			sm.sendAppend(m.From)
		} else {
			sm.ins[m.From].update(m.Index)
			if sm.maybeCommit() {
				sm.bcastAppend()
			}
		}
	case msgVote:
		sm.send(Message{To: m.From, Type: msgVoteResp, Index: -1})
	}
	return true
}

func stepCandidate(sm *stateMachine, m Message) bool {
	switch m.Type {
	case msgProp:
		return false
	case msgApp:
		sm.becomeFollower(sm.term, m.From)
		sm.handleAppendEntries(m)
	case msgVote:
		sm.send(Message{To: m.From, Type: msgVoteResp, Index: -1})
	case msgVoteResp:
		gr := sm.poll(m.From, m.Index >= 0)
		switch sm.q() {
		case gr:
			sm.becomeLeader()
			sm.bcastAppend()
		case len(sm.votes) - gr:
			sm.becomeFollower(sm.term, none)
		}
	}
	return true
}

func stepFollower(sm *stateMachine, m Message) bool {
	switch m.Type {
	case msgProp:
		if sm.lead == none {
			return false
		}
		m.To = sm.lead
		sm.send(m)
	case msgApp:
		sm.handleAppendEntries(m)
	case msgVote:
		if (sm.vote == none || sm.vote == m.From) && sm.log.isUpToDate(m.Index, m.LogTerm) {
			sm.vote = m.From
			sm.send(Message{To: m.From, Type: msgVoteResp, Index: sm.log.lastIndex()})
		} else {
			sm.send(Message{To: m.From, Type: msgVoteResp, Index: -1})
		}
	}
	return true
}
<|endoftext|>