<commit_before>package cq_test

import (
	_ "github.com/wfreeman/cq"
	. "launchpad.net/gocheck"
	"log"
	"testing"
	"time"
)

// This file is meant to hold integration tests where cq must be imported as _
// and is for testing transactions. Some of these are going to be long tests...

func Test(t *testing.T) {
	TestingT(t)
}

type TransactionSuite struct{}

var _ = Suite(&TransactionSuite{})

func clearTestRecords() {
	db := testConn()
	_, err := db.Exec("match (n:`TestRollback~~~~`) delete n")
	if err != nil {
		log.Fatal(err)
	}
	_, err = db.Exec("match (n:`TestCommit~~~~`) delete n")
	if err != nil {
		log.Fatal(err)
	}
}

func (s *TransactionSuite) SetUpTest(c *C) {
	clearTestRecords()
}

func (s *TransactionSuite) TearDownTest(c *C) {
	clearTestRecords()
}

func (s *TransactionSuite) TestTransactionRollback1(c *C) {
	testTransactionRollbackN(c, 1)
}
func (s *TransactionSuite) TestTransactionRollback7(c *C) {
	testTransactionRollbackN(c, 7)
}
func (s *TransactionSuite) TestTransactionRollback100(c *C) {
	testTransactionRollbackN(c, 100)
}
func (s *TransactionSuite) TestTransactionRollback1000(c *C) {
	testTransactionRollbackN(c, 1000)
}
func (s *TransactionSuite) TestTransactionRollback7777(c *C) {
	testTransactionRollbackN(c, 7777)
}

func testTransactionRollbackN(c *C, n int) {
	db := testConn()
	tx, err := db.Begin()
	c.Assert(err, IsNil)

	for i := 0; i < n; i++ {
		_, err := tx.Exec("create (:`TestRollback~~~~`)")
		c.Assert(err, IsNil)
	}

	err = tx.Rollback()
	c.Assert(err, IsNil)

	rows, err := db.Query("match (n:`TestRollback~~~~`) return count(1)")
	c.Assert(err, IsNil)
	defer rows.Close()

	rows.Next()

	var count int
	err = rows.Scan(&count)
	c.Assert(err, IsNil)

	if count > 0 {
		c.Fatal("rollback doesn't work")
	}
}

func (s *TransactionSuite) TestTransactionExecCommit1(c *C) {
	testTransactionExecCommitN(c, 1, 0)
}
func (s *TransactionSuite) TestTransactionExecCommit77(c *C) {
	testTransactionExecCommitN(c, 77, 0)
}
func (s *TransactionSuite) TestTransactionExecCommit100(c *C) {
	testTransactionExecCommitN(c, 100, 0)
}
func (s *TransactionSuite) TestTransactionExecCommit1000(c *C) {
	testTransactionExecCommitN(c, 1000, 0)
}
func (s *TransactionSuite) TestTransactionExecCommit7777(c *C) {
	testTransactionExecCommitN(c, 7777, 0)
}
func (s *TransactionSuite) TestTransactionExecCommit1Rec2Secs(c *C) {
	testTransactionExecCommitN(c, 1, 2*time.Second)
}

func testTransactionExecCommitN(c *C, n int, delay time.Duration) {
	db := testConn()
	tx, err := db.Begin()
	c.Assert(err, IsNil)

	for i := 0; i < n; i++ {
		_, err := tx.Exec("create (:`TestCommit~~~~` {id:{0}})", i)
		c.Assert(err, IsNil)
	}

	time.Sleep(delay)

	err = tx.Commit()
	c.Assert(err, IsNil)

	rows, err := db.Query("match (n:`TestCommit~~~~`) return n.id order by n.id")
	c.Assert(err, IsNil)
	defer rows.Close()

	var count int
	var i int
	for rows.Next() {
		if i > n {
			c.Fatal("i shouldn't be > n")
		}
		err = rows.Scan(&count)
		c.Assert(err, IsNil)
		i++
	}
	c.Assert(i, Equals, n)
}
<commit_msg>adding test for tx error checking<commit_after>package cq_test

import (
	_ "github.com/wfreeman/cq"
	. "launchpad.net/gocheck"
	"log"
	"testing"
	"time"
)

// This file is meant to hold integration tests where cq must be imported as _
// and is for testing transactions. Some of these are going to be long tests...

func Test(t *testing.T) {
	TestingT(t)
}

type TransactionSuite struct{}

var _ = Suite(&TransactionSuite{})

func clearTestRecords() {
	db := testConn()
	_, err := db.Exec("match (n:`TestRollback~~~~`) delete n")
	if err != nil {
		log.Fatal(err)
	}
	_, err = db.Exec("match (n:`TestCommit~~~~`) delete n")
	if err != nil {
		log.Fatal(err)
	}
}

func (s *TransactionSuite) SetUpTest(c *C) {
	clearTestRecords()
}

func (s *TransactionSuite) TearDownTest(c *C) {
	clearTestRecords()
}

func (s *TransactionSuite) TestTransactionRollback1(c *C) {
	testTransactionRollbackN(c, 1)
}
func (s *TransactionSuite) TestTransactionRollback7(c *C) {
	testTransactionRollbackN(c, 7)
}
func (s *TransactionSuite) TestTransactionRollback100(c *C) {
	testTransactionRollbackN(c, 100)
}
func (s *TransactionSuite) TestTransactionRollback1000(c *C) {
	testTransactionRollbackN(c, 1000)
}
func (s *TransactionSuite) TestTransactionRollback7777(c *C) {
	testTransactionRollbackN(c, 7777)
}

func testTransactionRollbackN(c *C, n int) {
	db := testConn()
	tx, err := db.Begin()
	c.Assert(err, IsNil)

	for i := 0; i < n; i++ {
		_, err := tx.Exec("create (:`TestRollback~~~~`)")
		c.Assert(err, IsNil)
	}

	err = tx.Rollback()
	c.Assert(err, IsNil)

	rows, err := db.Query("match (n:`TestRollback~~~~`) return count(1)")
	c.Assert(err, IsNil)
	defer rows.Close()

	rows.Next()

	var count int
	err = rows.Scan(&count)
	c.Assert(err, IsNil)

	if count > 0 {
		c.Fatal("rollback doesn't work")
	}
}

func (s *TransactionSuite) TestTransactionExecCommit1(c *C) {
	testTransactionExecCommitN(c, 1, 0)
}
func (s *TransactionSuite) TestTransactionExecCommit77(c *C) {
	testTransactionExecCommitN(c, 77, 0)
}
func (s *TransactionSuite) TestTransactionExecCommit100(c *C) {
	testTransactionExecCommitN(c, 100, 0)
}
func (s *TransactionSuite) TestTransactionExecCommit1000(c *C) {
	testTransactionExecCommitN(c, 1000, 0)
}
func (s *TransactionSuite) TestTransactionExecCommit7777(c *C) {
	testTransactionExecCommitN(c, 7777, 0)
}
func (s *TransactionSuite) TestTransactionExecCommit1Rec2Secs(c *C) {
	testTransactionExecCommitN(c, 1, 2*time.Second)
}

func testTransactionExecCommitN(c *C, n int, delay time.Duration) {
	db := testConn()
	tx, err := db.Begin()
	c.Assert(err, IsNil)

	for i := 0; i < n; i++ {
		_, err := tx.Exec("create (:`TestCommit~~~~` {id:{0}})", i)
		c.Assert(err, IsNil)
	}

	time.Sleep(delay)

	err = tx.Commit()
	c.Assert(err, IsNil)

	rows, err := db.Query("match (n:`TestCommit~~~~`) return n.id order by n.id")
	c.Assert(err, IsNil)
	defer rows.Close()

	var count int
	var i int
	for rows.Next() {
		if i > n {
			c.Fatal("i shouldn't be > n")
		}
		err = rows.Scan(&count)
		c.Assert(err, IsNil)
		i++
	}
	c.Assert(i, Equals, n)
}

func (s *TransactionSuite) TestTxBadCypherError(c *C) {
	db := testConn()
	tx, err := db.Begin()
	c.Assert(err, IsNil)

	for i := 0; i < 1000; i++ {
		_, err := tx.Exec("create (:`TestCommit~~~~` {id:{0}})")
		if i%100 != 99 {
			c.Assert(err, IsNil)
		} else {
			if err == nil {
				c.Fatal(err)
			}
		}
	}
	err = tx.Commit()
	if err == nil {
		c.Fatal(err)
	}
}
<|endoftext|>


<commit_before>//  Copyright 2016 The goscope Authors
//
//  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 dummy

import (
	"math"
	"testing"

	"github.com/zagrodzki/goscope/scope"
)

func TestRandom(t *testing.T) {
	ch := &randomChan{}
	data := ch.data(0)
	min, max := scope.Voltage(-1), scope.Voltage(1)
	avgDiff := data[len(data)-1] / scope.Voltage(len(data))
	var last scope.Voltage
	var stdDev scope.Voltage
	last = 0
	for _, s := range data {
		switch {
		case s > max:
			max = s
		case s < min:
			min = s
		}
		diff := s - last
		last = s
		stdDev += (diff - avgDiff) * (diff - avgDiff)
	}
	stdDev /= scope.Voltage(len(data))

	if got, min, max := math.Sqrt(float64(stdDev)), 0.05, 0.15; got < min || got > max {
		t.Errorf("sample difference stddev squared: expected between %v and %v, got %v", min, max, got)
	}
	if want := scope.Voltage(1); max != want {
		t.Errorf("maximal sample value in the data set: %v, want %v", max, want)
	}
	if want := scope.Voltage(-1); min != want {
		t.Errorf("minimal sample value in the data set: %v, want %v", min, want)
	}
}
<commit_msg>Find the real min/max, even if it's within -1..1 range. Better error messages.<commit_after>//  Copyright 2016 The goscope Authors
//
//  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 dummy

import (
	"math"
	"testing"

	"github.com/zagrodzki/goscope/scope"
)

func TestRandom(t *testing.T) {
	ch := &randomChan{}
	data := ch.data(0)
	min, max := data[0], data[0]
	avgDiff := data[len(data)-1] / scope.Voltage(len(data))
	var last scope.Voltage
	var stdDev scope.Voltage
	last = 0
	for _, s := range data {
		switch {
		case s > max:
			max = s
		case s < min:
			min = s
		}
		diff := s - last
		last = s
		stdDev += (diff - avgDiff) * (diff - avgDiff)
	}
	stdDev /= scope.Voltage(len(data))

	if got, min, max := math.Sqrt(float64(stdDev)), 0.05, 0.15; got < min || got > max {
		t.Errorf("sample difference stddev squared: expected between %v and %v, got %v", min, max, got)
	}
	if want := scope.Voltage(1); max != want {
		t.Errorf("maximal sample value in the data set: %v, want less equal than %v", max, want)
	}
	if want := scope.Voltage(-1); min != want {
		t.Errorf("minimal sample value in the data set: %v, want greater equal than %v", min, want)
	}
}
<|endoftext|>


<commit_before>/**
 * Copyright 2015 Comcast Cable Communications Management, LLC
 *
 * 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 eellib

import (
	"encoding/json"
	"errors"
	"sync"

	. "github.com/Comcast/eel/eel/jtl"
	. "github.com/Comcast/eel/eel/util"
)

// EELInit initalize environment for EEL API use
func EELInit(ctx Context) {
	Gctx = ctx
	eelSettings := new(EelSettings)
	eelSettings.MaxAttempts = 2
	eelSettings.InitialDelay = 125
	eelSettings.InitialBackoff = 3000
	eelSettings.BackoffMethod = "Exponential"
	eelSettings.HttpTimeout = 3000
	eelSettings.ResponseHeaderTimeout = 3000
	eelSettings.MaxMessageSize = 512000
	eelSettings.HttpTransactionHeader = "X-B3-TraceId"
	eelSettings.HttpTenantHeader = "X-TenantId"
	eelSettings.AppName = "eel"
	eelSettings.Name = "eel"
	eelSettings.Version = "1.0"
	ctx.AddConfigValue(EelConfig, eelSettings)
	eelServiceStats := new(ServiceStats)
	ctx.AddValue(EelTotalStats, eelServiceStats)
	InitHttpTransport(ctx)
}

// EELGetSettings get current settings for read / write
func EELGetSettings(ctx Context) (*EelSettings, error) {
	if Gctx == nil {
		return nil, errors.New("must call EELInit first")
	}
	if ctx == nil {
		return nil, errors.New("ctx cannot be nil")
	}
	if ctx.ConfigValue(EelConfig) == nil {
		return nil, errors.New("no settings")
	}
	return ctx.ConfigValue(EelConfig).(*EelSettings), nil
}

// EELGetSettings get current settings for read / write
func EELUpdateSettings(ctx Context, eelSettings *EelSettings) error {
	if Gctx == nil {
		return errors.New("must call EELInit first")
	}
	if ctx == nil {
		return errors.New("ctx cannot be nil")
	}
	if eelSettings == nil {
		return errors.New("no settings")
	}
	ctx.AddValue(EelConfig, eelSettings)
	InitHttpTransport(ctx)
	return nil
}

// EELNewHandlerFactory creates handler factory for given folder with handler files.
func EELNewHandlerFactory(ctx Context, configFolder string) (*HandlerFactory, []error) {
	if Gctx == nil {
		return nil, []error{errors.New("must call EELInit first")}
	}
	if ctx == nil {
		return nil, []error{errors.New("ctx cannot be nil")}
	}
	ctx = ctx.SubContext()
	ClearErrors(ctx)
	eelHandlerFactory, warnings := NewHandlerFactory(ctx, []string{configFolder})
	return eelHandlerFactory, warnings
}

// EELGetHandlersForEvent gets all handlers for a given event.
func EELGetHandlersForEvent(ctx Context, event interface{}, eelHandlerFactory *HandlerFactory) ([]*HandlerConfiguration, error) {
	if Gctx == nil {
		return nil, errors.New("must call EELInit first")
	}
	if ctx == nil {
		return nil, errors.New("ctx cannot be nil")
	}
	ctx = ctx.SubContext()
	ClearErrors(ctx)
	doc, err := NewJDocFromInterface(event)
	if err != nil {
		return nil, err
	}
	eelMatchingHandlers := eelHandlerFactory.GetHandlersForEvent(ctx, doc)
	return eelMatchingHandlers, nil
}

// EELGetPublishers is similar to EELTransformEvent but return slice of publishers instead of slice of events.
func EELGetPublishers(ctx Context, event interface{}, eelHandlerFactory *HandlerFactory) ([]EventPublisher, []error) {
	if Gctx == nil {
		return nil, []error{errors.New("must call EELInit first")}
	}
	if ctx == nil {
		return nil, []error{errors.New("ctx cannot be nil")}
	}
	ctx = ctx.SubContext()
	ClearErrors(ctx)
	doc, err := NewJDocFromInterface(event)
	if err != nil {
		return nil, []error{err}
	}
	publishers := make([]EventPublisher, 0)
	eelMatchingHandlers := eelHandlerFactory.GetHandlersForEvent(ctx, doc)
	for _, h := range eelMatchingHandlers {
		p, err := h.ProcessEvent(ctx, doc)
		if err != nil {
			return nil, []error{err}
		}
		publishers = append(publishers, p...)
	}
	return publishers, GetErrors(ctx)
}

// EELGetPublishersConcurrent is the concurrent version of EELGetPublishers. Useful when processing multiple
// expensive transformations at the same time (e.g. making heavy use of curl() function calls).
func EELGetPublishersConcurrent(ctx Context, event interface{}, eelHandlerFactory *HandlerFactory) ([]EventPublisher, []error) {
	if Gctx == nil {
		return nil, []error{errors.New("must call EELInit first")}
	}
	if ctx == nil {
		return nil, []error{errors.New("ctx cannot be nil")}
	}
	ctx = ctx.SubContext()
	ClearErrors(ctx)
	doc, err := NewJDocFromInterface(event)
	if err != nil {
		return nil, []error{err}
	}
	eelMatchingHandlers := eelHandlerFactory.GetHandlersForEvent(ctx, doc)
	var mtx sync.Mutex
	var wg sync.WaitGroup
	publishers := make([]EventPublisher, 0)
	errs := make([]error, 0)
	for _, h := range eelMatchingHandlers {
		wg.Add(1)
		go func(h *HandlerConfiguration) {
			sctx := ctx.SubContext()
			d, _ := NewJDocFromInterface(event)
			pp, err := h.ProcessEvent(sctx, d)
			if err != nil {
				mtx.Lock()
				errs = append(errs, err)
				mtx.Unlock()
				wg.Done()
				return
			}
			ee := GetErrors(sctx)
			if ee != nil && len(ee) > 0 {
				mtx.Lock()
				errs = append(errs, ee...)
				mtx.Unlock()
			}
			if pp != nil && len(pp) > 0 {
				mtx.Lock()
				publishers = append(publishers, pp...)
				mtx.Unlock()
			}
			wg.Done()
		}(h)
	}
	wg.Wait()
	if len(errs) > 0 {
		return publishers, errs
	} else {
		return publishers, nil
	}
}

// EELTransformEvent transforms single event based on set of configuration handlers. Can yield multiple results.
func EELTransformEvent(ctx Context, event interface{}, eelHandlerFactory *HandlerFactory) ([]interface{}, []error) {
	if Gctx == nil {
		return nil, []error{errors.New("must call EELInit first")}
	}
	if ctx == nil {
		return nil, []error{errors.New("ctx cannot be nil")}
	}
	ctx = ctx.SubContext()
	ClearErrors(ctx)
	publishers, errs := EELGetPublishers(ctx, event, eelHandlerFactory)
	if errs != nil {
		return nil, errs
	}
	events := make([]interface{}, 0)
	for _, p := range publishers {
		events = append(events, p.GetPayloadParsed().GetOriginalObject())
	}
	return events, GetErrors(ctx)
}

// EELTransformEventConcurrent is the concurrent version of EELTransformEvent. Useful when processing multiple
// expensive transformations at the same time (e.g. making heavy use of curl() function calls).
func EELTransformEventConcurrent(ctx Context, event interface{}, eelHandlerFactory *HandlerFactory) ([]interface{}, []error) {
	if Gctx == nil {
		return nil, []error{errors.New("must call EELInit first")}
	}
	if ctx == nil {
		return nil, []error{errors.New("ctx cannot be nil")}
	}
	ctx = ctx.SubContext()
	ClearErrors(ctx)
	publishers, errs := EELGetPublishersConcurrent(ctx, event, eelHandlerFactory)
	if errs != nil {
		return nil, errs
	}
	events := make([]interface{}, 0)
	for _, p := range publishers {
		events = append(events, p.GetPayloadParsed().GetOriginalObject())
	}
	return events, GetErrors(ctx)
}

// EELSingleTransform can work with raw JSON transformation or a transformation wrapped in a config handler.
// Transformation must yield single result or no result (if filtered).
func EELSimpleTransform(ctx Context, event string, transformation string, isTransformationByExample bool) (string, []error) {
	if Gctx == nil {
		return "", []error{errors.New("must call EELInit first")}
	}
	if ctx == nil {
		return "", []error{errors.New("ctx cannot be nil")}
	}
	ctx = ctx.SubContext()
	ClearErrors(ctx)
	doc, err := NewJDocFromString(event)
	if err != nil {
		return "", []error{err}
	}
	tf, err := NewJDocFromString(transformation)
	if err != nil {
		return "", []error{err}
	}
	h := new(HandlerConfiguration)
	err = json.Unmarshal([]byte(transformation), h)
	if err != nil || h.Transformation == nil {
		h.Transformation = tf.GetOriginalObject()
		h.IsTransformationByExample = isTransformationByExample
	}
	if h.Protocol == "" {
		h.Protocol = "http"
	}
	if h.Endpoint == nil {
		h.Endpoint = "http://localhost"
	}
	hf, _ := NewHandlerFactory(ctx, nil)
	h, _ = hf.GetHandlerConfigurationFromJson(ctx, "", *h)
	p, err := h.ProcessEvent(ctx, doc)
	if err != nil {
		return "", []error{err}
	}
	if len(p) > 1 {
		return "", []error{errors.New("transformation must yield single result")}
	} else if len(p) == 0 {
		return "", nil
	} else {
		return p[0].GetPayload(), GetErrors(ctx)
	}
}

// EELSingleTransform can work with raw JSON transformation or a transformation wrapped in a config handler.
// Transformation must yield single result or no result (if filtered).
func EELSimpleEvalExpression(ctx Context, event string, expr string) (string, []error) {
	if Gctx == nil {
		return "", []error{errors.New("must call EELInit first")}
	}
	if ctx == nil {
		return "", []error{errors.New("ctx cannot be nil")}
	}
	ctx = ctx.SubContext()
	ClearErrors(ctx)
	doc, err := NewJDocFromString(event)
	if err != nil {
		return "", []error{err}
	}
	result := doc.ParseExpression(ctx, expr)
	if result == nil {
		return "", GetErrors(ctx)
	}
	rdoc, err := NewJDocFromInterface(result)
	if err != nil {
		return "", []error{err}
	}
	return rdoc.StringPretty(), GetErrors(ctx)
}
<commit_msg>adjusted default settings for eellib<commit_after>/**
 * Copyright 2015 Comcast Cable Communications Management, LLC
 *
 * 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 eellib

import (
	"encoding/json"
	"errors"
	"sync"

	. "github.com/Comcast/eel/eel/jtl"
	. "github.com/Comcast/eel/eel/util"
)

// EELInit initalize environment for EEL API use
func EELInit(ctx Context) {
	Gctx = ctx
	eelSettings := new(EelSettings)
	eelSettings.MaxAttempts = 3
	eelSettings.InitialDelay = 125
	eelSettings.InitialBackoff = 500
	eelSettings.BackoffMethod = "Exponential"
	eelSettings.HttpTimeout = 3000
	eelSettings.ResponseHeaderTimeout = 3000
	eelSettings.MaxMessageSize = 512000
	eelSettings.HttpTransactionHeader = "X-B3-TraceId"
	eelSettings.HttpTenantHeader = "X-TenantId"
	eelSettings.AppName = "eellib"
	eelSettings.Name = "eellib"
	eelSettings.Version = "1.0"
	ctx.AddConfigValue(EelConfig, eelSettings)
	eelServiceStats := new(ServiceStats)
	ctx.AddValue(EelTotalStats, eelServiceStats)
	InitHttpTransport(ctx)
}

// EELGetSettings get current settings for read / write
func EELGetSettings(ctx Context) (*EelSettings, error) {
	if Gctx == nil {
		return nil, errors.New("must call EELInit first")
	}
	if ctx == nil {
		return nil, errors.New("ctx cannot be nil")
	}
	if ctx.ConfigValue(EelConfig) == nil {
		return nil, errors.New("no settings")
	}
	return ctx.ConfigValue(EelConfig).(*EelSettings), nil
}

// EELGetSettings get current settings for read / write
func EELUpdateSettings(ctx Context, eelSettings *EelSettings) error {
	if Gctx == nil {
		return errors.New("must call EELInit first")
	}
	if ctx == nil {
		return errors.New("ctx cannot be nil")
	}
	if eelSettings == nil {
		return errors.New("no settings")
	}
	ctx.AddValue(EelConfig, eelSettings)
	InitHttpTransport(ctx)
	return nil
}

// EELNewHandlerFactory creates handler factory for given folder with handler files.
func EELNewHandlerFactory(ctx Context, configFolder string) (*HandlerFactory, []error) {
	if Gctx == nil {
		return nil, []error{errors.New("must call EELInit first")}
	}
	if ctx == nil {
		return nil, []error{errors.New("ctx cannot be nil")}
	}
	ctx = ctx.SubContext()
	ClearErrors(ctx)
	eelHandlerFactory, warnings := NewHandlerFactory(ctx, []string{configFolder})
	return eelHandlerFactory, warnings
}

// EELGetHandlersForEvent gets all handlers for a given event.
func EELGetHandlersForEvent(ctx Context, event interface{}, eelHandlerFactory *HandlerFactory) ([]*HandlerConfiguration, error) {
	if Gctx == nil {
		return nil, errors.New("must call EELInit first")
	}
	if ctx == nil {
		return nil, errors.New("ctx cannot be nil")
	}
	ctx = ctx.SubContext()
	ClearErrors(ctx)
	doc, err := NewJDocFromInterface(event)
	if err != nil {
		return nil, err
	}
	eelMatchingHandlers := eelHandlerFactory.GetHandlersForEvent(ctx, doc)
	return eelMatchingHandlers, nil
}

// EELGetPublishers is similar to EELTransformEvent but return slice of publishers instead of slice of events.
func EELGetPublishers(ctx Context, event interface{}, eelHandlerFactory *HandlerFactory) ([]EventPublisher, []error) {
	if Gctx == nil {
		return nil, []error{errors.New("must call EELInit first")}
	}
	if ctx == nil {
		return nil, []error{errors.New("ctx cannot be nil")}
	}
	ctx = ctx.SubContext()
	ClearErrors(ctx)
	doc, err := NewJDocFromInterface(event)
	if err != nil {
		return nil, []error{err}
	}
	publishers := make([]EventPublisher, 0)
	eelMatchingHandlers := eelHandlerFactory.GetHandlersForEvent(ctx, doc)
	for _, h := range eelMatchingHandlers {
		p, err := h.ProcessEvent(ctx, doc)
		if err != nil {
			return nil, []error{err}
		}
		publishers = append(publishers, p...)
	}
	return publishers, GetErrors(ctx)
}

// EELGetPublishersConcurrent is the concurrent version of EELGetPublishers. Useful when processing multiple
// expensive transformations at the same time (e.g. making heavy use of curl() function calls).
func EELGetPublishersConcurrent(ctx Context, event interface{}, eelHandlerFactory *HandlerFactory) ([]EventPublisher, []error) {
	if Gctx == nil {
		return nil, []error{errors.New("must call EELInit first")}
	}
	if ctx == nil {
		return nil, []error{errors.New("ctx cannot be nil")}
	}
	ctx = ctx.SubContext()
	ClearErrors(ctx)
	doc, err := NewJDocFromInterface(event)
	if err != nil {
		return nil, []error{err}
	}
	eelMatchingHandlers := eelHandlerFactory.GetHandlersForEvent(ctx, doc)
	var mtx sync.Mutex
	var wg sync.WaitGroup
	publishers := make([]EventPublisher, 0)
	errs := make([]error, 0)
	for _, h := range eelMatchingHandlers {
		wg.Add(1)
		go func(h *HandlerConfiguration) {
			sctx := ctx.SubContext()
			d, _ := NewJDocFromInterface(event)
			pp, err := h.ProcessEvent(sctx, d)
			if err != nil {
				mtx.Lock()
				errs = append(errs, err)
				mtx.Unlock()
				wg.Done()
				return
			}
			ee := GetErrors(sctx)
			if ee != nil && len(ee) > 0 {
				mtx.Lock()
				errs = append(errs, ee...)
				mtx.Unlock()
			}
			if pp != nil && len(pp) > 0 {
				mtx.Lock()
				publishers = append(publishers, pp...)
				mtx.Unlock()
			}
			wg.Done()
		}(h)
	}
	wg.Wait()
	if len(errs) > 0 {
		return publishers, errs
	} else {
		return publishers, nil
	}
}

// EELTransformEvent transforms single event based on set of configuration handlers. Can yield multiple results.
func EELTransformEvent(ctx Context, event interface{}, eelHandlerFactory *HandlerFactory) ([]interface{}, []error) {
	if Gctx == nil {
		return nil, []error{errors.New("must call EELInit first")}
	}
	if ctx == nil {
		return nil, []error{errors.New("ctx cannot be nil")}
	}
	ctx = ctx.SubContext()
	ClearErrors(ctx)
	publishers, errs := EELGetPublishers(ctx, event, eelHandlerFactory)
	if errs != nil {
		return nil, errs
	}
	events := make([]interface{}, 0)
	for _, p := range publishers {
		events = append(events, p.GetPayloadParsed().GetOriginalObject())
	}
	return events, GetErrors(ctx)
}

// EELTransformEventConcurrent is the concurrent version of EELTransformEvent. Useful when processing multiple
// expensive transformations at the same time (e.g. making heavy use of curl() function calls).
func EELTransformEventConcurrent(ctx Context, event interface{}, eelHandlerFactory *HandlerFactory) ([]interface{}, []error) {
	if Gctx == nil {
		return nil, []error{errors.New("must call EELInit first")}
	}
	if ctx == nil {
		return nil, []error{errors.New("ctx cannot be nil")}
	}
	ctx = ctx.SubContext()
	ClearErrors(ctx)
	publishers, errs := EELGetPublishersConcurrent(ctx, event, eelHandlerFactory)
	if errs != nil {
		return nil, errs
	}
	events := make([]interface{}, 0)
	for _, p := range publishers {
		events = append(events, p.GetPayloadParsed().GetOriginalObject())
	}
	return events, GetErrors(ctx)
}

// EELSingleTransform can work with raw JSON transformation or a transformation wrapped in a config handler.
// Transformation must yield single result or no result (if filtered).
func EELSimpleTransform(ctx Context, event string, transformation string, isTransformationByExample bool) (string, []error) {
	if Gctx == nil {
		return "", []error{errors.New("must call EELInit first")}
	}
	if ctx == nil {
		return "", []error{errors.New("ctx cannot be nil")}
	}
	ctx = ctx.SubContext()
	ClearErrors(ctx)
	doc, err := NewJDocFromString(event)
	if err != nil {
		return "", []error{err}
	}
	tf, err := NewJDocFromString(transformation)
	if err != nil {
		return "", []error{err}
	}
	h := new(HandlerConfiguration)
	err = json.Unmarshal([]byte(transformation), h)
	if err != nil || h.Transformation == nil {
		h.Transformation = tf.GetOriginalObject()
		h.IsTransformationByExample = isTransformationByExample
	}
	if h.Protocol == "" {
		h.Protocol = "http"
	}
	if h.Endpoint == nil {
		h.Endpoint = "http://localhost"
	}
	hf, _ := NewHandlerFactory(ctx, nil)
	h, _ = hf.GetHandlerConfigurationFromJson(ctx, "", *h)
	p, err := h.ProcessEvent(ctx, doc)
	if err != nil {
		return "", []error{err}
	}
	if len(p) > 1 {
		return "", []error{errors.New("transformation must yield single result")}
	} else if len(p) == 0 {
		return "", nil
	} else {
		return p[0].GetPayload(), GetErrors(ctx)
	}
}

// EELSingleTransform can work with raw JSON transformation or a transformation wrapped in a config handler.
// Transformation must yield single result or no result (if filtered).
func EELSimpleEvalExpression(ctx Context, event string, expr string) (string, []error) {
	if Gctx == nil {
		return "", []error{errors.New("must call EELInit first")}
	}
	if ctx == nil {
		return "", []error{errors.New("ctx cannot be nil")}
	}
	ctx = ctx.SubContext()
	ClearErrors(ctx)
	doc, err := NewJDocFromString(event)
	if err != nil {
		return "", []error{err}
	}
	result := doc.ParseExpression(ctx, expr)
	if result == nil {
		return "", GetErrors(ctx)
	}
	rdoc, err := NewJDocFromInterface(result)
	if err != nil {
		return "", []error{err}
	}
	return rdoc.StringPretty(), GetErrors(ctx)
}
<|endoftext|>


<commit_before>package config

import "github.com/kelseyhightower/envconfig"

const (
	// SERVICENAME contains a service name prefix which used in ENV variables
	SERVICENAME = "GITHUBSTATBOT"
)

type Config struct {
	Mode               string `default:"prod"`
	Port               string `default:"8080"`
	TlsDir             string `default:"./"`
	LogDir             string `default:"./log/"`
	StaticFilesDir     string `default:"./static"`
	DbPath             string `default:"database.db"`
	GitHubClientId     string `required:"true"`
	GitHubClientSecret string `required:"true"`
	TelegramToken      string `required:"true"`
	AuthBasicUsername  string `default:"user"`
	AuthBasicPassword  string `default:"password"`
}

func Load() (*Config, error) {

	cfg := new(Config)

	err := envconfig.Process(SERVICENAME, cfg)
	if err != nil {
		return nil, err
	}

	return cfg, nil
}
<commit_msg>The project configuration to deploy to Heroku.<commit_after>package config

import "github.com/kelseyhightower/envconfig"

const (
	// SERVICENAME contains a service name prefix which used in ENV variables
	SERVICENAME = "GITHUBSTATBOT"
)

type Config struct {
	Mode               string `default:"prod"`
	Port               string `envconfig:"PORT",default:"8080"`
	TlsDir             string `default:"./"`
	LogDir             string `default:"./log/"`
	StaticFilesDir     string `default:"./static"`
	DbPath             string `default:"database.db"`
	GitHubClientId     string `required:"true"`
	GitHubClientSecret string `required:"true"`
	TelegramToken      string `required:"true"`
	AuthBasicUsername  string `default:"user"`
	AuthBasicPassword  string `default:"password"`
}

func Load() (*Config, error) {

	cfg := new(Config)

	err := envconfig.Process(SERVICENAME, cfg)
	if err != nil {
		return nil, err
	}

	return cfg, nil
}
<|endoftext|>


<commit_before>package piecepicker

import (
	"sort"

	"github.com/cenkalti/rain/internal/logger"
	"github.com/cenkalti/rain/internal/peer"
	"github.com/cenkalti/rain/internal/piece"
)

type PiecePicker struct {
	pieces                           []myPiece
	sortedPieces                     []*myPiece
	endgameParallelDownloadsPerPiece int
	available                        uint32
	log                              logger.Logger
}

type myPiece struct {
	*piece.Piece
	Having      peerSet
	AllowedFast peerSet
	Requested   peerSet
	Snubbed     peerSet
}

func (p *myPiece) RunningDownloads() int {
	return p.Requested.Len() - p.Snubbed.Len()
}

func New(pieces []piece.Piece, endgameParallelDownloadsPerPiece int, l logger.Logger) *PiecePicker {
	ps := make([]myPiece, len(pieces))
	for i := range pieces {
		ps[i] = myPiece{Piece: &pieces[i]}
	}
	sps := make([]*myPiece, len(ps))
	for i := range sps {
		sps[i] = &ps[i]
	}
	return &PiecePicker{
		pieces:                           ps,
		sortedPieces:                     sps,
		endgameParallelDownloadsPerPiece: endgameParallelDownloadsPerPiece,
		log:                              l,
	}
}

func (p *PiecePicker) Available() uint32 {
	return p.available
}

func (p *PiecePicker) RequestedPeers(i uint32) []*peer.Peer {
	return p.pieces[i].Requested.Peers
}

func (p *PiecePicker) HandleHave(pe *peer.Peer, i uint32) {
	pe.Bitfield.Set(i)
	p.addHavingPeer(i, pe)
}

func (p *PiecePicker) HandleAllowedFast(pe *peer.Peer, i uint32) {
	p.pieces[i].AllowedFast.Add(pe)
}

func (p *PiecePicker) HandleSnubbed(pe *peer.Peer, i uint32) {
	p.pieces[i].Snubbed.Add(pe)
}

func (p *PiecePicker) HandleCancelDownload(pe *peer.Peer, i uint32) {
	p.pieces[i].Requested.Remove(pe)
	p.pieces[i].Snubbed.Remove(pe)
}

func (p *PiecePicker) HandleDisconnect(pe *peer.Peer) {
	for i := range p.pieces {
		p.HandleCancelDownload(pe, uint32(i))
		p.pieces[i].AllowedFast.Remove(pe)
		p.removeHavingPeer(i, pe)
	}
}

func (p *PiecePicker) addHavingPeer(i uint32, pe *peer.Peer) {
	ok := p.pieces[i].Having.Add(pe)
	if ok && p.pieces[i].Having.Len() == 1 {
		p.available++
	}
}

func (p *PiecePicker) removeHavingPeer(i int, pe *peer.Peer) {
	ok := p.pieces[i].Having.Remove(pe)
	if ok && p.pieces[i].Having.Len() == 0 {
		p.available--
	}
}

func (p *PiecePicker) Pick() (*piece.Piece, *peer.Peer) {
	pi, pe := p.findPieceAndPeer()
	if pi == nil || pe == nil {
		return nil, nil
	}
	pe.Snubbed = false
	pi.Requested.Add(pe)
	return pi.Piece, pe
}

func (p *PiecePicker) xPickFor(pe *peer.Peer) *piece.Piece {
	// TODO implement
	return nil
}

func (p *PiecePicker) findPieceAndPeer() (*myPiece, *peer.Peer) {
	sort.Slice(p.sortedPieces, func(i, j int) bool {
		return len(p.sortedPieces[i].Having.Peers) < len(p.sortedPieces[j].Having.Peers)
	})
	pe, pi := p.selectAllowedFastPiece(true, true)
	if pe != nil && pi != nil {
		return pe, pi
	}
	pe, pi = p.selectUnchokedPeer(true, true)
	if pe != nil && pi != nil {
		return pe, pi
	}
	pe, pi = p.selectSnubbedPeer(true)
	if pe != nil && pi != nil {
		return pe, pi
	}
	pe, pi = p.selectDuplicatePiece()
	if pe != nil && pi != nil {
		return pe, pi
	}
	return nil, nil
}

func (p *PiecePicker) selectAllowedFastPiece(skipSnubbed, noDuplicate bool) (*myPiece, *peer.Peer) {
	return p.selectPiece(true, skipSnubbed, noDuplicate)
}

func (p *PiecePicker) selectUnchokedPeer(skipSnubbed, noDuplicate bool) (*myPiece, *peer.Peer) {
	return p.selectPiece(false, skipSnubbed, noDuplicate)
}

func (p *PiecePicker) selectSnubbedPeer(noDuplicate bool) (*myPiece, *peer.Peer) {
	pi, pe := p.selectAllowedFastPiece(false, noDuplicate)
	if pi != nil && pe != nil {
		return pi, pe
	}
	pi, pe = p.selectUnchokedPeer(false, noDuplicate)
	if pi != nil && pe != nil {
		return pi, pe
	}
	return nil, nil
}

func (p *PiecePicker) selectDuplicatePiece() (*myPiece, *peer.Peer) {
	pe, pi := p.selectAllowedFastPiece(true, false)
	if pe != nil && pi != nil {
		return pe, pi
	}
	pe, pi = p.selectUnchokedPeer(true, false)
	if pe != nil && pi != nil {
		return pe, pi
	}
	pe, pi = p.selectSnubbedPeer(false)
	if pe != nil && pi != nil {
		return pe, pi
	}
	return nil, nil
}

func (p *PiecePicker) selectPiece(preferAllowedFast, skipSnubbed, noDuplicate bool) (*myPiece, *peer.Peer) {
	for _, pi := range p.sortedPieces {
		if pi.Done {
			continue
		}
		if pi.Writing {
			continue
		}
		if noDuplicate && pi.Requested.Len() > 0 {
			continue
		} else if pi.RunningDownloads() >= p.endgameParallelDownloadsPerPiece {
			continue
		}
		for _, pe := range pi.Having.Peers {
			if pe.Downloading {
				continue
			}
			if skipSnubbed && pe.Snubbed {
				continue
			}
			if preferAllowedFast {
				if !pi.AllowedFast.Has(pe) {
					continue
				}
			} else {
				if pe.PeerChoking {
					continue
				}
			}
			return pi, pe
		}
	}
	return nil, nil
}
<commit_msg>pick for<commit_after>package piecepicker

import (
	"sort"

	"github.com/cenkalti/rain/internal/logger"
	"github.com/cenkalti/rain/internal/peer"
	"github.com/cenkalti/rain/internal/piece"
)

type PiecePicker struct {
	pieces                           []myPiece
	sortedPieces                     []*myPiece
	endgameParallelDownloadsPerPiece int
	available                        uint32
	log                              logger.Logger
}

type myPiece struct {
	*piece.Piece
	Having      peerSet
	AllowedFast peerSet
	Requested   peerSet
	Snubbed     peerSet
	Choked      peerSet
}

func (p *myPiece) RunningDownloads() int {
	return p.Requested.Len() - p.Snubbed.Len()
}

func New(pieces []piece.Piece, endgameParallelDownloadsPerPiece int, l logger.Logger) *PiecePicker {
	ps := make([]myPiece, len(pieces))
	for i := range pieces {
		ps[i] = myPiece{Piece: &pieces[i]}
	}
	sps := make([]*myPiece, len(ps))
	for i := range sps {
		sps[i] = &ps[i]
	}
	return &PiecePicker{
		pieces:                           ps,
		sortedPieces:                     sps,
		endgameParallelDownloadsPerPiece: endgameParallelDownloadsPerPiece,
		log:                              l,
	}
}

func (p *PiecePicker) Available() uint32 {
	return p.available
}

func (p *PiecePicker) RequestedPeers(i uint32) []*peer.Peer {
	return p.pieces[i].Requested.Peers
}

func (p *PiecePicker) HandleHave(pe *peer.Peer, i uint32) {
	pe.Bitfield.Set(i)
	p.addHavingPeer(i, pe)
}

func (p *PiecePicker) HandleAllowedFast(pe *peer.Peer, i uint32) {
	p.pieces[i].AllowedFast.Add(pe)
}

func (p *PiecePicker) HandleSnubbed(pe *peer.Peer, i uint32) {
	p.pieces[i].Snubbed.Add(pe)
}

func (p *PiecePicker) HandleChoke(pe *peer.Peer, i uint32) {
	p.pieces[i].Choked.Add(pe)
}

func (p *PiecePicker) HandleUnchoke(pe *peer.Peer, i uint32) {
	p.pieces[i].Choked.Remove(pe)
}

func (p *PiecePicker) HandleCancelDownload(pe *peer.Peer, i uint32) {
	p.pieces[i].Requested.Remove(pe)
	p.pieces[i].Snubbed.Remove(pe)
}

func (p *PiecePicker) HandleDisconnect(pe *peer.Peer) {
	for i := range p.pieces {
		p.HandleCancelDownload(pe, uint32(i))
		p.pieces[i].AllowedFast.Remove(pe)
		p.removeHavingPeer(i, pe)
	}
}

func (p *PiecePicker) addHavingPeer(i uint32, pe *peer.Peer) {
	ok := p.pieces[i].Having.Add(pe)
	if ok && p.pieces[i].Having.Len() == 1 {
		p.available++
	}
}

func (p *PiecePicker) removeHavingPeer(i int, pe *peer.Peer) {
	ok := p.pieces[i].Having.Remove(pe)
	if ok && p.pieces[i].Having.Len() == 0 {
		p.available--
	}
}

func (p *PiecePicker) Pick() (*piece.Piece, *peer.Peer) {
	pi, pe := p.findPieceAndPeer(nil)
	if pi == nil || pe == nil {
		return nil, nil
	}
	pe.Snubbed = false
	pi.Requested.Add(pe)
	return pi.Piece, pe
}

func (p *PiecePicker) PickFor(cp *peer.Peer) *piece.Piece {
	pi, pe := p.findPieceAndPeer(cp)
	if pi == nil || pe == nil {
		return nil
	}
	if pe != cp {
		panic("invalid peer")
	}
	pe.Snubbed = false
	pi.Requested.Add(pe)
	return pi.Piece
}

func (p *PiecePicker) findPieceAndPeer(cp *peer.Peer) (*myPiece, *peer.Peer) {
	sort.Slice(p.sortedPieces, func(i, j int) bool {
		return len(p.sortedPieces[i].Having.Peers) < len(p.sortedPieces[j].Having.Peers)
	})
	pe, pi := p.selectAllowedFastPiece(cp, true, true)
	if pe != nil && pi != nil {
		return pe, pi
	}
	pe, pi = p.selectUnchokedPeer(cp, true, true)
	if pe != nil && pi != nil {
		return pe, pi
	}
	pe, pi = p.selectSnubbedPeer(cp, true)
	if pe != nil && pi != nil {
		return pe, pi
	}
	pe, pi = p.selectDuplicatePiece(cp)
	if pe != nil && pi != nil {
		return pe, pi
	}
	return nil, nil
}

func (p *PiecePicker) selectAllowedFastPiece(cp *peer.Peer, skipSnubbed, noDuplicate bool) (*myPiece, *peer.Peer) {
	return p.selectPiece(cp, true, skipSnubbed, noDuplicate)
}

func (p *PiecePicker) selectUnchokedPeer(cp *peer.Peer, skipSnubbed, noDuplicate bool) (*myPiece, *peer.Peer) {
	return p.selectPiece(cp, false, skipSnubbed, noDuplicate)
}

func (p *PiecePicker) selectSnubbedPeer(cp *peer.Peer, noDuplicate bool) (*myPiece, *peer.Peer) {
	pi, pe := p.selectAllowedFastPiece(cp, false, noDuplicate)
	if pi != nil && pe != nil {
		return pi, pe
	}
	pi, pe = p.selectUnchokedPeer(cp, false, noDuplicate)
	if pi != nil && pe != nil {
		return pi, pe
	}
	return nil, nil
}

func (p *PiecePicker) selectDuplicatePiece(cp *peer.Peer) (*myPiece, *peer.Peer) {
	pe, pi := p.selectAllowedFastPiece(cp, true, false)
	if pe != nil && pi != nil {
		return pe, pi
	}
	pe, pi = p.selectUnchokedPeer(cp, true, false)
	if pe != nil && pi != nil {
		return pe, pi
	}
	pe, pi = p.selectSnubbedPeer(cp, false)
	if pe != nil && pi != nil {
		return pe, pi
	}
	return nil, nil
}

func (p *PiecePicker) selectPiece(pe *peer.Peer, preferAllowedFast, skipSnubbed, noDuplicate bool) (*myPiece, *peer.Peer) {
	if pe != nil {
		if pe.Downloading {
			return nil, nil
		}
		if skipSnubbed && pe.Snubbed {
			return nil, nil
		}
		if !preferAllowedFast && pe.PeerChoking {
			return nil, nil
		}
	}
	for _, pi := range p.sortedPieces {
		if pi.Done {
			continue
		}
		if pi.Writing {
			continue
		}
		if noDuplicate && pi.Requested.Len() > 0 {
			continue
		} else if pi.RunningDownloads() >= p.endgameParallelDownloadsPerPiece {
			continue
		}
		var l []*peer.Peer
		if pe != nil {
			l = []*peer.Peer{pe}
		} else {
			l = pi.Having.Peers
		}
		for _, pe := range l {
			if pe.Downloading {
				continue
			}
			if skipSnubbed && pe.Snubbed {
				continue
			}
			if preferAllowedFast {
				if !pi.AllowedFast.Has(pe) {
					continue
				}
			} else {
				if pe.PeerChoking {
					continue
				}
			}
			return pi, pe
		}
	}
	return nil, nil
}
<|endoftext|>


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

// Package export holds the definition of the telemetry Exporter interface,
// along with some simple implementations.
// Larger more complex exporters are in sub packages of their own.
package export

import (
	"context"
	"os"
	"sync"
	"time"

	"golang.org/x/tools/internal/telemetry"
)

type Exporter interface {
	StartSpan(context.Context, *telemetry.Span)
	FinishSpan(context.Context, *telemetry.Span)

	// Log is a function that handles logging events.
	// Observers may use information in the context to decide what to do with a
	// given log event. They should return true if they choose to handle the
	Log(context.Context, telemetry.Event)

	Metric(context.Context, telemetry.MetricData)

	Flush()
}

var (
	exporterMu sync.Mutex
	exporter   = LogWriter(os.Stderr, true)
)

func AddExporters(e ...Exporter) {
	exporterMu.Lock()
	defer exporterMu.Unlock()
	exporter = Multi(append([]Exporter{exporter}, e...)...)
}

func StartSpan(ctx context.Context, span *telemetry.Span, at time.Time) {
	exporterMu.Lock()
	defer exporterMu.Unlock()
	span.Start = at
	exporter.StartSpan(ctx, span)
}

func FinishSpan(ctx context.Context, span *telemetry.Span, at time.Time) {
	exporterMu.Lock()
	defer exporterMu.Unlock()
	span.Finish = at
	exporter.FinishSpan(ctx, span)
}

func Tag(ctx context.Context, at time.Time, tags telemetry.TagList) {
	exporterMu.Lock()
	defer exporterMu.Unlock()
	// If context has a span we need to add the tags to it
	span := telemetry.GetSpan(ctx)
	if span == nil {
		return
	}
	if span.Start.IsZero() {
		// span still being created, tag it directly
		span.Tags = append(span.Tags, tags...)
		return
	}
	// span in progress, add an event to the span
	span.Events = append(span.Events, telemetry.Event{
		At:   at,
		Tags: tags,
	})
}

func Log(ctx context.Context, event telemetry.Event) {
	exporterMu.Lock()
	defer exporterMu.Unlock()
	// If context has a span we need to add the event to it
	span := telemetry.GetSpan(ctx)
	if span != nil {
		span.Events = append(span.Events, event)
	}
	// and now also hand the event of to the current observer
	exporter.Log(ctx, event)
}

func Metric(ctx context.Context, data telemetry.MetricData) {
	exporterMu.Lock()
	defer exporterMu.Unlock()
	exporter.Metric(ctx, data)
}

func Flush() {
	exporterMu.Lock()
	defer exporterMu.Unlock()
	exporter.Flush()
}
<commit_msg>internal/telemetry: remove an extraneous comment<commit_after>// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package export holds the definition of the telemetry Exporter interface,
// along with some simple implementations.
// Larger more complex exporters are in sub packages of their own.
package export

import (
	"context"
	"os"
	"sync"
	"time"

	"golang.org/x/tools/internal/telemetry"
)

type Exporter interface {
	StartSpan(context.Context, *telemetry.Span)
	FinishSpan(context.Context, *telemetry.Span)

	// Log is a function that handles logging events.
	// Observers may use information in the context to decide what to do with a
	// given log event.
	Log(context.Context, telemetry.Event)

	Metric(context.Context, telemetry.MetricData)

	Flush()
}

var (
	exporterMu sync.Mutex
	exporter   = LogWriter(os.Stderr, true)
)

func AddExporters(e ...Exporter) {
	exporterMu.Lock()
	defer exporterMu.Unlock()
	exporter = Multi(append([]Exporter{exporter}, e...)...)
}

func StartSpan(ctx context.Context, span *telemetry.Span, at time.Time) {
	exporterMu.Lock()
	defer exporterMu.Unlock()
	span.Start = at
	exporter.StartSpan(ctx, span)
}

func FinishSpan(ctx context.Context, span *telemetry.Span, at time.Time) {
	exporterMu.Lock()
	defer exporterMu.Unlock()
	span.Finish = at
	exporter.FinishSpan(ctx, span)
}

func Tag(ctx context.Context, at time.Time, tags telemetry.TagList) {
	exporterMu.Lock()
	defer exporterMu.Unlock()
	// If context has a span we need to add the tags to it
	span := telemetry.GetSpan(ctx)
	if span == nil {
		return
	}
	if span.Start.IsZero() {
		// span still being created, tag it directly
		span.Tags = append(span.Tags, tags...)
		return
	}
	// span in progress, add an event to the span
	span.Events = append(span.Events, telemetry.Event{
		At:   at,
		Tags: tags,
	})
}

func Log(ctx context.Context, event telemetry.Event) {
	exporterMu.Lock()
	defer exporterMu.Unlock()
	// If context has a span we need to add the event to it
	span := telemetry.GetSpan(ctx)
	if span != nil {
		span.Events = append(span.Events, event)
	}
	// and now also hand the event of to the current observer
	exporter.Log(ctx, event)
}

func Metric(ctx context.Context, data telemetry.MetricData) {
	exporterMu.Lock()
	defer exporterMu.Unlock()
	exporter.Metric(ctx, data)
}

func Flush() {
	exporterMu.Lock()
	defer exporterMu.Unlock()
	exporter.Flush()
}
<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2015 Marc Rohlfs
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package main

import (
    "fmt"
    "os"
    "strconv"
    "strings"
    "time"
    "encoding/csv"
    "encoding/xml"
    "io/ioutil"
    "net/http"
    "github.com/everdev/mack"
    "github.com/jimlawless/cfg"
)

const TMP_TYME_EXPORT_CSV_FILE_NAME = "tmp-tyme-export.csv"

type TimeEntry struct {
    XMLName   xml.Name `xml:"time-entry"`
    Date      string   `xml:"date-at"`
    Minutes   int      `xml:"minutes"`
    Note      string   `xml:"note"`
    ServiceId int      `xml:"service-id"`
    ProjectId int      `xml:"project-id"`
}

type Projects struct {
    XMLName xml.Name  `xml:"projects"`
    Project []Project `xml:"project"`
}

type Project struct {
    XMLName      xml.Name `xml:"project"`
    Id           int      `xml:"id"`
    Name         string   `xml:"name"`
    CustomerId   int      `xml:"customer-id"`
    CustomerName string   `xml:"customer-name"`
}

type Services struct {
    XMLName xml.Name  `xml:"services"`
    Service []Service `xml:"service"`
}

type Service struct {
    Id   int    `xml:"id"`
    Name string `xml:"name"`
}

func main() {

    var startDate string
    if len(os.Args) >= 2 {
        startDate = os.Args[1]
    } else {
        startDate = time.Now().AddDate(0, 0, -1).Format("2006-01-02")
    }

    var endDate string
    if len(os.Args) >= 3 {
        endDate = os.Args[2]
    } else {
        endDate = time.Now().Format("2006-01-02")
    }

    config, err := cfg.LoadNewMap(os.Getenv("HOME") + "/tyme2mite.cfg")
    checkErr(err)
    miteImportActive := strings.ToLower(config["mite_import_active"]) == "true"
    miteBaseUrl := string(config["mite_base_url"])
    miteApiKey := string(config["mite_api_key"])
    miteRequestUrlPattern := miteBaseUrl + "/[RESOURCE_ID]?api_key=" + miteApiKey
    miteTimeEntriesUrl := strings.Replace(miteRequestUrlPattern, "[RESOURCE_ID]", "time_entries.xml", 1)
    miteProjectsUrl := strings.Replace(miteRequestUrlPattern, "[RESOURCE_ID]", "projects.xml", 1)
    miteServicesUrl := strings.Replace(miteRequestUrlPattern, "[RESOURCE_ID]", "services.xml", 1)

    if miteImportActive == false {
        fmt.Print("[DRY RUN] ")
    }
    fmt.Println("Transferring time entries from " + startDate + " to " + endDate + " ...")

    projects := Projects{}
    res, err := http.Get(miteProjectsUrl)
    checkErr(err)
    xmlBody, err := ioutil.ReadAll(res.Body)
    checkErr(err)
    xml.Unmarshal(xmlBody, &projects)
    res.Body.Close()

    services := Services{}
    resSrv, err := http.Get(miteServicesUrl)
    checkErr(err)
    xmlBodySrv, err := ioutil.ReadAll(resSrv.Body)
    checkErr(err)
    xml.Unmarshal(xmlBodySrv, &services)
    resSrv.Body.Close()

    err = mack.Tell("Tyme",
        "set ex to make new export",
        "set startDate of ex to (date \"" + startDate + "\")",
        "set endDate of ex to (date \"" + endDate + "\")",
        "set exportFormat of ex to csv",
        "set exportFileName of ex to \"" + TMP_TYME_EXPORT_CSV_FILE_NAME + "\"",
        "save export ex")
    checkErr(err)

    tmpTymeExportCsvFilePath := os.Getenv("HOME") + "/Downloads/" + TMP_TYME_EXPORT_CSV_FILE_NAME
    csvfile, err := os.Open(tmpTymeExportCsvFilePath)
    checkErr(err)

    defer csvfile.Close()
    os.Remove(tmpTymeExportCsvFilePath)

    csvReader := csv.NewReader(csvfile)
    checkErr(err)

    csvReader.Comma = ';'
    csvReader.FieldsPerRecord = -1

    csvColHeaders, err := csvReader.Read()
    checkErr(err)

    assert("Date", csvColHeaders[0])
    assert("Project", csvColHeaders[1])
    assert("Task", csvColHeaders[2])
    assert("Duration", csvColHeaders[6])
    assert("Notes", csvColHeaders[9])

    rawCSVdata, err := csvReader.ReadAll()
    checkErr(err)

    var timeEntries []TimeEntry
    for _, each := range rawCSVdata {

        date := each[0]

        duration := strings.Split(each[6], ":")
        hours, err := strconv.Atoi(duration[0])
        checkErr(err)
        minutes, err := strconv.Atoi(duration[1])
        checkErr(err)
        minutes = hours * 60 + minutes

        var projectId int
        customerProject := strings.Split(each[1], "|")
        customer := strings.TrimSpace(customerProject[0])
        project := strings.TrimSpace(customerProject[1])
        for idx := 0; idx < len(projects.Project); idx++ {
            if customer == projects.Project[idx].CustomerName && project == projects.Project[idx].Name {
                projectId = projects.Project[idx].Id
                break
            }
        }

        var notePrefix string
        var noteText string
        var service string
        taskService := strings.Split(each[2], "|")
        if len(taskService) > 1 {
            notePrefix = strings.TrimSpace(taskService[0]) + ": "
            noteText = each[9]
            service = strings.TrimSpace(taskService[1])
        } else {
            notePrefix = ""
            noteText = each[9]
            service = strings.TrimSpace(taskService[0])
        }

        var serviceId int
        for idx := 0; idx < len(services.Service); idx++ {
            if service == services.Service[idx].Name {
                serviceId = services.Service[idx].Id
                break
            }
        }

        cumulateTimeEntryIndex := -1
        for idx := 0; idx < len(timeEntries); idx++ {
            if timeEntries[idx].Date == date && timeEntries[idx].ProjectId == projectId && timeEntries[idx].ServiceId == serviceId {
                if len(notePrefix) == 0 || strings.HasPrefix(timeEntries[idx].Note, notePrefix) {
                    cumulateTimeEntryIndex = idx
                    break;
                }
            }
        }

        if cumulateTimeEntryIndex == -1 {
            var timeEntry TimeEntry
            timeEntry.Date = date
            timeEntry.Minutes = minutes
            timeEntry.Note = notePrefix + noteText
            timeEntry.ProjectId = projectId
            timeEntry.ServiceId = serviceId
            timeEntries = append(timeEntries, timeEntry)
        } else {
            timeEntries[cumulateTimeEntryIndex].Minutes += minutes
            timeEntries[cumulateTimeEntryIndex].Note = timeEntries[cumulateTimeEntryIndex].Note + ", " + noteText
        }
    }

    for idx := 0; idx < len(timeEntries); idx++ {

        xmlBody, err := xml.MarshalIndent(timeEntries[idx], "", "  ")
        checkErr(err)

        var xmlString = string(xmlBody)
        fmt.Println(xmlString)

        if miteImportActive {
            res, err := http.Post(miteTimeEntriesUrl, "application/xml", strings.NewReader(string(xmlBody)))
            checkErr(err)
            fmt.Print("Import result: ")
            fmt.Println(res)
        }

        fmt.Println()
    }

    if miteImportActive == false {
        fmt.Print("[DRY RUN] ")
    }
    fmt.Println("Transferred time entries from " + startDate + " to " + endDate)
}

func assert(expected string, actual string) {
    if expected != actual {
        panic("Unexpected column: " + actual)
    }
}

func checkErr(err error) {
    if err != nil {
        panic(err)
    }
}
<commit_msg>Leading and trailing whitespaces of customer and project names should be ignored when mapping them between Tyme and mite.<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2015 Marc Rohlfs
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package main

import (
    "fmt"
    "os"
    "strconv"
    "strings"
    "time"
    "encoding/csv"
    "encoding/xml"
    "io/ioutil"
    "net/http"
    "github.com/everdev/mack"
    "github.com/jimlawless/cfg"
)

const TMP_TYME_EXPORT_CSV_FILE_NAME = "tmp-tyme-export.csv"

type TimeEntry struct {
    XMLName   xml.Name `xml:"time-entry"`
    Date      string   `xml:"date-at"`
    Minutes   int      `xml:"minutes"`
    Note      string   `xml:"note"`
    ServiceId int      `xml:"service-id"`
    ProjectId int      `xml:"project-id"`
}

type Projects struct {
    XMLName xml.Name  `xml:"projects"`
    Project []Project `xml:"project"`
}

type Project struct {
    XMLName      xml.Name `xml:"project"`
    Id           int      `xml:"id"`
    Name         string   `xml:"name"`
    CustomerId   int      `xml:"customer-id"`
    CustomerName string   `xml:"customer-name"`
}

type Services struct {
    XMLName xml.Name  `xml:"services"`
    Service []Service `xml:"service"`
}

type Service struct {
    Id   int    `xml:"id"`
    Name string `xml:"name"`
}

func main() {

    var startDate string
    if len(os.Args) >= 2 {
        startDate = os.Args[1]
    } else {
        startDate = time.Now().AddDate(0, 0, -1).Format("2006-01-02")
    }

    var endDate string
    if len(os.Args) >= 3 {
        endDate = os.Args[2]
    } else {
        endDate = time.Now().Format("2006-01-02")
    }

    config, err := cfg.LoadNewMap(os.Getenv("HOME") + "/tyme2mite.cfg")
    checkErr(err)
    miteImportActive := strings.ToLower(config["mite_import_active"]) == "true"
    miteBaseUrl := string(config["mite_base_url"])
    miteApiKey := string(config["mite_api_key"])
    miteRequestUrlPattern := miteBaseUrl + "/[RESOURCE_ID]?api_key=" + miteApiKey
    miteTimeEntriesUrl := strings.Replace(miteRequestUrlPattern, "[RESOURCE_ID]", "time_entries.xml", 1)
    miteProjectsUrl := strings.Replace(miteRequestUrlPattern, "[RESOURCE_ID]", "projects.xml", 1)
    miteServicesUrl := strings.Replace(miteRequestUrlPattern, "[RESOURCE_ID]", "services.xml", 1)

    if miteImportActive == false {
        fmt.Print("[DRY RUN] ")
    }
    fmt.Println("Transferring time entries from " + startDate + " to " + endDate + " ...")

    projects := Projects{}
    res, err := http.Get(miteProjectsUrl)
    checkErr(err)
    xmlBody, err := ioutil.ReadAll(res.Body)
    checkErr(err)
    xml.Unmarshal(xmlBody, &projects)
    res.Body.Close()

    services := Services{}
    resSrv, err := http.Get(miteServicesUrl)
    checkErr(err)
    xmlBodySrv, err := ioutil.ReadAll(resSrv.Body)
    checkErr(err)
    xml.Unmarshal(xmlBodySrv, &services)
    resSrv.Body.Close()

    err = mack.Tell("Tyme",
        "set ex to make new export",
        "set startDate of ex to (date \"" + startDate + "\")",
        "set endDate of ex to (date \"" + endDate + "\")",
        "set exportFormat of ex to csv",
        "set exportFileName of ex to \"" + TMP_TYME_EXPORT_CSV_FILE_NAME + "\"",
        "save export ex")
    checkErr(err)

    tmpTymeExportCsvFilePath := os.Getenv("HOME") + "/Downloads/" + TMP_TYME_EXPORT_CSV_FILE_NAME
    csvfile, err := os.Open(tmpTymeExportCsvFilePath)
    checkErr(err)

    defer csvfile.Close()
    os.Remove(tmpTymeExportCsvFilePath)

    csvReader := csv.NewReader(csvfile)
    checkErr(err)

    csvReader.Comma = ';'
    csvReader.FieldsPerRecord = -1

    csvColHeaders, err := csvReader.Read()
    checkErr(err)

    assert("Date", csvColHeaders[0])
    assert("Project", csvColHeaders[1])
    assert("Task", csvColHeaders[2])
    assert("Duration", csvColHeaders[6])
    assert("Notes", csvColHeaders[9])

    rawCSVdata, err := csvReader.ReadAll()
    checkErr(err)

    var timeEntries []TimeEntry
    for _, each := range rawCSVdata {

        date := each[0]

        duration := strings.Split(each[6], ":")
        hours, err := strconv.Atoi(duration[0])
        checkErr(err)
        minutes, err := strconv.Atoi(duration[1])
        checkErr(err)
        minutes = hours * 60 + minutes

        var projectId int
        customerProject := strings.Split(each[1], "|")
        customerTyme := strings.TrimSpace(customerProject[0])
        projectTyme := strings.TrimSpace(customerProject[1])
        for idx := 0; idx < len(projects.Project); idx++ {
            projectMite := strings.TrimSpace(projects.Project[idx].Name)
            customerMite := strings.TrimSpace(projects.Project[idx].CustomerName)
            if customerTyme == customerMite && projectTyme == projectMite {
                projectId = projects.Project[idx].Id
                break
            }
        }

        var notePrefix string
        var noteText string
        var service string
        taskService := strings.Split(each[2], "|")
        if len(taskService) > 1 {
            notePrefix = strings.TrimSpace(taskService[0]) + ": "
            noteText = each[9]
            service = strings.TrimSpace(taskService[1])
        } else {
            notePrefix = ""
            noteText = each[9]
            service = strings.TrimSpace(taskService[0])
        }

        var serviceId int
        for idx := 0; idx < len(services.Service); idx++ {
            if service == services.Service[idx].Name {
                serviceId = services.Service[idx].Id
                break
            }
        }

        cumulateTimeEntryIndex := -1
        for idx := 0; idx < len(timeEntries); idx++ {
            if timeEntries[idx].Date == date && timeEntries[idx].ProjectId == projectId && timeEntries[idx].ServiceId == serviceId {
                if len(notePrefix) == 0 || strings.HasPrefix(timeEntries[idx].Note, notePrefix) {
                    cumulateTimeEntryIndex = idx
                    break;
                }
            }
        }

        if cumulateTimeEntryIndex == -1 {
            var timeEntry TimeEntry
            timeEntry.Date = date
            timeEntry.Minutes = minutes
            timeEntry.Note = notePrefix + noteText
            timeEntry.ProjectId = projectId
            timeEntry.ServiceId = serviceId
            timeEntries = append(timeEntries, timeEntry)
        } else {
            timeEntries[cumulateTimeEntryIndex].Minutes += minutes
            timeEntries[cumulateTimeEntryIndex].Note = timeEntries[cumulateTimeEntryIndex].Note + ", " + noteText
        }
    }

    for idx := 0; idx < len(timeEntries); idx++ {

        xmlBody, err := xml.MarshalIndent(timeEntries[idx], "", "  ")
        checkErr(err)

        var xmlString = string(xmlBody)
        fmt.Println(xmlString)

        if miteImportActive {
            res, err := http.Post(miteTimeEntriesUrl, "application/xml", strings.NewReader(string(xmlBody)))
            checkErr(err)
            fmt.Print("Import result: ")
            fmt.Println(res)
        }

        fmt.Println()
    }

    if miteImportActive == false {
        fmt.Print("[DRY RUN] ")
    }
    fmt.Println("Transferred time entries from " + startDate + " to " + endDate)
}

func assert(expected string, actual string) {
    if expected != actual {
        panic("Unexpected column: " + actual)
    }
}

func checkErr(err error) {
    if err != nil {
        panic(err)
    }
}
<|endoftext|>


<commit_before>//
// Copyright © 2011-2013 Guy M. Allard
//
// 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 stompngo

import (
	"log"
	"time"
)

// Exported Connection methods

/*
	Connected returns the current connection status.
*/
func (c *Connection) Connected() bool {
	return c.connected
}

/*
	Session returns the broker assigned session id.
*/
func (c *Connection) Session() string {
	return c.session
}

/*
	Protocol returns the current connection protocol level.
*/
func (c *Connection) Protocol() string {
	return c.protocol
}

/*
	SetLogger enables a client defined logger for this connection.

	Set to "nil" to disable logging.

	Example:
		// Start logging
		l := log.New(os.Stdout, "", log.Ldate|log.Lmicroseconds)
		c.SetLogger(l)
*/
func (c *Connection) SetLogger(l *log.Logger) {
	c.logger = l
}

/*
	SendTickerInterval returns any heartbeat send ticker interval in ms.  A return
	value of zero means	no heartbeats are being sent.
*/
func (c *Connection) SendTickerInterval() int64 {
	if c.hbd == nil {
		return 0
	}
	return c.hbd.sti / 1000000
}

/*
	ReceiveTickerInterval returns any heartbeat receive ticker interval in ms.
	A return value of zero means no heartbeats are being received.
*/
func (c *Connection) ReceiveTickerInterval() int64 {
	if c.hbd == nil {
		return 0
	}
	return c.hbd.rti / 1000000
}

/*
	SendTickerCount returns any heartbeat send ticker count.  A return value of
	zero usually indicates no send heartbeats are enabled.
*/
func (c *Connection) SendTickerCount() int64 {
	if c.hbd == nil {
		return 0
	}
	return c.hbd.sc
}

/*
	ReceiveTickerCount returns any heartbeat receive ticker count. A return
	value of zero usually indicates no read heartbeats are enabled.
*/
func (c *Connection) ReceiveTickerCount() int64 {
	if c.hbd == nil {
		return 0
	}
	return c.hbd.rc
}

// Package exported functions

/*
	Supported checks if a particular STOMP version is supported in the current
	implementation.
*/
func Supported(v string) bool {
	return hasValue(supported, v)
}

/*
	Protocols returns a slice of client supported protocol levels.
*/
func Protocols() []string {
	return supported
}

// Unexported Connection methods

/*
	Log data if possible.
*/
func (c *Connection) log(v ...interface{}) {
	if c.logger == nil {
		return
	}
	c.logger.Print(c.session, v)
	return
}

/*
	Shutdown logic.
*/
func (c *Connection) shutdown() {
	// Shutdown heartbeats if necessary
	if c.hbd != nil {
		if c.hbd.hbs {
			c.hbd.ssd <- true
		}
		if c.hbd.hbr {
			c.hbd.rsd <- true
		}
	}
	// Stop writer go routine
	c.wsd <- true
	// We are not connected
	c.connected = false
	return
}

/*
	Read error handler.
*/
func (c *Connection) handleReadError(md MessageData) {
	// Notify any general subscriber of error
	c.input <- md
	// Notify all individual subscribers of error
	c.subsLock.Lock()
	for key := range c.subs {
		c.subs[key] <- md
	}
	c.subsLock.Unlock()
	// Let further shutdown logic proceed normally.
	return
}

/*
	Frames Read Count.
*/
func (c *Connection) FramesRead() int64 {
	return c.mets.tfr
}

/*
	Frames Written Count.
*/
func (c *Connection) FramesWritten() int64 {
	return c.mets.tfw
}

/*
	Duration since client start.
*/
func (c *Connection) Running() time.Duration {
	return time.Since(c.mets.st)
}
<commit_msg>Correct exported methods location.<commit_after>//
// Copyright © 2011-2013 Guy M. Allard
//
// 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 stompngo

import (
	"log"
	"time"
)

// Exported Connection methods

/*
	Connected returns the current connection status.
*/
func (c *Connection) Connected() bool {
	return c.connected
}

/*
	Session returns the broker assigned session id.
*/
func (c *Connection) Session() string {
	return c.session
}

/*
	Protocol returns the current connection protocol level.
*/
func (c *Connection) Protocol() string {
	return c.protocol
}

/*
	SetLogger enables a client defined logger for this connection.

	Set to "nil" to disable logging.

	Example:
		// Start logging
		l := log.New(os.Stdout, "", log.Ldate|log.Lmicroseconds)
		c.SetLogger(l)
*/
func (c *Connection) SetLogger(l *log.Logger) {
	c.logger = l
}

/*
	SendTickerInterval returns any heartbeat send ticker interval in ms.  A return
	value of zero means	no heartbeats are being sent.
*/
func (c *Connection) SendTickerInterval() int64 {
	if c.hbd == nil {
		return 0
	}
	return c.hbd.sti / 1000000
}

/*
	ReceiveTickerInterval returns any heartbeat receive ticker interval in ms.
	A return value of zero means no heartbeats are being received.
*/
func (c *Connection) ReceiveTickerInterval() int64 {
	if c.hbd == nil {
		return 0
	}
	return c.hbd.rti / 1000000
}

/*
	SendTickerCount returns any heartbeat send ticker count.  A return value of
	zero usually indicates no send heartbeats are enabled.
*/
func (c *Connection) SendTickerCount() int64 {
	if c.hbd == nil {
		return 0
	}
	return c.hbd.sc
}

/*
	ReceiveTickerCount returns any heartbeat receive ticker count. A return
	value of zero usually indicates no read heartbeats are enabled.
*/
func (c *Connection) ReceiveTickerCount() int64 {
	if c.hbd == nil {
		return 0
	}
	return c.hbd.rc
}

// Package exported functions

/*
	Supported checks if a particular STOMP version is supported in the current
	implementation.
*/
func Supported(v string) bool {
	return hasValue(supported, v)
}

/*
	Protocols returns a slice of client supported protocol levels.
*/
func Protocols() []string {
	return supported
}

/*
	Frames Read Count.
*/
func (c *Connection) FramesRead() int64 {
	return c.mets.tfr
}

/*
	Frames Written Count.
*/
func (c *Connection) FramesWritten() int64 {
	return c.mets.tfw
}

/*
	Duration since client start.
*/
func (c *Connection) Running() time.Duration {
	return time.Since(c.mets.st)
}

// Unexported Connection methods

/*
	Log data if possible.
*/
func (c *Connection) log(v ...interface{}) {
	if c.logger == nil {
		return
	}
	c.logger.Print(c.session, v)
	return
}

/*
	Shutdown logic.
*/
func (c *Connection) shutdown() {
	// Shutdown heartbeats if necessary
	if c.hbd != nil {
		if c.hbd.hbs {
			c.hbd.ssd <- true
		}
		if c.hbd.hbr {
			c.hbd.rsd <- true
		}
	}
	// Stop writer go routine
	c.wsd <- true
	// We are not connected
	c.connected = false
	return
}

/*
	Read error handler.
*/
func (c *Connection) handleReadError(md MessageData) {
	// Notify any general subscriber of error
	c.input <- md
	// Notify all individual subscribers of error
	c.subsLock.Lock()
	for key := range c.subs {
		c.subs[key] <- md
	}
	c.subsLock.Unlock()
	// Let further shutdown logic proceed normally.
	return
}
<|endoftext|>


<commit_before>package gremgo

import (
	"fmt"
	"net/http"
	"time"

	"github.com/gorilla/websocket"
	"sync"
)

type dialer interface {
	connect() error
	isConnected() bool
	isDisposed() bool
	write([]byte) error
	read() ([]byte, error)
	close() error
	getAuth() *auth
	ping(errs chan error)
}

/////
/*
WebSocket Connection
*/
/////

// Ws is the dialer for a WebSocket connection
type Ws struct {
	host         string
	conn         *websocket.Conn
	auth         *auth
	disposed     bool
	connected    bool
	pingInterval time.Duration
	writingWait  time.Duration
	readingWait  time.Duration
	timeout      time.Duration
	quit         chan struct{}
	sync.RWMutex
}

//Auth is the container for authentication data of dialer
type auth struct {
	username string
	password string
}

func (ws *Ws) connect() (err error) {
	d := websocket.Dialer{
		WriteBufferSize:  524288,
		ReadBufferSize:   524288,
		HandshakeTimeout: 5 * time.Second, // Timeout or else we'll hang forever and never fail on bad hosts.
	}
	ws.conn, _, err = d.Dial(ws.host, http.Header{})
	if err != nil {

		// As of 3.2.2 the URL has changed.
		// https://groups.google.com/forum/#!msg/gremlin-users/x4hiHsmTsHM/Xe4GcPtRCAAJ
		ws.host = ws.host + "/gremlin"
		ws.conn, _, err = d.Dial(ws.host, http.Header{})
	}

	if err == nil {
		ws.connected = true
		ws.conn.SetPongHandler(func(appData string) error {
			ws.Lock()
			ws.connected = true
			ws.Unlock()
			return nil
		})
	}
	return
}

func (ws *Ws) isConnected() bool {
	return ws.connected
}

func (ws *Ws) isDisposed() bool {
	return ws.disposed
}

func (ws *Ws) write(msg []byte) (err error) {
	err = ws.conn.WriteMessage(2, msg)
	return
}

func (ws *Ws) read() (msg []byte, err error) {
	_, msg, err = ws.conn.ReadMessage()
	return
}

func (ws *Ws) close() (err error) {
	defer func() {
		close(ws.quit)
		ws.conn.Close()
		ws.disposed = true
	}()

	err = ws.conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) //Cleanly close the connection with the server
	return
}

func (ws *Ws) getAuth() *auth {
	if ws.auth == nil {
		panic("You must create a Secure Dialer for authenticate with the server")
	}
	return ws.auth
}

func (ws *Ws) ping(errs chan error) {
	ticker := time.NewTicker(ws.pingInterval)
	defer ticker.Stop()
	for {
		select {
		case <-ticker.C:
			connected := true
			if err := ws.conn.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(ws.writingWait)); err != nil {
				errs <- err
				connected = false
			}
			ws.Lock()
			ws.connected = connected
			ws.Unlock()

		case <-ws.quit:
			return
		}
	}
}

/////

func (c *Client) writeWorker(errs chan error, quit chan struct{}) { // writeWorker works on a loop and dispatches messages as soon as it recieves them
	for {
		select {
		case msg := <-c.requests:
			err := c.conn.write(msg)
			if err != nil {
				errs <- err
				c.Errored = true
				break
			}

		case <-quit:
			return
		}
	}
}

func (c *Client) readWorker(errs chan error, quit chan struct{}) { // readWorker works on a loop and sorts messages as soon as it recieves them
	for {
		msg, err := c.conn.read()
		if err != nil {
			errs <- err
			c.Errored = true
			break
		}
		if msg != nil {
			err = c.handleResponse(msg)
			fmt.Println("Msg not null")
			fmt.Println("Msg not null err: ", err)
		}
		if err != nil {
			errs <- err
			c.Errored = true
			break
		}

		select {
		case <-quit:
			return
		default:
			continue
		}
	}
}
<commit_msg>removing debug statements<commit_after>package gremgo

import (
	"net/http"
	"time"

	"github.com/gorilla/websocket"
	"sync"
)

type dialer interface {
	connect() error
	isConnected() bool
	isDisposed() bool
	write([]byte) error
	read() ([]byte, error)
	close() error
	getAuth() *auth
	ping(errs chan error)
}

/////
/*
WebSocket Connection
*/
/////

// Ws is the dialer for a WebSocket connection
type Ws struct {
	host         string
	conn         *websocket.Conn
	auth         *auth
	disposed     bool
	connected    bool
	pingInterval time.Duration
	writingWait  time.Duration
	readingWait  time.Duration
	timeout      time.Duration
	quit         chan struct{}
	sync.RWMutex
}

//Auth is the container for authentication data of dialer
type auth struct {
	username string
	password string
}

func (ws *Ws) connect() (err error) {
	d := websocket.Dialer{
		WriteBufferSize:  524288,
		ReadBufferSize:   524288,
		HandshakeTimeout: 5 * time.Second, // Timeout or else we'll hang forever and never fail on bad hosts.
	}
	ws.conn, _, err = d.Dial(ws.host, http.Header{})
	if err != nil {

		// As of 3.2.2 the URL has changed.
		// https://groups.google.com/forum/#!msg/gremlin-users/x4hiHsmTsHM/Xe4GcPtRCAAJ
		ws.host = ws.host + "/gremlin"
		ws.conn, _, err = d.Dial(ws.host, http.Header{})
	}

	if err == nil {
		ws.connected = true
		ws.conn.SetPongHandler(func(appData string) error {
			ws.Lock()
			ws.connected = true
			ws.Unlock()
			return nil
		})
	}
	return
}

func (ws *Ws) isConnected() bool {
	return ws.connected
}

func (ws *Ws) isDisposed() bool {
	return ws.disposed
}

func (ws *Ws) write(msg []byte) (err error) {
	err = ws.conn.WriteMessage(2, msg)
	return
}

func (ws *Ws) read() (msg []byte, err error) {
	_, msg, err = ws.conn.ReadMessage()
	return
}

func (ws *Ws) close() (err error) {
	defer func() {
		close(ws.quit)
		ws.conn.Close()
		ws.disposed = true
	}()

	err = ws.conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) //Cleanly close the connection with the server
	return
}

func (ws *Ws) getAuth() *auth {
	if ws.auth == nil {
		panic("You must create a Secure Dialer for authenticate with the server")
	}
	return ws.auth
}

func (ws *Ws) ping(errs chan error) {
	ticker := time.NewTicker(ws.pingInterval)
	defer ticker.Stop()
	for {
		select {
		case <-ticker.C:
			connected := true
			if err := ws.conn.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(ws.writingWait)); err != nil {
				errs <- err
				connected = false
			}
			ws.Lock()
			ws.connected = connected
			ws.Unlock()

		case <-ws.quit:
			return
		}
	}
}

/////

func (c *Client) writeWorker(errs chan error, quit chan struct{}) { // writeWorker works on a loop and dispatches messages as soon as it recieves them
	for {
		select {
		case msg := <-c.requests:
			err := c.conn.write(msg)
			if err != nil {
				errs <- err
				c.Errored = true
				break
			}

		case <-quit:
			return
		}
	}
}

func (c *Client) readWorker(errs chan error, quit chan struct{}) { // readWorker works on a loop and sorts messages as soon as it recieves them
	for {
		msg, err := c.conn.read()
		if err != nil {
			errs <- err
			c.Errored = true
			break
		}
		if msg != nil {
			err = c.handleResponse(msg)
		}
		if err != nil {
			errs <- err
			c.Errored = true
			break
		}

		select {
		case <-quit:
			return
		default:
			continue
		}
	}
}
<|endoftext|>


<commit_before>package quic

import (
	"net"
)

type Conn struct {
	Socket  *net.Conn
	Window  *Window
	Streams map[uint32]*Stream
}

func NewConnection(socket *net.Conn) (conn *Conn) {
	conn = &Conn{
		Socket:  socket,
		Window:  NewWindow(),
		Streams: make(map[uint32]*Stream),
	}
	return conn
}

func (conn *Conn) NewStream(streamID uint32) {
	conn.Streams[streamID] = NewStream(streamID, conn.Socket)
}

func (conn *Conn) ReadPacket(p Packet) {
	switch packet := p.(type) {
	case *VersionNegotiationPacket:
	case *FramePacket:
		for _, f := range packet.Frames {
			switch (*f).(type) {
			case *AckFrame:
			case *StopWaitingFrame:
			//case *CongestionFeedBackFrame:
			case *PingFrame:
				// Ack the packet containing this frame
			case *ConnectionCloseFrame:
				// close connection -> close streams -> send GoAwayFrame
			case *GoAwayFrame:
				// will not accept any frame on this connection
			case *StreamFrame, *WindowUpdateFrame, *BlockedFrame, *RstStreamFrame:
				//conn.Streams[f.StreamID].ReadFrame(f)
			}
		}
	case *FECPacket:
	case *PublicResetPacket:
		// Abrubt termination
	}
}
<commit_msg>Conn should have Transport<commit_after>package quic

import (
	"net"
)

type Conn struct {
	*Transport
	Window  *Window
	Streams map[uint32]*Stream
}

func NewConnection(rAddr *net.UDPAddr) (*Conn, error) {
	t, err := NewTransport(rAddr)
	if err != nil {
		return nil, err
	}
	return &Conn{
		Transport: t,
		Window:    NewWindow(),
		Streams:   make(map[uint32]*Stream),
	}, nil
}

func (conn *Conn) NewStream(streamID uint32) {
	conn.Streams[streamID] = NewStream(streamID, conn)
}

func (conn *Conn) WritePacket(p Packet) error {
	return conn.Send(p)
}

func (conn *Conn) ReadPacket(p Packet) {
	switch packet := p.(type) {
	case *VersionNegotiationPacket:
	case *FramePacket:
		for _, f := range packet.Frames {
			switch (*f).(type) {
			case *AckFrame:
			case *StopWaitingFrame:
			//case *CongestionFeedBackFrame:
			case *PingFrame:
				// Ack the packet containing this frame
			case *ConnectionCloseFrame:
				// close connection -> close streams -> send GoAwayFrame
			case *GoAwayFrame:
				// will not accept any frame on this connection
			case *StreamFrame, *WindowUpdateFrame, *BlockedFrame, *RstStreamFrame:
				//conn.Streams[f.StreamID].ReadFrame(f)
			}
		}
	case *FECPacket:
	case *PublicResetPacket:
		// Abrubt termination
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Nevio Vesic
// Please check out LICENSE file for more information about what you CAN and what you CANNOT do!
// Basically in short this is a free software for you to do whatever you want to do BUT copyright must be included!
// I didn't write all of this code so you could say it's yours.
// MIT License

package goesl

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"net"
	"strconv"
	"strings"
	"sync"
	"time"
)

// Main connection against ESL - Gotta add more description here
type SocketConnection struct {
	net.Conn
	err chan error
	m   chan *Message
	mtx sync.Mutex
}

// Dial - Will establish timedout dial against specified address. In this case, it will be freeswitch server
func (c *SocketConnection) Dial(network string, addr string, timeout time.Duration) (net.Conn, error) {
	return net.DialTimeout(network, addr, timeout)
}

// Send - Will send raw message to open net connection
func (c *SocketConnection) Send(cmd string) error {

	if strings.Contains(cmd, "\r\n") {
		return fmt.Errorf(EInvalidCommandProvided, cmd)
	}

	// lock mutex
	c.mtx.Lock()
	defer c.mtx.Unlock()

	_, err := io.WriteString(c, cmd)
	if err != nil {
		return err
	}

	_, err = io.WriteString(c, "\r\n\r\n")
	if err != nil {
		return err
	}

	return nil
}

// SendMany - Will loop against passed commands and return 1st error if error happens
func (c *SocketConnection) SendMany(cmds []string) error {

	for _, cmd := range cmds {
		if err := c.Send(cmd); err != nil {
			return err
		}
	}

	return nil
}

// SendEvent - Will loop against passed event headers
func (c *SocketConnection) SendEvent(eventHeaders []string) error {
	if len(eventHeaders) <= 0 {
		return fmt.Errorf(ECouldNotSendEvent, len(eventHeaders))
	}

	// lock mutex to prevent event headers from conflicting
	c.mtx.Lock()
	defer c.mtx.Unlock()

	_, err := io.WriteString(c, "sendevent ")
	if err != nil {
		return err
	}

	for _, eventHeader := range eventHeaders {
		_, err := io.WriteString(c, eventHeader)
		if err != nil {
			return err
		}

		_, err = io.WriteString(c, "\r\n")
		if err != nil {
			return err
		}

	}

	_, err = io.WriteString(c, "\r\n")
	if err != nil {
		return err
	}

	return nil
}

// Execute - Helper fuck to execute commands with its args and sync/async mode
func (c *SocketConnection) Execute(command, args string, sync bool) (m *Message, err error) {
	return c.SendMsg(map[string]string{
		"call-command":     "execute",
		"execute-app-name": command,
		"execute-app-arg":  args,
		"event-lock":       strconv.FormatBool(sync),
	}, "", "")
}

// ExecuteUUID - Helper fuck to execute uuid specific commands with its args and sync/async mode
func (c *SocketConnection) ExecuteUUID(uuid string, command string, args string, sync bool) (m *Message, err error) {
	return c.SendMsg(map[string]string{
		"call-command":     "execute",
		"execute-app-name": command,
		"execute-app-arg":  args,
		"event-lock":       strconv.FormatBool(sync),
	}, uuid, "")
}

// SendMsg - Basically this func will send message to the opened connection
func (c *SocketConnection) SendMsg(msg map[string]string, uuid, data string) (m *Message, err error) {

	b := bytes.NewBufferString("sendmsg")

	if uuid != "" {
		if strings.Contains(uuid, "\r\n") {
			return nil, fmt.Errorf(EInvalidCommandProvided, msg)
		}

		b.WriteString(" " + uuid)
	}

	b.WriteString("\n")

	for k, v := range msg {
		if strings.Contains(k, "\r\n") {
			return nil, fmt.Errorf(EInvalidCommandProvided, msg)
		}

		if v != "" {
			if strings.Contains(v, "\r\n") {
				return nil, fmt.Errorf(EInvalidCommandProvided, msg)
			}

			b.WriteString(fmt.Sprintf("%s: %s\n", k, v))
		}
	}

	b.WriteString("\n")

	if msg["content-length"] != "" && data != "" {
		b.WriteString(data)
	}

	// lock mutex
	c.mtx.Lock()
	_, err = b.WriteTo(c)
	if err != nil {
		c.mtx.Unlock()
		return nil, err
	}
	c.mtx.Unlock()

	select {
	case err := <-c.err:
		return nil, err
	case m := <-c.m:
		return m, nil
	}
}

// OriginatorAdd - Will return originator address known as net.RemoteAddr()
// This will actually be a freeswitch address
func (c *SocketConnection) OriginatorAddr() net.Addr {
	return c.RemoteAddr()
}

// ReadMessage - Will read message from channels and return them back accordingy.
// If error is received, error will be returned. If not, message will be returned back!
func (c *SocketConnection) ReadMessage() (*Message, error) {
	Debug("Waiting for connection message to be received ...")

	select {
	case err := <-c.err:
		return nil, err
	case msg := <-c.m:
		return msg, nil
	}
}

// Handle - Will handle new messages and close connection when there are no messages left to process
func (c *SocketConnection) Handle() {

	done := make(chan bool)

	go func() {
		for {
			msg, err := newMessage(bufio.NewReaderSize(c, ReadBufferSize), true)

			if err != nil {
				c.err <- err
				done <- true
				break
			}

			c.m <- msg
		}
	}()

	<-done

	// Closing the connection now as there's nothing left to do ...
	c.Close()
}

// Close - Will close down net connection and return error if error happen
func (c *SocketConnection) Close() error {
	if err := c.Conn.Close(); err != nil {
		return err
	}

	return nil
}
<commit_msg>reuse last buffer<commit_after>// Copyright 2015 Nevio Vesic
// Please check out LICENSE file for more information about what you CAN and what you CANNOT do!
// Basically in short this is a free software for you to do whatever you want to do BUT copyright must be included!
// I didn't write all of this code so you could say it's yours.
// MIT License

package goesl

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"net"
	"strconv"
	"strings"
	"sync"
	"time"
)

// Main connection against ESL - Gotta add more description here
type SocketConnection struct {
	net.Conn
	err chan error
	m   chan *Message
	mtx sync.Mutex
}

// Dial - Will establish timedout dial against specified address. In this case, it will be freeswitch server
func (c *SocketConnection) Dial(network string, addr string, timeout time.Duration) (net.Conn, error) {
	return net.DialTimeout(network, addr, timeout)
}

// Send - Will send raw message to open net connection
func (c *SocketConnection) Send(cmd string) error {

	if strings.Contains(cmd, "\r\n") {
		return fmt.Errorf(EInvalidCommandProvided, cmd)
	}

	// lock mutex
	c.mtx.Lock()
	defer c.mtx.Unlock()

	_, err := io.WriteString(c, cmd)
	if err != nil {
		return err
	}

	_, err = io.WriteString(c, "\r\n\r\n")
	if err != nil {
		return err
	}

	return nil
}

// SendMany - Will loop against passed commands and return 1st error if error happens
func (c *SocketConnection) SendMany(cmds []string) error {

	for _, cmd := range cmds {
		if err := c.Send(cmd); err != nil {
			return err
		}
	}

	return nil
}

// SendEvent - Will loop against passed event headers
func (c *SocketConnection) SendEvent(eventHeaders []string) error {
	if len(eventHeaders) <= 0 {
		return fmt.Errorf(ECouldNotSendEvent, len(eventHeaders))
	}

	// lock mutex to prevent event headers from conflicting
	c.mtx.Lock()
	defer c.mtx.Unlock()

	_, err := io.WriteString(c, "sendevent ")
	if err != nil {
		return err
	}

	for _, eventHeader := range eventHeaders {
		_, err := io.WriteString(c, eventHeader)
		if err != nil {
			return err
		}

		_, err = io.WriteString(c, "\r\n")
		if err != nil {
			return err
		}

	}

	_, err = io.WriteString(c, "\r\n")
	if err != nil {
		return err
	}

	return nil
}

// Execute - Helper fuck to execute commands with its args and sync/async mode
func (c *SocketConnection) Execute(command, args string, sync bool) (m *Message, err error) {
	return c.SendMsg(map[string]string{
		"call-command":     "execute",
		"execute-app-name": command,
		"execute-app-arg":  args,
		"event-lock":       strconv.FormatBool(sync),
	}, "", "")
}

// ExecuteUUID - Helper fuck to execute uuid specific commands with its args and sync/async mode
func (c *SocketConnection) ExecuteUUID(uuid string, command string, args string, sync bool) (m *Message, err error) {
	return c.SendMsg(map[string]string{
		"call-command":     "execute",
		"execute-app-name": command,
		"execute-app-arg":  args,
		"event-lock":       strconv.FormatBool(sync),
	}, uuid, "")
}

// SendMsg - Basically this func will send message to the opened connection
func (c *SocketConnection) SendMsg(msg map[string]string, uuid, data string) (m *Message, err error) {

	b := bytes.NewBufferString("sendmsg")

	if uuid != "" {
		if strings.Contains(uuid, "\r\n") {
			return nil, fmt.Errorf(EInvalidCommandProvided, msg)
		}

		b.WriteString(" " + uuid)
	}

	b.WriteString("\n")

	for k, v := range msg {
		if strings.Contains(k, "\r\n") {
			return nil, fmt.Errorf(EInvalidCommandProvided, msg)
		}

		if v != "" {
			if strings.Contains(v, "\r\n") {
				return nil, fmt.Errorf(EInvalidCommandProvided, msg)
			}

			b.WriteString(fmt.Sprintf("%s: %s\n", k, v))
		}
	}

	b.WriteString("\n")

	if msg["content-length"] != "" && data != "" {
		b.WriteString(data)
	}

	// lock mutex
	c.mtx.Lock()
	_, err = b.WriteTo(c)
	if err != nil {
		c.mtx.Unlock()
		return nil, err
	}
	c.mtx.Unlock()

	select {
	case err := <-c.err:
		return nil, err
	case m := <-c.m:
		return m, nil
	}
}

// OriginatorAdd - Will return originator address known as net.RemoteAddr()
// This will actually be a freeswitch address
func (c *SocketConnection) OriginatorAddr() net.Addr {
	return c.RemoteAddr()
}

// ReadMessage - Will read message from channels and return them back accordingy.
// If error is received, error will be returned. If not, message will be returned back!
func (c *SocketConnection) ReadMessage() (*Message, error) {
	Debug("Waiting for connection message to be received ...")

	select {
	case err := <-c.err:
		return nil, err
	case msg := <-c.m:
		return msg, nil
	}
}

// Handle - Will handle new messages and close connection when there are no messages left to process
func (c *SocketConnection) Handle() {

	done := make(chan bool)

	rbuf := bufio.NewReaderSize(c, ReadBufferSize)

	go func() {
		for {
			msg, err := newMessage(rbuf, true)

			if err != nil {
				c.err <- err
				done <- true
				break
			}

			c.m <- msg
		}
	}()

	<-done

	// Closing the connection now as there's nothing left to do ...
	c.Close()
}

// Close - Will close down net connection and return error if error happen
func (c *SocketConnection) Close() error {
	if err := c.Conn.Close(); err != nil {
		return err
	}

	return nil
}
<|endoftext|>