func NewFSockPool(maxFSocks int, fsaddr, fspasswd string, reconnects int, maxWaitConn time.Duration,
	eventHandlers map[string][]func(string, int), eventFilters map[string][]string,
	l logger, connIdx int, bgapiSup bool) *FSockPool {
	if l == nil {
		l = nopLogger{}
	}
	pool := &FSockPool{
		connIdx:       connIdx,
		fsAddr:        fsaddr,
		fsPasswd:      fspasswd,
		reconnects:    reconnects,
		maxWaitConn:   maxWaitConn,
		eventHandlers: eventHandlers,
		eventFilters:  eventFilters,
		logger:        l,
		allowedConns:  make(chan struct{}, maxFSocks),
		fSocks:        make(chan *FSock, maxFSocks),
		bgapiSup:      bgapiSup,
	}
	for i := 0; i < maxFSocks; i++ {
		pool.allowedConns <- struct{}{}  
	}
	return pool
}
Instantiates a new FSockPool
package fsock
import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"net"
	"strconv"
	"strings"
	"sync"
	"time"
)
var (
	DelayFunc func() func() int
	ErrConnectionPoolTimeout = errors.New("ConnectionPool timeout")
)
func init() {
	DelayFunc = fib
}
func NewFSock(fsaddr, fspaswd string, reconnects int,
	eventHandlers map[string][]func(string, int),
	eventFilters map[string][]string,
	l logger, connIdx int, bgapiSup bool) (fsock *FSock, err error) {
	if l == nil {
		l = nopLogger{}
	}
	fsock = &FSock{
		fsMutex:         new(sync.RWMutex),
		connIdx:         connIdx,
		fsaddress:       fsaddr,
		fspaswd:         fspaswd,
		eventHandlers:   eventHandlers,
		eventFilters:    eventFilters,
		backgroundChans: make(map[string]chan string),
		cmdChan:         make(chan string),
		reconnects:      reconnects,
		delayFunc:       DelayFunc(),
		logger:          l,
		bgapiSup:        bgapiSup,
	}
	if err = fsock.Connect(); err != nil {
		return nil, err
	}
	return
}
type FSock struct {
	conn            net.Conn
	fsMutex         *sync.RWMutex
	connIdx         int  
	buffer          *bufio.Reader
	fsaddress       string
	fspaswd         string
	eventHandlers   map[string][]func(string, int)  
	eventFilters    map[string][]string
	backgroundChans map[string]chan string
	cmdChan         chan string
	reconnects      int
	delayFunc       func() int
	stopReadEvents  chan struct{}  
	errReadEvents   chan error
	logger          logger
	bgapiSup        bool
}
func (fs *FSock) Connect() error {
	if fs.stopReadEvents != nil {
		close(fs.stopReadEvents)  
	}
	fs.stopReadEvents = make(chan struct{})
	fs.errReadEvents = make(chan error)
	return fs.connect()
}
func (fs *FSock) connect() (err error) {
	if fs.Connected() {
		fs.Disconnect()
	}
	var conn net.Conn
	if conn, err = net.Dial("tcp", fs.fsaddress); err != nil {
		fs.logger.Err(fmt.Sprintf("<FSock> Attempt to connect to FreeSWITCH, received: %s", err.Error()))
		return
	}
	fs.fsMutex.Lock()
	fs.conn = conn
	fs.fsMutex.Unlock()
	fs.logger.Info("<FSock> Successfully connected to FreeSWITCH!")
	fs.fsMutex.RLock()
	fs.buffer = bufio.NewReaderSize(fs.conn, 8192)  
	fs.fsMutex.RUnlock()
	var authChg string
	if authChg, err = fs.readHeaders(); err != nil {
		return fmt.Errorf("Received error<%s> when receiving the auth challenge", err)
	}
	if !strings.Contains(authChg, "auth/request") {
		return errors.New("No auth challenge received")
	}
	if err = fs.auth(); err != nil {  
		return
	}
	if err = fs.filterEvents(fs.eventFilters, fs.bgapiSup); err != nil {
		return
	}
	if err = fs.eventsPlain(getMapKeys(fs.eventHandlers), fs.bgapiSup); err != nil {
		return
	}
	go fs.readEvents()  
	return
}
func (fs *FSock) Connected() (ok bool) {
	fs.fsMutex.RLock()
	ok = (fs.conn != nil)
	fs.fsMutex.RUnlock()
	return
}
func (fs *FSock) Disconnect() (err error) {
	fs.fsMutex.Lock()
	if fs.conn != nil {
		fs.logger.Info("<FSock> Disconnecting from FreeSWITCH!")
		err = fs.conn.Close()
		fs.conn = nil
	}
	fs.fsMutex.Unlock()
	return
}
func (fs *FSock) ReconnectIfNeeded() (err error) {
	if fs.Connected() {  
		return
	}
	for i := 0; fs.reconnects == -1 || i < fs.reconnects; i++ {  
		if err = fs.connect(); err == nil && fs.Connected() {
			fs.delayFunc = DelayFunc()  
			break                       
		}
		time.Sleep(time.Duration(fs.delayFunc()) * time.Second)
	}
	if err == nil && !fs.Connected() {
		return errors.New("Not connected to FreeSWITCH")
	}
	return  
}
func (fs *FSock) send(cmd string) (err error) {
	fs.fsMutex.RLock()
	defer fs.fsMutex.RUnlock()
	if _, err = fs.conn.Write([]byte(cmd)); err != nil {
		fs.logger.Err(fmt.Sprintf("<FSock> Cannot write command to socket <%s>", err.Error()))
	}
	return
}
func (fs *FSock) auth() (err error) {
	if err = fs.send("auth " + fs.fspaswd + "\n\n"); err != nil {
		return
	}
	var rply string
	if rply, err = fs.readHeaders(); err != nil {
		return
	}
	if !strings.Contains(rply, "Reply-Text: +OK accepted") {
		return fmt.Errorf("Unexpected auth reply received: <%s>", rply)
	}
	return
}
func (fs *FSock) sendCmd(cmd string) (rply string, err error) {
	if err = fs.ReconnectIfNeeded(); err != nil {
		return
	}
	if err = fs.send(cmd + "\n"); err != nil {
		return
	}
	rply = <-fs.cmdChan
	if strings.Contains(rply, "-ERR") {
		return "", errors.New(strings.TrimSpace(rply))
	}
	return
}
func (fs *FSock) SendCmd(cmdStr string) (string, error) {
	return fs.sendCmd(cmdStr + "\n")
}
func (fs *FSock) SendCmdWithArgs(cmd string, args map[string]string, body string) (string, error) {
	for k, v := range args {
		cmd += k + ": " + v + "\n"
	}
	if len(body) != 0 {
		cmd += "\n" + body + "\n"
	}
	return fs.sendCmd(cmd)
}
func (fs *FSock) SendApiCmd(cmdStr string) (string, error) {
	return fs.sendCmd("api " + cmdStr + "\n")
}
func (fs *FSock) SendBgapiCmd(cmdStr string) (out chan string, err error) {
	jobUUID := genUUID()
	out = make(chan string)
	fs.fsMutex.Lock()
	fs.backgroundChans[jobUUID] = out
	fs.fsMutex.Unlock()
	_, err = fs.sendCmd("bgapi " + cmdStr + "\nJob-UUID:" + jobUUID + "\n")
	if err != nil {
		return nil, err
	}
	return
}
func (fs *FSock) SendMsgCmdWithBody(uuid string, cmdargs map[string]string, body string) (err error) {
	if len(cmdargs) == 0 {
		return errors.New("Need command arguments")
	}
	_, err = fs.SendCmdWithArgs("sendmsg "+uuid+"\n", cmdargs, body)
	return
}
func (fs *FSock) SendMsgCmd(uuid string, cmdargs map[string]string) error {
	return fs.SendMsgCmdWithBody(uuid, cmdargs, "")
}
func (fs *FSock) SendEventWithBody(eventSubclass string, eventParams map[string]string, body string) (string, error) {
	eventParams["Event-Subclass"] = eventSubclass
	return fs.SendCmdWithArgs("sendevent "+eventSubclass+"\n", eventParams, body)
}
func (fs *FSock) SendEvent(eventSubclass string, eventParams map[string]string) (string, error) {
	return fs.SendEventWithBody(eventSubclass, eventParams, "")
}
func (fs *FSock) ReadEvents() (err error) {
	for {
		if err = <-fs.errReadEvents; err == io.EOF {  
			if err = fs.ReconnectIfNeeded(); err != nil {
				return
			}
		}
	}
}
func (fs *FSock) LocalAddr() net.Addr {
	if !fs.Connected() {
		return nil
	}
	return fs.conn.LocalAddr()
}
func (fs *FSock) readHeaders() (header string, err error) {
	bytesRead := make([]byte, 0)
	var readLine []byte
	for {
		readLine, err = fs.buffer.ReadBytes('\n')
		if err != nil {
			fs.logger.Err(fmt.Sprintf("<FSock> Error reading headers: <%s>", err.Error()))
			fs.Disconnect()
			return
		}
		if len(bytes.TrimSpace(readLine)) == 0 {
			break
		}
		bytesRead = append(bytesRead, readLine...)
	}
	return string(bytesRead), nil
}
func (fs *FSock) readBody(noBytes int) (body string, err error) {
	bytesRead := make([]byte, noBytes)
	var readByte byte
	for i := 0; i < noBytes; i++ {
		if readByte, err = fs.buffer.ReadByte(); err != nil {
			fs.logger.Err(fmt.Sprintf("<FSock> Error reading message body: <%s>", err.Error()))
			fs.Disconnect()
			return
		}
		bytesRead[i] = readByte
	}
	return string(bytesRead), nil
}
func (fs *FSock) readEvent() (header string, body string, err error) {
	if header, err = fs.readHeaders(); err != nil {
		return
	}
	if !strings.Contains(header, "Content-Length") {  
		return
	}
	var cl int
	if cl, err = strconv.Atoi(headerVal(header, "Content-Length")); err != nil {
		err = fmt.Errorf("Cannot extract content length because<%s>", err)
		return
	}
	body, err = fs.readBody(cl)
	return
}
func (fs *FSock) readEvents() {
	for {
		select {
		case <-fs.stopReadEvents:
			return
		default:  
		}
		hdr, body, err := fs.readEvent()
		if err != nil {
			fs.errReadEvents <- err
			return
		}
		if strings.Contains(hdr, "api/response") {
			fs.cmdChan <- body
		} else if strings.Contains(hdr, "command/reply") {
			fs.cmdChan <- headerVal(hdr, "Reply-Text")
		} else if body != "" {  
			fs.dispatchEvent(body)
		}
	}
}
func (fs *FSock) eventsPlain(events []string, bgapiSup bool) (err error) {
	eventsCmd := "event plain"
	customEvents := ""
	for _, ev := range events {
		if ev == "ALL" {
			eventsCmd = "event plain all"
			break
		}
		if strings.HasPrefix(ev, "CUSTOM") {
			customEvents += ev[6:]  
			continue
		}
		eventsCmd += " " + ev
	}
	if eventsCmd != "event plain all" {
		if bgapiSup {
			eventsCmd += " BACKGROUND_JOB"  
		}
		if len(customEvents) != 0 {  
			eventsCmd += " " + "CUSTOM" + customEvents
		}
	}
	if err = fs.send(eventsCmd + "\n\n"); err != nil {
		fs.Disconnect()
		return
	}
	var rply string
	if rply, err = fs.readHeaders(); err != nil {
		return
	}
	if !strings.Contains(rply, "Reply-Text: +OK") {
		fs.Disconnect()
		return fmt.Errorf("Unexpected events-subscribe reply received: <%s>", rply)
	}
	return
}
func (fs *FSock) filterEvents(filters map[string][]string, bgapiSup bool) (err error) {
	if len(filters) == 0 {
		return nil
	}
	if bgapiSup {
		filters["Event-Name"] = append(filters["Event-Name"], "BACKGROUND_JOB")  
	}
	for hdr, vals := range filters {
		for _, val := range vals {
			if err = fs.send("filter " + hdr + " " + val + "\n\n"); err != nil {
				fs.Disconnect()
				return
			}
			var rply string
			if rply, err = fs.readHeaders(); err != nil {
				return
			}
			if !strings.Contains(rply, "Reply-Text: +OK") {
				fs.Disconnect()
				return fmt.Errorf("Unexpected filter-events reply received: <%s>", rply)
			}
		}
	}
	return nil
}
func (fs *FSock) dispatchEvent(event string) {
	eventName := headerVal(event, "Event-Name")
	if eventName == "BACKGROUND_JOB" {  
		go fs.doBackgroundJob(event)
		return
	}
	if eventName == "CUSTOM" {
		eventSubclass := headerVal(event, "Event-Subclass")
		if len(eventSubclass) != 0 {
			eventName += " " + urlDecode(eventSubclass)
		}
	}
	for _, handleName := range []string{eventName, "ALL"} {
		if _, hasHandlers := fs.eventHandlers[handleName]; hasHandlers {
			for _, handlerFunc := range fs.eventHandlers[handleName] {
				go handlerFunc(event, fs.connIdx)
			}
			return
		}
	}
	fs.logger.Warning(fmt.Sprintf("<FSock> No dispatcher for event: <%+v> with event name: %s", event, eventName))
}
func (fs *FSock) doBackgroundJob(event string) {  
	evMap := EventToMap(event)
	jobUUID, has := evMap["Job-UUID"]
	if !has {
		fs.logger.Err("<FSock> BACKGROUND_JOB with no Job-UUID")
		return
	}
	var out chan string
	fs.fsMutex.RLock()
	out, has = fs.backgroundChans[jobUUID]
	fs.fsMutex.RUnlock()
	if !has {
		fs.logger.Err(fmt.Sprintf("<FSock> BACKGROUND_JOB with UUID %s lost!", jobUUID))
		return  
	}
	fs.fsMutex.Lock()
	delete(fs.backgroundChans, jobUUID)
	fs.fsMutex.Unlock()
	out <- evMap[EventBodyTag]
}