func (st *S2S) Copy(r io.Reader) (int64, error) {
	if st.closed {
		return 0, fmt.Errorf("cannot send on closed connection")
	}
	if time.Now().Sub(st.lastSendTime) > time.Duration(st.maxIdleTime)*time.Second {
		st.reconnect(true)
	}
	bytes, err := io.Copy(st.buf, r)
	if err != nil {
		return bytes, err
	}
	st.lastSendTime = time.Now()
	st.reconnect(false)
	return bytes, nil
}
Copy takes a io.Reader and copies it to Splunk, needs to be encoded by EncodeEvent
package s2s
import (
	"bufio"
	"bytes"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io"
	"math/rand"
	"net"
	"strings"
	"sync"
	"time"
	"encoding/binary"
)
var bp sync.Pool
func init() {
	bp = sync.Pool{
		New: func() interface{} {
			bb := bytes.NewBuffer([]byte{})
			return bb
		},
	}
}
type S2S struct {
	buf                *bufio.Writer
	conn               net.Conn
	initialized        bool
	endpoint           string
	endpoints          []string
	closed             bool
	bufferBytes        int
	tls                bool
	cert               string
	serverName         string
	insecureSkipVerify bool
	rebalanceInterval  int
	lastConnectTime    time.Time
	maxIdleTime        int
	lastSendTime       time.Time
	mutex              *sync.RWMutex
	ignoreNextClosed   bool
}
type splunkSignature struct {
	signature  [128]byte
	serverName [256]byte
	mgmtPort   [16]byte
}
type Interface interface {
	Send(event map[string]string) (int64, error)
}
func NewS2S(endpoints []string, bufferBytes int) (*S2S, error) {
	return NewS2STLS(endpoints, bufferBytes, false, "", "", false)
}
func NewS2STLS(endpoints []string, bufferBytes int, tls bool, cert string, serverName string, insecureSkipVerify bool) (*S2S, error) {
	st := new(S2S)
	if len(endpoints) < 1 {
		return nil, fmt.Errorf("No endpoints specified")
	}
	st.mutex = &sync.RWMutex{}
	st.endpoints = endpoints
	st.bufferBytes = bufferBytes
	st.tls = tls
	st.cert = cert
	if serverName == "" {
		st.serverName = "SplunkServerDefaultCert"
	} else {
		st.serverName = serverName
	}
	st.insecureSkipVerify = insecureSkipVerify
	err := st.reconnect(true)
	if err != nil {
		return nil, err
	}
	st.rebalanceInterval = 300
	st.maxIdleTime = 15
	st.lastSendTime = time.Now()
	st.lastConnectTime = time.Now()
	st.initialized = true
	return st, nil
}
func (st *S2S) connect(endpoint string) error {
	st.mutex.Lock()
	var err error
	st.conn, err = net.DialTimeout("tcp", endpoint, 2*time.Second)
	if err != nil {
		return err
	}
	st.buf = bufio.NewWriterSize(st.conn, st.bufferBytes)
	if st.tls {
		config := &tls.Config{
			InsecureSkipVerify: st.insecureSkipVerify,
			ServerName:         st.serverName,
		}
		if len(st.cert) > 0 {
			roots := x509.NewCertPool()
			ok := roots.AppendCertsFromPEM([]byte(st.cert))
			if !ok {
				return fmt.Errorf("Failed to parse root certificate")
			}
			config.RootCAs = roots
		}
		st.conn = tls.Client(st.conn, config)
	}
	st.mutex.Unlock()
	err = st.sendSig()
	if err != nil {
		return err
	}
	go st.readAndDiscard()
	st.lastConnectTime = time.Now()
	st.lastSendTime = time.Now()
	return err
}
func (st *S2S) SetRebalanceInterval(interval int) {
	st.rebalanceInterval = interval
}
func (st *S2S) sendSig() error {
	st.mutex.RLock()
	defer st.mutex.RUnlock()
	endpointParts := strings.Split(st.endpoint, ":")
	if len(endpointParts) != 2 {
		return fmt.Errorf("Endpoint malformed.  Should look like server:port")
	}
	serverName := endpointParts[0]
	mgmtPort := endpointParts[1]
	var sig splunkSignature
	copy(sig.signature[:], "--splunk-cooked-mode-v2--")
	copy(sig.serverName[:], serverName)
	copy(sig.mgmtPort[:], mgmtPort)
	buf := &bytes.Buffer{}
	binary.Write(buf, binary.BigEndian, sig.signature)
	binary.Write(buf, binary.BigEndian, sig.serverName)
	binary.Write(buf, binary.BigEndian, sig.mgmtPort)
	st.buf.Write(buf.Bytes())
	return nil
}
func encodeString(tosend string, buf *bytes.Buffer) {
	l := uint32(len(tosend) + 1)
	binary.Write(buf, binary.BigEndian, l)
	binary.Write(buf, binary.BigEndian, []byte(tosend))
	binary.Write(buf, binary.BigEndian, []byte{0})
}
func encodeKeyValue(key, value string, buf *bytes.Buffer) {
	encodeString(key, buf)
	encodeString(value, buf)
}
func EncodeEvent(line map[string]string) []byte {
	buf := bp.Get().(*bytes.Buffer)
	defer bp.Put(buf)
	buf.Reset()
	binary.Write(buf, binary.BigEndian, uint32(0))  
	binary.Write(buf, binary.BigEndian, uint32(0))  
	_time := line["_time"]
	host := line["host"]
	source := line["source"]
	sourcetype := line["sourcetype"]
	index := line["index"]
	channel, hasChannel := line["_channel"]
	conf, hasConf := line["_conf"]
	_, hasLineBreaker := line["_linebreaker"]
	_, hasDone := line["_done"]
	var indexFields string
	if strings.ContainsRune(_time, '.') {
		timeparts := strings.Split(_time, ".")
		_time = timeparts[0]
		indexFields += "_subsecond::" + timeparts[1] + " "
	}
	for k, v := range line {
		switch k {
		case "source", "sourcetype", "host", "index", "_raw", "_time", "_channel", "_conf", "_linebreaker", "_done":
			break
		default:
			indexFields += k + "::" + v + " "
		}
	}
	maps := 7
	encodeKeyValue("_raw", line["_raw"], buf)
	if len(indexFields) > 0 {
		indexFields = strings.TrimRight(indexFields, " ")
		encodeKeyValue("_meta", indexFields, buf)
		maps++
	}
	if hasDone {
		encodeKeyValue("_done", "_done", buf)
		maps++
	}
	if hasLineBreaker {
		encodeKeyValue("_linebreaker", "_linebreaker", buf)
		maps++
	}
	encodeKeyValue("_hpn", "_hpn", buf)
	encodeKeyValue("_time", _time, buf)
	if hasConf {
		encodeKeyValue("_conf", conf, buf)
		maps++
	}
	encodeKeyValue("MetaData:Source", "source::"+source, buf)
	encodeKeyValue("MetaData:Host", "host::"+host, buf)
	encodeKeyValue("MetaData:Sourcetype", "sourcetype::"+sourcetype, buf)
	if hasChannel {
		encodeKeyValue("_channel", channel, buf)
		maps++
	}
	encodeKeyValue("_MetaData:Index", index, buf)
	binary.Write(buf, binary.BigEndian, uint32(0))  
	encodeString("_raw", buf)                       
	ret := buf.Bytes()
	binary.BigEndian.PutUint32(ret, uint32(len(ret)-4))  
	binary.BigEndian.PutUint32(ret[4:], uint32(maps))    
	return ret
}
func (st *S2S) Send(event map[string]string) (int64, error) {
	return st.Copy(bytes.NewBuffer(EncodeEvent(event)))
}