<commit_before>// Copyright 2021 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package txn

import (
	"context"
	"unsafe"

	"github.com/pingcap/kvproto/pkg/metapb"
	"github.com/pingcap/tidb/kv"
	derr "github.com/pingcap/tidb/store/driver/error"
	"github.com/pingcap/tidb/store/driver/options"
	"github.com/tikv/client-go/v2/tikv"
)

type tikvSnapshot struct {
	*tikv.KVSnapshot
}

// NewSnapshot creates a kv.Snapshot with tikv.KVSnapshot.
func NewSnapshot(snapshot *tikv.KVSnapshot) kv.Snapshot {
	return &tikvSnapshot{snapshot}
}

// BatchGet gets all the keys' value from kv-server and returns a map contains key/value pairs.
// The map will not contain nonexistent keys.
func (s *tikvSnapshot) BatchGet(ctx context.Context, keys []kv.Key) (map[string][]byte, error) {
	data, err := s.KVSnapshot.BatchGet(ctx, toTiKVKeys(keys))
	return data, extractKeyErr(err)
}

// Get gets the value for key k from snapshot.
func (s *tikvSnapshot) Get(ctx context.Context, k kv.Key) ([]byte, error) {
	data, err := s.KVSnapshot.Get(ctx, k)
	return data, extractKeyErr(err)
}

// Iter return a list of key-value pair after `k`.
func (s *tikvSnapshot) Iter(k kv.Key, upperBound kv.Key) (kv.Iterator, error) {
	scanner, err := s.KVSnapshot.Iter(k, upperBound)
	if err != nil {
		return nil, derr.ToTiDBErr(err)
	}
	return &tikvScanner{scanner.(*tikv.Scanner)}, err
}

// IterReverse creates a reversed Iterator positioned on the first entry which key is less than k.
func (s *tikvSnapshot) IterReverse(k kv.Key) (kv.Iterator, error) {
	scanner, err := s.KVSnapshot.IterReverse(k)
	if err != nil {
		return nil, derr.ToTiDBErr(err)
	}
	return &tikvScanner{scanner.(*tikv.Scanner)}, err
}

func (s *tikvSnapshot) SetOption(opt int, val interface{}) {
	switch opt {
	case kv.IsolationLevel:
		level := getTiKVIsolationLevel(val.(kv.IsoLevel))
		s.KVSnapshot.SetIsolationLevel(level)
	case kv.Priority:
		s.KVSnapshot.SetPriority(getTiKVPriority(val.(int)))
	case kv.NotFillCache:
		s.KVSnapshot.SetNotFillCache(val.(bool))
	case kv.SnapshotTS:
		s.KVSnapshot.SetSnapshotTS(val.(uint64))
	case kv.ReplicaRead:
		t := options.GetTiKVReplicaReadType(val.(kv.ReplicaReadType))
		s.KVSnapshot.SetReplicaRead(t)
	case kv.SampleStep:
		s.KVSnapshot.SetSampleStep(val.(uint32))
	case kv.TaskID:
		s.KVSnapshot.SetTaskID(val.(uint64))
	case kv.CollectRuntimeStats:
		if val == nil {
			s.KVSnapshot.SetRuntimeStats(nil)
		} else {
			s.KVSnapshot.SetRuntimeStats(val.(*tikv.SnapshotRuntimeStats))
		}
	case kv.IsStalenessReadOnly:
		s.KVSnapshot.SetIsStatenessReadOnly(val.(bool))
	case kv.MatchStoreLabels:
		s.KVSnapshot.SetMatchStoreLabels(val.([]*metapb.StoreLabel))
	case kv.ResourceGroupTag:
		s.KVSnapshot.SetResourceGroupTag(val.([]byte))
	}
}

func toTiKVKeys(keys []kv.Key) [][]byte {
	bytesKeys := *(*[][]byte)(unsafe.Pointer(&keys))
	return bytesKeys
}

func getTiKVIsolationLevel(level kv.IsoLevel) tikv.IsoLevel {
	switch level {
	case kv.SI:
		return tikv.SI
	case kv.RC:
		return tikv.RC
	default:
		return tikv.SI
	}
}

func getTiKVPriority(pri int) tikv.Priority {
	switch pri {
	case kv.PriorityHigh:
		return tikv.PriorityHigh
	case kv.PriorityLow:
		return tikv.PriorityLow
	default:
		return tikv.PriorityNormal
	}
}
<commit_msg>executor: fix KVSnapshot missing set txnScope (#25710)<commit_after>// Copyright 2021 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package txn

import (
	"context"
	"unsafe"

	"github.com/pingcap/kvproto/pkg/metapb"
	"github.com/pingcap/tidb/kv"
	derr "github.com/pingcap/tidb/store/driver/error"
	"github.com/pingcap/tidb/store/driver/options"
	"github.com/tikv/client-go/v2/tikv"
)

type tikvSnapshot struct {
	*tikv.KVSnapshot
}

// NewSnapshot creates a kv.Snapshot with tikv.KVSnapshot.
func NewSnapshot(snapshot *tikv.KVSnapshot) kv.Snapshot {
	return &tikvSnapshot{snapshot}
}

// BatchGet gets all the keys' value from kv-server and returns a map contains key/value pairs.
// The map will not contain nonexistent keys.
func (s *tikvSnapshot) BatchGet(ctx context.Context, keys []kv.Key) (map[string][]byte, error) {
	data, err := s.KVSnapshot.BatchGet(ctx, toTiKVKeys(keys))
	return data, extractKeyErr(err)
}

// Get gets the value for key k from snapshot.
func (s *tikvSnapshot) Get(ctx context.Context, k kv.Key) ([]byte, error) {
	data, err := s.KVSnapshot.Get(ctx, k)
	return data, extractKeyErr(err)
}

// Iter return a list of key-value pair after `k`.
func (s *tikvSnapshot) Iter(k kv.Key, upperBound kv.Key) (kv.Iterator, error) {
	scanner, err := s.KVSnapshot.Iter(k, upperBound)
	if err != nil {
		return nil, derr.ToTiDBErr(err)
	}
	return &tikvScanner{scanner.(*tikv.Scanner)}, err
}

// IterReverse creates a reversed Iterator positioned on the first entry which key is less than k.
func (s *tikvSnapshot) IterReverse(k kv.Key) (kv.Iterator, error) {
	scanner, err := s.KVSnapshot.IterReverse(k)
	if err != nil {
		return nil, derr.ToTiDBErr(err)
	}
	return &tikvScanner{scanner.(*tikv.Scanner)}, err
}

func (s *tikvSnapshot) SetOption(opt int, val interface{}) {
	switch opt {
	case kv.IsolationLevel:
		level := getTiKVIsolationLevel(val.(kv.IsoLevel))
		s.KVSnapshot.SetIsolationLevel(level)
	case kv.Priority:
		s.KVSnapshot.SetPriority(getTiKVPriority(val.(int)))
	case kv.NotFillCache:
		s.KVSnapshot.SetNotFillCache(val.(bool))
	case kv.SnapshotTS:
		s.KVSnapshot.SetSnapshotTS(val.(uint64))
	case kv.ReplicaRead:
		t := options.GetTiKVReplicaReadType(val.(kv.ReplicaReadType))
		s.KVSnapshot.SetReplicaRead(t)
	case kv.SampleStep:
		s.KVSnapshot.SetSampleStep(val.(uint32))
	case kv.TaskID:
		s.KVSnapshot.SetTaskID(val.(uint64))
	case kv.CollectRuntimeStats:
		if val == nil {
			s.KVSnapshot.SetRuntimeStats(nil)
		} else {
			s.KVSnapshot.SetRuntimeStats(val.(*tikv.SnapshotRuntimeStats))
		}
	case kv.IsStalenessReadOnly:
		s.KVSnapshot.SetIsStatenessReadOnly(val.(bool))
	case kv.MatchStoreLabels:
		s.KVSnapshot.SetMatchStoreLabels(val.([]*metapb.StoreLabel))
	case kv.ResourceGroupTag:
		s.KVSnapshot.SetResourceGroupTag(val.([]byte))
	case kv.TxnScope:
		s.KVSnapshot.SetTxnScope(val.(string))
	}
}

func toTiKVKeys(keys []kv.Key) [][]byte {
	bytesKeys := *(*[][]byte)(unsafe.Pointer(&keys))
	return bytesKeys
}

func getTiKVIsolationLevel(level kv.IsoLevel) tikv.IsoLevel {
	switch level {
	case kv.SI:
		return tikv.SI
	case kv.RC:
		return tikv.RC
	default:
		return tikv.SI
	}
}

func getTiKVPriority(pri int) tikv.Priority {
	switch pri {
	case kv.PriorityHigh:
		return tikv.PriorityHigh
	case kv.PriorityLow:
		return tikv.PriorityLow
	default:
		return tikv.PriorityNormal
	}
}
<|endoftext|>


<commit_before>package strava

import (
  "context"
	"testing"

	"gopkg.in/h2non/gock.v1"
)

func TestRequests(t *testing.T) {
	client := NewClient()

	t.Run("it handles successful GetAthlete requests", func(t *testing.T) {
		defer gock.Off()
		gock.New("https://www.strava.com").
			Get("/api/v3/athlete").
			Reply(200).
			BodyString(`
			{
				"id": 1234567890987654321,
				"username": "PtrTeixeira",
				"firstname": "Peter",
				"lastname": "Teixeira",
				"city": "Boston",
				"state": "MA",
				"country": "US",
				"sex": "M",
				"premium": true
			}
			`)

		athlete, err := client.GetAthlete(context.Background(), "fake-token")
		if err != nil {
			t.Error("Failed to make request", err)
		}

		if athlete.Id != 1234567890987654321 {
			t.Error("Failed to get correct athlete")
		}
	})

	t.Run("it handles unsuccessful GetAthlete requests", func(t *testing.T) {
		defer gock.Off()
		gock.New("https://www.strava.com").
			Get("/api/v3/athlete").
			Reply(403).
			BodyString(`
			{
				"errors": [
					{
						"code": "error-code",
						"field": "aspect-in-error",
						"resource": "/athlete"
					}
				],
				"message": "Please don't do that again"
			}
			`)

		athlete, err := client.GetAthlete(context.Background(), "fake-token")
		if err == nil || athlete != nil {
			t.Error("Request should have returned an error")
		}

		if err.Error() != "Please don't do that again" {
			t.Error("Got the incorrect value for the error")
		}
	})

	t.Run("it handles successful GetToken requests", func(t *testing.T) {
		defer gock.Off()
		gock.New("https://www.strava.com").
			Post("/oauth/token").
			MatchParam("client_id", "client-id").
			MatchParam("client_secret", "client-secret").
			MatchParam("code", "redirect-code").
			MatchParam("grant_type", "authorization_code").
			Reply(200).
			BodyString(`
			{
				"token_type": "Bearer",
				"access_token": "987654321234567898765432123456789",
				"athlete": {
				  "id": 1234567890987654321
				},
				"refresh_token": "1234567898765432112345678987654321",
				"expires_at": 1531378346,
				"state": "STRAVA"
			  }
			`)

		token, err := client.GetToken(context.Background(), "client-id", "client-secret", "redirect-code")
		if err != nil {
			t.Error("Failed to make request", err)
		}

		if token.AccessToken != "987654321234567898765432123456789" {
			t.Error("Failed to get correct athlete")
		}
	})
}
<commit_msg>gofmt<commit_after>package strava

import (
	"context"
	"testing"

	"gopkg.in/h2non/gock.v1"
)

func TestRequests(t *testing.T) {
	client := NewClient()

	t.Run("it handles successful GetAthlete requests", func(t *testing.T) {
		defer gock.Off()
		gock.New("https://www.strava.com").
			Get("/api/v3/athlete").
			Reply(200).
			BodyString(`
			{
				"id": 1234567890987654321,
				"username": "PtrTeixeira",
				"firstname": "Peter",
				"lastname": "Teixeira",
				"city": "Boston",
				"state": "MA",
				"country": "US",
				"sex": "M",
				"premium": true
			}
			`)

		athlete, err := client.GetAthlete(context.Background(), "fake-token")
		if err != nil {
			t.Error("Failed to make request", err)
		}

		if athlete.Id != 1234567890987654321 {
			t.Error("Failed to get correct athlete")
		}
	})

	t.Run("it handles unsuccessful GetAthlete requests", func(t *testing.T) {
		defer gock.Off()
		gock.New("https://www.strava.com").
			Get("/api/v3/athlete").
			Reply(403).
			BodyString(`
			{
				"errors": [
					{
						"code": "error-code",
						"field": "aspect-in-error",
						"resource": "/athlete"
					}
				],
				"message": "Please don't do that again"
			}
			`)

		athlete, err := client.GetAthlete(context.Background(), "fake-token")
		if err == nil || athlete != nil {
			t.Error("Request should have returned an error")
		}

		if err.Error() != "Please don't do that again" {
			t.Error("Got the incorrect value for the error")
		}
	})

	t.Run("it handles successful GetToken requests", func(t *testing.T) {
		defer gock.Off()
		gock.New("https://www.strava.com").
			Post("/oauth/token").
			MatchParam("client_id", "client-id").
			MatchParam("client_secret", "client-secret").
			MatchParam("code", "redirect-code").
			MatchParam("grant_type", "authorization_code").
			Reply(200).
			BodyString(`
			{
				"token_type": "Bearer",
				"access_token": "987654321234567898765432123456789",
				"athlete": {
				  "id": 1234567890987654321
				},
				"refresh_token": "1234567898765432112345678987654321",
				"expires_at": 1531378346,
				"state": "STRAVA"
			  }
			`)

		token, err := client.GetToken(context.Background(), "client-id", "client-secret", "redirect-code")
		if err != nil {
			t.Error("Failed to make request", err)
		}

		if token.AccessToken != "987654321234567898765432123456789" {
			t.Error("Failed to get correct athlete")
		}
	})
}
<|endoftext|>


<commit_before>package hub

import (
	"crypto/tls"
	"errors"
	"net"
	"sync"

	"github.com/v2ray/v2ray-core/common/log"
	v2net "github.com/v2ray/v2ray-core/common/net"
	"github.com/v2ray/v2ray-core/proxy"
	"github.com/v2ray/v2ray-core/transport"
)

var (
	ErrorClosedConnection = errors.New("Connection already closed.")
)

type TCPHub struct {
	sync.Mutex
	listener     net.Listener
	connCallback ConnectionHandler
	accepting    bool
}

func ListenTCP(address v2net.Address, port v2net.Port, callback ConnectionHandler, tlsConfig *tls.Config) (*TCPHub, error) {
	listener, err := net.ListenTCP("tcp", &net.TCPAddr{
		IP:   address.IP(),
		Port: int(port),
		Zone: "",
	})
	if err != nil {
		return nil, err
	}
	var hub *TCPHub
	if tlsConfig != nil {
		tlsListener := tls.NewListener(listener, tlsConfig)
		hub = &TCPHub{
			listener:     tlsListener,
			connCallback: callback,
		}
	} else {
		hub = &TCPHub{
			listener:     listener,
			connCallback: callback,
		}
	}

	go hub.start()
	return hub, nil
}
func ListenKCPhub(address v2net.Address, port v2net.Port, callback ConnectionHandler, tlsConfig *tls.Config) (*TCPHub, error) {
	listener, err := ListenKCP(address, port)
	if err != nil {
		return nil, err
	}
	var hub *TCPHub
	if tlsConfig != nil {
		tlsListener := tls.NewListener(listener, tlsConfig)
		hub = &TCPHub{
			listener:     tlsListener,
			connCallback: callback,
		}
	} else {
		hub = &TCPHub{
			listener:     listener,
			connCallback: callback,
		}
	}

	go hub.start()
	return hub, nil
}
func ListenTCP6(address v2net.Address, port v2net.Port, callback ConnectionHandler, proxyMeta *proxy.InboundHandlerMeta, tlsConfig *tls.Config) (*TCPHub, error) {
	if proxyMeta.KcpSupported && transport.IsKcpEnabled() {
		return ListenKCPhub(address, port, callback, tlsConfig)
	} else {
		return ListenTCP(address, port, callback, tlsConfig)
	}
	return nil, errors.New("ListenTCP6: Not Implemented")
}

func (this *TCPHub) Close() {
	this.accepting = false
	this.listener.Close()
}

func (this *TCPHub) start() {
	this.accepting = true
	for this.accepting {
		conn, err := this.listener.Accept()

		if err != nil {
			if this.accepting {
				log.Info("Listener: Failed to accept new TCP connection: ", err)
			}
			continue
		}
		go this.connCallback(&Connection{
			dest:     conn.RemoteAddr().String(),
			conn:     conn,
			listener: this,
		})
	}
}

// @Private
func (this *TCPHub) Recycle(dest string, conn net.Conn) {
	if this.accepting {
		go this.connCallback(&Connection{
			dest:     dest,
			conn:     conn,
			listener: this,
		})
	}
}
<commit_msg>KCP: This code cause unwanted effect<commit_after>package hub

import (
	"crypto/tls"
	"errors"
	"net"
	"sync"

	"github.com/v2ray/v2ray-core/common/log"
	v2net "github.com/v2ray/v2ray-core/common/net"
	"github.com/v2ray/v2ray-core/proxy"
	"github.com/v2ray/v2ray-core/transport"
)

var (
	ErrorClosedConnection = errors.New("Connection already closed.")
)

type TCPHub struct {
	sync.Mutex
	listener     net.Listener
	connCallback ConnectionHandler
	accepting    bool
}

func ListenTCP(address v2net.Address, port v2net.Port, callback ConnectionHandler, tlsConfig *tls.Config) (*TCPHub, error) {
	listener, err := net.ListenTCP("tcp", &net.TCPAddr{
		IP:   address.IP(),
		Port: int(port),
		Zone: "",
	})
	if err != nil {
		return nil, err
	}
	var hub *TCPHub
	if tlsConfig != nil {
		tlsListener := tls.NewListener(listener, tlsConfig)
		hub = &TCPHub{
			listener:     tlsListener,
			connCallback: callback,
		}
	} else {
		hub = &TCPHub{
			listener:     listener,
			connCallback: callback,
		}
	}

	go hub.start()
	return hub, nil
}
func ListenKCPhub(address v2net.Address, port v2net.Port, callback ConnectionHandler, tlsConfig *tls.Config) (*TCPHub, error) {
	listener, err := ListenKCP(address, port)
	if err != nil {
		return nil, err
	}
	var hub *TCPHub
	if tlsConfig != nil {
		tlsListener := tls.NewListener(listener, tlsConfig)
		hub = &TCPHub{
			listener:     tlsListener,
			connCallback: callback,
		}
	} else {
		hub = &TCPHub{
			listener:     listener,
			connCallback: callback,
		}
	}

	go hub.start()
	return hub, nil
}
func ListenTCP6(address v2net.Address, port v2net.Port, callback ConnectionHandler, proxyMeta *proxy.InboundHandlerMeta, tlsConfig *tls.Config) (*TCPHub, error) {
	if proxyMeta.KcpSupported && transport.IsKcpEnabled() {
		return ListenKCPhub(address, port, callback, tlsConfig)
	} else {
		return ListenTCP(address, port, callback, tlsConfig)
	}
	return nil, errors.New("ListenTCP6: Not Implemented")
}

func (this *TCPHub) Close() {
	this.accepting = false
	this.listener.Close()
}

func (this *TCPHub) start() {
	this.accepting = true
	for this.accepting {
		conn, err := this.listener.Accept()

		if err != nil {
			if this.accepting {
				log.Warning("Listener: Failed to accept new TCP connection: ", err)
			}
			continue
		}
		go this.connCallback(&Connection{
			dest:     conn.RemoteAddr().String(),
			conn:     conn,
			listener: this,
		})
	}
}

// @Private
func (this *TCPHub) Recycle(dest string, conn net.Conn) {
	if this.accepting {
		go this.connCallback(&Connection{
			dest:     dest,
			conn:     conn,
			listener: this,
		})
	}
}
<|endoftext|>


<commit_before>package api

import (
	"fmt"
	"strings"
	"sync"
	"time"
)

const (
	// DefaultLockSessionName is the Session Name we assign if none is provided
	DefaultLockSessionName = "Consul API Lock"

	// DefaultLockSessionTTL is the default session TTL if no Session is provided
	// when creating a new Lock. This is used because we do not have another
	// other check to depend upon.
	DefaultLockSessionTTL = "15s"

	// DefaultLockWaitTime is how long we block for at a time to check if lock
	// acquisition is possible. This affects the minimum time it takes to cancel
	// a Lock acquisition.
	DefaultLockWaitTime = 15 * time.Second

	// DefaultLockRetryTime is how long we wait after a failed lock acquisition
	// before attempting to do the lock again. This is so that once a lock-delay
	// is in effect, we do not hot loop retrying the acquisition.
	DefaultLockRetryTime = 5 * time.Second

	// DefaultMonitorRetryTime is how long we wait after a failed monitor check
	// of a lock (500 response code). This allows the monitor to ride out brief
	// periods of unavailability, subject to the MonitorRetries setting in the
	// lock options which is by default set to 0, disabling this feature.
	DefaultMonitorRetryTime = 2 * time.Second

	// LockFlagValue is a magic flag we set to indicate a key
	// is being used for a lock. It is used to detect a potential
	// conflict with a semaphore.
	LockFlagValue = 0x2ddccbc058a50c18
)

var (
	// ErrLockHeld is returned if we attempt to double lock
	ErrLockHeld = fmt.Errorf("Lock already held")

	// ErrLockNotHeld is returned if we attempt to unlock a lock
	// that we do not hold.
	ErrLockNotHeld = fmt.Errorf("Lock not held")

	// ErrLockInUse is returned if we attempt to destroy a lock
	// that is in use.
	ErrLockInUse = fmt.Errorf("Lock in use")

	// ErrLockConflict is returned if the flags on a key
	// used for a lock do not match expectation
	ErrLockConflict = fmt.Errorf("Existing key does not match lock use")
)

// Lock is used to implement client-side leader election. It is follows the
// algorithm as described here: https://consul.io/docs/guides/leader-election.html.
type Lock struct {
	c    *Client
	opts *LockOptions

	isHeld       bool
	sessionRenew chan struct{}
	lockSession  string
	l            sync.Mutex
}

// LockOptions is used to parameterize the Lock behavior.
type LockOptions struct {
	Key            string // Must be set and have write permissions
	Value          []byte // Optional, value to associate with the lock
	Session        string // Optional, created if not specified
	SessionName    string // Optional, defaults to DefaultLockSessionName
	SessionTTL     string // Optional, defaults to DefaultLockSessionTTL
	MonitorRetries int    // Optional, defaults to 0 which means no retries
}

// LockKey returns a handle to a lock struct which can be used
// to acquire and release the mutex. The key used must have
// write permissions.
func (c *Client) LockKey(key string) (*Lock, error) {
	opts := &LockOptions{
		Key: key,
	}
	return c.LockOpts(opts)
}

// LockOpts returns a handle to a lock struct which can be used
// to acquire and release the mutex. The key used must have
// write permissions.
func (c *Client) LockOpts(opts *LockOptions) (*Lock, error) {
	if opts.Key == "" {
		return nil, fmt.Errorf("missing key")
	}
	if opts.SessionName == "" {
		opts.SessionName = DefaultLockSessionName
	}
	if opts.SessionTTL == "" {
		opts.SessionTTL = DefaultLockSessionTTL
	} else {
		if _, err := time.ParseDuration(opts.SessionTTL); err != nil {
			return nil, fmt.Errorf("invalid SessionTTL: %v", err)
		}
	}
	l := &Lock{
		c:    c,
		opts: opts,
	}
	return l, nil
}

// Lock attempts to acquire the lock and blocks while doing so.
// Providing a non-nil stopCh can be used to abort the lock attempt.
// Returns a channel that is closed if our lock is lost or an error.
// This channel could be closed at any time due to session invalidation,
// communication errors, operator intervention, etc. It is NOT safe to
// assume that the lock is held until Unlock() unless the Session is specifically
// created without any associated health checks. By default Consul sessions
// prefer liveness over safety and an application must be able to handle
// the lock being lost.
func (l *Lock) Lock(stopCh <-chan struct{}) (<-chan struct{}, error) {
	// Hold the lock as we try to acquire
	l.l.Lock()
	defer l.l.Unlock()

	// Check if we already hold the lock
	if l.isHeld {
		return nil, ErrLockHeld
	}

	// Check if we need to create a session first
	l.lockSession = l.opts.Session
	if l.lockSession == "" {
		if s, err := l.createSession(); err != nil {
			return nil, fmt.Errorf("failed to create session: %v", err)
		} else {
			l.sessionRenew = make(chan struct{})
			l.lockSession = s
			session := l.c.Session()
			go session.RenewPeriodic(l.opts.SessionTTL, s, nil, l.sessionRenew)

			// If we fail to acquire the lock, cleanup the session
			defer func() {
				if !l.isHeld {
					close(l.sessionRenew)
					l.sessionRenew = nil
				}
			}()
		}
	}

	// Setup the query options
	kv := l.c.KV()
	qOpts := &QueryOptions{
		WaitTime: DefaultLockWaitTime,
	}

WAIT:
	// Check if we should quit
	select {
	case <-stopCh:
		return nil, nil
	default:
	}

	// Look for an existing lock, blocking until not taken
	pair, meta, err := kv.Get(l.opts.Key, qOpts)
	if err != nil {
		return nil, fmt.Errorf("failed to read lock: %v", err)
	}
	if pair != nil && pair.Flags != LockFlagValue {
		return nil, ErrLockConflict
	}
	locked := false
	if pair != nil && pair.Session == l.lockSession {
		goto HELD
	}
	if pair != nil && pair.Session != "" {
		qOpts.WaitIndex = meta.LastIndex
		goto WAIT
	}

	// Try to acquire the lock
	pair = l.lockEntry(l.lockSession)
	locked, _, err = kv.Acquire(pair, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to acquire lock: %v", err)
	}

	// Handle the case of not getting the lock
	if !locked {
		// Determine why the lock failed
		qOpts.WaitIndex = 0
		pair, meta, err = kv.Get(l.opts.Key, qOpts)
		if pair != nil && pair.Session != "" {
			//If the session is not null, this means that a wait can safely happen
			//using a long poll
			qOpts.WaitIndex = meta.LastIndex
			goto WAIT
		} else {
			// If the session is empty and the lock failed to acquire, then it means
			// a lock-delay is in effect and a timed wait must be used
			select {
			case <-time.After(DefaultLockRetryTime):
				goto WAIT
			case <-stopCh:
				return nil, nil
			}
		}
	}

HELD:
	// Watch to ensure we maintain leadership
	leaderCh := make(chan struct{})
	go l.monitorLock(l.lockSession, leaderCh)

	// Set that we own the lock
	l.isHeld = true

	// Locked! All done
	return leaderCh, nil
}

// Unlock released the lock. It is an error to call this
// if the lock is not currently held.
func (l *Lock) Unlock() error {
	// Hold the lock as we try to release
	l.l.Lock()
	defer l.l.Unlock()

	// Ensure the lock is actually held
	if !l.isHeld {
		return ErrLockNotHeld
	}

	// Set that we no longer own the lock
	l.isHeld = false

	// Stop the session renew
	if l.sessionRenew != nil {
		defer func() {
			close(l.sessionRenew)
			l.sessionRenew = nil
		}()
	}

	// Get the lock entry, and clear the lock session
	lockEnt := l.lockEntry(l.lockSession)
	l.lockSession = ""

	// Release the lock explicitly
	kv := l.c.KV()
	_, _, err := kv.Release(lockEnt, nil)
	if err != nil {
		return fmt.Errorf("failed to release lock: %v", err)
	}
	return nil
}

// Destroy is used to cleanup the lock entry. It is not necessary
// to invoke. It will fail if the lock is in use.
func (l *Lock) Destroy() error {
	// Hold the lock as we try to release
	l.l.Lock()
	defer l.l.Unlock()

	// Check if we already hold the lock
	if l.isHeld {
		return ErrLockHeld
	}

	// Look for an existing lock
	kv := l.c.KV()
	pair, _, err := kv.Get(l.opts.Key, nil)
	if err != nil {
		return fmt.Errorf("failed to read lock: %v", err)
	}

	// Nothing to do if the lock does not exist
	if pair == nil {
		return nil
	}

	// Check for possible flag conflict
	if pair.Flags != LockFlagValue {
		return ErrLockConflict
	}

	// Check if it is in use
	if pair.Session != "" {
		return ErrLockInUse
	}

	// Attempt the delete
	didRemove, _, err := kv.DeleteCAS(pair, nil)
	if err != nil {
		return fmt.Errorf("failed to remove lock: %v", err)
	}
	if !didRemove {
		return ErrLockInUse
	}
	return nil
}

// createSession is used to create a new managed session
func (l *Lock) createSession() (string, error) {
	session := l.c.Session()
	se := &SessionEntry{
		Name: l.opts.SessionName,
		TTL:  l.opts.SessionTTL,
	}
	id, _, err := session.Create(se, nil)
	if err != nil {
		return "", err
	}
	return id, nil
}

// lockEntry returns a formatted KVPair for the lock
func (l *Lock) lockEntry(session string) *KVPair {
	return &KVPair{
		Key:     l.opts.Key,
		Value:   l.opts.Value,
		Session: session,
		Flags:   LockFlagValue,
	}
}

// monitorLock is a long running routine to monitor a lock ownership
// It closes the stopCh if we lose our leadership.
func (l *Lock) monitorLock(session string, stopCh chan struct{}) {
	defer close(stopCh)
	kv := l.c.KV()
	opts := &QueryOptions{RequireConsistent: true}
WAIT:
	retries := l.opts.MonitorRetries
RETRY:
	pair, meta, err := kv.Get(l.opts.Key, opts)
	if err != nil {
		// TODO (slackpad) - Make a real error type here instead of using
		// a string check.
		const serverError = "Unexpected response code: 500"

		// If configured we can try to ride out a brief Consul unavailability
		// by doing retries. Note that we have to attempt the retry in a non-
		// blocking fashion so that we have a clean place to reset the retry
		// counter if service is restored.
		if retries > 0 && strings.Contains(err.Error(), serverError) {
			time.Sleep(DefaultMonitorRetryTime)
			retries--
			opts.WaitIndex = 0
			goto RETRY
		}
		return
	}
	if pair != nil && pair.Session == session {
		opts.WaitIndex = meta.LastIndex
		goto WAIT
	}
}
<commit_msg>Adds custom retry time for lock monitors.<commit_after>package api

import (
	"fmt"
	"strings"
	"sync"
	"time"
)

const (
	// DefaultLockSessionName is the Session Name we assign if none is provided
	DefaultLockSessionName = "Consul API Lock"

	// DefaultLockSessionTTL is the default session TTL if no Session is provided
	// when creating a new Lock. This is used because we do not have another
	// other check to depend upon.
	DefaultLockSessionTTL = "15s"

	// DefaultLockWaitTime is how long we block for at a time to check if lock
	// acquisition is possible. This affects the minimum time it takes to cancel
	// a Lock acquisition.
	DefaultLockWaitTime = 15 * time.Second

	// DefaultLockRetryTime is how long we wait after a failed lock acquisition
	// before attempting to do the lock again. This is so that once a lock-delay
	// is in effect, we do not hot loop retrying the acquisition.
	DefaultLockRetryTime = 5 * time.Second

	// DefaultMonitorRetryTime is how long we wait after a failed monitor check
	// of a lock (500 response code). This allows the monitor to ride out brief
	// periods of unavailability, subject to the MonitorRetries setting in the
	// lock options which is by default set to 0, disabling this feature.
	DefaultMonitorRetryTime = 2 * time.Second

	// LockFlagValue is a magic flag we set to indicate a key
	// is being used for a lock. It is used to detect a potential
	// conflict with a semaphore.
	LockFlagValue = 0x2ddccbc058a50c18
)

var (
	// ErrLockHeld is returned if we attempt to double lock
	ErrLockHeld = fmt.Errorf("Lock already held")

	// ErrLockNotHeld is returned if we attempt to unlock a lock
	// that we do not hold.
	ErrLockNotHeld = fmt.Errorf("Lock not held")

	// ErrLockInUse is returned if we attempt to destroy a lock
	// that is in use.
	ErrLockInUse = fmt.Errorf("Lock in use")

	// ErrLockConflict is returned if the flags on a key
	// used for a lock do not match expectation
	ErrLockConflict = fmt.Errorf("Existing key does not match lock use")
)

// Lock is used to implement client-side leader election. It is follows the
// algorithm as described here: https://consul.io/docs/guides/leader-election.html.
type Lock struct {
	c    *Client
	opts *LockOptions

	isHeld       bool
	sessionRenew chan struct{}
	lockSession  string
	l            sync.Mutex
}

// LockOptions is used to parameterize the Lock behavior.
type LockOptions struct {
	Key              string        // Must be set and have write permissions
	Value            []byte        // Optional, value to associate with the lock
	Session          string        // Optional, created if not specified
	SessionName      string        // Optional, defaults to DefaultLockSessionName
	SessionTTL       string        // Optional, defaults to DefaultLockSessionTTL
	MonitorRetries   int           // Optional, defaults to 0 which means no retries
	MonitorRetryTime time.Duration // Optional, defaults to DefaultMonitorRetryTime
}

// LockKey returns a handle to a lock struct which can be used
// to acquire and release the mutex. The key used must have
// write permissions.
func (c *Client) LockKey(key string) (*Lock, error) {
	opts := &LockOptions{
		Key: key,
	}
	return c.LockOpts(opts)
}

// LockOpts returns a handle to a lock struct which can be used
// to acquire and release the mutex. The key used must have
// write permissions.
func (c *Client) LockOpts(opts *LockOptions) (*Lock, error) {
	if opts.Key == "" {
		return nil, fmt.Errorf("missing key")
	}
	if opts.SessionName == "" {
		opts.SessionName = DefaultLockSessionName
	}
	if opts.SessionTTL == "" {
		opts.SessionTTL = DefaultLockSessionTTL
	} else {
		if _, err := time.ParseDuration(opts.SessionTTL); err != nil {
			return nil, fmt.Errorf("invalid SessionTTL: %v", err)
		}
	}
	if opts.MonitorRetryTime == 0 {
		opts.MonitorRetryTime = DefaultMonitorRetryTime
	}
	l := &Lock{
		c:    c,
		opts: opts,
	}
	return l, nil
}

// Lock attempts to acquire the lock and blocks while doing so.
// Providing a non-nil stopCh can be used to abort the lock attempt.
// Returns a channel that is closed if our lock is lost or an error.
// This channel could be closed at any time due to session invalidation,
// communication errors, operator intervention, etc. It is NOT safe to
// assume that the lock is held until Unlock() unless the Session is specifically
// created without any associated health checks. By default Consul sessions
// prefer liveness over safety and an application must be able to handle
// the lock being lost.
func (l *Lock) Lock(stopCh <-chan struct{}) (<-chan struct{}, error) {
	// Hold the lock as we try to acquire
	l.l.Lock()
	defer l.l.Unlock()

	// Check if we already hold the lock
	if l.isHeld {
		return nil, ErrLockHeld
	}

	// Check if we need to create a session first
	l.lockSession = l.opts.Session
	if l.lockSession == "" {
		if s, err := l.createSession(); err != nil {
			return nil, fmt.Errorf("failed to create session: %v", err)
		} else {
			l.sessionRenew = make(chan struct{})
			l.lockSession = s
			session := l.c.Session()
			go session.RenewPeriodic(l.opts.SessionTTL, s, nil, l.sessionRenew)

			// If we fail to acquire the lock, cleanup the session
			defer func() {
				if !l.isHeld {
					close(l.sessionRenew)
					l.sessionRenew = nil
				}
			}()
		}
	}

	// Setup the query options
	kv := l.c.KV()
	qOpts := &QueryOptions{
		WaitTime: DefaultLockWaitTime,
	}

WAIT:
	// Check if we should quit
	select {
	case <-stopCh:
		return nil, nil
	default:
	}

	// Look for an existing lock, blocking until not taken
	pair, meta, err := kv.Get(l.opts.Key, qOpts)
	if err != nil {
		return nil, fmt.Errorf("failed to read lock: %v", err)
	}
	if pair != nil && pair.Flags != LockFlagValue {
		return nil, ErrLockConflict
	}
	locked := false
	if pair != nil && pair.Session == l.lockSession {
		goto HELD
	}
	if pair != nil && pair.Session != "" {
		qOpts.WaitIndex = meta.LastIndex
		goto WAIT
	}

	// Try to acquire the lock
	pair = l.lockEntry(l.lockSession)
	locked, _, err = kv.Acquire(pair, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to acquire lock: %v", err)
	}

	// Handle the case of not getting the lock
	if !locked {
		// Determine why the lock failed
		qOpts.WaitIndex = 0
		pair, meta, err = kv.Get(l.opts.Key, qOpts)
		if pair != nil && pair.Session != "" {
			//If the session is not null, this means that a wait can safely happen
			//using a long poll
			qOpts.WaitIndex = meta.LastIndex
			goto WAIT
		} else {
			// If the session is empty and the lock failed to acquire, then it means
			// a lock-delay is in effect and a timed wait must be used
			select {
			case <-time.After(DefaultLockRetryTime):
				goto WAIT
			case <-stopCh:
				return nil, nil
			}
		}
	}

HELD:
	// Watch to ensure we maintain leadership
	leaderCh := make(chan struct{})
	go l.monitorLock(l.lockSession, leaderCh)

	// Set that we own the lock
	l.isHeld = true

	// Locked! All done
	return leaderCh, nil
}

// Unlock released the lock. It is an error to call this
// if the lock is not currently held.
func (l *Lock) Unlock() error {
	// Hold the lock as we try to release
	l.l.Lock()
	defer l.l.Unlock()

	// Ensure the lock is actually held
	if !l.isHeld {
		return ErrLockNotHeld
	}

	// Set that we no longer own the lock
	l.isHeld = false

	// Stop the session renew
	if l.sessionRenew != nil {
		defer func() {
			close(l.sessionRenew)
			l.sessionRenew = nil
		}()
	}

	// Get the lock entry, and clear the lock session
	lockEnt := l.lockEntry(l.lockSession)
	l.lockSession = ""

	// Release the lock explicitly
	kv := l.c.KV()
	_, _, err := kv.Release(lockEnt, nil)
	if err != nil {
		return fmt.Errorf("failed to release lock: %v", err)
	}
	return nil
}

// Destroy is used to cleanup the lock entry. It is not necessary
// to invoke. It will fail if the lock is in use.
func (l *Lock) Destroy() error {
	// Hold the lock as we try to release
	l.l.Lock()
	defer l.l.Unlock()

	// Check if we already hold the lock
	if l.isHeld {
		return ErrLockHeld
	}

	// Look for an existing lock
	kv := l.c.KV()
	pair, _, err := kv.Get(l.opts.Key, nil)
	if err != nil {
		return fmt.Errorf("failed to read lock: %v", err)
	}

	// Nothing to do if the lock does not exist
	if pair == nil {
		return nil
	}

	// Check for possible flag conflict
	if pair.Flags != LockFlagValue {
		return ErrLockConflict
	}

	// Check if it is in use
	if pair.Session != "" {
		return ErrLockInUse
	}

	// Attempt the delete
	didRemove, _, err := kv.DeleteCAS(pair, nil)
	if err != nil {
		return fmt.Errorf("failed to remove lock: %v", err)
	}
	if !didRemove {
		return ErrLockInUse
	}
	return nil
}

// createSession is used to create a new managed session
func (l *Lock) createSession() (string, error) {
	session := l.c.Session()
	se := &SessionEntry{
		Name: l.opts.SessionName,
		TTL:  l.opts.SessionTTL,
	}
	id, _, err := session.Create(se, nil)
	if err != nil {
		return "", err
	}
	return id, nil
}

// lockEntry returns a formatted KVPair for the lock
func (l *Lock) lockEntry(session string) *KVPair {
	return &KVPair{
		Key:     l.opts.Key,
		Value:   l.opts.Value,
		Session: session,
		Flags:   LockFlagValue,
	}
}

// monitorLock is a long running routine to monitor a lock ownership
// It closes the stopCh if we lose our leadership.
func (l *Lock) monitorLock(session string, stopCh chan struct{}) {
	defer close(stopCh)
	kv := l.c.KV()
	opts := &QueryOptions{RequireConsistent: true}
WAIT:
	retries := l.opts.MonitorRetries
RETRY:
	pair, meta, err := kv.Get(l.opts.Key, opts)
	if err != nil {
		// TODO (slackpad) - Make a real error type here instead of using
		// a string check.
		const serverError = "Unexpected response code: 500"

		// If configured we can try to ride out a brief Consul unavailability
		// by doing retries. Note that we have to attempt the retry in a non-
		// blocking fashion so that we have a clean place to reset the retry
		// counter if service is restored.
		if retries > 0 && strings.Contains(err.Error(), serverError) {
			time.Sleep(l.opts.MonitorRetryTime)
			retries--
			opts.WaitIndex = 0
			goto RETRY
		}
		return
	}
	if pair != nil && pair.Session == session {
		opts.WaitIndex = meta.LastIndex
		goto WAIT
	}
}
<|endoftext|>


<commit_before>/*
    MP3Cat is a fast command line utility for concatenating MP3 files
    without re-encoding. It supports both constant bit rate (CBR) and
    variable bit rate (VBR) files.
*/
package main

import (
    "fmt"
    "io"
    "os"
    "path"
    "path/filepath"
    "golang.org/x/crypto/ssh/terminal"
    "github.com/dmulholland/mp3lib"
    "github.com/dmulholland/args"
)

const version = "3.0.0"

var helptext = fmt.Sprintf(`
Usage: %s [FLAGS] [OPTIONS] [ARGUMENTS]

  This tool concatenates MP3 files without re-encoding. It supports both
  constant bit rate (CBR) and variable bit rate (VBR) MP3 files.

  Files to be merged can be specified as a list of filenames:

    $ mp3cat one.mp3 two.mp3 three.mp3

  Alternatively, an entire directory of .mp3 files can be merged:

    $ mp3cat --dir /path/to/directory

Arguments:
  [files]                 List of files to merge.

Options:
  -d, --dir <path>        Directory of files to merge.
  -i, --interlace <path>  Interlace a spacer file between each input file.
  -o, --out <path>        Output filename. Defaults to 'output.mp3'.

Flags:
  -f, --force             Overwrite an existing output file.
  -h, --help              Display this help text and exit.
  -q, --quiet             Run in quiet mode.
  -t, --tag               Copy the ID3 tag from the first input file.
  -v, --version           Display the application's version number and exit.
`, filepath.Base(os.Args[0]))

func main() {

    // Parse the command line arguments.
    parser := args.NewParser()
    parser.Helptext = helptext
    parser.Version = version
    parser.NewFlag("force f")
    parser.NewFlag("quiet q")
    parser.NewFlag("debug")
    parser.NewFlag("tag t")
    parser.NewString("out o", "output.mp3")
    parser.NewString("dir d")
    parser.NewString("interlace i")
    parser.Parse()

    // Make sure we have a list of files to merge.
    var files []string
    if parser.Found("dir") {
        globs, err := filepath.Glob(path.Join(parser.GetString("dir"), "*.mp3"))
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }
        if globs == nil || len(globs) == 0 {
            fmt.Fprintln(os.Stderr, "Error: no files found.")
            os.Exit(1)
        }
        files = globs
    } else if parser.HasArgs() {
        files = parser.GetArgs()
    } else {
        fmt.Fprintln(os.Stderr, "Error: you must specify files to merge.")
        os.Exit(1)
    }

    // Are we interlacing a spacer file?
    if parser.Found("interlace") {
        files = interlace(files, parser.GetString("interlace"))
    }

    // Make sure all the files in the list actually exist.
    validateFiles(files)

    // Set debug mode if the user supplied a --debug flag.
    if parser.GetFlag("debug") {
        mp3lib.DebugMode = true
    }

    // Merge the input files.
    merge(
        parser.GetString("out"),
        files,
        parser.GetFlag("force"),
        parser.GetFlag("quiet"),
        parser.GetFlag("tag"))
}

// Check that all the files in the list exist.
func validateFiles(files []string) {
    for _, file := range files {
        if _, err := os.Stat(file); err != nil {
            fmt.Fprintf(
                os.Stderr,
                "Error: the file '%v' does not exist.\n", file)
            os.Exit(1)
        }
    }
}

// Interlace a spacer file between each file in the list.
func interlace(files []string, spacer string) []string {
    var interlaced []string
    for _, file := range files {
        interlaced = append(interlaced, file)
        interlaced = append(interlaced, spacer)
    }
    return interlaced[:len(interlaced) - 1]
}

// Create a new file at the specified output path containing the merged
// contents of the list of input files.
func merge(outpath string, inpaths []string, force, quiet, tag bool) {

    var totalFrames uint32
    var totalBytes uint32
    var totalFiles int
    var firstBitRate int
    var isVBR bool

    // Only overwrite an existing file if the --force flag has been used.
    if _, err := os.Stat(outpath); err == nil {
        if !force {
            fmt.Fprintf(
                os.Stderr,
                "Error: the file '%v' already exists.\n", outpath)
            os.Exit(1)
        }
    }

    // If the list of input files includes the output file we'll end up in an
    // infinite loop.
    for _, filepath := range inpaths {
        if filepath == outpath {
            fmt.Fprintln(
                os.Stderr,
                "Error: the list of input files includes the output file.")
            os.Exit(1)
        }
    }

    // Create the output file.
    outfile, err := os.Create(outpath)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    if !quiet {
        line()
    }

    // Loop over the input files and append their MP3 frames to the output
    // file.
    for _, inpath := range inpaths {

        if !quiet {
            fmt.Println("+", inpath)
        }

        infile, err := os.Open(inpath)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        isFirstFrame := true

        for {

            // Read the next frame from the input file.
            frame := mp3lib.NextFrame(infile)
            if frame == nil {
                break
            }

            // Skip the first frame if it's a VBR header.
            if isFirstFrame {
                isFirstFrame = false
                if mp3lib.IsXingHeader(frame) || mp3lib.IsVbriHeader(frame) {
                    continue
                }
            }

            // If we detect more than one bitrate we'll need to add a VBR
            // header to the output file.
            if firstBitRate == 0 {
                firstBitRate = frame.BitRate
            } else if frame.BitRate != firstBitRate {
                isVBR = true
            }

            // Write the frame to the output file.
            _, err := outfile.Write(frame.RawBytes)
            if err != nil {
                fmt.Fprintln(os.Stderr, err)
                os.Exit(1)
            }

            totalFrames += 1
            totalBytes += uint32(len(frame.RawBytes))
        }

        infile.Close()
        totalFiles += 1
    }

    outfile.Close()
    if !quiet {
        line()
    }

    // If we detected multiple bitrates, prepend a VBR header to the file.
    if isVBR {
        if !quiet {
            fmt.Println("• Multiple bitrates detected. Adding VBR header.")
        }
        addXingHeader(outpath, totalFrames, totalBytes)
    }

    // Copy the ID3v2 tag from the first input file if requested. Order of
    // operations is important here. The ID3 tag must be the first item in
    // the file - in particular, it must come *before* any VBR header.
    if tag {
        if !quiet {
            fmt.Println("• Adding ID3 tag.")
        }
        addID3v2Tag(outpath, inpaths[0])
    }

    // Print a count of the number of files merged.
    if !quiet {
        fmt.Printf("• %v files merged.\n", totalFiles)
        line()
    }
}

// Prepend an Xing VBR header to the specified MP3 file.
func addXingHeader(filepath string, totalFrames, totalBytes uint32) {

    outputFile, err := os.Create(filepath + ".mp3cat.tmp")
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    inputFile, err := os.Open(filepath)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    templateFrame := mp3lib.NextFrame(inputFile)
    inputFile.Seek(0, 0)

    xingHeader := mp3lib.NewXingHeader(templateFrame, totalFrames, totalBytes)

    _, err = outputFile.Write(xingHeader.RawBytes)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    _, err = io.Copy(outputFile, inputFile)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    outputFile.Close()
    inputFile.Close()

    err = os.Remove(filepath)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    err = os.Rename(filepath + ".mp3cat.tmp", filepath)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
}

// Prepend an ID3v2 tag to the MP3 file at mp3Path, copying from tagPath.
func addID3v2Tag(mp3Path, tagPath string) {

    tagFile, err := os.Open(tagPath)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    id3tag := mp3lib.NextID3v2Tag(tagFile)
    tagFile.Close()

    if id3tag != nil {
        outputFile, err := os.Create(mp3Path + ".mp3cat.tmp")
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        inputFile, err := os.Open(mp3Path)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        _, err = outputFile.Write(id3tag.RawBytes)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        _, err = io.Copy(outputFile, inputFile)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        outputFile.Close()
        inputFile.Close()

        err = os.Remove(mp3Path)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        err = os.Rename(mp3Path + ".mp3cat.tmp", mp3Path)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }
    }
}

// Print a line to stdout if we're running in a terminal.
func line() {
    if terminal.IsTerminal(int(os.Stdout.Fd())) {
        width, _, err := terminal.GetSize(int(os.Stdout.Fd()))
        if err == nil {
            for i := 0; i < width; i++ {
                fmt.Print("─")
            }
            fmt.Println()
        }
    }
}
<commit_msg>Make the --dir flag case insensitive<commit_after>/*
    MP3Cat is a fast command line utility for concatenating MP3 files
    without re-encoding. It supports both constant bit rate (CBR) and
    variable bit rate (VBR) files.
*/
package main

import (
    "fmt"
    "io"
    "os"
    "path"
    "path/filepath"
    "golang.org/x/crypto/ssh/terminal"
    "github.com/dmulholland/mp3lib"
    "github.com/dmulholland/args"
)

const version = "3.1.0.dev"

var helptext = fmt.Sprintf(`
Usage: %s [FLAGS] [OPTIONS] [ARGUMENTS]

  This tool concatenates MP3 files without re-encoding. It supports both
  constant bit rate (CBR) and variable bit rate (VBR) MP3 files.

  Files to be merged can be specified as a list of filenames:

    $ mp3cat one.mp3 two.mp3 three.mp3

  Alternatively, an entire directory of .mp3 files can be merged:

    $ mp3cat --dir /path/to/directory

Arguments:
  [files]                 List of files to merge.

Options:
  -d, --dir <path>        Directory of files to merge.
  -i, --interlace <path>  Interlace a spacer file between each input file.
  -o, --out <path>        Output filename. Defaults to 'output.mp3'.

Flags:
  -f, --force             Overwrite an existing output file.
  -h, --help              Display this help text and exit.
  -q, --quiet             Run in quiet mode.
  -t, --tag               Copy the ID3 tag from the first input file.
  -v, --version           Display the application's version number and exit.
`, filepath.Base(os.Args[0]))

func main() {

    // Parse the command line arguments.
    parser := args.NewParser()
    parser.Helptext = helptext
    parser.Version = version
    parser.NewFlag("force f")
    parser.NewFlag("quiet q")
    parser.NewFlag("debug")
    parser.NewFlag("tag t")
    parser.NewString("out o", "output.mp3")
    parser.NewString("dir d")
    parser.NewString("interlace i")
    parser.Parse()

    // Make sure we have a list of files to merge.
    var files []string
    if parser.Found("dir") {
        pattern := "*.[Mm][Pp]3"
        globs, err := filepath.Glob(path.Join(parser.GetString("dir"), pattern))
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }
        if globs == nil || len(globs) == 0 {
            fmt.Fprintln(os.Stderr, "Error: no files found.")
            os.Exit(1)
        }
        files = globs
    } else if parser.HasArgs() {
        files = parser.GetArgs()
    } else {
        fmt.Fprintln(os.Stderr, "Error: you must specify files to merge.")
        os.Exit(1)
    }

    // Are we interlacing a spacer file?
    if parser.Found("interlace") {
        files = interlace(files, parser.GetString("interlace"))
    }

    // Make sure all the files in the list actually exist.
    validateFiles(files)

    // Set debug mode if the user supplied a --debug flag.
    if parser.GetFlag("debug") {
        mp3lib.DebugMode = true
    }

    // Merge the input files.
    merge(
        parser.GetString("out"),
        files,
        parser.GetFlag("force"),
        parser.GetFlag("quiet"),
        parser.GetFlag("tag"))
}

// Check that all the files in the list exist.
func validateFiles(files []string) {
    for _, file := range files {
        if _, err := os.Stat(file); err != nil {
            fmt.Fprintf(
                os.Stderr,
                "Error: the file '%v' does not exist.\n", file)
            os.Exit(1)
        }
    }
}

// Interlace a spacer file between each file in the list.
func interlace(files []string, spacer string) []string {
    var interlaced []string
    for _, file := range files {
        interlaced = append(interlaced, file)
        interlaced = append(interlaced, spacer)
    }
    return interlaced[:len(interlaced) - 1]
}

// Create a new file at the specified output path containing the merged
// contents of the list of input files.
func merge(outpath string, inpaths []string, force, quiet, tag bool) {

    var totalFrames uint32
    var totalBytes uint32
    var totalFiles int
    var firstBitRate int
    var isVBR bool

    // Only overwrite an existing file if the --force flag has been used.
    if _, err := os.Stat(outpath); err == nil {
        if !force {
            fmt.Fprintf(
                os.Stderr,
                "Error: the file '%v' already exists.\n", outpath)
            os.Exit(1)
        }
    }

    // If the list of input files includes the output file we'll end up in an
    // infinite loop.
    for _, filepath := range inpaths {
        if filepath == outpath {
            fmt.Fprintln(
                os.Stderr,
                "Error: the list of input files includes the output file.")
            os.Exit(1)
        }
    }

    // Create the output file.
    outfile, err := os.Create(outpath)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    if !quiet {
        line()
    }

    // Loop over the input files and append their MP3 frames to the output
    // file.
    for _, inpath := range inpaths {

        if !quiet {
            fmt.Println("+", inpath)
        }

        infile, err := os.Open(inpath)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        isFirstFrame := true

        for {

            // Read the next frame from the input file.
            frame := mp3lib.NextFrame(infile)
            if frame == nil {
                break
            }

            // Skip the first frame if it's a VBR header.
            if isFirstFrame {
                isFirstFrame = false
                if mp3lib.IsXingHeader(frame) || mp3lib.IsVbriHeader(frame) {
                    continue
                }
            }

            // If we detect more than one bitrate we'll need to add a VBR
            // header to the output file.
            if firstBitRate == 0 {
                firstBitRate = frame.BitRate
            } else if frame.BitRate != firstBitRate {
                isVBR = true
            }

            // Write the frame to the output file.
            _, err := outfile.Write(frame.RawBytes)
            if err != nil {
                fmt.Fprintln(os.Stderr, err)
                os.Exit(1)
            }

            totalFrames += 1
            totalBytes += uint32(len(frame.RawBytes))
        }

        infile.Close()
        totalFiles += 1
    }

    outfile.Close()
    if !quiet {
        line()
    }

    // If we detected multiple bitrates, prepend a VBR header to the file.
    if isVBR {
        if !quiet {
            fmt.Println("• Multiple bitrates detected. Adding VBR header.")
        }
        addXingHeader(outpath, totalFrames, totalBytes)
    }

    // Copy the ID3v2 tag from the first input file if requested. Order of
    // operations is important here. The ID3 tag must be the first item in
    // the file - in particular, it must come *before* any VBR header.
    if tag {
        if !quiet {
            fmt.Println("• Adding ID3 tag.")
        }
        addID3v2Tag(outpath, inpaths[0])
    }

    // Print a count of the number of files merged.
    if !quiet {
        fmt.Printf("• %v files merged.\n", totalFiles)
        line()
    }
}

// Prepend an Xing VBR header to the specified MP3 file.
func addXingHeader(filepath string, totalFrames, totalBytes uint32) {

    outputFile, err := os.Create(filepath + ".mp3cat.tmp")
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    inputFile, err := os.Open(filepath)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    templateFrame := mp3lib.NextFrame(inputFile)
    inputFile.Seek(0, 0)

    xingHeader := mp3lib.NewXingHeader(templateFrame, totalFrames, totalBytes)

    _, err = outputFile.Write(xingHeader.RawBytes)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    _, err = io.Copy(outputFile, inputFile)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    outputFile.Close()
    inputFile.Close()

    err = os.Remove(filepath)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    err = os.Rename(filepath + ".mp3cat.tmp", filepath)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
}

// Prepend an ID3v2 tag to the MP3 file at mp3Path, copying from tagPath.
func addID3v2Tag(mp3Path, tagPath string) {

    tagFile, err := os.Open(tagPath)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    id3tag := mp3lib.NextID3v2Tag(tagFile)
    tagFile.Close()

    if id3tag != nil {
        outputFile, err := os.Create(mp3Path + ".mp3cat.tmp")
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        inputFile, err := os.Open(mp3Path)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        _, err = outputFile.Write(id3tag.RawBytes)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        _, err = io.Copy(outputFile, inputFile)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        outputFile.Close()
        inputFile.Close()

        err = os.Remove(mp3Path)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        err = os.Rename(mp3Path + ".mp3cat.tmp", mp3Path)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }
    }
}

// Print a line to stdout if we're running in a terminal.
func line() {
    if terminal.IsTerminal(int(os.Stdout.Fd())) {
        width, _, err := terminal.GetSize(int(os.Stdout.Fd()))
        if err == nil {
            for i := 0; i < width; i++ {
                fmt.Print("─")
            }
            fmt.Println()
        }
    }
}
<|endoftext|>


<commit_before>package turbo

import (
	"encoding/json"
	"log"
	"strings"
)

type MsgHub struct {
	// Registered connections.
	connections map[uint64]*Conn
	// Register requests from the connections.
	registration chan *Conn
	// Unregister requests from connections.
	unregistration chan *Conn
	// Message bus reference
	bus *MsgBus
}

func NewMsgHub(bus *MsgBus) *MsgHub {
	hub := MsgHub{
		registration:   make(chan *Conn),
		unregistration: make(chan *Conn),
		connections:    make(map[uint64]*Conn),
		bus:            bus,
	}
	return &hub
}

func (hub *MsgHub) listen() {
	for {
		select {
		// There is a Conn 'c' in the registration queue
		case conn := <-hub.registration:
			hub.connections[conn.id] = conn
			log.Printf("Connection #%d connected.\n", conn.id)
		// There is a Conn 'c' in the unregistration queue
		case conn := <-hub.unregistration:
			delete(hub.connections, conn.id)
			hub.bus.unsubscribeAll(conn)
			close(conn.outbox)
			conn.ws.Close()
			log.Printf("Connection #%d was killed.\n", conn.id)
		}
	}
}

func (hub *MsgHub) registerConn(conn *Conn) {
	hub.registration <- conn
}

func (hub *MsgHub) unregisterConn(conn *Conn) {
	hub.unregistration <- conn
}

func (hub *MsgHub) route(rawMsg *RawMsg) {
	payload := rawMsg.Payload
	conn := rawMsg.Conn

	msg := Msg{}
	err := json.Unmarshal(payload, &msg)
	if err != nil {
		log.Fatalln("Msg router could not marshal json of an incoming msg", err)
		return
	}

	switch msg.Cmd {
	case MSG_CMD_ON:
		log.Printf("Connection #%d subscribed to: '%s', event: '%d'\n", conn.id, msg.Path, msg.Event)
		hub.bus.subscribe(msg.Event, msg.Path, conn)
	case MSG_CMD_OFF:
		log.Printf("Connection #%d unsubscribed from: '%s', event: '%d'\n", conn.id, msg.Path, msg.Event)
		hub.bus.unsubscribe(msg.Event, msg.Path, conn)
	case MSG_CMD_SET:
		log.Printf("Connection #%d has set a value to path: '%s'\n", conn.id, msg.Path)
		go hub.handleSet(&msg, conn)
	case MSG_CMD_UPDATE:
		log.Printf("Connection #%d has updated path: '%s'\n", conn.id, msg.Path)
		go hub.handleUpdate(&msg, conn)
	case MSG_CMD_REMOVE:
		log.Printf("Connection #%d has removed path: '%s'\n", conn.id, msg.Path)
		go hub.handleRemove(&msg, conn)
	case MSG_CMD_TRANS_SET:
		log.Printf("Connection #%d has done trans-set on path: '%s'\n", conn.id, msg.Path)
		go hub.handleTransSet(&msg, conn)
	case MSG_CMD_TRANS_GET:
		log.Printf("Connection #%d has done trans-get on path: '%s'\n", conn.id, msg.Path)
		go hub.handleTransGet(&msg, conn)
	default:
		log.Fatalf("Connection #%d submitted a message with cmd #%d which is unsupported\n", conn.id, msg.Cmd)
	}
}

func (hub *MsgHub) handleSet(msg *Msg, conn *Conn) {
	var unmarshalledValue interface{}
	jsonErr := json.Unmarshal(msg.Value, &unmarshalledValue)
	if jsonErr != nil {
		errStr := jsonErr.Error()
		hub.sendAck(conn, msg.Ack, &errStr, nil, "")
	} else {
		log.Println("Now setting value to path ", msg.Path)
		err := database.set(msg.Path, unmarshalledValue)
		if err != nil {
			errStr := err.Error()
			hub.sendAck(conn, msg.Ack, &errStr, nil, "")
		} else {
			hub.sendAck(conn, msg.Ack, nil, nil, "")
			hub.publishValueEvent(msg.Path, &msg.Value, conn)
		}
	}
}

// TODO add "remove with null" support
func (hub *MsgHub) handleUpdate(msg *Msg, conn *Conn) {
	if msg.Value == nil {
		return
	}
	propertyMap := make(map[string]json.RawMessage)
	json.Unmarshal(msg.Value, &propertyMap)
	responses := make(chan *error)
	for property, value := range propertyMap {
		go (func(path string, val json.RawMessage) {
			var unmarshalledValue interface{}

			newPath := hub.joinPaths(msg.Path, path)
			jsonErr := json.Unmarshal(val, &unmarshalledValue)
			if jsonErr != nil {
				responses <- &jsonErr
				return // ಠ_ಠ
			} else {
				err := database.set(path, unmarshalledValue)
				if err != nil {
					responses <- &err
					return
				}
			}
			responses <- nil
			hub.publishValueEvent(newPath, &val, conn)
		})(property, value)
	}
	// Collect the callbacks
	i := 1
	problems := ""
	for {
		select {
		case err := <-responses:
			if err != nil {
				problems += (*err).Error() + "\n"
			}

			if i == len(propertyMap) {
				// We're done - send the response
				if problems == "" {
					hub.sendAck(conn, msg.Ack, nil, nil, "")
				} else {
					hub.sendAck(conn, msg.Ack, &problems, nil, "")
				}
				close(responses)
				return
			} else {
				i = i + 1
			}
		}
	}
}

func (hub *MsgHub) handleRemove(msg *Msg, conn *Conn) {
	// Remove children first
	node := hub.bus.pathTree.get(msg.Path)
	if node == nil {
		err := "Path does not exist"
		hub.sendAck(conn, msg.Ack, &err, nil, "")
		return
	}
	// Depth first traversal of path
	node.cascade(func(subNode *PathTreeNode) {
		// Kill node and report to listeners
		subNode.destroy()
		hub.publishValueEvent(subNode.path, nil, conn)
	})
}

func (hub *MsgHub) handleTransSet(msg *Msg, conn *Conn) {
	// db get
	err, val := database.get(msg.Path)
	if err != nil {
		errStr := err.Error()
		hub.sendAck(conn, msg.Ack, &errStr, nil, "")
	}

	if val != nil {
		// grab le hash
		err, currValHash := hash(val)
		if err != nil {
			errStr := err.Error()
			hub.sendAck(conn, msg.Ack, &errStr, nil, "")
		}
		// compare le hashes
		if msg.Hash == string(currValHash[:]) {
			hub.handleSet(msg, conn) // actually
			// this should work dafaq
		} else {
			errStr := "conflict"
			hub.sendAck(conn, msg.Ack, &errStr, nil, "")
		}
	} else {
		// hashing dont make sense if val is nil
		// this is fine for now
		// no - we send nil
		hub.handleSet(msg, conn)
	}
}

func (hub *MsgHub) handleTransGet(msg *Msg, conn *Conn) {
	// db get
	err, val := database.get(msg.Path)
	if err != nil {
		errStr := err.Error()
		hub.sendAck(conn, msg.Ack, &errStr, nil, "")
		return
	}
	if val == nil {
		hub.sendAck(conn, msg.Ack, nil, nil, "")
		return
	}
	// grab le hash
	log.Println("Now hashing val:", val)
	err, currValHash := hash(val)
	if err != nil {
		errStr := err.Error()
		hub.sendAck(conn, msg.Ack, &errStr, nil, "")
	}
	// send the value with the hash
	hub.sendAck(conn, msg.Ack, nil, val, string(currValHash[:]))
}

func (hub *MsgHub) publishValueEvent(path string, value *json.RawMessage, conn *Conn) {
	var hasValueSubs bool
	var hasChildChangedSubs bool
	var parentPath string

	if hub.hasParent(path) {
		parentPath = hub.parentOf(path)
	}
	// Check if anyone is event subscribed to this
	hasValueSubs = hub.bus.hasSubscribers(EVENT_TYPE_VALUE, path)
	if parentPath != "" {
		hasChildChangedSubs = hub.bus.hasSubscribers(EVENT_TYPE_CHILD_CHANGED, path)
	}
	// Do not continue if there are no subscribers
	if !hasValueSubs && !hasChildChangedSubs {
		return
	}
	// This is what we are sending subscribers to a value related event
	evt := ValueEvent{
		Path:  path,
		Value: value,
	}
	// Send the event to value listeners
	if hasValueSubs {
		// Set the event type; jsonify
		evt.Event = EVENT_TYPE_VALUE
		evtJson, err := json.Marshal(evt)
		if err != nil {
			problem := "Couldn't marshal msg value json\n"
			log.Fatalln(problem, err)
			return
		}
		hub.bus.publish(EVENT_TYPE_VALUE, path, evtJson)
	}
	// Send the event to child changed listeners
	if hasChildChangedSubs && hub.hasParent(path) {
		// Set the event type, parent path; jsonify
		if value == nil {
			evt.Event = EVENT_TYPE_CHILD_REMOVED
		} else {
			evt.Event = EVENT_TYPE_CHILD_CHANGED
		}
		evt.Path = hub.parentOf(path)
		evtJson, err := json.Marshal(evt)
		if err != nil {
			problem := "Couldn't marshal msg value json\n"
			log.Fatalln(problem, err)
			return
		}
		hub.bus.publish(EVENT_TYPE_CHILD_CHANGED, path, evtJson)
	}
}

func (hub *MsgHub) hasParent(path string) bool {
	return path != "/"
}

func (hub *MsgHub) parentOf(path string) string {
	lastIndex := strings.LastIndex(path, "/")
	return path[0:lastIndex]
}

func (hub *MsgHub) joinPaths(base string, extension string) string {
	if !strings.HasSuffix(base, "/") {
		base = base + "/"
	}
	if strings.HasPrefix(extension, "/") {
		if len(extension) > 1 {
			extension = extension[1:]
		} else {
			extension = ""
		}
	}
	return base + extension
}

func (hub *MsgHub) sendAck(conn *Conn, ack int, errString *string, result interface{}, hash string) {
	response := Ack{
		Type:   MSG_CMD_ACK,
		Ack:    ack,
		Result: result,
	}
	if hash != "" {
		// Strings cant be nil in go
		// YES. I KNOW.
		response.Hash = hash
	}
	if errString != nil {
		log.Println("Sending problem back to client in ack form:", *errString)
		response.Error = *errString
	}
	payload, err := json.Marshal(response)
	if err == nil {
		select {
		case conn.outbox <- payload:
		default:
			hub.unregisterConn(conn)
		}
	}
}
<commit_msg>oops - forgot to send ack in handle remove<commit_after>package turbo

import (
	"encoding/json"
	"log"
	"strings"
)

type MsgHub struct {
	// Registered connections.
	connections map[uint64]*Conn
	// Register requests from the connections.
	registration chan *Conn
	// Unregister requests from connections.
	unregistration chan *Conn
	// Message bus reference
	bus *MsgBus
}

func NewMsgHub(bus *MsgBus) *MsgHub {
	hub := MsgHub{
		registration:   make(chan *Conn),
		unregistration: make(chan *Conn),
		connections:    make(map[uint64]*Conn),
		bus:            bus,
	}
	return &hub
}

func (hub *MsgHub) listen() {
	for {
		select {
		// There is a Conn 'c' in the registration queue
		case conn := <-hub.registration:
			hub.connections[conn.id] = conn
			log.Printf("Connection #%d connected.\n", conn.id)
		// There is a Conn 'c' in the unregistration queue
		case conn := <-hub.unregistration:
			delete(hub.connections, conn.id)
			hub.bus.unsubscribeAll(conn)
			close(conn.outbox)
			conn.ws.Close()
			log.Printf("Connection #%d was killed.\n", conn.id)
		}
	}
}

func (hub *MsgHub) registerConn(conn *Conn) {
	hub.registration <- conn
}

func (hub *MsgHub) unregisterConn(conn *Conn) {
	hub.unregistration <- conn
}

func (hub *MsgHub) route(rawMsg *RawMsg) {
	payload := rawMsg.Payload
	conn := rawMsg.Conn

	msg := Msg{}
	err := json.Unmarshal(payload, &msg)
	if err != nil {
		log.Fatalln("Msg router could not marshal json of an incoming msg", err)
		return
	}

	switch msg.Cmd {
	case MSG_CMD_ON:
		log.Printf("Connection #%d subscribed to: '%s', event: '%d'\n", conn.id, msg.Path, msg.Event)
		hub.bus.subscribe(msg.Event, msg.Path, conn)
	case MSG_CMD_OFF:
		log.Printf("Connection #%d unsubscribed from: '%s', event: '%d'\n", conn.id, msg.Path, msg.Event)
		hub.bus.unsubscribe(msg.Event, msg.Path, conn)
	case MSG_CMD_SET:
		log.Printf("Connection #%d has set a value to path: '%s'\n", conn.id, msg.Path)
		go hub.handleSet(&msg, conn)
	case MSG_CMD_UPDATE:
		log.Printf("Connection #%d has updated path: '%s'\n", conn.id, msg.Path)
		go hub.handleUpdate(&msg, conn)
	case MSG_CMD_REMOVE:
		log.Printf("Connection #%d has removed path: '%s'\n", conn.id, msg.Path)
		go hub.handleRemove(&msg, conn)
	case MSG_CMD_TRANS_SET:
		log.Printf("Connection #%d has done trans-set on path: '%s'\n", conn.id, msg.Path)
		go hub.handleTransSet(&msg, conn)
	case MSG_CMD_TRANS_GET:
		log.Printf("Connection #%d has done trans-get on path: '%s'\n", conn.id, msg.Path)
		go hub.handleTransGet(&msg, conn)
	default:
		log.Fatalf("Connection #%d submitted a message with cmd #%d which is unsupported\n", conn.id, msg.Cmd)
	}
}

func (hub *MsgHub) handleSet(msg *Msg, conn *Conn) {
	var unmarshalledValue interface{}
	jsonErr := json.Unmarshal(msg.Value, &unmarshalledValue)
	if jsonErr != nil {
		errStr := jsonErr.Error()
		hub.sendAck(conn, msg.Ack, &errStr, nil, "")
	} else {
		log.Println("Now setting value to path ", msg.Path)
		err := database.set(msg.Path, unmarshalledValue)
		if err != nil {
			errStr := err.Error()
			hub.sendAck(conn, msg.Ack, &errStr, nil, "")
		} else {
			hub.sendAck(conn, msg.Ack, nil, nil, "")
			hub.publishValueEvent(msg.Path, &msg.Value, conn)
		}
	}
}

// TODO add "remove with null" support
func (hub *MsgHub) handleUpdate(msg *Msg, conn *Conn) {
	if msg.Value == nil {
		return
	}
	propertyMap := make(map[string]json.RawMessage)
	json.Unmarshal(msg.Value, &propertyMap)
	responses := make(chan *error)
	for property, value := range propertyMap {
		go (func(path string, val json.RawMessage) {
			var unmarshalledValue interface{}

			newPath := hub.joinPaths(msg.Path, path)
			jsonErr := json.Unmarshal(val, &unmarshalledValue)
			if jsonErr != nil {
				responses <- &jsonErr
				return // ಠ_ಠ
			} else {
				err := database.set(path, unmarshalledValue)
				if err != nil {
					responses <- &err
					return
				}
			}
			responses <- nil
			hub.publishValueEvent(newPath, &val, conn)
		})(property, value)
	}
	// Collect the callbacks
	i := 1
	problems := ""
	for {
		select {
		case err := <-responses:
			if err != nil {
				problems += (*err).Error() + "\n"
			}

			if i == len(propertyMap) {
				// We're done - send the response
				if problems == "" {
					hub.sendAck(conn, msg.Ack, nil, nil, "")
				} else {
					hub.sendAck(conn, msg.Ack, &problems, nil, "")
				}
				close(responses)
				return
			} else {
				i = i + 1
			}
		}
	}
}

func (hub *MsgHub) handleRemove(msg *Msg, conn *Conn) {
	// Remove children first
	node := hub.bus.pathTree.get(msg.Path)
	if node == nil {
		err := "Path does not exist"
		hub.sendAck(conn, msg.Ack, &err, nil, "")
		return
	}
	// Depth first traversal of path
	node.cascade(func(subNode *PathTreeNode) {
		// Kill node and report to listeners
		subNode.destroy()
		hub.publishValueEvent(subNode.path, nil, conn)
	})
	hub.sendAck(conn, msg.Ack, nil, nil, "")
}

func (hub *MsgHub) handleTransSet(msg *Msg, conn *Conn) {
	// db get
	err, val := database.get(msg.Path)
	if err != nil {
		errStr := err.Error()
		hub.sendAck(conn, msg.Ack, &errStr, nil, "")
	}

	if val != nil {
		// grab le hash
		err, currValHash := hash(val)
		if err != nil {
			errStr := err.Error()
			hub.sendAck(conn, msg.Ack, &errStr, nil, "")
		}
		// compare le hashes
		if msg.Hash == string(currValHash[:]) {
			hub.handleSet(msg, conn) // actually
			// this should work dafaq
		} else {
			errStr := "conflict"
			hub.sendAck(conn, msg.Ack, &errStr, nil, "")
		}
	} else {
		// hashing dont make sense if val is nil
		// this is fine for now
		// no - we send nil
		hub.handleSet(msg, conn)
	}
}

func (hub *MsgHub) handleTransGet(msg *Msg, conn *Conn) {
	// db get
	err, val := database.get(msg.Path)
	if err != nil {
		errStr := err.Error()
		hub.sendAck(conn, msg.Ack, &errStr, nil, "")
		return
	}
	if val == nil {
		hub.sendAck(conn, msg.Ack, nil, nil, "")
		return
	}
	// grab le hash
	log.Println("Now hashing val:", val)
	err, currValHash := hash(val)
	if err != nil {
		errStr := err.Error()
		hub.sendAck(conn, msg.Ack, &errStr, nil, "")
	}
	// send the value with the hash
	hub.sendAck(conn, msg.Ack, nil, val, string(currValHash[:]))
}

func (hub *MsgHub) publishValueEvent(path string, value *json.RawMessage, conn *Conn) {
	var hasValueSubs bool
	var hasChildChangedSubs bool
	var parentPath string

	if hub.hasParent(path) {
		parentPath = hub.parentOf(path)
	}
	// Check if anyone is event subscribed to this
	hasValueSubs = hub.bus.hasSubscribers(EVENT_TYPE_VALUE, path)
	if parentPath != "" {
		hasChildChangedSubs = hub.bus.hasSubscribers(EVENT_TYPE_CHILD_CHANGED, path)
	}
	// Do not continue if there are no subscribers
	if !hasValueSubs && !hasChildChangedSubs {
		return
	}
	// This is what we are sending subscribers to a value related event
	evt := ValueEvent{
		Path:  path,
		Value: value,
	}
	// Send the event to value listeners
	if hasValueSubs {
		// Set the event type; jsonify
		evt.Event = EVENT_TYPE_VALUE
		evtJson, err := json.Marshal(evt)
		if err != nil {
			problem := "Couldn't marshal msg value json\n"
			log.Fatalln(problem, err)
			return
		}
		hub.bus.publish(EVENT_TYPE_VALUE, path, evtJson)
	}
	// Send the event to child changed listeners
	if hasChildChangedSubs && hub.hasParent(path) {
		// Set the event type, parent path; jsonify
		if value == nil {
			evt.Event = EVENT_TYPE_CHILD_REMOVED
		} else {
			evt.Event = EVENT_TYPE_CHILD_CHANGED
		}
		evt.Path = hub.parentOf(path)
		evtJson, err := json.Marshal(evt)
		if err != nil {
			problem := "Couldn't marshal msg value json\n"
			log.Fatalln(problem, err)
			return
		}
		hub.bus.publish(EVENT_TYPE_CHILD_CHANGED, path, evtJson)
	}
}

func (hub *MsgHub) hasParent(path string) bool {
	return path != "/"
}

func (hub *MsgHub) parentOf(path string) string {
	lastIndex := strings.LastIndex(path, "/")
	return path[0:lastIndex]
}

func (hub *MsgHub) joinPaths(base string, extension string) string {
	if !strings.HasSuffix(base, "/") {
		base = base + "/"
	}
	if strings.HasPrefix(extension, "/") {
		if len(extension) > 1 {
			extension = extension[1:]
		} else {
			extension = ""
		}
	}
	return base + extension
}

func (hub *MsgHub) sendAck(conn *Conn, ack int, errString *string, result interface{}, hash string) {
	response := Ack{
		Type:   MSG_CMD_ACK,
		Ack:    ack,
		Result: result,
	}
	if hash != "" {
		// Strings cant be nil in go
		// YES. I KNOW.
		response.Hash = hash
	}
	if errString != nil {
		log.Println("Sending problem back to client in ack form:", *errString)
		response.Error = *errString
	}
	payload, err := json.Marshal(response)
	if err == nil {
		select {
		case conn.outbox <- payload:
		default:
			hub.unregisterConn(conn)
		}
	}
}
<|endoftext|>


<commit_before>// Package muster provides a framework for writing libraries that internally
// batch operations.
//
// It will be useful to you if you're building an API that benefits from
// performing work in batches for whatever reason. Batching is triggered based
// on a maximum number of items in a batch, and/or based on a timeout for how
// long a batch waits before it is dispatched. For example if you're willing to
// wait for a maximum of X duration, you can just set BatchTimeout and keep
// adding things. Or if you don't care about how long you wait, just set
// MaxBatchSize and it will only fire when the batch is filled. For best
// results set both.
//
// This library provides a component that is intended to be used in a hidden
// fashion. This is in your best interest in order to avoid unnecessary
// coupling. You will typically achieve this by ensuring your implementation of
// muster.Batch and the use of muster.Client are private.
package muster

import (
	"errors"
	"sync"
	"time"
)

var errZeroBoth = errors.New(
	"muster: MaxBatchSize and BatchTimeout can't both be zero",
)

// The notifier is used to indicate to the Client when a batch has finished
// processing.
type Notifier interface {
	// Calling Done will indicate the batch has finished processing.
	Done()
}

// Represents a single batch where items will be added and Fire will be called
// exactly once. The Batch does not need to be safe for concurrent access;
// synchronization will be handled by the Client.
type Batch interface {
	// This should add the given single item to the Batch. This is the "other
	// end" of the Client.Work channel where your application will send items.
	Add(item interface{})

	// Fire off the Batch. It should call Notifier.Done() when it has finished
	// processing the Batch.
	Fire(notifier Notifier)
}

// The Client manages the background process that makes, populates & fires
// Batches.
type Client struct {
	// Capacity of work channel. If this is zero, the Work channel will be
	// blocking.
	PendingWorkCapacity int

	// Maximum number of items in a batch. If this is zero batches will only be
	// dispatched upon hitting the BatchTimeout. It is an error for both this and
	// the BatchTimeout to be zero.
	MaxBatchSize int

	// Duration after which to send a pending batch. If this is zero batches will
	// only be dispatched upon hitting the MaxBatchSize. It is an error for both
	// this and the MaxBatchSize to be zero.
	BatchTimeout time.Duration

	// This function should create a new empty Batch on each invocation.
	BatchMaker func() Batch

	// Once this Client has been started, send work items here to add to batch.
	Work chan interface{}

	workGroup sync.WaitGroup
}

// Start the background worker goroutines and get ready for accepting requests.
func (c *Client) Start() error {
	if int64(c.BatchTimeout) == 0 && c.MaxBatchSize == 0 {
		return errZeroBoth
	}

	c.Work = make(chan interface{}, c.PendingWorkCapacity)
	c.workGroup.Add(1) // this is the worker itself
	go c.worker()
	return nil
}

// Gracefully stop and return once all processing has finished.
func (c *Client) Stop() error {
	close(c.Work)
	c.workGroup.Wait()
	return nil
}

// Background process.
func (c *Client) worker() {
	defer c.workGroup.Done()
	var batch = c.BatchMaker()
	var count int
	var batchTimeout <-chan time.Time
	send := func() {
		c.workGroup.Add(1)
		go batch.Fire(&c.workGroup)
		batch = c.BatchMaker()
		count = 0
		batchTimeout = nil
	}
	recv := func(item interface{}, open bool) bool {
		if !open {
			if count != 0 {
				send()
			}
			return true
		}
		batch.Add(item)
		count++
		if c.MaxBatchSize != 0 && count >= c.MaxBatchSize {
			send()
		} else if int64(c.BatchTimeout) != 0 && batchTimeout == nil {
			batchTimeout = time.After(c.BatchTimeout)
		}
		return false
	}
	for {
		// We use two selects in order to first prefer draining the work queue.
		select {
		case item, open := <-c.Work:
			if recv(item, open) {
				return
			}
		default:
			select {
			case item, open := <-c.Work:
				if recv(item, open) {
					return
				}
			case <-batchTimeout:
				send()
			}
		}
	}
}
<commit_msg>specific values in package doc<commit_after>// Package muster provides a framework for writing libraries that internally
// batch operations.
//
// It will be useful to you if you're building an API that benefits from
// performing work in batches for whatever reason. Batching is triggered based
// on a maximum number of items in a batch, and/or based on a timeout for how
// long a batch waits before it is dispatched. For example if you're willing to
// wait for a maximum of a 1m duration, you can just set BatchTimeout and keep
// adding things. Or if you want batches of 50 just set MaxBatchSize and it
// will only fire when the batch is filled. For best results set both.
//
// This library provides a component that is intended to be used in a hidden
// fashion. This is in your best interest in order to avoid unnecessary
// coupling. You will typically achieve this by ensuring your implementation of
// muster.Batch and the use of muster.Client are private.
package muster

import (
	"errors"
	"sync"
	"time"
)

var errZeroBoth = errors.New(
	"muster: MaxBatchSize and BatchTimeout can't both be zero",
)

// The notifier is used to indicate to the Client when a batch has finished
// processing.
type Notifier interface {
	// Calling Done will indicate the batch has finished processing.
	Done()
}

// Represents a single batch where items will be added and Fire will be called
// exactly once. The Batch does not need to be safe for concurrent access;
// synchronization will be handled by the Client.
type Batch interface {
	// This should add the given single item to the Batch. This is the "other
	// end" of the Client.Work channel where your application will send items.
	Add(item interface{})

	// Fire off the Batch. It should call Notifier.Done() when it has finished
	// processing the Batch.
	Fire(notifier Notifier)
}

// The Client manages the background process that makes, populates & fires
// Batches.
type Client struct {
	// Capacity of work channel. If this is zero, the Work channel will be
	// blocking.
	PendingWorkCapacity int

	// Maximum number of items in a batch. If this is zero batches will only be
	// dispatched upon hitting the BatchTimeout. It is an error for both this and
	// the BatchTimeout to be zero.
	MaxBatchSize int

	// Duration after which to send a pending batch. If this is zero batches will
	// only be dispatched upon hitting the MaxBatchSize. It is an error for both
	// this and the MaxBatchSize to be zero.
	BatchTimeout time.Duration

	// This function should create a new empty Batch on each invocation.
	BatchMaker func() Batch

	// Once this Client has been started, send work items here to add to batch.
	Work chan interface{}

	workGroup sync.WaitGroup
}

// Start the background worker goroutines and get ready for accepting requests.
func (c *Client) Start() error {
	if int64(c.BatchTimeout) == 0 && c.MaxBatchSize == 0 {
		return errZeroBoth
	}

	c.Work = make(chan interface{}, c.PendingWorkCapacity)
	c.workGroup.Add(1) // this is the worker itself
	go c.worker()
	return nil
}

// Gracefully stop and return once all processing has finished.
func (c *Client) Stop() error {
	close(c.Work)
	c.workGroup.Wait()
	return nil
}

// Background process.
func (c *Client) worker() {
	defer c.workGroup.Done()
	var batch = c.BatchMaker()
	var count int
	var batchTimeout <-chan time.Time
	send := func() {
		c.workGroup.Add(1)
		go batch.Fire(&c.workGroup)
		batch = c.BatchMaker()
		count = 0
		batchTimeout = nil
	}
	recv := func(item interface{}, open bool) bool {
		if !open {
			if count != 0 {
				send()
			}
			return true
		}
		batch.Add(item)
		count++
		if c.MaxBatchSize != 0 && count >= c.MaxBatchSize {
			send()
		} else if int64(c.BatchTimeout) != 0 && batchTimeout == nil {
			batchTimeout = time.After(c.BatchTimeout)
		}
		return false
	}
	for {
		// We use two selects in order to first prefer draining the work queue.
		select {
		case item, open := <-c.Work:
			if recv(item, open) {
				return
			}
		default:
			select {
			case item, open := <-c.Work:
				if recv(item, open) {
					return
				}
			case <-batchTimeout:
				send()
			}
		}
	}
}
<|endoftext|>


<commit_before>// Package muster provides a framework to write batching enabled libraries.
//
// It will be useful to you if you're building an API that benefits from
// performing work in batches for whatever reason. Batching is triggered based
// on a maximum number items in a batch, and/or based on a timeout for how long
// a batch waits before it is dispatched. For example if you're willing to wait
// for a maximum of X duration, you can just set BatchTimeout and keep adding
// things. Or if you don't care about how long you wait, just set MaxBatchSize
// and it will only fire when the batch is filled. For best results set both.
//
// This library provides a component that is intended to be used in a hidden
// fashion in other libraries. This is in your best interest to avoid
// unnecessary coupling. You will typically achieve this by ensuring your
// implementation of muster.Batch and the use of muster.Client are private.
package muster

import (
	"errors"
	"sync"
	"time"
)

var errZeroBoth = errors.New(
	"muster: MaxBatchSize and BatchTimeout can't both be zero",
)

// The notifier is used to indicate to the Client when a batch has finished
// processing.
type Notifier interface {
	// Calling Done will indicate the batch has finished processing.
	Done()
}

// Represents a single batch where items will be added and Fire will be called
// exactly once. The Batch does not need to be safe for concurrent access;
// synchronization will be handled by the Client.
type Batch interface {
	// This should add the given single item to the Batch. This is the "other
	// end" of the Client.Work channel where your application will send items.
	Add(item interface{})

	// Fire off the Batch. It should call Notifier.Done() when it has finished
	// processing the Batch.
	Fire(notifier Notifier)
}

// The Client manages the background process that makes, populates & fires
// Batches.
type Client struct {
	// Capacity of work channel. If this is zero, the Work channel will be
	// blocking.
	PendingWorkCapacity int

	// Maximum number of items in a batch. If this is zero batches will only be
	// dispatched upon hitting the BatchTimeout. It is an error for both this and
	// the BatchTimeout to be zero.
	MaxBatchSize int

	// Duration after which to send a pending batch. If this is zero batches will
	// only be dispatched upon hitting the MaxBatchSize. It is an error for both
	// this and the MaxBatchSize to be zero.
	BatchTimeout time.Duration

	// This function should create a new empty Batch on each invocation.
	BatchMaker func() Batch

	// Once this Client has been started, send work items here to add to batch.
	Work chan interface{}

	workGroup sync.WaitGroup
}

// Start the background worker goroutines and get ready for accepting requests.
func (c *Client) Start() error {
	if int64(c.BatchTimeout) == 0 && c.MaxBatchSize == 0 {
		return errZeroBoth
	}

	c.Work = make(chan interface{}, c.PendingWorkCapacity)
	c.workGroup.Add(1) // this is the worker itself
	go c.worker()
	return nil
}

// Gracefully stop and return once all processing has finished.
func (c *Client) Stop() error {
	close(c.Work)
	c.workGroup.Wait()
	return nil
}

// Background process.
func (c *Client) worker() {
	defer c.workGroup.Done()
	var batch = c.BatchMaker()
	var count int
	var batchTimeout <-chan time.Time
	send := func() {
		c.workGroup.Add(1)
		go batch.Fire(&c.workGroup)
		batch = c.BatchMaker()
		count = 0
		batchTimeout = nil
	}
	recv := func(item interface{}, open bool) bool {
		if !open {
			if count != 0 {
				send()
			}
			return true
		}
		batch.Add(item)
		count++
		if c.MaxBatchSize != 0 && count >= c.MaxBatchSize {
			send()
		} else if int64(c.BatchTimeout) != 0 && batchTimeout == nil {
			batchTimeout = time.After(c.BatchTimeout)
		}
		return false
	}
	for {
		// We use two selects in order to first prefer draining the work queue.
		select {
		case item, open := <-c.Work:
			if recv(item, open) {
				return
			}
		default:
			select {
			case item, open := <-c.Work:
				if recv(item, open) {
					return
				}
			case <-batchTimeout:
				send()
			}
		}
	}
}
<commit_msg>doc nits<commit_after>// Package muster provides a framework for writing libraries that internally
// batch operations.
//
// It will be useful to you if you're building an API that benefits from
// performing work in batches for whatever reason. Batching is triggered based
// on a maximum number items in a batch, and/or based on a timeout for how long
// a batch waits before it is dispatched. For example if you're willing to wait
// for a maximum of X duration, you can just set BatchTimeout and keep adding
// things. Or if you don't care about how long you wait, just set MaxBatchSize
// and it will only fire when the batch is filled. For best results set both.
//
// This library provides a component that is intended to be used in a hidden
// fashion. This is in your best interest in order to avoid unnecessary
// coupling. You will typically achieve this by ensuring your implementation of
// muster.Batch and the use of muster.Client are private.
package muster

import (
	"errors"
	"sync"
	"time"
)

var errZeroBoth = errors.New(
	"muster: MaxBatchSize and BatchTimeout can't both be zero",
)

// The notifier is used to indicate to the Client when a batch has finished
// processing.
type Notifier interface {
	// Calling Done will indicate the batch has finished processing.
	Done()
}

// Represents a single batch where items will be added and Fire will be called
// exactly once. The Batch does not need to be safe for concurrent access;
// synchronization will be handled by the Client.
type Batch interface {
	// This should add the given single item to the Batch. This is the "other
	// end" of the Client.Work channel where your application will send items.
	Add(item interface{})

	// Fire off the Batch. It should call Notifier.Done() when it has finished
	// processing the Batch.
	Fire(notifier Notifier)
}

// The Client manages the background process that makes, populates & fires
// Batches.
type Client struct {
	// Capacity of work channel. If this is zero, the Work channel will be
	// blocking.
	PendingWorkCapacity int

	// Maximum number of items in a batch. If this is zero batches will only be
	// dispatched upon hitting the BatchTimeout. It is an error for both this and
	// the BatchTimeout to be zero.
	MaxBatchSize int

	// Duration after which to send a pending batch. If this is zero batches will
	// only be dispatched upon hitting the MaxBatchSize. It is an error for both
	// this and the MaxBatchSize to be zero.
	BatchTimeout time.Duration

	// This function should create a new empty Batch on each invocation.
	BatchMaker func() Batch

	// Once this Client has been started, send work items here to add to batch.
	Work chan interface{}

	workGroup sync.WaitGroup
}

// Start the background worker goroutines and get ready for accepting requests.
func (c *Client) Start() error {
	if int64(c.BatchTimeout) == 0 && c.MaxBatchSize == 0 {
		return errZeroBoth
	}

	c.Work = make(chan interface{}, c.PendingWorkCapacity)
	c.workGroup.Add(1) // this is the worker itself
	go c.worker()
	return nil
}

// Gracefully stop and return once all processing has finished.
func (c *Client) Stop() error {
	close(c.Work)
	c.workGroup.Wait()
	return nil
}

// Background process.
func (c *Client) worker() {
	defer c.workGroup.Done()
	var batch = c.BatchMaker()
	var count int
	var batchTimeout <-chan time.Time
	send := func() {
		c.workGroup.Add(1)
		go batch.Fire(&c.workGroup)
		batch = c.BatchMaker()
		count = 0
		batchTimeout = nil
	}
	recv := func(item interface{}, open bool) bool {
		if !open {
			if count != 0 {
				send()
			}
			return true
		}
		batch.Add(item)
		count++
		if c.MaxBatchSize != 0 && count >= c.MaxBatchSize {
			send()
		} else if int64(c.BatchTimeout) != 0 && batchTimeout == nil {
			batchTimeout = time.After(c.BatchTimeout)
		}
		return false
	}
	for {
		// We use two selects in order to first prefer draining the work queue.
		select {
		case item, open := <-c.Work:
			if recv(item, open) {
				return
			}
		default:
			select {
			case item, open := <-c.Work:
				if recv(item, open) {
					return
				}
			case <-batchTimeout:
				send()
			}
		}
	}
}
<|endoftext|>