<commit_before>/*
The statsd package provides a Statsd client. It supports all commands supported
by the Etsy statsd server implementation and automatically buffers stats into
512 byte packets.
*/
package statsd

import (
	"bytes"
	"fmt"
	"io"
	"math/rand"
	"net"
	"regexp"
	"sync"
	"time"
)

var (
	nonAlphaNum = regexp.MustCompile(`[^\w]+`)
)

type StatsReporter interface {
	Flush() error
	Count(bucket string, value float64, sampleRate float64)
	Gauge(bucket string, value float64)
	Timing(bucket string, value time.Duration)
	CountUnique(bucket string, value string)
}

type statsdClient struct {
	PacketSize int
	prefix     string
	writer     io.Writer
	mutex      sync.Mutex
	buffer     bytes.Buffer
}

// -- emptyClient

type emptyClient struct{}

func (c emptyClient) Flush() error                   { return nil }
func (c emptyClient) Count(string, float64, float64) {}
func (c emptyClient) Gauge(string, float64)          {}
func (c emptyClient) Timing(string, time.Duration)   {}
func (c emptyClient) CountUnique(string, string)     {}

// -- statsdClient

// New connects to the given Statsd server and, optionally, uses the given
// prefix for all metric bucket names. If the prefix is "foo.bar.", a call to
// Increment with a "baz.biz" name will result in a full bucket name of
// "foo.bar.baz.biz".
//
// If there is an error resolving the host, New will return an error as well as
// a no-op StatsReporter so that code mixed with statsd calls can continue to
// run without errors.
func New(host string, prefix string) (StatsReporter, error) {
	rand.Seed(time.Now().UnixNano())
	connection, err := net.DialTimeout("udp", host, time.Second)
	if err != nil {
		return &emptyClient{}, err
	}
	return &statsdClient{
		PacketSize: 512,
		writer:     connection,
		prefix:     prefix,
	}, nil
}

func (c *statsdClient) record(sampleRate float64, bucket string, value interface{}, kind string) {
	if sampleRate < 1 && sampleRate <= rand.Float64() {
		return
	}

	if sampleRate == 1 {
		c.send(fmt.Sprintf("%s%s:%v|%s", c.prefix, bucket, value, kind))
	} else {
		c.send(fmt.Sprintf("%s%s:%v|%s|@%f", c.prefix, bucket, value, kind, sampleRate))
	}
}

func (c *statsdClient) send(data string) error {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	// Flush buffer if needed
	if c.buffer.Len()+len(data)+1 >= c.PacketSize {
		err := c.Flush()
		if err != nil {
			return err
		}
	}

	// Add to buffer
	if c.buffer.Len() > 0 {
		c.buffer.WriteRune('\n')
	}
	c.buffer.WriteString(data)

	return nil
}

// Flush sends all buffered data to the statsd server, if there is any in the
// buffer.
func (c *statsdClient) Flush() error {
	if c.buffer.Len() > 0 {
		_, err := c.writer.Write(c.buffer.Bytes())
		if err != nil {
			return err
		}
		c.buffer.Reset()
	}
	return nil
}

// Gauge sets an arbitrary value. Only the value of the gauge at flush time is
// stored by statsd.
func (c *statsdClient) Gauge(bucket string, value float64) {
	c.record(1, bucket, value, "g")
}

// Count increments (or decrements) the value in a counter. Counters are
// recorded and then reset to 0 when Statsd flushes.
func (c *statsdClient) Count(bucket string, value float64, sampleRate float64) {
	c.record(sampleRate, bucket, value, "c")
}

// Timing records a time interval (in milliseconds). The percentiles, mean,
// standard deviation, sum, and lower and upper bounds are calculated by the
// Statsd server.
func (c *statsdClient) Timing(bucket string, value time.Duration) {
	c.record(1, bucket, float64(value/time.Millisecond), "ms")
}

// Unique records the number of unique values received between flushes using
// Statsd Sets.
func (c *statsdClient) CountUnique(bucket string, value string) {
	c.record(1, bucket, nonAlphaNum.ReplaceAllString(value, "_"), "s")
}
<commit_msg>Add a separate initializer for custom packet sizes.<commit_after>/*
The statsd package provides a Statsd client. It supports all commands supported
by the Etsy statsd server implementation and automatically buffers stats into
512 byte packets.
*/
package statsd

import (
	"bytes"
	"fmt"
	"io"
	"math/rand"
	"net"
	"regexp"
	"sync"
	"time"
)

var (
	nonAlphaNum = regexp.MustCompile(`[^\w]+`)
)

type StatsReporter interface {
	Flush() error
	Count(bucket string, value float64, sampleRate float64)
	Gauge(bucket string, value float64)
	Timing(bucket string, value time.Duration)
	CountUnique(bucket string, value string)
}

type statsdClient struct {
	PacketSize int
	prefix     string
	writer     io.Writer
	mutex      sync.Mutex
	buffer     bytes.Buffer
}

// -- emptyClient

type emptyClient struct{}

func (c emptyClient) Flush() error                   { return nil }
func (c emptyClient) Count(string, float64, float64) {}
func (c emptyClient) Gauge(string, float64)          {}
func (c emptyClient) Timing(string, time.Duration)   {}
func (c emptyClient) CountUnique(string, string)     {}

// -- statsdClient

// New is the same as calling NewWithPacketSize with a 512 byte packet size.
func New(host string, prefix string) (StatsReporter, error) {
	return NewWithPacketSize(host, prefix, 512)
}

// NewWithPacketSize connects to the given Statsd server and uses the given
// prefix for all metric bucket names. If the prefix is "foo.bar.", a call to
// Increment with a "baz.biz" name will result in a full bucket name of
// "foo.bar.baz.biz". The prefix can be an empty string.
//
// If there is an error resolving the host, New will return an error as well as
// a no-op StatsReporter so that code mixed with statsd calls can continue to
// run without errors.
func NewWithPacketSize(host string, prefix string, packetSize int) (StatsReporter, error) {
	rand.Seed(time.Now().UnixNano()) // used for sample rates
	connection, err := net.DialTimeout("udp", host, time.Second)
	if err != nil {
		return &emptyClient{}, err
	}
	return &statsdClient{
		PacketSize: 512,
		writer:     connection,
		prefix:     prefix,
	}, nil
}

func (c *statsdClient) record(sampleRate float64, bucket string, value interface{}, kind string) {
	if sampleRate < 1 && sampleRate <= rand.Float64() {
		return
	}

	if sampleRate == 1 {
		c.send(fmt.Sprintf("%s%s:%v|%s", c.prefix, bucket, value, kind))
	} else {
		c.send(fmt.Sprintf("%s%s:%v|%s|@%f", c.prefix, bucket, value, kind, sampleRate))
	}
}

func (c *statsdClient) send(data string) error {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	// Flush buffer if needed
	if c.buffer.Len()+len(data)+1 >= c.PacketSize {
		err := c.Flush()
		if err != nil {
			return err
		}
	}

	// Add to buffer
	if c.buffer.Len() > 0 {
		c.buffer.WriteRune('\n')
	}
	c.buffer.WriteString(data)

	return nil
}

// Flush sends all buffered data to the statsd server, if there is any in the
// buffer.
func (c *statsdClient) Flush() error {
	if c.buffer.Len() > 0 {
		_, err := c.writer.Write(c.buffer.Bytes())
		if err != nil {
			return err
		}
		c.buffer.Reset()
	}
	return nil
}

// Gauge sets an arbitrary value. Only the value of the gauge at flush time is
// stored by statsd.
func (c *statsdClient) Gauge(bucket string, value float64) {
	c.record(1, bucket, value, "g")
}

// Count increments (or decrements) the value in a counter. Counters are
// recorded and then reset to 0 when Statsd flushes.
func (c *statsdClient) Count(bucket string, value float64, sampleRate float64) {
	c.record(sampleRate, bucket, value, "c")
}

// Timing records a time interval (in milliseconds). The percentiles, mean,
// standard deviation, sum, and lower and upper bounds are calculated by the
// Statsd server.
func (c *statsdClient) Timing(bucket string, value time.Duration) {
	c.record(1, bucket, float64(value/time.Millisecond), "ms")
}

// Unique records the number of unique values received between flushes using
// Statsd Sets.
func (c *statsdClient) CountUnique(bucket string, value string) {
	c.record(1, bucket, nonAlphaNum.ReplaceAllString(value, "_"), "s")
}
<|endoftext|>


<commit_before>// BaruwaAPI Golang bindings for Baruwa REST API
// Copyright (C) 2019 Andrew Colin Kissa <andrew@topdog.za.net>

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

package api

import (
	"fmt"
	"net/url"

	"github.com/google/go-querystring/query"
)

// RelaySetting holds relay settings
type RelaySetting struct {
	ID              int     `json:"id,omitempty"`
	Address         string  `json:"address"`
	Username        string  `json:"username"`
	Enabled         bool    `json:"enabled"`
	RequireTLS      bool    `json:"require_tls"`
	Password1       string  `json:"password1,omitempty"`
	Password2       string  `json:"password2,omitempty"`
	Description     string  `json:"description"`
	LowScore        float64 `json:"low_score"`
	HighScore       float64 `json:"high_score"`
	SpamActions     int     `json:"spam_actions"`
	HighSpamActions int     `json:"highspam_actions"`
	BlockMacros     bool    `json:"block_macros"`
	RateLimit       int     `json:"ratelimit"`
}

// GetRelaySetting returns radius settings
// https://www.baruwa.com/docs/api/#retrieve-relay-settings
func (c *Client) GetRelaySetting(relayID int) (server *RelaySetting, err error) {
	if relayID <= 0 {
		err = fmt.Errorf("The relayID param should be > 0")
		return
	}

	err = c.get(fmt.Sprintf("relays/%d", relayID), nil, server)

	return
}

// CreateRelaySetting creates radius settings
// https://www.baruwa.com/docs/api/#create-relay-settings
func (c *Client) CreateRelaySetting(organizationID int, server *RelaySetting) (err error) {
	var v url.Values

	if organizationID <= 0 {
		err = fmt.Errorf("The organizationID param should be > 0")
		return
	}

	if server == nil {
		err = fmt.Errorf("The server param cannot be nil")
		return
	}

	if v, err = query.Values(server); err != nil {
		return
	}

	err = c.post(fmt.Sprintf("relays/%d", organizationID), v, server)

	return
}

// UpdateRelaySetting updates radius settings
// https://www.baruwa.com/docs/api/#update-relay-settings
func (c *Client) UpdateRelaySetting(server *RelaySetting) (err error) {
	var v url.Values

	if server == nil {
		err = fmt.Errorf("The server param cannot be nil")
		return
	}

	if server.ID <= 0 {
		err = fmt.Errorf("The server.ID param should be > 0")
		return
	}

	if v, err = query.Values(server); err != nil {
		return
	}

	err = c.put(fmt.Sprintf("relays/%d", server.ID), v, server)

	return
}

// DeleteRelaySetting deletes radius settings
// https://www.baruwa.com/docs/api/#delete-relay-settings
func (c *Client) DeleteRelaySetting(server *RelaySetting) (err error) {
	var v url.Values

	if server == nil {
		err = fmt.Errorf("The server param cannot be nil")
		return
	}

	if server.ID <= 0 {
		err = fmt.Errorf("The server.ID param should be > 0")
		return
	}

	if v, err = query.Values(server); err != nil {
		return
	}

	err = c.delete(fmt.Sprintf("relays/%d", server.ID), v)

	return
}
<commit_msg>FET: Add Relay Servers implementation<commit_after>// BaruwaAPI Golang bindings for Baruwa REST API
// Copyright (C) 2019 Andrew Colin Kissa <andrew@topdog.za.net>

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

package api

import (
	"fmt"
	"net/url"

	"github.com/google/go-querystring/query"
)

// RelaySetting holds relay settings
type RelaySetting struct {
	ID              int     `json:"id,omitempty"`
	Address         string  `json:"address"`
	Username        string  `json:"username"`
	Enabled         bool    `json:"enabled"`
	RequireTLS      bool    `json:"require_tls"`
	Password1       string  `json:"password1,omitempty"`
	Password2       string  `json:"password2,omitempty"`
	Description     string  `json:"description"`
	LowScore        float64 `json:"low_score"`
	HighScore       float64 `json:"high_score"`
	SpamActions     int     `json:"spam_actions"`
	HighSpamActions int     `json:"highspam_actions"`
	BlockMacros     bool    `json:"block_macros"`
	RateLimit       int     `json:"ratelimit"`
}

// GetRelaySetting returns radius settings
// https://www.baruwa.com/docs/api/#retrieve-relay-settings
func (c *Client) GetRelaySetting(relayID int) (server *RelaySetting, err error) {
	if relayID <= 0 {
		err = fmt.Errorf(relayIDError)
		return
	}

	server = &RelaySetting{}

	err = c.get(fmt.Sprintf("relays/%d", relayID), nil, server)

	return
}

// CreateRelaySetting creates radius settings
// https://www.baruwa.com/docs/api/#create-relay-settings
func (c *Client) CreateRelaySetting(organizationID int, server *RelaySetting) (err error) {
	var v url.Values

	if organizationID <= 0 {
		err = fmt.Errorf(organizationIDError)
		return
	}

	if server == nil {
		err = fmt.Errorf(serverParamError)
		return
	}

	if v, err = query.Values(server); err != nil {
		return
	}

	err = c.post(fmt.Sprintf("relays/%d", organizationID), v, server)

	return
}

// UpdateRelaySetting updates radius settings
// https://www.baruwa.com/docs/api/#update-relay-settings
func (c *Client) UpdateRelaySetting(server *RelaySetting) (err error) {
	var v url.Values

	if server == nil {
		err = fmt.Errorf(serverParamError)
		return
	}

	if server.ID <= 0 {
		err = fmt.Errorf(serverSIDError)
		return
	}

	if v, err = query.Values(server); err != nil {
		return
	}

	err = c.put(fmt.Sprintf("relays/%d", server.ID), v, server)

	return
}

// DeleteRelaySetting deletes radius settings
// https://www.baruwa.com/docs/api/#delete-relay-settings
func (c *Client) DeleteRelaySetting(server *RelaySetting) (err error) {
	var v url.Values

	if server == nil {
		err = fmt.Errorf(serverParamError)
		return
	}

	if server.ID <= 0 {
		err = fmt.Errorf(serverSIDError)
		return
	}

	if v, err = query.Values(server); err != nil {
		return
	}

	err = c.delete(fmt.Sprintf("relays/%d", server.ID), v)

	return
}
<|endoftext|>


<commit_before>// Package web provides a thin layer of support for writing web services. It
// integrates with the ardanlabs kit repo to provide support for routing and
// application ctx. The base things you need to write a web service is
// provided.
package web

import (
	"context"
	"encoding/json"
	"io"
	"log"
	"net/http"
	"os"
	"os/signal"
	"time"

	"github.com/braintree/manners"
	"github.com/dimfeld/httptreemux"
	"github.com/pborman/uuid"
	"gopkg.in/go-playground/validator.v8"
)

// TraceIDHeader is the header added to outgoing requests which adds the
// traceID to it.
const TraceIDHeader = "X-Trace-ID"

//==============================================================================

// validate provides a validator for checking models.
var validate = validator.New(&validator.Config{
	TagName:      "validate",
	FieldNameTag: "json",
})

// Unmarshal decodes the input to the struct type and checks the
// fields to verify the value is in a proper state.
func Unmarshal(r io.Reader, v interface{}) error {
	if err := json.NewDecoder(r).Decode(v); err != nil {
		return err
	}

	var inv InvalidError
	if fve := validate.Struct(v); fve != nil {
		for _, fe := range fve.(validator.ValidationErrors) {
			inv = append(inv, Invalid{Fld: fe.Field, Err: fe.Tag})
		}
		return inv
	}

	return nil
}

//==============================================================================

// Key represents the type of value for the context key.
type ctxKey int

// KeyValues is how request values or stored/retrieved.
const KeyValues ctxKey = 1

// Values represent state for each request.
type Values struct {
	TraceID    string
	Now        time.Time
	StatusCode int
}

//==============================================================================

// A Handler is a type that handles an http request within our own little mini
// framework.
type Handler func(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error

// A Middleware is a type that wraps a handler to remove boilerplate or other
// concerns not direct to any given Handler.
type Middleware func(Handler) Handler

//==============================================================================

// App is the entrypoint into our application and what configures our context
// object for each of our http handlers. Feel free to add any configuration
// data/logic on this App struct
type App struct {
	*httptreemux.TreeMux
	Values map[string]interface{}

	mw []Middleware
}

// New create an App value that handle a set of routes for the application.
// You can provide any number of middleware and they'll be used to wrap every
// request handler.
func New(mw ...Middleware) *App {
	return &App{
		TreeMux: httptreemux.New(),
		Values:  make(map[string]interface{}),
		mw:      mw,
	}
}

// Group creates a new App Group based on the current App and provided
// middleware.
func (a *App) Group(mw ...Middleware) *Group {
	return &Group{
		app: a,
		mw:  mw,
	}
}

// Use adds the set of provided middleware onto the Application middleware
// chain. Any route running off of this App will use all the middleware provided
// this way always regardless of the ordering of the Handle/Use functions.
func (a *App) Use(mw ...Middleware) {
	a.mw = append(a.mw, mw...)
}

// Handle is our mechanism for mounting Handlers for a given HTTP verb and path
// pair, this makes for really easy, convenient routing.
func (a *App) Handle(verb, path string, handler Handler, mw ...Middleware) {

	// Wrap up the application-wide first, this will call the first function
	// of each middleware which will return a function of type Handler. Each
	// Handler will then be wrapped up with the other handlers from the chain.
	handler = wrapMiddleware(wrapMiddleware(handler, mw), a.mw)

	// The function to execute for each request.
	h := func(w http.ResponseWriter, r *http.Request, params map[string]string) {

		// Create the context for the request.
		ctx, cancel := context.WithCancel(r.Context())
		defer cancel()

		// Set the context with the required values to
		// process the request.
		v := Values{
			TraceID: uuid.New(),
			Now:     time.Now(),
		}
		ctx = context.WithValue(ctx, KeyValues, &v)

		// Set the trace id on the outgoing requests before any other header to
		// ensure that the trace id is ALWAYS added to the request regardless of
		// any error occuring or not.
		w.Header().Set(TraceIDHeader, v.TraceID)

		// Call the wrapped handler functions.
		handler(ctx, w, r, params)
	}

	// Add this handler for the specified verb and route.
	a.TreeMux.Handle(verb, path, h)
}

//==============================================================================

// Group allows a segment of middleware to be shared amongst handlers.
type Group struct {
	app *App
	mw  []Middleware
}

// Use adds the set of provided middleware onto the Application middleware chain.
func (g *Group) Use(mw ...Middleware) {
	g.mw = append(g.mw, mw...)
}

// Handle proxies the Handle function of the underlying App.
func (g *Group) Handle(verb, path string, handler Handler, mw ...Middleware) {

	// Wrap up the route specific middleware last because rememeber, the
	// middleware is wrapped backwards.
	handler = wrapMiddleware(handler, mw)

	// Wrap it with the App wrapper and additionally the group level middleware.
	g.app.Handle(verb, path, handler, g.mw...)
}

//==============================================================================

// Run is called to start the web service.
func Run(host string, routes http.Handler, readTimeout, writeTimeout time.Duration) error {
	log.Printf("Run : Start : Using Host[%s]\n", host)

	// Create a new server and set timeout values.
	server := manners.NewWithServer(&http.Server{
		Addr:           host,
		Handler:        routes,
		ReadTimeout:    readTimeout,
		WriteTimeout:   writeTimeout,
		MaxHeaderBytes: 1 << 20,
	})

	go func() {

		// Listen for an interrupt signal from the OS.
		osSignals := make(chan os.Signal)
		signal.Notify(osSignals, os.Interrupt)

		sig := <-osSignals
		log.Printf("Run : Captured %v. Shutting Down...\n", sig)

		// Shut down the API server.
		server.Close()
	}()

	return server.ListenAndServe()
}

//==============================================================================

// wrapMiddleware wraps a handler with some middleware.
func wrapMiddleware(handler Handler, mw []Middleware) Handler {

	// Wrap with our group specific middleware.
	for i := len(mw) - 1; i >= 0; i-- {
		if mw[i] != nil {
			handler = mw[i](handler)
		}
	}

	return handler
}
<commit_msg>Realized I don't need the new context since the request context is valid.<commit_after>// Package web provides a thin layer of support for writing web services. It
// integrates with the ardanlabs kit repo to provide support for routing and
// application ctx. The base things you need to write a web service is
// provided.
package web

import (
	"context"
	"encoding/json"
	"io"
	"log"
	"net/http"
	"os"
	"os/signal"
	"time"

	"github.com/braintree/manners"
	"github.com/dimfeld/httptreemux"
	"github.com/pborman/uuid"
	"gopkg.in/go-playground/validator.v8"
)

// TraceIDHeader is the header added to outgoing requests which adds the
// traceID to it.
const TraceIDHeader = "X-Trace-ID"

//==============================================================================

// validate provides a validator for checking models.
var validate = validator.New(&validator.Config{
	TagName:      "validate",
	FieldNameTag: "json",
})

// Unmarshal decodes the input to the struct type and checks the
// fields to verify the value is in a proper state.
func Unmarshal(r io.Reader, v interface{}) error {
	if err := json.NewDecoder(r).Decode(v); err != nil {
		return err
	}

	var inv InvalidError
	if fve := validate.Struct(v); fve != nil {
		for _, fe := range fve.(validator.ValidationErrors) {
			inv = append(inv, Invalid{Fld: fe.Field, Err: fe.Tag})
		}
		return inv
	}

	return nil
}

//==============================================================================

// Key represents the type of value for the context key.
type ctxKey int

// KeyValues is how request values or stored/retrieved.
const KeyValues ctxKey = 1

// Values represent state for each request.
type Values struct {
	TraceID    string
	Now        time.Time
	StatusCode int
}

//==============================================================================

// A Handler is a type that handles an http request within our own little mini
// framework.
type Handler func(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error

// A Middleware is a type that wraps a handler to remove boilerplate or other
// concerns not direct to any given Handler.
type Middleware func(Handler) Handler

//==============================================================================

// App is the entrypoint into our application and what configures our context
// object for each of our http handlers. Feel free to add any configuration
// data/logic on this App struct
type App struct {
	*httptreemux.TreeMux
	Values map[string]interface{}

	mw []Middleware
}

// New create an App value that handle a set of routes for the application.
// You can provide any number of middleware and they'll be used to wrap every
// request handler.
func New(mw ...Middleware) *App {
	return &App{
		TreeMux: httptreemux.New(),
		Values:  make(map[string]interface{}),
		mw:      mw,
	}
}

// Group creates a new App Group based on the current App and provided
// middleware.
func (a *App) Group(mw ...Middleware) *Group {
	return &Group{
		app: a,
		mw:  mw,
	}
}

// Use adds the set of provided middleware onto the Application middleware
// chain. Any route running off of this App will use all the middleware provided
// this way always regardless of the ordering of the Handle/Use functions.
func (a *App) Use(mw ...Middleware) {
	a.mw = append(a.mw, mw...)
}

// Handle is our mechanism for mounting Handlers for a given HTTP verb and path
// pair, this makes for really easy, convenient routing.
func (a *App) Handle(verb, path string, handler Handler, mw ...Middleware) {

	// Wrap up the application-wide first, this will call the first function
	// of each middleware which will return a function of type Handler. Each
	// Handler will then be wrapped up with the other handlers from the chain.
	handler = wrapMiddleware(wrapMiddleware(handler, mw), a.mw)

	// The function to execute for each request.
	h := func(w http.ResponseWriter, r *http.Request, params map[string]string) {

		// Set the context with the required values to
		// process the request.
		v := Values{
			TraceID: uuid.New(),
			Now:     time.Now(),
		}
		ctx := context.WithValue(r.Context(), KeyValues, &v)

		// Set the trace id on the outgoing requests before any other header to
		// ensure that the trace id is ALWAYS added to the request regardless of
		// any error occuring or not.
		w.Header().Set(TraceIDHeader, v.TraceID)

		// Call the wrapped handler functions.
		handler(ctx, w, r, params)
	}

	// Add this handler for the specified verb and route.
	a.TreeMux.Handle(verb, path, h)
}

//==============================================================================

// Group allows a segment of middleware to be shared amongst handlers.
type Group struct {
	app *App
	mw  []Middleware
}

// Use adds the set of provided middleware onto the Application middleware chain.
func (g *Group) Use(mw ...Middleware) {
	g.mw = append(g.mw, mw...)
}

// Handle proxies the Handle function of the underlying App.
func (g *Group) Handle(verb, path string, handler Handler, mw ...Middleware) {

	// Wrap up the route specific middleware last because rememeber, the
	// middleware is wrapped backwards.
	handler = wrapMiddleware(handler, mw)

	// Wrap it with the App wrapper and additionally the group level middleware.
	g.app.Handle(verb, path, handler, g.mw...)
}

//==============================================================================

// Run is called to start the web service.
func Run(host string, routes http.Handler, readTimeout, writeTimeout time.Duration) error {
	log.Printf("Run : Start : Using Host[%s]\n", host)

	// Create a new server and set timeout values.
	server := manners.NewWithServer(&http.Server{
		Addr:           host,
		Handler:        routes,
		ReadTimeout:    readTimeout,
		WriteTimeout:   writeTimeout,
		MaxHeaderBytes: 1 << 20,
	})

	go func() {

		// Listen for an interrupt signal from the OS.
		osSignals := make(chan os.Signal)
		signal.Notify(osSignals, os.Interrupt)

		sig := <-osSignals
		log.Printf("Run : Captured %v. Shutting Down...\n", sig)

		// Shut down the API server.
		server.Close()
	}()

	return server.ListenAndServe()
}

//==============================================================================

// wrapMiddleware wraps a handler with some middleware.
func wrapMiddleware(handler Handler, mw []Middleware) Handler {

	// Wrap with our group specific middleware.
	for i := len(mw) - 1; i >= 0; i-- {
		if mw[i] != nil {
			handler = mw[i](handler)
		}
	}

	return handler
}
<|endoftext|>


<commit_before>// Copyright 2015 Matthew Collins
//
// 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 steven

import (
	"fmt"
	"math"
	"runtime"
	"time"

	"github.com/go-gl/glfw/v3.1/glfw"
	"github.com/thinkofdeath/steven/chat"
	"github.com/thinkofdeath/steven/protocol"
	"github.com/thinkofdeath/steven/protocol/mojang"
	"github.com/thinkofdeath/steven/render"
	"github.com/thinkofdeath/steven/ui"
)

var (
	profile       mojang.Profile
	server        string
	loadChan      = make(chan struct{})
	currentScreen screen
	connected     bool
)

type screen interface {
	init()
	tick(delta float64)
	hover(x, y float64, w, h int)
	click(down bool, x, y float64, w, h int)
	remove()
}

func setScreen(s screen) {
	if currentScreen != nil {
		currentScreen.remove()
	}
	currentScreen = s
	if s != nil {
		lockMouse = false
		window.SetInputMode(glfw.CursorMode, glfw.CursorNormal)
		for i := range Client.KeyState {
			Client.KeyState[i] = false
		}
		s.init()
	} else {
		Client.scene.Show()
	}
}

func Main(username, uuid, accessToken, s string) {
	profile = mojang.Profile{
		Username:    username,
		ID:          uuid,
		AccessToken: accessToken,
	}
	server = s

	// Done on its own goroutine so the connection
	// + window opening, can't be done in parallel
	go func() {
		render.LoadTextures()
		initBlocks()
		loadChan <- struct{}{}
	}()

	if profile.IsComplete() && server != "" {
		// Start connecting whilst starting the renderer
		connect()
	} else {
		Client.valid = false
	}

	setUIScale()

	startWindow()
}

func connect() {
	connected = true
	disconnectReason.Value = nil
	go startConnection(profile, server)
	server = ""
}

func setUIScale() {
	switch Config.Game.UIScale {
	case uiAuto:
		ui.DrawMode = ui.Scaled
		ui.Scale = 1.0
	case uiSmall:
		ui.DrawMode = ui.Unscaled
		ui.Scale = 0.4
	case uiMedium:
		ui.DrawMode = ui.Unscaled
		ui.Scale = 0.6
	case uiLarge:
		ui.DrawMode = ui.Unscaled
		ui.Scale = 1.0
	}
}

func start() {
	<-loadChan
	if Client.valid {
		Client.init()
	} else {
		fakeGen()
		if !profile.IsComplete() {
			setScreen(newLoginScreen())
		} else {
			setScreen(newServerList())
		}
	}
	render.FOV = Config.Render.FOV
	render.Start()
}

func rotate(x, y float64) {
	Client.Yaw -= x
	Client.Pitch -= y
	if Client.Pitch < (math.Pi/2)+0.01 {
		Client.Pitch = (math.Pi / 2) + 0.01
	}
	if Client.Pitch > (math.Pi/2)*3-0.01 {
		Client.Pitch = (math.Pi/2)*3 - 0.01
	}
}

var maxBuilders = runtime.NumCPU() * 2

var (
	ready            bool
	freeBuilders     = maxBuilders
	completeBuilders = make(chan buildPos, maxBuilders)
	syncChan         = make(chan func(), 200)
	ticker           = time.NewTicker(time.Second / 20)
	lastFrame        = time.Now()
)

func handleErrors() {
handle:
	for {
		select {
		case err := <-errorChan:
			if !connected {
				continue
			}
			connected = false

			// Replace the writer ready for the next connection
			close(writeChan)
			writeChan = make(chan protocol.Packet, 200)

			if conn != nil {
				conn.Close()
			}
			fmt.Printf("Disconnected: %s\n", err)
			// Reset the ready state to stop packets from being
			// sent.
			ready = false
			if err != errManualDisconnect && disconnectReason.Value == nil {
				txt := &chat.TextComponent{Text: err.Error()}
				txt.Color = chat.Red
				disconnectReason.Value = txt
			}
			setScreen(newServerList())
		default:
			break handle
		}
	}
}

func draw() {
	now := time.Now()
	diff := now.Sub(lastFrame)
	lastFrame = now
	delta := float64(diff.Nanoseconds()) / (float64(time.Second) / 60)
	delta = math.Min(delta, 1.6)
handle:
	for {
		select {
		case packet := <-readChan:
			defaultHandler.Handle(packet)
		case pos := <-completeBuilders:
			freeBuilders++
			if c := chunkMap[chunkPosition{pos.X, pos.Z}]; c != nil {
				if s := c.Sections[pos.Y]; s != nil {
					s.building = false
				}
			}
		case f := <-syncChan:
			f()
		default:
			break handle
		}
	}
	handleErrors()

	width, height := window.GetFramebufferSize()

	if currentScreen != nil {
		currentScreen.tick(delta)
	}

	if ready && Client.valid {
		Client.renderTick(delta)
		select {
		case <-ticker.C:
			tick()
		default:
		}
	} else {
		render.Camera.Yaw += 0.005 * delta
		if render.Camera.Yaw > math.Pi*2 {
			render.Camera.Yaw = 0
		}
	}
	ui.Draw(width, height, delta)

	render.Draw(width, height, delta)
	chunks := sortedChunks()

	// Search for 'dirty' chunk sections and start building
	// them if we have any builders free. To prevent race conditions
	// two flags are used, dirty and building, to allow a second
	// build to be requested whilst the chunk is still building
	// without either losing the change or having two builds
	// for the same section going on at once (where the second
	// could finish quicker causing the old version to be
	// displayed.
dirtyClean:
	for _, c := range chunks {
		for _, s := range c.Sections {
			if s == nil {
				continue
			}
			if freeBuilders <= 0 {
				break dirtyClean
			}
			if s.dirty && !s.building {
				freeBuilders--
				s.dirty = false
				s.building = true
				s.build(completeBuilders)
			}
		}
	}
}

// tick is called 20 times a second (bar any preformance issues).
// Minecraft is built around this fact so we have to follow it
// as well.
func tick() {
	Client.tick()
}
<commit_msg>steven: remove the player model on disconnect<commit_after>// Copyright 2015 Matthew Collins
//
// 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 steven

import (
	"fmt"
	"math"
	"runtime"
	"time"

	"github.com/go-gl/glfw/v3.1/glfw"
	"github.com/thinkofdeath/steven/chat"
	"github.com/thinkofdeath/steven/protocol"
	"github.com/thinkofdeath/steven/protocol/mojang"
	"github.com/thinkofdeath/steven/render"
	"github.com/thinkofdeath/steven/ui"
)

var (
	profile       mojang.Profile
	server        string
	loadChan      = make(chan struct{})
	currentScreen screen
	connected     bool
)

type screen interface {
	init()
	tick(delta float64)
	hover(x, y float64, w, h int)
	click(down bool, x, y float64, w, h int)
	remove()
}

func setScreen(s screen) {
	if currentScreen != nil {
		currentScreen.remove()
	}
	currentScreen = s
	if s != nil {
		lockMouse = false
		window.SetInputMode(glfw.CursorMode, glfw.CursorNormal)
		for i := range Client.KeyState {
			Client.KeyState[i] = false
		}
		s.init()
	} else {
		Client.scene.Show()
	}
}

func Main(username, uuid, accessToken, s string) {
	profile = mojang.Profile{
		Username:    username,
		ID:          uuid,
		AccessToken: accessToken,
	}
	server = s

	// Done on its own goroutine so the connection
	// + window opening, can't be done in parallel
	go func() {
		render.LoadTextures()
		initBlocks()
		loadChan <- struct{}{}
	}()

	if profile.IsComplete() && server != "" {
		// Start connecting whilst starting the renderer
		connect()
	} else {
		Client.valid = false
	}

	setUIScale()

	startWindow()
}

func connect() {
	connected = true
	disconnectReason.Value = nil
	go startConnection(profile, server)
	server = ""
}

func setUIScale() {
	switch Config.Game.UIScale {
	case uiAuto:
		ui.DrawMode = ui.Scaled
		ui.Scale = 1.0
	case uiSmall:
		ui.DrawMode = ui.Unscaled
		ui.Scale = 0.4
	case uiMedium:
		ui.DrawMode = ui.Unscaled
		ui.Scale = 0.6
	case uiLarge:
		ui.DrawMode = ui.Unscaled
		ui.Scale = 1.0
	}
}

func start() {
	<-loadChan
	if Client.valid {
		Client.init()
	} else {
		fakeGen()
		if !profile.IsComplete() {
			setScreen(newLoginScreen())
		} else {
			setScreen(newServerList())
		}
	}
	render.FOV = Config.Render.FOV
	render.Start()
}

func rotate(x, y float64) {
	Client.Yaw -= x
	Client.Pitch -= y
	if Client.Pitch < (math.Pi/2)+0.01 {
		Client.Pitch = (math.Pi / 2) + 0.01
	}
	if Client.Pitch > (math.Pi/2)*3-0.01 {
		Client.Pitch = (math.Pi/2)*3 - 0.01
	}
}

var maxBuilders = runtime.NumCPU() * 2

var (
	ready            bool
	freeBuilders     = maxBuilders
	completeBuilders = make(chan buildPos, maxBuilders)
	syncChan         = make(chan func(), 200)
	ticker           = time.NewTicker(time.Second / 20)
	lastFrame        = time.Now()
)

func handleErrors() {
handle:
	for {
		select {
		case err := <-errorChan:
			if !connected {
				continue
			}
			connected = false

			// Replace the writer ready for the next connection
			close(writeChan)
			writeChan = make(chan protocol.Packet, 200)

			if conn != nil {
				conn.Close()
			}
			fmt.Printf("Disconnected: %s\n", err)
			// Reset the ready state to stop packets from being
			// sent.
			ready = false
			if err != errManualDisconnect && disconnectReason.Value == nil {
				txt := &chat.TextComponent{Text: err.Error()}
				txt.Color = chat.Red
				disconnectReason.Value = txt
			}

			if Client.entity != nil && Client.entityAdded {
				Client.entityAdded = false
				Client.entities.container.RemoveEntity(Client.entity)
			}

			setScreen(newServerList())
		default:
			break handle
		}
	}
}

func draw() {
	now := time.Now()
	diff := now.Sub(lastFrame)
	lastFrame = now
	delta := float64(diff.Nanoseconds()) / (float64(time.Second) / 60)
	delta = math.Min(delta, 1.6)
handle:
	for {
		select {
		case packet := <-readChan:
			defaultHandler.Handle(packet)
		case pos := <-completeBuilders:
			freeBuilders++
			if c := chunkMap[chunkPosition{pos.X, pos.Z}]; c != nil {
				if s := c.Sections[pos.Y]; s != nil {
					s.building = false
				}
			}
		case f := <-syncChan:
			f()
		default:
			break handle
		}
	}
	handleErrors()

	width, height := window.GetFramebufferSize()

	if currentScreen != nil {
		currentScreen.tick(delta)
	}

	if ready && Client.valid {
		Client.renderTick(delta)
		select {
		case <-ticker.C:
			tick()
		default:
		}
	} else {
		render.Camera.Yaw += 0.005 * delta
		if render.Camera.Yaw > math.Pi*2 {
			render.Camera.Yaw = 0
		}
	}
	ui.Draw(width, height, delta)

	render.Draw(width, height, delta)
	chunks := sortedChunks()

	// Search for 'dirty' chunk sections and start building
	// them if we have any builders free. To prevent race conditions
	// two flags are used, dirty and building, to allow a second
	// build to be requested whilst the chunk is still building
	// without either losing the change or having two builds
	// for the same section going on at once (where the second
	// could finish quicker causing the old version to be
	// displayed.
dirtyClean:
	for _, c := range chunks {
		for _, s := range c.Sections {
			if s == nil {
				continue
			}
			if freeBuilders <= 0 {
				break dirtyClean
			}
			if s.dirty && !s.building {
				freeBuilders--
				s.dirty = false
				s.building = true
				s.build(completeBuilders)
			}
		}
	}
}

// tick is called 20 times a second (bar any preformance issues).
// Minecraft is built around this fact so we have to follow it
// as well.
func tick() {
	Client.tick()
}
<|endoftext|>


<commit_before>package opus

import (
	"fmt"
	"io"
	"unsafe"
)

/*
#cgo CFLAGS: -std=c99 -Wall -Werror -pedantic -Ibuild/include/opus
#include <opusfile.h>
#include <string.h>
#include "callback_proxy.h"
*/
import "C"

type Stream struct {
	oggfile *C.OggOpusFile
	read    io.Reader
	// Preallocated buffer to pass to the reader
	buf []byte
}

//export go_readproxy
func go_readproxy(p unsafe.Pointer, cbuf *C.uchar, cmaxbytes C.int) C.int {
	stream := (*Stream)(p)
	maxbytes := int(cmaxbytes)
	if maxbytes > cap(stream.buf) {
		maxbytes = cap(stream.buf)
	}
	// Don't bother cleaning up old data because that's not required by the
	// io.Reader API.
	n, err := stream.read.Read(stream.buf[:maxbytes])
	if err != nil || n == 0 {
		return 0
	}
	C.memcpy(unsafe.Pointer(cbuf), unsafe.Pointer(&stream.buf[0]), C.size_t(n))
	return C.int(n)
}

var callbacks = C.struct_OpusFileCallbacks{
	read:  C.op_read_func(C.c_readproxy),
	seek:  nil,
	tell:  nil,
	close: nil,
}

func NewStream(read io.Reader) (*Stream, error) {
	var s Stream
	err := s.Init(read)
	if err != nil {
		return nil, err
	}
	return &s, nil
}

// Init initializes a stream with an io.Reader to fetch opus encoded data from
// on demand. Errors from the reader are all transformed to an EOF, any actual
// error information is lost. The same happens when a read returns succesfully,
// but with zero bytes.
func (s *Stream) Init(read io.Reader) error {
	if s.oggfile != nil {
		return fmt.Errorf("opus stream is already initialized")
	}
	if read == nil {
		return fmt.Errorf("Reader must be non-nil")
	}
	s.read = read
	s.buf = make([]byte, maxEncodedFrameSize)
	var errno C.int
	oggfile := C.op_open_callbacks(
		unsafe.Pointer(s),
		&callbacks,
		nil,
		0,
		&errno)
	if errno != 0 {
		return opusfileerr(errno)
	}
	s.oggfile = oggfile
	return nil
}

// Read a chunk of raw opus data from the stream and decode it. Returns the
// number of decoded samples per channel. This means that a dual channel
// (stereo) feed will have twice as many samples as the value returned.
//
// Read may successfully read less bytes than requested, but it will never read
// exactly zero bytes succesfully if a non-zero buffer is supplied.
func (s *Stream) Read(pcm []int16) (int, error) {
	if s.oggfile == nil {
		return 0, fmt.Errorf("opus stream is uninitialized or already closed")
	}
	if len(pcm) == 0 {
		return 0, nil
	}
	n := C.op_read(
		s.oggfile,
		(*C.opus_int16)(&pcm[0]),
		C.int(len(pcm)),
		nil)
	if n < 0 {
		return 0, opusfileerr(n)
	}
	if n == 0 {
		return 0, io.EOF
	}
	return int(n), nil
}

func (s *Stream) ReadFloat32(pcm []float32) (int, error) {
	if s.oggfile == nil {
		return 0, fmt.Errorf("opus stream is uninitialized or already closed")
	}
	if len(pcm) == 0 {
		return 0, nil
	}
	n := C.op_read_float(
		s.oggfile,
		(*C.float)(&pcm[0]),
		C.int(len(pcm)),
		nil)
	if n < 0 {
		return 0, opusfileerr(n)
	}
	if n == 0 {
		return 0, io.EOF
	}
	return int(n), nil
}

func (s *Stream) Close() error {
	if s.oggfile == nil {
		return fmt.Errorf("opus stream is uninitialized or already closed")
	}
	C.op_free(s.oggfile)
	if closer, ok := s.read.(io.Closer); ok {
		return closer.Close()
	}
	return nil
}
<commit_msg>Flush last good bytes before returning EOF<commit_after>package opus

import (
	"fmt"
	"io"
	"unsafe"
)

/*
#cgo CFLAGS: -std=c99 -Wall -Werror -pedantic -Ibuild/include/opus
#include <opusfile.h>
#include <string.h>
#include "callback_proxy.h"
*/
import "C"

type Stream struct {
	oggfile *C.OggOpusFile
	read    io.Reader
	// Preallocated buffer to pass to the reader
	buf []byte
}

//export go_readproxy
func go_readproxy(p unsafe.Pointer, cbuf *C.uchar, cmaxbytes C.int) C.int {
	stream := (*Stream)(p)
	maxbytes := int(cmaxbytes)
	if maxbytes > cap(stream.buf) {
		maxbytes = cap(stream.buf)
	}
	// Don't bother cleaning up old data because that's not required by the
	// io.Reader API.
	n, err := stream.read.Read(stream.buf[:maxbytes])
	if (err != nil && err != io.EOF) || n == 0 {
		return 0
	}
	C.memcpy(unsafe.Pointer(cbuf), unsafe.Pointer(&stream.buf[0]), C.size_t(n))
	return C.int(n)
}

var callbacks = C.struct_OpusFileCallbacks{
	read:  C.op_read_func(C.c_readproxy),
	seek:  nil,
	tell:  nil,
	close: nil,
}

func NewStream(read io.Reader) (*Stream, error) {
	var s Stream
	err := s.Init(read)
	if err != nil {
		return nil, err
	}
	return &s, nil
}

// Init initializes a stream with an io.Reader to fetch opus encoded data from
// on demand. Errors from the reader are all transformed to an EOF, any actual
// error information is lost. The same happens when a read returns succesfully,
// but with zero bytes.
func (s *Stream) Init(read io.Reader) error {
	if s.oggfile != nil {
		return fmt.Errorf("opus stream is already initialized")
	}
	if read == nil {
		return fmt.Errorf("Reader must be non-nil")
	}
	s.read = read
	s.buf = make([]byte, maxEncodedFrameSize)
	var errno C.int
	oggfile := C.op_open_callbacks(
		unsafe.Pointer(s),
		&callbacks,
		nil,
		0,
		&errno)
	if errno != 0 {
		return opusfileerr(errno)
	}
	s.oggfile = oggfile
	return nil
}

// Read a chunk of raw opus data from the stream and decode it. Returns the
// number of decoded samples per channel. This means that a dual channel
// (stereo) feed will have twice as many samples as the value returned.
//
// Read may successfully read less bytes than requested, but it will never read
// exactly zero bytes succesfully if a non-zero buffer is supplied.
//
// The number of channels in the output data must be known in advance. It is
// possible to extract this information from the stream itself, but I'm not
// motivated to do that. Feel free to send a pull request.
func (s *Stream) Read(pcm []int16) (int, error) {
	if s.oggfile == nil {
		return 0, fmt.Errorf("opus stream is uninitialized or already closed")
	}
	if len(pcm) == 0 {
		return 0, nil
	}
	n := C.op_read(
		s.oggfile,
		(*C.opus_int16)(&pcm[0]),
		C.int(len(pcm)),
		nil)
	if n < 0 {
		return 0, opusfileerr(n)
	}
	if n == 0 {
		return 0, io.EOF
	}
	return int(n), nil
}

func (s *Stream) ReadFloat32(pcm []float32) (int, error) {
	if s.oggfile == nil {
		return 0, fmt.Errorf("opus stream is uninitialized or already closed")
	}
	if len(pcm) == 0 {
		return 0, nil
	}
	n := C.op_read_float(
		s.oggfile,
		(*C.float)(&pcm[0]),
		C.int(len(pcm)),
		nil)
	if n < 0 {
		return 0, opusfileerr(n)
	}
	if n == 0 {
		return 0, io.EOF
	}
	return int(n), nil
}

func (s *Stream) Close() error {
	if s.oggfile == nil {
		return fmt.Errorf("opus stream is uninitialized or already closed")
	}
	C.op_free(s.oggfile)
	if closer, ok := s.read.(io.Closer); ok {
		return closer.Close()
	}
	return nil
}
<|endoftext|>


<commit_before>package yamux

import (
	"bytes"
	"io"
	"sync"
	"sync/atomic"
	"time"
)

type streamState int

const (
	streamInit streamState = iota
	streamSYNSent
	streamSYNReceived
	streamEstablished
	streamLocalClose
	streamRemoteClose
	streamClosed
	streamReset
)

// Stream is used to represent a logical stream
// within a session.
type Stream struct {
	recvWindow uint32
	sendWindow uint32

	id      uint32
	session *Session

	state     streamState
	stateLock sync.Mutex

	recvBuf  *bytes.Buffer
	recvLock sync.Mutex

	controlHdr     header
	controlErr     chan error
	controlHdrLock sync.Mutex

	sendHdr  header
	sendErr  chan error
	sendLock sync.Mutex

	recvNotifyCh chan struct{}
	sendNotifyCh chan struct{}

	readDeadline  time.Time
	writeDeadline time.Time
}

// newStream is used to construct a new stream within
// a given session for an ID
func newStream(session *Session, id uint32, state streamState) *Stream {
	s := &Stream{
		id:           id,
		session:      session,
		state:        state,
		controlHdr:   header(make([]byte, headerSize)),
		controlErr:   make(chan error, 1),
		sendHdr:      header(make([]byte, headerSize)),
		sendErr:      make(chan error, 1),
		recvWindow:   initialStreamWindow,
		sendWindow:   initialStreamWindow,
		recvNotifyCh: make(chan struct{}, 1),
		sendNotifyCh: make(chan struct{}, 1),
	}
	return s
}

// Session returns the associated stream session
func (s *Stream) Session() *Session {
	return s.session
}

// StreamID returns the ID of this stream
func (s *Stream) StreamID() uint32 {
	return s.id
}

// Read is used to read from the stream
func (s *Stream) Read(b []byte) (n int, err error) {
	defer asyncNotify(s.recvNotifyCh)
START:
	s.stateLock.Lock()
	switch s.state {
	case streamLocalClose:
		fallthrough
	case streamRemoteClose:
		fallthrough
	case streamClosed:
		if s.recvBuf == nil || s.recvBuf.Len() == 0 {
			s.stateLock.Unlock()
			return 0, io.EOF
		}
	case streamReset:
		s.stateLock.Unlock()
		return 0, ErrConnectionReset
	}
	s.stateLock.Unlock()

	// If there is no data available, block
	s.recvLock.Lock()
	if s.recvBuf == nil || s.recvBuf.Len() == 0 {
		s.recvLock.Unlock()
		goto WAIT
	}

	// Read any bytes
	n, _ = s.recvBuf.Read(b)
	s.recvLock.Unlock()

	// Send a window update potentially
	err = s.sendWindowUpdate()
	return n, err

WAIT:
	var timeout <-chan time.Time
	if !s.readDeadline.IsZero() {
		delay := s.readDeadline.Sub(time.Now())
		timeout = time.After(delay)
	}
	select {
	case <-s.recvNotifyCh:
		goto START
	case <-timeout:
		return 0, ErrTimeout
	}
}

// Write is used to write to the stream
func (s *Stream) Write(b []byte) (n int, err error) {
	s.sendLock.Lock()
	defer s.sendLock.Unlock()
	total := 0
	for total < len(b) {
		n, err := s.write(b[total:])
		total += n
		if err != nil {
			return total, err
		}
	}
	return total, nil
}

// write is used to write to the stream, may return on
// a short write.
func (s *Stream) write(b []byte) (n int, err error) {
	var flags uint16
	var max uint32
	var body io.Reader
START:
	s.stateLock.Lock()
	switch s.state {
	case streamLocalClose:
		fallthrough
	case streamClosed:
		s.stateLock.Unlock()
		return 0, ErrStreamClosed
	case streamReset:
		s.stateLock.Unlock()
		return 0, ErrConnectionReset
	}
	s.stateLock.Unlock()

	// If there is no data available, block
	window := atomic.LoadUint32(&s.sendWindow)
	if window == 0 {
		goto WAIT
	}

	// Determine the flags if any
	flags = s.sendFlags()

	// Send up to our send window
	max = min(window, uint32(len(b)))
	body = bytes.NewReader(b[:max])

	// Send the header
	s.sendHdr.encode(typeData, flags, s.id, max)
	if err := s.session.waitForSendErr(s.sendHdr, body, s.sendErr); err != nil {
		return 0, err
	}

	// Reduce our send window
	atomic.AddUint32(&s.sendWindow, ^uint32(max-1))

	// Unlock
	return int(max), err

WAIT:
	var timeout <-chan time.Time
	if !s.writeDeadline.IsZero() {
		delay := s.writeDeadline.Sub(time.Now())
		timeout = time.After(delay)
	}
	select {
	case <-s.sendNotifyCh:
		goto START
	case <-timeout:
		return 0, ErrTimeout
	}
	return 0, nil
}

// sendFlags determines any flags that are appropriate
// based on the current stream state
func (s *Stream) sendFlags() uint16 {
	s.stateLock.Lock()
	defer s.stateLock.Unlock()
	var flags uint16
	switch s.state {
	case streamInit:
		flags |= flagSYN
		s.state = streamSYNSent
	case streamSYNReceived:
		flags |= flagACK
		s.state = streamEstablished
	}
	return flags
}

// sendWindowUpdate potentially sends a window update enabling
// further writes to take place. Must be invoked with the lock.
func (s *Stream) sendWindowUpdate() error {
	s.controlHdrLock.Lock()
	defer s.controlHdrLock.Unlock()

	// Determine the delta update
	max := s.session.config.MaxStreamWindowSize
	delta := max - atomic.LoadUint32(&s.recvWindow)

	// Determine the flags if any
	flags := s.sendFlags()

	// Check if we can omit the update
	if delta < (max/2) && flags == 0 {
		return nil
	}

	// Update our window
	atomic.AddUint32(&s.recvWindow, delta)

	// Send the header
	s.controlHdr.encode(typeWindowUpdate, flags, s.id, delta)
	if err := s.session.waitForSendErr(s.controlHdr, nil, s.controlErr); err != nil {
		return err
	}
	return nil
}

// sendClose is used to send a FIN
func (s *Stream) sendClose() error {
	s.controlHdrLock.Lock()
	defer s.controlHdrLock.Unlock()

	flags := s.sendFlags()
	flags |= flagFIN
	s.controlHdr.encode(typeWindowUpdate, flags, s.id, 0)
	if err := s.session.waitForSendErr(s.controlHdr, nil, s.controlErr); err != nil {
		return err
	}
	return nil
}

// Close is used to close the stream
func (s *Stream) Close() error {
	closeStream := false
	s.stateLock.Lock()
	switch s.state {
	// Opened means we need to signal a close
	case streamSYNSent:
		fallthrough
	case streamSYNReceived:
		fallthrough
	case streamEstablished:
		s.state = streamLocalClose
		goto SEND_CLOSE

	case streamLocalClose:
	case streamRemoteClose:
		s.state = streamClosed
		closeStream = true
		goto SEND_CLOSE

	case streamClosed:
	case streamReset:
	default:
		panic("unhandled state")
	}
	s.stateLock.Unlock()
	return nil
SEND_CLOSE:
	s.stateLock.Unlock()
	s.sendClose()
	s.notifyWaiting()
	if closeStream {
		s.session.closeStream(s.id)
	}
	return nil
}

// forceClose is used for when the session is exiting
func (s *Stream) forceClose() {
	s.stateLock.Lock()
	s.state = streamClosed
	s.stateLock.Unlock()
	s.notifyWaiting()
}

// processFlags is used to update the state of the stream
// based on set flags, if any. Lock must be held
func (s *Stream) processFlags(flags uint16) error {
	// Close the stream without holding the state lock
	closeStream := false
	defer func() {
		if closeStream {
			s.session.closeStream(s.id)
		}
	}()

	s.stateLock.Lock()
	defer s.stateLock.Unlock()
	if flags&flagACK == flagACK {
		if s.state == streamSYNSent {
			s.state = streamEstablished
		}
		s.session.establishStream()
	}
	if flags&flagFIN == flagFIN {
		switch s.state {
		case streamSYNSent:
			fallthrough
		case streamSYNReceived:
			fallthrough
		case streamEstablished:
			s.state = streamRemoteClose
			s.notifyWaiting()
		case streamLocalClose:
			s.state = streamClosed
			closeStream = true
			s.notifyWaiting()
		default:
			s.session.logger.Printf("[ERR] yamux: unexpected FIN flag in state %d", s.state)
			return ErrUnexpectedFlag
		}
	}
	if flags&flagRST == flagRST {
		if s.state == streamSYNSent {
			s.session.establishStream()
		}
		s.state = streamReset
		closeStream = true
		s.notifyWaiting()
	}
	return nil
}

// notifyWaiting notifies all the waiting channels
func (s *Stream) notifyWaiting() {
	asyncNotify(s.recvNotifyCh)
	asyncNotify(s.sendNotifyCh)
}

// incrSendWindow updates the size of our send window
func (s *Stream) incrSendWindow(hdr header, flags uint16) error {
	if err := s.processFlags(flags); err != nil {
		return err
	}

	// Increase window, unblock a sender
	atomic.AddUint32(&s.sendWindow, hdr.Length())
	asyncNotify(s.sendNotifyCh)
	return nil
}

// readData is used to handle a data frame
func (s *Stream) readData(hdr header, flags uint16, conn io.Reader) error {
	if err := s.processFlags(flags); err != nil {
		return err
	}

	// Check that our recv window is not exceeded
	length := hdr.Length()
	if length == 0 {
		return nil
	}
	if remain := atomic.LoadUint32(&s.recvWindow); length > remain {
		s.session.logger.Printf("[ERR] yamux: receive window exceeded (stream: %d, remain: %d, recv: %d)", s.id, remain, length)
		return ErrRecvWindowExceeded
	}

	// Wrap in a limited reader
	conn = &io.LimitedReader{R: conn, N: int64(length)}

	// Copy into buffer
	s.recvLock.Lock()
	if s.recvBuf == nil {
		// Allocate the receive buffer just-in-time to fit the full data frame.
		// This way we can read in the whole packet without further allocations.
		s.recvBuf = bytes.NewBuffer(make([]byte, 0, length))
	}
	if _, err := io.Copy(s.recvBuf, conn); err != nil {
		s.session.logger.Printf("[ERR] yamux: Failed to read stream data: %v", err)
		s.recvLock.Unlock()
		return err
	}

	// Decrement the receive window
	atomic.AddUint32(&s.recvWindow, ^uint32(length-1))
	s.recvLock.Unlock()

	// Unblock any readers
	asyncNotify(s.recvNotifyCh)
	return nil
}

// SetDeadline sets the read and write deadlines
func (s *Stream) SetDeadline(t time.Time) error {
	if err := s.SetReadDeadline(t); err != nil {
		return err
	}
	if err := s.SetWriteDeadline(t); err != nil {
		return err
	}
	return nil
}

// SetReadDeadline sets the deadline for future Read calls.
func (s *Stream) SetReadDeadline(t time.Time) error {
	s.readDeadline = t
	return nil
}

// SetWriteDeadline sets the deadline for future Write calls
func (s *Stream) SetWriteDeadline(t time.Time) error {
	s.writeDeadline = t
	return nil
}

// Shrink is used to compact the amount of buffers utilized
// This is useful when using Yamux in a connection pool to reduce
// the idle memory utilization.
func (s *Stream) Shrink() {
	s.recvLock.Lock()
	if s.recvBuf != nil && s.recvBuf.Len() == 0 {
		s.recvBuf = nil
	}
	s.recvLock.Unlock()
}
<commit_msg>properly handle closing streams before they are ACKed<commit_after>package yamux

import (
	"bytes"
	"io"
	"sync"
	"sync/atomic"
	"time"
)

type streamState int

const (
	streamInit streamState = iota
	streamSYNSent
	streamSYNReceived
	streamEstablished
	streamLocalClose
	streamRemoteClose
	streamEarlyClose
	streamClosed
	streamReset
)

// Stream is used to represent a logical stream
// within a session.
type Stream struct {
	recvWindow uint32
	sendWindow uint32

	id      uint32
	session *Session

	state     streamState
	stateLock sync.Mutex

	recvBuf  *bytes.Buffer
	recvLock sync.Mutex

	controlHdr     header
	controlErr     chan error
	controlHdrLock sync.Mutex

	sendHdr  header
	sendErr  chan error
	sendLock sync.Mutex

	recvNotifyCh chan struct{}
	sendNotifyCh chan struct{}

	readDeadline  time.Time
	writeDeadline time.Time
}

// newStream is used to construct a new stream within
// a given session for an ID
func newStream(session *Session, id uint32, state streamState) *Stream {
	s := &Stream{
		id:           id,
		session:      session,
		state:        state,
		controlHdr:   header(make([]byte, headerSize)),
		controlErr:   make(chan error, 1),
		sendHdr:      header(make([]byte, headerSize)),
		sendErr:      make(chan error, 1),
		recvWindow:   initialStreamWindow,
		sendWindow:   initialStreamWindow,
		recvNotifyCh: make(chan struct{}, 1),
		sendNotifyCh: make(chan struct{}, 1),
	}
	return s
}

// Session returns the associated stream session
func (s *Stream) Session() *Session {
	return s.session
}

// StreamID returns the ID of this stream
func (s *Stream) StreamID() uint32 {
	return s.id
}

// Read is used to read from the stream
func (s *Stream) Read(b []byte) (n int, err error) {
	defer asyncNotify(s.recvNotifyCh)
START:
	s.stateLock.Lock()
	switch s.state {
	case streamLocalClose:
		fallthrough
	case streamRemoteClose:
		fallthrough
	case streamClosed:
		if s.recvBuf == nil || s.recvBuf.Len() == 0 {
			s.stateLock.Unlock()
			return 0, io.EOF
		}
	case streamReset:
		s.stateLock.Unlock()
		return 0, ErrConnectionReset
	}
	s.stateLock.Unlock()

	// If there is no data available, block
	s.recvLock.Lock()
	if s.recvBuf == nil || s.recvBuf.Len() == 0 {
		s.recvLock.Unlock()
		goto WAIT
	}

	// Read any bytes
	n, _ = s.recvBuf.Read(b)
	s.recvLock.Unlock()

	// Send a window update potentially
	err = s.sendWindowUpdate()
	return n, err

WAIT:
	var timeout <-chan time.Time
	if !s.readDeadline.IsZero() {
		delay := s.readDeadline.Sub(time.Now())
		timeout = time.After(delay)
	}
	select {
	case <-s.recvNotifyCh:
		goto START
	case <-timeout:
		return 0, ErrTimeout
	}
}

// Write is used to write to the stream
func (s *Stream) Write(b []byte) (n int, err error) {
	s.sendLock.Lock()
	defer s.sendLock.Unlock()
	total := 0
	for total < len(b) {
		n, err := s.write(b[total:])
		total += n
		if err != nil {
			return total, err
		}
	}
	return total, nil
}

// write is used to write to the stream, may return on
// a short write.
func (s *Stream) write(b []byte) (n int, err error) {
	var flags uint16
	var max uint32
	var body io.Reader
START:
	s.stateLock.Lock()
	switch s.state {
	case streamLocalClose:
		fallthrough
	case streamClosed:
		s.stateLock.Unlock()
		return 0, ErrStreamClosed
	case streamReset:
		s.stateLock.Unlock()
		return 0, ErrConnectionReset
	}
	s.stateLock.Unlock()

	// If there is no data available, block
	window := atomic.LoadUint32(&s.sendWindow)
	if window == 0 {
		goto WAIT
	}

	// Determine the flags if any
	flags = s.sendFlags()

	// Send up to our send window
	max = min(window, uint32(len(b)))
	body = bytes.NewReader(b[:max])

	// Send the header
	s.sendHdr.encode(typeData, flags, s.id, max)
	if err := s.session.waitForSendErr(s.sendHdr, body, s.sendErr); err != nil {
		return 0, err
	}

	// Reduce our send window
	atomic.AddUint32(&s.sendWindow, ^uint32(max-1))

	// Unlock
	return int(max), err

WAIT:
	var timeout <-chan time.Time
	if !s.writeDeadline.IsZero() {
		delay := s.writeDeadline.Sub(time.Now())
		timeout = time.After(delay)
	}
	select {
	case <-s.sendNotifyCh:
		goto START
	case <-timeout:
		return 0, ErrTimeout
	}
	return 0, nil
}

// sendFlags determines any flags that are appropriate
// based on the current stream state
func (s *Stream) sendFlags() uint16 {
	s.stateLock.Lock()
	defer s.stateLock.Unlock()
	var flags uint16
	switch s.state {
	case streamInit:
		flags |= flagSYN
		s.state = streamSYNSent
	case streamSYNReceived:
		flags |= flagACK
		s.state = streamEstablished
	case streamEarlyClose:
		flags |= flagACK
		s.state = streamRemoteClose
	}
	return flags
}

// sendWindowUpdate potentially sends a window update enabling
// further writes to take place. Must be invoked with the lock.
func (s *Stream) sendWindowUpdate() error {
	s.controlHdrLock.Lock()
	defer s.controlHdrLock.Unlock()

	// Determine the delta update
	max := s.session.config.MaxStreamWindowSize
	delta := max - atomic.LoadUint32(&s.recvWindow)

	// Determine the flags if any
	flags := s.sendFlags()

	// Check if we can omit the update
	if delta < (max/2) && flags == 0 {
		return nil
	}

	// Update our window
	atomic.AddUint32(&s.recvWindow, delta)

	// Send the header
	s.controlHdr.encode(typeWindowUpdate, flags, s.id, delta)
	if err := s.session.waitForSendErr(s.controlHdr, nil, s.controlErr); err != nil {
		return err
	}
	return nil
}

// sendClose is used to send a FIN
func (s *Stream) sendClose() error {
	s.controlHdrLock.Lock()
	defer s.controlHdrLock.Unlock()

	flags := s.sendFlags()
	flags |= flagFIN
	s.controlHdr.encode(typeWindowUpdate, flags, s.id, 0)
	if err := s.session.waitForSendErr(s.controlHdr, nil, s.controlErr); err != nil {
		return err
	}
	return nil
}

// Close is used to close the stream
func (s *Stream) Close() error {
	closeStream := false
	s.stateLock.Lock()
	switch s.state {
	// Opened means we need to signal a close
	case streamSYNSent:
		fallthrough
	case streamSYNReceived:
		fallthrough
	case streamEstablished:
		s.state = streamLocalClose
		goto SEND_CLOSE

	case streamLocalClose:
	case streamRemoteClose:
		s.state = streamClosed
		closeStream = true
		goto SEND_CLOSE

	case streamClosed:
	case streamReset:
	default:
		panic("unhandled state")
	}
	s.stateLock.Unlock()
	return nil
SEND_CLOSE:
	s.stateLock.Unlock()
	s.sendClose()
	s.notifyWaiting()
	if closeStream {
		s.session.closeStream(s.id)
	}
	return nil
}

// forceClose is used for when the session is exiting
func (s *Stream) forceClose() {
	s.stateLock.Lock()
	s.state = streamClosed
	s.stateLock.Unlock()
	s.notifyWaiting()
}

// processFlags is used to update the state of the stream
// based on set flags, if any. Lock must be held
func (s *Stream) processFlags(flags uint16) error {
	// Close the stream without holding the state lock
	closeStream := false
	defer func() {
		if closeStream {
			s.session.closeStream(s.id)
		}
	}()

	s.stateLock.Lock()
	defer s.stateLock.Unlock()
	if flags&flagACK == flagACK {
		if s.state == streamSYNSent {
			s.state = streamEstablished
		}
		s.session.establishStream()
	}
	if flags&flagFIN == flagFIN {
		switch s.state {
		case streamSYNReceived:
			s.state = streamEarlyClose
			s.notifyWaiting()
		case streamSYNSent:
			fallthrough
		case streamEstablished:
			s.state = streamRemoteClose
			s.notifyWaiting()
		case streamLocalClose:
			s.state = streamClosed
			closeStream = true
			s.notifyWaiting()
		default:
			s.session.logger.Printf("[ERR] yamux: unexpected FIN flag in state %d", s.state)
			return ErrUnexpectedFlag
		}
	}
	if flags&flagRST == flagRST {
		if s.state == streamSYNSent {
			s.session.establishStream()
		}
		s.state = streamReset
		closeStream = true
		s.notifyWaiting()
	}
	return nil
}

// notifyWaiting notifies all the waiting channels
func (s *Stream) notifyWaiting() {
	asyncNotify(s.recvNotifyCh)
	asyncNotify(s.sendNotifyCh)
}

// incrSendWindow updates the size of our send window
func (s *Stream) incrSendWindow(hdr header, flags uint16) error {
	if err := s.processFlags(flags); err != nil {
		return err
	}

	// Increase window, unblock a sender
	atomic.AddUint32(&s.sendWindow, hdr.Length())
	asyncNotify(s.sendNotifyCh)
	return nil
}

// readData is used to handle a data frame
func (s *Stream) readData(hdr header, flags uint16, conn io.Reader) error {
	if err := s.processFlags(flags); err != nil {
		return err
	}

	// Check that our recv window is not exceeded
	length := hdr.Length()
	if length == 0 {
		return nil
	}
	if remain := atomic.LoadUint32(&s.recvWindow); length > remain {
		s.session.logger.Printf("[ERR] yamux: receive window exceeded (stream: %d, remain: %d, recv: %d)", s.id, remain, length)
		return ErrRecvWindowExceeded
	}

	// Wrap in a limited reader
	conn = &io.LimitedReader{R: conn, N: int64(length)}

	// Copy into buffer
	s.recvLock.Lock()
	if s.recvBuf == nil {
		// Allocate the receive buffer just-in-time to fit the full data frame.
		// This way we can read in the whole packet without further allocations.
		s.recvBuf = bytes.NewBuffer(make([]byte, 0, length))
	}
	if _, err := io.Copy(s.recvBuf, conn); err != nil {
		s.session.logger.Printf("[ERR] yamux: Failed to read stream data: %v", err)
		s.recvLock.Unlock()
		return err
	}

	// Decrement the receive window
	atomic.AddUint32(&s.recvWindow, ^uint32(length-1))
	s.recvLock.Unlock()

	// Unblock any readers
	asyncNotify(s.recvNotifyCh)
	return nil
}

// SetDeadline sets the read and write deadlines
func (s *Stream) SetDeadline(t time.Time) error {
	if err := s.SetReadDeadline(t); err != nil {
		return err
	}
	if err := s.SetWriteDeadline(t); err != nil {
		return err
	}
	return nil
}

// SetReadDeadline sets the deadline for future Read calls.
func (s *Stream) SetReadDeadline(t time.Time) error {
	s.readDeadline = t
	return nil
}

// SetWriteDeadline sets the deadline for future Write calls
func (s *Stream) SetWriteDeadline(t time.Time) error {
	s.writeDeadline = t
	return nil
}

// Shrink is used to compact the amount of buffers utilized
// This is useful when using Yamux in a connection pool to reduce
// the idle memory utilization.
func (s *Stream) Shrink() {
	s.recvLock.Lock()
	if s.recvBuf != nil && s.recvBuf.Len() == 0 {
		s.recvBuf = nil
	}
	s.recvLock.Unlock()
}
<|endoftext|>


<commit_before>// Copyright 2013 com authors
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

package com

import (
	"bytes"
	"crypto/hmac"
	"crypto/md5"
	"crypto/rand"
	"crypto/sha1"
	"crypto/sha256"
	"encoding/gob"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"hash"
	"io"
	r "math/rand"
	"strconv"
	"strings"
	"time"
	"unicode"
)

// Md5 md5 hash string
func Md5(str string) string {
	m := md5.New()
	io.WriteString(m, str)
	return fmt.Sprintf("%x", m.Sum(nil))
}

func ByteMd5(b []byte) string {
	m := md5.New()
	m.Write(b)
	return hex.EncodeToString(m.Sum(nil))
}

func Token(key string, val []byte, args ...string) string {
	hm := hmac.New(sha1.New, []byte(key))
	hm.Write(val)
	for _, v := range args {
		hm.Write([]byte(v))
	}
	return fmt.Sprintf("%02x", hm.Sum(nil))
}

func Encode(data interface{}) ([]byte, error) {
	//return JsonEncode(data)
	return GobEncode(data)
}

func Decode(data []byte, to interface{}) error {
	//return JsonDecode(data, to)
	return GobDecode(data, to)
}

func GobEncode(data interface{}) ([]byte, error) {
	var buf bytes.Buffer
	enc := gob.NewEncoder(&buf)
	err := enc.Encode(&data)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

func GobDecode(data []byte, to interface{}) error {
	buf := bytes.NewBuffer(data)
	dec := gob.NewDecoder(buf)
	return dec.Decode(to)
}

func JsonEncode(data interface{}) ([]byte, error) {
	val, err := json.Marshal(data)
	if err != nil {
		return nil, err
	}
	return val, nil
}

func JsonDecode(data []byte, to interface{}) error {
	return json.Unmarshal(data, to)
}

func sha(m hash.Hash, str string) string {
	io.WriteString(m, str)
	return fmt.Sprintf("%x", m.Sum(nil))
}

// Sha1 sha1 hash string
func Sha1(str string) string {
	return sha(sha1.New(), str)
}

// Sha256 sha256 hash string
func Sha256(str string) string {
	return sha(sha256.New(), str)
}

// Ltrim trim space on left
func Ltrim(str string) string {
	return strings.TrimLeftFunc(str, unicode.IsSpace)
}

// Rtrim trim space on right
func Rtrim(str string) string {
	return strings.TrimRightFunc(str, unicode.IsSpace)
}

// Trim trim space in all string length
func Trim(str string) string {
	return strings.TrimSpace(str)
}

// StrRepeat repeat string times
func StrRepeat(str string, times int) string {
	return strings.Repeat(str, times)
}

// StrReplace replace find all occurs to string
func StrReplace(str string, find string, to string) string {
	return strings.Replace(str, find, to, -1)
}

// IsLetter returns true if the 'l' is an English letter.
func IsLetter(l uint8) bool {
	n := (l | 0x20) - 'a'
	if n >= 0 && n < 26 {
		return true
	}
	return false
}

// Expand replaces {k} in template with match[k] or subs[atoi(k)] if k is not in match.
func Expand(template string, match map[string]string, subs ...string) string {
	var p []byte
	var i int
	for {
		i = strings.Index(template, "{")
		if i < 0 {
			break
		}
		p = append(p, template[:i]...)
		template = template[i+1:]
		i = strings.Index(template, "}")
		if s, ok := match[template[:i]]; ok {
			p = append(p, s...)
		} else {
			j, _ := strconv.Atoi(template[:i])
			if j >= len(subs) {
				p = append(p, []byte("Missing")...)
			} else {
				p = append(p, subs[j]...)
			}
		}
		template = template[i+1:]
	}
	p = append(p, template...)
	return string(p)
}

// Reverse s string, support unicode
func Reverse(s string) string {
	n := len(s)
	runes := make([]rune, n)
	for _, rune := range s {
		n--
		runes[n] = rune
	}
	return string(runes[n:])
}

// RandomCreateBytes generate random []byte by specify chars.
func RandomCreateBytes(n int, alphabets ...byte) []byte {
	const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
	var bytes = make([]byte, n)
	var randby bool
	if num, err := rand.Read(bytes); num != n || err != nil {
		r.Seed(time.Now().UnixNano())
		randby = true
	}
	for i, b := range bytes {
		if len(alphabets) == 0 {
			if randby {
				bytes[i] = alphanum[r.Intn(len(alphanum))]
			} else {
				bytes[i] = alphanum[b%byte(len(alphanum))]
			}
		} else {
			if randby {
				bytes[i] = alphabets[r.Intn(len(alphabets))]
			} else {
				bytes[i] = alphabets[b%byte(len(alphabets))]
			}
		}
	}
	return bytes
}

// Substr returns the substr from start to length.
func Substr(s string, dot string, lengthAndStart ...int) string {
	var start, length, argsLen, ln int
	argsLen = len(lengthAndStart)
	if argsLen > 0 {
		length = lengthAndStart[0]
	}
	if argsLen > 1 {
		start = lengthAndStart[1]
	}
	bt := []rune(s)
	if start < 0 {
		start = 0
	}
	ln = len(bt)
	if start > ln {
		start = start % ln
	}
	end := start + length
	if end > (ln - 1) {
		end = ln
	}
	if dot == "" || end == ln {
		return string(bt[start:end])
	}
	return string(bt[start:end]) + dot
}

func IsASCIIUpper(r rune) bool {
	return 'A' <= r && r <= 'Z'
}

func ToASCIIUpper(r rune) rune {
	if 'a' <= r && r <= 'z' {
		r -= ('a' - 'A')
	}
	return r
}

// GonicCase : webxTop => webx_top
func GonicCase(name string) string {
	s := make([]rune, 0, len(name)+3)
	for idx, chr := range name {
		if IsASCIIUpper(chr) && idx > 0 {
			if !IsASCIIUpper(s[len(s)-1]) {
				s = append(s, '_')
			}
		}
		if !IsASCIIUpper(chr) && idx > 1 {
			l := len(s)
			if IsASCIIUpper(s[l-1]) && IsASCIIUpper(s[l-2]) {
				s = append(s, s[l-1])
				s[l-1] = '_'
			}
		}
		s = append(s, chr)
	}
	return strings.ToLower(string(s))
}

// TitleCase : webx_top => Webx_Top
func TitleCase(name string) string {
	var s []rune
	upNextChar := true
	name = strings.ToLower(name)
	for _, chr := range name {
		switch {
		case upNextChar:
			upNextChar = false
			chr = ToASCIIUpper(chr)
		case chr == '_':
			upNextChar = true
			continue
		}
		s = append(s, chr)
	}
	return string(s)
}

// SnakeCase : WebxTop => webx_top
func SnakeCase(name string) string {
	var s []rune
	for idx, chr := range name {
		if isUpper := IsASCIIUpper(chr); isUpper {
			if idx > 0 {
				s = append(s, '_')
			}
			chr -= ('A' - 'a')
		}
		s = append(s, chr)
	}
	return string(s)
}

// CamelCase : webx_top => webxTop
func CamelCase(s string) string {
	n := ""
	var capNext bool
	for _, v := range s {
		if v >= 'a' && v <= 'z' {
			if capNext {
				n += strings.ToUpper(string(v))
				capNext = false
			} else {
				n += string(v)
			}
			continue
		}
		if v == '_' || v == ' ' {
			capNext = true
		} else {
			capNext = false
			n += string(v)
		}
	}
	return n
}

// PascalCase : webx_top => WebxTop
func PascalCase(s string) string {
	n := ""
	capNext := true
	for _, v := range s {
		if v >= 'a' && v <= 'z' {
			if capNext {
				n += strings.ToUpper(string(v))
				capNext = false
			} else {
				n += string(v)
			}
			continue
		}
		if v == '_' || v == ' ' {
			capNext = true
		} else {
			capNext = false
			n += string(v)
		}
	}
	return n
}

// UpperCaseFirst : webx => Webx
func UpperCaseFirst(name string) string {
	s := []rune(name)
	if len(s) > 0 {
		s[0] = unicode.ToUpper(s[0])
		name = string(s)
	}
	return name
}

// LowerCaseFirst : WEBX => wEBX
func LowerCaseFirst(name string) string {
	s := []rune(name)
	if len(s) > 0 {
		s[0] = unicode.ToLower(s[0])
		name = string(s)
	}
	return name
}

func AddSlashes(s string, args ...rune) string {
	b := []rune{'\''}
	if len(args) > 0 {
		b = append(b, args...)
	}
	return AddCSlashes(s, b...)
}

func AddCSlashes(s string, b ...rune) string {
	r := []rune{}
	for _, v := range []rune(s) {
		if v == '\\' {
			r = append(r, '\\')
		} else {
			for _, f := range b {
				if v == f {
					r = append(r, '\\')
					break
				}
			}
		}
		r = append(r, v)
	}
	s = string(r)
	return s
}

// MaskString 0123456789 => 012****789
func MaskString(v string, width ...float64) string {
	size := len(v)
	if size < 1 {
		return ``
	}
	if size == 1 {
		return `*`
	}
	show := 0.3
	if len(width) > 0 {
		show = width[0]
	}
	showSize := int(float64(size) * show)
	if showSize < 1 {
		showSize = 1
	}
	hideSize := size - showSize*2
	rights := showSize + hideSize
	if rights > 0 && hideSize > 0 && rights < size && showSize < size {
		return v[0:showSize] + strings.Repeat(`*`, hideSize) + v[rights:]
	}
	if show < 0.5 {
		showSize = int(float64(size) * 0.5)
		if showSize < 1 {
			showSize = 1
		}
		hideSize = size - showSize
		if hideSize > 0 && showSize < size {
			return v[0:showSize] + strings.Repeat(`*`, hideSize)
		}
	}
	return v[0:1] + strings.Repeat(`*`, size-1)
}
<commit_msg>update<commit_after>// Copyright 2013 com authors
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

package com

import (
	"bytes"
	"crypto/hmac"
	"crypto/md5"
	"crypto/rand"
	"crypto/sha1"
	"crypto/sha256"
	"encoding/base64"
	"encoding/gob"
	"encoding/hex"
	"encoding/json"
	"hash"
	"io"
	r "math/rand"
	"strconv"
	"strings"
	"time"
	"unicode"
)

// Md5 md5 hash string
func Md5(str string) string {
	m := md5.New()
	io.WriteString(m, str)
	return hex.EncodeToString(m.Sum(nil))
}

func ByteMd5(b []byte) string {
	m := md5.New()
	m.Write(b)
	return hex.EncodeToString(m.Sum(nil))
}

func Token(key string, val []byte, args ...string) string {
	hm := hmac.New(sha1.New, []byte(key))
	hm.Write(val)
	for _, v := range args {
		hm.Write([]byte(v))
	}
	return base64.URLEncoding.EncodeToString(hm.Sum(nil))
}

func Token256(key string, val []byte, args ...string) string {
	hm := hmac.New(sha256.New, []byte(key))
	hm.Write(val)
	for _, v := range args {
		hm.Write([]byte(v))
	}
	return base64.URLEncoding.EncodeToString(hm.Sum(nil))
}

func Encode(data interface{}) ([]byte, error) {
	//return JsonEncode(data)
	return GobEncode(data)
}

func Decode(data []byte, to interface{}) error {
	//return JsonDecode(data, to)
	return GobDecode(data, to)
}

func GobEncode(data interface{}) ([]byte, error) {
	var buf bytes.Buffer
	enc := gob.NewEncoder(&buf)
	err := enc.Encode(&data)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

func GobDecode(data []byte, to interface{}) error {
	buf := bytes.NewBuffer(data)
	dec := gob.NewDecoder(buf)
	return dec.Decode(to)
}

func JsonEncode(data interface{}) ([]byte, error) {
	val, err := json.Marshal(data)
	if err != nil {
		return nil, err
	}
	return val, nil
}

func JsonDecode(data []byte, to interface{}) error {
	return json.Unmarshal(data, to)
}

func sha(m hash.Hash, str string) string {
	io.WriteString(m, str)
	return base64.URLEncoding.EncodeToString(m.Sum(nil))
}

// Sha1 sha1 hash string
func Sha1(str string) string {
	return sha(sha1.New(), str)
}

// Sha256 sha256 hash string
func Sha256(str string) string {
	return sha(sha256.New(), str)
}

// Ltrim trim space on left
func Ltrim(str string) string {
	return strings.TrimLeftFunc(str, unicode.IsSpace)
}

// Rtrim trim space on right
func Rtrim(str string) string {
	return strings.TrimRightFunc(str, unicode.IsSpace)
}

// Trim trim space in all string length
func Trim(str string) string {
	return strings.TrimSpace(str)
}

// StrRepeat repeat string times
func StrRepeat(str string, times int) string {
	return strings.Repeat(str, times)
}

// StrReplace replace find all occurs to string
func StrReplace(str string, find string, to string) string {
	return strings.Replace(str, find, to, -1)
}

// IsLetter returns true if the 'l' is an English letter.
func IsLetter(l uint8) bool {
	n := (l | 0x20) - 'a'
	if n >= 0 && n < 26 {
		return true
	}
	return false
}

// Expand replaces {k} in template with match[k] or subs[atoi(k)] if k is not in match.
func Expand(template string, match map[string]string, subs ...string) string {
	var p []byte
	var i int
	for {
		i = strings.Index(template, "{")
		if i < 0 {
			break
		}
		p = append(p, template[:i]...)
		template = template[i+1:]
		i = strings.Index(template, "}")
		if s, ok := match[template[:i]]; ok {
			p = append(p, s...)
		} else {
			j, _ := strconv.Atoi(template[:i])
			if j >= len(subs) {
				p = append(p, []byte("Missing")...)
			} else {
				p = append(p, subs[j]...)
			}
		}
		template = template[i+1:]
	}
	p = append(p, template...)
	return string(p)
}

// Reverse s string, support unicode
func Reverse(s string) string {
	n := len(s)
	runes := make([]rune, n)
	for _, rune := range s {
		n--
		runes[n] = rune
	}
	return string(runes[n:])
}

// RandomCreateBytes generate random []byte by specify chars.
func RandomCreateBytes(n int, alphabets ...byte) []byte {
	const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
	var bytes = make([]byte, n)
	var randby bool
	if num, err := rand.Read(bytes); num != n || err != nil {
		r.Seed(time.Now().UnixNano())
		randby = true
	}
	for i, b := range bytes {
		if len(alphabets) == 0 {
			if randby {
				bytes[i] = alphanum[r.Intn(len(alphanum))]
			} else {
				bytes[i] = alphanum[b%byte(len(alphanum))]
			}
		} else {
			if randby {
				bytes[i] = alphabets[r.Intn(len(alphabets))]
			} else {
				bytes[i] = alphabets[b%byte(len(alphabets))]
			}
		}
	}
	return bytes
}

// Substr returns the substr from start to length.
func Substr(s string, dot string, lengthAndStart ...int) string {
	var start, length, argsLen, ln int
	argsLen = len(lengthAndStart)
	if argsLen > 0 {
		length = lengthAndStart[0]
	}
	if argsLen > 1 {
		start = lengthAndStart[1]
	}
	bt := []rune(s)
	if start < 0 {
		start = 0
	}
	ln = len(bt)
	if start > ln {
		start = start % ln
	}
	end := start + length
	if end > (ln - 1) {
		end = ln
	}
	if dot == "" || end == ln {
		return string(bt[start:end])
	}
	return string(bt[start:end]) + dot
}

func IsASCIIUpper(r rune) bool {
	return 'A' <= r && r <= 'Z'
}

func ToASCIIUpper(r rune) rune {
	if 'a' <= r && r <= 'z' {
		r -= ('a' - 'A')
	}
	return r
}

// GonicCase : webxTop => webx_top
func GonicCase(name string) string {
	s := make([]rune, 0, len(name)+3)
	for idx, chr := range name {
		if IsASCIIUpper(chr) && idx > 0 {
			if !IsASCIIUpper(s[len(s)-1]) {
				s = append(s, '_')
			}
		}
		if !IsASCIIUpper(chr) && idx > 1 {
			l := len(s)
			if IsASCIIUpper(s[l-1]) && IsASCIIUpper(s[l-2]) {
				s = append(s, s[l-1])
				s[l-1] = '_'
			}
		}
		s = append(s, chr)
	}
	return strings.ToLower(string(s))
}

// TitleCase : webx_top => Webx_Top
func TitleCase(name string) string {
	var s []rune
	upNextChar := true
	name = strings.ToLower(name)
	for _, chr := range name {
		switch {
		case upNextChar:
			upNextChar = false
			chr = ToASCIIUpper(chr)
		case chr == '_':
			upNextChar = true
			continue
		}
		s = append(s, chr)
	}
	return string(s)
}

// SnakeCase : WebxTop => webx_top
func SnakeCase(name string) string {
	var s []rune
	for idx, chr := range name {
		if isUpper := IsASCIIUpper(chr); isUpper {
			if idx > 0 {
				s = append(s, '_')
			}
			chr -= ('A' - 'a')
		}
		s = append(s, chr)
	}
	return string(s)
}

// CamelCase : webx_top => webxTop
func CamelCase(s string) string {
	n := ""
	var capNext bool
	for _, v := range s {
		if v >= 'a' && v <= 'z' {
			if capNext {
				n += strings.ToUpper(string(v))
				capNext = false
			} else {
				n += string(v)
			}
			continue
		}
		if v == '_' || v == ' ' {
			capNext = true
		} else {
			capNext = false
			n += string(v)
		}
	}
	return n
}

// PascalCase : webx_top => WebxTop
func PascalCase(s string) string {
	n := ""
	capNext := true
	for _, v := range s {
		if v >= 'a' && v <= 'z' {
			if capNext {
				n += strings.ToUpper(string(v))
				capNext = false
			} else {
				n += string(v)
			}
			continue
		}
		if v == '_' || v == ' ' {
			capNext = true
		} else {
			capNext = false
			n += string(v)
		}
	}
	return n
}

// UpperCaseFirst : webx => Webx
func UpperCaseFirst(name string) string {
	s := []rune(name)
	if len(s) > 0 {
		s[0] = unicode.ToUpper(s[0])
		name = string(s)
	}
	return name
}

// LowerCaseFirst : WEBX => wEBX
func LowerCaseFirst(name string) string {
	s := []rune(name)
	if len(s) > 0 {
		s[0] = unicode.ToLower(s[0])
		name = string(s)
	}
	return name
}

func AddSlashes(s string, args ...rune) string {
	b := []rune{'\''}
	if len(args) > 0 {
		b = append(b, args...)
	}
	return AddCSlashes(s, b...)
}

func AddCSlashes(s string, b ...rune) string {
	r := []rune{}
	for _, v := range []rune(s) {
		if v == '\\' {
			r = append(r, '\\')
		} else {
			for _, f := range b {
				if v == f {
					r = append(r, '\\')
					break
				}
			}
		}
		r = append(r, v)
	}
	s = string(r)
	return s
}

// MaskString 0123456789 => 012****789
func MaskString(v string, width ...float64) string {
	size := len(v)
	if size < 1 {
		return ``
	}
	if size == 1 {
		return `*`
	}
	show := 0.3
	if len(width) > 0 {
		show = width[0]
	}
	showSize := int(float64(size) * show)
	if showSize < 1 {
		showSize = 1
	}
	hideSize := size - showSize*2
	rights := showSize + hideSize
	if rights > 0 && hideSize > 0 && rights < size && showSize < size {
		return v[0:showSize] + strings.Repeat(`*`, hideSize) + v[rights:]
	}
	if show < 0.5 {
		showSize = int(float64(size) * 0.5)
		if showSize < 1 {
			showSize = 1
		}
		hideSize = size - showSize
		if hideSize > 0 && showSize < size {
			return v[0:showSize] + strings.Repeat(`*`, hideSize)
		}
	}
	return v[0:1] + strings.Repeat(`*`, size-1)
}
<|endoftext|>


<commit_before>// Package calendar generates and has utlilty functions to generate an HTML calendar for use in nlgids.
package calendar

import (
	"fmt"
	"sort"
	"time"
)

// TODO: next button should have a ref to point to something.

var avail = [...]string{"past", "busy", "free"}

type Available int

const (
	past Available = iota
	busy
	free
)

func (a Available) String() string { return avail[a] }

// Calendar holds the HTML that makes up the calendar. Each
// day is indexed by the 12 o' clock night time time.Time.
// All date are in the UTC timezone.
type Calendar struct {
	days map[time.Time]Available
}

type times []time.Time

func (t times) Len() int           { return len(t) }
func (t times) Less(i, j int) bool { return t[i].Before(t[j]) }
func (t times) Swap(i, j int)      { t[i], t[j] = t[j], t[i] }

func (c *Calendar) heading() string {
	// lang!
	return "
Sun	Mon	Tue	Wed	Thu	Fri	Sat
\n"
}

func (c *Calendar) openTR() string  { return "
\n" }
func (c *Calendar) closeTR() string { return "
\n" }

func (c *Calendar) entry(t time.Time) string {
	// Make something like:
	// 3
	d := c.days[t]
	day := fmt.Sprintf("%02d", t.Day())
	class := fmt.Sprintf("\t", d)
	close := "	\n"
	href := ""
	switch d {
	case free:
		date := fmt.Sprintf("%4d-%02d-%02d", t.Year(), t.Month(), t.Day())
		href = fmt.Sprintf("%s", date, day)
	case busy:
		href = day
	case past:
		href = day
	}
	s := class + href + close
	return s
}

func (c *Calendar) HTML() string {
	keys := times{}
	for k := range c.days {
		keys = append(keys, k)
	}

	s := c.heading()
	sort.Sort(keys)
	i := 0
	for _, k := range keys {
		if i%7 == 0 {
			if i > 0 {
				s += c.closeTR()
			}
			s += c.closeTR()
		}
		s += c.entry(k)
		i++
	}
	s += c.closeTR()
	return s
}

// New creates a new month calendar based on d. D must
// be in the form: YYYY-MM-DD.
func New(d string) (*Calendar, error) {
	date, err := time.Parse("2006-01-02", d)
	if err != nil {
		return nil, err
	}
	cal := &Calendar{days: make(map[time.Time]Available)}

	now := time.Now()
	today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.UTC)
	first := time.Date(date.Year(), date.Month(), 1, 0, 0, 0, 0, time.UTC)
	last := time.Date(date.Year(), date.Month()+1, 1, 0, 0, 0, 0, time.UTC)
	last = last.Add(-24 * time.Hour)

	// Add the remaining days of the previous month.
	for i := 0; i < int(first.Weekday()); i++ {
		lastMonthDay := first.AddDate(0, 0, -1*(i+1))
		cal.days[lastMonthDay] = free

		if lastMonthDay.Before(today) {
			cal.days[lastMonthDay] = past
		}
	}

	// Loop from i to lastDay and add the month.
	for i := 1; i <= last.Day(); i++ {
		day := time.Date(date.Year(), date.Month(), i, 0, 0, 0, 0, time.UTC)

		cal.days[day] = free

		if day.Before(today) {
			cal.days[day] = past
		}
	}

	// These are dates in the new month.
	j := 1
	for i := int(last.Weekday()) + 1; i < 7; i++ {
		nextMonthDay := last.AddDate(0, 0, j)
		cal.days[nextMonthDay] = free

		if nextMonthDay.Before(today) {
			cal.days[nextMonthDay] = past
		}

		j++
	}

	return cal, nil
}
<commit_msg>Calendar gen<commit_after>// Package calendar generates and has utlilty functions to generate an HTML calendar for use in nlgids.
package calendar

import (
	"fmt"
	"sort"
	"time"
)

// TODO: next button should have a ref to point to something.

var avail = [...]string{"past", "busy", "free"}

type Available int

const (
	past Available = iota
	busy
	free
)

func (a Available) String() string { return avail[a] }

// Calendar holds the HTML that makes up the calendar. Each
// day is indexed by the 12 o' clock night time time.Time.
// All date are in the UTC timezone.
type Calendar struct {
	days map[time.Time]Available
	// TODO: add date we are going see, for the month heading at the top of the calendar.
}

type times []time.Time

func (t times) Len() int           { return len(t) }
func (t times) Less(i, j int) bool { return t[i].Before(t[j]) }
func (t times) Swap(i, j int)      { t[i], t[j] = t[j], t[i] }

func (c *Calendar) heading() string {
	// lang!
	return "
Sun	Mon	Tue	Wed	Thu	Fri	Sat
\n"
}

func (c *Calendar) Header() string {
	// Todo insert links 'n such.
	return `

bla

`
}

func (c *Calendar) Footer() string {
	return `

`
}

func (c *Calendar) openTR() string  { return "
\n" }
func (c *Calendar) closeTR() string { return "
\n" }

func (c *Calendar) entry(t time.Time) string {
	// Make something like:
	// 3
	d := c.days[t]
	day := fmt.Sprintf("%02d", t.Day())
	class := fmt.Sprintf("\t", d)
	close := "	\n"
	href := ""
	switch d {
	case free:
		date := fmt.Sprintf("%4d-%02d-%02d", t.Year(), t.Month(), t.Day())
		href = fmt.Sprintf("%s", date, day)
	case busy:
		href = day
	case past:
		href = day
	}
	s := class + href + close
	return s
}

func (c *Calendar) HTML() string {
	s := c.Header()
	s += "
\n"
	s += c.html()
	s += "
"
	s += c.Footer()
	return s
}

func (c *Calendar) html() string {
	keys := times{}
	for k := range c.days {
		keys = append(keys, k)
	}

	s := c.heading()
	sort.Sort(keys)
	i := 0
	for _, k := range keys {
		if i%7 == 0 {
			if i > 0 {
				s += c.closeTR()
			}
			s += c.closeTR()
		}
		s += c.entry(k)
		i++
	}
	s += c.closeTR()
	return s
}

// New creates a new month calendar based on d. D must
// be in the form: YYYY-MM-DD.
func New(d string) (*Calendar, error) {
	date, err := time.Parse("2006-01-02", d)
	if err != nil {
		return nil, err
	}
	cal := &Calendar{days: make(map[time.Time]Available)}

	now := time.Now()
	today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.UTC)
	first := time.Date(date.Year(), date.Month(), 1, 0, 0, 0, 0, time.UTC)
	last := time.Date(date.Year(), date.Month()+1, 1, 0, 0, 0, 0, time.UTC)
	last = last.Add(-24 * time.Hour)

	// Add the remaining days of the previous month.
	for i := 0; i < int(first.Weekday()); i++ {
		lastMonthDay := first.AddDate(0, 0, -1*(i+1))
		cal.days[lastMonthDay] = free

		if lastMonthDay.Before(today) {
			cal.days[lastMonthDay] = past
		}
	}

	// Loop from i to lastDay and add the entire month.
	for i := 1; i <= last.Day(); i++ {
		day := time.Date(date.Year(), date.Month(), i, 0, 0, 0, 0, time.UTC)

		cal.days[day] = free

		if day.Before(today) {
			cal.days[day] = past
		}
	}

	// These are dates in the new month.
	j := 1
	for i := int(last.Weekday()) + 1; i < 7; i++ {
		nextMonthDay := last.AddDate(0, 0, j)
		cal.days[nextMonthDay] = free

		if nextMonthDay.Before(today) {
			cal.days[nextMonthDay] = past
		}

		j++
	}

	return cal, nil
}
<|endoftext|>


<commit_before>package config

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestURLConfig(t *testing.T) {
	u := NewURLConfig(EnvironmentOf(MapFetcher(map[string][]string{
		"http.key":                           []string{"root", "root-2"},
		"http.https://host.com.key":          []string{"host", "host-2"},
		"http.https://user@host.com/a.key":   []string{"user-a", "user-b"},
		"http.https://user@host.com.key":     []string{"user", "user-2"},
		"http.https://host.com/a.key":        []string{"host-a", "host-b"},
		"http.https://host.com:8080.key":     []string{"port", "port-2"},
		"http.https://host.com/repo.git.key": []string{".git"},
		"http.https://host.com/repo.key":     []string{"no .git"},
		"http.https://host.com/repo2.key":    []string{"no .git"},
		"http.http://host.com/repo.key":      []string{"http"},
	})))

	getOne := map[string]string{
		"https://root.com/a/b/c":                      "root-2",
		"https://host.com/":                           "host-2",
		"https://host.com/a/b/c":                      "host-b",
		"https://user:pass@host.com/a/b/c":            "user-b",
		"https://user:pass@host.com/z/b/c":            "user-2",
		"https://host.com:8080/a":                     "port-2",
		"https://host.com/repo.git/info/lfs":          ".git",
		"https://host.com/repo.git/info":              ".git",
		"https://host.com/repo.git":                   ".git",
		"https://host.com/repo":                       "no .git",
		"https://host.com/repo2.git/info/lfs/foo/bar": "no .git",
		"https://host.com/repo2.git/info/lfs":         "no .git",
		"https://host.com/repo2.git/info":             "host-2", // doesn't match /.git/info/lfs\Z/
		"https://host.com/repo2.git":                  "host-2", // ditto
		"https://host.com/repo2":                      "no .git",
		"http://host.com/repo":                        "http",
	}

	for rawurl, expected := range getOne {
		value, _ := u.Get("http", rawurl, "key")
		assert.Equal(t, expected, value, "get one: "+rawurl)
	}

	getAll := map[string][]string{
		"https://root.com/a/b/c":           []string{"root", "root-2"},
		"https://host.com/":                []string{"host", "host-2"},
		"https://host.com/a/b/c":           []string{"host-a", "host-b"},
		"https://user:pass@host.com/a/b/c": []string{"user-a", "user-b"},
		"https://user:pass@host.com/z/b/c": []string{"user", "user-2"},
		"https://host.com:8080/a":          []string{"port", "port-2"},
	}

	for rawurl, expected := range getAll {
		values := u.GetAll("http", rawurl, "key")
		assert.Equal(t, expected, values, "get all: "+rawurl)
	}
}
<commit_msg>config: add wildcard url matching tests<commit_after>package config

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestURLConfig(t *testing.T) {
	u := NewURLConfig(EnvironmentOf(MapFetcher(map[string][]string{
		"http.key":                           []string{"root", "root-2"},
		"http.https://host.com.key":          []string{"host", "host-2"},
		"http.https://user@host.com/a.key":   []string{"user-a", "user-b"},
		"http.https://user@host.com.key":     []string{"user", "user-2"},
		"http.https://host.com/a.key":        []string{"host-a", "host-b"},
		"http.https://host.com:8080.key":     []string{"port", "port-2"},
		"http.https://host.com/repo.git.key": []string{".git"},
		"http.https://host.com/repo.key":     []string{"no .git"},
		"http.https://host.com/repo2.key":    []string{"no .git"},
		"http.http://host.com/repo.key":      []string{"http"},
		"http.https://host.*/a.key":          []string{"wild"},
	})))

	getOne := map[string]string{
		"https://root.com/a/b/c":                      "root-2",
		"https://host.com/":                           "host-2",
		"https://host.com/a/b/c":                      "host-b",
		"https://user:pass@host.com/a/b/c":            "user-b",
		"https://user:pass@host.com/z/b/c":            "user-2",
		"https://host.com:8080/a":                     "port-2",
		"https://host.com/repo.git/info/lfs":          ".git",
		"https://host.com/repo.git/info":              ".git",
		"https://host.com/repo.git":                   ".git",
		"https://host.com/repo":                       "no .git",
		"https://host.com/repo2.git/info/lfs/foo/bar": "no .git",
		"https://host.com/repo2.git/info/lfs":         "no .git",
		"https://host.com/repo2.git/info":             "host-2", // doesn't match /.git/info/lfs\Z/
		"https://host.com/repo2.git":                  "host-2", // ditto
		"https://host.com/repo2":                      "no .git",
		"http://host.com/repo":                        "http",
		"https://host.wild/a/b/c":                     "wild",
	}

	for rawurl, expected := range getOne {
		value, _ := u.Get("http", rawurl, "key")
		assert.Equal(t, expected, value, "get one: "+rawurl)
	}

	getAll := map[string][]string{
		"https://root.com/a/b/c":           []string{"root", "root-2"},
		"https://host.com/":                []string{"host", "host-2"},
		"https://host.com/a/b/c":           []string{"host-a", "host-b"},
		"https://user:pass@host.com/a/b/c": []string{"user-a", "user-b"},
		"https://user:pass@host.com/z/b/c": []string{"user", "user-2"},
		"https://host.com:8080/a":          []string{"port", "port-2"},
	}

	for rawurl, expected := range getAll {
		values := u.GetAll("http", rawurl, "key")
		assert.Equal(t, expected, values, "get all: "+rawurl)
	}
}
<|endoftext|>