<commit_before>package wrphttp

import (
	"net/http"
	"net/url"
	"time"

	"github.com/Comcast/webpa-common/logging"
	"github.com/Comcast/webpa-common/middleware"
	"github.com/Comcast/webpa-common/middleware/fanout"
	"github.com/Comcast/webpa-common/tracing"
	"github.com/Comcast/webpa-common/transport/transporthttp"
	"github.com/Comcast/webpa-common/wrp"
	"github.com/Comcast/webpa-common/xhttp"
	"github.com/go-kit/kit/endpoint"
	"github.com/go-kit/kit/log"
	gokithttp "github.com/go-kit/kit/transport/http"
)

const (
	DefaultMethod                            = "POST"
	DefaultEndpoint                          = "http://localhost:7000/api/v2/device/send"
	DefaultMaxIdleConnsPerHost               = 20
	DefaultFanoutTimeout       time.Duration = 45 * time.Second
	DefaultClientTimeout       time.Duration = 30 * time.Second
	DefaultMaxClients          int64         = 10000
	DefaultConcurrency                       = 1000
)

// FanoutOptions describe the options available for a go-kit HTTP server that does fanout via fanout.New.
type FanoutOptions struct {
	// Logger is the go-kit logger to use when creating the service fanout.  If not set, logging.DefaultLogger is used.
	Logger log.Logger `json:"-"`

	// Method is the HTTP method to use for all endpoints.  If not set, DefaultMethod is used.
	Method string `json:"method,omitempty"`

	// Endpoints are the URLs for each endpoint to fan out to.  If not set, DefaultEndpoint is used.
	Endpoints []string `json:"endpoints,omitempty"`

	// Authorization is the Basic Auth token.  There is no default for this field.
	Authorization string `json:"authorization"`

	// Transport is the http.Client transport
	Transport http.Transport `json:"transport"`

	// FanoutTimeout is the timeout for the entire fanout operation.  If not supplied, DefaultFanoutTimeout is used.
	FanoutTimeout time.Duration `json:"timeout"`

	// ClientTimeout is the http.Client Timeout.  If not set, DefaultClientTimeout is used.
	ClientTimeout time.Duration `json:"clientTimeout"`

	// MaxClients is the maximum number of concurrent clients that can be using the fanout.  This should be set to
	// something larger than the Concurrency field.
	MaxClients int64 `json:"maxClients"`

	// Concurrency is the maximum number of concurrent fanouts allowed.  This is enforced via a Concurrent middleware.
	// If this is not set, DefaultConcurrency is used.
	Concurrency int `json:"concurrency"`

	// Middleware is the extra Middleware to append, which can (and often is) empty
	Middleware []endpoint.Middleware `json:"-"`
}

func (f *FanoutOptions) logger() log.Logger {
	if f != nil && f.Logger != nil {
		return f.Logger
	}

	return logging.DefaultLogger()
}

func (f *FanoutOptions) method() string {
	if f != nil && len(f.Method) > 0 {
		return f.Method
	}

	return DefaultMethod
}

func (f *FanoutOptions) endpoints() []string {
	if f != nil && len(f.Endpoints) > 0 {
		return f.Endpoints
	}

	return []string{DefaultEndpoint}
}

func (f *FanoutOptions) authorization() string {
	if f != nil && len(f.Authorization) > 0 {
		return f.Authorization
	}

	return ""
}

func (f *FanoutOptions) urls() ([]*url.URL, error) {
	var urls []*url.URL
	for _, endpoint := range f.endpoints() {
		url, err := url.Parse(endpoint)
		if err != nil {
			return nil, err
		}

		urls = append(urls, url)
	}

	return urls, nil
}

func (f *FanoutOptions) transport() *http.Transport {
	transport := new(http.Transport)

	if f != nil {
		*transport = f.Transport
	}

	if transport.MaxIdleConnsPerHost < 1 {
		transport.MaxIdleConnsPerHost = DefaultMaxIdleConnsPerHost
	}

	return transport
}

func (f *FanoutOptions) fanoutTimeout() time.Duration {
	if f != nil && f.FanoutTimeout > 0 {
		return f.FanoutTimeout
	}

	return DefaultFanoutTimeout
}

func (f *FanoutOptions) clientTimeout() time.Duration {
	if f != nil && f.ClientTimeout > 0 {
		return f.ClientTimeout
	}

	return DefaultClientTimeout
}

func (f *FanoutOptions) maxClients() int64 {
	if f != nil && f.MaxClients > 0 {
		return f.MaxClients
	}

	return DefaultMaxClients
}

func (f *FanoutOptions) concurrency() int {
	if f != nil && f.Concurrency > 0 {
		return f.Concurrency
	}

	return DefaultConcurrency
}

func (f *FanoutOptions) middleware() []endpoint.Middleware {
	if f != nil {
		return f.Middleware
	}

	return nil
}

// NewFanoutEndpoint uses the supplied options to produce a go-kit HTTP server endpoint which
// fans out to the HTTP endpoints specified in the options.  The endpoint returned from this
// can be used to build one or more go-kit transport/http.Server objects.
//
// The FanoutOptions can be nil, in which case a set of defaults is used.
func NewFanoutEndpoint(o *FanoutOptions) (endpoint.Endpoint, error) {
	urls, err := o.urls()
	if err != nil {
		return nil, err
	}

	var (
		httpClient = &http.Client{
			CheckRedirect: xhttp.CheckRedirect(
				xhttp.RedirectPolicy{
					Logger: o.logger(),
				},
			),
			Transport: o.transport(),
			Timeout:   o.clientTimeout(),
		}

		fanoutEndpoints = make(map[string]endpoint.Endpoint, len(urls))
		customHeader    = http.Header{
			"Accept": []string{"application/msgpack"},
		}
	)

	if authorization := o.authorization(); len(authorization) > 0 {
		customHeader.Set("Authorization", "Basic "+authorization)
	}

	for _, url := range urls {
		fanoutEndpoints[url.String()] =
			gokithttp.NewClient(
				o.method(),
				url,
				ClientEncodeRequestBody(wrp.Msgpack, customHeader),
				ClientDecodeResponseBody(wrp.Msgpack),
				gokithttp.SetClient(httpClient), gokithttp.ClientBefore(transporthttp.GetBody),
			).Endpoint()
	}

	var (
		middlewareChain = append(
			[]endpoint.Middleware{
				middleware.Logging,
				middleware.Busy(o.maxClients(), &xhttp.Error{Code: http.StatusServiceUnavailable, Text: "Server Busy"}),
				middleware.Timeout(o.fanoutTimeout()),
				middleware.Concurrent(o.concurrency(), &xhttp.Error{Code: http.StatusTooManyRequests, Text: "Too Many Requests"}),
			},
			o.middleware()...,
		)
	)

	return endpoint.Chain(
			middlewareChain[0],
			middlewareChain[1:]...,
		)(fanout.New(tracing.NewSpanner(), fanoutEndpoints)),
		nil
}
<commit_msg>Fixed the GetBody usage<commit_after>package wrphttp

import (
	"net/http"
	"net/url"
	"time"

	"github.com/Comcast/webpa-common/logging"
	"github.com/Comcast/webpa-common/middleware"
	"github.com/Comcast/webpa-common/middleware/fanout"
	"github.com/Comcast/webpa-common/tracing"
	"github.com/Comcast/webpa-common/transport/transporthttp"
	"github.com/Comcast/webpa-common/wrp"
	"github.com/Comcast/webpa-common/xhttp"
	"github.com/go-kit/kit/endpoint"
	"github.com/go-kit/kit/log"
	gokithttp "github.com/go-kit/kit/transport/http"
)

const (
	DefaultMethod                            = "POST"
	DefaultEndpoint                          = "http://localhost:7000/api/v2/device/send"
	DefaultMaxIdleConnsPerHost               = 20
	DefaultFanoutTimeout       time.Duration = 45 * time.Second
	DefaultClientTimeout       time.Duration = 30 * time.Second
	DefaultMaxClients          int64         = 10000
	DefaultConcurrency                       = 1000
)

// FanoutOptions describe the options available for a go-kit HTTP server that does fanout via fanout.New.
type FanoutOptions struct {
	// Logger is the go-kit logger to use when creating the service fanout.  If not set, logging.DefaultLogger is used.
	Logger log.Logger `json:"-"`

	// Method is the HTTP method to use for all endpoints.  If not set, DefaultMethod is used.
	Method string `json:"method,omitempty"`

	// Endpoints are the URLs for each endpoint to fan out to.  If not set, DefaultEndpoint is used.
	Endpoints []string `json:"endpoints,omitempty"`

	// Authorization is the Basic Auth token.  There is no default for this field.
	Authorization string `json:"authorization"`

	// Transport is the http.Client transport
	Transport http.Transport `json:"transport"`

	// FanoutTimeout is the timeout for the entire fanout operation.  If not supplied, DefaultFanoutTimeout is used.
	FanoutTimeout time.Duration `json:"timeout"`

	// ClientTimeout is the http.Client Timeout.  If not set, DefaultClientTimeout is used.
	ClientTimeout time.Duration `json:"clientTimeout"`

	// MaxClients is the maximum number of concurrent clients that can be using the fanout.  This should be set to
	// something larger than the Concurrency field.
	MaxClients int64 `json:"maxClients"`

	// Concurrency is the maximum number of concurrent fanouts allowed.  This is enforced via a Concurrent middleware.
	// If this is not set, DefaultConcurrency is used.
	Concurrency int `json:"concurrency"`

	// Middleware is the extra Middleware to append, which can (and often is) empty
	Middleware []endpoint.Middleware `json:"-"`
}

func (f *FanoutOptions) logger() log.Logger {
	if f != nil && f.Logger != nil {
		return f.Logger
	}

	return logging.DefaultLogger()
}

func (f *FanoutOptions) method() string {
	if f != nil && len(f.Method) > 0 {
		return f.Method
	}

	return DefaultMethod
}

func (f *FanoutOptions) endpoints() []string {
	if f != nil && len(f.Endpoints) > 0 {
		return f.Endpoints
	}

	return []string{DefaultEndpoint}
}

func (f *FanoutOptions) authorization() string {
	if f != nil && len(f.Authorization) > 0 {
		return f.Authorization
	}

	return ""
}

func (f *FanoutOptions) urls() ([]*url.URL, error) {
	var urls []*url.URL
	for _, endpoint := range f.endpoints() {
		url, err := url.Parse(endpoint)
		if err != nil {
			return nil, err
		}

		urls = append(urls, url)
	}

	return urls, nil
}

func (f *FanoutOptions) transport() *http.Transport {
	transport := new(http.Transport)

	if f != nil {
		*transport = f.Transport
	}

	if transport.MaxIdleConnsPerHost < 1 {
		transport.MaxIdleConnsPerHost = DefaultMaxIdleConnsPerHost
	}

	return transport
}

func (f *FanoutOptions) fanoutTimeout() time.Duration {
	if f != nil && f.FanoutTimeout > 0 {
		return f.FanoutTimeout
	}

	return DefaultFanoutTimeout
}

func (f *FanoutOptions) clientTimeout() time.Duration {
	if f != nil && f.ClientTimeout > 0 {
		return f.ClientTimeout
	}

	return DefaultClientTimeout
}

func (f *FanoutOptions) maxClients() int64 {
	if f != nil && f.MaxClients > 0 {
		return f.MaxClients
	}

	return DefaultMaxClients
}

func (f *FanoutOptions) concurrency() int {
	if f != nil && f.Concurrency > 0 {
		return f.Concurrency
	}

	return DefaultConcurrency
}

func (f *FanoutOptions) middleware() []endpoint.Middleware {
	if f != nil {
		return f.Middleware
	}

	return nil
}

// NewFanoutEndpoint uses the supplied options to produce a go-kit HTTP server endpoint which
// fans out to the HTTP endpoints specified in the options.  The endpoint returned from this
// can be used to build one or more go-kit transport/http.Server objects.
//
// The FanoutOptions can be nil, in which case a set of defaults is used.
func NewFanoutEndpoint(o *FanoutOptions) (endpoint.Endpoint, error) {
	urls, err := o.urls()
	if err != nil {
		return nil, err
	}

	logger := o.logger()
	var (
		httpClient = &http.Client{
			CheckRedirect: xhttp.CheckRedirect(
				xhttp.RedirectPolicy{
					Logger: logger,
				},
			),
			Transport: o.transport(),
			Timeout:   o.clientTimeout(),
		}

		fanoutEndpoints = make(map[string]endpoint.Endpoint, len(urls))
		customHeader    = http.Header{
			"Accept": []string{"application/msgpack"},
		}
	)

	if authorization := o.authorization(); len(authorization) > 0 {
		customHeader.Set("Authorization", "Basic "+authorization)
	}

	for _, url := range urls {
		fanoutEndpoints[url.String()] =
			gokithttp.NewClient(
				o.method(),
				url,
				ClientEncodeRequestBody(wrp.Msgpack, customHeader),
				ClientDecodeResponseBody(wrp.Msgpack),
				gokithttp.SetClient(httpClient), gokithttp.ClientBefore(transporthttp.GetBody(logger)),
			).Endpoint()
	}

	var (
		middlewareChain = append(
			[]endpoint.Middleware{
				middleware.Logging,
				middleware.Busy(o.maxClients(), &xhttp.Error{Code: http.StatusServiceUnavailable, Text: "Server Busy"}),
				middleware.Timeout(o.fanoutTimeout()),
				middleware.Concurrent(o.concurrency(), &xhttp.Error{Code: http.StatusTooManyRequests, Text: "Too Many Requests"}),
			},
			o.middleware()...,
		)
	)

	return endpoint.Chain(
			middlewareChain[0],
			middlewareChain[1:]...,
		)(fanout.New(tracing.NewSpanner(), fanoutEndpoints)),
		nil
}
<|endoftext|>


<commit_before>package announcer

import (
	"context"
	"errors"
	"math"
	"net"
	"sync"
	"time"

	"github.com/cenkalti/backoff"
	"github.com/cenkalti/rain/internal/logger"
	"github.com/cenkalti/rain/internal/tracker"
)

type Status int

const (
	NotContactedYet Status = iota
	Contacting
	Working
	NotWorking
)

type PeriodicalAnnouncer struct {
	Tracker       tracker.Tracker
	status        Status
	statsCommandC chan statsRequest
	numWant       int
	interval      time.Duration
	minInterval   time.Duration
	seeders       int
	leechers      int
	lastError     error
	log           logger.Logger
	completedC    chan struct{}
	newPeers      chan []*net.TCPAddr
	backoff       backoff.BackOff
	getTorrent    func() tracker.Torrent
	lastAnnounce  time.Time
	HasAnnounced  bool
	responseC     chan *tracker.AnnounceResponse
	errC          chan error
	closeC        chan struct{}
	doneC         chan struct{}

	needMorePeers  bool
	mNeedMorePeers sync.RWMutex
	needMorePeersC chan struct{}
}

func NewPeriodicalAnnouncer(trk tracker.Tracker, numWant int, minInterval time.Duration, getTorrent func() tracker.Torrent, completedC chan struct{}, newPeers chan []*net.TCPAddr, l logger.Logger) *PeriodicalAnnouncer {
	return &PeriodicalAnnouncer{
		Tracker:        trk,
		status:         NotContactedYet,
		statsCommandC:  make(chan statsRequest),
		numWant:        numWant,
		minInterval:    minInterval,
		log:            l,
		completedC:     completedC,
		newPeers:       newPeers,
		getTorrent:     getTorrent,
		needMorePeersC: make(chan struct{}, 1),
		responseC:      make(chan *tracker.AnnounceResponse),
		errC:           make(chan error),
		closeC:         make(chan struct{}),
		doneC:          make(chan struct{}),
		backoff: &backoff.ExponentialBackOff{
			InitialInterval:     5 * time.Second,
			RandomizationFactor: 0.5,
			Multiplier:          2,
			MaxInterval:         30 * time.Minute,
			MaxElapsedTime:      0, // never stop
			Clock:               backoff.SystemClock,
		},
	}
}

func (a *PeriodicalAnnouncer) Close() {
	close(a.closeC)
	<-a.doneC
}

type statsRequest struct {
	Response chan Stats
}

func (a *PeriodicalAnnouncer) Stats() Stats {
	var stats Stats
	req := statsRequest{Response: make(chan Stats, 1)}
	select {
	case a.statsCommandC <- req:
	case <-a.closeC:
	}
	select {
	case stats = <-req.Response:
	case <-a.closeC:
	}
	return stats
}

func (a *PeriodicalAnnouncer) NeedMorePeers(val bool) {
	a.mNeedMorePeers.Lock()
	a.needMorePeers = val
	a.mNeedMorePeers.Unlock()
	select {
	case a.needMorePeersC <- struct{}{}:
	case <-a.doneC:
	default:
	}
}

func (a *PeriodicalAnnouncer) Run() {
	defer close(a.doneC)
	a.backoff.Reset()

	timer := time.NewTimer(math.MaxInt64)
	defer timer.Stop()

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	go a.announce(ctx, tracker.EventStarted, a.numWant)
	a.status = Contacting
	for {
		select {
		case <-timer.C:
			if a.status == Contacting {
				break
			}
			go a.announce(ctx, tracker.EventNone, a.numWant)
			a.status = Contacting
		case resp := <-a.responseC:
			a.status = Working
			a.lastAnnounce = time.Now()
			a.seeders = int(resp.Seeders)
			a.leechers = int(resp.Leechers)
			a.interval = resp.Interval
			if resp.MinInterval > 0 {
				a.minInterval = resp.MinInterval
			}
			a.HasAnnounced = true
			a.lastError = nil
			a.backoff.Reset()
			a.mNeedMorePeers.RLock()
			needMorePeers := a.needMorePeers
			a.mNeedMorePeers.RUnlock()
			if needMorePeers {
				timer.Reset(a.minInterval)
			} else {
				timer.Reset(a.interval)
			}
		case a.lastError = <-a.errC:
			a.status = NotWorking
			a.lastAnnounce = time.Now()
			if a.lastError == context.Canceled {
				a.lastError = errors.New("timeout")
			}
			a.log.Debugln("announce error:", a.lastError)
			if terr, ok := a.lastError.(*tracker.Error); ok && terr.RetryIn > 0 {
				timer.Reset(terr.RetryIn)
			} else {
				timer.Reset(a.backoff.NextBackOff())
			}
		case <-a.needMorePeersC:
			a.mNeedMorePeers.RLock()
			needMorePeers := a.needMorePeers
			a.mNeedMorePeers.RUnlock()
			if a.status == Contacting {
				break
			}
			if needMorePeers {
				timer.Reset(time.Until(a.lastAnnounce.Add(a.minInterval)))
			} else {
				timer.Reset(time.Until(a.lastAnnounce.Add(a.interval)))
			}
		case <-a.completedC:
			if a.status == Contacting {
				cancel()
			}
			go a.announce(ctx, tracker.EventCompleted, 0)
			a.status = Contacting
			a.completedC = nil // do not send more than one "completed" event
		case req := <-a.statsCommandC:
			req.Response <- a.stats()
		case <-a.closeC:
			return
		}
	}
}

func (a *PeriodicalAnnouncer) announce(ctx context.Context, event tracker.Event, numWant int) {
	announce(ctx, a.Tracker, event, numWant, a.getTorrent(), a.responseC, a.errC)
}

type Stats struct {
	Status   Status
	Error    error
	Seeders  int
	Leechers int
}

func (a *PeriodicalAnnouncer) stats() Stats {
	return Stats{
		Status:   a.status,
		Error:    a.lastError,
		Seeders:  a.seeders,
		Leechers: a.leechers,
	}
}
<commit_msg>fix timeout error<commit_after>package announcer

import (
	"context"
	"errors"
	"math"
	"net"
	"sync"
	"time"

	"github.com/cenkalti/backoff"
	"github.com/cenkalti/rain/internal/logger"
	"github.com/cenkalti/rain/internal/tracker"
)

type Status int

const (
	NotContactedYet Status = iota
	Contacting
	Working
	NotWorking
)

type PeriodicalAnnouncer struct {
	Tracker       tracker.Tracker
	status        Status
	statsCommandC chan statsRequest
	numWant       int
	interval      time.Duration
	minInterval   time.Duration
	seeders       int
	leechers      int
	lastError     error
	log           logger.Logger
	completedC    chan struct{}
	newPeers      chan []*net.TCPAddr
	backoff       backoff.BackOff
	getTorrent    func() tracker.Torrent
	lastAnnounce  time.Time
	HasAnnounced  bool
	responseC     chan *tracker.AnnounceResponse
	errC          chan error
	closeC        chan struct{}
	doneC         chan struct{}

	needMorePeers  bool
	mNeedMorePeers sync.RWMutex
	needMorePeersC chan struct{}
}

func NewPeriodicalAnnouncer(trk tracker.Tracker, numWant int, minInterval time.Duration, getTorrent func() tracker.Torrent, completedC chan struct{}, newPeers chan []*net.TCPAddr, l logger.Logger) *PeriodicalAnnouncer {
	return &PeriodicalAnnouncer{
		Tracker:        trk,
		status:         NotContactedYet,
		statsCommandC:  make(chan statsRequest),
		numWant:        numWant,
		minInterval:    minInterval,
		log:            l,
		completedC:     completedC,
		newPeers:       newPeers,
		getTorrent:     getTorrent,
		needMorePeersC: make(chan struct{}, 1),
		responseC:      make(chan *tracker.AnnounceResponse),
		errC:           make(chan error),
		closeC:         make(chan struct{}),
		doneC:          make(chan struct{}),
		backoff: &backoff.ExponentialBackOff{
			InitialInterval:     5 * time.Second,
			RandomizationFactor: 0.5,
			Multiplier:          2,
			MaxInterval:         30 * time.Minute,
			MaxElapsedTime:      0, // never stop
			Clock:               backoff.SystemClock,
		},
	}
}

func (a *PeriodicalAnnouncer) Close() {
	close(a.closeC)
	<-a.doneC
}

type statsRequest struct {
	Response chan Stats
}

func (a *PeriodicalAnnouncer) Stats() Stats {
	var stats Stats
	req := statsRequest{Response: make(chan Stats, 1)}
	select {
	case a.statsCommandC <- req:
	case <-a.closeC:
	}
	select {
	case stats = <-req.Response:
	case <-a.closeC:
	}
	return stats
}

func (a *PeriodicalAnnouncer) NeedMorePeers(val bool) {
	a.mNeedMorePeers.Lock()
	a.needMorePeers = val
	a.mNeedMorePeers.Unlock()
	select {
	case a.needMorePeersC <- struct{}{}:
	case <-a.doneC:
	default:
	}
}

func (a *PeriodicalAnnouncer) Run() {
	defer close(a.doneC)
	a.backoff.Reset()

	timer := time.NewTimer(math.MaxInt64)
	defer timer.Stop()

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	go a.announce(ctx, tracker.EventStarted, a.numWant)
	a.status = Contacting
	for {
		select {
		case <-timer.C:
			if a.status == Contacting {
				break
			}
			go a.announce(ctx, tracker.EventNone, a.numWant)
			a.status = Contacting
		case resp := <-a.responseC:
			a.status = Working
			a.lastAnnounce = time.Now()
			a.seeders = int(resp.Seeders)
			a.leechers = int(resp.Leechers)
			a.interval = resp.Interval
			if resp.MinInterval > 0 {
				a.minInterval = resp.MinInterval
			}
			a.HasAnnounced = true
			a.lastError = nil
			a.backoff.Reset()
			a.mNeedMorePeers.RLock()
			needMorePeers := a.needMorePeers
			a.mNeedMorePeers.RUnlock()
			if needMorePeers {
				timer.Reset(a.minInterval)
			} else {
				timer.Reset(a.interval)
			}
		case a.lastError = <-a.errC:
			a.status = NotWorking
			a.lastAnnounce = time.Now()
			if oerr, ok := a.lastError.(*net.OpError); ok && oerr.Error() == "operation was canceled" {
				// Give more friendly error to the user
				a.lastError = errors.New("timeout")
			}
			a.log.Debugln("announce error:", a.lastError)
			if terr, ok := a.lastError.(*tracker.Error); ok && terr.RetryIn > 0 {
				timer.Reset(terr.RetryIn)
			} else {
				timer.Reset(a.backoff.NextBackOff())
			}
		case <-a.needMorePeersC:
			a.mNeedMorePeers.RLock()
			needMorePeers := a.needMorePeers
			a.mNeedMorePeers.RUnlock()
			if a.status == Contacting {
				break
			}
			if needMorePeers {
				timer.Reset(time.Until(a.lastAnnounce.Add(a.minInterval)))
			} else {
				timer.Reset(time.Until(a.lastAnnounce.Add(a.interval)))
			}
		case <-a.completedC:
			if a.status == Contacting {
				cancel()
			}
			go a.announce(ctx, tracker.EventCompleted, 0)
			a.status = Contacting
			a.completedC = nil // do not send more than one "completed" event
		case req := <-a.statsCommandC:
			req.Response <- a.stats()
		case <-a.closeC:
			return
		}
	}
}

func (a *PeriodicalAnnouncer) announce(ctx context.Context, event tracker.Event, numWant int) {
	announce(ctx, a.Tracker, event, numWant, a.getTorrent(), a.responseC, a.errC)
}

type Stats struct {
	Status   Status
	Error    error
	Seeders  int
	Leechers int
}

func (a *PeriodicalAnnouncer) stats() Stats {
	return Stats{
		Status:   a.status,
		Error:    a.lastError,
		Seeders:  a.seeders,
		Leechers: a.leechers,
	}
}
<|endoftext|>


<commit_before>package cache

import (
	"os"
	"path/filepath"
	"strings"
)

// Check ensures that all executables in path's directory are cached.
func Check(path string) {
	resultq := make(chan bool)

	dirname, basename := filepath.Split(path)
	requestq <- func() {
		for _, p := range executables[dirname] {
			if p == basename {
				resultq <- true

				break
			}
		}
		close(resultq)
	}

	if <-resultq {
		Files(dirname)
	}
}

// Executables returns executables (and directories) in dirname and schedules a rescan or dirname.
func Executables(dirname string) []string {
	resultq := make(chan []string)

	dirname = filepath.Clean(dirname)
	requestq <- func() {
		resultq <- executables[dirname]
		close(resultq)
	}

	res := <-resultq
	if res == nil {
		go Files(dirname)
	}

	return res
}

// Files caches executables (and directories) and returns files (and directories) found in dirname.
func Files(dirname string) []string {
	dirname = filepath.Clean(dirname)

	max := strings.Count(dirname, pathSeparator) + 1

	e := []string{}
	f := []string{}

	done := make(chan struct{})

	requestq <- func() {
		if _, ok := executables[dirname]; !ok {
			delete(executables, dirname)
		}
		close(done)
	}

	<-done

	_ = filepath.Walk(dirname, func(p string, i os.FileInfo, err error) error {
		if p == dirname {
			return nil
		}

		depth := strings.Count(p, pathSeparator)
		if depth > max {
			if i.IsDir() {
				return filepath.SkipDir
			}

			return nil
		} else if depth < max {
			return nil
		}

		switch {
		case p != pathSeparator && i.IsDir():
			p += pathSeparator

			e = append(e, p)
			f = append(f, p)

		case i.Mode()&0111 != 0:
			e = append(e, p)

		default:
			f = append(f, p)
		}

		return nil
	})

	requestq <- func() {
		if len(e) > 0 {
			executables[dirname] = e
		}
	}

	return f
}

// Populate scans each directory in the colon-separated list of dirnames.
func Populate(dirnames string) {
	for _, dirname := range strings.Split(dirnames, pathListSeparator) {
		if dirname == "" {
			dirname = "."
		} else {
			dirname = filepath.Clean(dirname)
		}

		stat, err := os.Stat(dirname)
		if err != nil || !stat.IsDir() {
			continue
		}

		Files(dirname)
	}
}

//nolint:gochecknoglobals
var (
	executables       = map[string][]string{}
	pathListSeparator = string(os.PathListSeparator)
	pathSeparator     = string(os.PathSeparator)
	requestq          chan func()
)

func init() { //nolint:gochecknoinits
	requestq = make(chan func(), 1)

	go service()
}

func service() {
	for {
		(<-requestq)()
	}
}
<commit_msg>Include executable filee.<commit_after>package cache

import (
	"os"
	"path/filepath"
	"strings"
)

// Check ensures that all executables in path's directory are cached.
func Check(path string) {
	resultq := make(chan bool)

	dirname, basename := filepath.Split(path)
	requestq <- func() {
		for _, p := range executables[dirname] {
			if p == basename {
				resultq <- true

				break
			}
		}
		close(resultq)
	}

	if <-resultq {
		Files(dirname)
	}
}

// Executables returns executables (and directories) in dirname and schedules a rescan or dirname.
func Executables(dirname string) []string {
	resultq := make(chan []string)

	dirname = filepath.Clean(dirname)
	requestq <- func() {
		resultq <- executables[dirname]
		close(resultq)
	}

	res := <-resultq
	if res == nil {
		go Files(dirname)
	}

	return res
}

// Files caches executables (and directories) and returns files (and directories) found in dirname.
func Files(dirname string) []string {
	dirname = filepath.Clean(dirname)

	max := strings.Count(dirname, pathSeparator) + 1

	e := []string{}
	f := []string{}

	done := make(chan struct{})

	requestq <- func() {
		if _, ok := executables[dirname]; !ok {
			delete(executables, dirname)
		}
		close(done)
	}

	<-done

	_ = filepath.Walk(dirname, func(p string, i os.FileInfo, err error) error {
		if p == dirname {
			return nil
		}

		depth := strings.Count(p, pathSeparator)
		if depth > max {
			if i.IsDir() {
				return filepath.SkipDir
			}

			return nil
		} else if depth < max {
			return nil
		}

		switch {
		case p != pathSeparator && i.IsDir():
			p += pathSeparator
			e = append(e, p)

		case i.Mode()&0111 != 0:
			e = append(e, p)
		}

		f = append(f, p)

		return nil
	})

	requestq <- func() {
		if len(e) > 0 {
			executables[dirname] = e
		}
	}

	return f
}

// Populate scans each directory in the colon-separated list of dirnames.
func Populate(dirnames string) {
	for _, dirname := range strings.Split(dirnames, pathListSeparator) {
		if dirname == "" {
			dirname = "."
		} else {
			dirname = filepath.Clean(dirname)
		}

		stat, err := os.Stat(dirname)
		if err != nil || !stat.IsDir() {
			continue
		}

		Files(dirname)
	}
}

//nolint:gochecknoglobals
var (
	executables       = map[string][]string{}
	pathListSeparator = string(os.PathListSeparator)
	pathSeparator     = string(os.PathSeparator)
	requestq          chan func()
)

func init() { //nolint:gochecknoinits
	requestq = make(chan func(), 1)

	go service()
}

func service() {
	for {
		(<-requestq)()
	}
}
<|endoftext|>


<commit_before>package adhier

// Config represents a configuration of the algorithm.
type Config struct {
	MinLevel uint8   // The minimal level of interpolation
	MaxLevel uint8   // The maximal level of interpolation
	MaxNodes uint32  // The maximal number of nodes
	AbsError float64 // The absolute error
	RelError float64 // The relative error
	Workers  uint32  // The number of concurrent workers
}

// DefaultConfig returns the default configuration of the algorithm.
func DefaultConfig() Config {
	return Config{
		MinLevel: 1,
		MaxLevel: 9,
		MaxNodes: 10000,
		AbsError: 1e-4,
		RelError: 1e-2,
		Workers:  0,
	}
}
<commit_msg>Improved the description of Config<commit_after>package adhier

// Config represents a configuration of the algorithm.
type Config struct {
	// The minimal level of interpolation. The nodes that belong to lower levels
	// are unconditionally included in the surrogate.
	MinLevel uint8
	// The maximal level of interpolation. The nodes that belong to this level
	// are not refined, and, thus, the algorithm stops.
	MaxLevel uint8
	// The maximal number of nodes. The algorithm stops after reaching this many
	// nodes.
	MaxNodes uint32
	// The absolute error. The parameter is used for local refinement and is
	// given in absolute units.
	AbsError float64
	// The relative error. The parameter is used for local refinement and is
	// given in relative units.
	RelError float64
	// The number of concurrent workers. The evaluation of the target function
	// and the surrogate itself is distributed among this many goroutines.
	Workers uint32
}

// DefaultConfig returns the default configuration of the algorithm.
func DefaultConfig() Config {
	return Config{
		MinLevel: 1,
		MaxLevel: 9,
		MaxNodes: 10000,
		AbsError: 1e-4,
		RelError: 1e-2,
		Workers:  0,
	}
}
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"reflect"
	"strings"
	"sync"

	"github.com/fatih/camelcase"
	"github.com/golang/groupcache"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/sirupsen/logrus"
)

type cacheStats struct {
	cacheName    string
	descriptions map[string]*prometheus.Desc
	*groupcache.Stats
	mutex *sync.Mutex // For concurrent descriptions map access
}

// RegisterCacheStats creates a new wrapper for groupcache stats that implements
// the prometheus.Collector interface, and registers it
func RegisterCacheStats(cacheName string, groupCacheStats *groupcache.Stats) {
	c := &cacheStats{cacheName, make(map[string]*prometheus.Desc), groupCacheStats, &sync.Mutex{}}
	if err := prometheus.Register(c); err != nil {
		logrus.Infof("error registering prometheus metric: %v", err)
	}
}

func (c *cacheStats) Describe(ch chan<- *prometheus.Desc) {
	for _, statFieldName := range groupCacheStatFields() {
		statName := c.statName(statFieldName)
		desc := prometheus.NewDesc(
			statName,
			fmt.Sprintf("groupcache %v", statFieldName),
			[]string{},
			nil,
		)
		func() {
			c.mutex.Lock()
			defer c.mutex.Unlock()
			c.descriptions[statName] = desc
		}()
		ch <- desc
	}
}

func (c *cacheStats) Collect(ch chan<- prometheus.Metric) {
	r := reflect.ValueOf(c)
	for _, statFieldName := range groupCacheStatFields() {
		value := reflect.Indirect(r).FieldByName(statFieldName)
		func() {
			c.mutex.Lock()
			defer c.mutex.Unlock()
			metric, err := prometheus.NewConstMetric(
				c.descriptions[c.statName(statFieldName)],
				prometheus.GaugeValue,
				float64(value.Int()),
			)
			if err != nil {
				logrus.Infof("error reporting prometheus cache metric %v: %v", c.statName(statFieldName), err)
			} else {
				ch <- metric
			}
		}()
	}
}

func (c *cacheStats) statName(fieldName string) string {
	var tokens []string
	for _, token := range camelcase.Split(fieldName) {
		tokens = append(tokens, strings.ToLower(token))
	}
	groupCacheStatName := strings.Join(tokens, "_")

	return fmt.Sprintf("pachyderm_pachd_cache_%v_%v_gauge", c.cacheName, groupCacheStatName)
}

func groupCacheStatFields() (fields []string) {
	s := &groupcache.Stats{}
	e := reflect.ValueOf(s).Elem()
	t := e.Type()
	for i := 0; i < e.NumField(); i++ {
		fields = append(fields, t.Field(i).Name)
	}
	return fields
}
<commit_msg>Use RWMutex to allow concurrent reads in Collect<commit_after>package server

import (
	"fmt"
	"reflect"
	"strings"
	"sync"

	"github.com/fatih/camelcase"
	"github.com/golang/groupcache"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/sirupsen/logrus"
)

type cacheStats struct {
	cacheName    string
	descriptions map[string]*prometheus.Desc
	*groupcache.Stats
	mutex *sync.RWMutex // For concurrent descriptions map access
}

// RegisterCacheStats creates a new wrapper for groupcache stats that implements
// the prometheus.Collector interface, and registers it
func RegisterCacheStats(cacheName string, groupCacheStats *groupcache.Stats) {
	c := &cacheStats{cacheName, make(map[string]*prometheus.Desc), groupCacheStats, &sync.RWMutex{}}
	if err := prometheus.Register(c); err != nil {
		logrus.Infof("error registering prometheus metric: %v", err)
	}
}

func (c *cacheStats) Describe(ch chan<- *prometheus.Desc) {
	for _, statFieldName := range groupCacheStatFields() {
		statName := c.statName(statFieldName)
		desc := prometheus.NewDesc(
			statName,
			fmt.Sprintf("groupcache %v", statFieldName),
			[]string{},
			nil,
		)
		func() {
			c.mutex.Lock()
			defer c.mutex.Unlock()
			c.descriptions[statName] = desc
		}()
		ch <- desc
	}
}

func (c *cacheStats) Collect(ch chan<- prometheus.Metric) {
	r := reflect.ValueOf(c)
	for _, statFieldName := range groupCacheStatFields() {
		value := reflect.Indirect(r).FieldByName(statFieldName)
		func() {
			c.mutex.RLock()
			defer c.mutex.RUnlock()
			metric, err := prometheus.NewConstMetric(
				c.descriptions[c.statName(statFieldName)],
				prometheus.GaugeValue,
				float64(value.Int()),
			)
			if err != nil {
				logrus.Infof("error reporting prometheus cache metric %v: %v", c.statName(statFieldName), err)
			} else {
				ch <- metric
			}
		}()
	}
}

func (c *cacheStats) statName(fieldName string) string {
	var tokens []string
	for _, token := range camelcase.Split(fieldName) {
		tokens = append(tokens, strings.ToLower(token))
	}
	groupCacheStatName := strings.Join(tokens, "_")

	return fmt.Sprintf("pachyderm_pachd_cache_%v_%v_gauge", c.cacheName, groupCacheStatName)
}

func groupCacheStatFields() (fields []string) {
	s := &groupcache.Stats{}
	e := reflect.ValueOf(s).Elem()
	t := e.Type()
	for i := 0; i < e.NumField(); i++ {
		fields = append(fields, t.Field(i).Name)
	}
	return fields
}
<|endoftext|>


<commit_before>package osregistry

import (
	"strings"
	"testing"
)

var testData = `
{
  "windows10": {
    "iso_url": "http://care.dlservice.microsoft.com/dl/download/C/3/9/C399EEA8-135D-4207-92C9-6AAB3259F6EF/10240.16384.150709-1700.TH1_CLIENTENTERPRISEEVAL_OEMRET_X64FRE_EN-US.ISO",
    "iso_checksum_type": "sha1",
    "iso_checksum": "56ab095075be28a90bc0b510835280975c6bb2ce",
    "windows_image_name": "Windows 10 Enterprise Evaluation",
    "virtualbox_guest_os_type": "Windows81_64",
    "vmware_guest_os_type": "windows8srv-64"
  },
  "windows2008r2": {
    "iso_url": "http://download.microsoft.com/download/7/5/E/75EC4E54-5B02-42D6-8879-D8D3A25FBEF7/7601.17514.101119-1850_x64fre_server_eval_en-us-GRMSXEVAL_EN_DVD.iso",
    "iso_checksum_type": "md5",
    "iso_checksum": "4263be2cf3c59177c45085c0a7bc6ca5",
    "windows_image_name": "Windows Server 2008 R2 SERVERSTANDARD",
    "virtualbox_guest_os_type": "Windows2008_64",
    "vmware_guest_os_type": "windows7srv-64"
  }
}
`

func TestCanListAllOSs(t *testing.T) {
	registry := createRegistry(t)
	os := registry.List()
	if len(os) != 2 {
		t.Errorf("Expected 2 OS entries, but got %d instead", len(os))
	} else {
		if os[0] != "windows10" {
			t.Errorf("Expected the first OS entry to be windows10, but was %s", os[0])
		}
		if os[1] != "windows2008r2" {
			t.Errorf("Expected the second OS entry to be windows2008r2, but was %s", os[0])
		}
	}
}

func TestCanLoadWindows10Config(t *testing.T) {
	registry := createRegistry(t)
	r, ok := registry.Get("windows10")
	if !ok {
		t.Error("Failed to load Windows10 from the OS registry")
	} else {
		var tests = []struct {
			actual   string
			expected string
		}{
			{r.Name, "windows10"},
			{r.IsoURL, "http://care.dlservice.microsoft.com/dl/download/C/3/9/C399EEA8-135D-4207-92C9-6AAB3259F6EF/10240.16384.150709-1700.TH1_CLIENTENTERPRISEEVAL_OEMRET_X64FRE_EN-US.ISO"},
			{r.IsoChecksum, "56ab095075be28a90bc0b510835280975c6bb2ce"},
			{r.IsoChecksumType, "sha1"},
			{r.VirtualboxGuestOsType, "Windows81_64"},
			{r.VmwareGuestOsType, "windows8srv-64"},
			{r.WindowsImageName, "Windows 10 Enterprise Evaluation"},
		}
		for _, ts := range tests {
			if ts.actual != ts.expected {
				t.Logf("%#v", r)
				t.Errorf("Expected \"%s\" but got \"%s\"", ts.expected, ts.actual)
			}
		}
	}
}

func TestCanLoadWindows2008R2Config(t *testing.T) {
	registry := createRegistry(t)
	r, ok := registry.Get("windows2008r2")
	if !ok {
		t.Error("Failed to load Windows 2008 R2 from the OS registry")
	} else {
		var tests = []struct {
			actual   string
			expected string
		}{
			{r.Name, "windows2008r2"},
			{r.IsoURL, "http://download.microsoft.com/download/7/5/E/75EC4E54-5B02-42D6-8879-D8D3A25FBEF7/7601.17514.101119-1850_x64fre_server_eval_en-us-GRMSXEVAL_EN_DVD.iso"},
			{r.IsoChecksum, "4263be2cf3c59177c45085c0a7bc6ca5"},
			{r.IsoChecksumType, "md5"},
			{r.VirtualboxGuestOsType, "Windows2008_64"},
			{r.VmwareGuestOsType, "windows7srv-64"},
			{r.WindowsImageName, "Windows Server 2008 R2 SERVERSTANDARD"},
		}
		for _, ts := range tests {
			if ts.actual != ts.expected {
				t.Logf("%#v", r)
				t.Errorf("Expected \"%s\" but got \"%s\"", ts.expected, ts.actual)
			}
		}
	}
}

func TestMissingOS(t *testing.T) {
	registry := createRegistry(t)
	_, ok := registry.Get("Linux")
	if ok {
		t.Error("Should have return !ok for Linux")
	}
}

func createRegistry(t *testing.T) *OperatingSystemRegistry {
	registry, err := New(strings.NewReader(testData))
	if err != nil {
		t.Error("Failed to load the OS registry:", err)
	}
	return registry
}
<commit_msg>Make test deterministic<commit_after>package osregistry

import (
	"sort"
	"strings"
	"testing"
)

var testData = `
{
  "windows10": {
    "iso_url": "http://care.dlservice.microsoft.com/dl/download/C/3/9/C399EEA8-135D-4207-92C9-6AAB3259F6EF/10240.16384.150709-1700.TH1_CLIENTENTERPRISEEVAL_OEMRET_X64FRE_EN-US.ISO",
    "iso_checksum_type": "sha1",
    "iso_checksum": "56ab095075be28a90bc0b510835280975c6bb2ce",
    "windows_image_name": "Windows 10 Enterprise Evaluation",
    "virtualbox_guest_os_type": "Windows81_64",
    "vmware_guest_os_type": "windows8srv-64"
  },
  "windows2008r2": {
    "iso_url": "http://download.microsoft.com/download/7/5/E/75EC4E54-5B02-42D6-8879-D8D3A25FBEF7/7601.17514.101119-1850_x64fre_server_eval_en-us-GRMSXEVAL_EN_DVD.iso",
    "iso_checksum_type": "md5",
    "iso_checksum": "4263be2cf3c59177c45085c0a7bc6ca5",
    "windows_image_name": "Windows Server 2008 R2 SERVERSTANDARD",
    "virtualbox_guest_os_type": "Windows2008_64",
    "vmware_guest_os_type": "windows7srv-64"
  }
}
`

func TestCanListAllOSs(t *testing.T) {
	registry := createRegistry(t)
	os := registry.List()
	sort.Strings(os)
	if len(os) != 2 {
		t.Errorf("Expected 2 OS entries, but got %d instead", len(os))
	} else {
		if os[0] != "windows10" {
			t.Errorf("Expected the first OS entry to be windows10, but was %s", os[0])
		}
		if os[1] != "windows2008r2" {
			t.Errorf("Expected the second OS entry to be windows2008r2, but was %s", os[0])
		}
	}
}

func TestCanLoadWindows10Config(t *testing.T) {
	registry := createRegistry(t)
	r, ok := registry.Get("windows10")
	if !ok {
		t.Error("Failed to load Windows10 from the OS registry")
	} else {
		var tests = []struct {
			actual   string
			expected string
		}{
			{r.Name, "windows10"},
			{r.IsoURL, "http://care.dlservice.microsoft.com/dl/download/C/3/9/C399EEA8-135D-4207-92C9-6AAB3259F6EF/10240.16384.150709-1700.TH1_CLIENTENTERPRISEEVAL_OEMRET_X64FRE_EN-US.ISO"},
			{r.IsoChecksum, "56ab095075be28a90bc0b510835280975c6bb2ce"},
			{r.IsoChecksumType, "sha1"},
			{r.VirtualboxGuestOsType, "Windows81_64"},
			{r.VmwareGuestOsType, "windows8srv-64"},
			{r.WindowsImageName, "Windows 10 Enterprise Evaluation"},
		}
		for _, ts := range tests {
			if ts.actual != ts.expected {
				t.Logf("%#v", r)
				t.Errorf("Expected \"%s\" but got \"%s\"", ts.expected, ts.actual)
			}
		}
	}
}

func TestCanLoadWindows2008R2Config(t *testing.T) {
	registry := createRegistry(t)
	r, ok := registry.Get("windows2008r2")
	if !ok {
		t.Error("Failed to load Windows 2008 R2 from the OS registry")
	} else {
		var tests = []struct {
			actual   string
			expected string
		}{
			{r.Name, "windows2008r2"},
			{r.IsoURL, "http://download.microsoft.com/download/7/5/E/75EC4E54-5B02-42D6-8879-D8D3A25FBEF7/7601.17514.101119-1850_x64fre_server_eval_en-us-GRMSXEVAL_EN_DVD.iso"},
			{r.IsoChecksum, "4263be2cf3c59177c45085c0a7bc6ca5"},
			{r.IsoChecksumType, "md5"},
			{r.VirtualboxGuestOsType, "Windows2008_64"},
			{r.VmwareGuestOsType, "windows7srv-64"},
			{r.WindowsImageName, "Windows Server 2008 R2 SERVERSTANDARD"},
		}
		for _, ts := range tests {
			if ts.actual != ts.expected {
				t.Logf("%#v", r)
				t.Errorf("Expected \"%s\" but got \"%s\"", ts.expected, ts.actual)
			}
		}
	}
}

func TestMissingOS(t *testing.T) {
	registry := createRegistry(t)
	_, ok := registry.Get("Linux")
	if ok {
		t.Error("Should have return !ok for Linux")
	}
}

func createRegistry(t *testing.T) *OperatingSystemRegistry {
	registry, err := New(strings.NewReader(testData))
	if err != nil {
		t.Error("Failed to load the OS registry:", err)
	}
	return registry
}
<|endoftext|>


<commit_before>package history

import "bufio"
import "bytes"
import "fmt"
import "os"
import "strconv"
import "strings"

import "../conio/readline"
import "../interpreter"

var histories = make([]string, 0)
var pointor = 0

func Get(n int) string {
	if n < 0 {
		n = len(histories) + n
	}
	if n >= len(histories) {
		return ""
	} else {
		return histories[n]
	}
}

func Len() int {
	return len(histories)
}

func LastHistory() string {
	if len(histories) <= 0 {
		return ""
	} else {
		return histories[len(histories)-1]
	}
}

func KeyFuncHistoryUp(this *readline.Buffer) readline.Result {
	if pointor <= 0 {
		pointor = len(histories)
	}
	pointor -= 1
	readline.KeyFuncClear(this)
	if pointor >= 0 {
		this.InsertString(0, histories[pointor])
		this.ViewStart = 0
		this.Cursor = 0
		readline.KeyFuncTail(this)
	}
	return readline.CONTINUE
}

func KeyFuncHistoryDown(this *readline.Buffer) readline.Result {
	pointor += 1
	if pointor >= len(histories) {
		pointor = 0
	}
	readline.KeyFuncClear(this)
	if pointor < len(histories) {
		this.InsertString(0, histories[pointor])
		this.ViewStart = 0
		this.Cursor = 0
		readline.KeyFuncTail(this)
	}
	return readline.CONTINUE
}

func Push(input string) {
	histories = append(histories, input)
	pointor = len(histories)
}

func Replace(line string) (string, bool) {
	var buffer bytes.Buffer
	var isReplaced = false
	reader := strings.NewReader(line)

	for reader.Len() > 0 {
		ch, _, _ := reader.ReadRune()
		if ch != '!' || reader.Len() <= 0 {
			buffer.WriteRune(ch)
			continue
		}
		ch, _, _ = reader.ReadRune()
		if n := strings.IndexRune("^$:*", ch); n >= 0 {
			reader.UnreadRune()
			if len(histories) > 0 {
				insertHisotry(&buffer, reader, histories[len(histories)-1])
				isReplaced = true
			}
			continue
		}
		if ch == '!' {
			if len(histories) > 0 {
				insertHisotry(&buffer, reader, histories[len(histories)-1])
				isReplaced = true
				continue
			} else {
				buffer.WriteRune('!')
				continue
			}
		}
		if n := strings.IndexRune("0123456789", ch); n >= 0 {
			backno := n
			for reader.Len() > 0 {
				ch, _, _ = reader.ReadRune()
				if n = strings.IndexRune("0123456789", ch); n >= 0 {
					backno = backno*10 + n
				} else {
					reader.UnreadRune()
					break
				}
			}
			backno = backno % len(histories)
			if 0 <= backno && backno < len(histories) {
				insertHisotry(&buffer, reader, histories[backno])
				isReplaced = true
			}
			continue
		}
		if ch == '-' && reader.Len() > 0 {
			ch, _, _ := reader.ReadRune()
			n := strings.IndexRune("0123456789", ch)
			if n >= 0 {
				number := n
				for reader.Len() > 0 {
					ch, _, _ = reader.ReadRune()
					n = strings.IndexRune("0123456789", ch)
					if n < 0 {
						reader.UnreadRune()
						break
					}
					number = number*10 + n
				}
				backno := len(histories) - number
				for backno < 0 {
					backno += len(histories)
				}
				if 0 <= backno && backno < len(histories) {
					insertHisotry(&buffer, reader, histories[backno])
					isReplaced = true
				} else {
					buffer.WriteString("!-0")
				}
			} else {
				buffer.WriteString("!-")
				buffer.WriteRune(ch)
			}
		} else {
			buffer.WriteRune('!')
			buffer.WriteRune(ch)
		}
	}
	return buffer.String(), isReplaced
}

func splitQ(s string) []string {
	args := make([]string, 0)
	reader := strings.NewReader(s)
	for reader.Len() > 0 {
		var buffer bytes.Buffer
		for {
			if reader.Len() <= 0 {
				return args
			}
			ch, _, _ := reader.ReadRune()
			if ch != ' ' {
				reader.UnreadRune()
				break
			}
		}
		quote := false
		for reader.Len() > 0 {
			ch, _, _ := reader.ReadRune()
			if ch == '"' {
				quote = !quote
			}
			if ch == ' ' && !quote {
				break
			}
			buffer.WriteRune(ch)
		}
		s := buffer.String()
		if s != "" {
			args = append(args, s)
		}
	}
	return args
}

func insertHisotry(buffer *bytes.Buffer, reader *strings.Reader, history1 string) {
	ch, siz, _ := reader.ReadRune()
	if siz > 0 && ch == '^' {
		args := splitQ(history1)
		if len(args) >= 2 {
			buffer.WriteString(args[1])
		}
	} else if siz > 0 && ch == '$' {
		args := splitQ(history1)
		if len(args) >= 2 {
			buffer.WriteString(args[len(args)-1])
		}
	} else if siz > 0 && ch == '*' {
		args := splitQ(history1)
		if len(args) >= 2 {
			buffer.WriteString(strings.Join(args[1:], " "))
		}
	} else if siz > 0 && ch == ':' {
		args := splitQ(history1)
		n := 0
		count := 0
		for reader.Len() > 0 {
			ch, _, _ = reader.ReadRune()
			index := strings.IndexRune("0123456789", ch)
			if index >= 0 {
				n = n*10 + index
				count++
			} else {
				reader.UnreadRune()
				break
			}
		}
		if count <= 0 {
			buffer.WriteRune(':')
		} else if n < len(args) {
			buffer.WriteString(args[n])
		}
	} else {
		if siz > 0 {
			reader.UnreadRune()
		}
		buffer.WriteString(history1)
	}
}

func CmdHistory(cmd *interpreter.Interpreter) (interpreter.NextT, error) {
	var num int
	if len(cmd.Args) >= 2 {
		num64, err := strconv.ParseInt(cmd.Args[1], 0, 32)
		if err != nil {
			return interpreter.CONTINUE, err
		}
		num = int(num64)
	} else {
		num = 10
	}
	var start int
	if len(histories) > num {
		start = len(histories) - num
	} else {
		start = 0
	}
	for i, s := range histories[start:] {
		fmt.Fprintf(cmd.Stdout, "%3d : %-s\n", start+i, s)
	}
	return interpreter.CONTINUE, nil
}

const max_histories = 2000

func Save(path string) error {
	var hist_ []string
	if len(histories) > max_histories {
		hist_ = histories[(len(histories) - max_histories):]
	} else {
		hist_ = histories
	}
	fd, err := os.Create(path)
	if err != nil {
		return err
	}
	defer fd.Close()
	for _, s := range hist_ {
		fmt.Fprintln(fd, s)
	}
	return nil
}

func Load(path string) error {
	fd, err := os.Open(path)
	if err != nil {
		return err
	}
	defer fd.Close()
	sc := bufio.NewScanner(fd)
	for sc.Scan() {
		histories = append(histories, sc.Text())
	}
	return nil
}
<commit_msg>history: support !str<commit_after>package history

import "bufio"
import "bytes"
import "fmt"
import "os"
import "strconv"
import "strings"

import "../conio/readline"
import "../interpreter"

var histories = make([]string, 0)
var pointor = 0

func Get(n int) string {
	if n < 0 {
		n = len(histories) + n
	}
	if n >= len(histories) {
		return ""
	} else {
		return histories[n]
	}
}

func Len() int {
	return len(histories)
}

func LastHistory() string {
	if len(histories) <= 0 {
		return ""
	} else {
		return histories[len(histories)-1]
	}
}

func KeyFuncHistoryUp(this *readline.Buffer) readline.Result {
	if pointor <= 0 {
		pointor = len(histories)
	}
	pointor -= 1
	readline.KeyFuncClear(this)
	if pointor >= 0 {
		this.InsertString(0, histories[pointor])
		this.ViewStart = 0
		this.Cursor = 0
		readline.KeyFuncTail(this)
	}
	return readline.CONTINUE
}

func KeyFuncHistoryDown(this *readline.Buffer) readline.Result {
	pointor += 1
	if pointor >= len(histories) {
		pointor = 0
	}
	readline.KeyFuncClear(this)
	if pointor < len(histories) {
		this.InsertString(0, histories[pointor])
		this.ViewStart = 0
		this.Cursor = 0
		readline.KeyFuncTail(this)
	}
	return readline.CONTINUE
}

func Push(input string) {
	histories = append(histories, input)
	pointor = len(histories)
}

func eventDesignerStrBegin(reader *strings.Reader) (string, bool) {
	var buf bytes.Buffer
	for reader.Len() > 0 {
		ch, _, _ := reader.ReadRune()
		if ch == ' ' {
			reader.UnreadRune()
			break
		}
		buf.WriteRune(ch)
	}
	str := buf.String()
	for i := len(histories) - 1; i >= 0; i-- {
		if strings.HasPrefix(histories[i], str) {
			return histories[i], true
		}
	}
	return "", false
}

func Replace(line string) (string, bool) {
	var buffer bytes.Buffer
	var isReplaced = false
	reader := strings.NewReader(line)

	for reader.Len() > 0 {
		ch, _, _ := reader.ReadRune()
		if ch != '!' || reader.Len() <= 0 {
			buffer.WriteRune(ch)
			continue
		}
		ch, _, _ = reader.ReadRune()
		if n := strings.IndexRune("^$:*", ch); n >= 0 {
			reader.UnreadRune()
			if len(histories) > 0 {
				insertHisotry(&buffer, reader, histories[len(histories)-1])
				isReplaced = true
			}
			continue
		}
		if ch == '!' { // !!
			if len(histories) > 0 {
				insertHisotry(&buffer, reader, histories[len(histories)-1])
				isReplaced = true
				continue
			} else {
				buffer.WriteRune('!')
				continue
			}
		}
		if n := strings.IndexRune("0123456789", ch); n >= 0 { // !n
			backno := n
			for reader.Len() > 0 {
				ch, _, _ = reader.ReadRune()
				if n = strings.IndexRune("0123456789", ch); n >= 0 {
					backno = backno*10 + n
				} else {
					reader.UnreadRune()
					break
				}
			}
			backno = backno % len(histories)
			if 0 <= backno && backno < len(histories) {
				insertHisotry(&buffer, reader, histories[backno])
				isReplaced = true
			}
			continue
		}
		if ch == '-' && reader.Len() > 0 { // !-n
			ch, _, _ := reader.ReadRune()
			n := strings.IndexRune("0123456789", ch)
			if n >= 0 {
				number := n
				for reader.Len() > 0 {
					ch, _, _ = reader.ReadRune()
					n = strings.IndexRune("0123456789", ch)
					if n < 0 {
						reader.UnreadRune()
						break
					}
					number = number*10 + n
				}
				backno := len(histories) - number
				for backno < 0 {
					backno += len(histories)
				}
				if 0 <= backno && backno < len(histories) {
					insertHisotry(&buffer, reader, histories[backno])
					isReplaced = true
				} else {
					buffer.WriteString("!-0")
				}
			} else {
				buffer.WriteString("!-")
				buffer.WriteRune(ch)
			}
		} else { // !str
			reader.UnreadRune() // "-"
			str, ok := eventDesignerStrBegin(reader)
			if ok {
				buffer.WriteString(str)
				isReplaced = true
			} else {
				buffer.WriteRune('!')
				buffer.WriteRune(ch)
			}
		}
	}
	return buffer.String(), isReplaced
}

func splitQ(s string) []string {
	args := make([]string, 0)
	reader := strings.NewReader(s)
	for reader.Len() > 0 {
		var buffer bytes.Buffer
		for {
			if reader.Len() <= 0 {
				return args
			}
			ch, _, _ := reader.ReadRune()
			if ch != ' ' {
				reader.UnreadRune()
				break
			}
		}
		quote := false
		for reader.Len() > 0 {
			ch, _, _ := reader.ReadRune()
			if ch == '"' {
				quote = !quote
			}
			if ch == ' ' && !quote {
				break
			}
			buffer.WriteRune(ch)
		}
		s := buffer.String()
		if s != "" {
			args = append(args, s)
		}
	}
	return args
}

func insertHisotry(buffer *bytes.Buffer, reader *strings.Reader, history1 string) {
	ch, siz, _ := reader.ReadRune()
	if siz > 0 && ch == '^' {
		args := splitQ(history1)
		if len(args) >= 2 {
			buffer.WriteString(args[1])
		}
	} else if siz > 0 && ch == '$' {
		args := splitQ(history1)
		if len(args) >= 2 {
			buffer.WriteString(args[len(args)-1])
		}
	} else if siz > 0 && ch == '*' {
		args := splitQ(history1)
		if len(args) >= 2 {
			buffer.WriteString(strings.Join(args[1:], " "))
		}
	} else if siz > 0 && ch == ':' {
		args := splitQ(history1)
		n := 0
		count := 0
		for reader.Len() > 0 {
			ch, _, _ = reader.ReadRune()
			index := strings.IndexRune("0123456789", ch)
			if index >= 0 {
				n = n*10 + index
				count++
			} else {
				reader.UnreadRune()
				break
			}
		}
		if count <= 0 {
			buffer.WriteRune(':')
		} else if n < len(args) {
			buffer.WriteString(args[n])
		}
	} else {
		if siz > 0 {
			reader.UnreadRune()
		}
		buffer.WriteString(history1)
	}
}

func CmdHistory(cmd *interpreter.Interpreter) (interpreter.NextT, error) {
	var num int
	if len(cmd.Args) >= 2 {
		num64, err := strconv.ParseInt(cmd.Args[1], 0, 32)
		if err != nil {
			return interpreter.CONTINUE, err
		}
		num = int(num64)
	} else {
		num = 10
	}
	var start int
	if len(histories) > num {
		start = len(histories) - num
	} else {
		start = 0
	}
	for i, s := range histories[start:] {
		fmt.Fprintf(cmd.Stdout, "%3d : %-s\n", start+i, s)
	}
	return interpreter.CONTINUE, nil
}

const max_histories = 2000

func Save(path string) error {
	var hist_ []string
	if len(histories) > max_histories {
		hist_ = histories[(len(histories) - max_histories):]
	} else {
		hist_ = histories
	}
	fd, err := os.Create(path)
	if err != nil {
		return err
	}
	defer fd.Close()
	for _, s := range hist_ {
		fmt.Fprintln(fd, s)
	}
	return nil
}

func Load(path string) error {
	fd, err := os.Open(path)
	if err != nil {
		return err
	}
	defer fd.Close()
	sc := bufio.NewScanner(fd)
	for sc.Scan() {
		histories = append(histories, sc.Text())
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"context"
	"encoding/json"
	"html/template"
	"net/http"
	"net/http/pprof"
	"os"
	"time"

	"github.com/elazarl/go-bindata-assetfs"
	"github.com/gorilla/websocket"
	"github.com/nicksnyder/go-i18n/i18n"
	bloomsky "github.com/patrickalin/bloomsky-api-go"
	"github.com/patrickalin/bloomsky-client-go/assembly-assetfs"
)

type httpServer struct {
	bloomskyMessageToHTTP chan bloomsky.Bloomsky
	httpServ              *http.Server
	conn                  *websocket.Conn
	msgJSON               []byte
	templates             map[string]*template.Template
	store                 store
}

type meas struct {
	Timestamp time.Time
	Value     float64
}

type pageHome struct {
	Websockerurl string
}

type pageLog struct {
	LogTxt string
}

type pageHistory struct {
	Websockerurl string
	Store        template.JS
}

type logStru struct {
	Time  string `json:"time"`
	Msg   string `json:"msg"`
	Level string `json:"level"`
	Param string `json:"param"`
	Fct   string `json:"fct"`
}

//listen
func (httpServ *httpServer) listen(context context.Context) {
	go func() {
		for {
			mybloomsky := <-httpServ.bloomskyMessageToHTTP
			var err error

			httpServ.msgJSON, err = json.Marshal(mybloomsky.GetBloomskyStruct())
			checkErr(err, funcName(), "Marshal json Error", "")

			if httpServ.msgJSON == nil {
				logFatal(err, funcName(), "JSON Empty", "")
			}

			if httpServ.conn != nil {
				err = httpServ.conn.WriteMessage(websocket.TextMessage, httpServ.msgJSON)
				checkErr(err, funcName(), "Impossible to write to websocket", "")
			}

			logDebug(funcName(), "Listen", string(httpServ.msgJSON))
		}
	}()
}

// Websocket handler to send data
func (httpServ *httpServer) refreshdata(w http.ResponseWriter, r *http.Request) {
	logDebug(funcName(), "Refresh data Websocket handle", "")

	upgrader := websocket.Upgrader{}

	var err error

	httpServ.conn, err = upgrader.Upgrade(w, r, nil)
	checkErr(err, funcName(), "Upgrade upgrader", "")

	if err = httpServ.conn.WriteMessage(websocket.TextMessage, httpServ.msgJSON); err != nil {
		logFatal(err, funcName(), "Impossible to write to websocket", "")
	}
}

func getWs(r *http.Request) string {
	if r.TLS == nil {
		return "ws://"
	}
	return "wss://"
}

// Home bloomsky handler
func (httpServ *httpServer) home(w http.ResponseWriter, r *http.Request) {

	logDebug(funcName(), "Home Http handle", "")

	p := pageHome{Websockerurl: getWs(r) + r.Host + "/refreshdata"}
	if err := httpServ.templates["home"].Execute(w, p); err != nil {
		logFatal(err, funcName(), "Execute template home", "")
	}
}

// Home bloomsky handler
func (httpServ *httpServer) history(w http.ResponseWriter, r *http.Request) {
	logDebug(funcName(), "Home History handle", "")

	p := pageHistory{Websockerurl: getWs(r) + r.Host + "/refreshdata", Store: template.JS(httpServ.store.String("temp"))}
	if err := httpServ.templates["history"].Execute(w, p); err != nil {
		logFatal(err, funcName(), "Execute template history", "")
	}
}

// Log handler
func (httpServ *httpServer) log(w http.ResponseWriter, r *http.Request) {
	logDebug(funcName(), "Log Http handle", "")

	p := map[string]interface{}{"logRange": createArrayLog()}

	err := httpServ.templates["log"].Execute(w, p)
	checkErr(err, funcName(), "Compile template log", "")
}

func getFileServer(dev bool) http.FileSystem {
	if dev {
		return http.Dir("static")
	}
	return &assetfs.AssetFS{Asset: assemblyAssetfs.Asset, AssetDir: assemblyAssetfs.AssetDir, AssetInfo: assemblyAssetfs.AssetInfo, Prefix: "static"}
}

//createWebServer create web server
func createWebServer(in chan bloomsky.Bloomsky, HTTPPort string, HTTPSPort string, translate i18n.TranslateFunc, devel bool, store store) (*httpServer, error) {

	t := make(map[string]*template.Template)
	t["home"] = GetHTMLTemplate("bloomsky", []string{"tmpl/index.html", "tmpl/bloomsky/script.html", "tmpl/bloomsky/body.html", "tmpl/bloomsky/menu.html", "tmpl/header.html", "tmpl/endScript.html"}, map[string]interface{}{"T": translate}, devel)
	t["history"] = GetHTMLTemplate("bloomsky", []string{"tmpl/index.html", "tmpl/history/script.html", "tmpl/history/body.html", "tmpl/history/menu.html", "tmpl/header.html", "tmpl/endScript.html"}, map[string]interface{}{"T": translate}, devel)
	t["log"] = GetHTMLTemplate("bloomsky", []string{"tmpl/index.html", "tmpl/log/script.html", "tmpl/log/body.html", "tmpl/log/menu.html", "tmpl/header.html", "tmpl/endScript.html"}, map[string]interface{}{"T": translate}, devel)

	server := &httpServer{bloomskyMessageToHTTP: in,
		templates: t,
		store:     store}

	fs := http.FileServer(getFileServer(devel))

	s := http.NewServeMux()

	s.Handle("/static/", http.StripPrefix("/static/", fs))
	s.Handle("/favicon.ico", fs)
	s.HandleFunc("/", server.home)
	s.HandleFunc("/refreshdata", server.refreshdata)
	s.HandleFunc("/log", server.log)
	s.HandleFunc("/history", server.history)
	s.HandleFunc("/debug/pprof/", pprof.Index)
	s.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
	s.HandleFunc("/debug/pprof/profile", pprof.Profile)
	s.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
	s.HandleFunc("/debug/pprof/trace", pprof.Trace)

	h := &http.Server{Addr: HTTPPort, Handler: s}
	go func() {
		err := h.ListenAndServe()
		checkErr(err, funcName(), "Error when I create the server HTTP (don't forget ':')", "")
	}()

	hs := &http.Server{Addr: HTTPSPort, Handler: s}
	go func() {
		err := hs.ListenAndServeTLS("server.crt", "server.key")
		checkErr(err, funcName(), "Error when I create the server HTTPS (don't forget ':')", "")
	}()

	logInfo(funcName(), "Server HTTP listen on port", HTTPPort)
	logInfo(funcName(), "Server HTTPS listen on port", HTTPSPort)

	server.httpServ = h
	return server, nil
}

func createArrayLog() (logRange []logStru) {
	file, err := os.Open("bloomsky.log")
	checkErr(err, funcName(), "Imposible to open file", "bloomsky.log")

	defer file.Close()
	scanner := bufio.NewScanner(file)

	var tt logStru
	for scanner.Scan() {
		json.Unmarshal([]byte(scanner.Text()), &tt)
		checkErr(err, funcName(), "Impossible to unmarshall log", scanner.Text())

		logRange = append(logRange, tt)
	}

	scanner.Err()
	checkErr(err, funcName(), "Scanner Err", "")

	return logRange
}
<commit_msg>modify history<commit_after>package main

import (
	"bufio"
	"context"
	"encoding/json"
	"html/template"
	"net/http"
	"net/http/pprof"
	"os"
	"time"

	"github.com/elazarl/go-bindata-assetfs"
	"github.com/gorilla/websocket"
	"github.com/nicksnyder/go-i18n/i18n"
	bloomsky "github.com/patrickalin/bloomsky-api-go"
	"github.com/patrickalin/bloomsky-client-go/assembly-assetfs"
)

type httpServer struct {
	bloomskyMessageToHTTP chan bloomsky.Bloomsky
	httpServ              *http.Server
	conn                  *websocket.Conn
	msgJSON               []byte
	templates             map[string]*template.Template
	store                 store
}

type meas struct {
	Timestamp time.Time
	Value     float64
}

type pageLog struct {
	LogTxt string
}

type pageHome struct {
	Websockerurl string
}

type logStru struct {
	Time  string `json:"time"`
	Msg   string `json:"msg"`
	Level string `json:"level"`
	Param string `json:"param"`
	Fct   string `json:"fct"`
}

const logfile = "bloomsky.log"

//listen
func (httpServ *httpServer) listen(context context.Context) {
	go func() {
		for {
			mybloomsky := <-httpServ.bloomskyMessageToHTTP
			var err error

			httpServ.msgJSON, err = json.Marshal(mybloomsky.GetBloomskyStruct())
			checkErr(err, funcName(), "Marshal json Error", "")

			if httpServ.msgJSON == nil {
				logFatal(err, funcName(), "JSON Empty", "")
			}

			if httpServ.conn != nil {
				httpServ.refreshWebsocket()
			}

			logDebug(funcName(), "Listen", string(httpServ.msgJSON))
		}
	}()
}

func (httpServ *httpServer) refreshWebsocket() {
	t := append(httpServ.msgJSON, []byte("SEPARATOR"+httpServ.store.String("temperatureCelsius"))...)
	t = append(t, []byte("SEPARATOR"+httpServ.store.String("windGustkmh"))...)
	err := httpServ.conn.WriteMessage(websocket.TextMessage, t)
	checkErr(err, funcName(), "Impossible to write to websocket", "")
}

// Websocket handler to send data
func (httpServ *httpServer) refreshdata(w http.ResponseWriter, r *http.Request) {
	logDebug(funcName(), "Refresh data Websocket handle", "")

	upgrader := websocket.Upgrader{}

	var err error

	httpServ.conn, err = upgrader.Upgrade(w, r, nil)
	checkErr(err, funcName(), "Upgrade upgrader", "")

	if err = httpServ.conn.WriteMessage(websocket.TextMessage, httpServ.msgJSON); err != nil {
		logFatal(err, funcName(), "Impossible to write to websocket", "")
	}
}

// Websocket handler to send data
func (httpServ *httpServer) refreshHistory(w http.ResponseWriter, r *http.Request) {
	logDebug(funcName(), "Refresh history Websocket handle", "")

	upgrader := websocket.Upgrader{}

	var err error

	httpServ.conn, err = upgrader.Upgrade(w, r, nil)
	checkErr(err, funcName(), "Upgrade upgrader", "")

	httpServ.refreshWebsocket()
}

func getWs(r *http.Request) string {
	if r.TLS == nil {
		return "ws://"
	}
	return "wss://"
}

// Home bloomsky handler
func (httpServ *httpServer) home(w http.ResponseWriter, r *http.Request) {
	logDebug(funcName(), "Home Http handle", "")

	p := pageHome{Websockerurl: getWs(r) + r.Host + "/refreshdata"}
	if err := httpServ.templates["home"].Execute(w, p); err != nil {
		logFatal(err, funcName(), "Execute template home", "")
	}
}

// Home bloomsky handler
func (httpServ *httpServer) history(w http.ResponseWriter, r *http.Request) {
	logDebug(funcName(), "Home History handle", "")

	p := pageHome{Websockerurl: getWs(r) + r.Host + "/refreshhistory"}
	if err := httpServ.templates["history"].Execute(w, p); err != nil {
		logFatal(err, funcName(), "Execute template history", "")
	}
}

// Log handler
func (httpServ *httpServer) log(w http.ResponseWriter, r *http.Request) {
	logDebug(funcName(), "Log Http handle", "")

	p := map[string]interface{}{"logRange": createArrayLog(logfile)}

	err := httpServ.templates["log"].Execute(w, p)
	checkErr(err, funcName(), "Compile template log", "")
}

func getFileServer(dev bool) http.FileSystem {
	if dev {
		return http.Dir("static")
	}
	return &assetfs.AssetFS{Asset: assemblyAssetfs.Asset, AssetDir: assemblyAssetfs.AssetDir, AssetInfo: assemblyAssetfs.AssetInfo, Prefix: "static"}
}

//createWebServer create web server
func createWebServer(in chan bloomsky.Bloomsky, HTTPPort string, HTTPSPort string, translate i18n.TranslateFunc, devel bool, store store) (*httpServer, error) {

	t := make(map[string]*template.Template)
	t["home"] = GetHTMLTemplate("bloomsky", []string{"tmpl/index.html", "tmpl/bloomsky/script.html", "tmpl/bloomsky/body.html", "tmpl/bloomsky/menu.html", "tmpl/header.html", "tmpl/endScript.html"}, map[string]interface{}{"T": translate}, devel)
	t["history"] = GetHTMLTemplate("bloomsky", []string{"tmpl/index.html", "tmpl/history/script.html", "tmpl/history/body.html", "tmpl/history/menu.html", "tmpl/header.html", "tmpl/endScript.html"}, map[string]interface{}{"T": translate}, devel)
	t["log"] = GetHTMLTemplate("bloomsky", []string{"tmpl/index.html", "tmpl/log/script.html", "tmpl/log/body.html", "tmpl/log/menu.html", "tmpl/header.html", "tmpl/endScript.html"}, map[string]interface{}{"T": translate}, devel)

	server := &httpServer{bloomskyMessageToHTTP: in,
		templates: t,
		store:     store}

	fs := http.FileServer(getFileServer(devel))

	s := http.NewServeMux()

	s.Handle("/static/", http.StripPrefix("/static/", fs))
	s.Handle("/favicon.ico", fs)
	s.HandleFunc("/", server.home)
	s.HandleFunc("/refreshdata", server.refreshdata)
	s.HandleFunc("/refreshhistory", server.refreshHistory)
	s.HandleFunc("/log", server.log)
	s.HandleFunc("/history", server.history)
	s.HandleFunc("/debug/pprof/", pprof.Index)
	s.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
	s.HandleFunc("/debug/pprof/profile", pprof.Profile)
	s.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
	s.HandleFunc("/debug/pprof/trace", pprof.Trace)

	h := &http.Server{Addr: HTTPPort, Handler: s}
	go func() {
		err := h.ListenAndServe()
		checkErr(err, funcName(), "Error when I create the server HTTP (don't forget ':')", "")
	}()

	hs := &http.Server{Addr: HTTPSPort, Handler: s}
	go func() {
		err := hs.ListenAndServeTLS("server.crt", "server.key")
		checkErr(err, funcName(), "Error when I create the server HTTPS (don't forget ':')", "")
	}()

	logInfo(funcName(), "Server HTTP listen on port", HTTPPort)
	logInfo(funcName(), "Server HTTPS listen on port", HTTPSPort)

	server.httpServ = h
	return server, nil
}

func createArrayLog(logFile string) (logRange []logStru) {
	file, err := os.Open(logFile)
	checkErr(err, funcName(), "Imposible to open file", "bloomsky.log")

	defer file.Close()
	scanner := bufio.NewScanner(file)

	var tt logStru
	for scanner.Scan() {
		json.Unmarshal([]byte(scanner.Text()), &tt)
		checkErr(err, funcName(), "Impossible to unmarshall log", scanner.Text())

		logRange = append(logRange, tt)
	}

	scanner.Err()
	checkErr(err, funcName(), "Scanner Err", "")

	return logRange
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"github.com/streadway/amqp"
	"koding/tools/amqputil"
	"log"
)

type Consumer struct {
	conn    *amqp.Connection
	channel *amqp.Channel
	tag     string
}

type Producer struct {
	conn    *amqp.Connection
	channel *amqp.Channel
}

type JoinMsg struct {
	Name       string `json:"name"`
	BindingKey string `json:"bindingKey"`
	Exchange   string `json:"exchange"`
	RoutingKey string `json:"routingKey"`
	Suffix     string `json:"suffix"`
}

type LeaveMsg struct {
	RoutingKey string `json:"routingKey"`
}

var authPairs map[string]JoinMsg
var producer *Producer

func main() {
	log.Println("routing worker started")

	authPairs = make(map[string]JoinMsg)

	var err error
	producer, err = createProducer()
	if err != nil {
		log.Println(err)
	}

	startRouting()
}

func startRouting() {
	c := &Consumer{
		conn:    nil,
		channel: nil,
		tag:     "",
	}

	var err error

	log.Printf("creating consumer connections")
	c.conn = amqputil.CreateConnection("routing")
	c.channel = amqputil.CreateChannel(c.conn)

	err = c.channel.ExchangeDeclare("routing-control", "fanout", false, true, false, false, nil)
	if err != nil {
		log.Fatal("exchange.declare: %s", err)
	}

	if _, err := c.channel.QueueDeclare("", false, true, false, false, nil); err != nil {
		log.Fatal("queue.declare: %s", err)
	}

	if err := c.channel.QueueBind("", "", "routing-control", false, nil); err != nil {
		log.Fatal("queue.bind: %s", err)
	}

	authStream, err := c.channel.Consume("", "", true, false, false, false, nil)
	if err != nil {
		log.Fatal("basic.consume: %s", err)
	}

	log.Println("routing started...")
	for msg := range authStream {
		log.Printf("got %dB message data: [%v]-[%s] %s",
			len(msg.Body),
			msg.DeliveryTag,
			msg.RoutingKey,
			msg.Body)

		switch msg.RoutingKey {
		case "auth.join":
			var join JoinMsg
			err := json.Unmarshal(msg.Body, &join)
			if err != nil {
				log.Print("bad json incoming msg: ", err)
			}

			authPairs[join.RoutingKey] = join

			log.Println("Auth pairs:", authPairs) // this is just for debug

			declareExchange(c, join.Exchange)

			go consumeAndRepublish(c, join.Exchange, join.BindingKey, join.RoutingKey, join.Suffix)
		case "auth.leave":
			var leave LeaveMsg
			err := json.Unmarshal(msg.Body, &leave)
			if err != nil {
				log.Print("bad json incoming msg: ", err)
			}

			// delete user from the authPairs map and cancel it from consuming
			err = c.channel.Cancel(authPairs[leave.RoutingKey].BindingKey, false)
			if err != nil {
				log.Fatal("basic.cancel: %s", err)
			}
			delete(authPairs, leave.RoutingKey)

		default:
			log.Println("routing key is not defined: ", msg.RoutingKey)
		}
	}
}

func declareExchange(c *Consumer, exchange string) {
	if err := c.channel.ExchangeDeclare(exchange, "topic", false, true, false, false, nil); err != nil {
		log.Fatal("exchange.declare: %s", err)
	}
}

func consumeAndRepublish(c *Consumer, exchange, bindingKey, routingKey, suffix string) {
	log.Printf("Consume from:\n exchange %s\n bindingKey %s\n routingKey %s\n",
		exchange, bindingKey, routingKey)

	if len(suffix) > 0 {
		routingKey += suffix
	}

	if _, err := c.channel.QueueDeclare("", false, true, true, false, nil); err != nil {
		log.Fatal("queue.declare: %s", err)
	}

	if err := c.channel.QueueBind("", bindingKey, exchange, false, nil); err != nil {
		log.Fatal("queue.bind: %s", err)
	}

	messages, err := c.channel.Consume("", bindingKey, true, false, false, false, nil)
	if err != nil {
		log.Fatal("basic.consume: %s", err)
	}

	for msg := range messages {
		log.Printf("messages stream got %dB message data: [%v] %s",
			len(msg.Body),
			msg.DeliveryTag,
			msg.Body)

		publishToBroker(msg.Body, routingKey)
	}

}

func publishToBroker(data []byte, routingKey string) {
	msg := amqp.Publishing{
		Headers:         amqp.Table{},
		ContentType:     "text/plain",
		ContentEncoding: "",
		Body:            data,
		DeliveryMode:    1, // 1=non-persistent, 2=persistent
		Priority:        0, // 0-9
	}

	log.Println("publishing data ", string(data))
	err := producer.channel.Publish("broker", routingKey, false, false, msg)
	if err != nil {
		log.Printf("error while publishing proxy message: %s", err)
	}

}

func createProducer() (*Producer, error) {
	p := &Producer{
		conn:    nil,
		channel: nil,
	}

	log.Printf("creating publisher connections")

	p.conn = amqputil.CreateConnection("deneme")
	p.channel = amqputil.CreateChannel(p.conn)

	return p, nil
}
<commit_msg>reintroduced exchanges map, removing exchange reference when getting a cancel<commit_after>package main

import (
	"encoding/json"
	"github.com/streadway/amqp"
	"koding/tools/amqputil"
	"log"
)

type Consumer struct {
	conn    *amqp.Connection
	channel *amqp.Channel
	tag     string
}

type Producer struct {
	conn    *amqp.Connection
	channel *amqp.Channel
}

type JoinMsg struct {
	Name       string `json:"name"`
	BindingKey string `json:"bindingKey"`
	Exchange   string `json:"exchange"`
	RoutingKey string `json:"routingKey"`
	Suffix     string `json:"suffix"`
}

type LeaveMsg struct {
	RoutingKey string `json:"routingKey"`
}

var authPairs map[string]JoinMsg
var exchanges map[string]bool
var producer *Producer

func main() {
	log.Println("routing worker started")

	authPairs = make(map[string]JoinMsg)
	exchanges = make(map[string]bool)

	var err error
	producer, err = createProducer()
	if err != nil {
		log.Println(err)
	}

	startRouting()
}

func startRouting() {
	c := &Consumer{
		conn:    nil,
		channel: nil,
		tag:     "",
	}

	var err error

	log.Printf("creating consumer connections")
	c.conn = amqputil.CreateConnection("routing")
	c.channel = amqputil.CreateChannel(c.conn)

	err = c.channel.ExchangeDeclare("routing-control", "fanout", false, true, false, false, nil)
	if err != nil {
		log.Fatal("exchange.declare: %s", err)
	}

	if _, err := c.channel.QueueDeclare("", false, true, false, false, nil); err != nil {
		log.Fatal("queue.declare: %s", err)
	}

	if err := c.channel.QueueBind("", "", "routing-control", false, nil); err != nil {
		log.Fatal("queue.bind: %s", err)
	}

	authStream, err := c.channel.Consume("", "", true, false, false, false, nil)
	if err != nil {
		log.Fatal("basic.consume: %s", err)
	}

	log.Println("routing started...")
	for msg := range authStream {
		log.Printf("got %dB message data: [%v]-[%s] %s",
			len(msg.Body),
			msg.DeliveryTag,
			msg.RoutingKey,
			msg.Body)

		switch msg.RoutingKey {
		case "auth.join":
			var join JoinMsg
			err := json.Unmarshal(msg.Body, &join)
			if err != nil {
				log.Print("bad json incoming msg: ", err)
			}

			authPairs[join.RoutingKey] = join

			log.Println("Auth pairs:", authPairs) // this is just for debug

			declareExchange(c, join.Exchange)

			go consumeAndRepublish(c, join.Exchange, join.BindingKey, join.RoutingKey, join.Suffix)
		case "auth.leave":
			var leave LeaveMsg
			err := json.Unmarshal(msg.Body, &leave)
			if err != nil {
				log.Print("bad json incoming msg: ", err)
			}

			// cancel consuming
			err = c.channel.Cancel(authPairs[leave.RoutingKey].BindingKey, false)
			if err != nil {
				log.Fatal("basic.cancel: %s", err)
			}
			// delete exchange reference so it gets redeclared
			delete(exchanges, authPairs[leave.RoutingKey].Exchange)
			// delete authPairs map
			delete(authPairs, leave.RoutingKey)

		default:
			log.Println("routing key is not defined: ", msg.RoutingKey)
		}
	}
}

func declareExchange(c *Consumer, exchange string) {
	if !exchanges[exchange] {
		if err := c.channel.ExchangeDeclare(exchange, "topic", false, true, false, false, nil); err != nil {
			log.Fatal("exchange.declare: %s", err)
		}
		exchanges[exchange] = true
	}
}

func consumeAndRepublish(c *Consumer, exchange, bindingKey, routingKey, suffix string) {
	log.Printf("Consume from:\n exchange %s\n bindingKey %s\n routingKey %s\n",
		exchange, bindingKey, routingKey)

	if len(suffix) > 0 {
		routingKey += suffix
	}

	if _, err := c.channel.QueueDeclare("", false, true, true, false, nil); err != nil {
		log.Fatal("queue.declare: %s", err)
	}

	if err := c.channel.QueueBind("", bindingKey, exchange, false, nil); err != nil {
		log.Fatal("queue.bind: %s", err)
	}

	messages, err := c.channel.Consume("", bindingKey, true, false, false, false, nil)
	if err != nil {
		log.Fatal("basic.consume: %s", err)
	}

	for msg := range messages {
		log.Printf("messages stream got %dB message data: [%v] %s",
			len(msg.Body),
			msg.DeliveryTag,
			msg.Body)

		publishToBroker(msg.Body, routingKey)
	}

}

func publishToBroker(data []byte, routingKey string) {
	msg := amqp.Publishing{
		Headers:         amqp.Table{},
		ContentType:     "text/plain",
		ContentEncoding: "",
		Body:            data,
		DeliveryMode:    1, // 1=non-persistent, 2=persistent
		Priority:        0, // 0-9
	}

	log.Println("publishing data ", string(data))
	err := producer.channel.Publish("broker", routingKey, false, false, msg)
	if err != nil {
		log.Printf("error while publishing proxy message: %s", err)
	}

}

func createProducer() (*Producer, error) {
	p := &Producer{
		conn:    nil,
		channel: nil,
	}

	log.Printf("creating publisher connections")

	p.conn = amqputil.CreateConnection("deneme")
	p.channel = amqputil.CreateChannel(p.conn)

	return p, nil
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/rand"
	"encoding/base64"
	"encoding/json"
	"github.com/streadway/amqp"
	"koding/tools/amqputil"
	"log"
)

type Consumer struct {
	conn    *amqp.Connection
	channel *amqp.Channel
	tag     string
}

type Producer struct {
	conn    *amqp.Connection
	channel *amqp.Channel
}

type JoinMsg struct {
	Name        string `json:"name"`
	BindingKey  string `json:"bindingKey"`
	Exchange    string `json:"exchange"`
	RoutingKey  string `json:"routingKey"`
	ConsumerTag string
	Suffix      string `json:"suffix"`
}

type LeaveMsg struct {
	RoutingKey string `json:"routingKey"`
}

var authPairs map[string]JoinMsg
var exchanges map[string]uint
var producer *Producer

func main() {
	log.Println("routing worker started")

	authPairs = make(map[string]JoinMsg)
	exchanges = make(map[string]uint)

	var err error
	producer, err = createProducer()
	if err != nil {
		log.Println(err)
	}

	startRouting()
}

func startRouting() {
	c := &Consumer{
		conn:    nil,
		channel: nil,
		tag:     "",
	}

	var err error

	log.Printf("creating consumer connections")
	c.conn = amqputil.CreateConnection("routing")
	c.channel = amqputil.CreateChannel(c.conn)

	err = c.channel.ExchangeDeclare("routing-control", "fanout", false, true, false, false, nil)
	if err != nil {
		log.Fatalf("exchange.declare: %s", err)
	}

	if _, err := c.channel.QueueDeclare("", false, true, false, false, nil); err != nil {
		log.Fatalf("queue.declare: %s", err)
	}

	if err := c.channel.QueueBind("", "", "routing-control", false, nil); err != nil {
		log.Fatalf("queue.bind: %s", err)
	}

	authStream, err := c.channel.Consume("", "", true, false, false, false, nil)
	if err != nil {
		log.Fatalf("basic.consume: %s", err)
	}

	log.Println("routing started...")
	for msg := range authStream {
		log.Printf("got %dB message data: [%v]-[%s] %s",
			len(msg.Body),
			msg.DeliveryTag,
			msg.RoutingKey,
			msg.Body)

		switch msg.RoutingKey {
		case "auth.join":
			var join JoinMsg
			err := json.Unmarshal(msg.Body, &join)
			if err != nil {
				log.Print("bad json incoming msg: ", err)
			}

			join.ConsumerTag = generateUniqueConsumerTag(join.BindingKey)
			authPairs[join.RoutingKey] = join

			log.Println("Auth pairs:", authPairs) // this is just for debug

			declareExchange(c, join.Exchange)

			go consumeAndRepublish(c, join.Exchange, join.BindingKey, join.RoutingKey, join.Suffix, join.ConsumerTag)
		case "auth.leave":
			var leave LeaveMsg
			err := json.Unmarshal(msg.Body, &leave)
			if err != nil {
				log.Print("bad json incoming msg: ", err)
			}

			// cancel consuming
			err = c.channel.Cancel(authPairs[leave.RoutingKey].ConsumerTag, false)
			if err != nil {
				log.Fatalf("basic.cancel: %s", err)
			}
			decrementExchangeCounter(leave)

		default:
			log.Println("routing key is not defined: ", msg.RoutingKey)
		}
	}
}

func generateUniqueConsumerTag(bindingKey string) string {
	r := make([]byte, 32/8)
	rand.Read(r)
	return bindingKey + "." + base64.StdEncoding.EncodeToString(r)
}

func declareExchange(c *Consumer, exchange string) {
	if exchanges[exchange] <= 0 {
		if err := c.channel.ExchangeDeclare(exchange, "topic", false, true, false, false, nil); err != nil {
			log.Fatalf("exchange.declare: %s", err)
		}
		exchanges[exchange] = 0
	}
	exchanges[exchange]++
}

func decrementExchangeCounter(leave LeaveMsg) {
	exchange := authPairs[leave.RoutingKey].Exchange
	// decrement exchange counter
	exchanges[exchange]--
	// delete authPairs map
	delete(authPairs, leave.RoutingKey)
}

func consumeAndRepublish(c *Consumer, exchange, bindingKey, routingKey, suffix string, consumerTag string) {
	log.Printf("Consume from:\n exchange %s\n bindingKey %s\n routingKey %s\n consumerTag %s\n",
		exchange, bindingKey, routingKey, consumerTag)

	if len(suffix) > 0 {
		routingKey += suffix
	}

	if _, err := c.channel.QueueDeclare("", false, true, true, false, nil); err != nil {
		log.Fatalf("queue.declare: %s", err)
	}

	if err := c.channel.QueueBind("", bindingKey, exchange, false, nil); err != nil {
		log.Fatalf("queue.bind: %s", err)
	}

	messages, err := c.channel.Consume("", consumerTag, true, false, false, false, nil)
	if err != nil {
		log.Fatalf("basic.consume: %s", err)
	}

	for msg := range messages {
		log.Printf("messages stream got %dB message data: [%v] %s",
			len(msg.Body),
			msg.DeliveryTag,
			msg.Body)

		publishToBroker(msg.Body, routingKey)
	}

}

func publishToBroker(data []byte, routingKey string) {
	msg := amqp.Publishing{
		Headers:         amqp.Table{},
		ContentType:     "text/plain",
		ContentEncoding: "",
		Body:            data,
		DeliveryMode:    1, // 1=non-persistent, 2=persistent
		Priority:        0, // 0-9
	}

	log.Println("publishing data ", string(data), routingKey)
	err := producer.channel.Publish("broker", routingKey, false, false, msg)
	if err != nil {
		log.Printf("error while publishing proxy message: %s", err)
	}

}

func createProducer() (*Producer, error) {
	p := &Producer{
		conn:    nil,
		channel: nil,
	}

	log.Printf("creating publisher connections")

	p.conn = amqputil.CreateConnection("routing")
	p.channel = amqputil.CreateChannel(p.conn)

	return p, nil
}
<commit_msg>hack around a state-related bug in streadway/amqp<commit_after>package main

import (
	"crypto/rand"
	"encoding/base64"
	"encoding/json"
	"github.com/streadway/amqp"
	"koding/tools/amqputil"
	"log"
)

type Consumer struct {
	conn    *amqp.Connection
	channel *amqp.Channel
	tag     string
}

type Producer struct {
	conn    *amqp.Connection
	channel *amqp.Channel
}

type JoinMsg struct {
	Name        string `json:"name"`
	BindingKey  string `json:"bindingKey"`
	Exchange    string `json:"exchange"`
	RoutingKey  string `json:"routingKey"`
	ConsumerTag string
	Suffix      string `json:"suffix"`
}

type LeaveMsg struct {
	RoutingKey string `json:"routingKey"`
}

var authPairs map[string]JoinMsg
var exchanges map[string]uint
var producer *Producer

func main() {
	log.Println("routing worker started")

	authPairs = make(map[string]JoinMsg)
	exchanges = make(map[string]uint)

	var err error
	producer, err = createProducer()
	if err != nil {
		log.Println(err)
	}

	startRouting()
}

func startRouting() {
	c := &Consumer{
		conn:    nil,
		channel: nil,
		tag:     "",
	}

	var err error

	log.Printf("creating consumer connections")
	c.conn = amqputil.CreateConnection("routing")
	c.channel = amqputil.CreateChannel(c.conn)

	err = c.channel.ExchangeDeclare("routing-control", "fanout", false, true, false, false, nil)
	if err != nil {
		log.Fatalf("exchange.declare: %s", err)
	}

	if _, err := c.channel.QueueDeclare("", false, true, false, false, nil); err != nil {
		log.Fatalf("queue.declare: %s", err)
	}

	if err := c.channel.QueueBind("", "", "routing-control", false, nil); err != nil {
		log.Fatalf("queue.bind: %s", err)
	}

	authStream, err := c.channel.Consume("", "", true, false, false, false, nil)
	if err != nil {
		log.Fatalf("basic.consume: %s", err)
	}

	log.Println("routing started...")
	for msg := range authStream {
		log.Printf("got %dB message data: [%v]-[%s] %s",
			len(msg.Body),
			msg.DeliveryTag,
			msg.RoutingKey,
			msg.Body)

		switch msg.RoutingKey {
		case "auth.join":
			var join JoinMsg
			err := json.Unmarshal(msg.Body, &join)
			if err != nil {
				log.Print("bad json incoming msg: ", err)
			}

			join.ConsumerTag = generateUniqueConsumerTag(join.BindingKey)
			authPairs[join.RoutingKey] = join

			log.Println("Auth pairs:", authPairs) // this is just for debug

			declareExchange(c, join.Exchange)

			go consumeAndRepublish(c, join.Exchange, join.BindingKey, join.RoutingKey, join.Suffix, join.ConsumerTag)
		case "auth.leave":
			var leave LeaveMsg
			err := json.Unmarshal(msg.Body, &leave)
			if err != nil {
				log.Print("bad json incoming msg: ", err)
			}

			// cancel consuming
			err = c.channel.Cancel(authPairs[leave.RoutingKey].ConsumerTag, false)
			if err != nil {
				log.Fatalf("basic.cancel: %s", err)
			}
			decrementExchangeCounter(leave)

		default:
			log.Println("routing key is not defined: ", msg.RoutingKey)
		}
	}
}

func generateUniqueConsumerTag(bindingKey string) string {
	r := make([]byte, 32/8)
	rand.Read(r)
	return bindingKey + "." + base64.StdEncoding.EncodeToString(r)
}

func generateUniqueQueueName() string {
	r := make([]byte, 32/8)
	rand.Read(r)
	return base64.StdEncoding.EncodeToString(r)
}

func declareExchange(c *Consumer, exchange string) {
	if exchanges[exchange] <= 0 {
		if err := c.channel.ExchangeDeclare(exchange, "topic", false, true, false, false, nil); err != nil {
			log.Fatalf("exchange.declare: %s", err)
		}
		exchanges[exchange] = 0
	}
	exchanges[exchange]++
}

func decrementExchangeCounter(leave LeaveMsg) {
	exchange := authPairs[leave.RoutingKey].Exchange
	// decrement exchange counter
	exchanges[exchange]--
	// delete authPairs map
	delete(authPairs, leave.RoutingKey)
}

func consumeAndRepublish(c *Consumer, exchange, bindingKey, routingKey, suffix string, consumerTag string) {
	log.Printf("Consume from:\n exchange %s\n bindingKey %s\n routingKey %s\n consumerTag %s\n",
		exchange, bindingKey, routingKey, consumerTag)

	if len(suffix) > 0 {
		routingKey += suffix
	}

	uniqueQueueName := generateUniqueQueueName()

	if _, err := c.channel.QueueDeclare(uniqueQueueName, false, true, true, false, nil); err != nil {
		log.Fatalf("queue.declare: %s", err)
	}

	if err := c.channel.QueueBind(uniqueQueueName, bindingKey, exchange, false, nil); err != nil {
		log.Fatalf("queue.bind: %s", err)
	}

	messages, err := c.channel.Consume(uniqueQueueName, consumerTag, true, false, false, false, nil)
	if err != nil {
		log.Fatalf("basic.consume: %s", err)
	}

	for msg := range messages {
		log.Printf("messages stream got %dB message data: [%v] %s",
			len(msg.Body),
			msg.DeliveryTag,
			msg.Body)

		publishToBroker(msg.Body, routingKey)
	}

}

func publishToBroker(data []byte, routingKey string) {
	msg := amqp.Publishing{
		Headers:         amqp.Table{},
		ContentType:     "text/plain",
		ContentEncoding: "",
		Body:            data,
		DeliveryMode:    1, // 1=non-persistent, 2=persistent
		Priority:        0, // 0-9
	}

	log.Println("publishing data ", string(data), routingKey)
	err := producer.channel.Publish("broker", routingKey, false, false, msg)
	if err != nil {
		log.Printf("error while publishing proxy message: %s", err)
	}

}

func createProducer() (*Producer, error) {
	p := &Producer{
		conn:    nil,
		channel: nil,
	}

	log.Printf("creating publisher connections")

	p.conn = amqputil.CreateConnection("routing")
	p.channel = amqputil.CreateChannel(p.conn)

	return p, nil
}
<|endoftext|>