<commit_before>package GoSDK

import (
//"fmt"
)

const (
	_BUCKET_SETS_PREAMBLE = "/api/v/4/bucket_sets/"
)

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

func (d *DevClient) GetBucketSets(systemKey string) ([]interface{}, error) {
	return getBucketSets(d, _BUCKET_SETS_PREAMBLE+systemKey)
}

func (u *UserClient) GetBucketSets(systemKey string) ([]interface{}, error) {
	return getBucketSets(u, _BUCKET_SETS_PREAMBLE+systemKey)
}

func (dv *DeviceClient) GetBucketSets(systemKey string) ([]interface{}, error) {
	return getBucketSets(dv, _BUCKET_SETS_PREAMBLE+systemKey)
}

func getBucketSets(c cbClient, endpoint string) ([]interface{}, error) {
	creds, err := c.credentials()
	if err != nil {
		return nil, err
	}
	resp, err := get(c, endpoint, nil, creds, nil)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.([]interface{}), nil
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

func (d *DevClient) GetBucketSet(systemKey, deploymentName string) (map[string]interface{}, error) {
	return getBucketSet(d, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName)
}

func (u *UserClient) GetBucketSet(systemKey, deploymentName string) (map[string]interface{}, error) {
	return getBucketSet(u, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName)
}

func (dv *DeviceClient) GetBucketSet(systemKey, deploymentName string) (map[string]interface{}, error) {
	return getBucketSet(dv, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName)
}

func getBucketSet(c cbClient, endpoint string) (map[string]interface{}, error) {
	creds, err := c.credentials()
	if err != nil {
		return nil, err
	}
	resp, err := get(c, endpoint, nil, creds, nil)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.(map[string]interface{}), nil
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

func (d *DevClient) GetBucketSetFiles(systemKey, deploymentName, box string) (map[string]interface{}, error) {
	return getBucketSetFiles(d, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/files", box)
}

func (u *UserClient) GetBucketSetFiles(systemKey, deploymentName, box string) (map[string]interface{}, error) {
	return getBucketSetFiles(u, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/files", box)
}

func (dv *DeviceClient) GetBucketSetFiles(systemKey, deploymentName, box string) (map[string]interface{}, error) {
	return getBucketSetFiles(dv, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/files", box)
}

func getBucketSetFiles(c cbClient, endpoint, box string) (map[string]interface{}, error) {
	creds, err := c.credentials()
	if err != nil {
		return nil, err
	}
	qs := map[string]string{"box": box}
	resp, err := get(c, endpoint, qs, creds, nil)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.(map[string]interface{}), nil
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

func (d *DevClient) CreateBucketSetFile(systemKey, deploymentName, box, relPath, contents string) (map[string]interface{}, error) {
	return createBucketSetFile(d, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/create", box, relPath, contents)
}

func (u *UserClient) CreateBucketSetFile(systemKey, deploymentName, box, relPath, contents string) (map[string]interface{}, error) {
	return createBucketSetFile(u, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/create", box, relPath, contents)
}

func (dv *DeviceClient) CreateBucketSetFile(systemKey, deploymentName, box, relPath, contents string) (map[string]interface{}, error) {
	return createBucketSetFile(dv, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/create", box, relPath, contents)
}

func createBucketSetFile(c cbClient, endpoint, box, relPath, contents string) (map[string]interface{}, error) {

	creds, err := c.credentials()
	if err != nil {
		return nil, err
	}

	data := map[string]interface{}{
		"box":      box,
		"path":     relPath,
		"contents": contents,
	}

	resp, err := post(c, endpoint, data, creds, nil)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.(map[string]interface{}), nil
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

func (d *DevClient) GetBucketSetFile(systemKey, deploymentName, box, relPath string) (map[string]interface{}, error) {
	return getBucketSetFile(d, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/meta", box, relPath)
}

func (u *UserClient) GetBucketSetFile(systemKey, deploymentName, box, relPath string) (map[string]interface{}, error) {
	return getBucketSetFile(u, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/meta", box, relPath)
}

func (dv *DeviceClient) GetBucketSetFile(systemKey, deploymentName, box, relPath string) (map[string]interface{}, error) {
	return getBucketSetFile(dv, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/meta", box, relPath)
}

func getBucketSetFile(c cbClient, endpoint, box, relPath string) (map[string]interface{}, error) {
	queries := map[string]string{"box": box, "path": relPath}
	creds, err := c.credentials()
	if err != nil {
		return nil, err
	}
	resp, err := get(c, endpoint, queries, creds, nil)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.(map[string]interface{}), nil
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

func (d *DevClient) ReadBucketSetFile(systemKey, deploymentName, box, relPath string) (map[string]interface{}, error) {
	return readBucketSetFile(d, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/read", box, relPath)
}

func (u *UserClient) ReadGetBucketSetFile(systemKey, deploymentName, box, relPath string) (map[string]interface{}, error) {
	return readBucketSetFile(u, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/read", box, relPath)
}

func (dv *DeviceClient) ReadGetBucketSetFile(systemKey, deploymentName, box, relPath string) (map[string]interface{}, error) {
	return readBucketSetFile(dv, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/read", box, relPath)
}

func readBucketSetFile(c cbClient, endpoint, box, relPath string) (map[string]interface{}, error) {
	queries := map[string]string{"box": box, "path": relPath}
	creds, err := c.credentials()
	if err != nil {
		return nil, err
	}
	resp, err := get(c, endpoint, queries, creds, nil)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.(map[string]interface{}), nil
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

func (d *DevClient) CopyBucketSetFile(systemKey, deploymentName, fromBox, fromPath, toBox, toPath string) error {
	return copyBucketSetFile(d, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/copy", fromBox, fromPath, toBox, toPath)
}

func (u *UserClient) CopyBucketSetFile(systemKey, deploymentName, fromBox, fromPath, toBox, toPath string) error {
	return copyBucketSetFile(u, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/copy", fromBox, fromPath, toBox, toPath)
}

func (dv *DeviceClient) CopyBucketSetFile(systemKey, deploymentName, fromBox, fromPath, toBox, toPath string) error {
	return copyBucketSetFile(dv, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/copy", fromBox, fromPath, toBox, toPath)
}

func copyBucketSetFile(c cbClient, endpoint, fromBox, fromPath, toBox, toPath string) error {
	creds, err := c.credentials()
	if err != nil {
		return err
	}
	data := map[string]interface{}{
		"from_box":  fromBox,
		"from_path": fromPath,
		"to_box":    toBox,
		"to_path":   toPath,
	}
	_, err = post(c, endpoint, data, creds, nil)
	return err
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

func (d *DevClient) MoveBucketSetFile(systemKey, deploymentName, fromBox, fromPath, toBox, toPath string) error {
	return moveBucketSetFile(d, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/move", fromBox, fromPath, toBox, toPath)
}

func (u *UserClient) MoveBucketSetFile(systemKey, deploymentName, fromBox, fromPath, toBox, toPath string) error {
	return moveBucketSetFile(u, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/move", fromBox, fromPath, toBox, toPath)
}

func (dv *DeviceClient) MoveBucketSetFile(systemKey, deploymentName, fromBox, fromPath, toBox, toPath string) error {
	return moveBucketSetFile(dv, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/move", fromBox, fromPath, toBox, toPath)
}

func moveBucketSetFile(c cbClient, endpoint, fromBox, fromPath, toBox, toPath string) error {
	creds, err := c.credentials()
	if err != nil {
		return err
	}
	data := map[string]interface{}{
		"from_box":  fromBox,
		"from_path": fromPath,
		"to_box":    toBox,
		"to_path":   toPath,
	}
	_, err = post(c, endpoint, data, creds, nil)
	return err
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

func (d *DevClient) DeleteBucketSetFile(systemKey, deploymentName, box, relPath string) error {
	return deleteBucketSetFile(d, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/delete", box, relPath)
}

func (u *UserClient) DeleteBucketSetFile(systemKey, deploymentName, box, relPath string) error {
	return deleteBucketSetFile(u, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/delete", box, relPath)
}

func (dv *DeviceClient) DeleteBucketSetFile(systemKey, deploymentName, box, relPath string) error {
	return deleteBucketSetFile(dv, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/delete", box, relPath)
}

func deleteBucketSetFile(c cbClient, endpoint, box, relPath string) error {

	creds, err := c.credentials()
	if err != nil {
		return err
	}

	data := map[string]interface{}{
		"box":  box,
		"path": relPath,
	}

	_, err = post(c, endpoint, data, creds, nil)
	return err
}
<commit_msg>fixed return values for readBucketSetFile<commit_after>package GoSDK

import (
//"fmt"
)

const (
	_BUCKET_SETS_PREAMBLE = "/api/v/4/bucket_sets/"
)

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

func (d *DevClient) GetBucketSets(systemKey string) ([]interface{}, error) {
	return getBucketSets(d, _BUCKET_SETS_PREAMBLE+systemKey)
}

func (u *UserClient) GetBucketSets(systemKey string) ([]interface{}, error) {
	return getBucketSets(u, _BUCKET_SETS_PREAMBLE+systemKey)
}

func (dv *DeviceClient) GetBucketSets(systemKey string) ([]interface{}, error) {
	return getBucketSets(dv, _BUCKET_SETS_PREAMBLE+systemKey)
}

func getBucketSets(c cbClient, endpoint string) ([]interface{}, error) {
	creds, err := c.credentials()
	if err != nil {
		return nil, err
	}
	resp, err := get(c, endpoint, nil, creds, nil)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.([]interface{}), nil
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

func (d *DevClient) GetBucketSet(systemKey, deploymentName string) (map[string]interface{}, error) {
	return getBucketSet(d, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName)
}

func (u *UserClient) GetBucketSet(systemKey, deploymentName string) (map[string]interface{}, error) {
	return getBucketSet(u, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName)
}

func (dv *DeviceClient) GetBucketSet(systemKey, deploymentName string) (map[string]interface{}, error) {
	return getBucketSet(dv, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName)
}

func getBucketSet(c cbClient, endpoint string) (map[string]interface{}, error) {
	creds, err := c.credentials()
	if err != nil {
		return nil, err
	}
	resp, err := get(c, endpoint, nil, creds, nil)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.(map[string]interface{}), nil
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

func (d *DevClient) GetBucketSetFiles(systemKey, deploymentName, box string) (map[string]interface{}, error) {
	return getBucketSetFiles(d, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/files", box)
}

func (u *UserClient) GetBucketSetFiles(systemKey, deploymentName, box string) (map[string]interface{}, error) {
	return getBucketSetFiles(u, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/files", box)
}

func (dv *DeviceClient) GetBucketSetFiles(systemKey, deploymentName, box string) (map[string]interface{}, error) {
	return getBucketSetFiles(dv, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/files", box)
}

func getBucketSetFiles(c cbClient, endpoint, box string) (map[string]interface{}, error) {
	creds, err := c.credentials()
	if err != nil {
		return nil, err
	}
	qs := map[string]string{"box": box}
	resp, err := get(c, endpoint, qs, creds, nil)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.(map[string]interface{}), nil
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

func (d *DevClient) CreateBucketSetFile(systemKey, deploymentName, box, relPath, contents string) (map[string]interface{}, error) {
	return createBucketSetFile(d, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/create", box, relPath, contents)
}

func (u *UserClient) CreateBucketSetFile(systemKey, deploymentName, box, relPath, contents string) (map[string]interface{}, error) {
	return createBucketSetFile(u, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/create", box, relPath, contents)
}

func (dv *DeviceClient) CreateBucketSetFile(systemKey, deploymentName, box, relPath, contents string) (map[string]interface{}, error) {
	return createBucketSetFile(dv, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/create", box, relPath, contents)
}

func createBucketSetFile(c cbClient, endpoint, box, relPath, contents string) (map[string]interface{}, error) {

	creds, err := c.credentials()
	if err != nil {
		return nil, err
	}

	data := map[string]interface{}{
		"box":      box,
		"path":     relPath,
		"contents": contents,
	}

	resp, err := post(c, endpoint, data, creds, nil)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.(map[string]interface{}), nil
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

func (d *DevClient) GetBucketSetFile(systemKey, deploymentName, box, relPath string) (map[string]interface{}, error) {
	return getBucketSetFile(d, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/meta", box, relPath)
}

func (u *UserClient) GetBucketSetFile(systemKey, deploymentName, box, relPath string) (map[string]interface{}, error) {
	return getBucketSetFile(u, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/meta", box, relPath)
}

func (dv *DeviceClient) GetBucketSetFile(systemKey, deploymentName, box, relPath string) (map[string]interface{}, error) {
	return getBucketSetFile(dv, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/meta", box, relPath)
}

func getBucketSetFile(c cbClient, endpoint, box, relPath string) (map[string]interface{}, error) {
	queries := map[string]string{"box": box, "path": relPath}
	creds, err := c.credentials()
	if err != nil {
		return nil, err
	}
	resp, err := get(c, endpoint, queries, creds, nil)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.(map[string]interface{}), nil
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

func (d *DevClient) ReadBucketSetFile(systemKey, deploymentName, box, relPath string) (string, error) {
	return readBucketSetFile(d, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/read", box, relPath)
}

func (u *UserClient) ReadGetBucketSetFile(systemKey, deploymentName, box, relPath string) (string, error) {
	return readBucketSetFile(u, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/read", box, relPath)
}

func (dv *DeviceClient) ReadGetBucketSetFile(systemKey, deploymentName, box, relPath string) (string, error) {
	return readBucketSetFile(dv, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/read", box, relPath)
}

func readBucketSetFile(c cbClient, endpoint, box, relPath string) (string, error) {
	queries := map[string]string{"box": box, "path": relPath}
	creds, err := c.credentials()
	if err != nil {
		return "", err
	}
	resp, err := get(c, endpoint, queries, creds, nil)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return "", err
	}
	return resp.Body.(string), nil
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

func (d *DevClient) CopyBucketSetFile(systemKey, deploymentName, fromBox, fromPath, toBox, toPath string) error {
	return copyBucketSetFile(d, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/copy", fromBox, fromPath, toBox, toPath)
}

func (u *UserClient) CopyBucketSetFile(systemKey, deploymentName, fromBox, fromPath, toBox, toPath string) error {
	return copyBucketSetFile(u, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/copy", fromBox, fromPath, toBox, toPath)
}

func (dv *DeviceClient) CopyBucketSetFile(systemKey, deploymentName, fromBox, fromPath, toBox, toPath string) error {
	return copyBucketSetFile(dv, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/copy", fromBox, fromPath, toBox, toPath)
}

func copyBucketSetFile(c cbClient, endpoint, fromBox, fromPath, toBox, toPath string) error {
	creds, err := c.credentials()
	if err != nil {
		return err
	}
	data := map[string]interface{}{
		"from_box":  fromBox,
		"from_path": fromPath,
		"to_box":    toBox,
		"to_path":   toPath,
	}
	_, err = post(c, endpoint, data, creds, nil)
	return err
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

func (d *DevClient) MoveBucketSetFile(systemKey, deploymentName, fromBox, fromPath, toBox, toPath string) error {
	return moveBucketSetFile(d, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/move", fromBox, fromPath, toBox, toPath)
}

func (u *UserClient) MoveBucketSetFile(systemKey, deploymentName, fromBox, fromPath, toBox, toPath string) error {
	return moveBucketSetFile(u, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/move", fromBox, fromPath, toBox, toPath)
}

func (dv *DeviceClient) MoveBucketSetFile(systemKey, deploymentName, fromBox, fromPath, toBox, toPath string) error {
	return moveBucketSetFile(dv, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/move", fromBox, fromPath, toBox, toPath)
}

func moveBucketSetFile(c cbClient, endpoint, fromBox, fromPath, toBox, toPath string) error {
	creds, err := c.credentials()
	if err != nil {
		return err
	}
	data := map[string]interface{}{
		"from_box":  fromBox,
		"from_path": fromPath,
		"to_box":    toBox,
		"to_path":   toPath,
	}
	_, err = post(c, endpoint, data, creds, nil)
	return err
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

func (d *DevClient) DeleteBucketSetFile(systemKey, deploymentName, box, relPath string) error {
	return deleteBucketSetFile(d, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/delete", box, relPath)
}

func (u *UserClient) DeleteBucketSetFile(systemKey, deploymentName, box, relPath string) error {
	return deleteBucketSetFile(u, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/delete", box, relPath)
}

func (dv *DeviceClient) DeleteBucketSetFile(systemKey, deploymentName, box, relPath string) error {
	return deleteBucketSetFile(dv, _BUCKET_SETS_PREAMBLE+systemKey+"/"+deploymentName+"/file/delete", box, relPath)
}

func deleteBucketSetFile(c cbClient, endpoint, box, relPath string) error {

	creds, err := c.credentials()
	if err != nil {
		return err
	}

	data := map[string]interface{}{
		"box":  box,
		"path": relPath,
	}

	_, err = post(c, endpoint, data, creds, nil)
	return err
}
<|endoftext|>


<commit_before>package buffstreams

import ()

import (
	"encoding/binary"
	"errors"
	"io"
	"log"
	"net"
	"sync"
)

// BuffManager represents the object used to govern interactions between tcp endpoints.
// You can use it to read from and write to streaming or non-streaming TCP connections
// and have it handle packaging data with a header describing the size of the data payload.
// This is to make it easy to work with wire formats like ProtocolBuffers, which require
// a custom-delimeter situation to be sent in a streaming fashion.
type BuffManager struct {
	dialedConnections map[string]*net.TCPConn
	listeningSockets  map[string]*net.TCPListener
	// TODO find a way to sanely provide this to a Dialer or a Receiver on a per-connection basis
	maxMessageSizeBitLength int
	enableLogging           bool
	// TODO I could control access to the maps better if I centralized how they got accessed - less locking code littered around
	sync.RWMutex
}

// BuffManagerConfig represents a set of options that the person building systems ontop of
type BuffManagerConfig struct {
	// Controls how large the largest Message may be. The server will reject any messages whose clients
	// header size does not match this configuration
	MaxMessageSize int
	// Controls the ability to enable logging errors occuring in the library
	EnableLogging bool
}

// Creates a new *BuffManager based on the provided BuffManagerConfig
func New(cfg BuffManagerConfig) *BuffManager {
	bm := &BuffManager{
		dialedConnections: make(map[string]*net.TCPConn),
		listeningSockets:  make(map[string]*net.TCPListener),
		enableLogging:     cfg.EnableLogging,
	}
	maxMessageSize := 4096
	// 0 is the default, and the message must be atleast 1 byte large
	if cfg.MaxMessageSize != 0 {
		maxMessageSize = cfg.MaxMessageSize
	}
	bm.maxMessageSizeBitLength = messageSizeToBitLength(maxMessageSize)
	return bm
}

// ListenCallback is a function type that calling code will need to implement in order
// to receive arrays of bytes from the socket. Each slice of bytes will be stripped of the
// size header, meaning you can directly serialize the raw slice. You would then perform your
// custom logic for interpretting the message, before returning. You can optionally
// return an error, which in turn will be logged if EnableLogging is set to true.
type ListenCallback func([]byte) error

// FormatAddress is to cover the event that you want/need a programmtically correct way
// to format an address/port to use with StartListening or WriteTo
func FormatAddress(address string, port string) string {
	return address + ":" + port
}

// StartListening is an asyncrhonous, non-blocking method. It begins listening on the given
// port, and will invoke the povided ListenCallback once it receives a full payload of bytes.
// In the event of an transport error, it will disconnect the client. It is the clients responsibility
// to re-connect if needed.
func (bm *BuffManager) StartListening(port string, cb ListenCallback) error {
	address := FormatAddress("", port)
	tcpAddr, err := net.ResolveTCPAddr("tcp", address)
	if err != nil {
		return err
	}
	receiveSocket, err := net.ListenTCP("tcp", tcpAddr)
	if err != nil {
		return err
	}
	bm.startListening(address, receiveSocket, cb)
	return nil
}

func (bm *BuffManager) startListening(address string, socket *net.TCPListener, cb ListenCallback) {
	bm.Lock()
	bm.listeningSockets[address] = socket
	bm.Unlock()

	go func(address string, maxMessageSizeBitLength int, enableLogging bool, listener net.Listener) {
		for {
			// Wait for someone to connect
			conn, err := listener.Accept()
			if err != nil {
				if enableLogging == true {
					log.Printf("Error attempting to accept connection: %s", err)
				}
			} else {
				// Hand this off and immediately listen for more
				go handleListenedConn(address, conn, maxMessageSizeBitLength, enableLogging, cb)
			}
		}
	}(address, bm.maxMessageSizeBitLength, bm.enableLogging, socket)
}

func handleListenedConn(address string, conn net.Conn, maxMessageSize int, enableLogging bool, cb ListenCallback) {
	// If there is any error, close the connection officially and break out of the listen-loop.
	// We don't store these connections anywhere else, and if we can't recover from an error on the socket
	// we want to kill the connection, exit the goroutine, and let the client handle re-connecting if need be.
	for {
		// Handle getting the data header
		headerByteSize := maxMessageSize
		headerBuffer := make([]byte, headerByteSize)
		var headerReadError error
		var totalHeaderBytesRead = 0
		var bytesRead = 0
		// First, read the number of bytes required to determine the message length
		for totalHeaderBytesRead < headerByteSize && headerReadError == nil {
			// While we haven't read enough yet, pass in the slice that represents where we are in the buffer
			bytesRead, headerReadError = readFromConnection(conn, headerBuffer[totalHeaderBytesRead:])
			totalHeaderBytesRead += bytesRead
		}
		if headerReadError != nil {
			if enableLogging == true {
				if headerReadError != io.EOF {
					// Log the error we got from the call to read
					log.Print("Error when trying to read from address %s. Tried to read %d, actually read %d. Underlying error: %s", address, headerByteSize, totalHeaderBytesRead, headerReadError)
				} else {
					// Client closed the conn
					log.Printf("Address %s: Client closed connection. Underlying error: %s", address, headerReadError)
				}
			}
			conn.Close()
			return
		}

		// Now turn that buffer of bytes into an integer - represnts size of message body
		msgLength, bytesParsed := binary.Uvarint(headerBuffer)
		// Not sure what the correct way to handle these errors are. For now, bomb out
		if bytesParsed == 0 {
			// "Buffer too small"
			if enableLogging == true {
				log.Printf("Address %s: 0 Bytes parsed from header. Underlying error: %s", address, headerReadError)
			}
			conn.Close()
			return
		} else if bytesParsed < 0 {
			// "Buffer overflow"
			if enableLogging == true {
				log.Printf("Address %s: Buffer Less than zero bytes parsed from header. Underlying error: %s", address, headerReadError)
			}
			conn.Close()
			return
		}
		dataBuffer := make([]byte, msgLength)
		var dataReadError error
		var totalDataBytesRead = 0
		bytesRead = 0
		for totalDataBytesRead < int(msgLength) && dataReadError == nil {
			// While we haven't read enough yet, pass in the slice that represents where we are in the buffer
			bytesRead, dataReadError = readFromConnection(conn, dataBuffer[totalDataBytesRead:])
			totalDataBytesRead += bytesRead
		}

		if dataReadError != nil {
			if enableLogging == true {
				if dataReadError != io.EOF {
					// log the error from the call to read
					log.Printf("Address %s: Failure to read from connection. Was told to read %d by the header, actually read %d. Underlying error: %s", address, msgLength, totalDataBytesRead, dataReadError)
				} else {
					// The client wrote the header but closed the connection
					log.Printf("Address %s: Client closed connection. Underlying error: %s", address, dataReadError)
				}
			}
			conn.Close()
			return
		}

		// If we read bytes, there wasn't an error, or if there was it was only EOF
		// And readbytes + EOF is normal, just as readbytes + no err, next read 0 bytes EOF
		// So... we take action on the actual message data
		if totalDataBytesRead > 0 && (dataReadError == nil || (dataReadError != nil && dataReadError.Error() == "EOF")) {
			err := cb(dataBuffer)
			if err != nil && enableLogging == true {
				log.Printf("Error in Callback: %s", err)
				// TODO if it's a protobuffs error, it means we likely had an issue and can't
				// deserialize data? Should we kill the connection and have the client start over?
				// At this point, there isn't a reliable recovery mechanic for the server
			}
		}
	}
}

func readFromConnection(reader net.Conn, buffer []byte) (int, error) {
	// This fills the buffer
	bytesLen, err := reader.Read(buffer)
	// Output the content of the bytes to the queue
	if bytesLen == 0 {
		if err != nil && err == io.EOF {
			// "End of individual transmission"
			// We're just done reading from that conn
			return bytesLen, err
		}
	}

	if err != nil {
		//"Underlying network failure?"
		// Not sure what this error would be, but it could exist and i've seen it handled
		// as a general case in other networking code. Following in the footsteps of (greatness|madness)
		return bytesLen, err
	}
	// Read some bytes, return the length
	return bytesLen, nil
}

func (bm *BuffManager) dialOut(address string) (*net.TCPConn, error) {
	// We want to hold this lock for both the lookup as well as for setting it in the map
	// Skipping defer for now, need to profile it's performance
	bm.Lock()
	if _, ok := bm.dialedConnections[address]; ok {
		bm.Unlock()
		return nil, errors.New("You have a connection to this ip and port open already")
	}
	tcpAddr, err := net.ResolveTCPAddr("tcp", address)
	if err != nil {
		bm.Unlock()
		return nil, err
	}
	conn, err := net.DialTCP("tcp", nil, tcpAddr)
	if err != nil {
		bm.Unlock()
		return nil, err
	} else {
		// Store the connection, it's valid
		bm.dialedConnections[address] = conn
		bm.Unlock()
	}
	return conn, nil
}

func (bm *BuffManager) closeDialer(address string) error {
	bm.Lock()
	// We want to hold this lock for both the lookup as well as the delete
	// Either way, we can't release until we're ready to return.
	//Skipping defer for now, need to profile it's performance
	if conn, ok := bm.dialedConnections[address]; ok {
		err := conn.Close()
		// Grab lock to delete from the map
		delete(bm.dialedConnections, address)
		// Release immediately
		bm.Unlock()
		return err
	}
	bm.Unlock()
	return nil
}

// WriteTo allows you to dial to a remote or local TCP endpoint, and send either 1 or a stream
// of bytes as messages. Each array of bytes you pass in will be pre-pended with it's size
// within the size of the pre-defined maximum message size. If the connection isn't open yet,
// WriteTo will open it, and cache it. If for anyreason the connection breaks, it will be disposed
// and upon the next write, a new one will dial out.
func (bm *BuffManager) WriteTo(address string, data []byte, persist bool) (int, error) {
	var conn *net.TCPConn
	var err error
	var ok bool

	// Get the connection if it's cached, or open a new one
	bm.RLock()
	conn, ok = bm.dialedConnections[address]
	bm.RUnlock()
	if ok != true {
		conn, err = bm.dialOut(address)
		if err != nil {
			// Error dialing out, cannot write
			// bail
			return 0, err
		}
	}
	// Calculate how big the message is, using a consistent header size.
	msgLenHeader := uInt16ToByteArray(uint16(len(data)), bm.maxMessageSizeBitLength)
	// Append the size to the message, so now it has a header
	toWrite := append(msgLenHeader, data...)

	toWriteLen := len(toWrite)

	// Three conditions could have occured:
	// 1. There was an error
	// 2. Not all bytes were written
	// 3. Both 1 and 2

	// If there was an error, that should take handling precedence. If the connection
	// was closed, or is otherwise in a bad state, we have to abort and re-open the connection
	// to try again, as we can't realistically finish the write. We have to retry it, or return
	// and error to the user?

	// TODO configurable message retries

	// If there was not an error, and we simply didn't finish the write, we should enter
	// a write-until-complete loop, where we continue to write the data until the server accepts
	// all of it.

	// If both issues occurred, we'll need to find a way to determine if the error
	// is recoverable (is the connection in a bad state) or not

	var writeError error
	var totalBytesWritten = 0
	var bytesWritten = 0
	// First, read the number of bytes required to determine the message length
	for totalBytesWritten < toWriteLen && writeError == nil {
		// While we haven't read enough yet
		// If there are remainder bytes, adjust the contents of toWrite
		// totalBytesWritten will be the index of the nextByte waiting to be read
		bytesWritten, writeError = conn.Write(toWrite[totalBytesWritten:])
		totalBytesWritten += bytesWritten
	}

	if writeError != nil || persist == false {
		if writeError != nil && bm.enableLogging == true {
			log.Printf("Error while writing data to %s. Expected to write %d, actually wrote %d. Underlying error: %s", address, len(toWrite), totalBytesWritten, writeError)
		}
		writeError = bm.closeDialer(address)
		if writeError != nil {
			// TODO ponder the following:
			// What if some bytes written, then failure, then also the close throws an error
			// []error is a better return type, but not sure if thats a thing you're supposed to do...
			// Possibilities for error not as complicated as i'm thinking?
			if bm.enableLogging == true {
				// The error will get returned up the stack, no need to log it here?
				log.Print("There was a subsequent error cleaning up the connection to %s")
			}
			return totalBytesWritten, writeError
		}
	}

	// Return the bytes written, any error
	return totalBytesWritten, writeError
}
<commit_msg>Cleaning up some comments<commit_after>package buffstreams

import ()

import (
	"encoding/binary"
	"errors"
	"io"
	"log"
	"net"
	"sync"
)

// BuffManager represents the object used to govern interactions between tcp endpoints.
// You can use it to read from and write to streaming or non-streaming TCP connections
// and have it handle packaging data with a header describing the size of the data payload.
// This is to make it easy to work with wire formats like ProtocolBuffers, which require
// a custom-delimeter situation to be sent in a streaming fashion.
type BuffManager struct {
	dialedConnections map[string]*net.TCPConn
	listeningSockets  map[string]*net.TCPListener
	// TODO find a way to sanely provide this to a Dialer or a Receiver on a per-connection basis
	maxMessageSizeBitLength int
	enableLogging           bool
	// TODO I could control access to the maps better if I centralized how they got accessed - less locking code littered around
	sync.RWMutex
}

// BuffManagerConfig represents a set of options that the person building systems ontop of
type BuffManagerConfig struct {
	// Controls how large the largest Message may be. The server will reject any messages whose clients
	// header size does not match this configuration
	MaxMessageSize int
	// Controls the ability to enable logging errors occuring in the library
	EnableLogging bool
}

// Creates a new *BuffManager based on the provided BuffManagerConfig
func New(cfg BuffManagerConfig) *BuffManager {
	bm := &BuffManager{
		dialedConnections: make(map[string]*net.TCPConn),
		listeningSockets:  make(map[string]*net.TCPListener),
		enableLogging:     cfg.EnableLogging,
	}
	maxMessageSize := 4096
	// 0 is the default, and the message must be atleast 1 byte large
	if cfg.MaxMessageSize != 0 {
		maxMessageSize = cfg.MaxMessageSize
	}
	bm.maxMessageSizeBitLength = messageSizeToBitLength(maxMessageSize)
	return bm
}

// ListenCallback is a function type that calling code will need to implement in order
// to receive arrays of bytes from the socket. Each slice of bytes will be stripped of the
// size header, meaning you can directly serialize the raw slice. You would then perform your
// custom logic for interpretting the message, before returning. You can optionally
// return an error, which in turn will be logged if EnableLogging is set to true.
type ListenCallback func([]byte) error

// FormatAddress is to cover the event that you want/need a programmtically correct way
// to format an address/port to use with StartListening or WriteTo
func FormatAddress(address string, port string) string {
	return address + ":" + port
}

// StartListening is an asyncrhonous, non-blocking method. It begins listening on the given
// port, and fire off a goroutine for every client connection it receives. That goroutine will
// read the fixed header, then the message payload, and then invoke the povided ListenCallbacl.
// In the event of an transport error, it will disconnect the client. It is the clients responsibility
// to re-connect if needed.
func (bm *BuffManager) StartListening(port string, cb ListenCallback) error {
	address := FormatAddress("", port)
	tcpAddr, err := net.ResolveTCPAddr("tcp", address)
	if err != nil {
		return err
	}
	receiveSocket, err := net.ListenTCP("tcp", tcpAddr)
	if err != nil {
		return err
	}
	bm.startListening(address, receiveSocket, cb)
	return nil
}

func (bm *BuffManager) startListening(address string, socket *net.TCPListener, cb ListenCallback) {
	bm.Lock()
	bm.listeningSockets[address] = socket
	bm.Unlock()

	go func(address string, maxMessageSizeBitLength int, enableLogging bool, listener net.Listener) {
		for {
			// Wait for someone to connect
			conn, err := listener.Accept()
			if err != nil {
				if enableLogging == true {
					log.Printf("Error attempting to accept connection: %s", err)
				}
			} else {
				// Hand this off and immediately listen for more
				go handleListenedConn(address, conn, maxMessageSizeBitLength, enableLogging, cb)
			}
		}
	}(address, bm.maxMessageSizeBitLength, bm.enableLogging, socket)
}

func handleListenedConn(address string, conn net.Conn, maxMessageSize int, enableLogging bool, cb ListenCallback) {
	// If there is any error, close the connection officially and break out of the listen-loop.
	// We don't store these connections anywhere else, and if we can't recover from an error on the socket
	// we want to kill the connection, exit the goroutine, and let the client handle re-connecting if need be.
	for {
		// Handle getting the data header
		headerByteSize := maxMessageSize
		headerBuffer := make([]byte, headerByteSize)
		var headerReadError error
		var totalHeaderBytesRead = 0
		var bytesRead = 0
		// First, read the number of bytes required to determine the message length
		for totalHeaderBytesRead < headerByteSize && headerReadError == nil {
			// While we haven't read enough yet, pass in the slice that represents where we are in the buffer
			bytesRead, headerReadError = readFromConnection(conn, headerBuffer[totalHeaderBytesRead:])
			totalHeaderBytesRead += bytesRead
		}
		if headerReadError != nil {
			if enableLogging == true {
				if headerReadError != io.EOF {
					// Log the error we got from the call to read
					log.Print("Error when trying to read from address %s. Tried to read %d, actually read %d. Underlying error: %s", address, headerByteSize, totalHeaderBytesRead, headerReadError)
				} else {
					// Client closed the conn
					log.Printf("Address %s: Client closed connection. Underlying error: %s", address, headerReadError)
				}
			}
			conn.Close()
			return
		}

		// Now turn that buffer of bytes into an integer - represnts size of message body
		msgLength, bytesParsed := binary.Uvarint(headerBuffer)
		// Not sure what the correct way to handle these errors are. For now, bomb out
		if bytesParsed == 0 {
			// "Buffer too small"
			if enableLogging == true {
				log.Printf("Address %s: 0 Bytes parsed from header. Underlying error: %s", address, headerReadError)
			}
			conn.Close()
			return
		} else if bytesParsed < 0 {
			// "Buffer overflow"
			if enableLogging == true {
				log.Printf("Address %s: Buffer Less than zero bytes parsed from header. Underlying error: %s", address, headerReadError)
			}
			conn.Close()
			return
		}
		dataBuffer := make([]byte, msgLength)
		var dataReadError error
		var totalDataBytesRead = 0
		bytesRead = 0
		for totalDataBytesRead < int(msgLength) && dataReadError == nil {
			// While we haven't read enough yet, pass in the slice that represents where we are in the buffer
			bytesRead, dataReadError = readFromConnection(conn, dataBuffer[totalDataBytesRead:])
			totalDataBytesRead += bytesRead
		}

		if dataReadError != nil {
			if enableLogging == true {
				if dataReadError != io.EOF {
					// log the error from the call to read
					log.Printf("Address %s: Failure to read from connection. Was told to read %d by the header, actually read %d. Underlying error: %s", address, msgLength, totalDataBytesRead, dataReadError)
				} else {
					// The client wrote the header but closed the connection
					log.Printf("Address %s: Client closed connection. Underlying error: %s", address, dataReadError)
				}
			}
			conn.Close()
			return
		}

		// If we read bytes, there wasn't an error, or if there was it was only EOF
		// And readbytes + EOF is normal, just as readbytes + no err, next read 0 bytes EOF
		// So... we take action on the actual message data
		if totalDataBytesRead > 0 && (dataReadError == nil || (dataReadError != nil && dataReadError.Error() == "EOF")) {
			err := cb(dataBuffer)
			if err != nil && enableLogging == true {
				log.Printf("Error in Callback: %s", err)
				// TODO if it's a protobuffs error, it means we likely had an issue and can't
				// deserialize data? Should we kill the connection and have the client start over?
				// At this point, there isn't a reliable recovery mechanic for the server
			}
		}
	}
}

func readFromConnection(reader net.Conn, buffer []byte) (int, error) {
	// This fills the buffer
	bytesLen, err := reader.Read(buffer)
	// Output the content of the bytes to the queue
	if bytesLen == 0 {
		if err != nil && err == io.EOF {
			// "End of individual transmission"
			// We're just done reading from that conn
			return bytesLen, err
		}
	}

	if err != nil {
		//"Underlying network failure?"
		// Not sure what this error would be, but it could exist and i've seen it handled
		// as a general case in other networking code. Following in the footsteps of (greatness|madness)
		return bytesLen, err
	}
	// Read some bytes, return the length
	return bytesLen, nil
}

func (bm *BuffManager) dialOut(address string) (*net.TCPConn, error) {
	// We want to hold this lock for both the lookup as well as for setting it in the map
	// Skipping defer for now, need to profile it's performance
	bm.Lock()
	if _, ok := bm.dialedConnections[address]; ok {
		bm.Unlock()
		return nil, errors.New("You have a connection to this ip and port open already")
	}
	tcpAddr, err := net.ResolveTCPAddr("tcp", address)
	if err != nil {
		bm.Unlock()
		return nil, err
	}
	conn, err := net.DialTCP("tcp", nil, tcpAddr)
	if err != nil {
		bm.Unlock()
		return nil, err
	} else {
		// Store the connection, it's valid
		bm.dialedConnections[address] = conn
		bm.Unlock()
	}
	return conn, nil
}

func (bm *BuffManager) closeDialer(address string) error {
	bm.Lock()
	// We want to hold this lock for both the lookup as well as the delete
	// Either way, we can't release until we're ready to return.
	//Skipping defer for now, need to profile it's performance
	if conn, ok := bm.dialedConnections[address]; ok {
		err := conn.Close()
		// Grab lock to delete from the map
		delete(bm.dialedConnections, address)
		// Release immediately
		bm.Unlock()
		return err
	}
	bm.Unlock()
	return nil
}

// WriteTo allows you to dial to a remote or local TCP endpoint, and send either 1 or a stream
// of bytes as messages. Each array of bytes you pass in will be pre-pended with it's size
// within the size of the pre-defined maximum message size. If the connection isn't open yet,
// WriteTo will open it, and cache it. If for anyreason the connection breaks, it will be disposed
// and upon the next write, a new one will dial out.
func (bm *BuffManager) WriteTo(address string, data []byte, persist bool) (int, error) {
	var conn *net.TCPConn
	var err error
	var ok bool

	// Get the connection if it's cached, or open a new one
	bm.RLock()
	conn, ok = bm.dialedConnections[address]
	bm.RUnlock()
	if ok != true {
		conn, err = bm.dialOut(address)
		if err != nil {
			// Error dialing out, cannot write
			// bail
			return 0, err
		}
	}
	// Calculate how big the message is, using a consistent header size.
	msgLenHeader := uInt16ToByteArray(uint16(len(data)), bm.maxMessageSizeBitLength)
	// Append the size to the message, so now it has a header
	toWrite := append(msgLenHeader, data...)

	toWriteLen := len(toWrite)

	// Three conditions could have occured:
	// 1. There was an error
	// 2. Not all bytes were written
	// 3. Both 1 and 2

	// If there was an error, that should take handling precedence. If the connection
	// was closed, or is otherwise in a bad state, we have to abort and re-open the connection
	// to try again, as we can't realistically finish the write. We have to retry it, or return
	// and error to the user?

	// TODO configurable message retries

	// If there was not an error, and we simply didn't finish the write, we should enter
	// a write-until-complete loop, where we continue to write the data until the server accepts
	// all of it.

	// If both issues occurred, we'll need to find a way to determine if the error
	// is recoverable (is the connection in a bad state) or not

	var writeError error
	var totalBytesWritten = 0
	var bytesWritten = 0
	// First, read the number of bytes required to determine the message length
	for totalBytesWritten < toWriteLen && writeError == nil {
		// While we haven't read enough yet
		// If there are remainder bytes, adjust the contents of toWrite
		// totalBytesWritten will be the index of the nextByte waiting to be read
		bytesWritten, writeError = conn.Write(toWrite[totalBytesWritten:])
		totalBytesWritten += bytesWritten
	}

	if writeError != nil || persist == false {
		if writeError != nil && bm.enableLogging == true {
			log.Printf("Error while writing data to %s. Expected to write %d, actually wrote %d. Underlying error: %s", address, len(toWrite), totalBytesWritten, writeError)
		}
		writeError = bm.closeDialer(address)
		if writeError != nil {
			// TODO ponder the following:
			// What if some bytes written, then failure, then also the close throws an error
			// []error is a better return type, but not sure if thats a thing you're supposed to do...
			// Possibilities for error not as complicated as i'm thinking?
			if bm.enableLogging == true {
				// The error will get returned up the stack, no need to log it here?
				log.Print("There was a subsequent error cleaning up the connection to %s")
			}
			return totalBytesWritten, writeError
		}
	}

	// Return the bytes written, any error
	return totalBytesWritten, writeError
}
<|endoftext|>


<commit_before>package buffstreams

import ()

import (
	"encoding/binary"
	"errors"
	"log"
	"net"
	"sync"
)

type BuffManager struct {
	dialedConnections map[string]net.Conn
	listeningSockets  map[string]net.Listener
	// TODO find a way to sanely provide this to a Dialer or a Receiver on a per-connection basis
	MaxMessageSizeBitLength int
	EnableLogging           bool
	// TODO I could control access to the maps better if I centralized how they got accessed - less locking code littered around
	sync.RWMutex
}

type BuffManagerConfig struct {
	MaxMessageSize int
	EnableLogging  bool
}

func New(cfg BuffManagerConfig) *BuffManager {
	bm := &BuffManager{
		dialedConnections: make(map[string]net.Conn),
		listeningSockets:  make(map[string]net.Listener),
		EnableLogging:     cfg.EnableLogging,
	}
	maxMessageSize := 4096
	// 0 is the default, and the message must be atleast 1 byte large
	if cfg.MaxMessageSize != 0 {
		maxMessageSize = cfg.MaxMessageSize
	}
	bm.MaxMessageSizeBitLength = MessageSizeToBitLength(maxMessageSize)
	return bm
}

type ListenCallback func([]byte) error

func formatAddress(address string, port string) string {
	return address + ":" + port
}

func (bm *BuffManager) StartListening(port string, cb ListenCallback) error {
	address := formatAddress("", port)
	receiveSocket, err := net.Listen("tcp", address)
	if err != nil {
		return err
	}
	bm.startListening(address, receiveSocket, cb)
	return nil
}

func (bm *BuffManager) startListening(address string, socket net.Listener, cb ListenCallback) {
	bm.Lock()
	bm.listeningSockets[address] = socket
	bm.Unlock()

	go func(address string, maxMessageSizeBitLength int, enableLogging bool, listener net.Listener) {
		for {
			// Wait for someone to connect
			conn, err := listener.Accept()
			if err != nil {
				if enableLogging == true {
					log.Print("Error attempting to accept connection")
					log.Print(err)
				}
			} else {
				// Hand this off and immediately listen for more
				go handleListenedConn(address, conn, bm.MaxMessageSizeBitLength, enableLogging, cb)
			}
		}
	}(address, bm.MaxMessageSizeBitLength, bm.EnableLogging, socket)
}

func handleListenedConn(address string, conn net.Conn, maxMessageSize int, enableLogging bool, cb ListenCallback) {
	for {
		// Handle getting the data header
		headerByteSize := maxMessageSize
		headerBuffer := make([]byte, headerByteSize)
		// First, read the number of bytes required to determine the message length
		_, err := readFromConnection(conn, headerBuffer)
		if err != nil && err.Error() == "EOF" {
			// Log the error we got from the call to read
			if enableLogging == true {
				log.Printf("Address %s: Client closed connection", address)
				log.Print(err)
			}
			conn.Close()
			return
		}

		// Now turn that buffer of bytes into an integer - represnts size of message body
		msgLength, bytesParsed := binary.Uvarint(headerBuffer)
		// Not sure what the correct way to handle these errors are. For now, bomb out
		if bytesParsed == 0 {
			// "Buffer too small"
			if enableLogging == true {
				log.Printf("Address %s: 0 Bytes parsed from header", address)
				log.Print(err)
			}
			conn.Close()
			return
		} else if bytesParsed < 0 {
			// "Buffer overflow"
			if enableLogging == true {
				log.Printf("Address %s: Buffer Less than zero bytes parsed from header", address)
				log.Print(err)
			}
			conn.Close()
			return
		}
		dataBuffer := make([]byte, msgLength)
		bytesLen, err := readFromConnection(conn, dataBuffer)
		if err != nil && err.Error() == "EOF" {
			// log the error from the call to read
			if enableLogging == true {
				log.Printf("Address %s: Failure to read from connection", address)
				log.Print(err)
			}
			conn.Close()
			return
		}

		// If we read bytes, there wasn't an error, or if there was it was only EOF
		// And readbytes + EOF is normal, just as readbytes + no err, next read 0 bytes EOF
		// So... we take action on the actual message data
		if bytesLen > 0 && (err == nil || (err != nil && err.Error() == "EOF")) {
			// I ultimately have some design choices here
			// Currently, I am invoking a delegate thats been passed down the stack
			// I could...
			// Just push it onto a queue (not a slow ass channel, but a queue)
			// which has a reference passed down to it, and the main process
			// spawns a goroutine to reap off the queue and handle those in parallel

			// Callback, atm
			err = cb(dataBuffer)
			if err != nil && enableLogging == true {
				log.Printf("Error in Callback")
				log.Print(err)
			}
		}
	}
}

func readFromConnection(reader net.Conn, buffer []byte) (int, error) {
	// This fills the buffer
	bytesLen, err := reader.Read(buffer)
	// Output the content of the bytes to the queue
	if bytesLen == 0 {
		if err != nil && err.Error() == "EOF" {
			// "End of individual transmission"
			// We're just done reading from that conn
			return bytesLen, err
		}
	}

	if err != nil {
		//"Underlying network failure?"
		// Not sure what this error would be, but it could exist and i've seen it handled
		// as a general case in other networking code. Following in the footsteps of (greatness|madness)
	}
	// Read some bytes, return the length
	return bytesLen, nil
}

// If you want to dial out but not immediately write, use this method
func (bm *BuffManager) DialOut(ip string, port string) error {
	address := formatAddress(ip, port)
	bm.RLock()
	if _, ok := bm.dialedConnections[address]; ok == true {
		bm.RUnlock()
		// Need to clean it out on any error...
		return errors.New("You have a connection to this ip and port open already")
	}
	bm.RUnlock()
	tcpAddr, err := net.ResolveTCPAddr("tcp", address)
	if err != nil {
		return err
	}
	conn, err := net.DialTCP("tcp", nil, tcpAddr)
	if err != nil {
		return err
	} else {
		// Store the connection, it's valid
		bm.Lock()
		bm.dialedConnections[address] = conn
		bm.Unlock()
	}
	return nil
}

func (bm *BuffManager) CloseDialer(ip string, port string) error {
	address := formatAddress(ip, port)
	bm.Lock()
	if _, ok := bm.dialedConnections[address]; ok != true {
		err := bm.dialedConnections[address].Close()
		delete(bm.dialedConnections, address)
		bm.Unlock()
		return err
	}
	bm.Unlock()
	return nil
}

// Write data and dial out if the conn isn't open
func (bm *BuffManager) WriteTo(ip string, port string, data []byte, persist bool) (int, error) {
	address := formatAddress(ip, port)
	// Get the connection if it's cached, or open a new one
	bm.RLock()
	if _, ok := bm.dialedConnections[address]; ok != true {
		bm.RUnlock()
		err := bm.DialOut(ip, port)
		if err != nil {
			// Error dialing out, cannot write
			// bail
			return 0, err
		}
	} else {
		bm.RUnlock()
	}
	// Calculate how big the message is, using a consistent header size.
	toWriteLen := UInt16ToByteArray(uint16(len(data)), bm.MaxMessageSizeBitLength)
	// Append the size to the message, so now it has a header
	toWrite := append(toWriteLen, data...)
	bm.Lock()
	written, err := bm.dialedConnections[address].Write(toWrite)
	bm.Unlock()
	if err != nil || persist == true {
		err := bm.CloseDialer(ip, port)
		if err != nil {
			// TODO ponder the following:
			// Error closing the dialer, should we still return 0 written?
			// What if some bytes written, then failure, then also the close throws an error
			// []error is a better return type, but not sure if thats a thing you're supposed to do...
			// Possibilities for error not as complicated as i'm thinking?
			if bm.EnableLogging == true {
				// The error will get returned up the stack, no need to log it here?
				log.Print("There was an error writing the message, and a subsequent error cleaning up the connection")
			}
			return 0, err
		}
	}
	// Return the bytes written, any error
	return written, err
}
<commit_msg>Reworking some of the locking around writing data to shorten time needed to lock, privatized dialing code in favor of WriteTo being the one true path<commit_after>package buffstreams

import ()

import (
	"encoding/binary"
	"errors"
	"log"
	"net"
	"sync"
)

type BuffManager struct {
	dialedConnections map[string]net.Conn
	listeningSockets  map[string]net.Listener
	// TODO find a way to sanely provide this to a Dialer or a Receiver on a per-connection basis
	MaxMessageSizeBitLength int
	EnableLogging           bool
	// TODO I could control access to the maps better if I centralized how they got accessed - less locking code littered around
	sync.RWMutex
}

type BuffManagerConfig struct {
	MaxMessageSize int
	EnableLogging  bool
}

func New(cfg BuffManagerConfig) *BuffManager {
	bm := &BuffManager{
		dialedConnections: make(map[string]net.Conn),
		listeningSockets:  make(map[string]net.Listener),
		EnableLogging:     cfg.EnableLogging,
	}
	maxMessageSize := 4096
	// 0 is the default, and the message must be atleast 1 byte large
	if cfg.MaxMessageSize != 0 {
		maxMessageSize = cfg.MaxMessageSize
	}
	bm.MaxMessageSizeBitLength = MessageSizeToBitLength(maxMessageSize)
	return bm
}

type ListenCallback func([]byte) error

func formatAddress(address string, port string) string {
	return address + ":" + port
}

func (bm *BuffManager) StartListening(port string, cb ListenCallback) error {
	address := formatAddress("", port)
	receiveSocket, err := net.Listen("tcp", address)
	if err != nil {
		return err
	}
	bm.startListening(address, receiveSocket, cb)
	return nil
}

func (bm *BuffManager) startListening(address string, socket net.Listener, cb ListenCallback) {
	bm.Lock()
	bm.listeningSockets[address] = socket
	bm.Unlock()

	go func(address string, maxMessageSizeBitLength int, enableLogging bool, listener net.Listener) {
		for {
			// Wait for someone to connect
			conn, err := listener.Accept()
			if err != nil {
				if enableLogging == true {
					log.Print("Error attempting to accept connection")
					log.Print(err)
				}
			} else {
				// Hand this off and immediately listen for more
				go handleListenedConn(address, conn, bm.MaxMessageSizeBitLength, enableLogging, cb)
			}
		}
	}(address, bm.MaxMessageSizeBitLength, bm.EnableLogging, socket)
}

func handleListenedConn(address string, conn net.Conn, maxMessageSize int, enableLogging bool, cb ListenCallback) {
	for {
		// Handle getting the data header
		headerByteSize := maxMessageSize
		headerBuffer := make([]byte, headerByteSize)
		// First, read the number of bytes required to determine the message length
		_, err := readFromConnection(conn, headerBuffer)
		if err != nil && err.Error() == "EOF" {
			// Log the error we got from the call to read
			if enableLogging == true {
				log.Printf("Address %s: Client closed connection", address)
				log.Print(err)
			}
			conn.Close()
			return
		}

		// Now turn that buffer of bytes into an integer - represnts size of message body
		msgLength, bytesParsed := binary.Uvarint(headerBuffer)
		// Not sure what the correct way to handle these errors are. For now, bomb out
		if bytesParsed == 0 {
			// "Buffer too small"
			if enableLogging == true {
				log.Printf("Address %s: 0 Bytes parsed from header", address)
				log.Print(err)
			}
			conn.Close()
			return
		} else if bytesParsed < 0 {
			// "Buffer overflow"
			if enableLogging == true {
				log.Printf("Address %s: Buffer Less than zero bytes parsed from header", address)
				log.Print(err)
			}
			conn.Close()
			return
		}
		dataBuffer := make([]byte, msgLength)
		bytesLen, err := readFromConnection(conn, dataBuffer)
		if err != nil && err.Error() == "EOF" {
			// log the error from the call to read
			if enableLogging == true {
				log.Printf("Address %s: Failure to read from connection", address)
				log.Print(err)
			}
			conn.Close()
			return
		}

		// If we read bytes, there wasn't an error, or if there was it was only EOF
		// And readbytes + EOF is normal, just as readbytes + no err, next read 0 bytes EOF
		// So... we take action on the actual message data
		if bytesLen > 0 && (err == nil || (err != nil && err.Error() == "EOF")) {
			// I ultimately have some design choices here
			// Currently, I am invoking a delegate thats been passed down the stack
			// I could...
			// Just push it onto a queue (not a slow ass channel, but a queue)
			// which has a reference passed down to it, and the main process
			// spawns a goroutine to reap off the queue and handle those in parallel

			// Callback, atm
			err = cb(dataBuffer)
			if err != nil && enableLogging == true {
				log.Printf("Error in Callback")
				log.Print(err)
			}
		}
	}
}

func readFromConnection(reader net.Conn, buffer []byte) (int, error) {
	// This fills the buffer
	bytesLen, err := reader.Read(buffer)
	// Output the content of the bytes to the queue
	if bytesLen == 0 {
		if err != nil && err.Error() == "EOF" {
			// "End of individual transmission"
			// We're just done reading from that conn
			return bytesLen, err
		}
	}

	if err != nil {
		//"Underlying network failure?"
		// Not sure what this error would be, but it could exist and i've seen it handled
		// as a general case in other networking code. Following in the footsteps of (greatness|madness)
	}
	// Read some bytes, return the length
	return bytesLen, nil
}

func (bm *BuffManager) dialOut(ip string, port string) (*net.TCPConn, error) {
	address := formatAddress(ip, port)
	bm.RLock()
	if _, ok := bm.dialedConnections[address]; ok == true {
		bm.RUnlock()
		// Need to clean it out on any error...
		return nil, errors.New("You have a connection to this ip and port open already")
	}
	bm.RUnlock()
	tcpAddr, err := net.ResolveTCPAddr("tcp", address)
	if err != nil {
		return nil, err
	}
	conn, err := net.DialTCP("tcp", nil, tcpAddr)
	if err != nil {
		return nil, err
	} else {
		// Store the connection, it's valid
		bm.Lock()
		bm.dialedConnections[address] = conn
		bm.Unlock()
	}
	return conn, nil
}

func (bm *BuffManager) closeDialer(ip string, port string) error {
	address := formatAddress(ip, port)
	bm.Lock()
	if _, ok := bm.dialedConnections[address]; ok != true {
		err := bm.dialedConnections[address].Close()
		delete(bm.dialedConnections, address)
		bm.Unlock()
		return err
	}
	bm.Unlock()
	return nil
}

// Write data and dial out if the conn isn't open
func (bm *BuffManager) WriteTo(ip string, port string, data []byte, persist bool) (int, error) {
	address := formatAddress(ip, port)

	var conn net.Conn
	var err error
	var ok bool

	// Get the connection if it's cached, or open a new one
	bm.RLock()
	conn, ok = bm.dialedConnections[address]
	bm.RUnlock()
	if ok != true {
		conn, err = bm.dialOut(ip, port)
		if err != nil {
			// Error dialing out, cannot write
			// bail
			return 0, err
		}
	}
	// Calculate how big the message is, using a consistent header size.
	toWriteLen := UInt16ToByteArray(uint16(len(data)), bm.MaxMessageSizeBitLength)
	// Append the size to the message, so now it has a header
	toWrite := append(toWriteLen, data...)
	// Writes are threadsafe in net.Conns
	written, err := conn.Write(toWrite)

	if err != nil || persist == true {
		err := bm.closeDialer(ip, port)
		conn = nil
		if err != nil {
			// TODO ponder the following:
			// Error closing the dialer, should we still return 0 written?
			// What if some bytes written, then failure, then also the close throws an error
			// []error is a better return type, but not sure if thats a thing you're supposed to do...
			// Possibilities for error not as complicated as i'm thinking?
			if bm.EnableLogging == true {
				// The error will get returned up the stack, no need to log it here?
				log.Print("There was an error writing the message, and a subsequent error cleaning up the connection")
			}
			return 0, err
		}
	}
	// Return the bytes written, any error
	return written, err
}
<|endoftext|>


<commit_before>package main
/*
 * Copyright 2016 ThoughtWorks, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import (
	"fmt"
	"os"
	"os/exec"
	"io"
	"bytes"
	"io/ioutil"
	"strings"
	"flag"
)

var goAgentFilename = "gocd-golang-agent"

var targetOS = map[string][]string{
	"darwin" : {"amd64"},
	"linux" : {"386", "amd64"},
//	"windows" : {"386", "amd64"},  // Windows build is broken because of undefined syscall.Statfs_t and syscall.Statfs
}

var goAgent = "github.com/gocd-contrib/gocd-golang-agent"

var targetOSmap = map[string]string{
	"darwin" : "MacOS",
	"linux" : "Linux",
	"windows" : "Windows",
	"solaris" : "Solaris",
	"netbsd" : "NetBSD",
	"freebsd" : "FreeBSD",
}

var ext_libraries []string =  []string{
	"golang.org/x/net/websocket",
	"golang.org/x/text",
	"golang.org/x/crypto/ssh",
	"github.com/satori/go.uuid",
	"github.com/xli/assert",
	"github.com/bmatcuk/doublestar"}

var testReport = "testreport.xml"

func shouldSkipDependent(exlib string) bool {
	exlibGoEnv := "GO_REVISION_SRC_" + strings.ToUpper(strings.Join(strings.Split(strings.Join(strings.Split(exlib,"."),"_"),"/"),"_"))
	if len(os.Getenv(exlibGoEnv)) > 0 {
		return true
	}
	return false
}

func getDependencies(excludeLib string) {
	fmt.Println("==================================")
	fmt.Println("Download Dependencies")
	go_args := []string{}
	args := []string{"get","-u"}
	for _, exlib := range ext_libraries {
		if strings.Contains(excludeLib,exlib) {
			fmt.Printf("Exclude from go get. Please manually run : go get %s\n", exlib)
		}else {
			if !shouldSkipDependent(exlib) {
				go_args = append(args, exlib)
				_, err := exec.Command("go", go_args...).Output()
				if err != nil {
					fmt.Println(err)
				}
				fmt.Printf("Get : %s\n", exlib)
			} else {
				fmt.Printf("Skip %s since it is part of GoCD Material\n", exlib)
			}
		}
	}
}

func getGitHash() string {
	out, err := exec.Command("git", "-C", "src/" + goAgent,"rev-parse", "HEAD").Output()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(2)
	}
	return string(out)
}

func buildBinary(){
	fmt.Println("==================================")
	fmt.Println("Building Binary")
	os.RemoveAll("output")
	os.Mkdir("output",0755)
	os.Setenv("CGO_ENABLED","0")
	for buildOS, buildArchs := range targetOS {
		for _, buildArch := range buildArchs {
			fmt.Println("---> " + targetOSmap[buildOS] + " - " + buildArch)
			os.Setenv("GOOS", buildOS)
			os.Setenv("GOARCH", buildArch)
			ldFlags := "-w -X main.Githash=" + getGitHash()
			buildVersion := os.Getenv("BUILD_VERSION")
			if len(buildVersion) > 0 {
				ldFlags = ldFlags + "-X main.Version=" + buildVersion
			}
			out, err := exec.Command("go", "build", "-a", "-tags", "netgo", "-ldflags", ldFlags, "-o", "output/" + goAgentFilename + "_" + buildOS + "_" + buildArch, goAgent).Output()
			if err != nil {
				fmt.Fprintln(os.Stderr, err)
				os.Exit(2)
			}
			if len(string(out)) > 0 {
				fmt.Println(out)
			}
		}
	}

}

func runTest(pwd string){
	fmt.Println("==================================")
	fmt.Println("Running Test")
	args := []string{"get","-u","github.com/jstemmer/go-junit-report"}
	reportCmd := pwd + "/bin/go-junit-report"
	reportCmd_args := []string{" > ", testReport}

	_, err := exec.Command("go", args...).Output()
	if err != nil{
		fmt.Println(err)
	}
	_, err = os.Stat(testReport)
	if err == nil {
		os.Remove(testReport)
	}
	args = []string{"test", "-test.v", goAgent + "..." }
	gotest := exec.Command("go", args...)
	goreport := exec.Command(reportCmd,reportCmd_args...)
	reader, writer := io.Pipe()
	gotest.Stdout = writer
	goreport.Stdin = reader

	var buf bytes.Buffer
	goreport.Stdout = &buf

	gotest.Start()
	goreport.Start()
	gotest.Wait()
	writer.Close()
	goreport.Wait()
	err = ioutil.WriteFile(testReport,buf.Bytes(),0644)
	if err != nil{
		panic(err)
	}
}

func main() {

	var excludeLib string

	flag.StringVar(&excludeLib, "excludelib", "github.com/gocd-contrib/fake_agent", "exclude dependencies in comma separated format, eg github.com/gocd-contrib/fake_agent,")
	flag.Parse()

	pwd, err := os.Getwd()
	if err == nil {
		os.Setenv("GOPATH",pwd)
		os.Setenv("GOBIN", pwd + "/bin")
	}

	getDependencies(excludeLib)
	runTest(pwd)
	buildBinary()

}<commit_msg>Fix git command not to use flag to find running directory<commit_after>package main
/*
 * Copyright 2016 ThoughtWorks, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import (
	"fmt"
	"os"
	"os/exec"
	"io"
	"bytes"
	"io/ioutil"
	"strings"
	"flag"
)

var goAgentFilename = "gocd-golang-agent"

var targetOS = map[string][]string{
	"darwin" : {"amd64"},
	"linux" : {"386", "amd64"},
//	"windows" : {"386", "amd64"},  // Windows build is broken because of undefined syscall.Statfs_t and syscall.Statfs
}

var goAgent = "github.com/gocd-contrib/gocd-golang-agent"

var targetOSmap = map[string]string{
	"darwin" : "MacOS",
	"linux" : "Linux",
	"windows" : "Windows",
	"solaris" : "Solaris",
	"netbsd" : "NetBSD",
	"freebsd" : "FreeBSD",
}

var ext_libraries []string =  []string{
	"golang.org/x/net/websocket",
	"golang.org/x/text",
	"golang.org/x/crypto/ssh",
	"github.com/satori/go.uuid",
	"github.com/xli/assert",
	"github.com/bmatcuk/doublestar"}

var testReport = "testreport.xml"

func shouldSkipDependent(exlib string) bool {
	exlibGoEnv := "GO_REVISION_SRC_" + strings.ToUpper(strings.Join(strings.Split(strings.Join(strings.Split(exlib,"."),"_"),"/"),"_"))
	if len(os.Getenv(exlibGoEnv)) > 0 {
		return true
	}
	return false
}

func getDependencies(excludeLib string) {
	fmt.Println("==================================")
	fmt.Println("Download Dependencies")
	go_args := []string{}
	args := []string{"get","-u"}
	for _, exlib := range ext_libraries {
		if strings.Contains(excludeLib,exlib) {
			fmt.Printf("Exclude from go get. Please manually run : go get %s\n", exlib)
		}else {
			if !shouldSkipDependent(exlib) {
				go_args = append(args, exlib)
				_, err := exec.Command("go", go_args...).Output()
				if err != nil {
					fmt.Println(err)
				}
				fmt.Printf("Get : %s\n", exlib)
			} else {
				fmt.Printf("Skip %s since it is part of GoCD Material\n", exlib)
			}
		}
	}
}

func getGitHash(pwd string) string {
	runCmd := exec.Command("git", "rev-parse", "HEAD")
	runCmd.Dir = pwd + "/src/" + goAgent
	out, err := runCmd.Output()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(2)
	}
	return string(out)
}

func buildBinary(pwd string){
	fmt.Println("==================================")
	fmt.Println("Building Binary")
	os.RemoveAll("output")
	os.Mkdir("output",0755)
	os.Setenv("CGO_ENABLED","0")
	for buildOS, buildArchs := range targetOS {
		for _, buildArch := range buildArchs {
			fmt.Println("---> " + targetOSmap[buildOS] + " - " + buildArch)
			os.Setenv("GOOS", buildOS)
			os.Setenv("GOARCH", buildArch)
			ldFlags := "-w -X main.Githash=" + getGitHash(pwd)
			buildVersion := os.Getenv("BUILD_VERSION")
			if len(buildVersion) > 0 {
				ldFlags = ldFlags + "-X main.Version=" + buildVersion
			}
			out, err := exec.Command("go", "build", "-a", "-tags", "netgo", "-ldflags", ldFlags, "-o", "output/" + goAgentFilename + "_" + buildOS + "_" + buildArch, goAgent).Output()
			if err != nil {
				fmt.Fprintln(os.Stderr, err)
				os.Exit(2)
			}
			if len(string(out)) > 0 {
				fmt.Println(out)
			}
		}
	}

}

func runTest(pwd string){
	fmt.Println("==================================")
	fmt.Println("Running Test")
	args := []string{"get","-u","github.com/jstemmer/go-junit-report"}
	reportCmd := pwd + "/bin/go-junit-report"
	reportCmd_args := []string{" > ", testReport}

	_, err := exec.Command("go", args...).Output()
	if err != nil{
		fmt.Println(err)
	}
	_, err = os.Stat(testReport)
	if err == nil {
		os.Remove(testReport)
	}
	args = []string{"test", "-test.v", goAgent + "..." }
	gotest := exec.Command("go", args...)
	goreport := exec.Command(reportCmd,reportCmd_args...)
	reader, writer := io.Pipe()
	gotest.Stdout = writer
	goreport.Stdin = reader

	var buf bytes.Buffer
	goreport.Stdout = &buf

	gotest.Start()
	goreport.Start()
	gotest.Wait()
	writer.Close()
	goreport.Wait()
	err = ioutil.WriteFile(testReport,buf.Bytes(),0644)
	if err != nil{
		panic(err)
	}
}

func main() {

	var excludeLib string

	flag.StringVar(&excludeLib, "excludelib", "github.com/gocd-contrib/fake_agent", "exclude dependencies in comma separated format, eg github.com/gocd-contrib/fake_agent,")
	flag.Parse()

	pwd, err := os.Getwd()
	if err == nil {
		os.Setenv("GOPATH",pwd)
		os.Setenv("GOBIN", pwd + "/bin")
	}

	getDependencies(excludeLib)
	runTest(pwd)
	buildBinary(pwd)

}<|endoftext|>


<commit_before>// Simple script to build bosun and scollector. This is not required, but it will properly insert version date and commit
// metadata into the resulting binaries, which `go build` will not do by default.
package main

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"strings"
	"time"
)

func main() {
	// Get current commit SHA
	cmd := exec.Command("git", "rev-parse", "HEAD")
	output, err := cmd.CombinedOutput()
	if err != nil {
		log.Fatal(err)
	}
	sha := strings.TrimSpace(string(output))

	timeStr := time.Now().UTC().Format("20060102150405")

	ldFlags := fmt.Sprintf("-X bosun.org/version.VersionSHA %s -X bosun.org/version.VersionDate %s", sha, timeStr)

	for _, app := range []string{"bosun", "scollector"} {
		cmd = exec.Command("go", "build", "-v", "-ldflags", ldFlags, fmt.Sprintf("bosun.org/cmd/%s", app))
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		err = cmd.Run()
		if err != nil {
			log.Fatal(err)
		}
	}
}
<commit_msg>modifying build script<commit_after>// Simple script to build bosun and scollector. This is not required, but it will properly insert version date and commit
// metadata into the resulting binaries, which `go build` will not do by default.
package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/exec"
	"strings"
	"time"
)

func main() {
	shaFlag := flag.String("sha", "", "Sha to embed")
	flag.Parse()
	// Get current commit SHA
	sha := *shaFlag
	if sha == "" {
		cmd := exec.Command("git", "rev-parse", "HEAD")
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		output, err := cmd.CombinedOutput()
		if err != nil {
			log.Fatal(err)
		}
		sha = strings.TrimSpace(string(output))
	}

	timeStr := time.Now().UTC().Format("20060102150405")

	ldFlags := fmt.Sprintf("-X bosun.org/version.VersionSHA %s -X bosun.org/version.VersionDate %s", sha, timeStr)

	for _, app := range []string{"bosun", "scollector"} {
		cmd := exec.Command("go", "build", "-v", "-ldflags", ldFlags, fmt.Sprintf("bosun.org/cmd/%s", app))
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		err := cmd.Run()
		if err != nil {
			log.Fatal(err)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 The casbin Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package casbin

// GetRolesForUser gets roles for a user.
func (e *Enforcer) GetRolesForUser(name string) []string {
	return e.model["g"]["g"].RM.GetRoles(name)
}

// HasRoleForUser determines whether a user has a role.
func (e *Enforcer) HasRoleForUser(name string, role string) bool {
	roles := e.GetRolesForUser(name)

	has_role := false
	for _, r := range roles {
		if r == role {
			has_role = true
			break
		}
	}

	return has_role
}

// AddRoleForUser adds a role for a user.
func (e *Enforcer) AddRoleForUser(user string, role string) {
	e.AddGroupingPolicy(user, role)
}

// DeleteRoleForUser deletes a role for a user.
func (e *Enforcer) DeleteRoleForUser(user string, role string) {
	e.RemoveGroupingPolicy(user, role)
}

// DeleteRolesForUser deletes all roles for a user.
func (e *Enforcer) DeleteRolesForUser(user string) {
	e.RemoveFilteredGroupingPolicy(0, user)
}

// DeleteUser deletes a user.
func (e *Enforcer) DeleteUser(user string) {
	e.RemoveFilteredGroupingPolicy(0, user)
}

// DeleteRole deletes a role.
func (e *Enforcer) DeleteRole(role string) {
	e.RemoveFilteredGroupingPolicy(1, role)
	e.RemoveFilteredPolicy(0, role)
}

// DeletePermission deletes a permission.
func (e *Enforcer) DeletePermission(permission string) {
	e.RemoveFilteredPolicy(1, permission)
}

// AddPermissionForUser adds a permission for a user or role.
func (e *Enforcer) AddPermissionForUser(user string, permission string) {
	e.AddPolicy(user, permission)
}

// DeletePermissionForUser deletes a permission for a user or role.
func (e *Enforcer) DeletePermissionForUser(user string, permission string) {
	e.RemovePolicy(user, permission)
}

// DeletePermissionsForUser deletes permissions for a user or role.
func (e *Enforcer) DeletePermissionsForUser(user string) {
	e.RemoveFilteredPolicy(0, user)
}

// GetPermissionsForUser gets permissions for a user or role.
func (e *Enforcer) GetPermissionsForUser(user string) []string {
	res := []string{}

	policy := e.GetFilteredPolicy(0, user)
	for _, rule := range policy {
		res = append(res, rule[1])
	}

	return res
}
<commit_msg>Support multi-element permission operations for RBAC API.<commit_after>// Copyright 2017 The casbin Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package casbin

// GetRolesForUser gets roles for a user.
func (e *Enforcer) GetRolesForUser(name string) []string {
	return e.model["g"]["g"].RM.GetRoles(name)
}

// HasRoleForUser determines whether a user has a role.
func (e *Enforcer) HasRoleForUser(name string, role string) bool {
	roles := e.GetRolesForUser(name)

	has_role := false
	for _, r := range roles {
		if r == role {
			has_role = true
			break
		}
	}

	return has_role
}

// AddRoleForUser adds a role for a user.
func (e *Enforcer) AddRoleForUser(user string, role string) {
	e.AddGroupingPolicy(user, role)
}

// DeleteRoleForUser deletes a role for a user.
func (e *Enforcer) DeleteRoleForUser(user string, role string) {
	e.RemoveGroupingPolicy(user, role)
}

// DeleteRolesForUser deletes all roles for a user.
func (e *Enforcer) DeleteRolesForUser(user string) {
	e.RemoveFilteredGroupingPolicy(0, user)
}

// DeleteUser deletes a user.
func (e *Enforcer) DeleteUser(user string) {
	e.RemoveFilteredGroupingPolicy(0, user)
}

// DeleteRole deletes a role.
func (e *Enforcer) DeleteRole(role string) {
	e.RemoveFilteredGroupingPolicy(1, role)
	e.RemoveFilteredPolicy(0, role)
}

// DeletePermission deletes a permission.
func (e *Enforcer) DeletePermission(permission ...string) {
	e.RemoveFilteredPolicy(1, permission...)
}

// AddPermissionForUser adds a permission for a user or role.
func (e *Enforcer) AddPermissionForUser(user string, permission ...string) {
	params := make([]interface{}, 0, len(permission) + 1)

	params= append(params, user)
	for _, perm := range permission {
		params = append(params, perm)
	}

	e.AddPolicy(params...)
}

// DeletePermissionForUser deletes a permission for a user or role.
func (e *Enforcer) DeletePermissionForUser(user string, permission ...string) {
	params := make([]interface{}, 0, len(permission) + 1)

	params= append(params, user)
	for _, perm := range permission {
		params = append(params, perm)
	}

	e.RemovePolicy(params...)
}

// DeletePermissionsForUser deletes permissions for a user or role.
func (e *Enforcer) DeletePermissionsForUser(user string) {
	e.RemoveFilteredPolicy(0, user)
}

// GetPermissionsForUser gets permissions for a user or role.
func (e *Enforcer) GetPermissionsForUser(user string) []string {
	res := []string{}

	policy := e.GetFilteredPolicy(0, user)
	for _, rule := range policy {
		res = append(res, rule[1])
	}

	return res
}
<|endoftext|>


<commit_before>// Copyright 2010 Aaron Jacobs. All rights reserved.
// See the LICENSE file for licensing details.

// The build package implements the core building logic of igo.
package build

import (
	"igo/set"
	"os"
	"path"
	"strings"
)

// GetPackageInfo computes a set of files that must be compiled to build the
// package contained in the supplied directory, and dependencies of those
// files. If includeTests is true, this will include test files.
func GetPackageInfo(dir string, includeTests bool) (files *set.StringSet, deps *set.StringSet) {
	var visitor packageInfoVisitor
	visitor.includeTests = includeTests
	visitor.originalDir = dir

	path.Walk(dir, &visitor, nil)
	return &visitor.files, &visitor.deps
}

type packageInfoVisitor struct {
	includeTests bool
	originalDir string

	files set.StringSet
	deps set.StringSet
}

func (v *packageInfoVisitor) VisitDir(path string, d *os.Dir) bool {
	// Ignore sub-directories, but do recurse into the original directory.
	return path == v.originalDir
}

func (v *packageInfoVisitor) VisitFile(file string, d *os.Dir) {
	// Ignore files that aren't Go source.
	if path.Ext(file) != ".go" {
		return
  }

	// Ignore test files if we haven't been told to include them.
	if !v.includeTests && strings.HasSuffix(file, "_test.go") {
		return
	}

	v.files.Insert(file)
}
<commit_msg>Finished implementing GetPackageInfo.<commit_after>// Copyright 2010 Aaron Jacobs. All rights reserved.
// See the LICENSE file for licensing details.

// The build package implements the core building logic of igo.
package build

import (
	"igo/parse"
	"igo/set"
	"io/ioutil"
	"os"
	"path"
	"strings"
)

// GetPackageInfo computes a set of files that must be compiled to build the
// package contained in the supplied directory, and dependencies of those
// files. If includeTests is true, this will include test files.
func GetPackageInfo(dir string, includeTests bool) (files *set.StringSet, deps *set.StringSet) {
	var visitor packageInfoVisitor
	visitor.includeTests = includeTests
	visitor.originalDir = dir

	path.Walk(dir, &visitor, nil)
	return &visitor.files, &visitor.deps
}

type packageInfoVisitor struct {
	includeTests bool
	originalDir string

	files set.StringSet
	deps set.StringSet
}

func (v *packageInfoVisitor) VisitDir(path string, d *os.Dir) bool {
	// Ignore sub-directories, but do recurse into the original directory.
	return path == v.originalDir
}

func (v *packageInfoVisitor) VisitFile(file string, d *os.Dir) {
	// Ignore files that aren't Go source.
	if path.Ext(file) != ".go" {
		return
  }

	// Ignore test files if we haven't been told to include them.
	if !v.includeTests && strings.HasSuffix(file, "_test.go") {
		return
	}

	v.files.Insert(file)

	contents, err := ioutil.ReadFile(file)
	if err == nil {
		v.deps.Union(parse.ExtractImports(string(contents)))
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"time"

	golang "github.com/bullettrain-sh/bullettrain-go-golang"
	nodejs "github.com/bullettrain-sh/bullettrain-go-nodejs"
	python "github.com/bullettrain-sh/bullettrain-go-python"
	ruby "github.com/bullettrain-sh/bullettrain-go-ruby"
	//git "github.com/bullettrain-sh/bullettrain-go-git"
	//php "github.com/bullettrain-sh/bullettrain-go-php"
	"github.com/fatih/color"
)

// Here we must check if ENVIRONMENT variables have the appropriate value
// we may expect them to hold.
func init() {

}

func main() {
	color.NoColor = false // force terminal to use colours
	const lineEnding = "$"

	var segmentList []renderer = getSegments()

	// Create a channel for each segment.
	chans := make([]chan string, len(segmentList))
	for i := range chans {
		chans[i] = make(chan string)
	}

	// Spin off a goroutine for each segment.
	for i, segment := range segmentList {
		go segment.Render(chans[i])
	}

	// Gather each goroutine's response through their channels,
	// keeping their order.
	for _, c := range chans {
		fmt.Print(<-c)
	}

	fmt.Printf("\n%s x", color.HiGreenString(lineEnding))
}

type renderer interface {
	Render(c chan<- string)
}

// Configure the segments and store them in the right order.
func getSegments() []renderer {
	return []renderer{
		&timeSegment{color.FgHiWhite, color.BgBlack},
		&separator{color.FgBlack, color.BgYellow},
		&python.Segment{color.FgHiWhite, color.BgYellow},
		&separator{color.FgYellow, color.BgRed},
		&ruby.Segment{color.FgHiWhite, color.BgRed},
		&separator{color.FgRed, color.BgBlue},
		&golang.Segment{color.FgHiWhite, color.BgBlue},
		&separator{color.FgBlue, color.BgGreen},
		&nodejs.Segment{color.FgHiWhite, color.BgGreen},
		&separator{color.FgGreen, color.BgGreen},
	}
}

//  _____                            _
// /  ___|                          | |
// \ `--.  ___ _ __   __ _ _ __ __ _| |_ ___  _ __
//  `--. \/ _ \ '_ \ / _` | '__/ _` | __/ _ \| '__|
// /\__/ /  __/ |_) | (_| | | | (_| | || (_) | |
// \____/ \___| .__/ \__,_|_|  \__,_|\__\___/|_|
//            | |
//            |_|

type separator struct {
	fg, bg color.Attribute
}

func (s *separator) Render(ch chan<- string) {
	const segmentSeparator string = ""
	defer close(ch)

	col := color.New(s.fg, s.bg)
	ch <- col.Sprint(segmentSeparator)
}

//  _____ _
// |_   _(_)
//   | |  _ _ __ ___   ___
//   | | | | '_ ` _ \ / _ \
//   | | | | | | | | |  __/
//   \_/ |_|_| |_| |_|\___|

type timeSegment struct {
	fg, bg color.Attribute
}

func (s *timeSegment) Render(ch chan<- string) {
	defer close(ch)

	col := color.New(s.fg, s.bg)
	t := time.Now()
	ch <- col.Sprintf(" %02d:%02d:%02d ", t.Hour(), t.Minute(), t.Second())
}
<commit_msg>Making separator background optional.<commit_after>package main

import (
	"fmt"
	"time"

	golang "github.com/bullettrain-sh/bullettrain-go-golang"
	nodejs "github.com/bullettrain-sh/bullettrain-go-nodejs"
	python "github.com/bullettrain-sh/bullettrain-go-python"
	ruby "github.com/bullettrain-sh/bullettrain-go-ruby"
	//git "github.com/bullettrain-sh/bullettrain-go-git"
	//php "github.com/bullettrain-sh/bullettrain-go-php"
	"github.com/fatih/color"
)

// Here we must check if ENVIRONMENT variables have the appropriate value
// we may expect them to hold.
func init() {

}

func main() {
	color.NoColor = false // force terminal to use colours
	const lineEnding = "$"

	var segmentList []renderer = getSegments()

	// Create a channel for each segment.
	chans := make([]chan string, len(segmentList))
	for i := range chans {
		chans[i] = make(chan string)
	}

	// Spin off a goroutine for each segment.
	for i, segment := range segmentList {
		go segment.Render(chans[i])
	}

	// Gather each goroutine's response through their channels,
	// keeping their order.
	for _, c := range chans {
		fmt.Print(<-c)
	}

	fmt.Printf("\n%s x", color.HiGreenString(lineEnding))
}

type renderer interface {
	Render(c chan<- string)
}

// Configure the segments and store them in the right order.
func getSegments() []renderer {
	return []renderer{
		&timeSegment{color.FgHiWhite, color.BgBlack},
		&separator{color.FgBlack, color.BgYellow},
		&python.Segment{color.FgHiWhite, color.BgYellow},
		&separator{color.FgYellow, color.BgRed},
		&ruby.Segment{color.FgHiWhite, color.BgRed},
		&separator{color.FgRed, color.BgBlue},
		&golang.Segment{color.FgHiWhite, color.BgBlue},
		&separator{color.FgBlue, color.BgGreen},
		&nodejs.Segment{color.FgHiWhite, color.BgGreen},
		&separator{color.FgGreen, nil},
	}
}

//  _____                            _
// /  ___|                          | |
// \ `--.  ___ _ __   __ _ _ __ __ _| |_ ___  _ __
//  `--. \/ _ \ '_ \ / _` | '__/ _` | __/ _ \| '__|
// /\__/ /  __/ |_) | (_| | | | (_| | || (_) | |
// \____/ \___| .__/ \__,_|_|  \__,_|\__\___/|_|
//            | |
//            |_|

type separator struct {
	fg color.Attribute
	bg interface{}
}

func (s *separator) Render(ch chan<- string) {
	// let's have a space at the end to make sure it will leave enough space in
	// terminals to render the char correclty
	const segmentSeparator string = " "
	defer close(ch)

	col := color.New(s.fg)
	switch s.bg.(type) {
	case color.Attribute:
		col.Add(s.bg.(color.Attribute))
	}

	ch <- col.Sprint(segmentSeparator)
}

//  _____ _
// |_   _(_)
//   | |  _ _ __ ___   ___
//   | | | | '_ ` _ \ / _ \
//   | | | | | | | | |  __/
//   \_/ |_|_| |_| |_|\___|

type timeSegment struct {
	fg, bg color.Attribute
}

func (s *timeSegment) Render(ch chan<- string) {
	defer close(ch)

	col := color.New(s.fg, s.bg)
	t := time.Now()
	ch <- col.Sprintf(" %02d:%02d:%02d ", t.Hour(), t.Minute(), t.Second())
}
<|endoftext|>


<commit_before>package bus

import (
	"fmt"
	"net"
	"sync"
	"time"

	proto "github.com/huin/mqtt"
	"github.com/jeffallen/mqtt"
	"github.com/ninjasphere/go-ninja/config"
)

type TinyBus struct {
	connecting    sync.WaitGroup
	mqtt          *mqtt.ClientConn
	subscriptions []*subscription
	host          string
	id            string
}

func ConnectTinyBus(host, id string) (*TinyBus, error) {

	bus := &TinyBus{
		subscriptions: make([]*subscription, 0),
		host:          host,
		id:            id,
	}

	bus.connect()

	return bus, nil
}

type wrappedConn struct {
	net.Conn
	done chan bool
}

func (c wrappedConn) Close() error {
	log.Warningf("Connection closed!")
	c.done <- true
	c.Conn.Close()
	return nil
}

func (b *TinyBus) connect() {

	b.connecting.Add(1)
	defer func() {
		b.connecting.Done()

		b.publish(&proto.Publish{
			Header: proto.Header{
				Retain: true,
			},
			TopicName: fmt.Sprintf("node/%s/module/%s/state/connected", config.Serial(), b.id),
			Payload:   proto.BytesPayload([]byte("true")),
		})
	}()

	var conn wrappedConn
	for {
		tcpConn, err := net.Dial("tcp", b.host)
		if err == nil {
			conn = wrappedConn{
				Conn: tcpConn,
				done: make(chan bool, 1),
			}
			break
		}

		//log.Warningf("Failed to connect to: %s", err)
		time.Sleep(time.Millisecond * 500)
	}

	if b.mqtt != nil {
		log.Infof("Reconnected to mqtt server")
	}

	mqtt := mqtt.NewClientConn(conn)
	mqtt.ClientId = b.id

	err := mqtt.ConnectCustom(&proto.Connect{
		WillFlag:    true,
		WillQos:     0,
		WillRetain:  true,
		WillTopic:   fmt.Sprintf("$node/%s/module/%s/state/connected", config.Serial(), b.id),
		WillMessage: "false",
	})

	if err != nil {
		log.Fatalf("MQTT Failed to connect to: %s", err)
	}

	b.mqtt = mqtt

	for _, s := range b.subscriptions {
		b.subscribe(s)
	}

	go func() {
		for m := range mqtt.Incoming {
			b.onIncoming(m)
		}
	}()

	go func() {
		<-conn.done
		b.connect()
	}()
}

func (b *TinyBus) onIncoming(message *proto.Publish) {
	for _, sub := range b.subscriptions {
		if matches(sub.topic, message.TopicName) {
			go sub.callback(message.TopicName, []byte(message.Payload.(proto.BytesPayload)))
		}
	}
}

func (b *TinyBus) Publish(topic string, payload []byte) {
	b.connecting.Wait()

	b.publish(&proto.Publish{
		TopicName: topic,
		Payload:   proto.BytesPayload(payload),
	})

}

func (b *TinyBus) publish(message *proto.Publish) {
	b.mqtt.Publish(message)
}

func (b *TinyBus) Subscribe(topic string, callback func(topic string, payload []byte)) (*subscription, error) {

	subscription := &subscription{
		topic:    topic,
		callback: callback,
	}

	err := b.subscribe(subscription)
	if err != nil {
		return nil, err
	}

	b.subscriptions = append(b.subscriptions, subscription)

	return subscription, nil
}

func (b *TinyBus) subscribe(subscription *subscription) error {
	_ = b.mqtt.Subscribe([]proto.TopicQos{proto.TopicQos{subscription.topic, proto.QosAtMostOnce}})
	//spew.Dump("subscription ack", ack)
	// TODO: Check ack
	return nil
}
<commit_msg>Switch to fork of jeffallen/mqtt<commit_after>package bus

import (
	"fmt"
	"net"
	"sync"
	"time"

	proto "github.com/huin/mqtt"
	"github.com/ninjasphere/go-ninja/config"
	"github.com/ninjasphere/mqtt"
)

type TinyBus struct {
	connecting    sync.WaitGroup
	mqtt          *mqtt.ClientConn
	subscriptions []*subscription
	host          string
	id            string
}

func ConnectTinyBus(host, id string) (*TinyBus, error) {

	bus := &TinyBus{
		subscriptions: make([]*subscription, 0),
		host:          host,
		id:            id,
	}

	bus.connect()

	return bus, nil
}

type wrappedConn struct {
	net.Conn
	done chan bool
}

func (c wrappedConn) Close() error {
	log.Warningf("Connection closed!")
	c.done <- true
	c.Conn.Close()
	return nil
}

func (b *TinyBus) connect() {

	b.connecting.Add(1)
	defer func() {
		b.connecting.Done()

		b.publish(&proto.Publish{
			Header: proto.Header{
				Retain: true,
			},
			TopicName: fmt.Sprintf("node/%s/module/%s/state/connected", config.Serial(), b.id),
			Payload:   proto.BytesPayload([]byte("true")),
		})
	}()

	var conn wrappedConn
	for {
		tcpConn, err := net.Dial("tcp", b.host)
		if err == nil {
			conn = wrappedConn{
				Conn: tcpConn,
				done: make(chan bool, 1),
			}
			break
		}

		//log.Warningf("Failed to connect to: %s", err)
		time.Sleep(time.Millisecond * 500)
	}

	if b.mqtt != nil {
		log.Infof("Reconnected to mqtt server")
	}

	mqtt := mqtt.NewClientConn(conn)
	mqtt.ClientId = b.id

	err := mqtt.ConnectCustom(&proto.Connect{
		WillFlag:    true,
		WillQos:     0,
		WillRetain:  true,
		WillTopic:   fmt.Sprintf("$node/%s/module/%s/state/connected", config.Serial(), b.id),
		WillMessage: "false",
	})

	if err != nil {
		log.Fatalf("MQTT Failed to connect to: %s", err)
	}

	b.mqtt = mqtt

	for _, s := range b.subscriptions {
		b.subscribe(s)
	}

	go func() {
		for m := range mqtt.Incoming {
			b.onIncoming(m)
		}
	}()

	go func() {
		<-conn.done
		b.connect()
	}()
}

func (b *TinyBus) onIncoming(message *proto.Publish) {
	for _, sub := range b.subscriptions {
		if matches(sub.topic, message.TopicName) {
			go sub.callback(message.TopicName, []byte(message.Payload.(proto.BytesPayload)))
		}
	}
}

func (b *TinyBus) Publish(topic string, payload []byte) {
	b.connecting.Wait()

	b.publish(&proto.Publish{
		TopicName: topic,
		Payload:   proto.BytesPayload(payload),
	})

}

func (b *TinyBus) publish(message *proto.Publish) {
	b.mqtt.Publish(message)
}

func (b *TinyBus) Subscribe(topic string, callback func(topic string, payload []byte)) (*subscription, error) {

	subscription := &subscription{
		topic:    topic,
		callback: callback,
	}

	err := b.subscribe(subscription)
	if err != nil {
		return nil, err
	}

	b.subscriptions = append(b.subscriptions, subscription)

	return subscription, nil
}

func (b *TinyBus) subscribe(subscription *subscription) error {
	_ = b.mqtt.Subscribe([]proto.TopicQos{proto.TopicQos{subscription.topic, proto.QosAtMostOnce}})
	//spew.Dump("subscription ack", ack)
	// TODO: Check ack
	return nil
}
<|endoftext|>