<commit_before>package etcd

import (
	"encoding/json"

	"github.com/coreos/torus"
	"github.com/coreos/torus/models"
	"github.com/coreos/torus/ring"

	etcdv3 "github.com/coreos/etcd/clientv3"
	"golang.org/x/net/context"
)

func initEtcdMetadata(cfg torus.Config, gmd torus.GlobalMetadata, ringType torus.RingType) error {
	gmdbytes, err := json.Marshal(gmd)
	if err != nil {
		return err
	}
	emptyRing, err := ring.CreateRing(&models.Ring{
		Type:              uint32(ringType),
		Version:           1,
		ReplicationFactor: 2,
	})
	if err != nil {
		return err
	}
	ringb, err := emptyRing.Marshal()
	if err != nil {
		return err
	}

	client, err := etcdv3.New(etcdv3.Config{Endpoints: []string{cfg.MetadataAddress}, TLS: cfg.TLS})
	if err != nil {
		return err
	}
	defer client.Close()

	txn := client.Txn(context.Background())
	resp, err := txn.If(
		etcdv3.Compare(etcdv3.Version(MkKey("meta", "globalmetadata")), "=", 0),
	).Then(
		etcdv3.OpPut(MkKey("meta", "volumeminter"), string(Uint64ToBytes(1))),
		etcdv3.OpPut(MkKey("meta", "globalmetadata"), string(gmdbytes)),
	).Commit()
	if err != nil {
		return err
	}
	if !resp.Succeeded {
		return torus.ErrExists
	}
	_, err = client.Put(context.Background(), MkKey("meta", "the-one-ring"), string(ringb))
	if err != nil {
		return err
	}
	return nil
}

func wipeEtcdMetadata(cfg torus.Config) error {
	client, err := etcdv3.New(etcdv3.Config{Endpoints: []string{cfg.MetadataAddress}, TLS: cfg.TLS})
	if err != nil {
		return err
	}
	defer client.Close()
	_, err = client.Delete(context.Background(), MkKey(), etcdv3.WithPrefix())
	if err != nil {
		return err
	}
	return nil
}

func setRing(cfg torus.Config, r torus.Ring) error {
	client, err := etcdv3.New(etcdv3.Config{Endpoints: []string{cfg.MetadataAddress}, TLS: cfg.TLS})
	if err != nil {
		return err
	}
	defer client.Close()

	resp, err := client.Get(context.Background(), MkKey("meta", "the-one-ring"))
	if err != nil {
		return err
	}
	if len(resp.Kvs) == 0 {
		return torus.ErrNoGlobalMetadata
	}
	oldr, err := ring.Unmarshal(resp.Kvs[0].Value)
	if err != nil {
		return err
	}
	if oldr.Version() != r.Version()-1 {
		return torus.ErrNonSequentialRing
	}
	b, err := r.Marshal()
	if err != nil {
		return err
	}
	_, err = client.Put(context.Background(), MkKey("meta", "the-one-ring"), string(b))
	return err
}
<commit_msg>Add trace log to init and wipe ring<commit_after>package etcd

import (
	"encoding/json"

	"github.com/coreos/torus"
	"github.com/coreos/torus/models"
	"github.com/coreos/torus/ring"

	etcdv3 "github.com/coreos/etcd/clientv3"
	"golang.org/x/net/context"
)

func initEtcdMetadata(cfg torus.Config, gmd torus.GlobalMetadata, ringType torus.RingType) error {
	clog.Tracef("Initializing etcd metadata at %v", cfg.MetadataAddress)
	gmdbytes, err := json.Marshal(gmd)
	if err != nil {
		return err
	}
	emptyRing, err := ring.CreateRing(&models.Ring{
		Type:              uint32(ringType),
		Version:           1,
		ReplicationFactor: 2,
	})
	if err != nil {
		return err
	}
	ringb, err := emptyRing.Marshal()
	if err != nil {
		return err
	}

	client, err := etcdv3.New(etcdv3.Config{Endpoints: []string{cfg.MetadataAddress}, TLS: cfg.TLS})
	if err != nil {
		return err
	}
	defer client.Close()

	txn := client.Txn(context.Background())
	resp, err := txn.If(
		etcdv3.Compare(etcdv3.Version(MkKey("meta", "globalmetadata")), "=", 0),
	).Then(
		etcdv3.OpPut(MkKey("meta", "volumeminter"), string(Uint64ToBytes(1))),
		etcdv3.OpPut(MkKey("meta", "globalmetadata"), string(gmdbytes)),
	).Commit()
	if err != nil {
		return err
	}
	if !resp.Succeeded {
		return torus.ErrExists
	}
	_, err = client.Put(context.Background(), MkKey("meta", "the-one-ring"), string(ringb))
	if err != nil {
		return err
	}
	return nil
}

func wipeEtcdMetadata(cfg torus.Config) error {
	clog.Tracef("Wiping etcd metadata at %v", cfg.MetadataAddress)
	client, err := etcdv3.New(etcdv3.Config{Endpoints: []string{cfg.MetadataAddress}, TLS: cfg.TLS})
	if err != nil {
		return err
	}
	defer client.Close()
	_, err = client.Delete(context.Background(), MkKey(), etcdv3.WithPrefix())
	if err != nil {
		return err
	}
	return nil
}

func setRing(cfg torus.Config, r torus.Ring) error {
	clog.Tracef("Setting ring data at %v", cfg.MetadataAddress)
	client, err := etcdv3.New(etcdv3.Config{Endpoints: []string{cfg.MetadataAddress}, TLS: cfg.TLS})
	if err != nil {
		return err
	}
	defer client.Close()

	resp, err := client.Get(context.Background(), MkKey("meta", "the-one-ring"))
	if err != nil {
		return err
	}
	if len(resp.Kvs) == 0 {
		return torus.ErrNoGlobalMetadata
	}
	oldr, err := ring.Unmarshal(resp.Kvs[0].Value)
	if err != nil {
		return err
	}
	if oldr.Version() != r.Version()-1 {
		return torus.ErrNonSequentialRing
	}
	b, err := r.Marshal()
	if err != nil {
		return err
	}
	_, err = client.Put(context.Background(), MkKey("meta", "the-one-ring"), string(b))
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	serial "go.bug.st/serial"
	"encoding/binary"
	"os"
	"fmt"
)

// Commands "enum"
const (
	CommandClose uint8 = iota
)

var openPorts map[uint8]OpenSerialPort
var portClosed chan uint8
var stdoutChannel chan []byte

// Message types
type OpenSerialPort struct {
	commandChannel chan uint8
	writeSerialChannel chan []byte
}

type PortListEvent struct {
	Event string `json:"event"`
	Data []SerialPort `json:"data"`
}

type SerialDataEvent struct {
	Event string `json:"event"`
	Id uint8 `json:"id"`
	Data []byte `json:"data"`
}

type PortOpenEvent struct {
	Event string `json:"event"`
	Id uint8 `json:"id"`
	DevicePath string `json:"devicePath"`
}

type PortClosedEvent struct {
	Event string `json:"event"`
	Id uint8 `json:"id"`
}

type ErrorEvent struct {
	Event string `json:"event"`
	InResponseTo string `json:"inResponseTo"`
	Id int16 `json:"id"`
	Error string `json:"error"`
}

func writeNMToStdout(json string) {
	size := make([]byte, 4)
    binary.LittleEndian.PutUint32(size, uint32(len(json)))

	os.Stdout.Write(size)
	fmt.Print(json)
}

func outputNativeDebugMessage(message string) {
    json := fmt.Sprintf("{\"debug\": \"%s\"}", message)
    writeNMToStdout(json)
}

func outputErrorMessage(inResponseTo []byte, id int16, error string) {
	event := ErrorEvent { Event: "Error", InResponseTo: string(inResponseTo[:]), Id: id, Error: error }
	js, err := json.Marshal(event)
	if err != nil {
		//http.Error(w, err.Error(), http.StatusInternalServerError)
		outputNativeDebugMessage("Internal error")
		return
	}

	writeNMToStdout(string(js[:]))
}

func sendJsonEventNative(daEvent interface{}) {
	js, err := json.Marshal(daEvent)
	if err != nil {
		//http.Error(w, err.Error(), http.StatusInternalServerError)
		outputNativeDebugMessage("Internal error")
		return
	}

	msg := make([]byte, 4)
    binary.LittleEndian.PutUint32(msg, uint32(len(js)))
    msg = append(msg, js...)

    stdoutChannel <- msg
}

func sendDebugMessage(message string) {
    js := fmt.Sprintf("{\"debug\": \"%s\"}", message)
	msg := make([]byte, 4)
    binary.LittleEndian.PutUint32(msg, uint32(len(js)))
    msg = append(msg, js...)
    stdoutChannel <- msg
}

func listCommPorts() {
	reply := PortListEvent{Event: "SerialPorts"}

	ports, _ := GetSerialPortList()

	for _, port := range ports {
		reply.Data = append(reply.Data, port)
	}

	sendJsonEventNative(reply)
}

func connectSerialPort(devicePath string, baudRate int) (bool, string) {
	// TODO: Handle potential panics better
	defer func() {
		if r := recover(); r != nil {
		}
	}()

	if baudRate < 1 {
		baudRate = 9600 // Default to 9600
	}

	outputNativeDebugMessage(fmt.Sprintf("Requesting Serial connection to %v (baudrate:%d)", devicePath, baudRate))

	if len(devicePath) == 0 {
		return false, "Invalid devicePath"
	}

	mode := &serial.Mode{
		BaudRate: baudRate,
		Parity:   serial.PARITY_NONE,
		DataBits: 8,
		StopBits: serial.STOPBITS_ONE,
	}

	port, err := serial.OpenPort(devicePath, mode)
	if err != nil {
		//http.Error(w, err.Error(), http.StatusInternalServerError)
		return false, "Could not open serial port"
	}

	// Find unused id
	id := uint8(0)
	for {
		idAvailable := true
		for key, _ := range openPorts {
			if (key == id) {
				idAvailable = false
				break
			}
		}
		if (idAvailable) {
			break
		} else {
			id++
		}
	}

	outputNativeDebugMessage(fmt.Sprintf("Using id %v", id))

	openPorts[id] = OpenSerialPort{make(chan uint8), make(chan []byte, 5)}

	// Serial I/O routine
	go func() {

		readCommandChannel := make(chan uint8, 1)
		writeCommandChannel := make(chan uint8, 1)

		readDone := make(chan bool)
		writeDone := make(chan bool)

		// Read routine
		go func() {
			for {
				buff := make([]byte, 2048) // 512 bytes is max for high speed USB (so this leaves space for minimum 4 packages)
				n, err := port.Read(buff)
				if err != nil {
					sendJsonEventNative(ErrorEvent { Event: "Error", InResponseTo: "", Id: int16(id), Error: "Error reading from serial port" })
					openPorts[id].commandChannel <- CommandClose
					readDone <- true
					break
				}
				if n == 0 {
					sendJsonEventNative(ErrorEvent { Event: "Error", InResponseTo: "", Id: int16(id), Error: "EOF reading from serial port" })
					openPorts[id].commandChannel <- CommandClose
					readDone <- true
					break
				}
				event := SerialDataEvent{ Event: "data", Id: id, Data: buff[:n]}
				sendJsonEventNative(event)
				select {
				case command := <- readCommandChannel:
					if command == CommandClose {
						readDone <- true
						break
					}
				default:
					// No command, continue with serial input
				}
			}
		}()

		// Write routine
		go func() {
			for {
				select {
				case message := <- openPorts[id].writeSerialChannel:
					_, err = port.Write(message)
					if (err != nil) {
						sendJsonEventNative(ErrorEvent { Event: "Error", InResponseTo: "", Id: int16(id), Error: "Error writing to serial port" })
						openPorts[id].commandChannel <- CommandClose
						writeDone <- true
						break
					}
				case command := <- writeCommandChannel:
					if command == CommandClose {
						writeDone <- true
						break
					}
				}
			}
		}()

		for {
			command := <- openPorts[id].commandChannel
			readCommandChannel <- command
			writeCommandChannel <- command
			if command == CommandClose {
				outputNativeDebugMessage("CommandClose received")
				<- readDone
				<- writeDone
				port.Close()
				sendJsonEventNative(PortClosedEvent{ Event: "PortClosed", Id: id })
				portClosed <- id
				break;
			}
		}
	}()

	sendJsonEventNative(PortOpenEvent{ Event: "PortOpen", Id: id, DevicePath: devicePath })

	return true, ""
}

func handleStdinRead(msgBuffer *[]byte, msgLength *int) {
	for {
		if len(*msgBuffer) >= 4 && *msgLength == -1 {
			// First four bytes are the message length
			*msgLength = int(binary.LittleEndian.Uint32((*msgBuffer)[:4]))
			*msgBuffer = (*msgBuffer)[4:]
		}

		if *msgLength != -1 && len(*msgBuffer) >= *msgLength {
			msg := (*msgBuffer)[:*msgLength]
			*msgBuffer = (*msgBuffer)[*msgLength:]
			*msgLength = -1

			// Message received; decode msg
			var jsonMsg interface{}
			err := json.Unmarshal(msg, &jsonMsg)
			if (err != nil) {
				outputErrorMessage(msg, -1, "Command not valid JSON")
				continue
			}
			command := jsonMsg.(map[string]interface{})

			switch command["command"] {
			case "close":
				id, ok := command["id"].(float64)
				if !ok {
					outputErrorMessage(msg, -1, "Missing id in close command")
					break
				}
				if port, ok := openPorts[uint8(id)]; ok {
					outputNativeDebugMessage(fmt.Sprintf("Closing serial port %v", id))
					port.commandChannel <- CommandClose
				} else {
					outputErrorMessage(msg, int16(id), "No open port with that id")
				}

			case "open":
				devicePath, ok := command["devicePath"].(string)
				if !ok {
					outputErrorMessage(msg, -1, "Missing devicePath!")
					break
				}
				baudRate, ok := command["baudRate"].(float64)
				if !ok {
					outputErrorMessage(msg, -1, "Missing baudRate!")
					break
				}
				outputNativeDebugMessage(fmt.Sprintf("Opening serial port %v", devicePath))
				connected, error := connectSerialPort(devicePath, int(baudRate))
				if !connected {
					outputErrorMessage(msg, -1, error)
				}

			case "listPorts":
				listCommPorts()

			case "write":
				var commandStruct SerialDataEvent
				err := json.Unmarshal(msg, &commandStruct)
				if (err != nil) {
					outputErrorMessage(msg, -1, "Malformed write command")
					break
				}
				if port, ok := openPorts[commandStruct.Id]; ok {
					port.writeSerialChannel <- commandStruct.Data
				} else {
					outputErrorMessage(msg, int16(commandStruct.Id), "No open port with that id")
				}

			default:
				outputErrorMessage(msg, -1, "Command not recognized")
			}
		} else {
			// No more messages to decode
			break;
		}
	}
}

func nativeMsgMainLoop() {
	openPorts = make(map[uint8]OpenSerialPort)
	portClosed = make(chan uint8)

	stdinChannel := make(chan []byte, 5)
	stdoutChannel = make(chan []byte, 100)

	// Set up a channel to read from stdin
	go func() {
		buf := make([]byte, 1000)
		for {
			n, err := os.Stdin.Read(buf);
			if (err != nil) {
				break;
			}
			stdinChannel <- buf[:n]
		}
	}()

	msgLength := -1
	var msgBuffer []byte
	for {
		// Wait for commands on stdin, output on stdout or serial port state change
		select {
			case received := <-stdinChannel:
				msgBuffer = append(msgBuffer, received...)
				handleStdinRead(&msgBuffer, &msgLength)

			case msg := <-stdoutChannel:
				os.Stdout.Write(msg)

			case id := <-portClosed:
				outputNativeDebugMessage(fmt.Sprintf("Serial port closed: %v", id))
				delete(openPorts, id)
		}
	}
}

func main() {
	outputNativeDebugMessage(fmt.Sprintf("The proxy service is serving on stdin/stdout"))
	nativeMsgMainLoop()
}
<commit_msg>Small buffer fix (slightly more stable) + gofmt style applied<commit_after>package main

import (
	"encoding/binary"
	"encoding/json"
	"fmt"
	serial "go.bug.st/serial"
	"os"
)

// Commands "enum"
const (
	CommandClose uint8 = iota
)

var openPorts map[uint8]OpenSerialPort
var portClosed chan uint8
var stdoutChannel chan []byte

// Message types
type OpenSerialPort struct {
	commandChannel     chan uint8
	writeSerialChannel chan []byte
}

type PortListEvent struct {
	Event string       `json:"event"`
	Data  []SerialPort `json:"data"`
}

type SerialDataEvent struct {
	Event string `json:"event"`
	Id    uint8  `json:"id"`
	Data  []byte `json:"data"`
}

type PortOpenEvent struct {
	Event      string `json:"event"`
	Id         uint8  `json:"id"`
	DevicePath string `json:"devicePath"`
}

type PortClosedEvent struct {
	Event string `json:"event"`
	Id    uint8  `json:"id"`
}

type ErrorEvent struct {
	Event        string `json:"event"`
	InResponseTo string `json:"inResponseTo"`
	Id           int16  `json:"id"`
	Error        string `json:"error"`
}

func writeNMToStdout(json string) {
	size := make([]byte, 4)
	binary.LittleEndian.PutUint32(size, uint32(len(json)))

	os.Stdout.Write(size)
	fmt.Print(json)
}

func outputNativeDebugMessage(message string) {
	json := fmt.Sprintf("{\"debug\": \"%s\"}", message)
	writeNMToStdout(json)
}

func outputErrorMessage(inResponseTo []byte, id int16, error string) {
	event := ErrorEvent{Event: "Error", InResponseTo: string(inResponseTo[:]), Id: id, Error: error}
	js, err := json.Marshal(event)
	if err != nil {
		//http.Error(w, err.Error(), http.StatusInternalServerError)
		outputNativeDebugMessage("Internal error")
		return
	}

	writeNMToStdout(string(js[:]))
}

func sendJsonEventNative(daEvent interface{}) {
	js, err := json.Marshal(daEvent)
	if err != nil {
		//http.Error(w, err.Error(), http.StatusInternalServerError)
		outputNativeDebugMessage("Internal error")
		return
	}

	msg := make([]byte, 4)
	binary.LittleEndian.PutUint32(msg, uint32(len(js)))
	msg = append(msg, js...)

	stdoutChannel <- msg
}

func sendDebugMessage(message string) {
	js := fmt.Sprintf("{\"debug\": \"%s\"}", message)
	msg := make([]byte, 4)
	binary.LittleEndian.PutUint32(msg, uint32(len(js)))
	msg = append(msg, js...)
	stdoutChannel <- msg
}

func listCommPorts() {
	reply := PortListEvent{Event: "SerialPorts"}

	ports, _ := GetSerialPortList()

	for _, port := range ports {
		reply.Data = append(reply.Data, port)
	}

	sendJsonEventNative(reply)
}

func connectSerialPort(devicePath string, baudRate int) (bool, string) {
	// TODO: Handle potential panics better
	defer func() {
		if r := recover(); r != nil {
		}
	}()

	if baudRate < 1 {
		baudRate = 9600 // Default to 9600
	}

	outputNativeDebugMessage(fmt.Sprintf("Requesting Serial connection to %v (baudrate:%d)", devicePath, baudRate))

	if len(devicePath) == 0 {
		return false, "Invalid devicePath"
	}

	mode := &serial.Mode{
		BaudRate: baudRate,
		Parity:   serial.PARITY_NONE,
		DataBits: 8,
		StopBits: serial.STOPBITS_ONE,
	}

	port, err := serial.OpenPort(devicePath, mode)
	if err != nil {
		//http.Error(w, err.Error(), http.StatusInternalServerError)
		return false, "Could not open serial port"
	}

	// Find unused id
	id := uint8(0)
	for {
		idAvailable := true
		for key := range openPorts {
			if key == id {
				idAvailable = false
				break
			}
		}
		if idAvailable {
			break
		} else {
			id++
		}
	}

	outputNativeDebugMessage(fmt.Sprintf("Using id %v", id))

	openPorts[id] = OpenSerialPort{make(chan uint8), make(chan []byte, 5)}

	// Serial I/O routine
	go func() {

		readCommandChannel := make(chan uint8, 1)
		writeCommandChannel := make(chan uint8, 1)

		readDone := make(chan bool)
		writeDone := make(chan bool)

		// Read routine
		go func() {
			for {
				buff := make([]byte, 2048) // 512 bytes is max for high speed USB (so this leaves space for minimum 4 packages)
				n, err := port.Read(buff)
				if err != nil {
					sendJsonEventNative(ErrorEvent{Event: "Error", InResponseTo: "", Id: int16(id), Error: "Error reading from serial port"})
					openPorts[id].commandChannel <- CommandClose
					readDone <- true
					break
				}
				if n == 0 {
					sendJsonEventNative(ErrorEvent{Event: "Error", InResponseTo: "", Id: int16(id), Error: "EOF reading from serial port"})
					openPorts[id].commandChannel <- CommandClose
					readDone <- true
					break
				}
				event := SerialDataEvent{Event: "data", Id: id, Data: buff[:n]}
				sendJsonEventNative(event)
				select {
				case command := <-readCommandChannel:
					if command == CommandClose {
						readDone <- true
						break
					}
				default:
					// No command, continue with serial input
				}
			}
		}()

		// Write routine
		go func() {
			for {
				select {
				case message := <-openPorts[id].writeSerialChannel:
					_, err = port.Write(message)
					if err != nil {
						sendJsonEventNative(ErrorEvent{Event: "Error", InResponseTo: "", Id: int16(id), Error: "Error writing to serial port"})
						openPorts[id].commandChannel <- CommandClose
						writeDone <- true
						break
					}
				case command := <-writeCommandChannel:
					if command == CommandClose {
						writeDone <- true
						break
					}
				}
			}
		}()

		for {
			command := <-openPorts[id].commandChannel
			readCommandChannel <- command
			writeCommandChannel <- command
			if command == CommandClose {
				outputNativeDebugMessage("CommandClose received")
				<-readDone
				<-writeDone
				port.Close()
				sendJsonEventNative(PortClosedEvent{Event: "PortClosed", Id: id})
				portClosed <- id
				break
			}
		}
	}()

	sendJsonEventNative(PortOpenEvent{Event: "PortOpen", Id: id, DevicePath: devicePath})

	return true, ""
}

func handleStdinRead(msgBuffer *[]byte, msgLength *int) {
	for {
		if len(*msgBuffer) >= 4 && *msgLength == -1 {
			// First four bytes are the message length
			*msgLength = int(binary.LittleEndian.Uint32((*msgBuffer)[:4]))
			*msgBuffer = (*msgBuffer)[4:]
		}

		if *msgLength != -1 && len(*msgBuffer) >= *msgLength {
			msg := (*msgBuffer)[:*msgLength]
			*msgBuffer = (*msgBuffer)[*msgLength:]
			*msgLength = -1

			// Message received; decode msg
			var jsonMsg interface{}
			err := json.Unmarshal(msg, &jsonMsg)
			if err != nil {
				outputErrorMessage(msg, -1, "Command not valid JSON")
				continue
			}
			command := jsonMsg.(map[string]interface{})

			switch command["command"] {
			case "close":
				id, ok := command["id"].(float64)
				if !ok {
					outputErrorMessage(msg, -1, "Missing id in close command")
					break
				}
				if port, ok := openPorts[uint8(id)]; ok {
					outputNativeDebugMessage(fmt.Sprintf("Closing serial port %v", id))
					port.commandChannel <- CommandClose
				} else {
					outputErrorMessage(msg, int16(id), "No open port with that id")
				}

			case "open":
				devicePath, ok := command["devicePath"].(string)
				if !ok {
					outputErrorMessage(msg, -1, "Missing devicePath!")
					break
				}
				baudRate, ok := command["baudRate"].(float64)
				if !ok {
					outputErrorMessage(msg, -1, "Missing baudRate!")
					break
				}
				outputNativeDebugMessage(fmt.Sprintf("Opening serial port %v", devicePath))
				connected, error := connectSerialPort(devicePath, int(baudRate))
				if !connected {
					outputErrorMessage(msg, -1, error)
				}

			case "listPorts":
				listCommPorts()

			case "write":
				var commandStruct SerialDataEvent
				err := json.Unmarshal(msg, &commandStruct)
				if err != nil {
					outputErrorMessage(msg, -1, "Malformed write command")
					break
				}
				if port, ok := openPorts[commandStruct.Id]; ok {
					port.writeSerialChannel <- commandStruct.Data
				} else {
					outputErrorMessage(msg, int16(commandStruct.Id), "No open port with that id")
				}

			default:
				outputErrorMessage(msg, -1, "Command not recognized")
			}
		} else {
			// No more messages to decode
			break
		}
	}
}

func nativeMsgMainLoop() {
	openPorts = make(map[uint8]OpenSerialPort)
	portClosed = make(chan uint8)

	stdinChannel := make(chan []byte, 5)
	stdoutChannel = make(chan []byte, 100)

	// Set up a channel to read from stdin
	go func() {
		for {
			buf := make([]byte, 1000)
			n, err := os.Stdin.Read(buf)
			if err != nil {
				break
			}
			stdinChannel <- buf[:n]
		}
	}()

	msgLength := -1
	var msgBuffer []byte
	for {
		// Wait for commands on stdin, output on stdout or serial port state change
		select {
		case received := <-stdinChannel:
			msgBuffer = append(msgBuffer, received...)
			handleStdinRead(&msgBuffer, &msgLength)

		case msg := <-stdoutChannel:
			os.Stdout.Write(msg)

		case id := <-portClosed:
			outputNativeDebugMessage(fmt.Sprintf("Serial port closed: %v", id))
			delete(openPorts, id)
		}
	}
}

func main() {
	outputNativeDebugMessage(fmt.Sprintf("The proxy service is serving on stdin/stdout"))
	nativeMsgMainLoop()
}
<|endoftext|>


<commit_before>package nbfx

import (
	//"bytes"
	"encoding/xml"
	"errors"
	"fmt"
	"io"
	"math"
)

type encoder struct {
	codec codec
}

func NewEncoder() Encoder {
	return NewEncoderWithStrings(nil)
}

func NewEncoderWithStrings(dictionaryStrings map[uint32]string) Encoder {
	encoder := &encoder{codec{make(map[uint32]string), make(map[string]uint32)}}
	if dictionaryStrings != nil {
		for k, v := range dictionaryStrings {
			encoder.codec.addDictionaryString(k, v)
		}
	}
	return encoder
}

func (e *encoder) Encode(xmlString string) ([]byte, error) {
	//reader := bytes.NewReader([]byte(xmlString))
	//binBuffer := &bytes.Buffer{}
	//xmlDecoder := xml.NewDecoder(reader)
	//token, err := xmlDecoder.RawToken()
	//for err == nil {
	//	record := getRecordFromToken(&e.codec, token)
	//	if record == nil {
	//		return binBuffer.Bytes(), errors.New(fmt.Sprintf("Unknown Token %s", token))
	//	}
	//	err = record.writeElement(binBuffer)
	//	if err != nil {
	//		return binBuffer.Bytes(), errors.New(fmt.Sprintf("Error writing Token %s :: %s", token, err.Error()))
	//	}
	//	token, err = xmlDecoder.RawToken()
	//}
	//return binBuffer.Bytes(), nil
	return nil, errors.New("Restore this")
}

func getRecordFromToken(codec *codec, token xml.Token) record {
	switch token.(type) {
	case xml.StartElement:
		return getStartElementRecordFromToken(codec, token.(xml.StartElement))
	}

	return nil
}

func getStartElementRecordFromToken(codec *codec, startElement xml.StartElement) record {
	//fmt.Printf("Getting start element for %s", startElement.Name.Local)
	prefix := startElement.Name.Space
	name := startElement.Name.Local
	prefixIndex := -1
	if len(prefix) == 1 && byte(prefix[0]) >= byte('a') && byte(prefix[0]) <= byte('z') {
		prefixIndex = int(byte(prefix[0]) - byte('a'))
	}
	var nameIndex uint32
	isNameIndexAssigned := false
	if i, ok := codec.reverseDict[name]; ok {
		nameIndex = i
		isNameIndexAssigned = true
	}

	if prefix == "" {
		if !isNameIndexAssigned {
			return &shortElementRecord{name: name}
		} else {
			return &dictionaryElementRecord{nameIndex: nameIndex}
		}
	} else if prefixIndex != -1 {
		if !isNameIndexAssigned {
			return &prefixElementAZRecord{prefixIndex: byte(prefixIndex), name: name}
		} else {
			return &prefixDictionaryElementAZRecord{prefixIndex: byte(prefixIndex), nameIndex: nameIndex}
		}
	} else {
		if !isNameIndexAssigned {
			return &elementRecord{prefix: prefix, name: name}
		} else {
			return &dictionaryElementRecord{prefix: prefix, nameIndex: nameIndex}
		}
	}
}

func writeString(writer io.Writer, str string) (int, error) {
	var strBytes = []byte(str)
	lenByteLen, err := writeMultiByteInt31(writer, uint32(len(strBytes)))
	if err != nil {
		return lenByteLen, err
	}
	strByteLen, err := writer.Write(strBytes)
	return lenByteLen + strByteLen, err
}

func writeMultiByteInt31(writer io.Writer, num uint32) (int, error) {
	max := uint32(2147483647)
	if num > max {
		return 0, errors.New(fmt.Sprintf("Overflow: i (%d) must be <= max (%d)", num, max))
	}
	buf := new([5]byte)
	val := num
	i := 4
	lastByte := 0
	for ; i >= 0; i-- {
		var base uint32
		if i > 0 {
			base = uint32(math.Pow(128, float64(i)))
		} else {
			base = 0
		}
		digit := byte(0x00)
		if val >= base {
			if base > 0 {
				digit = byte(math.Floor(float64(val / base)))
				val -= uint32(digit) * base
			} else {
				digit = byte(val)
			}
		}
		buf[i] = digit
	}

	haveLastByte := false
	for j := len(buf) - 1; j >= 0; j-- {
		if !haveLastByte && buf[j] > 0x00 {
			haveLastByte = true
			lastByte = j
		} else if haveLastByte {
			buf[j] = buf[j] + MASK_MBI31
		}
	}

	return writer.Write(buf[0 : lastByte+1])
}
<commit_msg>writeMultiByteInt31 recursively<commit_after>package nbfx

import (
	"encoding/xml"
	"errors"
	"fmt"
	"io"
)

type encoder struct {
	codec codec
}

func NewEncoder() Encoder {
	return NewEncoderWithStrings(nil)
}

func NewEncoderWithStrings(dictionaryStrings map[uint32]string) Encoder {
	encoder := &encoder{codec{make(map[uint32]string), make(map[string]uint32)}}
	if dictionaryStrings != nil {
		for k, v := range dictionaryStrings {
			encoder.codec.addDictionaryString(k, v)
		}
	}
	return encoder
}

func (e *encoder) Encode(xmlString string) ([]byte, error) {
	//reader := bytes.NewReader([]byte(xmlString))
	//binBuffer := &bytes.Buffer{}
	//xmlDecoder := xml.NewDecoder(reader)
	//token, err := xmlDecoder.RawToken()
	//for err == nil {
	//	record := getRecordFromToken(&e.codec, token)
	//	if record == nil {
	//		return binBuffer.Bytes(), errors.New(fmt.Sprintf("Unknown Token %s", token))
	//	}
	//	err = record.writeElement(binBuffer)
	//	if err != nil {
	//		return binBuffer.Bytes(), errors.New(fmt.Sprintf("Error writing Token %s :: %s", token, err.Error()))
	//	}
	//	token, err = xmlDecoder.RawToken()
	//}
	//return binBuffer.Bytes(), nil
	return nil, errors.New("Restore this")
}

func getRecordFromToken(codec *codec, token xml.Token) record {
	switch token.(type) {
	case xml.StartElement:
		return getStartElementRecordFromToken(codec, token.(xml.StartElement))
	}

	return nil
}

func getStartElementRecordFromToken(codec *codec, startElement xml.StartElement) record {
	//fmt.Printf("Getting start element for %s", startElement.Name.Local)
	prefix := startElement.Name.Space
	name := startElement.Name.Local
	prefixIndex := -1
	if len(prefix) == 1 && byte(prefix[0]) >= byte('a') && byte(prefix[0]) <= byte('z') {
		prefixIndex = int(byte(prefix[0]) - byte('a'))
	}
	var nameIndex uint32
	isNameIndexAssigned := false
	if i, ok := codec.reverseDict[name]; ok {
		nameIndex = i
		isNameIndexAssigned = true
	}

	if prefix == "" {
		if !isNameIndexAssigned {
			return &shortElementRecord{name: name}
		} else {
			return &dictionaryElementRecord{nameIndex: nameIndex}
		}
	} else if prefixIndex != -1 {
		if !isNameIndexAssigned {
			return &prefixElementAZRecord{prefixIndex: byte(prefixIndex), name: name}
		} else {
			return &prefixDictionaryElementAZRecord{prefixIndex: byte(prefixIndex), nameIndex: nameIndex}
		}
	} else {
		if !isNameIndexAssigned {
			return &elementRecord{prefix: prefix, name: name}
		} else {
			return &dictionaryElementRecord{prefix: prefix, nameIndex: nameIndex}
		}
	}
}

func writeString(writer io.Writer, str string) (int, error) {
	var strBytes = []byte(str)
	lenByteLen, err := writeMultiByteInt31(writer, uint32(len(strBytes)))
	if err != nil {
		return lenByteLen, err
	}
	strByteLen, err := writer.Write(strBytes)
	return lenByteLen + strByteLen, err
}

func writeMultiByteInt31(writer io.Writer, num uint32) (int, error) {
	max := uint32(2147483647)
	if num > max {
		return 0, errors.New(fmt.Sprintf("Overflow: i (%d) must be <= max (%d)", num, max))
	}
	if num < 128 {
		return writer.Write([]byte{byte(num)})
	}
	q := num / 128
	rem := num % 128
	n1, err := writer.Write([]byte{byte(128 + rem)})
	if err != nil {
		return n1, err
	}
	n2, err := writeMultiByteInt31(writer, q)
	return n1 + n2, err
}
<|endoftext|>


<commit_before>// This source file is part of the PG-DHCP project.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package dhcp

import (
	"net"
	"testing"
	"time"

	"github.com/lfkeitel/verbose"
	d4 "github.com/onesimus-systems/dhcp4"

	"github.com/lfkeitel/verbose"
)

// TestGiveLeaseFromMultiplePools is targeted at the Network.getFreeLease()
// method. This test ensures that if a subnet has multiple pools and the first
// is already filled with claimed leases (not necessarily active leases), that
// it will go to the next pool in the subnet and get a lease from there.
// This test uses network3 in the test config and uses IP range 10.0.8.0/24
// with only an unregistered block.
func TestGiveLeaseFromMultiplePools(t *testing.T) {
	ds := &testDeviceStore{}
	sc := &ServerConfig{
		LeaseStore:  &testLeaseStore{},
		DeviceStore: ds,
		Env:         EnvTesting,
		LogPath:     "",
		Log:         verbose.New(""),
	}

	// Setup Configuration
	c, err := ParseFile("./testdata/testConfig.conf")
	if err != nil {
		t.Fatalf("Test config failed parsing: %v", err)
	}

	network := c.networks["network3"]

	pool := network.subnets[0].pools[0]
	// Expire all leases, make one claimed
	for i := 0; i < pool.getCountOfIPs(); i++ {
		lease := pool.getFreeLease(sc)
		if lease == nil {
			t.Fatal("Pool returned nil lease")
		}
		lease.End = time.Now().Add(time.Duration(3610) * time.Second)
	}

	for _, l := range pool.leases {
		l.End = time.Now().Add(time.Duration(-1*c.global.unregisteredSettings.freeLeaseAfter) *
			time.Second).Add(time.Duration(300) * time.Second)
	}

	l := pool.leases["10.0.8.90"]
	l.End = time.Now().Add(time.Duration(-1*c.global.unregisteredSettings.freeLeaseAfter) * time.Second).Add(time.Duration(240) * time.Second)

	server := NewDHCPServer(c, sc)
	mac, _ := net.ParseMAC("12:34:56:12:34:56")

	unregTestDevice := &testDevice{
		store:      ds,
		registered: false,
		mac:        mac,
	}

	opts := []d4.Option{
		d4.Option{
			Code:  d4.OptionParameterRequestList,
			Value: []byte{0x1, 0x3, 0x6, 0xf, 0x23},
		},
	}
	p := d4.RequestPacket(d4.Discover, mac, nil, nil, false, opts)
	p.SetGIAddr(net.ParseIP("10.0.8.5"))

	// Process a DISCOVER request
	ds.setNextDevice(unregTestDevice)
	dp := server.ServeDHCP(p, d4.Discover, p.ParseOptions())
	if dp == nil {
		t.Fatal("Processed packet is nil")
	}

	checkIP(dp, []byte{0xa, 0x0, 0x8, 0x78}, t)
}
<commit_msg>Remove duplicate import<commit_after>// This source file is part of the PG-DHCP project.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package dhcp

import (
	"net"
	"testing"
	"time"

	"github.com/lfkeitel/verbose"
	d4 "github.com/onesimus-systems/dhcp4"
)

// TestGiveLeaseFromMultiplePools is targeted at the Network.getFreeLease()
// method. This test ensures that if a subnet has multiple pools and the first
// is already filled with claimed leases (not necessarily active leases), that
// it will go to the next pool in the subnet and get a lease from there.
// This test uses network3 in the test config and uses IP range 10.0.8.0/24
// with only an unregistered block.
func TestGiveLeaseFromMultiplePools(t *testing.T) {
	ds := &testDeviceStore{}
	sc := &ServerConfig{
		LeaseStore:  &testLeaseStore{},
		DeviceStore: ds,
		Env:         EnvTesting,
		LogPath:     "",
		Log:         verbose.New(""),
	}

	// Setup Configuration
	c, err := ParseFile("./testdata/testConfig.conf")
	if err != nil {
		t.Fatalf("Test config failed parsing: %v", err)
	}

	network := c.networks["network3"]

	pool := network.subnets[0].pools[0]
	// Expire all leases, make one claimed
	for i := 0; i < pool.getCountOfIPs(); i++ {
		lease := pool.getFreeLease(sc)
		if lease == nil {
			t.Fatal("Pool returned nil lease")
		}
		lease.End = time.Now().Add(time.Duration(3610) * time.Second)
	}

	for _, l := range pool.leases {
		l.End = time.Now().Add(time.Duration(-1*c.global.unregisteredSettings.freeLeaseAfter) *
			time.Second).Add(time.Duration(300) * time.Second)
	}

	l := pool.leases["10.0.8.90"]
	l.End = time.Now().Add(time.Duration(-1*c.global.unregisteredSettings.freeLeaseAfter) * time.Second).Add(time.Duration(240) * time.Second)

	server := NewDHCPServer(c, sc)
	mac, _ := net.ParseMAC("12:34:56:12:34:56")

	unregTestDevice := &testDevice{
		store:      ds,
		registered: false,
		mac:        mac,
	}

	opts := []d4.Option{
		d4.Option{
			Code:  d4.OptionParameterRequestList,
			Value: []byte{0x1, 0x3, 0x6, 0xf, 0x23},
		},
	}
	p := d4.RequestPacket(d4.Discover, mac, nil, nil, false, opts)
	p.SetGIAddr(net.ParseIP("10.0.8.5"))

	// Process a DISCOVER request
	ds.setNextDevice(unregTestDevice)
	dp := server.ServeDHCP(p, d4.Discover, p.ParseOptions())
	if dp == nil {
		t.Fatal("Processed packet is nil")
	}

	checkIP(dp, []byte{0xa, 0x0, 0x8, 0x78}, t)
}
<|endoftext|>


<commit_before>package apns

import (
	"bytes"
	"encoding/binary"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"time"
)

const (
	PriorityImmediate     = 10
	PriorityPowerConserve = 5
)

const (
	commandID = 2

	// Items IDs
	deviceTokenItemID            = 1
	payloadItemID                = 2
	notificationIdentifierItemID = 3
	expirationDateItemID         = 4
	priorityItemID               = 5

	// Item lengths
	deviceTokenItemLength            = 32
	notificationIdentifierItemLength = 4
	expirationDateItemLength         = 4
	priorityItemLength               = 1
)

type NotificationResult struct {
	Notif Notification
	Err   Error
}

type Alert struct {
	// Do not add fields without updating the implemenation of isZero.
	Body         string   `json:"body,omitempty"`
	LocKey       string   `json:"loc-key,omitempty"`
	LocArgs      []string `json:"loc-args,omitempty"`
	ActionLocKey string   `json:"action-loc-key,omitempty"`
	LaunchImage  string   `json:"launch-image,omitempty"`
}

func (a *Alert) isZero() bool {
	return len(a.Body)+len(a.LocKey)+len(a.LocArgs)+len(a.ActionLocKey)+len(a.LaunchImage) == 0
}

type APS struct {
	Alert            Alert
	Badge            *int
	Sound            string
	ContentAvailable int
}

func (aps APS) MarshalJSON() ([]byte, error) {
	data := make(map[string]interface{})

	if !aps.Alert.isZero() {
		data["alert"] = aps.Alert
	}
	if aps.Badge != nil {
		data["badge"] = aps.Badge
	}
	if aps.Sound != "" {
		data["sound"] = aps.Sound
	}
	if aps.ContentAvailable != 0 {
		data["content-available"] = aps.ContentAvailable
	}

	return json.Marshal(data)
}

type Payload struct {
	APS          APS
	customValues map[string]interface{}
}

type Notification struct {
	ID          string
	DeviceToken string
	Identifier  uint32
	Expiration  *time.Time
	Priority    int
	Payload     *Payload
}

func NewNotification() Notification {
	return Notification{Payload: NewPayload()}
}

func NewPayload() *Payload {
	return &Payload{customValues: map[string]interface{}{}}
}

func (p *Payload) SetCustomValue(key string, value interface{}) error {
	if key == "aps" {
		return errors.New("cannot assign a custom APS value in payload")
	}

	p.customValues[key] = value

	return nil
}

func (p *Payload) MarshalJSON() ([]byte, error) {
	p.customValues["aps"] = p.APS

	return json.Marshal(p.customValues)
}

func (n Notification) ToBinary() ([]byte, error) {
	b := []byte{}

	binTok, err := hex.DecodeString(n.DeviceToken)
	if err != nil {
		return b, fmt.Errorf("convert token to hex error: %s", err)
	}

	j, _ := json.Marshal(n.Payload)

	buf := bytes.NewBuffer(b)

	// Token
	binary.Write(buf, binary.BigEndian, uint8(deviceTokenItemID))
	binary.Write(buf, binary.BigEndian, uint16(deviceTokenItemLength))
	binary.Write(buf, binary.BigEndian, binTok)

	// Payload
	binary.Write(buf, binary.BigEndian, uint8(payloadItemID))
	binary.Write(buf, binary.BigEndian, uint16(len(j)))
	binary.Write(buf, binary.BigEndian, j)

	// Identifier
	binary.Write(buf, binary.BigEndian, uint8(notificationIdentifierItemID))
	binary.Write(buf, binary.BigEndian, uint16(notificationIdentifierItemLength))
	binary.Write(buf, binary.BigEndian, uint32(n.Identifier))

	// Expiry
	binary.Write(buf, binary.BigEndian, uint8(expirationDateItemID))
	binary.Write(buf, binary.BigEndian, uint16(expirationDateItemLength))
	if n.Expiration == nil {
		binary.Write(buf, binary.BigEndian, uint32(0))
	} else {
		binary.Write(buf, binary.BigEndian, uint32(n.Expiration.Unix()))
	}

	// Priority
	binary.Write(buf, binary.BigEndian, uint8(priorityItemID))
	binary.Write(buf, binary.BigEndian, uint16(priorityItemLength))
	binary.Write(buf, binary.BigEndian, uint8(n.Priority))

	framebuf := bytes.NewBuffer([]byte{})
	binary.Write(framebuf, binary.BigEndian, uint8(commandID))
	binary.Write(framebuf, binary.BigEndian, uint32(buf.Len()))
	binary.Write(framebuf, binary.BigEndian, buf.Bytes())

	return framebuf.Bytes(), nil
}
<commit_msg>notification: shuffle code around<commit_after>package apns

import (
	"bytes"
	"encoding/binary"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"time"
)

const (
	PriorityImmediate     = 10
	PriorityPowerConserve = 5
)

const (
	commandID = 2

	// Items IDs
	deviceTokenItemID            = 1
	payloadItemID                = 2
	notificationIdentifierItemID = 3
	expirationDateItemID         = 4
	priorityItemID               = 5

	// Item lengths
	deviceTokenItemLength            = 32
	notificationIdentifierItemLength = 4
	expirationDateItemLength         = 4
	priorityItemLength               = 1
)

type NotificationResult struct {
	Notif Notification
	Err   Error
}

type Alert struct {
	// Do not add fields without updating the implementation of isZero.
	Body         string   `json:"body,omitempty"`
	LocKey       string   `json:"loc-key,omitempty"`
	LocArgs      []string `json:"loc-args,omitempty"`
	ActionLocKey string   `json:"action-loc-key,omitempty"`
	LaunchImage  string   `json:"launch-image,omitempty"`
}

func (a *Alert) isZero() bool {
	return len(a.Body) == 0 && len(a.LocKey) == 0 && len(a.LocArgs) == 0 && len(a.ActionLocKey) == 0 && len(a.LaunchImage) == 0
}

type APS struct {
	Alert            Alert
	Badge            *int // 0 to clear notifications, nil to leave as is.
	Sound            string
	ContentAvailable int
}

func (aps APS) MarshalJSON() ([]byte, error) {
	data := make(map[string]interface{})

	if !aps.Alert.isZero() {
		data["alert"] = aps.Alert
	}
	if aps.Badge != nil {
		data["badge"] = aps.Badge
	}
	if aps.Sound != "" {
		data["sound"] = aps.Sound
	}
	if aps.ContentAvailable != 0 {
		data["content-available"] = aps.ContentAvailable
	}

	return json.Marshal(data)
}

type Payload struct {
	APS          APS
	customValues map[string]interface{}
}

func (p *Payload) MarshalJSON() ([]byte, error) {
	p.customValues["aps"] = p.APS

	return json.Marshal(p.customValues)
}

func (p *Payload) SetCustomValue(key string, value interface{}) error {
	if key == "aps" {
		return errors.New("cannot assign a custom APS value in payload")
	}

	p.customValues[key] = value

	return nil
}

type Notification struct {
	ID          string
	DeviceToken string
	Identifier  uint32
	Expiration  *time.Time
	Priority    int
	Payload     *Payload
}

func NewNotification() Notification {
	return Notification{Payload: NewPayload()}
}

func NewPayload() *Payload {
	return &Payload{customValues: map[string]interface{}{}}
}

func (n Notification) ToBinary() ([]byte, error) {
	b := []byte{}

	binTok, err := hex.DecodeString(n.DeviceToken)
	if err != nil {
		return b, fmt.Errorf("convert token to hex error: %s", err)
	}

	j, _ := json.Marshal(n.Payload)

	buf := bytes.NewBuffer(b)

	// Token
	binary.Write(buf, binary.BigEndian, uint8(deviceTokenItemID))
	binary.Write(buf, binary.BigEndian, uint16(deviceTokenItemLength))
	binary.Write(buf, binary.BigEndian, binTok)

	// Payload
	binary.Write(buf, binary.BigEndian, uint8(payloadItemID))
	binary.Write(buf, binary.BigEndian, uint16(len(j)))
	binary.Write(buf, binary.BigEndian, j)

	// Identifier
	binary.Write(buf, binary.BigEndian, uint8(notificationIdentifierItemID))
	binary.Write(buf, binary.BigEndian, uint16(notificationIdentifierItemLength))
	binary.Write(buf, binary.BigEndian, uint32(n.Identifier))

	// Expiry
	binary.Write(buf, binary.BigEndian, uint8(expirationDateItemID))
	binary.Write(buf, binary.BigEndian, uint16(expirationDateItemLength))
	if n.Expiration == nil {
		binary.Write(buf, binary.BigEndian, uint32(0))
	} else {
		binary.Write(buf, binary.BigEndian, uint32(n.Expiration.Unix()))
	}

	// Priority
	binary.Write(buf, binary.BigEndian, uint8(priorityItemID))
	binary.Write(buf, binary.BigEndian, uint16(priorityItemLength))
	binary.Write(buf, binary.BigEndian, uint8(n.Priority))

	framebuf := bytes.NewBuffer([]byte{})
	binary.Write(framebuf, binary.BigEndian, uint8(commandID))
	binary.Write(framebuf, binary.BigEndian, uint32(buf.Len()))
	binary.Write(framebuf, binary.BigEndian, buf.Bytes())

	return framebuf.Bytes(), nil
}
<|endoftext|>


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

//go:build windows
// +build windows

package sqlserverreceiver

import (
	"context"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.opentelemetry.io/collector/component/componenttest"
	"go.uber.org/zap"
	"go.uber.org/zap/zaptest/observer"
)

func TestSqlServerScraper(t *testing.T) {
	factory := NewFactory()
	cfg := factory.CreateDefaultConfig().(*Config)
	logger, obsLogs := observer.New(zap.WarnLevel)
	settings := componenttest.NewNopReceiverCreateSettings()
	settings.Logger = logger
	s := newSqlServerScraper(settings, cfg)

	s.start(context.Background(), nil)
	assert.Equal(t, 0, len(s.watcherRecorders))
	assert.Equal(t, 21, obsLogs.Len())
	assert.Equal(t, 21, obsLogs.FilterMessageSnippet("failed to create perf counter with path \\SQLServer:").Len())
	assert.Equal(t, 21, obsLogs.FilterMessageSnippet("The specified object was not found on the computer.").Len())
	assert.Equal(t, 1, obsLogs.FilterMessageSnippet("\\SQLServer:General Statistics\\").Len())
	assert.Equal(t, 3, obsLogs.FilterMessageSnippet("\\SQLServer:SQL Statistics\\").Len())
	assert.Equal(t, 2, obsLogs.FilterMessageSnippet("\\SQLServer:Locks(_Total)\\").Len())
	assert.Equal(t, 6, obsLogs.FilterMessageSnippet("\\SQLServer:Buffer Manager\\").Len())
	assert.Equal(t, 1, obsLogs.FilterMessageSnippet("\\SQLServer:Access Methods(_Total)\\").Len())
	assert.Equal(t, 8, obsLogs.FilterMessageSnippet("\\SQLServer:Databases(*)\\").Len())

	metrics, err := s.scrape(context.Background())
	require.NoError(t, err)
	assert.Equal(t, 0, metrics.ResourceMetrics().Len())

	err = s.shutdown(context.Background())
	require.NoError(t, err)
}
<commit_msg>[chore] fix broken test (#10164)<commit_after>// Copyright 2020, OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//go:build windows
// +build windows

package sqlserverreceiver

import (
	"context"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.opentelemetry.io/collector/component/componenttest"
	"go.uber.org/zap"
	"go.uber.org/zap/zaptest/observer"
)

func TestSqlServerScraper(t *testing.T) {
	factory := NewFactory()
	cfg := factory.CreateDefaultConfig().(*Config)
	logger, obsLogs := observer.New(zap.WarnLevel)
	settings := componenttest.NewNopReceiverCreateSettings()
	settings.Logger = zap.New(logger)
	s := newSqlServerScraper(settings, cfg)

	s.start(context.Background(), nil)
	assert.Equal(t, 0, len(s.watcherRecorders))
	assert.Equal(t, 21, obsLogs.Len())
	assert.Equal(t, 21, obsLogs.FilterMessageSnippet("failed to create perf counter with path \\SQLServer:").Len())
	assert.Equal(t, 21, obsLogs.FilterMessageSnippet("The specified object was not found on the computer.").Len())
	assert.Equal(t, 1, obsLogs.FilterMessageSnippet("\\SQLServer:General Statistics\\").Len())
	assert.Equal(t, 3, obsLogs.FilterMessageSnippet("\\SQLServer:SQL Statistics\\").Len())
	assert.Equal(t, 2, obsLogs.FilterMessageSnippet("\\SQLServer:Locks(_Total)\\").Len())
	assert.Equal(t, 6, obsLogs.FilterMessageSnippet("\\SQLServer:Buffer Manager\\").Len())
	assert.Equal(t, 1, obsLogs.FilterMessageSnippet("\\SQLServer:Access Methods(_Total)\\").Len())
	assert.Equal(t, 8, obsLogs.FilterMessageSnippet("\\SQLServer:Databases(*)\\").Len())

	metrics, err := s.scrape(context.Background())
	require.NoError(t, err)
	assert.Equal(t, 0, metrics.ResourceMetrics().Len())

	err = s.shutdown(context.Background())
	require.NoError(t, err)
}
<|endoftext|>


<commit_before>// Copyright 2016 Lars Wiegman. All rights reserved. Use of this source code is
// governed by a BSD-style license that can be found in the LICENSE file.

package multipass

import "html/template"

const (
	loginPage = iota
	tokenInvalidPage
	tokenSentPage
	continueOrSignoutPage
)

type page struct {
	PJAX        bool
	Page        int
	NextURL     string
	LoginPath   string
	SignoutPath string
}

func loadTemplates() (*template.Template, error) {
	tmpl := template.New("")
	template.Must(tmpl.New("head").Parse(`<!DOCTYPE html>

	<meta charset="utf-8">
	<meta name="description" content="Multipass">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
	<title>Multipass</title>

`))

	template.Must(tmpl.New("tail").Parse(`</body></html>`))

	template.Must(tmpl.New("loginform").Parse(`
<form action="{{ .LoginPath }}" method=POST class="login-form">
	<input type=hidden name=url value="{{ .NextURL }}" />
	<input type=email name=handle placeholder="Your handle ..." />
	<input type=submit value="Submit" class="btn btn-default">
</form>`))

	template.Must(tmpl.New("signoutform").Parse(`
<form action="{{ .SignoutPath }}" method=POST class="login-form">
	<input type=hidden name=url value="{{ .NextURL }}" />
	<input type=submit value="Signout" class="btn btn-danger">
</form>`))

	template.Must(tmpl.New("page").Parse(`
	{{ if ne .PJAX true }}{{ template "head"}}{{end}}

Multipass

		{{ if eq .Page 0 }}
			{{ template "loginform" . }}

This resource is protected. Submit your handle to gain access.

		{{ else if eq .Page 1 }}

Your access token has expired or is invalid. Submit your handle to request a new one.

			{{ template "loginform" . }}

This resource is protected.

		{{ else if eq .Page 2 }}

A message with an access token was sent to your handle; Follow the login link in the message to gain access.

		{{ else if eq .Page 3 }}

Continue to access the private resource or signout.

			Continue
			{{ template "signoutform" . }}

This resource is protected.

		{{ else }}

Default page

		{{ end }}

{{ if ne .PJAX true }}{{ template "tail" }}{{ end }}`))

	template.Must(tmpl.New("style").Parse(`
* {
	-webkit-box-sizing: border-box;
	-moz-box-sizing: border-box;
	box-sizing: border-box;
}
article, aside, details, figcaption, figure, footer, header, hgroup,
main, nav, section, summary {
	display: block;
}
body {
	margin: 0;
}
input { -webkit-appearance: none; }
.wrapper {
	display: -webkit-flex;
	-webkit-flex-direction: column;
}
.login-box {
	width: 480px;
	margin: 4rem auto;
	text-align: center;
	padding: 1rem 0;
	border: solid .2rem #0d8eba;
	border-radius: .8rem;
	padding: 1rem 2rem;
	background-color: #fff;
}
.login-box h1 {
	font-family: sans-serif;
	font-size: 3rem;
	color: #444;
}
.login-form {
}
.login-form input {
}
.login-form input[type=text] {
	font-size: 1.2rem;
	padding: 0 1rem;
	border-color: #ddd;
	color: #444;
	margin: .6rem 0;
	width: 100%;
	height: 2.8rem;
	border-radius: .4rem;
	border-style: solid;
	border-width: .2rem;
}
.login-form input[type=submit] {
}
.btn {
	font-family: sans-serif;
	font-size: 1.2rem;
	margin: 1rem 0;
	padding: .5rem 1rem;
	width: 100%;
	height: 2.8rem;
	border-radius: .4rem;
	border-style: solid;
	border-width: .16rem;
	text-transform: uppercase;
	color: #fff;
	height: 3rem;
	cursor: pointer;
	display: inline-block;
	line-height: 2rem;
}
a.btn {
	text-decoration: none;
	color: white;
}
.btn-default {
	background-color: #0d8eba;
	border-color: #0d8eba;
}
.btn-success {
	background-color: #0dba77;
	border-color: #0dba77;
}
.btn-danger {
	background-color: #ba2f0d;
	border-color: #ba2f0d;
}
.notice {
	font-style: italic;
	color: #666;
}
/* Narrow */
@media only screen and (max-width: 480px) {
	body {
		background-color: #0d8eba;
	}
	.login-box {
		width: 100%;
		border: none;
		border-radius: 0;
	}
}
/* Medium */
@media only screen and (min-width: 481px) and (max-width: 960px) {
}
/* Wide */
@media only screen and (min-width: 961px) {
}
`))
	return tmpl, nil
}
<commit_msg>Fix missing CSS selector for email type input<commit_after>// Copyright 2016 Lars Wiegman. All rights reserved. Use of this source code is
// governed by a BSD-style license that can be found in the LICENSE file.

package multipass

import "html/template"

const (
	loginPage = iota
	tokenInvalidPage
	tokenSentPage
	continueOrSignoutPage
)

type page struct {
	PJAX        bool
	Page        int
	NextURL     string
	LoginPath   string
	SignoutPath string
}

func loadTemplates() (*template.Template, error) {
	tmpl := template.New("")
	template.Must(tmpl.New("head").Parse(`<!DOCTYPE html>

	<meta charset="utf-8">
	<meta name="description" content="Multipass">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
	<title>Multipass</title>

`))

	template.Must(tmpl.New("tail").Parse(`</body></html>`))

	template.Must(tmpl.New("loginform").Parse(`
<form action="{{ .LoginPath }}" method=POST class="login-form">
	<input type=hidden name=url value="{{ .NextURL }}" />
	<input type=email name=handle placeholder="Your handle ..." />
	<input type=submit value="Submit" class="btn btn-default">
</form>`))

	template.Must(tmpl.New("signoutform").Parse(`
<form action="{{ .SignoutPath }}" method=POST class="login-form">
	<input type=hidden name=url value="{{ .NextURL }}" />
	<input type=submit value="Signout" class="btn btn-danger">
</form>`))

	template.Must(tmpl.New("page").Parse(`
	{{ if ne .PJAX true }}{{ template "head"}}{{end}}

Multipass

		{{ if eq .Page 0 }}
			{{ template "loginform" . }}

This resource is protected. Submit your handle to gain access.

		{{ else if eq .Page 1 }}

Your access token has expired or is invalid. Submit your handle to request a new one.

			{{ template "loginform" . }}

This resource is protected.

		{{ else if eq .Page 2 }}

A message with an access token was sent to your handle; Follow the login link in the message to gain access.

		{{ else if eq .Page 3 }}

Continue to access the private resource or signout.

			Continue
			{{ template "signoutform" . }}

This resource is protected.

		{{ else }}

Default page

		{{ end }}

{{ if ne .PJAX true }}{{ template "tail" }}{{ end }}`))

	template.Must(tmpl.New("style").Parse(`
* {
	-webkit-box-sizing: border-box;
	-moz-box-sizing: border-box;
	box-sizing: border-box;
}
article, aside, details, figcaption, figure, footer, header, hgroup,
main, nav, section, summary {
	display: block;
}
body {
	margin: 0;
}
input { -webkit-appearance: none; }
.wrapper {
	display: -webkit-flex;
	-webkit-flex-direction: column;
}
.login-box {
	width: 480px;
	margin: 4rem auto;
	text-align: center;
	padding: 1rem 0;
	border: solid .2rem #0d8eba;
	border-radius: .8rem;
	padding: 1rem 2rem;
	background-color: #fff;
}
.login-box h1 {
	font-family: sans-serif;
	font-size: 3rem;
	color: #444;
}
.login-form {
}
.login-form input {
}
.login-form input[type=text], .login-form input[type=email] {
	font-size: 1.2rem;
	padding: 0 1rem;
	border-color: #ddd;
	color: #444;
	margin: .6rem 0;
	width: 100%;
	height: 2.8rem;
	border-radius: .4rem;
	border-style: solid;
	border-width: .2rem;
}
.login-form input[type=submit] {
}
.btn {
	font-family: sans-serif;
	font-size: 1.2rem;
	margin: 1rem 0;
	padding: .5rem 1rem;
	width: 100%;
	height: 2.8rem;
	border-radius: .4rem;
	border-style: solid;
	border-width: .16rem;
	text-transform: uppercase;
	color: #fff;
	height: 3rem;
	cursor: pointer;
	display: inline-block;
	line-height: 2rem;
}
a.btn {
	text-decoration: none;
	color: white;
}
.btn-default {
	background-color: #0d8eba;
	border-color: #0d8eba;
}
.btn-success {
	background-color: #0dba77;
	border-color: #0dba77;
}
.btn-danger {
	background-color: #ba2f0d;
	border-color: #ba2f0d;
}
.notice {
	font-style: italic;
	color: #666;
}
/* Narrow */
@media only screen and (max-width: 480px) {
	body {
		background-color: #0d8eba;
	}
	.login-box {
		width: 100%;
		border: none;
		border-radius: 0;
	}
}
/* Medium */
@media only screen and (min-width: 481px) and (max-width: 960px) {
}
/* Wide */
@media only screen and (min-width: 961px) {
}
`))
	return tmpl, nil
}
<|endoftext|>


<commit_before>package handler

import (
	"net/http"
	"strconv"
	"strings"

	"ratings/parser"
	"ratings/responses"

	"github.com/go-playground/validator"
	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
	"github.com/labstack/gommon/log"
)

type RequestValidator struct {
	validator *validator.Validate
}

func (rv *RequestValidator) Validate(request interface{}) error {
	return rv.validator.Struct(request)
}

func badRequestMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
	return func(context echo.Context) error {
		var message string

		if !hasAcceptHeader(context) {
			message = "Accept header is missing"

			return responses.ErrorResponse(http.StatusBadRequest, message, context)
		}

		if context.Request().Method == echo.OPTIONS && hasContentTypeHeader(context) {
			message = "OPTIONS requests must have no body"

			return responses.ErrorResponse(http.StatusBadRequest, message, context)
		}

		if context.Request().Method == echo.POST && !hasContentTypeHeader(context) {
			message = "Content-Type header is missing"

			return responses.ErrorResponse(http.StatusBadRequest, message, context)
		}

		return next(context)
	}
}

func notAcceptableMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
	return func(context echo.Context) error {
		var message string

		if !isValidAcceptHeader(context) {
			message = "Not accepting JSON responses"

			return responses.ErrorResponse(http.StatusNotAcceptable, message, context)
		}

		return next(context)
	}
}

func unsupportedMediaTypeMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
	return func(context echo.Context) error {
		var message string

		if context.Request().Method == echo.POST {
			if !isValidContentTypeHeader(context) || !isValidCharacterEncoding(context) {
				message = "Request body must be UTF-8 encoded JSON"

				return responses.ErrorResponse(http.StatusUnsupportedMediaType, message, context)
			}
		}

		return next(context)
	}
}

func notImplementedMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
	return func(context echo.Context) error {
		if context.Request().Method != echo.POST && context.Request().Method != echo.OPTIONS {
			return responses.ErrorResponse(http.StatusNotImplemented, "", context)
		}

		return next(context)
	}
}

func corsMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
	return func(context echo.Context) error {
		context.Response().Header().Set("Access-Control-Allow-Origin", "*")
		context.Response().Header().Set("Access-Control-Allow-Methods", "OPTIONS, POST")
		context.Response().Header().Set("Access-Control-Allow-Headers", "Content-Type, Accept, Authorization")

		return next(context)
	}
}

func hasAcceptHeader(context echo.Context) bool {
	if header := context.Request().Header.Get("Accept"); strings.TrimSpace(header) != "" {
		return true
	}

	return false
}

func isValidAcceptHeader(context echo.Context) bool {
	accepted := "application/json"

	if header := context.Request().Header.Get("Accept"); strings.Contains(strings.ToLower(header), accepted) || header == "*/*" {
		return true
	}

	return false
}

func hasContentTypeHeader(context echo.Context) bool {
	if header := context.Request().Header.Get("Content-Type"); strings.TrimSpace(header) != "" {
		return true
	}

	return false
}

func isValidContentTypeHeader(context echo.Context) bool {
	text := "text/plain"
	json := "application/json"

	if header := context.Request().Header.Get("Content-Type"); strings.Contains(strings.ToLower(header), text) || strings.Contains(strings.ToLower(header), json) {
		return true
	}

	return false
}

func isValidCharacterEncoding(context echo.Context) bool {
	charset := "charset=utf-8"

	if header := context.Request().Header.Get("Content-Type"); strings.HasSuffix(strings.ToLower(header), charset) {
		return true
	}

	return false
}

func Handler(port int, handlers map[string]echo.HandlerFunc) http.Handler {
	e := echo.New()
	validate := validator.New()

	parser.RegisterCustomValidators(validate)

	e.Use(middleware.Secure())
	e.Use(middleware.Logger())
	e.Use(middleware.Recover())
	e.Use(notImplementedMiddleware)
	e.Use(notAcceptableMiddleware)
	e.Use(badRequestMiddleware)
	e.Use(unsupportedMediaTypeMiddleware)
	e.Use(corsMiddleware)

	e.OPTIONS("/", handlers["OptionsRoot"])
	e.OPTIONS("/ratings", handlers["OptionsRatings"])
	e.POST("/ratings", handlers["PostRatings"])

	e.Logger.SetLevel(log.ERROR)

	e.Server.Addr = ":" + strconv.Itoa(port)
	e.HTTPErrorHandler = responses.ErrorHandler
	e.Validator = &RequestValidator{validator: validate}

	return e
}
<commit_msg>Added debug mode support<commit_after>package handler

import (
	"net/http"
	"os"
	"strconv"
	"strings"

	"ratings/parser"
	"ratings/responses"

	"github.com/go-playground/validator"
	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
	"github.com/labstack/gommon/log"
)

type RequestValidator struct {
	validator *validator.Validate
}

func (rv *RequestValidator) Validate(request interface{}) error {
	return rv.validator.Struct(request)
}

func badRequestMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
	return func(context echo.Context) error {
		var message string

		if !hasAcceptHeader(context) {
			message = "Accept header is missing"

			return responses.ErrorResponse(http.StatusBadRequest, message, context)
		}

		if context.Request().Method == echo.OPTIONS && hasContentTypeHeader(context) {
			message = "OPTIONS requests must have no body"

			return responses.ErrorResponse(http.StatusBadRequest, message, context)
		}

		if context.Request().Method == echo.POST && !hasContentTypeHeader(context) {
			message = "Content-Type header is missing"

			return responses.ErrorResponse(http.StatusBadRequest, message, context)
		}

		return next(context)
	}
}

func notAcceptableMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
	return func(context echo.Context) error {
		var message string

		if !isValidAcceptHeader(context) {
			message = "Not accepting JSON responses"

			return responses.ErrorResponse(http.StatusNotAcceptable, message, context)
		}

		return next(context)
	}
}

func unsupportedMediaTypeMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
	return func(context echo.Context) error {
		var message string

		if context.Request().Method == echo.POST {
			if !isValidContentTypeHeader(context) || !isValidCharacterEncoding(context) {
				message = "Request body must be UTF-8 encoded JSON"

				return responses.ErrorResponse(http.StatusUnsupportedMediaType, message, context)
			}
		}

		return next(context)
	}
}

func notImplementedMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
	return func(context echo.Context) error {
		if context.Request().Method != echo.POST && context.Request().Method != echo.OPTIONS {
			return responses.ErrorResponse(http.StatusNotImplemented, "", context)
		}

		return next(context)
	}
}

func corsMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
	return func(context echo.Context) error {
		context.Response().Header().Set("Access-Control-Allow-Origin", "*")
		context.Response().Header().Set("Access-Control-Allow-Methods", "OPTIONS, POST")
		context.Response().Header().Set("Access-Control-Allow-Headers", "Content-Type, Accept, Authorization")

		return next(context)
	}
}

func hasAcceptHeader(context echo.Context) bool {
	if header := context.Request().Header.Get("Accept"); strings.TrimSpace(header) != "" {
		return true
	}

	return false
}

func isValidAcceptHeader(context echo.Context) bool {
	accepted := "application/json"

	if header := context.Request().Header.Get("Accept"); strings.Contains(strings.ToLower(header), accepted) || header == "*/*" {
		return true
	}

	return false
}

func hasContentTypeHeader(context echo.Context) bool {
	if header := context.Request().Header.Get("Content-Type"); strings.TrimSpace(header) != "" {
		return true
	}

	return false
}

func isValidContentTypeHeader(context echo.Context) bool {
	text := "text/plain"
	json := "application/json"

	if header := context.Request().Header.Get("Content-Type"); strings.Contains(strings.ToLower(header), text) || strings.Contains(strings.ToLower(header), json) {
		return true
	}

	return false
}

func isValidCharacterEncoding(context echo.Context) bool {
	charset := "charset=utf-8"

	if header := context.Request().Header.Get("Content-Type"); strings.HasSuffix(strings.ToLower(header), charset) {
		return true
	}

	return false
}

func Handler(port int, handlers map[string]echo.HandlerFunc) http.Handler {
	e := echo.New()
	validate := validator.New()
	env := os.Getenv("API_RATINGS_ENV")

	parser.RegisterCustomValidators(validate)

	e.Use(middleware.Secure())
	e.Use(middleware.Logger())
	e.Use(middleware.Recover())
	e.Use(notImplementedMiddleware)
	e.Use(notAcceptableMiddleware)
	e.Use(badRequestMiddleware)
	e.Use(unsupportedMediaTypeMiddleware)
	e.Use(corsMiddleware)

	e.OPTIONS("/", handlers["OptionsRoot"])
	e.OPTIONS("/ratings", handlers["OptionsRatings"])
	e.POST("/ratings", handlers["PostRatings"])

	if env == "DEV" {
		e.Logger.SetLevel(log.DEBUG)
		e.Debug = true
	} else {
		e.Logger.SetLevel(log.ERROR)
	}

	e.Server.Addr = ":" + strconv.Itoa(port)
	e.HTTPErrorHandler = responses.ErrorHandler
	e.Validator = &RequestValidator{validator: validate}

	return e
}
<|endoftext|>


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

package gopos

import (
	"encoding/hex"
	"strings"
	"testing"
)

func TestBcd(t *testing.T) {

	strOk := "80FFA256AE10"
	strBinOk, err1 := bcd(strOk)

	if err1 != nil {
		t.Error("bcd() failed: ", err1)
	}
	strOkRecover := strings.ToUpper(hex.EncodeToString(strBinOk))

	if strOk != strOkRecover {
		t.Error("Original and converted does not match")
	}

	strNotEven := "80FFA256AE1"
	_, err2 := bcd(strNotEven)
	if err2 == nil {
		t.Error("bcd() should have failed: ")
	}

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

package gopos

import (
	"encoding/hex"
	"strings"
	"testing"
)

func TestBcd(t *testing.T) {

	strOk := "80FFA256AE10"
	strBinOk, err1 := bcd(strOk)

	if err1 != nil {
		t.Error("bcd() failed: ", err1)
	}
	strOkRecover := strings.ToUpper(hex.EncodeToString(strBinOk))

	if strOk != strOkRecover {
		t.Error("Original and converted does not match")
	}

	strNotEven := "80FFA256AE1"
	_, err2 := bcd(strNotEven)
	if err2 == nil {
		t.Error("bcd() should have failed: ")
	}

	t.Error("Failed test")
}
<|endoftext|>


<commit_before>package expleto

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	// "path/filepath"
	"strings"
	"testing"
)

func func_name(string) ([]byte, error) {

	return nil, errors.New("Can't read ")

}
func TestGetDataFromFile(t *testing.T) {
	// non exists files
	nonexist_cfgFiles := []string{
		"fixtures/nonexist/config/app.json",
		"fixtures/nonexist/config/app.yml",
		"fixtures/nonexist/config/app.toml",
	}
	for _, f := range nonexist_cfgFiles {
		_, err := GetDataFromFile(f)
		if err == nil {
			t.Fatal("There wasn't raise an error")
		}
		str := fmt.Sprintf("%v", err)
		if !strings.Contains(str, "no such file or directory") {
			t.Fatal("The '" + str + "' is not contain the err 'no such file or directory'")
		}
	}

	t_file, err := ioutil.TempFile(os.TempDir(), "temp.json")
	defer os.Remove(t_file.Name())
	if err != nil {
		t.Fatal(err)
	}
	// ioutil.ReadFile = func_name
	_, err = GetDataFromFile(t_file.Name())
	if err != nil {
		t.Fatal("There shouldn't raise an error")
	}

}
<commit_msg>adding unit tests for utils<commit_after>package expleto

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	// "path/filepath"
	"strings"
	"testing"
)

func func_name(string) ([]byte, error) {

	return nil, errors.New("Can't read ")

}
func TestGetDataFromFile(t *testing.T) {
	// non exists files
	nonexist_cfgFiles := []string{
		"fixtures/nonexist/config/app.json",
		"fixtures/nonexist/config/app.yml",
		"fixtures/nonexist/config/app.toml",
	}
	for _, f := range nonexist_cfgFiles {
		_, err := GetDataFromFile(f)
		if err == nil {
			t.Fatal("There wasn't raise an error")
		}
		str := fmt.Sprintf("%v", err)
		if !strings.Contains(str, "no such file or directory") {
			t.Fatal("The '" + str + "' is not contain the err 'no such file or directory'")
		}
	}

	t_file, err := ioutil.TempFile(os.TempDir(), "temp.json")
	defer os.Remove(t_file.Name())
	if err != nil {
		t.Fatal(err)
	}
	// ioutil.ReadFile = func_name
	_, err = GetDataFromFile(t_file.Name())
	if err != nil {
		t.Fatal("There shouldn't raise an error")
	}

}

func TestFormatError(t *testing.T) {
	err := errors.New("test 1")
	err1 := FormatError(err)
	err_str := fmt.Sprintf("%v", err)

	err1_str := fmt.Sprintf("%v", err1)
	if err_str == err1_str {
		t.Fatal("err1_str: " + err1_str + " == err_str " + err_str)
	}
}
<|endoftext|>


<commit_before>package decoders

import (
	"errors"
	"fmt"
	"log"
	"math"
	"regexp"
	"strconv"
	"time"

	"github.com/seadsystem/Backend/DB/landingzone/constants"
)

type SeadPacket struct {
	Type      byte
	Location  byte
	Timestamp time.Time
	Period    float64
	Count     uint
	Data      float32
	Serial    int
}

var headerRegex *regexp.Regexp
var InvalidHeader = errors.New("Invalid header.")
var InvalidPacket = errors.New("Invalid packet.")
var InvalidTime = errors.New("Invalid time.")

// init sets up stuff we need with proper error handling. If it isn't complicated or doesn't need error handling, it can probably just be assigned directly.
func init() {
	var err error
	headerRegex, err = regexp.Compile(constants.HEADER_REGEX)
	if err != nil {
		log.Panic("Regex compile error:", err)
	}
}

// DecodeHeader verifies that the header is in the correct format and extracts the serial number
func DecodeHeader(packet []byte) (serial int, offset float64, err error) {
	serialStrings := headerRegex.FindSubmatch(packet)

	if serialStrings == nil || len(serialStrings) != 3 {
		err = InvalidHeader
		return
	}

	log.Printf("Header serial string: %s\n", string(serialStrings[1]))

	double_time, err = asciiTimeToDouble(buffer[i : i+14])
	if err != nil {
		return
	}

	offset = float64(time.Now().Unix()) - double_time

	serial, err = strconv.Atoi(string(serialStrings[1]))
	return
}

// DecodePacket extracts the data sent from sensor
func DecodePacket(buffer []byte, offset float64) (packet SeadPacket, err error) {
	for i := 0; i < len(buffer); {
		datatype := buffer[i]
		i++

		// Switch on the type of data sent in the packet
		switch {
		case datatype == 'T':
			// Type
			packet.Type = buffer[i]
			i++
		case datatype == 'l':
			// Location
			packet.Location = buffer[i]
			i++
		case datatype == 't':
			// Timestamp
			var double_time float64
			double_time, err = asciiTimeToDouble(buffer[i : i+14])
			double_time += offset
			nano := math.Pow10(6)
			packet.Timestamp = time.Unix(int64(double_time), (int_time*nano)%int64(nano))
			//packet.Timestamp, err = asciiTimeToDouble(buffer[i : i+14])
			i += 14
		case datatype == 'P':
			// Period separator
			packet.Period, err = asciiTimeToDouble(buffer[i : i+14])
			i += 14
		case datatype == 'C':
			// Count
			packet.Count = Binary2uint(buffer[i : i+2])
			i += 2
		case datatype == 'D':
			// Data
			// if count isn't set, return error
			if packet.Count == 0 {
				err = InvalidPacket
			} else {
				count := 2 * int(packet.Count)
				packet.Data = math.Float32frombits(uint32(Binary2uint(buffer[i : i+count])))
				i += count
			}
		case datatype == 'S':
			// Serial
			packet.Serial, err = strconv.Atoi(string(buffer[i : i+6]))
			i += 6
		case datatype == 'X':
			return
		default:
			err = InvalidPacket
		}

		if err != nil {
			return
		}
	}
	err = InvalidPacket
	return
}

func doubleToAsciiTime(double_time float64) string {
	// TODO: Check if this logic is correct or if we need to use http://golang.org/pkg/math/#Mod
	int_time := int(double_time)
	var days = math.Floor(double_time / (60 * 60 * 24))
	var hours = (int_time % (60 * 60 * 24)) / (60 * 60)
	var minutes = (int_time % (60 * 60)) / 60
	var seconds = (int_time % (60)) / 1
	var milliseconds = (int_time * 1000) % 1000
	var clock_time = (int_time * 12000) % 12

	return fmt.Sprintf("%03d%02d%02d%02d%03d%02d", days, hours, minutes, seconds, milliseconds, clock_time)
}

func asciiTimeToDouble(ascii_time []byte) (time float64, err error) {
	// Check time string format
	if len(ascii_time) != 16 {
		err = InvalidTime
	}
	_, err = strconv.Atoi(string(ascii_time))
	if err != nil {
		return
	}

	// Do the conversion now that we know it should work
	var ptr int = 0
	days, err := strconv.Atoi(string(ascii_time[ptr : ptr+3]))
	if err != nil {
		return
	}
	ptr += 3
	time += float64(60 * 60 * 24 * days)
	hours, err := strconv.Atoi(string(ascii_time[ptr : ptr+2]))
	if err != nil {
		return
	}
	ptr += 2
	time += float64(60 * 60 * hours)
	minutes, err := strconv.Atoi(string(ascii_time[ptr : ptr+2]))
	if err != nil {
		return
	}
	ptr += 2
	time += float64(60 * minutes)
	seconds, err := strconv.Atoi(string(ascii_time[ptr : ptr+2]))
	if err != nil {
		return
	}
	ptr += 2
	time += float64(seconds)
	milliseconds, err := strconv.Atoi(string(ascii_time[ptr : ptr+3]))
	if err != nil {
		return
	}
	ptr += 3
	time += float64(milliseconds) / 1000.0
	clock, err := strconv.Atoi(string(ascii_time[ptr : ptr+2]))
	if err != nil {
		return
	}
	ptr += 2
	time += float64(clock) / 12000.0
	return
}

// Every checks if every byte in a slice meets some criteria
func Every(data []byte, check func(byte) bool) bool {
	for _, element := range data {
		if !check(element) {
			return false
		}
	}
	return true
}

// Binary2uint converts a byte array containing binary data into an int
func Binary2uint(data []byte) (total uint) {
	for index, element := range data {
		total += uint(element) << uint(index*8)
	}
	return
}

// Binary2uint64 converts a byte array containing binary data into an int
func Binary2uint64(data []byte) (total uint64) {
	for index, element := range data {
		total += uint64(element) << uint64(index*8)
	}
	return
}
<commit_msg>Hopefully fixed variable naming.<commit_after>package decoders

import (
	"errors"
	"fmt"
	"log"
	"math"
	"regexp"
	"strconv"
	"time"

	"github.com/seadsystem/Backend/DB/landingzone/constants"
)

type SeadPacket struct {
	Type      byte
	Location  byte
	Timestamp time.Time
	Period    float64
	Count     uint
	Data      float32
	Serial    int
}

var headerRegex *regexp.Regexp
var InvalidHeader = errors.New("Invalid header.")
var InvalidPacket = errors.New("Invalid packet.")
var InvalidTime = errors.New("Invalid time.")

// init sets up stuff we need with proper error handling. If it isn't complicated or doesn't need error handling, it can probably just be assigned directly.
func init() {
	var err error
	headerRegex, err = regexp.Compile(constants.HEADER_REGEX)
	if err != nil {
		log.Panic("Regex compile error:", err)
	}
}

// DecodeHeader verifies that the header is in the correct format and extracts the serial number
func DecodeHeader(packet []byte) (serial int, offset float64, err error) {
	headerStrings := headerRegex.FindSubmatch(packet)

	if headerStrings == nil || len(headerStrings) != 3 {
		err = InvalidHeader
		return
	}

	log.Printf("Header serial string: %s\n", string(headerStrings[1]))

	var double_time float64
	double_time, err = asciiTimeToDouble(headerStrings[2])
	if err != nil {
		return
	}

	offset = float64(time.Now().Unix()) - double_time

	serial, err = strconv.Atoi(string(headerStrings[1]))
	return
}

// DecodePacket extracts the data sent from sensor
func DecodePacket(buffer []byte, offset float64) (packet SeadPacket, err error) {
	for i := 0; i < len(buffer); {
		datatype := buffer[i]
		i++

		// Switch on the type of data sent in the packet
		switch {
		case datatype == 'T':
			// Type
			packet.Type = buffer[i]
			i++
		case datatype == 'l':
			// Location
			packet.Location = buffer[i]
			i++
		case datatype == 't':
			// Timestamp
			var double_time float64
			double_time, err = asciiTimeToDouble(buffer[i : i+14])
			double_time += offset
			nano := math.Pow10(6)
			packet.Timestamp = time.Unix(int64(double_time), (int_time*nano)%int64(nano))
			//packet.Timestamp, err = asciiTimeToDouble(buffer[i : i+14])
			i += 14
		case datatype == 'P':
			// Period separator
			packet.Period, err = asciiTimeToDouble(buffer[i : i+14])
			i += 14
		case datatype == 'C':
			// Count
			packet.Count = Binary2uint(buffer[i : i+2])
			i += 2
		case datatype == 'D':
			// Data
			// if count isn't set, return error
			if packet.Count == 0 {
				err = InvalidPacket
			} else {
				count := 2 * int(packet.Count)
				packet.Data = math.Float32frombits(uint32(Binary2uint(buffer[i : i+count])))
				i += count
			}
		case datatype == 'S':
			// Serial
			packet.Serial, err = strconv.Atoi(string(buffer[i : i+6]))
			i += 6
		case datatype == 'X':
			return
		default:
			err = InvalidPacket
		}

		if err != nil {
			return
		}
	}
	err = InvalidPacket
	return
}

func doubleToAsciiTime(double_time float64) string {
	// TODO: Check if this logic is correct or if we need to use http://golang.org/pkg/math/#Mod
	int_time := int(double_time)
	var days = math.Floor(double_time / (60 * 60 * 24))
	var hours = (int_time % (60 * 60 * 24)) / (60 * 60)
	var minutes = (int_time % (60 * 60)) / 60
	var seconds = (int_time % (60)) / 1
	var milliseconds = (int_time * 1000) % 1000
	var clock_time = (int_time * 12000) % 12

	return fmt.Sprintf("%03d%02d%02d%02d%03d%02d", days, hours, minutes, seconds, milliseconds, clock_time)
}

func asciiTimeToDouble(ascii_time []byte) (time float64, err error) {
	// Check time string format
	if len(ascii_time) != 16 {
		err = InvalidTime
	}
	_, err = strconv.Atoi(string(ascii_time))
	if err != nil {
		return
	}

	// Do the conversion now that we know it should work
	var ptr int = 0
	days, err := strconv.Atoi(string(ascii_time[ptr : ptr+3]))
	if err != nil {
		return
	}
	ptr += 3
	time += float64(60 * 60 * 24 * days)
	hours, err := strconv.Atoi(string(ascii_time[ptr : ptr+2]))
	if err != nil {
		return
	}
	ptr += 2
	time += float64(60 * 60 * hours)
	minutes, err := strconv.Atoi(string(ascii_time[ptr : ptr+2]))
	if err != nil {
		return
	}
	ptr += 2
	time += float64(60 * minutes)
	seconds, err := strconv.Atoi(string(ascii_time[ptr : ptr+2]))
	if err != nil {
		return
	}
	ptr += 2
	time += float64(seconds)
	milliseconds, err := strconv.Atoi(string(ascii_time[ptr : ptr+3]))
	if err != nil {
		return
	}
	ptr += 3
	time += float64(milliseconds) / 1000.0
	clock, err := strconv.Atoi(string(ascii_time[ptr : ptr+2]))
	if err != nil {
		return
	}
	ptr += 2
	time += float64(clock) / 12000.0
	return
}

// Every checks if every byte in a slice meets some criteria
func Every(data []byte, check func(byte) bool) bool {
	for _, element := range data {
		if !check(element) {
			return false
		}
	}
	return true
}

// Binary2uint converts a byte array containing binary data into an int
func Binary2uint(data []byte) (total uint) {
	for index, element := range data {
		total += uint(element) << uint(index*8)
	}
	return
}

// Binary2uint64 converts a byte array containing binary data into an int
func Binary2uint64(data []byte) (total uint64) {
	for index, element := range data {
		total += uint64(element) << uint64(index*8)
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"../nsq"
	"../util/pqueue"
	"bitly/notify"
	"container/heap"
	"errors"
	"log"
	"sync"
	"time"
)

const (
	// TODO: move this into the nsqd object so its overwritable for tests
	// the amount of time to wait for a client to respond to a message
	msgTimeout = int64(60 * time.Second)

	// the amount of time a worker will wait when idle
	defaultWorkerWait = 250 * time.Millisecond
)

// Channel represents the concrete type for a NSQ channel (and also
// implements the Queue interface)
//
// There can be many channels per topic and each with there own distinct
// clients subscribed.
//
// Channels maintain all client and message, orchestrating in-flight
// messages, timeouts, requeueing, etc.
type Channel struct {
	sync.RWMutex // embed a r/w mutex

	topicName string
	name      string

	backend nsq.BackendQueue

	incomingMessageChan chan *nsq.Message
	memoryMsgChan       chan *nsq.Message
	clientMessageChan   chan *nsq.Message
	exitChan            chan int

	// state tracking
	clients          []*nsq.ServerClient
	requeuePQ        pqueue.PriorityQueue
	requeueMutex     sync.Mutex
	inFlightMessages map[string]interface{}
	inFlightPQ       pqueue.PriorityQueue
	inFlightMutex    sync.Mutex

	// stat counters
	requeueCount int64
	getCount     int64
	putCount     int64
	timeoutCount int64
}

// NewChannel creates a new instance of the Channel type and returns a pointer
func NewChannel(topicName string, channelName string, inMemSize int64, dataPath string, maxBytesPerFile int64) *Channel {
	c := &Channel{
		topicName: topicName,
		name:      channelName,
		// backend names, for uniqueness, automatically include the topic... <topic>:<channel>
		backend:             NewDiskQueue(topicName+":"+channelName, dataPath, maxBytesPerFile),
		incomingMessageChan: make(chan *nsq.Message, 5),
		memoryMsgChan:       make(chan *nsq.Message, inMemSize),
		clientMessageChan:   make(chan *nsq.Message),
		exitChan:            make(chan int),
		clients:             make([]*nsq.ServerClient, 0, 5),
		inFlightMessages:    make(map[string]interface{}),
		inFlightPQ:          pqueue.New(int(inMemSize / 10)),
		requeuePQ:           pqueue.New(int(inMemSize / 10)),
	}
	go c.router()
	go c.messagePump()
	go c.requeueWorker()
	go c.inFlightWorker()
	notify.Post("new_channel", c)
	return c
}

// MemoryChan implements the Queue interface
func (c *Channel) MemoryChan() chan *nsq.Message {
	return c.memoryMsgChan
}

// BackendQueue implements the Queue interface
func (c *Channel) BackendQueue() nsq.BackendQueue {
	return c.backend
}

// InFlight implements the Queue interface
func (c *Channel) InFlight() map[string]interface{} {
	return c.inFlightMessages
}

// PutMessage writes to the appropriate incoming message channel
// (which will be routed asynchronously)
func (c *Channel) PutMessage(msg *nsq.Message) {
	c.incomingMessageChan <- msg
}

// FinishMessage successfully discards an in-flight message
func (c *Channel) FinishMessage(id []byte) error {
	item, err := c.popInFlightMessage(id)
	if err != nil {
		log.Printf("ERROR: failed to finish message(%s) - %s", id, err.Error())
	} else {
		c.removeFromInFlightPQ(item)
	}
	return err
}

// RequeueMessage requeues a message based on `timeoutMs`, ie:
//
// `timeoutMs` == 0 - requeue a message immediately
// `timeoutMs`  > 0 - asynchronously wait for the specified timeout
//     and requeue a message (aka "deferred requeue")
//
func (c *Channel) RequeueMessage(id []byte, timeout time.Duration) error {
	if timeout == 0 {
		return c.doRequeue(id)
	}

	item, err := c.getInFlightMessage(id)
	if err != nil {
		return err
	}
	c.removeFromInFlightPQ(item)
	c.addToDeferredPQ(item.Value.(*nsq.Message), timeout)

	return nil
}

// doRequeue performs the low level operations to requeue a message
func (c *Channel) doRequeue(id []byte) error {
	item, err := c.popInFlightMessage(id)
	if err != nil {
		log.Printf("ERROR: failed to requeue message(%s) - %s", id, err.Error())
	} else {
		msg := item.Value.(*nsq.Message)
		c.incomingMessageChan <- msg
	}
	return err
}

// Router handles the muxing of incoming Channel messages, either writing
// to the in-memory channel or to the backend
func (c *Channel) router() {
	for {
		select {
		case msg := <-c.incomingMessageChan:
			select {
			case c.memoryMsgChan <- msg:
			default:
				err := WriteMessageToBackend(msg, c)
				if err != nil {
					log.Printf("ERROR: failed to write message to backend - %s", err.Error())
					// TODO: requeue?
					continue
				}
			}
			c.putCount++
		case <-c.exitChan:
			return
		}
	}
}

// pushInFlightMessage atomically adds a message to the in-flight dictionary
func (c *Channel) pushInFlightMessage(item *pqueue.Item) error {
	c.Lock()
	defer c.Unlock()

	id := item.Value.(*nsq.Message).Id
	_, ok := c.inFlightMessages[string(id)]
	if ok {
		return errors.New("E_ID_ALREADY_IN_FLIGHT")
	}
	c.inFlightMessages[string(id)] = item

	return nil
}

// popInFlightMessage atomically removes a message from the in-flight dictionary
func (c *Channel) popInFlightMessage(id []byte) (*pqueue.Item, error) {
	c.Lock()
	defer c.Unlock()

	item, ok := c.inFlightMessages[string(id)]
	if !ok {
		return nil, errors.New("E_ID_NOT_IN_FLIGHT")
	}
	delete(c.inFlightMessages, string(id))

	return item.(*pqueue.Item), nil
}

// getInFlightMessage retrieves a message from the in-flight dictionary by ID
func (c *Channel) getInFlightMessage(id []byte) (*pqueue.Item, error) {
	c.RLock()
	defer c.RUnlock()

	item, ok := c.inFlightMessages[string(id)]
	if !ok {
		return nil, errors.New("E_ID_NOT_IN_FLIGHT")
	}

	return item.(*pqueue.Item), nil
}

func (c *Channel) addToInFlightPQ(item *pqueue.Item) {
	c.inFlightMutex.Lock()
	defer c.inFlightMutex.Unlock()
	heap.Push(&c.inFlightPQ, item)
}

func (c *Channel) removeFromInFlightPQ(item *pqueue.Item) {
	c.inFlightMutex.Lock()
	defer c.inFlightMutex.Unlock()
	heap.Remove(&c.inFlightPQ, item.Index)
}

func (c *Channel) addToDeferredPQ(msg *nsq.Message, timeout time.Duration) {
	c.requeueMutex.Lock()
	defer c.requeueMutex.Unlock()
	absTs := time.Now().UnixNano() + int64(timeout)
	heap.Push(&c.requeuePQ, &pqueue.Item{Value: msg, Priority: -absTs})
}

// messagePump reads messages from either memory or backend and writes
// to the client output go channel
//
// it is also performs in-flight accounting and initiates the auto-requeue
// goroutine
func (c *Channel) messagePump() {
	var msg *nsq.Message
	var buf []byte
	var err error

	for {
		select {
		case msg = <-c.memoryMsgChan:
		case buf = <-c.backend.ReadChan():
			msg, err = nsq.DecodeMessage(buf)
			if err != nil {
				log.Printf("ERROR: failed to decode message - %s", err.Error())
				continue
			}
		case <-c.exitChan:
			return
		}

		msg.Attempts++

		c.clientMessageChan <- msg

		absTs := time.Now().UnixNano() + msgTimeout
		item := &pqueue.Item{Value: msg, Priority: -absTs}
		c.pushInFlightMessage(item)
		c.addToInFlightPQ(item)

		c.getCount++
	}
}

// Close cleanly closes the Channel
func (c *Channel) Close() error {
	var err error

	log.Printf("CHANNEL(%s): closing", c.name)

	close(c.exitChan)
	FlushQueue(c)

	err = c.backend.Close()
	if err != nil {
		return err
	}

	return nil
}

// AddClient adds the ServerClient the Channel's client list
func (c *Channel) AddClient(client *nsq.ServerClient) {
	c.Lock()
	defer c.Unlock()

	found := false
	for _, cli := range c.clients {
		if cli == client {
			found = true
			break
		}
	}

	if !found {
		c.clients = append(c.clients, client)
	}
}

// RemoveClient removes the ServerClient from the Channel's client list
func (c *Channel) RemoveClient(client *nsq.ServerClient) {
	c.Lock()
	defer c.Unlock()

	finalClients := make([]*nsq.ServerClient, 0, len(c.clients))
	for _, cli := range c.clients {
		if cli != client {
			finalClients = append(finalClients, cli)
		}
	}

	c.clients = finalClients
}

func (c *Channel) requeueWorker() {
	pqWorker(&c.requeuePQ, &c.requeueMutex, func(item *pqueue.Item) {
		msg := item.Value.(*nsq.Message)
		c.requeueCount++
		c.doRequeue(msg.Id)
	})
}

func (c *Channel) inFlightWorker() {
	pqWorker(&c.inFlightPQ, &c.inFlightMutex, func(item *pqueue.Item) {
		msg := item.Value.(*nsq.Message)
		c.timeoutCount++
		c.requeueCount++
		c.doRequeue(msg.Id)
	})
}

func pqWorker(pq *pqueue.PriorityQueue, mutex *sync.Mutex, callback func(item *pqueue.Item)) {
	waitTime := defaultWorkerWait
	for {
		<-time.After(waitTime)
		now := time.Now().UnixNano()
		for {
			mutex.Lock()
			if pq.Len() == 0 {
				mutex.Unlock()
				waitTime = defaultWorkerWait
				break
			}
			item := pq.Peek().(*pqueue.Item)
			if now < -item.Priority {
				waitTime = time.Duration((-item.Priority)-now) + time.Millisecond
				mutex.Unlock()
				break
			}
			item = heap.Pop(pq).(*pqueue.Item)
			mutex.Unlock()

			callback(item)
		}
	}
}
<commit_msg>thread safe requeueCount<commit_after>package main

import (
	"../nsq"
	"../util/pqueue"
	"bitly/notify"
	"container/heap"
	"errors"
	"log"
	"sync"
	"time"
)

const (
	// TODO: move this into the nsqd object so its overwritable for tests
	// the amount of time to wait for a client to respond to a message
	msgTimeout = int64(60 * time.Second)

	// the amount of time a worker will wait when idle
	defaultWorkerWait = 250 * time.Millisecond
)

// Channel represents the concrete type for a NSQ channel (and also
// implements the Queue interface)
//
// There can be many channels per topic and each with there own distinct
// clients subscribed.
//
// Channels maintain all client and message, orchestrating in-flight
// messages, timeouts, requeueing, etc.
type Channel struct {
	sync.RWMutex // embed a r/w mutex

	topicName string
	name      string

	backend nsq.BackendQueue

	incomingMessageChan chan *nsq.Message
	memoryMsgChan       chan *nsq.Message
	clientMessageChan   chan *nsq.Message
	exitChan            chan int

	// state tracking
	clients          []*nsq.ServerClient
	requeuePQ        pqueue.PriorityQueue
	requeueMutex     sync.Mutex
	inFlightMessages map[string]interface{}
	inFlightPQ       pqueue.PriorityQueue
	inFlightMutex    sync.Mutex

	// stat counters
	requeueCount int64
	getCount     int64
	putCount     int64
	timeoutCount int64
}

// NewChannel creates a new instance of the Channel type and returns a pointer
func NewChannel(topicName string, channelName string, inMemSize int64, dataPath string, maxBytesPerFile int64) *Channel {
	c := &Channel{
		topicName: topicName,
		name:      channelName,
		// backend names, for uniqueness, automatically include the topic... <topic>:<channel>
		backend:             NewDiskQueue(topicName+":"+channelName, dataPath, maxBytesPerFile),
		incomingMessageChan: make(chan *nsq.Message, 5),
		memoryMsgChan:       make(chan *nsq.Message, inMemSize),
		clientMessageChan:   make(chan *nsq.Message),
		exitChan:            make(chan int),
		clients:             make([]*nsq.ServerClient, 0, 5),
		inFlightMessages:    make(map[string]interface{}),
		inFlightPQ:          pqueue.New(int(inMemSize / 10)),
		requeuePQ:           pqueue.New(int(inMemSize / 10)),
	}
	go c.router()
	go c.messagePump()
	go c.requeueWorker()
	go c.inFlightWorker()
	notify.Post("new_channel", c)
	return c
}

// MemoryChan implements the Queue interface
func (c *Channel) MemoryChan() chan *nsq.Message {
	return c.memoryMsgChan
}

// BackendQueue implements the Queue interface
func (c *Channel) BackendQueue() nsq.BackendQueue {
	return c.backend
}

// InFlight implements the Queue interface
func (c *Channel) InFlight() map[string]interface{} {
	return c.inFlightMessages
}

// PutMessage writes to the appropriate incoming message channel
// (which will be routed asynchronously)
func (c *Channel) PutMessage(msg *nsq.Message) {
	c.incomingMessageChan <- msg
}

// FinishMessage successfully discards an in-flight message
func (c *Channel) FinishMessage(id []byte) error {
	item, err := c.popInFlightMessage(id)
	if err != nil {
		log.Printf("ERROR: failed to finish message(%s) - %s", id, err.Error())
	} else {
		c.removeFromInFlightPQ(item)
	}
	return err
}

// RequeueMessage requeues a message based on `timeoutMs`, ie:
//
// `timeoutMs` == 0 - requeue a message immediately
// `timeoutMs`  > 0 - asynchronously wait for the specified timeout
//     and requeue a message (aka "deferred requeue")
//
func (c *Channel) RequeueMessage(id []byte, timeout time.Duration) error {
	if timeout == 0 {
		return c.doRequeue(id)
	}

	item, err := c.getInFlightMessage(id)
	if err != nil {
		return err
	}
	c.removeFromInFlightPQ(item)
	c.addToDeferredPQ(item.Value.(*nsq.Message), timeout)

	return nil
}

// doRequeue performs the low level operations to requeue a message
func (c *Channel) doRequeue(id []byte) error {
	item, err := c.popInFlightMessage(id)
	if err != nil {
		log.Printf("ERROR: failed to requeue message(%s) - %s", id, err.Error())
	} else {
		c.Lock()
		c.requeueCount++
		c.Unlock()
		msg := item.Value.(*nsq.Message)
		c.incomingMessageChan <- msg
	}
	return err
}

// Router handles the muxing of incoming Channel messages, either writing
// to the in-memory channel or to the backend
func (c *Channel) router() {
	for {
		select {
		case msg := <-c.incomingMessageChan:
			select {
			case c.memoryMsgChan <- msg:
			default:
				err := WriteMessageToBackend(msg, c)
				if err != nil {
					log.Printf("ERROR: failed to write message to backend - %s", err.Error())
					// TODO: requeue?
					continue
				}
			}
			c.putCount++
		case <-c.exitChan:
			return
		}
	}
}

// pushInFlightMessage atomically adds a message to the in-flight dictionary
func (c *Channel) pushInFlightMessage(item *pqueue.Item) error {
	c.Lock()
	defer c.Unlock()

	id := item.Value.(*nsq.Message).Id
	_, ok := c.inFlightMessages[string(id)]
	if ok {
		return errors.New("E_ID_ALREADY_IN_FLIGHT")
	}
	c.inFlightMessages[string(id)] = item

	return nil
}

// popInFlightMessage atomically removes a message from the in-flight dictionary
func (c *Channel) popInFlightMessage(id []byte) (*pqueue.Item, error) {
	c.Lock()
	defer c.Unlock()

	item, ok := c.inFlightMessages[string(id)]
	if !ok {
		return nil, errors.New("E_ID_NOT_IN_FLIGHT")
	}
	delete(c.inFlightMessages, string(id))

	return item.(*pqueue.Item), nil
}

// getInFlightMessage retrieves a message from the in-flight dictionary by ID
func (c *Channel) getInFlightMessage(id []byte) (*pqueue.Item, error) {
	c.RLock()
	defer c.RUnlock()

	item, ok := c.inFlightMessages[string(id)]
	if !ok {
		return nil, errors.New("E_ID_NOT_IN_FLIGHT")
	}

	return item.(*pqueue.Item), nil
}

func (c *Channel) addToInFlightPQ(item *pqueue.Item) {
	c.inFlightMutex.Lock()
	defer c.inFlightMutex.Unlock()
	heap.Push(&c.inFlightPQ, item)
}

func (c *Channel) removeFromInFlightPQ(item *pqueue.Item) {
	c.inFlightMutex.Lock()
	defer c.inFlightMutex.Unlock()
	heap.Remove(&c.inFlightPQ, item.Index)
}

func (c *Channel) addToDeferredPQ(msg *nsq.Message, timeout time.Duration) {
	c.requeueMutex.Lock()
	defer c.requeueMutex.Unlock()
	absTs := time.Now().UnixNano() + int64(timeout)
	heap.Push(&c.requeuePQ, &pqueue.Item{Value: msg, Priority: -absTs})
}

// messagePump reads messages from either memory or backend and writes
// to the client output go channel
//
// it is also performs in-flight accounting and initiates the auto-requeue
// goroutine
func (c *Channel) messagePump() {
	var msg *nsq.Message
	var buf []byte
	var err error

	for {
		select {
		case msg = <-c.memoryMsgChan:
		case buf = <-c.backend.ReadChan():
			msg, err = nsq.DecodeMessage(buf)
			if err != nil {
				log.Printf("ERROR: failed to decode message - %s", err.Error())
				continue
			}
		case <-c.exitChan:
			return
		}

		msg.Attempts++

		c.clientMessageChan <- msg

		absTs := time.Now().UnixNano() + msgTimeout
		item := &pqueue.Item{Value: msg, Priority: -absTs}
		c.pushInFlightMessage(item)
		c.addToInFlightPQ(item)

		c.getCount++
	}
}

// Close cleanly closes the Channel
func (c *Channel) Close() error {
	var err error

	log.Printf("CHANNEL(%s): closing", c.name)

	close(c.exitChan)
	FlushQueue(c)

	err = c.backend.Close()
	if err != nil {
		return err
	}

	return nil
}

// AddClient adds the ServerClient the Channel's client list
func (c *Channel) AddClient(client *nsq.ServerClient) {
	c.Lock()
	defer c.Unlock()

	found := false
	for _, cli := range c.clients {
		if cli == client {
			found = true
			break
		}
	}

	if !found {
		c.clients = append(c.clients, client)
	}
}

// RemoveClient removes the ServerClient from the Channel's client list
func (c *Channel) RemoveClient(client *nsq.ServerClient) {
	c.Lock()
	defer c.Unlock()

	finalClients := make([]*nsq.ServerClient, 0, len(c.clients))
	for _, cli := range c.clients {
		if cli != client {
			finalClients = append(finalClients, cli)
		}
	}

	c.clients = finalClients
}

func (c *Channel) requeueWorker() {
	pqWorker(&c.requeuePQ, &c.requeueMutex, func(item *pqueue.Item) {
		msg := item.Value.(*nsq.Message)
		c.doRequeue(msg.Id)
	})
}

func (c *Channel) inFlightWorker() {
	pqWorker(&c.inFlightPQ, &c.inFlightMutex, func(item *pqueue.Item) {
		msg := item.Value.(*nsq.Message)
		c.timeoutCount++ // do not need to lock around this
		c.doRequeue(msg.Id)
	})
}

func pqWorker(pq *pqueue.PriorityQueue, mutex *sync.Mutex, callback func(item *pqueue.Item)) {
	waitTime := defaultWorkerWait
	for {
		<-time.After(waitTime)
		now := time.Now().UnixNano()
		for {
			mutex.Lock()
			if pq.Len() == 0 {
				mutex.Unlock()
				waitTime = defaultWorkerWait
				break
			}
			item := pq.Peek().(*pqueue.Item)
			if now < -item.Priority {
				waitTime = time.Duration((-item.Priority)-now) + time.Millisecond
				mutex.Unlock()
				break
			}
			item = heap.Pop(pq).(*pqueue.Item)
			mutex.Unlock()

			callback(item)
		}
	}
}
<|endoftext|>