<commit_before>// Copyright 2009 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 math

/*
	Floating-point hyperbolic tangent.

	Sinh and Cosh are called except for large arguments, which
	would cause overflow improperly.
*/

// Tanh computes the hyperbolic tangent of x.
//
// Special cases are:
//	Tanh(±0) = ±0
//	Tanh(±Inf) = ±1
//	Tanh(NaN) = NaN
func Tanh(x float64) float64 {
	if x < 0 {
		x = -x
		if x > 21 {
			return -1
		}
		return -Sinh(x) / Cosh(x)
	}
	if x > 21 {
		return 1
	}
	return Sinh(x) / Cosh(x)
}
<commit_msg>math: Faster Tanh<commit_after>// Copyright 2009 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 math

// The original C code, the long comment, and the constants
// below were from http://netlib.sandia.gov/cephes/cmath/sin.c,
// available from http://www.netlib.org/cephes/cmath.tgz.
// The go code is a simplified version of the original C.
//      tanh.c
//
//      Hyperbolic tangent
//
// SYNOPSIS:
//
// double x, y, tanh();
//
// y = tanh( x );
//
// DESCRIPTION:
//
// Returns hyperbolic tangent of argument in the range MINLOG to MAXLOG.
//      MAXLOG = 8.8029691931113054295988e+01 = log(2**127)
//      MINLOG = -8.872283911167299960540e+01 = log(2**-128)
//
// A rational function is used for |x| < 0.625.  The form
// x + x**3 P(x)/Q(x) of Cody & Waite is employed.
// Otherwise,
//      tanh(x) = sinh(x)/cosh(x) = 1  -  2/(exp(2x) + 1).
//
// ACCURACY:
//
//                      Relative error:
// arithmetic   domain     # trials      peak         rms
//    IEEE      -2,2        30000       2.5e-16     5.8e-17
//
// Cephes Math Library Release 2.8:  June, 2000
// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier
//
// The readme file at http://netlib.sandia.gov/cephes/ says:
//    Some software in this archive may be from the book _Methods and
// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster
// International, 1989) or from the Cephes Mathematical Library, a
// commercial product. In either event, it is copyrighted by the author.
// What you see here may be used freely but it comes with no support or
// guarantee.
//
//   The two known misprints in the book are repaired here in the
// source listings for the gamma function and the incomplete beta
// integral.
//
//   Stephen L. Moshier
//   moshier@na-net.ornl.gov
//

var tanhP = [...]float64{
	-9.64399179425052238628E-1,
	-9.92877231001918586564E1,
	-1.61468768441708447952E3,
}
var tanhQ = [...]float64{
	1.12811678491632931402E2,
	2.23548839060100448583E3,
	4.84406305325125486048E3,
}

// Tanh computes the hyperbolic tangent of x.
//
// Special cases are:
//	Tanh(±0) = ±0
//	Tanh(±Inf) = ±1
//	Tanh(NaN) = NaN
func Tanh(x float64) float64 {
	const MAXLOG = 8.8029691931113054295988e+01 // log(2**127)
	z := Abs(x)
	switch {
	case z > 0.5*MAXLOG:
		if x < 0 {
			return -1
		}
		return 1
	case z >= 0.625:
		s := Exp(2 * z)
		z = 1 - 2/(s+1)
		if x < 0 {
			z = -z
		}
	default:
		if x == 0 {
			return x
		}
		s := x * x
		z = x + x*s*((tanhP[0]*s+tanhP[1])*s+tanhP[2])/(((s+tanhQ[0])*s+tanhQ[1])*s+tanhQ[2])
	}
	return z
}
<|endoftext|>


<commit_before>// Copyright 2009 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 time

import (
	"os"
	"sync"
)

// A Ticker holds a synchronous channel that delivers `ticks' of a clock
// at intervals.
type Ticker struct {
	C        <-chan int64 // The channel on which the ticks are delivered.
	c        chan<- int64 // The same channel, but the end we use.
	ns       int64
	shutdown chan bool // Buffered channel used to signal shutdown.
	nextTick int64
	next     *Ticker
}

// Stop turns off a ticker.  After Stop, no more ticks will be sent.
func (t *Ticker) Stop() {
	// Make it non-blocking so multiple Stops don't block.
	_ = t.shutdown <- true
}

// Tick is a convenience wrapper for NewTicker providing access to the ticking
// channel only.  Useful for clients that have no need to shut down the ticker.
func Tick(ns int64) <-chan int64 {
	if ns <= 0 {
		return nil
	}
	return NewTicker(ns).C
}

type alarmer struct {
	wakeUp   chan bool // wakeup signals sent/received here
	wakeMeAt chan int64
	wakeTime int64
}

// Set alarm to go off at time ns, if not already set earlier.
func (a *alarmer) set(ns int64) {
	switch {
	case a.wakeTime > ns:
		// Next tick we expect is too late; shut down the late runner
		// and (after fallthrough) start a new wakeLoop.
		a.wakeMeAt <- -1
		fallthrough
	case a.wakeMeAt == nil:
		// There's no wakeLoop, start one.
		a.wakeMeAt = make(chan int64, 10)
		go wakeLoop(a.wakeMeAt, a.wakeUp)
		fallthrough
	case a.wakeTime == 0:
		// Nobody else is waiting; it's just us.
		a.wakeTime = ns
		a.wakeMeAt <- ns
	default:
		// There's already someone scheduled.
	}
}

// Channel to notify tickerLoop of new Tickers being created.
var newTicker chan *Ticker

func startTickerLoop() {
	newTicker = make(chan *Ticker)
	go tickerLoop()
}

// wakeLoop delivers ticks at scheduled times, sleeping until the right moment.
// If another, earlier Ticker is created while it sleeps, tickerLoop() will start a new
// wakeLoop but they will share the wakeUp channel and signal that this one
// is done by giving it a negative time request.
func wakeLoop(wakeMeAt chan int64, wakeUp chan bool) {
	for {
		wakeAt := <-wakeMeAt
		if wakeAt < 0 { // tickerLoop has started another wakeLoop
			return
		}
		now := Nanoseconds()
		if wakeAt > now {
			Sleep(wakeAt - now)
			now = Nanoseconds()
		}
		wakeUp <- true
	}
}

// A single tickerLoop serves all ticks to Tickers.  It waits for two events:
// either the creation of a new Ticker or a tick from the alarm,
// signalling a time to wake up one or more Tickers.
func tickerLoop() {
	// Represents the next alarm to be delivered.
	var alarm alarmer
	// All wakeLoops deliver wakeups to this channel.
	alarm.wakeUp = make(chan bool, 10)
	var now, prevTime, wakeTime int64
	var tickers *Ticker
	for {
		select {
		case t := <-newTicker:
			// Add Ticker to list
			t.next = tickers
			tickers = t
			// Arrange for a new alarm if this one precedes the existing one.
			alarm.set(t.nextTick)
		case <-alarm.wakeUp:
			now = Nanoseconds()
			// Ignore an old time due to a dying wakeLoop
			if now < prevTime {
				continue
			}
			wakeTime = now + 1e15 // very long in the future
			var prev *Ticker = nil
			// Scan list of tickers, delivering updates to those
			// that need it and determining the next wake time.
			// TODO(r): list should be sorted in time order.
			for t := tickers; t != nil; t = t.next {
				if _, ok := <-t.shutdown; ok {
					// Ticker is done; remove it from list.
					if prev == nil {
						tickers = t.next
					} else {
						prev.next = t.next
					}
					continue
				}
				if t.nextTick <= now {
					if len(t.c) == 0 {
						// Only send if there's room.  We must not block.
						// The channel is allocated with a one-element
						// buffer, which is sufficient: if he hasn't picked
						// up the last tick, no point in sending more.
						t.c <- now
					}
					t.nextTick += t.ns
					if t.nextTick <= now {
						// Still behind; advance in one big step.
						t.nextTick += (now - t.nextTick + t.ns) / t.ns * t.ns
					}
				}
				if t.nextTick < wakeTime {
					wakeTime = t.nextTick
				}
				prev = t
			}
			if tickers != nil {
				// Please send wakeup at earliest required time.
				// If there are no tickers, don't bother.
				alarm.wakeMeAt <- wakeTime
			} else {
				alarm.wakeTime = 0
			}
		}
		prevTime = now
	}
}

var onceStartTickerLoop sync.Once

// NewTicker returns a new Ticker containing a channel that will
// send the time, in nanoseconds, every ns nanoseconds.  It adjusts the
// intervals to make up for pauses in delivery of the ticks. The value of
// ns must be greater than zero; if not, NewTicker will panic.
func NewTicker(ns int64) *Ticker {
	if ns <= 0 {
		panic(os.ErrorString("non-positive interval for NewTicker"))
	}
	c := make(chan int64, 1) //  See comment on send in tickerLoop
	t := &Ticker{
		C:        c,
		c:        c,
		ns:       ns,
		shutdown: make(chan bool, 1),
		nextTick: Nanoseconds() + ns,
	}
	onceStartTickerLoop.Do(startTickerLoop)
	// must be run in background so global Tickers can be created
	go func() { newTicker <- t }()
	return t
}
<commit_msg>time: fix tick accuracy when using multiple Tickers<commit_after>// Copyright 2009 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 time

import (
	"os"
	"sync"
)

// A Ticker holds a synchronous channel that delivers `ticks' of a clock
// at intervals.
type Ticker struct {
	C        <-chan int64 // The channel on which the ticks are delivered.
	c        chan<- int64 // The same channel, but the end we use.
	ns       int64
	shutdown chan bool // Buffered channel used to signal shutdown.
	nextTick int64
	next     *Ticker
}

// Stop turns off a ticker.  After Stop, no more ticks will be sent.
func (t *Ticker) Stop() {
	// Make it non-blocking so multiple Stops don't block.
	_ = t.shutdown <- true
}

// Tick is a convenience wrapper for NewTicker providing access to the ticking
// channel only.  Useful for clients that have no need to shut down the ticker.
func Tick(ns int64) <-chan int64 {
	if ns <= 0 {
		return nil
	}
	return NewTicker(ns).C
}

type alarmer struct {
	wakeUp   chan bool // wakeup signals sent/received here
	wakeMeAt chan int64
	wakeTime int64
}

// Set alarm to go off at time ns, if not already set earlier.
func (a *alarmer) set(ns int64) {
	switch {
	case a.wakeTime > ns:
		// Next tick we expect is too late; shut down the late runner
		// and (after fallthrough) start a new wakeLoop.
		close(a.wakeMeAt)
		fallthrough
	case a.wakeMeAt == nil:
		// There's no wakeLoop, start one.
		a.wakeMeAt = make(chan int64)
		a.wakeUp = make(chan bool, 1)
		go wakeLoop(a.wakeMeAt, a.wakeUp)
		fallthrough
	case a.wakeTime == 0:
		// Nobody else is waiting; it's just us.
		a.wakeTime = ns
		a.wakeMeAt <- ns
	default:
		// There's already someone scheduled.
	}
}

// Channel to notify tickerLoop of new Tickers being created.
var newTicker chan *Ticker

func startTickerLoop() {
	newTicker = make(chan *Ticker)
	go tickerLoop()
}

// wakeLoop delivers ticks at scheduled times, sleeping until the right moment.
// If another, earlier Ticker is created while it sleeps, tickerLoop() will start a new
// wakeLoop and signal that this one is done by closing the wakeMeAt channel.
func wakeLoop(wakeMeAt chan int64, wakeUp chan bool) {
	for wakeAt := range wakeMeAt {
		Sleep(wakeAt - Nanoseconds())
		wakeUp <- true
	}
}

// A single tickerLoop serves all ticks to Tickers.  It waits for two events:
// either the creation of a new Ticker or a tick from the alarm,
// signalling a time to wake up one or more Tickers.
func tickerLoop() {
	// Represents the next alarm to be delivered.
	var alarm alarmer
	var now, wakeTime int64
	var tickers *Ticker
	for {
		select {
		case t := <-newTicker:
			// Add Ticker to list
			t.next = tickers
			tickers = t
			// Arrange for a new alarm if this one precedes the existing one.
			alarm.set(t.nextTick)
		case <-alarm.wakeUp:
			now = Nanoseconds()
			wakeTime = now + 1e15 // very long in the future
			var prev *Ticker = nil
			// Scan list of tickers, delivering updates to those
			// that need it and determining the next wake time.
			// TODO(r): list should be sorted in time order.
			for t := tickers; t != nil; t = t.next {
				if _, ok := <-t.shutdown; ok {
					// Ticker is done; remove it from list.
					if prev == nil {
						tickers = t.next
					} else {
						prev.next = t.next
					}
					continue
				}
				if t.nextTick <= now {
					if len(t.c) == 0 {
						// Only send if there's room.  We must not block.
						// The channel is allocated with a one-element
						// buffer, which is sufficient: if he hasn't picked
						// up the last tick, no point in sending more.
						t.c <- now
					}
					t.nextTick += t.ns
					if t.nextTick <= now {
						// Still behind; advance in one big step.
						t.nextTick += (now - t.nextTick + t.ns) / t.ns * t.ns
					}
				}
				if t.nextTick < wakeTime {
					wakeTime = t.nextTick
				}
				prev = t
			}
			if tickers != nil {
				// Please send wakeup at earliest required time.
				// If there are no tickers, don't bother.
				alarm.wakeTime = wakeTime
				alarm.wakeMeAt <- wakeTime
			} else {
				alarm.wakeTime = 0
			}
		}
	}
}

var onceStartTickerLoop sync.Once

// NewTicker returns a new Ticker containing a channel that will
// send the time, in nanoseconds, every ns nanoseconds.  It adjusts the
// intervals to make up for pauses in delivery of the ticks. The value of
// ns must be greater than zero; if not, NewTicker will panic.
func NewTicker(ns int64) *Ticker {
	if ns <= 0 {
		panic(os.ErrorString("non-positive interval for NewTicker"))
	}
	c := make(chan int64, 1) //  See comment on send in tickerLoop
	t := &Ticker{
		C:        c,
		c:        c,
		ns:       ns,
		shutdown: make(chan bool, 1),
		nextTick: Nanoseconds() + ns,
	}
	onceStartTickerLoop.Do(startTickerLoop)
	// must be run in background so global Tickers can be created
	go func() { newTicker <- t }()
	return t
}
<|endoftext|>


<commit_before>package nats

import (
	"testing"
	"time"
)

func startReconnectServer(t *testing.T) *server {
	return startServer(t, 22222, "")
}

func TestReconnectDisallowedFlags(t *testing.T) {
	ts := startReconnectServer(t)
	ch := make(chan bool)
	opts := DefaultOptions
	opts.Url = "nats://localhost:22222"
	opts.AllowReconnect = false
	opts.ClosedCB = func(_ *Conn) {
		ch <- true
	}
	nc, err := opts.Connect()
	if err != nil {
		t.Fatalf("Should have connected ok: %v", err)
	}

	ts.stopServer()
	if e := wait(ch); e != nil {
		t.Fatal("Did not trigger ClosedCB correctly")
	}
	nc.Close()
}

func TestReconnectAllowedFlags(t *testing.T) {
	ts := startReconnectServer(t)
	ch := make(chan bool)
	opts := DefaultOptions
	opts.Url = "nats://localhost:22222"
	opts.AllowReconnect = true
	opts.MaxReconnect = 2
	opts.ReconnectWait = 1 * time.Second

	opts.ClosedCB = func(_ *Conn) {
		ch <- true
	}
	nc, err := opts.Connect()
	if err != nil {
		t.Fatalf("Should have connected ok: %v", err)
	}
	ts.stopServer()

	// We want wait to timeout here, and the connection
	// should not trigger the Close CB.
	if e := wait(ch); e == nil {
		t.Fatal("Triggered ClosedCB incorrectly")
	}
	if !nc.isReconnecting() {
		t.Fatal("Expected to be in a reconnecting state")
	}

	// clear the CloseCB since ch will block
	nc.Opts.ClosedCB = nil
	nc.Close()

}

var reconnectOpts = Options{
	Url:            "nats://localhost:22222",
	AllowReconnect: true,
	MaxReconnect:   10,
	ReconnectWait:  100 * time.Millisecond,
	Timeout:        DefaultTimeout,
}

func TestBasicReconnectFunctionality(t *testing.T) {
	ts := startReconnectServer(t)
	ch := make(chan bool)

	opts := reconnectOpts
	nc, _ := opts.Connect()
	ec, err := NewEncodedConn(nc, "default")
	if err != nil {
		t.Fatalf("Failed to create an encoded connection: %v\n", err)
	}

	testString := "bar"
	ec.Subscribe("foo", func(s string) {
		if s != testString {
			t.Fatal("String doesn't match")
		}
		ch <- true
	})
	ec.Flush()

	ts.stopServer()
	// server is stopped here...

	dch := make(chan bool)
	opts.DisconnectedCB = func(_ *Conn) {
		dch <- true
	}
	wait(dch)

	if err := ec.Publish("foo", testString); err != nil {
		t.Fatalf("Failed to publish message: %v\n", err)
	}

	ts = startReconnectServer(t)
	defer ts.stopServer()

	if err := ec.FlushTimeout(5 * time.Second); err != nil {
		t.Fatalf("Error on Flush: %v", err)
	}

	if e := wait(ch); e != nil {
		t.Fatal("Did not receive our message")
	}

	expectedReconnectCount := uint64(1)
	if ec.Conn.Stats.Reconnects != expectedReconnectCount {
		t.Fatalf("Reconnect count incorrect: %d vs %d\n",
			ec.Conn.Stats.Reconnects, expectedReconnectCount)
	}

	nc.Close()
}

func TestExtendedReconnectFunctionality(t *testing.T) {
	ts := startReconnectServer(t)
	cbCalled := false
	rcbCalled := false

	opts := reconnectOpts
	dch := make(chan bool)
	opts.DisconnectedCB = func(_ *Conn) {
		cbCalled = true
		dch <- true
	}
	rch := make(chan bool)
	opts.ReconnectedCB = func(_ *Conn) {
		rcbCalled = true
		rch <- true
	}
	nc, err := opts.Connect()
	if err != nil {
		t.Fatalf("Should have connected ok: %v", err)
	}
	ec, err := NewEncodedConn(nc, "default")
	if err != nil {
		t.Fatalf("Failed to create an encoded connection: %v\n", err)
	}
	testString := "bar"
	received := 0

	ec.Subscribe("foo", func(s string) {
		received += 1
	})

	sub, _ := ec.Subscribe("foobar", func(s string) {
		received += 1
	})

	ec.Publish("foo", testString)
	ec.Flush()

	ts.stopServer()
	// server is stopped here..

	// wait for disconnect
	if e := waitTime(dch, 2*time.Second); e != nil {
		t.Fatal("Did not receive a disconnect callback message")
	}

	// Sub while disconnected
	ec.Subscribe("bar", func(s string) {
		received += 1
	})

	// Unsub while disconnected
	sub.Unsubscribe()

	if err = ec.Publish("foo", testString); err != nil {
		t.Fatalf("Received an error after disconnect: %v\n", err)
	}

	if err = ec.Publish("bar", testString); err != nil {
		t.Fatalf("Received an error after disconnect: %v\n", err)
	}

	ts = startReconnectServer(t)
	defer ts.stopServer()

	// server is restarted here..
	// wait for reconnect
	if e := waitTime(rch, 2*time.Second); e != nil {
		t.Fatal("Did not receive a reconnect callback message")
	}

	if err = ec.Publish("foobar", testString); err != nil {
		t.Fatalf("Received an error after server restarted: %v\n", err)
	}

	if err = ec.Publish("foo", testString); err != nil {
		t.Fatalf("Received an error after server restarted: %v\n", err)
	}

	ch := make(chan bool)
	ec.Subscribe("done", func(b bool) {
		ch <- true
	})
	ec.Publish("done", true)

	if e := wait(ch); e != nil {
		t.Fatal("Did not receive our message")
	}

	if received != 4 {
		t.Fatalf("Received != %d, equals %d\n", 4, received)
	}

	if !cbCalled {
		t.Fatal("Did not have DisconnectedCB called")
	}
	if !rcbCalled {
		t.Fatal("Did not have ReconnectedCB called")
	}
}

func TestParseStateReconnectFunctionality(t *testing.T) {
	ts := startReconnectServer(t)
	ch := make(chan bool)

	opts := reconnectOpts
	nc, _ := opts.Connect()
	ec, err := NewEncodedConn(nc, "default")
	if err != nil {
		t.Fatalf("Failed to create an encoded connection: %v\n", err)
	}

	testString := "bar"
	ec.Subscribe("foo", func(s string) {
		if s != testString {
			t.Fatal("String doesn't match")
		}
		ch <- true
	})
	ec.Flush()

	// Simulate partialState, this needs to be cleared
	nc.ps.state = OP_PON

	ts.stopServer()
	// server is stopped here...

	dch := make(chan bool)
	opts.DisconnectedCB = func(_ *Conn) {
		dch <- true
	}
	wait(dch)

	if err := ec.Publish("foo", testString); err != nil {
		t.Fatalf("Failed to publish message: %v\n", err)
	}

	ts = startReconnectServer(t)
	defer ts.stopServer()

	if err := ec.FlushTimeout(5 * time.Second); err != nil {
		t.Fatalf("Error on Flush: %v", err)
	}

	if e := wait(ch); e != nil {
		t.Fatal("Did not receive our message")
	}

	expectedReconnectCount := uint64(1)
	if ec.Conn.Stats.Reconnects != expectedReconnectCount {
		t.Fatalf("Reconnect count incorrect: %d vs %d\n",
			ec.Conn.Stats.Reconnects, expectedReconnectCount)
	}

	nc.Close()
}

func TestIsClosed(t *testing.T) {
	ts := startReconnectServer(t)
	nc := newConnection(t)
	if nc.IsClosed() == true {
		t.Fatalf("IsClosed returned true when the connection is still open.")
	}
	ts.stopServer()
	if nc.IsClosed() == true {
		t.Fatalf("IsClosed returned true when the connection is still open.")
	}
	ts = startReconnectServer(t)
	if nc.IsClosed() == true {
		t.Fatalf("IsClosed returned true when the connection is still open.")
	}
	nc.Close()
	if nc.IsClosed() == false {
		t.Fatalf("IsClosed returned false after Close() was called.")
	}
	ts.stopServer()
}
<commit_msg>Fix data races<commit_after>package nats

import (
	"sync/atomic"
	"testing"
	"time"
)

func startReconnectServer(t *testing.T) *server {
	return startServer(t, 22222, "")
}

func TestReconnectDisallowedFlags(t *testing.T) {
	ts := startReconnectServer(t)
	ch := make(chan bool)
	opts := DefaultOptions
	opts.Url = "nats://localhost:22222"
	opts.AllowReconnect = false
	opts.ClosedCB = func(_ *Conn) {
		ch <- true
	}
	nc, err := opts.Connect()
	if err != nil {
		t.Fatalf("Should have connected ok: %v", err)
	}

	ts.stopServer()
	if e := wait(ch); e != nil {
		t.Fatal("Did not trigger ClosedCB correctly")
	}
	nc.Close()
}

func TestReconnectAllowedFlags(t *testing.T) {
	ts := startReconnectServer(t)
	ch := make(chan bool)
	opts := DefaultOptions
	opts.Url = "nats://localhost:22222"
	opts.AllowReconnect = true
	opts.MaxReconnect = 2
	opts.ReconnectWait = 1 * time.Second

	opts.ClosedCB = func(_ *Conn) {
		ch <- true
	}
	nc, err := opts.Connect()
	if err != nil {
		t.Fatalf("Should have connected ok: %v", err)
	}
	ts.stopServer()

	// We want wait to timeout here, and the connection
	// should not trigger the Close CB.
	if e := wait(ch); e == nil {
		t.Fatal("Triggered ClosedCB incorrectly")
	}
	if !nc.isReconnecting() {
		t.Fatal("Expected to be in a reconnecting state")
	}

	// clear the CloseCB since ch will block
	nc.Opts.ClosedCB = nil
	nc.Close()

}

var reconnectOpts = Options{
	Url:            "nats://localhost:22222",
	AllowReconnect: true,
	MaxReconnect:   10,
	ReconnectWait:  100 * time.Millisecond,
	Timeout:        DefaultTimeout,
}

func TestBasicReconnectFunctionality(t *testing.T) {
	ts := startReconnectServer(t)
	ch := make(chan bool)

	opts := reconnectOpts
	nc, _ := opts.Connect()
	ec, err := NewEncodedConn(nc, "default")
	if err != nil {
		t.Fatalf("Failed to create an encoded connection: %v\n", err)
	}

	testString := "bar"
	ec.Subscribe("foo", func(s string) {
		if s != testString {
			t.Fatal("String doesn't match")
		}
		ch <- true
	})
	ec.Flush()

	ts.stopServer()
	// server is stopped here...

	dch := make(chan bool)
	opts.DisconnectedCB = func(_ *Conn) {
		dch <- true
	}
	wait(dch)

	if err := ec.Publish("foo", testString); err != nil {
		t.Fatalf("Failed to publish message: %v\n", err)
	}

	ts = startReconnectServer(t)
	defer ts.stopServer()

	if err := ec.FlushTimeout(5 * time.Second); err != nil {
		t.Fatalf("Error on Flush: %v", err)
	}

	if e := wait(ch); e != nil {
		t.Fatal("Did not receive our message")
	}

	expectedReconnectCount := uint64(1)
	if ec.Conn.Stats.Reconnects != expectedReconnectCount {
		t.Fatalf("Reconnect count incorrect: %d vs %d\n",
			ec.Conn.Stats.Reconnects, expectedReconnectCount)
	}

	nc.Close()
}

func TestExtendedReconnectFunctionality(t *testing.T) {
	ts := startReconnectServer(t)

	opts := reconnectOpts
	dch := make(chan bool)
	opts.DisconnectedCB = func(_ *Conn) {
		dch <- true
	}
	rch := make(chan bool)
	opts.ReconnectedCB = func(_ *Conn) {
		rch <- true
	}
	nc, err := opts.Connect()
	if err != nil {
		t.Fatalf("Should have connected ok: %v", err)
	}
	ec, err := NewEncodedConn(nc, "default")
	if err != nil {
		t.Fatalf("Failed to create an encoded connection: %v\n", err)
	}
	testString := "bar"
	received := int32(0)

	ec.Subscribe("foo", func(s string) {
		atomic.AddInt32(&received, 1)
	})

	sub, _ := ec.Subscribe("foobar", func(s string) {
		atomic.AddInt32(&received, 1)
	})

	ec.Publish("foo", testString)
	ec.Flush()

	ts.stopServer()
	// server is stopped here..

	// wait for disconnect
	if e := waitTime(dch, 2*time.Second); e != nil {
		t.Fatal("Did not receive a disconnect callback message")
	}

	// Sub while disconnected
	ec.Subscribe("bar", func(s string) {
		atomic.AddInt32(&received, 1)
	})

	// Unsub while disconnected
	sub.Unsubscribe()

	if err = ec.Publish("foo", testString); err != nil {
		t.Fatalf("Received an error after disconnect: %v\n", err)
	}

	if err = ec.Publish("bar", testString); err != nil {
		t.Fatalf("Received an error after disconnect: %v\n", err)
	}

	ts = startReconnectServer(t)
	defer ts.stopServer()

	// server is restarted here..
	// wait for reconnect
	if e := waitTime(rch, 2*time.Second); e != nil {
		t.Fatal("Did not receive a reconnect callback message")
	}

	if err = ec.Publish("foobar", testString); err != nil {
		t.Fatalf("Received an error after server restarted: %v\n", err)
	}

	if err = ec.Publish("foo", testString); err != nil {
		t.Fatalf("Received an error after server restarted: %v\n", err)
	}

	ch := make(chan bool)
	ec.Subscribe("done", func(b bool) {
		ch <- true
	})
	ec.Publish("done", true)

	if e := wait(ch); e != nil {
		t.Fatal("Did not receive our message")
	}

	if received != 4 {
		t.Fatalf("Received != %d, equals %d\n", 4, received)
	}
}

func TestParseStateReconnectFunctionality(t *testing.T) {
	ts := startReconnectServer(t)
	ch := make(chan bool)

	opts := reconnectOpts
	nc, _ := opts.Connect()
	ec, err := NewEncodedConn(nc, "default")
	if err != nil {
		t.Fatalf("Failed to create an encoded connection: %v\n", err)
	}

	testString := "bar"
	ec.Subscribe("foo", func(s string) {
		if s != testString {
			t.Fatal("String doesn't match")
		}
		ch <- true
	})
	ec.Flush()

	// Simulate partialState, this needs to be cleared
	nc.mu.Lock()
	nc.ps.state = OP_PON
	nc.mu.Unlock()

	ts.stopServer()
	// server is stopped here...

	dch := make(chan bool)
	opts.DisconnectedCB = func(_ *Conn) {
		dch <- true
	}
	wait(dch)

	if err := ec.Publish("foo", testString); err != nil {
		t.Fatalf("Failed to publish message: %v\n", err)
	}

	ts = startReconnectServer(t)
	defer ts.stopServer()

	if err := ec.FlushTimeout(5 * time.Second); err != nil {
		t.Fatalf("Error on Flush: %v", err)
	}

	if e := wait(ch); e != nil {
		t.Fatal("Did not receive our message")
	}

	expectedReconnectCount := uint64(1)
	if ec.Conn.Stats.Reconnects != expectedReconnectCount {
		t.Fatalf("Reconnect count incorrect: %d vs %d\n",
			ec.Conn.Stats.Reconnects, expectedReconnectCount)
	}

	nc.Close()
}

func TestIsClosed(t *testing.T) {
	ts := startReconnectServer(t)
	nc := newConnection(t)
	if nc.IsClosed() == true {
		t.Fatalf("IsClosed returned true when the connection is still open.")
	}
	ts.stopServer()
	if nc.IsClosed() == true {
		t.Fatalf("IsClosed returned true when the connection is still open.")
	}
	ts = startReconnectServer(t)
	if nc.IsClosed() == true {
		t.Fatalf("IsClosed returned true when the connection is still open.")
	}
	nc.Close()
	if nc.IsClosed() == false {
		t.Fatalf("IsClosed returned false after Close() was called.")
	}
	ts.stopServer()
}
<|endoftext|>


<commit_before>package main

// finds types.. O(n^2)
// looks for 0.01% (1 in 10,000) frequency or more
import (
	"bufio"
	"compress/gzip"
	"fmt"
	"log"
	"os"
	"sort"
	"strconv"
	"strings"

	"github.com/xrash/smetrics"
)

type pair struct {
	word  string
	count int
}

type counts []pair

func (s counts) Len() int      { return len(s) }
func (s counts) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s counts) Less(i, j int) bool {
	if s[i].count < s[j].count {
		return true
	}
	if s[i].count > s[j].count {
		return false
	}
	return s[i].word < s[j].word
}

// LoadCSV loads in a CSV in form of WORD,COUNT
func loadCSV(fname string) (counts, error) {
	fi, err := os.Open(fname)
	if err != nil {
		return nil, err
	}
	defer fi.Close()
	fizip, err := gzip.NewReader(fi)
	if err != nil {
		return nil, err
	}
	defer fizip.Close()
	words := make(counts, 0, 5000000)
	scanner := bufio.NewScanner(fizip)
	for scanner.Scan() {
		line := scanner.Text()
		parts := strings.SplitN(line, ",", 2)
		if len(parts) != 2 {
			return nil, fmt.Errorf("Got extra junk in line: %q", line)
		}
		c, err := strconv.Atoi(parts[1])
		if err != nil {
			return nil, fmt.Errorf("Number conversion failed: %q", line)
		}
		words = append(words, pair{parts[0], c})
	}
	if err := scanner.Err(); err != nil {
		return nil, err
	}
	sort.Sort(sort.Reverse(words))
	return words, nil
}

// LoadWordList loads in a list of known-good words
func LoadWordList(fname string) (map[string]bool, error) {
	fi, err := os.Open(fname)
	if err != nil {
		return nil, err
	}
	defer fi.Close()
	out := make(map[string]bool)
	intro := true
	scanner := bufio.NewScanner(fi)
	for scanner.Scan() {
		line := scanner.Text()
		if intro {
			if line == "---" {
				intro = false
			}
			continue
		}
		out[strings.ToLower(line)] = true
	}
	if err := scanner.Err(); err != nil {
		return nil, err
	}
	return out, nil
}

func main() {

	// load known-good words`
	truewords, err := LoadWordList("dict.txt")
	if err != nil {
		log.Fatalf("Unable to read wordlist")
	}
	log.Printf("Got %d real words from dictionary", len(truewords))

	// load frequency counts
	words, err := loadCSV("RC_2015-total.csv.gz")
	if err != nil {
		log.Fatalf("Unable to freq counts: %s", err)
	}

	// make total count
	total := 0
	sum := 0
	for _, kv := range words {
		total += kv.count
	}
	log.Printf("Got %d uniques, %d total", len(words), total)

	for top := 0; top < len(words); top++ {
		a := words[top]
		sum += a.count
		// must have at least this many occurances to
		// have an entry

		// TODO: what is the break point?  A CDF/PDF
		// would be better instead of absolute
		// TODO: move comparison out of loop
		if a.count < 20000 {
			break
		}
		aword := a.word
		for bottom := top + 1; bottom < len(words); bottom++ {
			b := words[bottom]

			// misspelling must occur twice
			// TODO: again this is a fixed point in the list
			if b.count < 2 {
				break
			}
			ratio := 100.0 * float64(b.count) / float64(a.count)

			// great than one percent probably means
			// bword is just a different work that is similar
			if ratio > 1.0 {
				continue
			}
			// if less than 0.01% ignore, too rare
			if ratio < 0.01 {
				break
			}

			bword := b.word

			// handle "foobar"/"foobars"
			if strings.HasPrefix(aword, bword) || strings.HasPrefix(bword, aword) {
				continue
			}

			// handle "foobars", "foobarr"
			if aword[:len(aword)-1] == bword[:len(bword)-1] {
				continue
			}
			val := smetrics.JaroWinkler(aword, bword, 0.7, 4)
			if val > 0.96 {
				fmt.Printf("%s,%s,%d,%f,%d,%d,%d,%f,%f\n",
					aword, bword,
					top, 100.0*float64(sum)/float64(total), bottom,
					a.count, b.count, ratio,
					val)
			}
		}
	}
}
<commit_msg>parameterize<commit_after>package main

// finds types.. O(n^2)
// looks for 0.01% (1 in 10,000) frequency or more
import (
	"bufio"
	"compress/gzip"
	"flag"
	"fmt"
	"log"
	"os"
	"sort"
	"strconv"
	"strings"

	"github.com/xrash/smetrics"
)

type pair struct {
	word  string
	count int
}

type counts []pair

func (s counts) Len() int      { return len(s) }
func (s counts) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s counts) Less(i, j int) bool {
	if s[i].count < s[j].count {
		return true
	}
	if s[i].count > s[j].count {
		return false
	}
	return s[i].word < s[j].word
}

// LoadCSV loads in a CSV in form of WORD,COUNT
func loadCSV(fname string) (counts, error) {
	fi, err := os.Open(fname)
	if err != nil {
		return nil, err
	}
	defer fi.Close()
	fizip, err := gzip.NewReader(fi)
	if err != nil {
		return nil, err
	}
	defer fizip.Close()
	words := make(counts, 0, 5000000)
	scanner := bufio.NewScanner(fizip)
	for scanner.Scan() {
		line := scanner.Text()
		parts := strings.SplitN(line, ",", 2)
		if len(parts) != 2 {
			return nil, fmt.Errorf("Got extra junk in line: %q", line)
		}
		c, err := strconv.Atoi(parts[1])
		if err != nil {
			return nil, fmt.Errorf("Number conversion failed: %q", line)
		}
		words = append(words, pair{parts[0], c})
	}
	if err := scanner.Err(); err != nil {
		return nil, err
	}
	sort.Sort(sort.Reverse(words))
	return words, nil
}

// LoadWordList loads in a list of known-good words
func LoadWordList(fname string) (map[string]bool, error) {
	fi, err := os.Open(fname)
	if err != nil {
		return nil, err
	}
	defer fi.Close()
	out := make(map[string]bool)
	intro := true
	scanner := bufio.NewScanner(fi)
	for scanner.Scan() {
		line := scanner.Text()
		if intro {
			if line == "---" {
				intro = false
			}
			continue
		}
		out[strings.ToLower(line)] = true
	}
	if err := scanner.Err(); err != nil {
		return nil, err
	}
	return out, nil
}

func main() {
	dictfile := flag.String("d", "dict.txt", "aspell wordlist")
	//outfile := flag.String("o", "RC_2015-score.csv", "outfile")
	infile := flag.String("i", "RC_2015-total.csv.gz", "infile")
	minScore := flag.Float64("minscore", 0.96, "min Jaro-Winkler score")
	minRatio := flag.Float64("minratio", 0.01, "error ratio")
	flag.Parse()
	// load known-good words`
	truewords, err := LoadWordList(*dictfile)
	if err != nil {
		log.Fatalf("Unable to read wordlist")
	}
	log.Printf("Got %d real words from dictionary", len(truewords))

	// load frequency counts
	words, err := loadCSV(*infile)
	if err != nil {
		log.Fatalf("Unable to freq counts: %s", err)
	}

	// make total count
	total := 0
	sum := 0
	for _, kv := range words {
		total += kv.count
	}
	log.Printf("Got %d uniques, %d total", len(words), total)

	for top := 0; top < len(words); top++ {
		a := words[top]
		sum += a.count
		cdf := 100.0 * float64(sum) / float64(total)
		// must have at least this many occurances to
		// have an entry

		// TODO: what is the break point?  A CDF/PDF
		// would be better instead of absolute
		// TODO: move comparison out of loop
		if a.count < 20000 {
			break
		}
		aword := a.word
		for bottom := top + 1; bottom < len(words); bottom++ {
			b := words[bottom]
			// misspelling must occur twice
			// TODO: again this is a fixed point in the list
			if b.count < 2 {
				break
			}
			ratio := 100.0 * float64(b.count) / float64(a.count)

			// great than one percent probably means
			// bword is just a different work that is similar
			if ratio > 1.0 {
				continue
			}
			// if less than 0.01% ignore, too rare
			if ratio < *minRatio {
				break
			}

			bword := b.word

			// handle "foobar"/"foobars"
			if strings.HasPrefix(aword, bword) || strings.HasPrefix(bword, aword) {
				continue
			}

			// handle "foobars", "foobarr"
			if aword[:len(aword)-1] == bword[:len(bword)-1] {
				continue
			}
			val := smetrics.JaroWinkler(aword, bword, 0.7, 4)
			if val >= *minScore {
				fmt.Printf("%s,%s,%d,%f,%d,%d,%d,%f,%f\n",
					aword, bword,
					top, cdf, a.count,
					bottom, b.count, ratio,
					val)
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net"
	"os"
	"path/filepath"
)

/*
	Parameter boundaries do not include the command or collection

	This has one parameter if Command.CollectionRequired is true
	otherwise it is considered two.
	> ADD foobar "this item"
*/
type Command struct {
	Name        string
	Parameters  []CommandParameter
	HelpMessage string
	Callback    func([]interface{}, net.Conn, *Collection)
}

var Commands map[string]Command

func init() {
	Commands = map[string]Command{}
	Commands["create"] = Command{
		Name:        "create",
		HelpMessage: "Create a new collection",
		Parameters: []CommandParameter{
			CommandParameter{
				Name:     "name",
				Type:     "collection",
				Required: true,
			},
		},
		Callback: func(params []interface{}, conn net.Conn, coll *Collection) {
			perms := os.FileMode(0700)
			os.MkdirAll(coll.DataPath, perms)
			os.MkdirAll(coll.ConfigPath, perms)
			coll.SetConfig("maxitems", "100")
			conn.Write([]byte("+OK\r\n"))
		},
	}
	Commands["delete"] = Command{
		Name:        "delete",
		HelpMessage: "Delete an existing collection",
		Parameters: []CommandParameter{
			CommandParameter{
				Name:     "name",
				Type:     "collection",
				Required: true,
			},
		},
		Callback: func(params []interface{}, conn net.Conn, coll *Collection) {
			os.RemoveAll(coll.BasePath)
			conn.Write([]byte("+OK\r\n"))
		},
	}

	Commands["get"] = Command{
		Name:        "get",
		HelpMessage: "Get all items in collection in ascending order",
		Parameters: []CommandParameter{
			CommandParameter{
				Name:     "name",
				Type:     "collection",
				Required: true,
			},
		},
		Callback: func(params []interface{}, conn net.Conn, coll *Collection) {
			files := coll.GetDataFiles()
			streamFiles(files, conn)
		},
	}

	Commands["add"] = Command{
		Name:        "add",
		HelpMessage: "Add item in collection",
		Parameters: []CommandParameter{
			CommandParameter{
				Name:     "name",
				Type:     "collection",
				Required: true,
			},
			CommandParameter{
				Name:     "data",
				Type:     "binary",
				Required: false,
			},
		},
		Callback: func(params []interface{}, conn net.Conn, coll *Collection) {
			filename := timestamp()
			fullFilePath := filepath.Join(coll.DataPath, filename)
			file, err := os.Create(fullFilePath)
			if err != nil {
				conn.Write([]byte("-cannot add item\r\n"))
			} else {
				file.Chmod(os.FileMode(0700))
				if len(params) > 0 {
					fmt.Fprint(file, params[0])
				}
				file.Close()
			}
			conn.Write([]byte("+OK\r\n"))
		},
	}

	Commands["ping"] = Command{
		Name:        "ping",
		HelpMessage: "Ping reccs for life",
		Parameters:  []CommandParameter{},
		Callback: func(params []interface{}, conn net.Conn, coll *Collection) {
			conn.Write([]byte("+PONG\r\n"))
		},
	}
}
<commit_msg>add command now properly asserts type to write<commit_after>package main

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

/*
	Parameter boundaries do not include the command or collection

	This has one parameter if Command.CollectionRequired is true
	otherwise it is considered two.
	> ADD foobar "this item"
*/
type Command struct {
	Name        string
	Parameters  []CommandParameter
	HelpMessage string
	Callback    func([]interface{}, net.Conn, *Collection)
}

var Commands map[string]Command

func init() {
	Commands = map[string]Command{}
	Commands["create"] = Command{
		Name:        "create",
		HelpMessage: "Create a new collection",
		Parameters: []CommandParameter{
			CommandParameter{
				Name:     "name",
				Type:     "collection",
				Required: true,
			},
		},
		Callback: func(params []interface{}, conn net.Conn, coll *Collection) {
			perms := os.FileMode(0700)
			os.MkdirAll(coll.DataPath, perms)
			os.MkdirAll(coll.ConfigPath, perms)
			coll.SetConfig("maxitems", "100")
			conn.Write([]byte("+OK\r\n"))
		},
	}
	Commands["delete"] = Command{
		Name:        "delete",
		HelpMessage: "Delete an existing collection",
		Parameters: []CommandParameter{
			CommandParameter{
				Name:     "name",
				Type:     "collection",
				Required: true,
			},
		},
		Callback: func(params []interface{}, conn net.Conn, coll *Collection) {
			os.RemoveAll(coll.BasePath)
			conn.Write([]byte("+OK\r\n"))
		},
	}

	Commands["get"] = Command{
		Name:        "get",
		HelpMessage: "Get all items in collection in ascending order",
		Parameters: []CommandParameter{
			CommandParameter{
				Name:     "name",
				Type:     "collection",
				Required: true,
			},
		},
		Callback: func(params []interface{}, conn net.Conn, coll *Collection) {
			files := coll.GetDataFiles()
			streamFiles(files, conn)
		},
	}

	Commands["add"] = Command{
		Name:        "add",
		HelpMessage: "Add item in collection",
		Parameters: []CommandParameter{
			CommandParameter{
				Name:     "name",
				Type:     "collection",
				Required: true,
			},
			CommandParameter{
				Name:     "data",
				Type:     "binary",
				Required: false,
			},
		},
		Callback: func(params []interface{}, conn net.Conn, coll *Collection) {
			filename := timestamp()
			fullFilePath := filepath.Join(coll.DataPath, filename)
			file, err := os.Create(fullFilePath)
			if err != nil {
				conn.Write([]byte("-cannot add item\r\n"))
			} else {
				file.Chmod(os.FileMode(0700))
				if len(params) > 0 {
					file.Write(params[0].([]byte))
				}
				file.Close()
			}
			conn.Write([]byte("+OK\r\n"))
		},
	}

	Commands["ping"] = Command{
		Name:        "ping",
		HelpMessage: "Ping reccs for life",
		Parameters:  []CommandParameter{},
		Callback: func(params []interface{}, conn net.Conn, coll *Collection) {
			conn.Write([]byte("+PONG\r\n"))
		},
	}
}
<|endoftext|>


<commit_before>/*

For keeping a minimum running, perhaps when doing a routing table update, if destination hosts are all
 expired or about to expire we start more.

*/

package main

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	"github.com/iron-io/iron_go/cache"
	"github.com/iron-io/iron_go/worker"
	"github.com/iron-io/common"
	"log"
	"math/rand"
	// "net"
	"net/http"
	"net/url"
	"reflect"
	"strings"
	"time"
	"runtime"
	"flag"
//	"io/ioutil"
)

var config struct {
	Iron struct {
	Token      string `json:"token"`
	ProjectId  string `json:"project_id"`
} `json:"iron"`
	Logging       struct {
	To     string `json:"to"`
	Level  string `json:"level"`
	Prefix string `json:"prefix"`
}
}

//var routingTable = map[string]*Route{}
var icache = cache.New("routing-table")

func init() {

}

type Route struct {
	// TODO: Change destinations to a simple cache so it can expire entries after 55 minutes (the one we use in common?)
	Host         string `json:"host"`
	Destinations []string  `json:"destinations"`
	ProjectId    string  `json:"project_id"`
	Token        string  `json:"token"` // store this so we can queue up new workers on demand
	CodeName     string  `json:"code_name"`
}

// for adding new hosts
type Route2 struct {
	Host string `json:"host"`
	Dest string `json:"dest"`
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	log.Println("Running on", runtime.NumCPU(), "CPUs")

	var configFile string
	var env string
	flag.StringVar(&configFile, "c", "", "Config file name")
	// when this was e, it was erroring out.
	flag.StringVar(&env, "e2", "development", "environment")

	flag.Parse() // Scans the arg list and sets up flags

	// Deployer is now passing -c in since we're using upstart and it doesn't know what directory to run in
	if configFile == "" {
		configFile = "config_" + env + ".json"
	}

	common.LoadConfig("iron_mq", configFile, &config)
	common.SetLogLevel(config.Logging.Level)
	common.SetLogLocation(config.Logging.To, config.Logging.Prefix)

	icache.Settings.UseConfigMap(map[string]interface{}{"token": config.Iron.Token, "project_id": config.Iron.ProjectId})

	r := mux.NewRouter()
	s := r.Headers("Iron-Router", "").Subrouter()
	s.HandleFunc("/", AddWorker)
	r.HandleFunc("/addworker", AddWorker)

	r.HandleFunc("/", ProxyFunc)

	http.Handle("/", r)
	port := 80
	fmt.Println("listening and serving on port", port)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", port), nil))
}

func ProxyFunc(w http.ResponseWriter, req *http.Request) {
	fmt.Println("HOST:", req.Host)
	host := strings.Split(req.Host, ":")[0]

	// We look up the destinations in the routing table and there can be 3 possible scenarios:
	// 1) This host was never registered so we return 404
	// 2) This host has active workers so we do the proxy
	// 3) This host has no active workers so we queue one (or more) up and return a 503 or something with message that says "try again in a minute"
	//	route := routingTable[host]
	route, err := getRoute(host)
	// choose random dest
	if err != nil {
		common.SendError(w, 400, fmt.Sprintln("Host not configured or error!", err))
		return
	}
	//	if route == nil { // route.Host == "" {
	//		common.SendError(w, 400, fmt.Sprintln(w, "Host not configured!"))
	//		return
	//	}
	destIndex := rand.Intn(len(route.Destinations))
	destUrlString := route.Destinations[destIndex]
	// todo: should check if http:// already exists.
	destUrlString2 := "http://" + destUrlString
	destUrl, err := url.Parse(destUrlString2)
	if err != nil {
		fmt.Println("error!", err)
		panic(err)
	}
	fmt.Println("proxying to", destUrl)
	proxy := NewSingleHostReverseProxy(destUrl)
	err = proxy.ServeHTTP(w, req)
	if err != nil {
		fmt.Println("Error proxying!", err)
		etype := reflect.TypeOf(err)
		fmt.Println("err type:", etype)
		w.WriteHeader(http.StatusInternalServerError)
		// can't figure out how to compare types so comparing strings.... lame.
		if strings.Contains(etype.String(), "net.OpError") { // == reflect.TypeOf(net.OpError{}) { // couldn't figure out a better way to do this
			if len(route.Destinations) > 1 {
				fmt.Println("It's a network error, removing this destination from routing table.")
				route.Destinations = append(route.Destinations[:destIndex], route.Destinations[destIndex + 1:]...)
				err := putRoute(route)
				if err != nil {
					fmt.Println("couldn't update routing table 1", err)
					common.SendError(w, 400, fmt.Sprintln("couldn't update routing table 1", err))
					return
				}
				fmt.Println("New route:", route)
				return
			} else {
				fmt.Println("It's a network error and no other destinations available so we're going to remove it and start new task.")
				route.Destinations = append(route.Destinations[:destIndex], route.Destinations[destIndex + 1:]...)
				err := putRoute(route)
				if err != nil {
					fmt.Println("couldn't update routing table:", err)
					common.SendError(w, 400, fmt.Sprintln("couldn't update routing table", err))
					return
				}
				fmt.Println("New route:", route)
			}
			// start new worker
			payload := map[string]interface{}{
				"token":      route.Token,
				"project_id": route.ProjectId,
				"code_name":  route.CodeName,
			}
			workerapi := worker.New()
			workerapi.Settings.UseConfigMap(payload)
			jsonPayload, err := json.Marshal(payload)
			if err != nil {
				fmt.Println("Couldn't marshal json!", err)
				return
			}
			timeout := time.Second*120
			task := worker.Task{
				CodeName: route.CodeName,
				Payload:  string(jsonPayload),
				Timeout:  &timeout, // let's have these die quickly while testing
			}
			tasks := make([]worker.Task, 1)
			tasks[0] = task
			taskIds, err := workerapi.TaskQueue(tasks...)
			fmt.Println("Tasks queued.", taskIds)
			if err != nil {
				fmt.Println("Couldn't queue up worker!", err)
				return
			}
		}
		// start new worker if it's a connection error
		return
	}
	fmt.Println("Served!")
	// todo: how to handle destination failures. I got this in log output when testing a bad endpoint:
	// 2012/12/26 23:22:08 http: proxy error: dial tcp 127.0.0.1:8082: connection refused
}

// When a worker starts up, it calls this
func AddWorker(w http.ResponseWriter, req *http.Request) {
	log.Println("AddWorker called!")

//	s, err := ioutil.ReadAll(req.Body)
//	fmt.Println("req.body:", err, string(s))

	// get project id and token
	projectId := req.FormValue("project_id")
	token := req.FormValue("token")
	codeName := req.FormValue("code_name")
	fmt.Println("project_id:", projectId, "token:", token, "code_name:", codeName)

	// check header for what operation to perform
	routerHeader := req.Header.Get("Iron-Router")
	if routerHeader == "register" {
		route := Route{}
		if !common.ReadJSON(w, req, &route) {
			return
		}
		fmt.Println("body read into route:", route)
		route.ProjectId = projectId
		route.Token = token
		route.CodeName = codeName
		// todo: do we need to close body?
		err := putRoute(route)
		if err != nil {
			fmt.Println("couldn't register host:", err)
			common.SendError(w, 400, fmt.Sprintln("Could not register host!", err))
			return
		}
		fmt.Println("registered route:", route)
		fmt.Fprintln(w, "Host registered successfully.")

	} else {
		r2 := Route2{}
		if !common.ReadJSON(w, req, &r2) {
			return
		}
		// todo: do we need to close body?
		fmt.Println("DECODED:", r2)
		route, err := getRoute(r2.Host)
		//		route := routingTable[r2.Host]
		if err != nil {
			common.SendError(w, 400, fmt.Sprintln("This host is not registered!", err))
			return
			//			route = &Route{}
		}
		fmt.Println("ROUTE:", route)
		route.Destinations = append(route.Destinations, r2.Dest)
		fmt.Println("ROUTE new:", route)
		err = putRoute(route)
		if err != nil {
			fmt.Println("couldn't register host:", err)
			common.SendError(w, 400, fmt.Sprintln("Could not register host!", err))
			return
		}
		//		routingTable[r2.Host] = route
		//		fmt.Println("New routing table:", routingTable)
		fmt.Fprintln(w, "Worker added")
	}
}

func getRoute(host string) (Route, error) {
	rx, err := icache.Get(host)
	route := Route{}
	if err == nil {
		route = rx.(Route)
	}
	return route, err
}

func putRoute(route Route) (error) {
	item := cache.Item{}
	v, err := json.Marshal(route)
	if err != nil {
		return err
	}
	item.Value = string(v)
	err = icache.Put(route.Host, &item)
	return err
}
<commit_msg>more bullshit<commit_after>/*

For keeping a minimum running, perhaps when doing a routing table update, if destination hosts are all
 expired or about to expire we start more.

*/

package main

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	"github.com/iron-io/iron_go/cache"
	"github.com/iron-io/iron_go/worker"
	"github.com/iron-io/common"
	"log"
	"math/rand"
	// "net"
	"net/http"
	"net/url"
	"reflect"
	"strings"
	"time"
	"runtime"
	"flag"
//	"io/ioutil"
)

var config struct {
	Iron struct {
	Token      string `json:"token"`
	ProjectId  string `json:"project_id"`
} `json:"iron"`
	Logging       struct {
	To     string `json:"to"`
	Level  string `json:"level"`
	Prefix string `json:"prefix"`
}
}

//var routingTable = map[string]*Route{}
var icache = cache.New("routing-table")

func init() {

}

type Route struct {
	// TODO: Change destinations to a simple cache so it can expire entries after 55 minutes (the one we use in common?)
	Host         string `json:"host"`
	Destinations []string  `json:"destinations"`
	ProjectId    string  `json:"project_id"`
	Token        string  `json:"token"` // store this so we can queue up new workers on demand
	CodeName     string  `json:"code_name"`
}

// for adding new hosts
type Route2 struct {
	Host string `json:"host"`
	Dest string `json:"dest"`
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	log.Println("Running on", runtime.NumCPU(), "CPUs")

	var configFile string
	var env string
	flag.StringVar(&configFile, "c", "", "Config file name")
	// when this was e, it was erroring out.
	flag.StringVar(&env, "e2", "development", "environment")

	flag.Parse() // Scans the arg list and sets up flags

	// Deployer is now passing -c in since we're using upstart and it doesn't know what directory to run in
	if configFile == "" {
		configFile = "config_" + env + ".json"
	}

	common.LoadConfig("iron_mq", configFile, &config)
	common.SetLogLevel(config.Logging.Level)
	common.SetLogLocation(config.Logging.To, config.Logging.Prefix)

	icache.Settings.UseConfigMap(map[string]interface{}{"token": config.Iron.Token, "project_id": config.Iron.ProjectId})

	r := mux.NewRouter()
	s := r.Headers("Iron-Router", "").Subrouter()
	s.HandleFunc("/", AddWorker)
	r.HandleFunc("/addworker", AddWorker)

	r.HandleFunc("/", ProxyFunc)

	http.Handle("/", r)
	port := 80
	fmt.Println("listening and serving on port", port)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", port), nil))
}

func ProxyFunc(w http.ResponseWriter, req *http.Request) {
	fmt.Println("HOST:", req.Host)
	host := strings.Split(req.Host, ":")[0]

	// We look up the destinations in the routing table and there can be 3 possible scenarios:
	// 1) This host was never registered so we return 404
	// 2) This host has active workers so we do the proxy
	// 3) This host has no active workers so we queue one (or more) up and return a 503 or something with message that says "try again in a minute"
	//	route := routingTable[host]
	route, err := getRoute(host)
	// choose random dest
	if err != nil {
		common.SendError(w, 400, fmt.Sprintln("Host not configured or error!", err))
		return
	}
	//	if route == nil { // route.Host == "" {
	//		common.SendError(w, 400, fmt.Sprintln(w, "Host not configured!"))
	//		return
	//	}
	destIndex := rand.Intn(len(route.Destinations))
	destUrlString := route.Destinations[destIndex]
	// todo: should check if http:// already exists.
	destUrlString2 := "http://" + destUrlString
	destUrl, err := url.Parse(destUrlString2)
	if err != nil {
		fmt.Println("error!", err)
		panic(err)
	}
	fmt.Println("proxying to", destUrl)
	proxy := NewSingleHostReverseProxy(destUrl)
	err = proxy.ServeHTTP(w, req)
	if err != nil {
		fmt.Println("Error proxying!", err)
		etype := reflect.TypeOf(err)
		fmt.Println("err type:", etype)
		w.WriteHeader(http.StatusInternalServerError)
		// can't figure out how to compare types so comparing strings.... lame.
		if strings.Contains(etype.String(), "net.OpError") { // == reflect.TypeOf(net.OpError{}) { // couldn't figure out a better way to do this
			if len(route.Destinations) > 1 {
				fmt.Println("It's a network error, removing this destination from routing table.")
				route.Destinations = append(route.Destinations[:destIndex], route.Destinations[destIndex + 1:]...)
				err := putRoute(route)
				if err != nil {
					fmt.Println("couldn't update routing table 1", err)
					common.SendError(w, 400, fmt.Sprintln("couldn't update routing table 1", err))
					return
				}
				fmt.Println("New route:", route)
				return
			} else {
				fmt.Println("It's a network error and no other destinations available so we're going to remove it and start new task.")
				route.Destinations = append(route.Destinations[:destIndex], route.Destinations[destIndex + 1:]...)
				err := putRoute(route)
				if err != nil {
					fmt.Println("couldn't update routing table:", err)
					common.SendError(w, 400, fmt.Sprintln("couldn't update routing table", err))
					return
				}
				fmt.Println("New route:", route)
			}
			// start new worker
			payload := map[string]interface{}{
				"token":      route.Token,
				"project_id": route.ProjectId,
				"code_name":  route.CodeName,
			}
			workerapi := worker.New()
			workerapi.Settings.UseConfigMap(payload)
			jsonPayload, err := json.Marshal(payload)
			if err != nil {
				fmt.Println("Couldn't marshal json!", err)
				return
			}
			timeout := time.Second*120
			task := worker.Task{
				CodeName: route.CodeName,
				Payload:  string(jsonPayload),
				Timeout:  &timeout, // let's have these die quickly while testing
			}
			tasks := make([]worker.Task, 1)
			tasks[0] = task
			taskIds, err := workerapi.TaskQueue(tasks...)
			fmt.Println("Tasks queued.", taskIds)
			if err != nil {
				fmt.Println("Couldn't queue up worker!", err)
				return
			}
		}
		// start new worker if it's a connection error
		return
	}
	fmt.Println("Served!")
	// todo: how to handle destination failures. I got this in log output when testing a bad endpoint:
	// 2012/12/26 23:22:08 http: proxy error: dial tcp 127.0.0.1:8082: connection refused
}

// When a worker starts up, it calls this
func AddWorker(w http.ResponseWriter, req *http.Request) {
	log.Println("AddWorker called!")

//	s, err := ioutil.ReadAll(req.Body)
//	fmt.Println("req.body:", err, string(s))

	// get project id and token
	projectId := req.FormValue("project_id")
	token := req.FormValue("token")
	codeName := req.FormValue("code_name")
	fmt.Println("project_id:", projectId, "token:", token, "code_name:", codeName)

	// check header for what operation to perform
	routerHeader := req.Header.Get("Iron-Router")
	if routerHeader == "register" {
		route := Route{}
		if !common.ReadJSON(w, req, &route) {
			return
		}
		fmt.Println("body read into route:", route)
		route.ProjectId = projectId
		route.Token = token
		route.CodeName = codeName
		// todo: do we need to close body?
		err := putRoute(&route)
		if err != nil {
			fmt.Println("couldn't register host:", err)
			common.SendError(w, 400, fmt.Sprintln("Could not register host!", err))
			return
		}
		fmt.Println("registered route:", route)
		fmt.Fprintln(w, "Host registered successfully.")

	} else {
		r2 := Route2{}
		if !common.ReadJSON(w, req, &r2) {
			return
		}
		// todo: do we need to close body?
		fmt.Println("DECODED:", r2)
		route, err := getRoute(r2.Host)
		//		route := routingTable[r2.Host]
		if err != nil {
			common.SendError(w, 400, fmt.Sprintln("This host is not registered!", err))
			return
			//			route = &Route{}
		}
		fmt.Println("ROUTE:", route)
		route.Destinations = append(route.Destinations, r2.Dest)
		fmt.Println("ROUTE new:", route)
		err = putRoute(route)
		if err != nil {
			fmt.Println("couldn't register host:", err)
			common.SendError(w, 400, fmt.Sprintln("Could not register host!", err))
			return
		}
		//		routingTable[r2.Host] = route
		//		fmt.Println("New routing table:", routingTable)
		fmt.Fprintln(w, "Worker added")
	}
}

func getRoute(host string) (*Route, error) {
	rx, err := icache.Get(host)
	if err != nil {
		return nil, err
	}
	rx2 := []byte(rx.(string))
	route := Route{}
	err = json.Unmarshal(rx2, &route)
	if err == nil {
		route = rx.(Route)
	}
	return &route, err
}

func putRoute(route *Route) (error) {
	item := cache.Item{}
	v, err := json.Marshal(route)
	if err != nil {
		return err
	}
	item.Value = string(v)
	err = icache.Put(route.Host, &item)
	return err
}
<|endoftext|>


<commit_before><commit_msg>Broadcast inidices wait for all done<commit_after><|endoftext|>


<commit_before>package memcache

import (
	"fmt"
	memcache_impl "github.com/bradfitz/gomemcache/memcache"
	"github.com/pierrre/imageproxy"
)

type MemcacheCache struct {
	Prefix   string
	Memcache *memcache_impl.Client
}

func (cache *MemcacheCache) Get(key string) (image *imageproxy.Image, err error) {
	hashedKey := imageproxy.HashCacheKey(key)
	item, err := cache.Memcache.Get(hashedKey)
	if err != nil {
		fmt.Println(err)
		return
	}
	image = &imageproxy.Image{}
	err = image.Unserialize(item.Value)
	if err != nil {
		image = nil
	}
	return
}

func (cache *MemcacheCache) Set(key string, image *imageproxy.Image) (err error) {
	serialized, err := image.Serialize()
	if err != nil {
		return
	}
	hashedKey := imageproxy.HashCacheKey(key)
	item := &memcache_impl.Item{
		Key:   hashedKey,
		Value: serialized,
	}
	err = cache.Memcache.Set(item)
	return
}
<commit_msg>Implements prefix for memcache<commit_after>package memcache

import (
	"fmt"
	memcache_impl "github.com/bradfitz/gomemcache/memcache"
	"github.com/pierrre/imageproxy"
)

type MemcacheCache struct {
	Prefix   string
	Memcache *memcache_impl.Client
}

func (cache *MemcacheCache) Get(key string) (image *imageproxy.Image, err error) {
	hashedKey := imageproxy.HashCacheKey(key)
	item, err := cache.Memcache.Get(cache.Prefix + hashedKey)
	if err != nil {
		fmt.Println(err)
		return
	}
	image = &imageproxy.Image{}
	err = image.Unserialize(item.Value)
	if err != nil {
		image = nil
	}
	return
}

func (cache *MemcacheCache) Set(key string, image *imageproxy.Image) (err error) {
	serialized, err := image.Serialize()
	if err != nil {
		return
	}
	hashedKey := imageproxy.HashCacheKey(key)
	item := &memcache_impl.Item{
		Key:   cache.Prefix + hashedKey,
		Value: serialized,
	}
	err = cache.Memcache.Set(item)
	return
}
<|endoftext|>


<commit_before>package provider

import (
	"errors"
	"time"

	"github.com/prometheus/client_golang/prometheus"

	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/metrics"
	"github.com/go-kit/kit/metrics/discard"
	"github.com/go-kit/kit/metrics/dogstatsd"
	kitexp "github.com/go-kit/kit/metrics/expvar"
	"github.com/go-kit/kit/metrics/graphite"
	kitprom "github.com/go-kit/kit/metrics/prometheus"
	"github.com/go-kit/kit/metrics/statsd"
)

// Provider represents a union set of constructors and lifecycle management
// functions for each supported metrics backend. It should be used by those who
// need to easily swap out implementations, e.g. dynamically, or at a single
// point in an intermediating framework.
type Provider interface {
	NewCounter(name, help string) metrics.Counter
	NewHistogram(name, help string, min, max int64, sigfigs int, quantiles ...int) (metrics.Histogram, error)
	NewGauge(name, help string) metrics.Gauge
	Stop()
}

// NewGraphiteProvider will return a Provider implementation that is a simple
// wrapper around a graphite.Emitter. All metric names will be prefixed with the
// given value and data will be emitted once every interval. If no network value
// is given, it will default to "udp".
func NewGraphiteProvider(network, address, prefix string, interval time.Duration, logger log.Logger) (Provider, error) {
	if network == "" {
		network = "udp"
	}
	if address == "" {
		return nil, errors.New("address is required")
	}
	return graphiteProvider{
		e: graphite.NewEmitter(network, address, prefix, interval, logger),
	}, nil
}

type graphiteProvider struct {
	e *graphite.Emitter
}

var _ Provider = graphiteProvider{}

// NewCounter implements Provider. Help is ignored.
func (p graphiteProvider) NewCounter(name, _ string) metrics.Counter {
	return p.e.NewCounter(name)
}

// NewHistogram implements Provider. Help is ignored.
func (p graphiteProvider) NewHistogram(name, _ string, min, max int64, sigfigs int, quantiles ...int) (metrics.Histogram, error) {
	return p.e.NewHistogram(name, min, max, sigfigs, quantiles...)
}

// NewGauge implements Provider. Help is ignored.
func (p graphiteProvider) NewGauge(name, _ string) metrics.Gauge {
	return p.e.NewGauge(name)
}

// Stop implements Provider.
func (p graphiteProvider) Stop() {
	p.e.Stop()
}

// NewStatsdProvider will return a Provider implementation that is a simple
// wrapper around a statsd.Emitter. All metric names will be prefixed with the
// given value and data will be emitted once every interval or when the buffer
// has reached its max size. If no network value is given, it will default to
// "udp".
func NewStatsdProvider(network, address, prefix string, interval time.Duration, logger log.Logger) (Provider, error) {
	if network == "" {
		network = "udp"
	}
	if address == "" {
		return nil, errors.New("address is required")
	}
	return statsdProvider{
		e: statsd.NewEmitter(network, address, prefix, interval, logger),
	}, nil
}

type statsdProvider struct {
	e *statsd.Emitter
}

var _ Provider = statsdProvider{}

// NewCounter implements Provider. Help is ignored.
func (p statsdProvider) NewCounter(name, _ string) metrics.Counter {
	return p.e.NewCounter(name)
}

// NewHistogram implements Provider. Help is ignored.
func (p statsdProvider) NewHistogram(name, _ string, min, max int64, sigfigs int, quantiles ...int) (metrics.Histogram, error) {
	return p.e.NewHistogram(name), nil
}

// NewGauge implements Provider. Help is ignored.
func (p statsdProvider) NewGauge(name, _ string) metrics.Gauge {
	return p.e.NewGauge(name)
}

// Stop will call the underlying statsd.Emitter's Stop method.
func (p statsdProvider) Stop() {
	p.e.Stop()
}

// NewDogStatsdProvider will return a Provider implementation that is a simple
// wrapper around a dogstatsd.Emitter. All metric names will be prefixed with
// the given value and data will be emitted once every interval or when the
// buffer has reached its max size. If no network value is given, it will
// default to "udp".
func NewDogStatsdProvider(network, address, prefix string, interval time.Duration, logger log.Logger) (Provider, error) {
	if network == "" {
		network = "udp"
	}
	if address == "" {
		return nil, errors.New("address is required")
	}
	return dogstatsdProvider{
		e: dogstatsd.NewEmitter(network, address, prefix, interval, logger),
	}, nil
}

type dogstatsdProvider struct {
	e *dogstatsd.Emitter
}

var _ Provider = dogstatsdProvider{}

// NewCounter implements Provider. Help is ignored.
func (p dogstatsdProvider) NewCounter(name, _ string) metrics.Counter {
	return p.e.NewCounter(name)
}

// NewHistogram implements Provider. Help is ignored.
func (p dogstatsdProvider) NewHistogram(name, _ string, min, max int64, sigfigs int, quantiles ...int) (metrics.Histogram, error) {
	return p.e.NewHistogram(name), nil
}

// NewGauge implements Provider. Help is ignored.
func (p dogstatsdProvider) NewGauge(name, _ string) metrics.Gauge {
	return p.e.NewGauge(name)
}

// Stop will call the underlying statsd.Emitter's Stop method.
func (p dogstatsdProvider) Stop() {
	p.e.Stop()
}

// NewExpvarProvider is a very thin wrapper over the expvar package.
// If a prefix is provided, it will prefix all metric names.
func NewExpvarProvider(prefix string) Provider {
	return expvarProvider{prefix: prefix}
}

type expvarProvider struct {
	prefix string
}

var _ Provider = expvarProvider{}

// NewCounter implements Provider. Help is ignored.
func (p expvarProvider) NewCounter(name, _ string) metrics.Counter {
	return kitexp.NewCounter(p.prefix + name)
}

// NewHistogram implements Provider. Help is ignored.
func (p expvarProvider) NewHistogram(name, _ string, min, max int64, sigfigs int, quantiles ...int) (metrics.Histogram, error) {
	return kitexp.NewHistogram(p.prefix+name, min, max, sigfigs, quantiles...), nil
}

// NewGauge implements Provider. Help is ignored.
func (p expvarProvider) NewGauge(name, _ string) metrics.Gauge {
	return kitexp.NewGauge(p.prefix + name)
}

// Stop is a no-op.
func (expvarProvider) Stop() {}

type prometheusProvider struct {
	namespace string
	subsystem string
}

var _ Provider = prometheusProvider{}

// NewPrometheusProvider returns a Prometheus provider that uses the provided
// namespace and subsystem for all metrics. Help is not provided, which is very
// much against Prometheus guidelines. Therefore, avoid using the Prometheus
// provider unless it is absolutely necessary; prefer constructing Prometheus
// metrics directly.
func NewPrometheusProvider(namespace, subsystem string) Provider {
	return prometheusProvider{
		namespace: namespace,
		subsystem: subsystem,
	}
}

// NewCounter implements Provider.
func (p prometheusProvider) NewCounter(name, help string) metrics.Counter {
	return kitprom.NewCounter(prometheus.CounterOpts{
		Namespace: p.namespace,
		Subsystem: p.subsystem,
		Name:      name,
		Help:      help,
	}, nil)
}

// NewHistogram ignores all parameters except name and help.
func (p prometheusProvider) NewHistogram(name, help string, _, _ int64, _ int, _ ...int) (metrics.Histogram, error) {
	return kitprom.NewHistogram(prometheus.HistogramOpts{
		Namespace: p.namespace,
		Subsystem: p.subsystem,
		Name:      name,
		Help:      help,
	}, nil), nil
}

// NewGauge implements Provider.
func (p prometheusProvider) NewGauge(name, help string) metrics.Gauge {
	return kitprom.NewGauge(prometheus.GaugeOpts{
		Namespace: p.namespace,
		Subsystem: p.subsystem,
		Name:      name,
		Help:      help,
	}, nil)
}

// Stop is a no-op.
func (prometheusProvider) Stop() {}

var _ Provider = discardProvider{}

// NewDiscardProvider returns a provider that will discard all metrics.
func NewDiscardProvider() Provider {
	return discardProvider{}
}

type discardProvider struct{}

func (p discardProvider) NewCounter(name string, _ string) metrics.Counter {
	return discard.NewCounter(name)
}

func (p discardProvider) NewHistogram(name string, _ string, _ int64, _ int64, _ int, _ ...int) (metrics.Histogram, error) {
	return discard.NewHistogram(name), nil
}

func (p discardProvider) NewGauge(name string, _ string) metrics.Gauge {
	return discard.NewGauge(name)
}

// Stop is a no-op.
func (p discardProvider) Stop() {}
<commit_msg>updating prometheusProvider docs<commit_after>package provider

import (
	"errors"
	"time"

	"github.com/prometheus/client_golang/prometheus"

	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/metrics"
	"github.com/go-kit/kit/metrics/discard"
	"github.com/go-kit/kit/metrics/dogstatsd"
	kitexp "github.com/go-kit/kit/metrics/expvar"
	"github.com/go-kit/kit/metrics/graphite"
	kitprom "github.com/go-kit/kit/metrics/prometheus"
	"github.com/go-kit/kit/metrics/statsd"
)

// Provider represents a union set of constructors and lifecycle management
// functions for each supported metrics backend. It should be used by those who
// need to easily swap out implementations, e.g. dynamically, or at a single
// point in an intermediating framework.
type Provider interface {
	NewCounter(name, help string) metrics.Counter
	NewHistogram(name, help string, min, max int64, sigfigs int, quantiles ...int) (metrics.Histogram, error)
	NewGauge(name, help string) metrics.Gauge
	Stop()
}

// NewGraphiteProvider will return a Provider implementation that is a simple
// wrapper around a graphite.Emitter. All metric names will be prefixed with the
// given value and data will be emitted once every interval. If no network value
// is given, it will default to "udp".
func NewGraphiteProvider(network, address, prefix string, interval time.Duration, logger log.Logger) (Provider, error) {
	if network == "" {
		network = "udp"
	}
	if address == "" {
		return nil, errors.New("address is required")
	}
	return graphiteProvider{
		e: graphite.NewEmitter(network, address, prefix, interval, logger),
	}, nil
}

type graphiteProvider struct {
	e *graphite.Emitter
}

var _ Provider = graphiteProvider{}

// NewCounter implements Provider. Help is ignored.
func (p graphiteProvider) NewCounter(name, _ string) metrics.Counter {
	return p.e.NewCounter(name)
}

// NewHistogram implements Provider. Help is ignored.
func (p graphiteProvider) NewHistogram(name, _ string, min, max int64, sigfigs int, quantiles ...int) (metrics.Histogram, error) {
	return p.e.NewHistogram(name, min, max, sigfigs, quantiles...)
}

// NewGauge implements Provider. Help is ignored.
func (p graphiteProvider) NewGauge(name, _ string) metrics.Gauge {
	return p.e.NewGauge(name)
}

// Stop implements Provider.
func (p graphiteProvider) Stop() {
	p.e.Stop()
}

// NewStatsdProvider will return a Provider implementation that is a simple
// wrapper around a statsd.Emitter. All metric names will be prefixed with the
// given value and data will be emitted once every interval or when the buffer
// has reached its max size. If no network value is given, it will default to
// "udp".
func NewStatsdProvider(network, address, prefix string, interval time.Duration, logger log.Logger) (Provider, error) {
	if network == "" {
		network = "udp"
	}
	if address == "" {
		return nil, errors.New("address is required")
	}
	return statsdProvider{
		e: statsd.NewEmitter(network, address, prefix, interval, logger),
	}, nil
}

type statsdProvider struct {
	e *statsd.Emitter
}

var _ Provider = statsdProvider{}

// NewCounter implements Provider. Help is ignored.
func (p statsdProvider) NewCounter(name, _ string) metrics.Counter {
	return p.e.NewCounter(name)
}

// NewHistogram implements Provider. Help is ignored.
func (p statsdProvider) NewHistogram(name, _ string, min, max int64, sigfigs int, quantiles ...int) (metrics.Histogram, error) {
	return p.e.NewHistogram(name), nil
}

// NewGauge implements Provider. Help is ignored.
func (p statsdProvider) NewGauge(name, _ string) metrics.Gauge {
	return p.e.NewGauge(name)
}

// Stop will call the underlying statsd.Emitter's Stop method.
func (p statsdProvider) Stop() {
	p.e.Stop()
}

// NewDogStatsdProvider will return a Provider implementation that is a simple
// wrapper around a dogstatsd.Emitter. All metric names will be prefixed with
// the given value and data will be emitted once every interval or when the
// buffer has reached its max size. If no network value is given, it will
// default to "udp".
func NewDogStatsdProvider(network, address, prefix string, interval time.Duration, logger log.Logger) (Provider, error) {
	if network == "" {
		network = "udp"
	}
	if address == "" {
		return nil, errors.New("address is required")
	}
	return dogstatsdProvider{
		e: dogstatsd.NewEmitter(network, address, prefix, interval, logger),
	}, nil
}

type dogstatsdProvider struct {
	e *dogstatsd.Emitter
}

var _ Provider = dogstatsdProvider{}

// NewCounter implements Provider. Help is ignored.
func (p dogstatsdProvider) NewCounter(name, _ string) metrics.Counter {
	return p.e.NewCounter(name)
}

// NewHistogram implements Provider. Help is ignored.
func (p dogstatsdProvider) NewHistogram(name, _ string, min, max int64, sigfigs int, quantiles ...int) (metrics.Histogram, error) {
	return p.e.NewHistogram(name), nil
}

// NewGauge implements Provider. Help is ignored.
func (p dogstatsdProvider) NewGauge(name, _ string) metrics.Gauge {
	return p.e.NewGauge(name)
}

// Stop will call the underlying statsd.Emitter's Stop method.
func (p dogstatsdProvider) Stop() {
	p.e.Stop()
}

// NewExpvarProvider is a very thin wrapper over the expvar package.
// If a prefix is provided, it will prefix all metric names.
func NewExpvarProvider(prefix string) Provider {
	return expvarProvider{prefix: prefix}
}

type expvarProvider struct {
	prefix string
}

var _ Provider = expvarProvider{}

// NewCounter implements Provider. Help is ignored.
func (p expvarProvider) NewCounter(name, _ string) metrics.Counter {
	return kitexp.NewCounter(p.prefix + name)
}

// NewHistogram implements Provider. Help is ignored.
func (p expvarProvider) NewHistogram(name, _ string, min, max int64, sigfigs int, quantiles ...int) (metrics.Histogram, error) {
	return kitexp.NewHistogram(p.prefix+name, min, max, sigfigs, quantiles...), nil
}

// NewGauge implements Provider. Help is ignored.
func (p expvarProvider) NewGauge(name, _ string) metrics.Gauge {
	return kitexp.NewGauge(p.prefix + name)
}

// Stop is a no-op.
func (expvarProvider) Stop() {}

type prometheusProvider struct {
	namespace string
	subsystem string
}

var _ Provider = prometheusProvider{}

// NewPrometheusProvider returns a Prometheus provider that uses the provided
// namespace and subsystem for all metrics.
func NewPrometheusProvider(namespace, subsystem string) Provider {
	return prometheusProvider{
		namespace: namespace,
		subsystem: subsystem,
	}
}

// NewCounter implements Provider.
func (p prometheusProvider) NewCounter(name, help string) metrics.Counter {
	return kitprom.NewCounter(prometheus.CounterOpts{
		Namespace: p.namespace,
		Subsystem: p.subsystem,
		Name:      name,
		Help:      help,
	}, nil)
}

// NewHistogram ignores all parameters except name and help.
func (p prometheusProvider) NewHistogram(name, help string, _, _ int64, _ int, _ ...int) (metrics.Histogram, error) {
	return kitprom.NewHistogram(prometheus.HistogramOpts{
		Namespace: p.namespace,
		Subsystem: p.subsystem,
		Name:      name,
		Help:      help,
	}, nil), nil
}

// NewGauge implements Provider.
func (p prometheusProvider) NewGauge(name, help string) metrics.Gauge {
	return kitprom.NewGauge(prometheus.GaugeOpts{
		Namespace: p.namespace,
		Subsystem: p.subsystem,
		Name:      name,
		Help:      help,
	}, nil)
}

// Stop is a no-op.
func (prometheusProvider) Stop() {}

var _ Provider = discardProvider{}

// NewDiscardProvider returns a provider that will discard all metrics.
func NewDiscardProvider() Provider {
	return discardProvider{}
}

type discardProvider struct{}

func (p discardProvider) NewCounter(name string, _ string) metrics.Counter {
	return discard.NewCounter(name)
}

func (p discardProvider) NewHistogram(name string, _ string, _ int64, _ int64, _ int, _ ...int) (metrics.Histogram, error) {
	return discard.NewHistogram(name), nil
}

func (p discardProvider) NewGauge(name string, _ string) metrics.Gauge {
	return discard.NewGauge(name)
}

// Stop is a no-op.
func (p discardProvider) Stop() {}
<|endoftext|>


<commit_before>package middleware

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/labstack/echo/v4"
	"github.com/stretchr/testify/assert"
)

func TestBodyLimit(t *testing.T) {
	e := echo.New()
	hw := []byte("Hello, World!")
	req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(hw))
	rec := httptest.NewRecorder()
	c := e.NewContext(req, rec)
	h := func(c echo.Context) error {
		body, err := ioutil.ReadAll(c.Request().Body)
		if err != nil {
			return err
		}
		return c.String(http.StatusOK, string(body))
	}

	assert := assert.New(t)

	// Based on content length (within limit)
	if assert.NoError(BodyLimit("2M")(h)(c)) {
		assert.Equal(http.StatusOK, rec.Code)
		assert.Equal(hw, rec.Body.Bytes())
	}

	// Based on content read (overlimit)
	he := BodyLimit("2B")(h)(c).(*echo.HTTPError)
	assert.Equal(http.StatusRequestEntityTooLarge, he.Code)

	// Based on content read (within limit)
	req = httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(hw))
	rec = httptest.NewRecorder()
	c = e.NewContext(req, rec)
	if assert.NoError(BodyLimit("2M")(h)(c)) {
		assert.Equal(http.StatusOK, rec.Code)
		assert.Equal("Hello, World!", rec.Body.String())
	}

	// Based on content read (overlimit)
	req = httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(hw))
	rec = httptest.NewRecorder()
	c = e.NewContext(req, rec)
	he = BodyLimit("2B")(h)(c).(*echo.HTTPError)
	assert.Equal(http.StatusRequestEntityTooLarge, he.Code)
}

func TestBodyLimitReader(t *testing.T) {
	hw := []byte("Hello, World!")
	e := echo.New()
	req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(hw))
	rec := httptest.NewRecorder()

	config := BodyLimitConfig{
		Skipper: DefaultSkipper,
		Limit:   "2B",
		limit:   2,
	}
	reader := &limitedReader{
		BodyLimitConfig: config,
		reader:          ioutil.NopCloser(bytes.NewReader(hw)),
		context:         e.NewContext(req, rec),
	}

	// read all should return ErrStatusRequestEntityTooLarge
	_, err := ioutil.ReadAll(reader)
	he := err.(*echo.HTTPError)
	assert.Equal(t, http.StatusRequestEntityTooLarge, he.Code)

	// reset reader and read two bytes must succeed
	bt := make([]byte, 2)
	reader.Reset(ioutil.NopCloser(bytes.NewReader(hw)), e.NewContext(req, rec))
	n, err := reader.Read(bt)
	assert.Equal(t, 2, n)
	assert.Equal(t, nil, err)
}
<commit_msg>Fix body_limit middleware unit test<commit_after>package middleware

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/labstack/echo/v4"
	"github.com/stretchr/testify/assert"
)

func TestBodyLimit(t *testing.T) {
	e := echo.New()
	hw := []byte("Hello, World!")
	req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(hw))
	rec := httptest.NewRecorder()
	c := e.NewContext(req, rec)
	h := func(c echo.Context) error {
		body, err := ioutil.ReadAll(c.Request().Body)
		if err != nil {
			return err
		}
		return c.String(http.StatusOK, string(body))
	}

	assert := assert.New(t)

	// Based on content length (within limit)
	if assert.NoError(BodyLimit("2M")(h)(c)) {
		assert.Equal(http.StatusOK, rec.Code)
		assert.Equal(hw, rec.Body.Bytes())
	}

	// Based on content length (overlimit)
	he := BodyLimit("2B")(h)(c).(*echo.HTTPError)
	assert.Equal(http.StatusRequestEntityTooLarge, he.Code)

	// Based on content read (within limit)
	req = httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(hw))
	req.ContentLength = -1
	rec = httptest.NewRecorder()
	c = e.NewContext(req, rec)
	if assert.NoError(BodyLimit("2M")(h)(c)) {
		assert.Equal(http.StatusOK, rec.Code)
		assert.Equal("Hello, World!", rec.Body.String())
	}

	// Based on content read (overlimit)
	req = httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(hw))
	req.ContentLength = -1
	rec = httptest.NewRecorder()
	c = e.NewContext(req, rec)
	he = BodyLimit("2B")(h)(c).(*echo.HTTPError)
	assert.Equal(http.StatusRequestEntityTooLarge, he.Code)
}

func TestBodyLimitReader(t *testing.T) {
	hw := []byte("Hello, World!")
	e := echo.New()
	req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(hw))
	rec := httptest.NewRecorder()

	config := BodyLimitConfig{
		Skipper: DefaultSkipper,
		Limit:   "2B",
		limit:   2,
	}
	reader := &limitedReader{
		BodyLimitConfig: config,
		reader:          ioutil.NopCloser(bytes.NewReader(hw)),
		context:         e.NewContext(req, rec),
	}

	// read all should return ErrStatusRequestEntityTooLarge
	_, err := ioutil.ReadAll(reader)
	he := err.(*echo.HTTPError)
	assert.Equal(t, http.StatusRequestEntityTooLarge, he.Code)

	// reset reader and read two bytes must succeed
	bt := make([]byte, 2)
	reader.Reset(ioutil.NopCloser(bytes.NewReader(hw)), e.NewContext(req, rec))
	n, err := reader.Read(bt)
	assert.Equal(t, 2, n)
	assert.Equal(t, nil, err)
}
<|endoftext|>


<commit_before>/*

   Copyright 2016 Wenhui Shen <www.webx.top>

   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 tplfunc

import (
	"fmt"
	"html/template"
	"net/url"
	"path/filepath"
	"strings"
	"time"

	"github.com/webx-top/captcha"
	"github.com/webx-top/com"
)

func New() (r template.FuncMap) {
	r = template.FuncMap{}
	for name, function := range TplFuncMap {
		r[name] = function
	}
	return
}

var TplFuncMap template.FuncMap = template.FuncMap{
	"Now":             Now,
	"Eq":              Eq,
	"Add":             Add,
	"Sub":             Sub,
	"IsNil":           IsNil,
	"IsEmpty":         IsEmpty,
	"NotEmpty":        NotEmpty,
	"Html":            ToHTML,
	"Js":              ToJS,
	"Css":             ToCSS,
	"ToJS":            ToJS,
	"ToCSS":           ToCSS,
	"ToURL":           ToURL,
	"ToHTML":          ToHTML,
	"ToHTMLAttr":      ToHTMLAttr,
	"ToHTMLAttrs":     ToHTMLAttrs,
	"ToStrSlice":      ToStrSlice,
	"Concat":          Concat,
	"ElapsedMemory":   com.ElapsedMemory, //内存消耗
	"TotalRunTime":    com.TotalRunTime,  //运行时长(从启动服务时算起)
	"CaptchaForm":     CaptchaForm,       //验证码图片
	"FormatByte":      com.FormatByte,    //字节转为适合理解的格式
	"FriendlyTime":    FriendlyTime,
	"FormatPastTime":  com.FormatPastTime, //以前距离现在多长时间
	"DateFormat":      com.DateFormat,
	"DateFormatShort": com.DateFormatShort,
	"Replace":         strings.Replace, //strings.Replace(s, old, new, n)
	"Contains":        strings.Contains,
	"HasPrefix":       strings.HasPrefix,
	"HasSuffix":       strings.HasSuffix,
	"Split":           strings.Split,
	"Join":            strings.Join,
	"Ext":             filepath.Ext,
	"InExt":           InExt,
	"Str":             com.Str,
	"Int":             com.Int,
	"Int32":           com.Int32,
	"Int64":           com.Int64,
	"Float32":         com.Float32,
	"Float64":         com.Float64,
	"InSlice":         com.InSlice,
	"InSlicex":        com.InSliceIface,
	"Substr":          com.Substr,
	"StripTags":       com.StripTags,
	"Default":         Default,
	"JsonEncode":      JsonEncode,
	"UrlEncode":       com.UrlEncode,
	"UrlDecode":       com.UrlDecode,
	"Base64Encode":    com.Base64Encode,
	"Base64Decode":    com.Base64Decode,
	"Set":             Set,
	"Append":          Append,
	"Nl2br":           NlToBr,
	"AddSuffix":       AddSuffix,
	"InStrSlice":      InStrSlice,
	"SearchStrSlice":  SearchStrSlice,
	"URLValues":       URLValues,
	"ToSlice":         ToSlice,
	"NoOutput":        NoOutput,
}

func JsonEncode(s interface{}) string {
	r, _ := com.SetJSON(s)
	return r
}

func NoOutput(_ interface{}) interface{} {
	return nil
}

func URLValues(values ...interface{}) url.Values {
	v := url.Values{}
	var k string
	for i, j := 0, len(values); i < j; i++ {
		if i%2 == 0 {
			k = fmt.Sprint(values[i])
			continue
		}
		v.Add(k, fmt.Sprint(values[i]))
		k = ``
	}
	if len(k) > 0 {
		v.Add(k, ``)
		k = ``
	}
	return v
}

func ToStrSlice(s ...string) []string {
	return s
}

func ToSlice(s ...interface{}) []interface{} {
	return s
}

func Concat(s ...string) string {
	return strings.Join(s, ``)
}

func InExt(fileName string, exts ...string) bool {
	ext := filepath.Ext(fileName)
	ext = strings.ToLower(ext)
	for _, _ext := range exts {
		if ext == strings.ToLower(_ext) {
			return true
		}
	}
	return false
}

func Default(defaultV interface{}, v interface{}) interface{} {
	switch val := v.(type) {
	case nil:
		return defaultV
	case string:
		if len(val) == 0 {
			return defaultV
		}
	case uint8, int8, uint, int, uint32, int32, int64, uint64:
		if val == 0 {
			return defaultV
		}
	case float32, float64:
		if val == 0.0 {
			return defaultV
		}
	default:
		if len(com.Str(v)) == 0 {
			return defaultV
		}
	}
	return v
}

func Set(renderArgs map[string]interface{}, key string, value interface{}) string {
	renderArgs[key] = value
	return ``
}

func Append(renderArgs map[string]interface{}, key string, value interface{}) string {
	if renderArgs[key] == nil {
		renderArgs[key] = []interface{}{value}
	} else {
		renderArgs[key] = append(renderArgs[key].([]interface{}), value)
	}
	return ``
}

//NlToBr Replaces newlines with

func NlToBr(text string) template.HTML {
	return template.HTML(Nl2br(text))
}

//CaptchaForm 验证码表单域
func CaptchaForm(args ...string) template.HTML {
	id := "captcha"
	format := `<input type="hidden" name="captchaId" id="%[2]sId" value="%[1]s" />`
	switch len(args) {
	case 2:
		format = args[1]
		fallthrough
	case 1:
		id = args[0]
	}
	cid := captcha.New()
	return template.HTML(fmt.Sprintf(format, cid, id))
}

//CaptchaVerify 验证码验证
func CaptchaVerify(captchaSolution string, idGet func(string) string) bool {
	//id := r.FormValue("captchaId")
	id := idGet("captchaId")
	if !captcha.VerifyString(id, captchaSolution) {
		return false
	}
	return true
}

//Nl2br 将换行符替换为

func Nl2br(text string) string {
	return com.Nl2br(template.HTMLEscapeString(text))
}

func IsNil(a interface{}) bool {
	switch a.(type) {
	case nil:
		return true
	}
	return false
}

func Add(left interface{}, right interface{}) interface{} {
	var rleft, rright int64
	var fleft, fright float64
	isInt := true
	switch left.(type) {
	case int:
		rleft = int64(left.(int))
	case int8:
		rleft = int64(left.(int8))
	case int16:
		rleft = int64(left.(int16))
	case int32:
		rleft = int64(left.(int32))
	case int64:
		rleft = left.(int64)
	case float32:
		fleft = float64(left.(float32))
		isInt = false
	case float64:
		fleft = left.(float64)
		isInt = false
	}

	switch right.(type) {
	case int:
		rright = int64(right.(int))
	case int8:
		rright = int64(right.(int8))
	case int16:
		rright = int64(right.(int16))
	case int32:
		rright = int64(right.(int32))
	case int64:
		rright = right.(int64)
	case float32:
		fright = float64(left.(float32))
		isInt = false
	case float64:
		fleft = left.(float64)
		isInt = false
	}

	intSum := rleft + rright

	if isInt {
		return intSum
	}
	return fleft + fright + float64(intSum)
}

func Sub(left interface{}, right interface{}) interface{} {
	var rleft, rright int64
	var fleft, fright float64
	isInt := true
	switch left.(type) {
	case int:
		rleft = int64(left.(int))
	case int8:
		rleft = int64(left.(int8))
	case int16:
		rleft = int64(left.(int16))
	case int32:
		rleft = int64(left.(int32))
	case int64:
		rleft = left.(int64)
	case float32:
		fleft = float64(left.(float32))
		isInt = false
	case float64:
		fleft = left.(float64)
		isInt = false
	}

	switch right.(type) {
	case int:
		rright = int64(right.(int))
	case int8:
		rright = int64(right.(int8))
	case int16:
		rright = int64(right.(int16))
	case int32:
		rright = int64(right.(int32))
	case int64:
		rright = right.(int64)
	case float32:
		fright = float64(left.(float32))
		isInt = false
	case float64:
		fleft = left.(float64)
		isInt = false
	}

	if isInt {
		return rleft - rright
	}
	return fleft + float64(rleft) - (fright + float64(rright))
}

func Now() time.Time {
	return time.Now()
}

func Eq(left interface{}, right interface{}) bool {
	leftIsNil := (left == nil)
	rightIsNil := (right == nil)
	if leftIsNil || rightIsNil {
		if leftIsNil && rightIsNil {
			return true
		}
		return false
	}
	return fmt.Sprintf("%v", left) == fmt.Sprintf("%v", right)
}

func ToHTML(raw string) template.HTML {
	return template.HTML(raw)
}

func ToHTMLAttr(raw string) template.HTMLAttr {
	return template.HTMLAttr(raw)
}

func ToHTMLAttrs(raw map[string]interface{}) (r map[template.HTMLAttr]interface{}) {
	r = make(map[template.HTMLAttr]interface{})
	for k, v := range raw {
		r[ToHTMLAttr(k)] = v
	}
	return
}

func ToJS(raw string) template.JS {
	return template.JS(raw)
}

func ToCSS(raw string) template.CSS {
	return template.CSS(raw)
}

func ToURL(raw string) template.URL {
	return template.URL(raw)
}

func AddSuffix(s string, suffix string, args ...string) string {
	beforeChar := `.`
	if len(args) > 0 {
		beforeChar = args[0]
		if beforeChar == `` {
			return s + suffix
		}
	}
	p := strings.LastIndex(s, beforeChar)
	if p < 0 {
		return s
	}
	return s[0:p] + suffix + s[p:]
}

func IsEmpty(a interface{}) bool {
	switch v := a.(type) {
	case nil:
		return true
	case string:
		return len(v) == 0
	case []interface{}:
		return len(v) < 1
	default:
		switch fmt.Sprintf(`%v`, a) {
		case `<nil>`, ``, `[]`:
			return true
		}
	}
	return false
}

func NotEmpty(a interface{}) bool {
	return !IsEmpty(a)
}

func InStrSlice(values []string, value string) bool {
	for _, v := range values {
		if v == value {
			return true
		}
	}
	return false
}

func SearchStrSlice(values []string, value string) int {
	for i, v := range values {
		if v == value {
			return i
		}
	}
	return -1
}

func FriendlyTime(t interface{}, args ...string) string {
	if v, y := t.(time.Duration); y {
		return com.FriendlyTime(v, args...)
	}
	if v, y := t.(int64); y {
		return com.FriendlyTime(time.Duration(v), args...)
	}
	if v, y := t.(int); y {
		return com.FriendlyTime(time.Duration(v), args...)
	}
	if v, y := t.(uint); y {
		return com.FriendlyTime(time.Duration(v), args...)
	}
	if v, y := t.(int32); y {
		return com.FriendlyTime(time.Duration(v), args...)
	}
	if v, y := t.(uint32); y {
		return com.FriendlyTime(time.Duration(v), args...)
	}
	return com.FriendlyTime(time.Duration(com.Int64(t)), args...)
}
<commit_msg>update<commit_after>/*

   Copyright 2016 Wenhui Shen <www.webx.top>

   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 tplfunc

import (
	"fmt"
	"html/template"
	"log"
	"net/url"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"regexp"

	"github.com/webx-top/captcha"
	"github.com/webx-top/com"
)

func New() (r template.FuncMap) {
	r = template.FuncMap{}
	for name, function := range TplFuncMap {
		r[name] = function
	}
	return
}

var TplFuncMap template.FuncMap = template.FuncMap{
	"Now":             Now,
	"Eq":              Eq,
	"Add":             Add,
	"Sub":             Sub,
	"IsNil":           IsNil,
	"IsEmpty":         IsEmpty,
	"NotEmpty":        NotEmpty,
	"Html":            ToHTML,
	"Js":              ToJS,
	"Css":             ToCSS,
	"ToJS":            ToJS,
	"ToCSS":           ToCSS,
	"ToURL":           ToURL,
	"ToHTML":          ToHTML,
	"ToHTMLAttr":      ToHTMLAttr,
	"ToHTMLAttrs":     ToHTMLAttrs,
	"ToStrSlice":      ToStrSlice,
	"Concat":          Concat,
	"ElapsedMemory":   com.ElapsedMemory, //内存消耗
	"TotalRunTime":    com.TotalRunTime,  //运行时长(从启动服务时算起)
	"CaptchaForm":     CaptchaForm,       //验证码图片
	"FormatByte":      com.FormatByte,    //字节转为适合理解的格式
	"FriendlyTime":    FriendlyTime,
	"FormatPastTime":  com.FormatPastTime, //以前距离现在多长时间
	"DateFormat":      com.DateFormat,
	"DateFormatShort": com.DateFormatShort,
	"Replace":         strings.Replace, //strings.Replace(s, old, new, n)
	"Contains":        strings.Contains,
	"HasPrefix":       strings.HasPrefix,
	"HasSuffix":       strings.HasSuffix,
	"Split":           strings.Split,
	"Join":            strings.Join,
	"Ext":             filepath.Ext,
	"InExt":           InExt,
	"Str":             com.Str,
	"Int":             com.Int,
	"Int32":           com.Int32,
	"Int64":           com.Int64,
	"Float32":         com.Float32,
	"Float64":         com.Float64,
	"InSlice":         com.InSlice,
	"InSlicex":        com.InSliceIface,
	"Substr":          com.Substr,
	"StripTags":       com.StripTags,
	"Default":         Default,
	"JsonEncode":      JsonEncode,
	"UrlEncode":       com.UrlEncode,
	"UrlDecode":       com.UrlDecode,
	"Base64Encode":    com.Base64Encode,
	"Base64Decode":    com.Base64Decode,
	"Set":             Set,
	"Append":          Append,
	"Nl2br":           NlToBr,   // \n替换为

	"Ts2time":         TsToTime, // 时间戳数字转time.Time
	"Ts2date":         TsToDate, // 时间戳数字转日期字符串
	"AddSuffix":       AddSuffix,
	"InStrSlice":      InStrSlice,
	"SearchStrSlice":  SearchStrSlice,
	"URLValues":       URLValues,
	"ToSlice":         ToSlice,
	"NoOutput":        NoOutput,
	"Regexp":          regexp.MustCompile,
	"RegexpPOSIX":     regexp.MustCompilePOSIX,
}

func JsonEncode(s interface{}) string {
	r, _ := com.SetJSON(s)
	return r
}

func NoOutput(_ interface{}) interface{} {
	return nil
}

func URLValues(values ...interface{}) url.Values {
	v := url.Values{}
	var k string
	for i, j := 0, len(values); i < j; i++ {
		if i%2 == 0 {
			k = fmt.Sprint(values[i])
			continue
		}
		v.Add(k, fmt.Sprint(values[i]))
		k = ``
	}
	if len(k) > 0 {
		v.Add(k, ``)
		k = ``
	}
	return v
}

func ToStrSlice(s ...string) []string {
	return s
}

func ToSlice(s ...interface{}) []interface{} {
	return s
}

func Concat(s ...string) string {
	return strings.Join(s, ``)
}

func InExt(fileName string, exts ...string) bool {
	ext := filepath.Ext(fileName)
	ext = strings.ToLower(ext)
	for _, _ext := range exts {
		if ext == strings.ToLower(_ext) {
			return true
		}
	}
	return false
}

func Default(defaultV interface{}, v interface{}) interface{} {
	switch val := v.(type) {
	case nil:
		return defaultV
	case string:
		if len(val) == 0 {
			return defaultV
		}
	case uint8, int8, uint, int, uint32, int32, int64, uint64:
		if val == 0 {
			return defaultV
		}
	case float32, float64:
		if val == 0.0 {
			return defaultV
		}
	default:
		if len(com.Str(v)) == 0 {
			return defaultV
		}
	}
	return v
}

func Set(renderArgs map[string]interface{}, key string, value interface{}) string {
	renderArgs[key] = value
	return ``
}

func Append(renderArgs map[string]interface{}, key string, value interface{}) string {
	if renderArgs[key] == nil {
		renderArgs[key] = []interface{}{value}
	} else {
		renderArgs[key] = append(renderArgs[key].([]interface{}), value)
	}
	return ``
}

//NlToBr Replaces newlines with

func NlToBr(text string) template.HTML {
	return template.HTML(Nl2br(text))
}

//CaptchaForm 验证码表单域
func CaptchaForm(args ...string) template.HTML {
	id := "captcha"
	format := `<input type="hidden" name="captchaId" id="%[2]sId" value="%[1]s" />`
	switch len(args) {
	case 2:
		format = args[1]
		fallthrough
	case 1:
		id = args[0]
	}
	cid := captcha.New()
	return template.HTML(fmt.Sprintf(format, cid, id))
}

//CaptchaVerify 验证码验证
func CaptchaVerify(captchaSolution string, idGet func(string) string) bool {
	//id := r.FormValue("captchaId")
	id := idGet("captchaId")
	if !captcha.VerifyString(id, captchaSolution) {
		return false
	}
	return true
}

//Nl2br 将换行符替换为

func Nl2br(text string) string {
	return com.Nl2br(template.HTMLEscapeString(text))
}

func IsNil(a interface{}) bool {
	switch a.(type) {
	case nil:
		return true
	}
	return false
}

func Add(left interface{}, right interface{}) interface{} {
	var rleft, rright int64
	var fleft, fright float64
	isInt := true
	switch left.(type) {
	case int:
		rleft = int64(left.(int))
	case int8:
		rleft = int64(left.(int8))
	case int16:
		rleft = int64(left.(int16))
	case int32:
		rleft = int64(left.(int32))
	case int64:
		rleft = left.(int64)
	case float32:
		fleft = float64(left.(float32))
		isInt = false
	case float64:
		fleft = left.(float64)
		isInt = false
	}

	switch right.(type) {
	case int:
		rright = int64(right.(int))
	case int8:
		rright = int64(right.(int8))
	case int16:
		rright = int64(right.(int16))
	case int32:
		rright = int64(right.(int32))
	case int64:
		rright = right.(int64)
	case float32:
		fright = float64(left.(float32))
		isInt = false
	case float64:
		fleft = left.(float64)
		isInt = false
	}

	intSum := rleft + rright

	if isInt {
		return intSum
	}
	return fleft + fright + float64(intSum)
}

func Sub(left interface{}, right interface{}) interface{} {
	var rleft, rright int64
	var fleft, fright float64
	isInt := true
	switch left.(type) {
	case int:
		rleft = int64(left.(int))
	case int8:
		rleft = int64(left.(int8))
	case int16:
		rleft = int64(left.(int16))
	case int32:
		rleft = int64(left.(int32))
	case int64:
		rleft = left.(int64)
	case float32:
		fleft = float64(left.(float32))
		isInt = false
	case float64:
		fleft = left.(float64)
		isInt = false
	}

	switch right.(type) {
	case int:
		rright = int64(right.(int))
	case int8:
		rright = int64(right.(int8))
	case int16:
		rright = int64(right.(int16))
	case int32:
		rright = int64(right.(int32))
	case int64:
		rright = right.(int64)
	case float32:
		fright = float64(left.(float32))
		isInt = false
	case float64:
		fleft = left.(float64)
		isInt = false
	}

	if isInt {
		return rleft - rright
	}
	return fleft + float64(rleft) - (fright + float64(rright))
}

func Now() time.Time {
	return time.Now()
}

func Eq(left interface{}, right interface{}) bool {
	leftIsNil := (left == nil)
	rightIsNil := (right == nil)
	if leftIsNil || rightIsNil {
		if leftIsNil && rightIsNil {
			return true
		}
		return false
	}
	return fmt.Sprintf("%v", left) == fmt.Sprintf("%v", right)
}

func ToHTML(raw string) template.HTML {
	return template.HTML(raw)
}

func ToHTMLAttr(raw string) template.HTMLAttr {
	return template.HTMLAttr(raw)
}

func ToHTMLAttrs(raw map[string]interface{}) (r map[template.HTMLAttr]interface{}) {
	r = make(map[template.HTMLAttr]interface{})
	for k, v := range raw {
		r[ToHTMLAttr(k)] = v
	}
	return
}

func ToJS(raw string) template.JS {
	return template.JS(raw)
}

func ToCSS(raw string) template.CSS {
	return template.CSS(raw)
}

func ToURL(raw string) template.URL {
	return template.URL(raw)
}

func AddSuffix(s string, suffix string, args ...string) string {
	beforeChar := `.`
	if len(args) > 0 {
		beforeChar = args[0]
		if beforeChar == `` {
			return s + suffix
		}
	}
	p := strings.LastIndex(s, beforeChar)
	if p < 0 {
		return s
	}
	return s[0:p] + suffix + s[p:]
}

func IsEmpty(a interface{}) bool {
	switch v := a.(type) {
	case nil:
		return true
	case string:
		return len(v) == 0
	case []interface{}:
		return len(v) < 1
	default:
		switch fmt.Sprintf(`%v`, a) {
		case `<nil>`, ``, `[]`:
			return true
		}
	}
	return false
}

func NotEmpty(a interface{}) bool {
	return !IsEmpty(a)
}

func InStrSlice(values []string, value string) bool {
	for _, v := range values {
		if v == value {
			return true
		}
	}
	return false
}

func SearchStrSlice(values []string, value string) int {
	for i, v := range values {
		if v == value {
			return i
		}
	}
	return -1
}

func FriendlyTime(t interface{}, args ...string) string {
	if v, y := t.(time.Duration); y {
		return com.FriendlyTime(v, args...)
	}
	if v, y := t.(int64); y {
		return com.FriendlyTime(time.Duration(v), args...)
	}
	if v, y := t.(int); y {
		return com.FriendlyTime(time.Duration(v), args...)
	}
	if v, y := t.(uint); y {
		return com.FriendlyTime(time.Duration(v), args...)
	}
	if v, y := t.(int32); y {
		return com.FriendlyTime(time.Duration(v), args...)
	}
	if v, y := t.(uint32); y {
		return com.FriendlyTime(time.Duration(v), args...)
	}
	return com.FriendlyTime(time.Duration(com.Int64(t)), args...)
}

func TsToTime(timestamp interface{}) time.Time {
	return TimestampToTime(timestamp)
}

func TsToDate(format string, timestamp interface{}) string {
	t := TimestampToTime(timestamp)
	if t.IsZero() {
		return ``
	}
	return t.Format(format)
}

func TimestampToTime(timestamp interface{}) time.Time {
	var ts int64
	switch v := timestamp.(type) {
	case int64:
		ts = v
	case uint:
		ts = int64(v)
	case int:
		ts = int64(v)
	case uint32:
		ts = int64(v)
	case int32:
		ts = int64(v)
	case uint64:
		ts = int64(v)
	default:
		i, e := strconv.ParseInt(fmt.Sprint(timestamp), 10, 64)
		if e != nil {
			log.Println(e)
		}
		ts = i
	}
	return time.Unix(ts, 0)
}
<|endoftext|>