<commit_before>package router

import (
	"code.google.com/p/go-bit/bit"
	"code.google.com/p/go.crypto/nacl/box"
	"code.google.com/p/go.crypto/nacl/secretbox"
	"crypto/rand"
	"crypto/sha256"
	"encoding/binary"
	"encoding/gob"
	"fmt"
	"sync"
)

func GenerateKeyPair() (publicKey, privateKey *[32]byte, err error) {
	return box.GenerateKey(rand.Reader)
}

func FormSessionKey(remotePublicKey, localPrivateKey *[32]byte, secretKey []byte) *[32]byte {
	var sharedKey [32]byte
	box.Precompute(&sharedKey, remotePublicKey, localPrivateKey)
	sharedKeySlice := sharedKey[:]
	sharedKeySlice = append(sharedKeySlice, secretKey...)
	sessionKey := sha256.Sum256(sharedKeySlice)
	return &sessionKey
}

// Frame Encryptors

type Encryptor interface {
	FrameOverhead() int
	PacketOverhead() int
	IsEmpty() bool
	Bytes() ([]byte, error)
	AppendFrame(src []byte, dst []byte, frame []byte)
	TotalLen() int
}

type NonEncryptor struct {
	buf       []byte
	bufTail   []byte
	buffered  int
	prefixLen int
}

type NaClEncryptor struct {
	NonEncryptor
	buf        []byte
	prefixLen  int
	sessionKey *[32]byte
	nonce      [24]byte
	seqNo      uint64
	df         bool
}

func NewNonEncryptor(prefix []byte) *NonEncryptor {
	buf := make([]byte, MaxUDPPacketSize)
	prefixLen := copy(buf, prefix)
	return &NonEncryptor{
		buf:       buf,
		bufTail:   buf[prefixLen:],
		buffered:  prefixLen,
		prefixLen: prefixLen}
}

func (ne *NonEncryptor) PacketOverhead() int {
	return ne.prefixLen
}

func (ne *NonEncryptor) FrameOverhead() int {
	return NameSize + NameSize + 2
}

func (ne *NonEncryptor) IsEmpty() bool {
	return ne.buffered == ne.prefixLen
}

func (ne *NonEncryptor) Bytes() ([]byte, error) {
	buf := ne.buf[:ne.buffered]
	ne.buffered = ne.prefixLen
	ne.bufTail = ne.buf[ne.prefixLen:]
	return buf, nil
}

func (ne *NonEncryptor) AppendFrame(src []byte, dst []byte, frame []byte) {
	bufTail := ne.bufTail
	srcLen := copy(bufTail, src)
	bufTail = bufTail[srcLen:]
	dstLen := copy(bufTail, dst)
	bufTail = bufTail[dstLen:]
	binary.BigEndian.PutUint16(bufTail, uint16(len(frame)))
	bufTail = bufTail[2:]
	copy(bufTail, frame)
	ne.bufTail = bufTail[len(frame):]
	ne.buffered += srcLen + dstLen + 2 + len(frame)
}

func (ne *NonEncryptor) TotalLen() int {
	return ne.buffered
}

func NewNaClEncryptor(prefix []byte, sessionKey *[32]byte, outbound bool, df bool) *NaClEncryptor {
	buf := make([]byte, MaxUDPPacketSize)
	prefixLen := copy(buf, prefix)
	ne := &NaClEncryptor{
		NonEncryptor: *NewNonEncryptor([]byte{}),
		buf:          buf,
		prefixLen:    prefixLen,
		sessionKey:   sessionKey,
		df:           df}
	if outbound {
		ne.nonce[0] |= (1 << 7)
	}
	return ne
}

func (ne *NaClEncryptor) Bytes() ([]byte, error) {
	plaintext, err := ne.NonEncryptor.Bytes()
	if err != nil {
		return nil, err
	}
	// We carry the DF flag in the (unencrypted portion of the)
	// payload, rather than just extracting it from the packet headers
	// at the receiving end, since we do not trust routers not to mess
	// with headers. As we have different decryptors for non-DF and
	// DF, that would result in hard to track down packet drops due to
	// crypto errors.
	seqNoAndDF := ne.seqNo
	if ne.df {
		seqNoAndDF |= (1 << 63)
	}
	ciphertext := ne.buf
	binary.BigEndian.PutUint64(ciphertext[ne.prefixLen:], seqNoAndDF)
	binary.BigEndian.PutUint64(ne.nonce[16:24], seqNoAndDF)
	// Seal *appends* to ciphertext
	ciphertext = secretbox.Seal(ciphertext[:ne.prefixLen+8], plaintext, &ne.nonce, ne.sessionKey)
	ne.seqNo++
	return ciphertext, nil
}

func (ne *NaClEncryptor) PacketOverhead() int {
	return ne.prefixLen + 8 + secretbox.Overhead + ne.NonEncryptor.PacketOverhead()
}

func (ne *NaClEncryptor) TotalLen() int {
	return ne.PacketOverhead() + ne.NonEncryptor.TotalLen()
}

// Frame Decryptors

type FrameConsumer func(src []byte, dst []byte, frame []byte)

type Decryptor interface {
	IterateFrames([]byte, FrameConsumer) error
}

type NonDecryptor struct {
}

type NaClDecryptor struct {
	NonDecryptor
	sessionKey *[32]byte
	instance   *NaClDecryptorInstance
	instanceDF *NaClDecryptorInstance
}

type NaClDecryptorInstance struct {
	nonce               [24]byte
	currentWindow       uint64
	usedOffsets         *bit.Set
	previousUsedOffsets *bit.Set
}

func NewNaClDecryptorInstance(outbound bool) *NaClDecryptorInstance {
	di := &NaClDecryptorInstance{usedOffsets: bit.New()}
	if !outbound {
		di.nonce[0] |= (1 << 7)
	}
	return di
}

type PacketDecodingError struct {
	Desc string
}

func NewNonDecryptor() *NonDecryptor {
	return &NonDecryptor{}
}

func (nd *NonDecryptor) IterateFrames(packet []byte, consumer FrameConsumer) error {
	for len(packet) >= (2 + NameSize + NameSize) {
		srcNameByte := packet[:NameSize]
		packet = packet[NameSize:]
		dstNameByte := packet[:NameSize]
		packet = packet[NameSize:]
		length := binary.BigEndian.Uint16(packet[:2])
		packet = packet[2:]
		if len(packet) < int(length) {
			return PacketDecodingError{Desc: fmt.Sprintf("too short; expected frame of length %d, got %d", length, len(packet))}
		}
		frame := packet[:length]
		packet = packet[length:]
		consumer(srcNameByte, dstNameByte, frame)
	}
	if len(packet) > 0 {
		return PacketDecodingError{Desc: fmt.Sprintf("%d octets of trailing garbage", len(packet))}
	}
	return nil
}

func NewNaClDecryptor(sessionKey *[32]byte, outbound bool) *NaClDecryptor {
	return &NaClDecryptor{
		NonDecryptor: *NewNonDecryptor(),
		sessionKey:   sessionKey,
		instance:     NewNaClDecryptorInstance(outbound),
		instanceDF:   NewNaClDecryptorInstance(outbound)}
}

func (nd *NaClDecryptor) IterateFrames(packet []byte, consumer FrameConsumer) error {
	if len(packet) < 8 {
		return PacketDecodingError{Desc: fmt.Sprintf("encrypted UDP packet too short; expected length >= 8, got %d", len(packet))}
	}
	buf, success := nd.decrypt(packet)
	if !success {
		return PacketDecodingError{Desc: fmt.Sprint("UDP packet decryption failed")}
	}
	return nd.NonDecryptor.IterateFrames(buf, consumer)
}

func (nd *NaClDecryptor) decrypt(buf []byte) ([]byte, bool) {
	seqNoAndDF := binary.BigEndian.Uint64(buf[:8])
	df := (seqNoAndDF & (1 << 63)) != 0
	seqNo := seqNoAndDF & ((1 << 63) - 1)
	var di *NaClDecryptorInstance
	if df {
		di = nd.instanceDF
	} else {
		di = nd.instance
	}
	binary.BigEndian.PutUint64(di.nonce[16:24], seqNoAndDF)
	result, success := secretbox.Open(nil, buf[8:], &di.nonce, nd.sessionKey)
	if !success {
		return nil, false
	}
	// Drop duplicates. We do this *after* decryption since we must
	// not advance our state unless decryption succeeded. Doing so
	// would open an easy attack vector where an adversary could
	// inject a packet with a sequence number of (1 << 63) - 1,
	// causing all subsequent genuine packets to get dropped.
	offset, usedOffsets := di.advanceState(seqNo)
	if usedOffsets == nil || usedOffsets.Contains(offset) {
		// We have detected a possible replay attack, but it is
		// possible we may have just received a very old packet, or
		// duplication may have occurred in the network. So let's just
		// drop the packet silently.
		return nil, true
	}
	usedOffsets.Add(offset)
	return result, success
}

// We record seen message sequence numbers in a sliding window of
// 2*WindowSize which slides in WindowSize increments. This allows us
// to process out-of-order delivery within the window, while
// accurately discarding duplicates. By contrast any messages with
// sequence numbers below the window are discarded as potential
// duplicates.
//
// There are two sets, corresponding to the lower and upper half of
// the window. We slide the window s.t. that 2nd set always contains
// the highest seen sequence number. We do this regardless of how far
// ahead of the current window that sequence number might be, so we
// can cope with large gaps resulting from packet loss.

const (
	WindowSize = 20 // bits
)

func (di *NaClDecryptorInstance) advanceState(seqNo uint64) (int, *bit.Set) {
	var (
		offset = int(seqNo & ((1 << WindowSize) - 1))
		window = seqNo >> WindowSize
	)
	switch delta := int64(window - di.currentWindow); {
	case delta < -1:
		return offset, nil
	case delta == -1:
		return offset, di.previousUsedOffsets
	default:
		return offset, di.usedOffsets
	case delta == +1:
		di.currentWindow = window
		di.previousUsedOffsets = di.usedOffsets
		di.usedOffsets = bit.New()
		return offset, di.usedOffsets
	case delta > +1:
		di.currentWindow = window
		di.previousUsedOffsets = bit.New()
		di.usedOffsets = bit.New()
		return offset, di.usedOffsets
	}
}

// TCP Senders/Receivers

// The lowest 64 bits of the nonce contain the message sequence
// number. The top most bit indicates the connection polarity at the
// sender - '1' for outbound. The remaining 127 bits are zero. The
// polarity is needed so that the two ends of a connection do not use
// the same nonces. This is a requirement of the NaCl Security Model;
// see http://nacl.cr.yp.to/box.html.
type TCPCryptoState struct {
	sessionKey *[32]byte
	nonce      [24]byte
	seqNo      uint64
}

func NewTCPCryptoState(sessionKey *[32]byte, outbound bool) *TCPCryptoState {
	s := &TCPCryptoState{sessionKey: sessionKey}
	if outbound {
		s.nonce[0] |= (1 << 7)
	}
	return s
}

func (s *TCPCryptoState) advance() {
	s.seqNo++
	binary.BigEndian.PutUint64(s.nonce[16:24], s.seqNo)
}

type TCPSender interface {
	Send([]byte) error
}

type SimpleTCPSender struct {
	encoder *gob.Encoder
}

type EncryptedTCPSender struct {
	SimpleTCPSender
	sync.RWMutex
	state *TCPCryptoState
}

func NewSimpleTCPSender(encoder *gob.Encoder) *SimpleTCPSender {
	return &SimpleTCPSender{encoder: encoder}
}

func (sender *SimpleTCPSender) Send(msg []byte) error {
	return sender.encoder.Encode(msg)
}

func NewEncryptedTCPSender(encoder *gob.Encoder, sessionKey *[32]byte, outbound bool) *EncryptedTCPSender {
	return &EncryptedTCPSender{
		SimpleTCPSender: *NewSimpleTCPSender(encoder),
		state:           NewTCPCryptoState(sessionKey, outbound)}
}

func (sender *EncryptedTCPSender) Send(msg []byte) error {
	sender.Lock()
	defer sender.Unlock()
	encodedMsg := secretbox.Seal(nil, msg, &sender.state.nonce, sender.state.sessionKey)
	sender.state.advance()
	return sender.SimpleTCPSender.Send(encodedMsg)
}

type TCPReceiver interface {
	Decode([]byte) ([]byte, error)
}

type SimpleTCPReceiver struct {
}

type EncryptedTCPReceiver struct {
	SimpleTCPReceiver
	state *TCPCryptoState
}

func NewSimpleTCPReceiver() *SimpleTCPReceiver {
	return &SimpleTCPReceiver{}
}

func (receiver *SimpleTCPReceiver) Decode(msg []byte) ([]byte, error) {
	return msg, nil
}

func NewEncryptedTCPReceiver(sessionKey *[32]byte, outbound bool) *EncryptedTCPReceiver {
	return &EncryptedTCPReceiver{
		SimpleTCPReceiver: *NewSimpleTCPReceiver(),
		state:             NewTCPCryptoState(sessionKey, !outbound)}
}

func (receiver *EncryptedTCPReceiver) Decode(msg []byte) ([]byte, error) {
	decodedMsg, success := secretbox.Open(nil, msg, &receiver.state.nonce, receiver.state.sessionKey)
	if !success {
		return nil, fmt.Errorf("Unable to decrypt TCP msg")
	}
	receiver.state.advance()
	return decodedMsg, nil
}
<commit_msg>Set high bit in TCP nonce to differentiate from UDP<commit_after>package router

import (
	"code.google.com/p/go-bit/bit"
	"code.google.com/p/go.crypto/nacl/box"
	"code.google.com/p/go.crypto/nacl/secretbox"
	"crypto/rand"
	"crypto/sha256"
	"encoding/binary"
	"encoding/gob"
	"fmt"
	"sync"
)

func GenerateKeyPair() (publicKey, privateKey *[32]byte, err error) {
	return box.GenerateKey(rand.Reader)
}

func FormSessionKey(remotePublicKey, localPrivateKey *[32]byte, secretKey []byte) *[32]byte {
	var sharedKey [32]byte
	box.Precompute(&sharedKey, remotePublicKey, localPrivateKey)
	sharedKeySlice := sharedKey[:]
	sharedKeySlice = append(sharedKeySlice, secretKey...)
	sessionKey := sha256.Sum256(sharedKeySlice)
	return &sessionKey
}

// Frame Encryptors

type Encryptor interface {
	FrameOverhead() int
	PacketOverhead() int
	IsEmpty() bool
	Bytes() ([]byte, error)
	AppendFrame(src []byte, dst []byte, frame []byte)
	TotalLen() int
}

type NonEncryptor struct {
	buf       []byte
	bufTail   []byte
	buffered  int
	prefixLen int
}

type NaClEncryptor struct {
	NonEncryptor
	buf        []byte
	prefixLen  int
	sessionKey *[32]byte
	nonce      [24]byte
	seqNo      uint64
	df         bool
}

func NewNonEncryptor(prefix []byte) *NonEncryptor {
	buf := make([]byte, MaxUDPPacketSize)
	prefixLen := copy(buf, prefix)
	return &NonEncryptor{
		buf:       buf,
		bufTail:   buf[prefixLen:],
		buffered:  prefixLen,
		prefixLen: prefixLen}
}

func (ne *NonEncryptor) PacketOverhead() int {
	return ne.prefixLen
}

func (ne *NonEncryptor) FrameOverhead() int {
	return NameSize + NameSize + 2
}

func (ne *NonEncryptor) IsEmpty() bool {
	return ne.buffered == ne.prefixLen
}

func (ne *NonEncryptor) Bytes() ([]byte, error) {
	buf := ne.buf[:ne.buffered]
	ne.buffered = ne.prefixLen
	ne.bufTail = ne.buf[ne.prefixLen:]
	return buf, nil
}

func (ne *NonEncryptor) AppendFrame(src []byte, dst []byte, frame []byte) {
	bufTail := ne.bufTail
	srcLen := copy(bufTail, src)
	bufTail = bufTail[srcLen:]
	dstLen := copy(bufTail, dst)
	bufTail = bufTail[dstLen:]
	binary.BigEndian.PutUint16(bufTail, uint16(len(frame)))
	bufTail = bufTail[2:]
	copy(bufTail, frame)
	ne.bufTail = bufTail[len(frame):]
	ne.buffered += srcLen + dstLen + 2 + len(frame)
}

func (ne *NonEncryptor) TotalLen() int {
	return ne.buffered
}

func NewNaClEncryptor(prefix []byte, sessionKey *[32]byte, outbound bool, df bool) *NaClEncryptor {
	buf := make([]byte, MaxUDPPacketSize)
	prefixLen := copy(buf, prefix)
	ne := &NaClEncryptor{
		NonEncryptor: *NewNonEncryptor([]byte{}),
		buf:          buf,
		prefixLen:    prefixLen,
		sessionKey:   sessionKey,
		df:           df}
	if outbound {
		ne.nonce[0] |= (1 << 7)
	}
	return ne
}

func (ne *NaClEncryptor) Bytes() ([]byte, error) {
	plaintext, err := ne.NonEncryptor.Bytes()
	if err != nil {
		return nil, err
	}
	// We carry the DF flag in the (unencrypted portion of the)
	// payload, rather than just extracting it from the packet headers
	// at the receiving end, since we do not trust routers not to mess
	// with headers. As we have different decryptors for non-DF and
	// DF, that would result in hard to track down packet drops due to
	// crypto errors.
	seqNoAndDF := ne.seqNo
	if ne.df {
		seqNoAndDF |= (1 << 63)
	}
	ciphertext := ne.buf
	binary.BigEndian.PutUint64(ciphertext[ne.prefixLen:], seqNoAndDF)
	binary.BigEndian.PutUint64(ne.nonce[16:24], seqNoAndDF)
	// Seal *appends* to ciphertext
	ciphertext = secretbox.Seal(ciphertext[:ne.prefixLen+8], plaintext, &ne.nonce, ne.sessionKey)
	ne.seqNo++
	return ciphertext, nil
}

func (ne *NaClEncryptor) PacketOverhead() int {
	return ne.prefixLen + 8 + secretbox.Overhead + ne.NonEncryptor.PacketOverhead()
}

func (ne *NaClEncryptor) TotalLen() int {
	return ne.PacketOverhead() + ne.NonEncryptor.TotalLen()
}

// Frame Decryptors

type FrameConsumer func(src []byte, dst []byte, frame []byte)

type Decryptor interface {
	IterateFrames([]byte, FrameConsumer) error
}

type NonDecryptor struct {
}

type NaClDecryptor struct {
	NonDecryptor
	sessionKey *[32]byte
	instance   *NaClDecryptorInstance
	instanceDF *NaClDecryptorInstance
}

type NaClDecryptorInstance struct {
	nonce               [24]byte
	currentWindow       uint64
	usedOffsets         *bit.Set
	previousUsedOffsets *bit.Set
}

func NewNaClDecryptorInstance(outbound bool) *NaClDecryptorInstance {
	di := &NaClDecryptorInstance{usedOffsets: bit.New()}
	if !outbound {
		di.nonce[0] |= (1 << 7)
	}
	return di
}

type PacketDecodingError struct {
	Desc string
}

func NewNonDecryptor() *NonDecryptor {
	return &NonDecryptor{}
}

func (nd *NonDecryptor) IterateFrames(packet []byte, consumer FrameConsumer) error {
	for len(packet) >= (2 + NameSize + NameSize) {
		srcNameByte := packet[:NameSize]
		packet = packet[NameSize:]
		dstNameByte := packet[:NameSize]
		packet = packet[NameSize:]
		length := binary.BigEndian.Uint16(packet[:2])
		packet = packet[2:]
		if len(packet) < int(length) {
			return PacketDecodingError{Desc: fmt.Sprintf("too short; expected frame of length %d, got %d", length, len(packet))}
		}
		frame := packet[:length]
		packet = packet[length:]
		consumer(srcNameByte, dstNameByte, frame)
	}
	if len(packet) > 0 {
		return PacketDecodingError{Desc: fmt.Sprintf("%d octets of trailing garbage", len(packet))}
	}
	return nil
}

func NewNaClDecryptor(sessionKey *[32]byte, outbound bool) *NaClDecryptor {
	return &NaClDecryptor{
		NonDecryptor: *NewNonDecryptor(),
		sessionKey:   sessionKey,
		instance:     NewNaClDecryptorInstance(outbound),
		instanceDF:   NewNaClDecryptorInstance(outbound)}
}

func (nd *NaClDecryptor) IterateFrames(packet []byte, consumer FrameConsumer) error {
	if len(packet) < 8 {
		return PacketDecodingError{Desc: fmt.Sprintf("encrypted UDP packet too short; expected length >= 8, got %d", len(packet))}
	}
	buf, success := nd.decrypt(packet)
	if !success {
		return PacketDecodingError{Desc: fmt.Sprint("UDP packet decryption failed")}
	}
	return nd.NonDecryptor.IterateFrames(buf, consumer)
}

func (nd *NaClDecryptor) decrypt(buf []byte) ([]byte, bool) {
	seqNoAndDF := binary.BigEndian.Uint64(buf[:8])
	df := (seqNoAndDF & (1 << 63)) != 0
	seqNo := seqNoAndDF & ((1 << 63) - 1)
	var di *NaClDecryptorInstance
	if df {
		di = nd.instanceDF
	} else {
		di = nd.instance
	}
	binary.BigEndian.PutUint64(di.nonce[16:24], seqNoAndDF)
	result, success := secretbox.Open(nil, buf[8:], &di.nonce, nd.sessionKey)
	if !success {
		return nil, false
	}
	// Drop duplicates. We do this *after* decryption since we must
	// not advance our state unless decryption succeeded. Doing so
	// would open an easy attack vector where an adversary could
	// inject a packet with a sequence number of (1 << 63) - 1,
	// causing all subsequent genuine packets to get dropped.
	offset, usedOffsets := di.advanceState(seqNo)
	if usedOffsets == nil || usedOffsets.Contains(offset) {
		// We have detected a possible replay attack, but it is
		// possible we may have just received a very old packet, or
		// duplication may have occurred in the network. So let's just
		// drop the packet silently.
		return nil, true
	}
	usedOffsets.Add(offset)
	return result, success
}

// We record seen message sequence numbers in a sliding window of
// 2*WindowSize which slides in WindowSize increments. This allows us
// to process out-of-order delivery within the window, while
// accurately discarding duplicates. By contrast any messages with
// sequence numbers below the window are discarded as potential
// duplicates.
//
// There are two sets, corresponding to the lower and upper half of
// the window. We slide the window s.t. that 2nd set always contains
// the highest seen sequence number. We do this regardless of how far
// ahead of the current window that sequence number might be, so we
// can cope with large gaps resulting from packet loss.

const (
	WindowSize = 20 // bits
)

func (di *NaClDecryptorInstance) advanceState(seqNo uint64) (int, *bit.Set) {
	var (
		offset = int(seqNo & ((1 << WindowSize) - 1))
		window = seqNo >> WindowSize
	)
	switch delta := int64(window - di.currentWindow); {
	case delta < -1:
		return offset, nil
	case delta == -1:
		return offset, di.previousUsedOffsets
	default:
		return offset, di.usedOffsets
	case delta == +1:
		di.currentWindow = window
		di.previousUsedOffsets = di.usedOffsets
		di.usedOffsets = bit.New()
		return offset, di.usedOffsets
	case delta > +1:
		di.currentWindow = window
		di.previousUsedOffsets = bit.New()
		di.usedOffsets = bit.New()
		return offset, di.usedOffsets
	}
}

// TCP Senders/Receivers

// The lowest 64 bits of the nonce contain the message sequence
// number. The top most bit indicates the connection polarity at the
// sender - '1' for outbound; the next indicates protocol type - '1'
// for TCP. The remaining 126 bits are zero. The polarity is needed so
// that the two ends of a connection do not use the same nonces; the
// protocol type so that the TCP and UDP sender nonces are disjoint.
// This is a requirement of the NaCl Security Model; see
// http://nacl.cr.yp.to/box.html.
type TCPCryptoState struct {
	sessionKey *[32]byte
	nonce      [24]byte
	seqNo      uint64
}

func NewTCPCryptoState(sessionKey *[32]byte, outbound bool) *TCPCryptoState {
	s := &TCPCryptoState{sessionKey: sessionKey}
	if outbound {
		s.nonce[0] |= (1 << 7)
	}
	// Ensure that TCP and UDP sequences are disjoint
	s.nonce[0] |= (1 << 6)
	return s
}

func (s *TCPCryptoState) advance() {
	s.seqNo++
	binary.BigEndian.PutUint64(s.nonce[16:24], s.seqNo)
}

type TCPSender interface {
	Send([]byte) error
}

type SimpleTCPSender struct {
	encoder *gob.Encoder
}

type EncryptedTCPSender struct {
	SimpleTCPSender
	sync.RWMutex
	state *TCPCryptoState
}

func NewSimpleTCPSender(encoder *gob.Encoder) *SimpleTCPSender {
	return &SimpleTCPSender{encoder: encoder}
}

func (sender *SimpleTCPSender) Send(msg []byte) error {
	return sender.encoder.Encode(msg)
}

func NewEncryptedTCPSender(encoder *gob.Encoder, sessionKey *[32]byte, outbound bool) *EncryptedTCPSender {
	return &EncryptedTCPSender{
		SimpleTCPSender: *NewSimpleTCPSender(encoder),
		state:           NewTCPCryptoState(sessionKey, outbound)}
}

func (sender *EncryptedTCPSender) Send(msg []byte) error {
	sender.Lock()
	defer sender.Unlock()
	encodedMsg := secretbox.Seal(nil, msg, &sender.state.nonce, sender.state.sessionKey)
	sender.state.advance()
	return sender.SimpleTCPSender.Send(encodedMsg)
}

type TCPReceiver interface {
	Decode([]byte) ([]byte, error)
}

type SimpleTCPReceiver struct {
}

type EncryptedTCPReceiver struct {
	SimpleTCPReceiver
	state *TCPCryptoState
}

func NewSimpleTCPReceiver() *SimpleTCPReceiver {
	return &SimpleTCPReceiver{}
}

func (receiver *SimpleTCPReceiver) Decode(msg []byte) ([]byte, error) {
	return msg, nil
}

func NewEncryptedTCPReceiver(sessionKey *[32]byte, outbound bool) *EncryptedTCPReceiver {
	return &EncryptedTCPReceiver{
		SimpleTCPReceiver: *NewSimpleTCPReceiver(),
		state:             NewTCPCryptoState(sessionKey, !outbound)}
}

func (receiver *EncryptedTCPReceiver) Decode(msg []byte) ([]byte, error) {
	decodedMsg, success := secretbox.Open(nil, msg, &receiver.state.nonce, receiver.state.sessionKey)
	if !success {
		return nil, fmt.Errorf("Unable to decrypt TCP msg")
	}
	receiver.state.advance()
	return decodedMsg, nil
}
<|endoftext|>


<commit_before>package Router

import (
	"errors"
	"github.com/ian-kent/go-log/log"
	"github.com/ian-kent/gotcha/config"
	"github.com/ian-kent/gotcha/events"
	"github.com/ian-kent/gotcha/http"
	"github.com/ian-kent/gotcha/mime"
	"github.com/ian-kent/gotcha/router/route"
	nethttp "net/http"
	"regexp"
	"time"
)

// http://stackoverflow.com/questions/6564558/wildcards-in-the-pattern-for-http-handlefunc

type HandlerFunc func(*http.Session)

func (f HandlerFunc) ServeHTTP(session *http.Session) {
	f(session)
}

type Router struct {
	Config *Config.Config
	Routes map[*route.Route]HandlerFunc
}

func Create(config *Config.Config) *Router {
	return &Router{
		Config: config,
		Routes: make(map[*route.Route]HandlerFunc),
	}
}

func (h *Router) Get(pattern string, handler HandlerFunc) {
	h.Handler([]string{"GET"}, pattern, handler)
}

func (h *Router) Post(pattern string, handler HandlerFunc) {
	h.Handler([]string{"POST"}, pattern, handler)
}

func (h *Router) Put(pattern string, handler HandlerFunc) {
	h.Handler([]string{"PUT"}, pattern, handler)
}

func (h *Router) Delete(pattern string, handler HandlerFunc) {
	h.Handler([]string{"DELETE"}, pattern, handler)
}

func (h *Router) Patch(pattern string, handler HandlerFunc) {
	h.Handler([]string{"PATCH"}, pattern, handler)
}

func (h *Router) Options(pattern string, handler HandlerFunc) {
	h.Handler([]string{"OPTIONS"}, pattern, handler)
}

func (h *Router) Static(filename string) HandlerFunc {
	return func(session *http.Session) {
		// TODO beware of ..?
		re := regexp.MustCompile("{{(\\w+)}}")
		fcopy := re.ReplaceAllStringFunc(filename, func(m string) string {
			parts := re.FindStringSubmatch(m)
			log.Trace("Found var: %s; name: %s", m, parts[1])
			if val, ok := session.Stash[parts[1]]; ok {
				log.Trace("Value found in stash for %s: %s", parts[1], val)
				return val.(string)
			}
			log.Trace("No value found in stash for var: %s", parts[1])
			return m
		})
		asset, err := h.Config.AssetLoader(fcopy)
		if err != nil {
			log.Debug("Static file not found: %s", fcopy)
			session.RenderNotFound()
		} else {
			m := MIME.TypeFromFilename(fcopy)
			if len(m) > 0 {
				log.Debug("Setting Content-Type: %s", m)
				session.Response.Headers.Add("Content-Type", m[0])
			}
			session.Response.Write(asset)
		}
	}
}

func PatternToRegex(pattern string) *regexp.Regexp {
	return regexp.MustCompile("^" + pattern + "$")
}

func (h *Router) Handler(methods []string, path string, handler HandlerFunc) {
	pattern := PatternToRegex(path)
	m := make(map[string]int, 0)
	for _, v := range methods {
		m[v] = 1
	}
	h.Routes[&route.Route{m, path, pattern}] = handler
}

func (h *Router) HandleFunc(methods []string, path string, handler func(*http.Session)) {
	pattern := PatternToRegex(path)
	m := make(map[string]int, 0)
	for _, v := range methods {
		m[v] = 1
	}
	h.Routes[&route.Route{m, path, pattern}] = HandlerFunc(handler)
}

func (h *Router) Serve(session *http.Session) {
	for route, handler := range h.Routes {
		if matches := route.Pattern.FindStringSubmatch(session.Request.URL.Path); len(matches) > 0 {
			_, ok := route.Methods[session.Request.Method]
			if ok {
				for i, named := range route.Pattern.SubexpNames() {
					if len(named) > 0 {
						log.Trace("Matched named pattern '%s': %s", named, matches[i])
						session.Stash[named] = matches[i]
					}
				}
				session.Route = route
				defer func() {
					if e := recover(); e != nil {
						switch e.(type) {
						case string:
							session.RenderException(500, errors.New(e.(string)))
						default:
							session.RenderException(500, e.(error))
						}
						session.Response.Send()
					}
				}()
				// FIXME func() is passed directly to all events,
				// so may get called multiple times :/
				h.Config.Events.Emit(session, events.BeforeHandler, func() {
					handler.ServeHTTP(session)
					h.Config.Events.Emit(session, events.AfterHandler, func() {
						session.Response.Send()
					})
				})
				return
			}
		}
	}
	// no pattern matched; send 404 response
	session.RenderNotFound()
	session.Response.Send()
}

func (h *Router) ServeHTTP(w nethttp.ResponseWriter, r *nethttp.Request) {
	session := http.CreateSession(h.Config, r, w)
	tStart := time.Now().UnixNano()

	h.Serve(session)

	t := float64(time.Now().UnixNano()-tStart) / 100000 // ms
	log.Printf("%s %s (%3.2fms) (%d)", r.Method, r.URL, t, session.Response.Status)
	h.Config.Events.Emit(session, events.AfterResponse, func() {})
}
<commit_msg>Add comment on event emit<commit_after>package Router

import (
	"errors"
	"github.com/ian-kent/go-log/log"
	"github.com/ian-kent/gotcha/config"
	"github.com/ian-kent/gotcha/events"
	"github.com/ian-kent/gotcha/http"
	"github.com/ian-kent/gotcha/mime"
	"github.com/ian-kent/gotcha/router/route"
	nethttp "net/http"
	"regexp"
	"time"
)

// http://stackoverflow.com/questions/6564558/wildcards-in-the-pattern-for-http-handlefunc

type HandlerFunc func(*http.Session)

func (f HandlerFunc) ServeHTTP(session *http.Session) {
	f(session)
}

type Router struct {
	Config *Config.Config
	Routes map[*route.Route]HandlerFunc
}

func Create(config *Config.Config) *Router {
	return &Router{
		Config: config,
		Routes: make(map[*route.Route]HandlerFunc),
	}
}

func (h *Router) Get(pattern string, handler HandlerFunc) {
	h.Handler([]string{"GET"}, pattern, handler)
}

func (h *Router) Post(pattern string, handler HandlerFunc) {
	h.Handler([]string{"POST"}, pattern, handler)
}

func (h *Router) Put(pattern string, handler HandlerFunc) {
	h.Handler([]string{"PUT"}, pattern, handler)
}

func (h *Router) Delete(pattern string, handler HandlerFunc) {
	h.Handler([]string{"DELETE"}, pattern, handler)
}

func (h *Router) Patch(pattern string, handler HandlerFunc) {
	h.Handler([]string{"PATCH"}, pattern, handler)
}

func (h *Router) Options(pattern string, handler HandlerFunc) {
	h.Handler([]string{"OPTIONS"}, pattern, handler)
}

func (h *Router) Static(filename string) HandlerFunc {
	return func(session *http.Session) {
		// TODO beware of ..?
		re := regexp.MustCompile("{{(\\w+)}}")
		fcopy := re.ReplaceAllStringFunc(filename, func(m string) string {
			parts := re.FindStringSubmatch(m)
			log.Trace("Found var: %s; name: %s", m, parts[1])
			if val, ok := session.Stash[parts[1]]; ok {
				log.Trace("Value found in stash for %s: %s", parts[1], val)
				return val.(string)
			}
			log.Trace("No value found in stash for var: %s", parts[1])
			return m
		})
		asset, err := h.Config.AssetLoader(fcopy)
		if err != nil {
			log.Debug("Static file not found: %s", fcopy)
			session.RenderNotFound()
		} else {
			m := MIME.TypeFromFilename(fcopy)
			if len(m) > 0 {
				log.Debug("Setting Content-Type: %s", m)
				session.Response.Headers.Add("Content-Type", m[0])
			}
			session.Response.Write(asset)
		}
	}
}

func PatternToRegex(pattern string) *regexp.Regexp {
	return regexp.MustCompile("^" + pattern + "$")
}

func (h *Router) Handler(methods []string, path string, handler HandlerFunc) {
	pattern := PatternToRegex(path)
	m := make(map[string]int, 0)
	for _, v := range methods {
		m[v] = 1
	}
	h.Routes[&route.Route{m, path, pattern}] = handler
}

func (h *Router) HandleFunc(methods []string, path string, handler func(*http.Session)) {
	pattern := PatternToRegex(path)
	m := make(map[string]int, 0)
	for _, v := range methods {
		m[v] = 1
	}
	h.Routes[&route.Route{m, path, pattern}] = HandlerFunc(handler)
}

func (h *Router) Serve(session *http.Session) {
	for route, handler := range h.Routes {
		if matches := route.Pattern.FindStringSubmatch(session.Request.URL.Path); len(matches) > 0 {
			_, ok := route.Methods[session.Request.Method]
			if ok {
				for i, named := range route.Pattern.SubexpNames() {
					if len(named) > 0 {
						log.Trace("Matched named pattern '%s': %s", named, matches[i])
						session.Stash[named] = matches[i]
					}
				}
				session.Route = route
				defer func() {
					if e := recover(); e != nil {
						switch e.(type) {
						case string:
							session.RenderException(500, errors.New(e.(string)))
						default:
							session.RenderException(500, e.(error))
						}
						session.Response.Send()
					}
				}()
				// func() will be executed only if *all* event handlers call next()
				emitted := h.Config.Events.Emit(session, events.BeforeHandler, func() {
					handler.ServeHTTP(session)
					h.Config.Events.Emit(session, events.AfterHandler, func() {
						session.Response.Send()
					})
				})
				return
			}
		}
	}
	// no pattern matched; send 404 response
	session.RenderNotFound()
	session.Response.Send()
}

func (h *Router) ServeHTTP(w nethttp.ResponseWriter, r *nethttp.Request) {
	session := http.CreateSession(h.Config, r, w)
	tStart := time.Now().UnixNano()

	h.Serve(session)

	t := float64(time.Now().UnixNano()-tStart) / 100000 // ms
	log.Printf("%s %s (%3.2fms) (%d)", r.Method, r.URL, t, session.Response.Status)
	h.Config.Events.Emit(session, events.AfterResponse, func() {})
}
<|endoftext|>


<commit_before>package router

import (
	"errors"
	"fmt"
	stdlog "log"
	"os"
	"sync"
	"time"

	"github.com/gammazero/nexus/logger"
	"github.com/gammazero/nexus/wamp"
)

// log is an instance of a logger that implements the logger.Logger interface.
// A stdlib logger is assigned by default (for convenience), but this can be
// reassigned, using SetLogger(), to use any other logging package.
var log logger.Logger = stdlog.New(os.Stdout, "", stdlog.LstdFlags)

// SetLogger assigns a logger instance to the router package.  Use this to
// assign an instance of anything that that implements the logger.Logger
// interface, before using the router package.
func SetLogger(logger logger.Logger) { log = logger }

// Log returns the logger that the router package is set to use.
func Logger() logger.Logger { return log }

// Enable debug logging for router package.
var DebugEnabled bool

const helloTimeout = 5 * time.Second

// Advertise roles supported by this router.  Feature information is provided
// by the broker and dealer implementations.
var routerRoles = map[string]interface{}{
	"roles": map[string]interface{}{
		"broker": map[string]interface{}{},
		"dealer": map[string]interface{}{},
	},
}

// A Router handles new Peers and routes requests to the requested Realm.
type Router interface {
	// AddRealm creates a new Realm and adds that to the router.
	AddRealm(wamp.URI, bool, bool) (*Realm, error)

	// AddCustomRealm the given realm to the router.
	//AddCustomRealm(realm *Realm) error

	// Attach connects a client to the router and to the requested realm.
	Attach(wamp.Peer) error

	// LocalClient starts a local session and returns a Peer to communicate
	// with that session.
	LocalClient(wamp.URI, map[string]interface{}) (wamp.Peer, error)

	// Close stops the router and waits message processing to stop.
	Close()
}

type routerReq struct {
	session *Session
	msg     wamp.Message
}

// DefaultRouter is the default WAMP router implementation.
type router struct {
	realms map[wamp.URI]*Realm

	actionChan  chan func()
	closingChan chan struct{}
	waitReamls  sync.WaitGroup

	autoRealm bool
	strictURI bool
}

// NewRouter creates a WAMP router.
//
// If authRealm is true, realms that do not exist are automatically created on
// client HELLO.  Caution, enabling this allows unauthenticated clients to
// create new realms.
//
// The strictURI parameter enabled strict URI validation.
func NewRouter(autoRealm, strictURI bool) Router {
	r := &router{
		realms:      map[wamp.URI]*Realm{},
		closingChan: make(chan struct{}),
		actionChan:  make(chan func()),

		autoRealm: autoRealm,
		strictURI: strictURI,
	}
	go r.routerRun()
	return r
}

// Single goroutine used to safely access router data.
func (r *router) routerRun() {
	for action := range r.actionChan {
		action()
	}
}

// AddRealm creates a new Realm and adds that to the router.
//
// At least one realm is needed, unless automatic realm creation is enabled.
func (r *router) AddRealm(uri wamp.URI, anonymousAuth, allowDisclose bool) (*Realm, error) {
	if !uri.ValidURI(r.strictURI, "") {
		return nil, fmt.Errorf(
			"invalid realm URI %v (URI strict checking %v)", uri, r.strictURI)
	}
	var realm *Realm
	sync := make(chan error)
	r.actionChan <- func() {
		if _, ok := r.realms[uri]; ok {
			sync <- errors.New("realm already exists: " + string(uri))
			return
		}
		realm = NewRealm(uri, r.strictURI, anonymousAuth, allowDisclose)
		r.realms[uri] = realm
		sync <- nil
	}
	err := <-sync
	if err != nil {
		return nil, fmt.Errorf("error adding realm: %v", err)
	}
	log.Print("Added realm: ", uri)
	return realm, nil
}

// Attach connects a client to the router and to the requested realm.
func (r *router) Attach(client wamp.Peer) error {
	sendAbort := func(reason wamp.URI, abortErr error) {
		abortMsg := wamp.Abort{Reason: reason}
		if abortErr != nil {
			abortMsg.Details = map[string]interface{}{"error": abortErr.Error()}
			log.Print("Aborting client connection: ", abortErr)
		}
		client.Send(&abortMsg)
		client.Close()
	}

	// Check that router is not shutting down.
	if r.closing() {
		sendAbort(wamp.ErrSystemShutdown, nil)
		return errors.New("router is closing, not accepting new clients")
	}

	// Receive HELLO message from the client.
	msg, err := wamp.RecvTimeout(client, helloTimeout)
	if err != nil {
		return errors.New("did not receive HELLO: " + err.Error())
	}
	log.Printf("New client sent: %s: %+v", msg.MessageType(), msg)

	// A WAMP session is initiated by the Client sending a HELLO message to the
	// Router.  The HELLO message MUST be the very first message sent by the
	// Client after the transport has been established.
	hello, ok := msg.(*wamp.Hello)
	if !ok {
		// Note: This URI is not official and there is no requirement to send
		// an error back to the client in this case.  Seems helpful to at least
		// let the client know what was wrong.
		err = fmt.Errorf("protocol error: expected HELLO, received %s",
			msg.MessageType())
		sendAbort(wamp.URI("wamp.exception.protocol_violation"), err)
		return err
	}

	// Client is required to provide a non-empty realm.
	if string(hello.Realm) == "" {
		err = errors.New("no realm requested")
		sendAbort(wamp.ErrNoSuchRealm, err)
		return err
	}
	// Lookup or create realm to attach to.
	var realm *Realm
	sync := make(chan error)
	r.actionChan <- func() {
		// Realm is a string identifying the realm this session should attach
		// to.  Check if the requested realm exists.
		var ok bool
		realm, ok = r.realms[hello.Realm]
		if !ok {
			// If the router is not configured to automatically create the
			// realm, then respond with an ABORT message.
			if !r.autoRealm {
				sendAbort(wamp.ErrNoSuchRealm, nil)
				sync <- fmt.Errorf("no realm \"%s\" exists on this router",
					string(hello.Realm))
				return
			}
			// Create the new realm that allows anonymous authentication and
			// allows disclosing caller ID.
			realm = NewRealm(hello.Realm, r.strictURI, true, true)
			r.realms[hello.Realm] = realm
			log.Print("Auto-added realm: ", hello.Realm)
		}
		sync <- nil
	}
	err = <-sync
	if err != nil {
		return err
	}

	hello.Details = wamp.NormalizeDict(hello.Details)

	// A Client must announce the roles it supports via
	// Hello.Details.roles|dict, where the keys can be: publisher, subscriber,
	// caller, callee.  If the client announces any roles, to list specific
	// features for the role, then check that the role is something this router
	// recognizes.
	_roleVals, err := wamp.DictValue(hello.Details, []string{"roles"})
	if err != nil {
		err = errors.New("no client roles specified")
		sendAbort(wamp.ErrNoSuchRole, err)
		return err
	}
	roleVals, ok := _roleVals.(map[string]interface{})
	if !ok || len(roleVals) == 0 {
		err = errors.New("no client roles specified")
		sendAbort(wamp.ErrNoSuchRole, err)
		return err
	}
	for roleName, _ := range roleVals {
		switch roleName {
		case "publisher", "subscriber", "caller", "callee":
		default:
			err = errors.New("invalid client role specified: " + roleName)
			sendAbort(wamp.ErrNoSuchRole, err)
			return err
		}
	}

	// The default authentication method is "WAMP-Anonymous" if client does not
	// specify otherwise.
	if _, ok = hello.Details["authmethods"]; !ok {
		if hello.Details == nil {
			hello.Details = map[string]interface{}{}
		}
		hello.Details["authmethods"] = []string{"anonymous"}
	}

	// Handle any necessary client auth.  This results in either a WELCOME
	// message or an error.
	//
	// Authentication may take some some.
	welcome, err := realm.authClient(client, hello.Details)
	if err != nil {
		sendAbort(wamp.ErrAuthenticationFailed, err)
		return errors.New("authentication error: " + err.Error())
	}

	// Fill in the values of the welcome message and send to client.
	welcome.ID = wamp.GlobalID()
	if welcome.Details == nil {
		welcome.Details = map[string]interface{}{}
	}
	for k, v := range routerRoles {
		if _, ok := welcome.Details[k]; !ok {
			welcome.Details[k] = v
		}
	}
	roles := welcome.Details["roles"].(map[string]interface{})
	roles["broker"] = realm.broker.Features()
	roles["dealer"] = realm.dealer.Features()

	client.Send(welcome)

	// Populate session details.
	details := map[string]interface{}{}
	details["realm"] = hello.Realm
	details["roles"] = roles
	details["authid"] = welcome.Details["authid"]
	details["authrole"] = welcome.Details["authrole"]
	details["authmethod"] = welcome.Details["authmethod"]
	details["authprovider"] = welcome.Details["authprovider"]

	// Create new session.
	sess := &Session{
		Peer:    client,
		ID:      welcome.ID,
		Details: details,
		stop:    make(chan wamp.URI, 1),
	}

	log.Print("Created session: ", welcome.ID)

	r.waitReamls.Add(1)
	go func() {
		realm.handleSession(sess, false)
		sess.Close()
		r.waitReamls.Done()
	}()
	return nil
}

// LocalClient returns a Peer connected to a local session running within the
// specified realm.
//
// This allows creation and attachment of a client session without having to
// go through the process of HELLO... auth... WELCOME.
func (r *router) LocalClient(realmURI wamp.URI, details map[string]interface{}) (wamp.Peer, error) {
	sync := make(chan *Realm)
	r.actionChan <- func() {
		realm := r.realms[realmURI]
		sync <- realm
	}
	realm := <-sync
	if realm == nil {
		return nil, errors.New("no such realm: " + string(realmURI))
	}

	// Start internal session and return remote leg of router uplink.
	return realm.bridgeSession(details, false)
}

// Close stops the router and waits message processing to stop.
func (r *router) Close() {
	if r.closing() {
		return
	}
	close(r.closingChan)
	sync := make(chan struct{})
	r.actionChan <- func() {
		for i := range r.realms {
			r.realms[i].Close()
		}
		sync <- struct{}{}
	}
	<-sync
	r.waitReamls.Wait()
	close(r.actionChan)
}

// Closed returns true if the router has been closed.
func (r *router) closing() bool {
	select {
	case <-r.closingChan:
		return true
	default:
	}
	return false
}
<commit_msg>Fix router.Close()<commit_after>package router

import (
	"errors"
	"fmt"
	stdlog "log"
	"os"
	"sync"
	"time"

	"github.com/gammazero/nexus/logger"
	"github.com/gammazero/nexus/wamp"
)

// log is an instance of a logger that implements the logger.Logger interface.
// A stdlib logger is assigned by default (for convenience), but this can be
// reassigned, using SetLogger(), to use any other logging package.
var log logger.Logger = stdlog.New(os.Stdout, "", stdlog.LstdFlags)

// SetLogger assigns a logger instance to the router package.  Use this to
// assign an instance of anything that that implements the logger.Logger
// interface, before using the router package.
func SetLogger(logger logger.Logger) { log = logger }

// Log returns the logger that the router package is set to use.
func Logger() logger.Logger { return log }

// Enable debug logging for router package.
var DebugEnabled bool

const helloTimeout = 5 * time.Second

// Advertise roles supported by this router.  Feature information is provided
// by the broker and dealer implementations.
var routerRoles = map[string]interface{}{
	"roles": map[string]interface{}{
		"broker": map[string]interface{}{},
		"dealer": map[string]interface{}{},
	},
}

// A Router handles new Peers and routes requests to the requested Realm.
type Router interface {
	// AddRealm creates a new Realm and adds that to the router.
	AddRealm(wamp.URI, bool, bool) (*Realm, error)

	// AddCustomRealm the given realm to the router.
	//AddCustomRealm(realm *Realm) error

	// Attach connects a client to the router and to the requested realm.
	Attach(wamp.Peer) error

	// LocalClient starts a local session and returns a Peer to communicate
	// with that session.
	LocalClient(wamp.URI, map[string]interface{}) (wamp.Peer, error)

	// Close stops the router and waits message processing to stop.
	Close()
}

type routerReq struct {
	session *Session
	msg     wamp.Message
}

// DefaultRouter is the default WAMP router implementation.
type router struct {
	realms map[wamp.URI]*Realm

	actionChan chan func()
	waitReamls sync.WaitGroup

	autoRealm bool
	strictURI bool
	closed    bool
}

// NewRouter creates a WAMP router.
//
// If authRealm is true, realms that do not exist are automatically created on
// client HELLO.  Caution, enabling this allows unauthenticated clients to
// create new realms.
//
// The strictURI parameter enabled strict URI validation.
func NewRouter(autoRealm, strictURI bool) Router {
	r := &router{
		realms:     map[wamp.URI]*Realm{},
		actionChan: make(chan func()),

		autoRealm: autoRealm,
		strictURI: strictURI,
	}
	go r.routerRun()
	return r
}

// Single goroutine used to safely access router data.
func (r *router) routerRun() {
	for action := range r.actionChan {
		action()
	}
}

// AddRealm creates a new Realm and adds that to the router.
//
// At least one realm is needed, unless automatic realm creation is enabled.
func (r *router) AddRealm(uri wamp.URI, anonymousAuth, allowDisclose bool) (*Realm, error) {
	if !uri.ValidURI(r.strictURI, "") {
		return nil, fmt.Errorf(
			"invalid realm URI %v (URI strict checking %v)", uri, r.strictURI)
	}
	var realm *Realm
	sync := make(chan error)
	r.actionChan <- func() {
		if r.closed {
			sync <- errors.New("router closed")
			return
		}
		if _, ok := r.realms[uri]; ok {
			sync <- errors.New("realm already exists: " + string(uri))
			return
		}
		realm = NewRealm(uri, r.strictURI, anonymousAuth, allowDisclose)
		r.realms[uri] = realm
		sync <- nil
	}
	err := <-sync
	if err != nil {
		return nil, fmt.Errorf("error adding realm: %v", err)
	}
	log.Print("Added realm: ", uri)
	return realm, nil
}

// Attach connects a client to the router and to the requested realm.
func (r *router) Attach(client wamp.Peer) error {
	sendAbort := func(reason wamp.URI, abortErr error) {
		abortMsg := wamp.Abort{Reason: reason}
		if abortErr != nil {
			abortMsg.Details = map[string]interface{}{"error": abortErr.Error()}
			log.Print("Aborting client connection: ", abortErr)
		}
		client.Send(&abortMsg)
		client.Close()
	}

	// Receive HELLO message from the client.
	msg, err := wamp.RecvTimeout(client, helloTimeout)
	if err != nil {
		return errors.New("did not receive HELLO: " + err.Error())
	}
	log.Printf("New client sent: %s: %+v", msg.MessageType(), msg)

	// A WAMP session is initiated by the Client sending a HELLO message to the
	// Router.  The HELLO message MUST be the very first message sent by the
	// Client after the transport has been established.
	hello, ok := msg.(*wamp.Hello)
	if !ok {
		// Note: This URI is not official and there is no requirement to send
		// an error back to the client in this case.  Seems helpful to at least
		// let the client know what was wrong.
		err = fmt.Errorf("protocol error: expected HELLO, received %s",
			msg.MessageType())
		sendAbort(wamp.URI("wamp.exception.protocol_violation"), err)
		return err
	}

	// Client is required to provide a non-empty realm.
	if string(hello.Realm) == "" {
		err = errors.New("no realm requested")
		sendAbort(wamp.ErrNoSuchRealm, err)
		return err
	}
	// Lookup or create realm to attach to.
	var realm *Realm
	sync := make(chan error)
	r.actionChan <- func() {
		if r.closed {
			sendAbort(wamp.ErrSystemShutdown, nil)
			sync <- errors.New("router is closing, not accepting new clients")
			return
		}
		// Realm is a string identifying the realm this session should attach
		// to.  Check if the requested realm exists.
		var ok bool
		realm, ok = r.realms[hello.Realm]
		if !ok {
			// If the router is not configured to automatically create the
			// realm, then respond with an ABORT message.
			if !r.autoRealm {
				sendAbort(wamp.ErrNoSuchRealm, nil)
				sync <- fmt.Errorf("no realm \"%s\" exists on this router",
					string(hello.Realm))
				return
			}
			// Create the new realm that allows anonymous authentication and
			// allows disclosing caller ID.
			realm = NewRealm(hello.Realm, r.strictURI, true, true)
			r.realms[hello.Realm] = realm
			log.Print("Auto-added realm: ", hello.Realm)
		}
		sync <- nil
	}
	err = <-sync
	if err != nil {
		return err
	}

	hello.Details = wamp.NormalizeDict(hello.Details)

	// A Client must announce the roles it supports via
	// Hello.Details.roles|dict, where the keys can be: publisher, subscriber,
	// caller, callee.  If the client announces any roles, to list specific
	// features for the role, then check that the role is something this router
	// recognizes.
	_roleVals, err := wamp.DictValue(hello.Details, []string{"roles"})
	if err != nil {
		err = errors.New("no client roles specified")
		sendAbort(wamp.ErrNoSuchRole, err)
		return err
	}
	roleVals, ok := _roleVals.(map[string]interface{})
	if !ok || len(roleVals) == 0 {
		err = errors.New("no client roles specified")
		sendAbort(wamp.ErrNoSuchRole, err)
		return err
	}
	for roleName, _ := range roleVals {
		switch roleName {
		case "publisher", "subscriber", "caller", "callee":
		default:
			err = errors.New("invalid client role specified: " + roleName)
			sendAbort(wamp.ErrNoSuchRole, err)
			return err
		}
	}

	// The default authentication method is "WAMP-Anonymous" if client does not
	// specify otherwise.
	if _, ok = hello.Details["authmethods"]; !ok {
		if hello.Details == nil {
			hello.Details = map[string]interface{}{}
		}
		hello.Details["authmethods"] = []string{"anonymous"}
	}

	// Handle any necessary client auth.  This results in either a WELCOME
	// message or an error.
	//
	// Authentication may take some some.
	welcome, err := realm.authClient(client, hello.Details)
	if err != nil {
		sendAbort(wamp.ErrAuthenticationFailed, err)
		return errors.New("authentication error: " + err.Error())
	}

	// Fill in the values of the welcome message and send to client.
	welcome.ID = wamp.GlobalID()
	if welcome.Details == nil {
		welcome.Details = map[string]interface{}{}
	}
	for k, v := range routerRoles {
		if _, ok := welcome.Details[k]; !ok {
			welcome.Details[k] = v
		}
	}
	roles := welcome.Details["roles"].(map[string]interface{})
	roles["broker"] = realm.broker.Features()
	roles["dealer"] = realm.dealer.Features()

	client.Send(welcome)

	// Populate session details.
	details := map[string]interface{}{}
	details["realm"] = hello.Realm
	details["roles"] = roles
	details["authid"] = welcome.Details["authid"]
	details["authrole"] = welcome.Details["authrole"]
	details["authmethod"] = welcome.Details["authmethod"]
	details["authprovider"] = welcome.Details["authprovider"]

	// Create new session.
	sess := &Session{
		Peer:    client,
		ID:      welcome.ID,
		Details: details,
		stop:    make(chan wamp.URI, 1),
	}

	log.Print("Created session: ", welcome.ID)

	r.waitReamls.Add(1)
	go func() {
		realm.handleSession(sess, false)
		sess.Close()
		r.waitReamls.Done()
	}()
	return nil
}

// LocalClient returns a Peer connected to a local session running within the
// specified realm.
//
// This allows creation and attachment of a client session without having to
// go through the process of HELLO... auth... WELCOME.
func (r *router) LocalClient(realmURI wamp.URI, details map[string]interface{}) (wamp.Peer, error) {
	sync := make(chan *Realm)
	r.actionChan <- func() {
		realm := r.realms[realmURI]
		sync <- realm
	}
	realm := <-sync
	if realm == nil {
		return nil, errors.New("no such realm: " + string(realmURI))
	}

	// Start internal session and return remote leg of router uplink.
	return realm.bridgeSession(details, false)
}

// Close stops the router and waits message processing to stop.
func (r *router) Close() {
	sync := make(chan struct{})
	r.actionChan <- func() {
		// Prevent new or attachment to existing realms.
		r.closed = true
		// Close all existing realms.
		for i := range r.realms {
			r.realms[i].Close()
		}
		sync <- struct{}{}
	}
	<-sync
	// Wait for all existing realms to close.
	r.waitReamls.Wait()
}
<|endoftext|>


<commit_before>package routes

import (
  "net/http"

  "github.com/gorilla/mux"
  "github.com/gorilla/websocket"

  "github.com/OlivierBoucher/go-tracking-server/middlewares"
  "github.com/OlivierBoucher/go-tracking-server/ctx"
)
var upgrader = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
    CheckOrigin: func(r *http.Request) bool {
      //TODO: Perform checks on the request and deny/accept
      return true
    },
}
//Handler for /track route
//From here we have a valid authentified json request
func handleTrack(c *ctx.Context, w http.ResponseWriter, r *http.Request) {
  //TODO: Handle the json payload
}
//Handler for /connected route
//Allows websocket connection
func handleConnected(c *ctx.Context, w http.ResponseWriter, r *http.Request) {
  conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        //TODO: Handle the error
        panic(err)
    }
    for {
        messageType, p, err := conn.ReadMessage()
        if err != nil {
            return
        }
        //TODO: Define a protocol

        err = conn.WriteMessage(messageType, p);
        if  err != nil {
            return
        }
    }
}
//Handlers - Returns a mux router containing all handlers for all routes
func Handlers(c *ctx.Context) *mux.Router {
  r := mux.NewRouter()
  //Each supported route is being added to the router
  trackHandler := &ctx.Handler{c, handleTrack}
  r.Handle("/track", middlewares.EnforceJSONHandler(middlewares.AuthHandler(trackHandler))).Methods("POST", "GET")
  connectedHandler := &ctx.Handler{c, handleConnected}
  r.Handle("/connected", connectedHandler)

  return r
}
<commit_msg>Completed events for POST request flow<commit_after>package routes

import (
  "net/http"
  "bytes"

  "github.com/gorilla/mux"
  "github.com/gorilla/websocket"

  "github.com/OlivierBoucher/go-tracking-server/middlewares"
  "github.com/OlivierBoucher/go-tracking-server/ctx"
)
var upgrader = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
    CheckOrigin: func(r *http.Request) bool {
      //TODO: Perform checks on the request and deny/accept
      return true
    },
}
//Handler for /track route
//From here we have a valid authentified json request with correct schema
func handleTrack(c *ctx.Context, w http.ResponseWriter, r *http.Request) {
  buf := new(bytes.Buffer)
  buf.ReadFrom(r.Body)
  c.Queue.PublishEventsTrackingTask(buf.Bytes())
}
//Handler for /connected route
//Allows websocket connection
func handleConnected(c *ctx.Context, w http.ResponseWriter, r *http.Request) {
  conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        //TODO: Handle the error
        panic(err)
    }
    for {
        messageType, p, err := conn.ReadMessage()
        if err != nil {
            return
        }
        //TODO: Define a protocol

        err = conn.WriteMessage(messageType, p);
        if  err != nil {
            return
        }
    }
}
//Handlers - Returns a mux router containing all handlers for all routes
func Handlers(c *ctx.Context) *mux.Router {
  r := mux.NewRouter()
  //Each supported route is being added to the router
  trackHandler := &ctx.Handler{c, handleTrack}
  r.Handle("/track", middlewares.EnforceJSONHandler(middlewares.AuthHandler(middlewares.ValidateEventTrackingPayloadHandler(trackHandler)))).Methods("POST", "GET")
  connectedHandler := &ctx.Handler{c, handleConnected}
  r.Handle("/connected", connectedHandler)

  return r
}
<|endoftext|>


<commit_before>package dht

import (
	"expvar"
	"fmt"
	"net"
	"time"

	log "github.com/golang/glog"
)

func newRoutingTable() *routingTable {
	return &routingTable{
		&nTree{},
		make(map[string]*remoteNode),
		"",
		nil,
		0,
	}
}

type routingTable struct {
	*nTree
	// addresses is a map of UDP addresses in host:port format and
	// remoteNodes. A string is used because it's not possible to create
	// a map using net.UDPAddr
	// as a key.
	addresses map[string]*remoteNode

	// Neighborhood.
	nodeId       string // This shouldn't be here. Move neighborhood upkeep one level up?
	boundaryNode *remoteNode
	// How many prefix bits are shared between boundaryNode and nodeId.
	proximity int
}

// hostPortToNode finds or creates a node based on the specified hostPort
// specification, which should be a UDP address in the form "host:port".
// Specifying an illegal string is illegal and will generate a panic.
func (r *routingTable) hostPortToNode(hostPort string) (node *remoteNode, addr string, existed bool, err error) {
	if hostPort == "" {
		panic("programming error: hostPortToNode received a nil hostPort")
	}
	address, err := net.ResolveUDPAddr("udp", hostPort)
	if err != nil {
		return nil, "", false, err
	}
	if address.String() == "" {
		panic("programming error: address resolution for hostPortToNode returned an empty string")
	}
	n, existed := r.addresses[address.String()]
	return n, address.String(), existed, nil
}

func (r *routingTable) length() int {
	return len(r.addresses)
}

func (r *routingTable) reachableNodes() (tbl map[string][]byte) {
	tbl = make(map[string][]byte)
	for addr, r := range r.addresses {
		if addr == "" {
			log.V(3).Infof("reachableNodes: found empty address for node %x.", r.id)
			continue
		}
		if r.reachable && len(r.id) == 20 {
			tbl[addr] = []byte(r.id)
		}
	}

	hexId := fmt.Sprintf("%x", r.nodeId)
	// This creates a new expvar everytime, but the alternative is too
	// bothersome (get the current value, type cast it, ensure it
	// exists..). Also I'm not using NewInt because I don't want to publish
	// the value.
	v := new(expvar.Int)
	v.Set(int64(len(tbl)))
	reachableNodes.Set(hexId, v)
	return

}

func (r *routingTable) numNodes() int {
	return len(r.addresses)
}

func isValidAddr(addr string) bool {
	if addr == "" {
		return false
	}
	if h, p, err := net.SplitHostPort(addr); h == "" || p == "" || err != nil {
		return false
	}
	return true
}

// update the existing routingTable entry for this node by setting its correct
// infohash id. Gives an error if the node was not found.
func (r *routingTable) update(node *remoteNode) error {
	_, addr, existed, err := r.hostPortToNode(node.address.String())
	if err != nil {
		return err
	}
	if !isValidAddr(addr) {
		return fmt.Errorf("routingTable.update received an invalid address %v", addr)
	}
	if !existed {
		return fmt.Errorf("node missing from the routing table:", node.address.String())
	}
	if node.id != "" {
		r.nTree.insert(node)
		totalNodes.Add(1)
		r.addresses[addr].id = node.id
	}
	return nil
}

// insert the provided node into the routing table. Gives an error if another
// node already existed with that address.
func (r *routingTable) insert(node *remoteNode) error {
	if node.address.Port == 0 || node.address.IP.IsUnspecified() {
		panic("routingTable.insert() got a node with a nil address")
	}
	_, addr, existed, err := r.hostPortToNode(node.address.String())
	if err != nil {
		return err
	}
	if !isValidAddr(addr) {
		return fmt.Errorf("routingTable.insert received an invalid address %v", addr)

	}
	if existed {
		return nil // fmt.Errorf("node already existed in routing table: %v", node.address.String())
	}
	r.addresses[addr] = node
	// We don't know the ID of all nodes.
	if !bogusId(node.id) {
		// recursive version of node insertion.
		r.nTree.insert(node)
		totalNodes.Add(1)
	}
	return nil
}

// getOrCreateNode returns a node for hostPort, which can be an IP:port or
// Host:port, which will be resolved if possible.  Preferably return an entry
// that is already in the routing table, but create a new one otherwise, thus
// being idempotent.
func (r *routingTable) getOrCreateNode(id string, hostPort string) (node *remoteNode, err error) {
	node, addr, existed, err := r.hostPortToNode(hostPort)
	if err != nil {
		return nil, err
	}
	if existed {
		return node, nil
	}
	udpAddr, err := net.ResolveUDPAddr("udp", addr)
	if err != nil {
		return nil, err
	}
	node = newRemoteNode(*udpAddr, id)
	return node, r.insert(node)
}

func (r *routingTable) kill(n *remoteNode) {
	delete(r.addresses, n.address.String())
	r.nTree.cut(InfoHash(n.id), 0)
	totalKilledNodes.Add(1)

	if n.id == r.boundaryNode.id {
		r.resetNeighborhoodBoundary()
	}
}

func (r *routingTable) resetNeighborhoodBoundary() {
	r.proximity = 0
	// Try to find a distant one within the neighborhood and promote it as
	// the most distant node in the neighborhood.
	neighbors := r.lookup(InfoHash(r.nodeId))
	if len(neighbors) > 0 {
		r.boundaryNode = neighbors[len(neighbors)-1]
		r.proximity = commonBits(r.nodeId, r.boundaryNode.id)
	}

}

func (r *routingTable) cleanup(cleanupPeriod time.Duration) (needPing []*remoteNode) {
	needPing = make([]*remoteNode, 0, 10)
	t0 := time.Now()
	// Needs some serious optimization.
	for addr, n := range r.addresses {
		if addr != n.address.String() {
			log.V(3).Infof("cleanup: node address mismatches: %v != %v. Deleting node", addr, n.address.String())
			r.kill(n)
			continue
		}
		if addr == "" {
			log.V(3).Infof("cleanup: found empty address for node %x. Deleting node", n.id)
			r.kill(n)
			continue
		}
		if n.reachable {
			if len(n.pendingQueries) == 0 {
				goto PING
			}
			// Tolerate 2 cleanup cycles.
			if time.Since(n.lastResponseTime) > cleanupPeriod*2+(time.Minute) {
				log.V(4).Infof("DHT: Old node seen %v ago. Deleting", time.Since(n.lastResponseTime))
				r.kill(n)
				continue
			}
			if time.Since(n.lastResponseTime).Nanoseconds() < cleanupPeriod.Nanoseconds()/2 {
				// Seen recently. Don't need to ping.
				continue
			}

		} else {
			// Not reachable.
			if len(n.pendingQueries) > 2 {
				// Didn't reply to 2 consecutive queries.
				log.V(4).Infof("DHT: Node never replied to ping. Deleting. %v", n.address)
				r.kill(n)
				continue
			}
		}
	PING:
		needPing = append(needPing, n)
	}
	duration := time.Since(t0)
	// If this pauses the server for too long I may have to segment the cleanup.
	// 2000 nodes: it takes ~12ms
	// 4000 nodes: ~24ms.
	log.V(3).Info("DHT: Routing table cleanup took %v", duration)
	return needPing
}

// neighborhoodUpkeep will update the routingtable if the node n is closer than
// the 8 nodes in our neighborhood, by replacing the least close one
// (boundary). n.id is assumed to have length 20.
func (r *routingTable) neighborhoodUpkeep(n *remoteNode) {
	if r.proximity == 0 {
		r.addNewNeighbor(n, true)
		return
	}
	if r.length() < kNodes {
		r.addNewNeighbor(n, false)
		return
	}
	cmp := commonBits(r.nodeId, n.id)
	if cmp == 0 {
		// Same node id.
		return
	}
	if cmp > r.proximity {
		r.addNewNeighbor(n, true)
		return
	}
}

func (r *routingTable) addNewNeighbor(n *remoteNode, displaceBoundary bool) {
	if err := r.insert(n); err != nil {
		log.V(3).Infof("addNewNeighbor error: %v", err)
		return
	}
	if displaceBoundary && r.boundaryNode != nil {
		// This will also take care of setting a new boundary.
		r.kill(r.boundaryNode)
	} else {
		r.resetNeighborhoodBoundary()
	}
	log.V(4).Infof("New neighbor added to neighborhood with proximity %d", r.proximity)
}

// pingSlowly pings the remote nodes in needPing, distributing the pings
// throughout an interval of cleanupPeriod, to avoid network traffic bursts. It
// doesn't really send the pings, but signals to the main goroutine that it
// should ping the nodes, using the pingRequest channel.
func pingSlowly(pingRequest chan *remoteNode, needPing []*remoteNode, cleanupPeriod time.Duration, stop chan bool) {
	if len(needPing) == 0 {
		return
	}
	duration := cleanupPeriod - (1 * time.Minute)
	perPingWait := duration / time.Duration(len(needPing))
	for _, r := range needPing {
		pingRequest <- r
		select {
		case <-time.After(perPingWait):
		case <-stop:
			return
		}
	}
}

var (
	// totalKilledNodes is a monotonically increasing counter of times nodes were killed from
	// the routing table. If a node is later added to the routing table and killed again, it is
	// counted twice.
	totalKilledNodes = expvar.NewInt("totalKilledNodes")
	// totalNodes is a monotonically increasing counter of times nodes were added to the routing
	// table. If a node is removed then later added again, it is counted twice.
	totalNodes = expvar.NewInt("totalNodes")
	// reachableNodes is the count of all reachable nodes from a particular DHT node. The map
	// key is the local node's infohash. The value is a gauge with the count of reachable nodes
	// at the latest time the routing table was persisted on disk.
	reachableNodes = expvar.NewMap("reachableNodes")
)
<commit_msg>DHT neighborhood upkeep getting confused when remote node has zero proximity.<commit_after>package dht

import (
	"expvar"
	"fmt"
	"net"
	"time"

	log "github.com/golang/glog"
)

func newRoutingTable() *routingTable {
	return &routingTable{
		&nTree{},
		make(map[string]*remoteNode),
		"",
		nil,
		0,
	}
}

type routingTable struct {
	*nTree
	// addresses is a map of UDP addresses in host:port format and
	// remoteNodes. A string is used because it's not possible to create
	// a map using net.UDPAddr
	// as a key.
	addresses map[string]*remoteNode

	// Neighborhood.
	nodeId       string // This shouldn't be here. Move neighborhood upkeep one level up?
	boundaryNode *remoteNode
	// How many prefix bits are shared between boundaryNode and nodeId.
	proximity int
}

// hostPortToNode finds or creates a node based on the specified hostPort
// specification, which should be a UDP address in the form "host:port".
// Specifying an illegal string is illegal and will generate a panic.
func (r *routingTable) hostPortToNode(hostPort string) (node *remoteNode, addr string, existed bool, err error) {
	if hostPort == "" {
		panic("programming error: hostPortToNode received a nil hostPort")
	}
	address, err := net.ResolveUDPAddr("udp", hostPort)
	if err != nil {
		return nil, "", false, err
	}
	if address.String() == "" {
		panic("programming error: address resolution for hostPortToNode returned an empty string")
	}
	n, existed := r.addresses[address.String()]
	return n, address.String(), existed, nil
}

func (r *routingTable) length() int {
	return len(r.addresses)
}

func (r *routingTable) reachableNodes() (tbl map[string][]byte) {
	tbl = make(map[string][]byte)
	for addr, r := range r.addresses {
		if addr == "" {
			log.V(3).Infof("reachableNodes: found empty address for node %x.", r.id)
			continue
		}
		if r.reachable && len(r.id) == 20 {
			tbl[addr] = []byte(r.id)
		}
	}

	hexId := fmt.Sprintf("%x", r.nodeId)
	// This creates a new expvar everytime, but the alternative is too
	// bothersome (get the current value, type cast it, ensure it
	// exists..). Also I'm not using NewInt because I don't want to publish
	// the value.
	v := new(expvar.Int)
	v.Set(int64(len(tbl)))
	reachableNodes.Set(hexId, v)
	return

}

func (r *routingTable) numNodes() int {
	return len(r.addresses)
}

func isValidAddr(addr string) bool {
	if addr == "" {
		return false
	}
	if h, p, err := net.SplitHostPort(addr); h == "" || p == "" || err != nil {
		return false
	}
	return true
}

// update the existing routingTable entry for this node by setting its correct
// infohash id. Gives an error if the node was not found.
func (r *routingTable) update(node *remoteNode) error {
	_, addr, existed, err := r.hostPortToNode(node.address.String())
	if err != nil {
		return err
	}
	if !isValidAddr(addr) {
		return fmt.Errorf("routingTable.update received an invalid address %v", addr)
	}
	if !existed {
		return fmt.Errorf("node missing from the routing table:", node.address.String())
	}
	if node.id != "" {
		r.nTree.insert(node)
		totalNodes.Add(1)
		r.addresses[addr].id = node.id
	}
	return nil
}

// insert the provided node into the routing table. Gives an error if another
// node already existed with that address.
func (r *routingTable) insert(node *remoteNode) error {
	if node.address.Port == 0 || node.address.IP.IsUnspecified() {
		panic("routingTable.insert() got a node with a nil address")
	}
	_, addr, existed, err := r.hostPortToNode(node.address.String())
	if err != nil {
		return err
	}
	if !isValidAddr(addr) {
		return fmt.Errorf("routingTable.insert received an invalid address %v", addr)

	}
	if existed {
		return nil // fmt.Errorf("node already existed in routing table: %v", node.address.String())
	}
	r.addresses[addr] = node
	// We don't know the ID of all nodes.
	if !bogusId(node.id) {
		// recursive version of node insertion.
		r.nTree.insert(node)
		totalNodes.Add(1)
	}
	return nil
}

// getOrCreateNode returns a node for hostPort, which can be an IP:port or
// Host:port, which will be resolved if possible.  Preferably return an entry
// that is already in the routing table, but create a new one otherwise, thus
// being idempotent.
func (r *routingTable) getOrCreateNode(id string, hostPort string) (node *remoteNode, err error) {
	node, addr, existed, err := r.hostPortToNode(hostPort)
	if err != nil {
		return nil, err
	}
	if existed {
		return node, nil
	}
	udpAddr, err := net.ResolveUDPAddr("udp", addr)
	if err != nil {
		return nil, err
	}
	node = newRemoteNode(*udpAddr, id)
	return node, r.insert(node)
}

func (r *routingTable) kill(n *remoteNode) {
	delete(r.addresses, n.address.String())
	r.nTree.cut(InfoHash(n.id), 0)
	totalKilledNodes.Add(1)

	if n.id == r.boundaryNode.id {
		r.resetNeighborhoodBoundary()
	}
}

func (r *routingTable) resetNeighborhoodBoundary() {
	r.proximity = 0
	// Try to find a distant one within the neighborhood and promote it as
	// the most distant node in the neighborhood.
	neighbors := r.lookup(InfoHash(r.nodeId))
	if len(neighbors) > 0 {
		r.boundaryNode = neighbors[len(neighbors)-1]
		r.proximity = commonBits(r.nodeId, r.boundaryNode.id)
	}

}

func (r *routingTable) cleanup(cleanupPeriod time.Duration) (needPing []*remoteNode) {
	needPing = make([]*remoteNode, 0, 10)
	t0 := time.Now()
	// Needs some serious optimization.
	for addr, n := range r.addresses {
		if addr != n.address.String() {
			log.V(3).Infof("cleanup: node address mismatches: %v != %v. Deleting node", addr, n.address.String())
			r.kill(n)
			continue
		}
		if addr == "" {
			log.V(3).Infof("cleanup: found empty address for node %x. Deleting node", n.id)
			r.kill(n)
			continue
		}
		if n.reachable {
			if len(n.pendingQueries) == 0 {
				goto PING
			}
			// Tolerate 2 cleanup cycles.
			if time.Since(n.lastResponseTime) > cleanupPeriod*2+(time.Minute) {
				log.V(4).Infof("DHT: Old node seen %v ago. Deleting", time.Since(n.lastResponseTime))
				r.kill(n)
				continue
			}
			if time.Since(n.lastResponseTime).Nanoseconds() < cleanupPeriod.Nanoseconds()/2 {
				// Seen recently. Don't need to ping.
				continue
			}

		} else {
			// Not reachable.
			if len(n.pendingQueries) > 2 {
				// Didn't reply to 2 consecutive queries.
				log.V(4).Infof("DHT: Node never replied to ping. Deleting. %v", n.address)
				r.kill(n)
				continue
			}
		}
	PING:
		needPing = append(needPing, n)
	}
	duration := time.Since(t0)
	// If this pauses the server for too long I may have to segment the cleanup.
	// 2000 nodes: it takes ~12ms
	// 4000 nodes: ~24ms.
	log.V(3).Info("DHT: Routing table cleanup took %v", duration)
	return needPing
}

// neighborhoodUpkeep will update the routingtable if the node n is closer than
// the 8 nodes in our neighborhood, by replacing the least close one
// (boundary). n.id is assumed to have length 20.
func (r *routingTable) neighborhoodUpkeep(n *remoteNode) {
	if r.boundaryNode == nil {
		r.addNewNeighbor(n, false)
		return
	}
	if r.length() < kNodes {
		r.addNewNeighbor(n, false)
		return
	}
	cmp := commonBits(r.nodeId, n.id)
	if cmp == 0 {
		// Not significantly better.
		return
	}
	if cmp > r.proximity {
		r.addNewNeighbor(n, true)
		return
	}
}

func (r *routingTable) addNewNeighbor(n *remoteNode, displaceBoundary bool) {
	if err := r.insert(n); err != nil {
		log.V(3).Infof("addNewNeighbor error: %v", err)
		return
	}
	if displaceBoundary && r.boundaryNode != nil {
		// This will also take care of setting a new boundary.
		r.kill(r.boundaryNode)
	} else {
		r.resetNeighborhoodBoundary()
	}
	log.V(4).Infof("New neighbor added to neighborhood with proximity %d", r.proximity)
}

// pingSlowly pings the remote nodes in needPing, distributing the pings
// throughout an interval of cleanupPeriod, to avoid network traffic bursts. It
// doesn't really send the pings, but signals to the main goroutine that it
// should ping the nodes, using the pingRequest channel.
func pingSlowly(pingRequest chan *remoteNode, needPing []*remoteNode, cleanupPeriod time.Duration, stop chan bool) {
	if len(needPing) == 0 {
		return
	}
	duration := cleanupPeriod - (1 * time.Minute)
	perPingWait := duration / time.Duration(len(needPing))
	for _, r := range needPing {
		pingRequest <- r
		select {
		case <-time.After(perPingWait):
		case <-stop:
			return
		}
	}
}

var (
	// totalKilledNodes is a monotonically increasing counter of times nodes were killed from
	// the routing table. If a node is later added to the routing table and killed again, it is
	// counted twice.
	totalKilledNodes = expvar.NewInt("totalKilledNodes")
	// totalNodes is a monotonically increasing counter of times nodes were added to the routing
	// table. If a node is removed then later added again, it is counted twice.
	totalNodes = expvar.NewInt("totalNodes")
	// reachableNodes is the count of all reachable nodes from a particular DHT node. The map
	// key is the local node's infohash. The value is a gauge with the count of reachable nodes
	// at the latest time the routing table was persisted on disk.
	reachableNodes = expvar.NewMap("reachableNodes")
)
<|endoftext|>


<commit_before>package routes

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"

	"github.com/buger/jsonparser"
	"github.com/lcanal/demspirals/models"
	"github.com/spf13/viper"
)

//TeamRoster grabs full team roster
func TeamRoster(w http.ResponseWriter, r *http.Request) {
	var players []models.Player
	const MAXPAGECOUNT = 5

	client := &http.Client{}

	for currentPage := 1; currentPage < MAXPAGECOUNT; currentPage++ {
		apiBase := viper.GetString("apiBaseURL") + "/football/nfl/rosters" + "?per_page=40&page="
		apiPagedURL := apiBase + strconv.Itoa(currentPage)
		log.Printf("Grabbing URL: %s", apiPagedURL)

		req, _ := http.NewRequest("GET", apiPagedURL, nil)
		req.Header.Add("Authorization", "Token token="+viper.GetString("creds.accessToken"))
		req.Header.Add("Content-Type", "application/json")
		req.Header.Add("Accept", "application/vnd.stattleship.com; version=1")
		resp, err := client.Do(req)
		if err != nil {
			log.Printf("Error pulling team rosters auth : %s\n", err)
		}

		if resp.StatusCode != 200 {
			log.Printf("Error with request: %s (%d)\n", resp.Status, resp.StatusCode)
		}

		data, _ := ioutil.ReadAll(resp.Body)
		//log.Printf("data: %s\n", body)

		jsonparser.ArrayEach(
			data,
			func(player []byte, dataType jsonparser.ValueType, offset int, err error) {
				playername, _ := jsonparser.GetString(player, "name")
				playerpos, _ := jsonparser.GetString(player, "position_name")
				playerslug, _ := jsonparser.GetString(player, "slug")

				newPlayer := models.Player{
					Slug:     playerslug,
					Name:     playername,
					Position: playerpos,
				}

				players = append(players, newPlayer)
			},
			"players",
		)
	}

	//Gotta return something...
	length := strconv.Itoa(len(players))
	fmt.Fprintf(w, length)
}
<commit_msg>Using map instead of slices<commit_after>package routes

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"

	"github.com/buger/jsonparser"
	"github.com/lcanal/demspirals/models"
	"github.com/spf13/viper"
)

//TeamRoster grabs full team roster
func TeamRoster(w http.ResponseWriter, r *http.Request) {
	var players map[string]models.Player
	const MAXPAGECOUNT = 5

	client := &http.Client{}
	players = make(map[string]models.Player)

	for currentPage := 1; currentPage < MAXPAGECOUNT; currentPage++ {
		apiBase := viper.GetString("apiBaseURL") + "/football/nfl/rosters" + "?per_page=40&page="
		apiPagedURL := apiBase + strconv.Itoa(currentPage)
		log.Printf("Grabbing URL: %s", apiPagedURL)

		req, _ := http.NewRequest("GET", apiPagedURL, nil)
		req.Header.Add("Authorization", "Token token="+viper.GetString("creds.accessToken"))
		req.Header.Add("Content-Type", "application/json")
		req.Header.Add("Accept", "application/vnd.stattleship.com; version=1")
		resp, err := client.Do(req)
		if err != nil {
			log.Printf("Error pulling team rosters auth : %s\n", err)
		}

		if resp.StatusCode != 200 {
			log.Printf("Error with request: %s (%d)\n", resp.Status, resp.StatusCode)
		}

		data, _ := ioutil.ReadAll(resp.Body)
		//log.Printf("data: %s\n", body)

		jsonparser.ArrayEach(
			data,
			func(player []byte, dataType jsonparser.ValueType, offset int, err error) {
				playername, _ := jsonparser.GetString(player, "name")
				playerpos, _ := jsonparser.GetString(player, "position_name")
				playerslug, _ := jsonparser.GetString(player, "slug")

				newPlayer := models.Player{
					Slug:     playerslug,
					Name:     playername,
					Position: playerpos,
				}

				players[playerslug] = newPlayer
			},
			"players",
		)
	}

	//Gotta return something...
	length := strconv.Itoa(len(players))
	fmt.Fprintf(w, length)
}
<|endoftext|>


<commit_before>// bufdb is used to persist buffer.Buffer's to a bolt.DB database.
package bufdb

import (
	"github.com/boltdb/bolt"
	"github.com/djherbis/buffer"
	"github.com/djherbis/stow"
)

// BufferStore manages buffer.Buffer persistance.
type BufferStore struct {
	buffers *stow.Store
}

// NewBufferStore creates a new BufferStore, using the underlying
// bolt.DB "bucket" to persist buffers.
func NewBufferStore(db *bolt.DB, bucket []byte) *BufferStore {
	return &BufferStore{buffers: stow.NewStore(db, bucket)}
}

// Put persists buffer.Buffer b, modifying b after Put will not affect the stored buffer.
// This will fail if the buffer is not gob-encodable.
func (s *BufferStore) Put(key []byte, b buffer.Buffer) error {
	return s.buffers.Put(key, b)
}

// Pull will retrive (and removes) the buffer stored under "key".
// ErrNotFound indicates that the buffer was not found in the database,
// other errors indicate gob-decoding failures.
func (s *BufferStore) Pull(key []byte) (b buffer.Buffer, err error) {
	err = s.buffers.Pull(key, &b)
	return b, err
}

// DeleteAll will call Reset() on every buffer managed by this store,
// and then delete the buffer from the store.
func (s *BufferStore) DeleteAll() error {
	s.buffers.ForEach(func(i interface{}) {
		if b, ok := i.(buffer.Buffer); ok {
			b.Reset()
		}
	})
	return s.buffers.DeleteAll()
}

type BufferPoolStore struct {
	pools *stow.Store
}

func NewBufferPoolStore(db *bolt.DB, bucket []byte) *BufferPoolStore {
	return &BufferPoolStore{pools: stow.NewStore(db, bucket)}
}

func (s *BufferPoolStore) Put(key []byte, p buffer.Pool) error {
	return s.pools.Put(key, p)
}

func (s *BufferPoolStore) Pull(key []byte) (p buffer.Pool, err error) {
	err = s.pools.Pull(key, &p)
	return p, err
}

func (s *BufferPoolStore) DeleteAll() error {
	return s.pools.DeleteAll()
}
<commit_msg>stow ForEach upgrade<commit_after>// bufdb is used to persist buffer.Buffer's to a bolt.DB database.
package bufdb

import (
	"github.com/boltdb/bolt"
	"github.com/djherbis/buffer"
	"github.com/djherbis/stow"
)

// BufferStore manages buffer.Buffer persistance.
type BufferStore struct {
	buffers *stow.Store
}

// NewBufferStore creates a new BufferStore, using the underlying
// bolt.DB "bucket" to persist buffers.
func NewBufferStore(db *bolt.DB, bucket []byte) *BufferStore {
	return &BufferStore{buffers: stow.NewStore(db, bucket)}
}

// Put persists buffer.Buffer b, modifying b after Put will not affect the stored buffer.
// This will fail if the buffer is not gob-encodable.
func (s *BufferStore) Put(key []byte, b buffer.Buffer) error {
	return s.buffers.Put(key, b)
}

// Pull will retrive (and removes) the buffer stored under "key".
// ErrNotFound indicates that the buffer was not found in the database,
// other errors indicate gob-decoding failures.
func (s *BufferStore) Pull(key []byte) (b buffer.Buffer, err error) {
	err = s.buffers.Pull(key, &b)
	return b, err
}

// DeleteAll will call Reset() on every buffer managed by this store,
// and then delete the buffer from the store.
func (s *BufferStore) DeleteAll() error {
	s.buffers.ForEach(func(b buffer.Buffer) {
		b.Reset()
	})
	return s.buffers.DeleteAll()
}

type BufferPoolStore struct {
	pools *stow.Store
}

func NewBufferPoolStore(db *bolt.DB, bucket []byte) *BufferPoolStore {
	return &BufferPoolStore{pools: stow.NewStore(db, bucket)}
}

func (s *BufferPoolStore) Put(key []byte, p buffer.Pool) error {
	return s.pools.Put(key, p)
}

func (s *BufferPoolStore) Pull(key []byte) (p buffer.Pool, err error) {
	err = s.pools.Pull(key, &p)
	return p, err
}

func (s *BufferPoolStore) DeleteAll() error {
	return s.pools.DeleteAll()
}
<|endoftext|>


<commit_before>package wellington

import (
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/fatih/color"
	libsass "github.com/wellington/go-libsass"
)

var testch chan struct{}

// BuildOptions holds a set of read only arguments to the builder.
// Channels from this are used to communicate between the workers
// and loaders executing builds.
type BuildOptions struct {
	wg      sync.WaitGroup
	closing chan struct{}

	workwg sync.WaitGroup
	err    error
	done   chan error
	status chan error
	queue  chan string

	async      bool
	paths      []string
	bArgs      *BuildArgs
	partialMap *SafePartialMap
}

// NewBuild accepts arguments to reate a new Builder
func NewBuild(paths []string, args *BuildArgs, pMap *SafePartialMap, async bool) *BuildOptions {
	return &BuildOptions{
		done:   make(chan error),
		status: make(chan error),

		queue:   make(chan string),
		closing: make(chan struct{}),

		paths:      paths,
		bArgs:      args,
		partialMap: pMap,
		async:      async,
	}
}

// ErrParitalMap when no partial map is found
var ErrPartialMap = errors.New("No partial map found")

// Build compiles all valid Sass files found in the passed paths.
// It will block until all files are compiled.
func (b *BuildOptions) Build() error {

	if b.partialMap == nil {
		return ErrPartialMap
	}

	b.wg.Add(1)
	go func() {
		defer b.wg.Done()
		b.doBuild()
	}()

	go b.loadWork()

	return <-b.done
}

func (b *BuildOptions) loadWork() {
	paths := pathsToFiles(b.paths, true)
	for _, path := range paths {
		b.queue <- path
	}
	close(b.queue)
}

func (b *BuildOptions) doBuild() {
	for {
		select {
		case <-b.closing:
			return
		case path := <-b.queue:
			if len(path) == 0 {
				b.workwg.Wait()
				b.done <- nil
				return
			}
			b.workwg.Add(1)
			go func(path string) {
				err := b.build(path)
				defer b.workwg.Done()
				if err != nil {
					b.done <- err
				}
			}(path)
		}
	}
}

func (b *BuildOptions) build(path string) error {
	return LoadAndBuild(path, b.bArgs, b.partialMap)
}

// Close shuts down the builder ensuring all go routines have properly
// closed before returning.
func (b *BuildOptions) Close() error {
	close(b.closing)
	b.wg.Wait()
	return nil
}

var inputFileTypes = []string{".scss", ".sass"}

func (b *BuildArgs) getOut(path string) (io.WriteCloser, string, error) {
	var (
		out  io.WriteCloser
		fout string
	)
	if b == nil {
		return nil, "", errors.New("build args is nil")
	}
	if len(b.BuildDir) == 0 {
		out = os.Stdout
		return out, "", nil
	}

	// Build output file based off build directory and input filename
	rel, _ := filepath.Rel(b.Includes, filepath.Dir(path))
	filename := updateFileOutputType(filepath.Base(path))
	fout = filepath.Join(b.BuildDir, rel, filename)

	dir := filepath.Dir(fout)
	err := os.MkdirAll(dir, 0755)
	if err != nil {
		return nil, "", fmt.Errorf("Failed to create directory: %s",
			dir)
	}

	return out, fout, nil
}

// LoadAndBuild kicks off parser and compiling. It expands directories
// to recursively locate Sass files
// TODO: make this function testable
func LoadAndBuild(path string, gba *BuildArgs, pMap *SafePartialMap) error {
	if len(path) == 0 {
		return errors.New("invalid path passed")
	}

	// file detected!
	if isImportable(path) {
		out, fout, err := gba.getOut(path)
		if err != nil {
			return err
		}
		return loadAndBuild(path, gba, pMap, out, fout)
	}

	out, fout, err := gba.getOut(path)
	if err != nil {
		return err
	}
	err = loadAndBuild(path, gba, pMap, out, fout)
	if err != nil {
		return err
	}

	return nil
}

func loadAndBuild(sassFile string, gba *BuildArgs, partialMap *SafePartialMap, out io.WriteCloser, fout string) error {

	defer func() {
		// How can this be removed?
		if out != os.Stdout {
			out.Close()
		}
	}()
	// If no imagedir specified, assume relative to the input file
	if gba.Dir == "" {
		gba.Dir = filepath.Dir(sassFile)
	}

	ctx := libsass.NewContext()
	ctx.Payload = gba.Payload
	ctx.OutputStyle = gba.Style
	ctx.ImageDir = gba.Dir
	ctx.FontDir = gba.Font
	// Assumption that output is a file
	ctx.BuildDir = filepath.Dir(fout)
	ctx.GenImgDir = gba.Gen
	ctx.MainFile = sassFile
	ctx.Comments = gba.Comments
	ctx.IncludePaths = []string{filepath.Dir(sassFile)}

	ctx.Imports.Init()
	if gba.Includes != "" {
		ctx.IncludePaths = append(ctx.IncludePaths,
			strings.Split(gba.Includes, ",")...)
	}
	// TODO: remove this!
	fRead, err := os.Open(sassFile)
	if err != nil {
		return err
	}
	defer fRead.Close()
	if fout != "" {
		out, err = os.Create(fout)
		if err != nil {
			return fmt.Errorf("Failed to create file: %s", sassFile)
		}
	}

	err = ctx.FileCompile(sassFile, out)
	if err != nil {
		return errors.New(color.RedString("%s", err))
	}

	for _, inc := range ctx.ResolvedImports {
		partialMap.AddRelation(ctx.MainFile, inc)
	}

	go func(sassFile string) {
		select {
		case <-testch:
		default:
			fmt.Printf("Rebuilt: %s\n", sassFile)
		}
	}(sassFile)
	return nil
}

func updateFileOutputType(filename string) string {
	for _, filetype := range inputFileTypes {
		filename = strings.Replace(filename, filetype, ".css", 1)
	}
	return filename
}
<commit_msg>fix race conditions with os.Stdout<commit_after>package wellington

import (
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/fatih/color"
	libsass "github.com/wellington/go-libsass"
)

var testch chan struct{}

// BuildOptions holds a set of read only arguments to the builder.
// Channels from this are used to communicate between the workers
// and loaders executing builds.
type BuildOptions struct {
	wg      sync.WaitGroup
	closing chan struct{}

	workwg sync.WaitGroup
	err    error
	done   chan error
	status chan error
	queue  chan string

	async      bool
	paths      []string
	bArgs      *BuildArgs
	partialMap *SafePartialMap
}

// NewBuild accepts arguments to reate a new Builder
func NewBuild(paths []string, args *BuildArgs, pMap *SafePartialMap, async bool) *BuildOptions {
	return &BuildOptions{
		done:   make(chan error),
		status: make(chan error),

		queue:   make(chan string),
		closing: make(chan struct{}),

		paths:      paths,
		bArgs:      args,
		partialMap: pMap,
		async:      async,
	}
}

// ErrParitalMap when no partial map is found
var ErrPartialMap = errors.New("No partial map found")

// Build compiles all valid Sass files found in the passed paths.
// It will block until all files are compiled.
func (b *BuildOptions) Build() error {

	if b.partialMap == nil {
		return ErrPartialMap
	}

	b.wg.Add(1)
	go func() {
		defer b.wg.Done()
		b.doBuild()
	}()

	go b.loadWork()

	return <-b.done
}

func (b *BuildOptions) loadWork() {
	paths := pathsToFiles(b.paths, true)
	for _, path := range paths {
		b.queue <- path
	}
	close(b.queue)
}

func (b *BuildOptions) doBuild() {
	for {
		select {
		case <-b.closing:
			return
		case path := <-b.queue:
			if len(path) == 0 {
				b.workwg.Wait()
				b.done <- nil
				return
			}
			b.workwg.Add(1)
			go func(path string) {
				err := b.build(path)
				defer b.workwg.Done()
				if err != nil {
					b.done <- err
				}
			}(path)
		}
	}
}

func (b *BuildOptions) build(path string) error {
	return LoadAndBuild(path, b.bArgs, b.partialMap)
}

// Close shuts down the builder ensuring all go routines have properly
// closed before returning.
func (b *BuildOptions) Close() error {
	close(b.closing)
	b.wg.Wait()
	return nil
}

var inputFileTypes = []string{".scss", ".sass"}

func (b *BuildArgs) getOut(path string) (io.WriteCloser, string, error) {
	var (
		out  io.WriteCloser
		fout string
	)
	if b == nil {
		return nil, "", errors.New("build args is nil")
	}
	if len(b.BuildDir) == 0 {
		out = os.Stdout
		return out, "", nil
	}

	// Build output file based off build directory and input filename
	rel, _ := filepath.Rel(b.Includes, filepath.Dir(path))
	filename := updateFileOutputType(filepath.Base(path))
	fout = filepath.Join(b.BuildDir, rel, filename)

	dir := filepath.Dir(fout)
	err := os.MkdirAll(dir, 0755)
	if err != nil {
		return nil, "", fmt.Errorf("Failed to create directory: %s",
			dir)
	}

	return out, fout, nil
}

// LoadAndBuild kicks off parser and compiling. It expands directories
// to recursively locate Sass files
// TODO: make this function testable
func LoadAndBuild(path string, gba *BuildArgs, pMap *SafePartialMap) error {
	if len(path) == 0 {
		return errors.New("invalid path passed")
	}

	// file detected!
	if isImportable(path) {
		out, fout, err := gba.getOut(path)
		if err != nil {
			return err
		}
		return loadAndBuild(path, gba, pMap, out, fout)
	}

	out, fout, err := gba.getOut(path)
	if err != nil {
		return err
	}
	err = loadAndBuild(path, gba, pMap, out, fout)
	if err != nil {
		return err
	}

	return nil
}

func loadAndBuild(sassFile string, gba *BuildArgs, partialMap *SafePartialMap, out io.WriteCloser, fout string) error {

	defer func() {
		// Complicated logic to avoid inspecting os.Stdout which would
		// race against testing package
		if closer, ok := out.(*io.PipeWriter); ok {
			fmt.Printf("closing % #v\n", closer)
			closer.Close()
		} else if closer, ok := out.(*os.File); ok {
			closer.Close()
		} else {
			fmt.Printf("failed % #v\n", out)
		}
	}()
	imageDir := gba.Dir
	// If no imagedir specified, assume relative to the input file
	if len(imageDir) == 0 {
		imageDir = filepath.Dir(sassFile)
	}

	ctx := libsass.NewContext()
	ctx.Payload = gba.Payload
	ctx.OutputStyle = gba.Style
	ctx.ImageDir = imageDir
	ctx.FontDir = gba.Font
	// Assumption that output is a file
	ctx.BuildDir = filepath.Dir(fout)
	ctx.GenImgDir = gba.Gen
	ctx.MainFile = sassFile
	ctx.Comments = gba.Comments
	ctx.IncludePaths = []string{filepath.Dir(sassFile)}

	ctx.Imports.Init()
	if gba.Includes != "" {
		ctx.IncludePaths = append(ctx.IncludePaths,
			strings.Split(gba.Includes, ",")...)
	}
	// TODO: remove this!
	fRead, err := os.Open(sassFile)
	if err != nil {
		return err
	}
	defer fRead.Close()
	if fout != "" {
		out, err = os.Create(fout)
		if err != nil {
			return fmt.Errorf("Failed to create file: %s", sassFile)
		}
	}

	err = ctx.FileCompile(sassFile, out)
	if err != nil {
		return errors.New(color.RedString("%s", err))
	}

	for _, inc := range ctx.ResolvedImports {
		partialMap.AddRelation(ctx.MainFile, inc)
	}

	go func(sassFile string) {
		select {
		case <-testch:
		default:
			fmt.Printf("Rebuilt: %s\n", sassFile)
		}
	}(sassFile)
	return nil
}

func updateFileOutputType(filename string) string {
	for _, filetype := range inputFileTypes {
		filename = strings.Replace(filename, filetype, ".css", 1)
	}
	return filename
}
<|endoftext|>


<commit_before>package main

const buildID = "v0.0.5, git+sha: c5e80a3"
<commit_msg>- adding the build tag<commit_after>package main

const buildID = "v0.0.5, git+sha: da94978"
<|endoftext|>