<commit_before>package commands

import (
	"errors"
	"fmt"
	"os"
	"strconv"
	"time"

	"github.com/concourse/concourse/atc"
	"github.com/concourse/concourse/fly/commands/internal/displayhelpers"
	"github.com/concourse/concourse/fly/commands/internal/flaghelpers"
	"github.com/concourse/concourse/fly/rc"
	"github.com/concourse/concourse/fly/ui"
	"github.com/concourse/concourse/go-concourse/concourse"
	"github.com/fatih/color"
)

const timeDateLayout = "2006-01-02@15:04:05-0700"
const inputTimeLayout = "2006-01-02 15:04:05"

type BuildsCommand struct {
	AllTeams    bool                     `short:"a" long:"all-teams" description:"Show builds for the all teams that user has access to"`
	Count       int                      `short:"c" long:"count" default:"50" description:"Number of builds you want to limit the return to"`
	CurrentTeam bool                     `long:"current-team" description:"Show builds for the currently targeted team"`
	Job         flaghelpers.JobFlag      `short:"j" long:"job" value-name:"PIPELINE/JOB" description:"Name of a job to get builds for"`
	Json        bool                     `long:"json" description:"Print command result as JSON"`
	Pipeline    flaghelpers.PipelineFlag `short:"p" long:"pipeline" description:"Name of a pipeline to get builds for"`
	Teams       []string                 `short:"t"  long:"team" description:"Show builds for these teams"`
	Since       string                   `long:"since" description:"Start of the range to filter builds"`
	Until       string                   `long:"until" description:"End of the range to filter builds"`
}

func (command *BuildsCommand) Execute([]string) error {
	target, err := rc.LoadTarget(Fly.Target, Fly.Verbose)
	if err != nil {
		return err
	}

	err = target.Validate()
	if err != nil {
		return err
	}

	var (
		timeSince time.Time
		timeUntil time.Time
		page      = concourse.Page{}
	)

	if command.Since != "" {
		timeSince, err = time.ParseInLocation(inputTimeLayout, command.Since, time.Now().Location())
		if err != nil {
			return errors.New("Since time should be in the format: " + inputTimeLayout)
		}
		page.Since = int(timeSince.Unix())
	}

	if command.Until != "" {
		timeUntil, err = time.ParseInLocation(inputTimeLayout, command.Until, time.Now().Location())
		if err != nil {
			return errors.New("Until time should be in the format: " + inputTimeLayout)
		}
		page.Until = int(timeUntil.Unix())
	}

	if timeSince.After(timeUntil) && command.Since != "" && command.Until != "" {
		return errors.New("Cannot have --since after --until")
	}

	if command.pipelineFlag() && command.jobFlag() {
		return errors.New("Cannot specify both --pipeline and --job")
	}

	if command.CurrentTeam && command.AllTeams {
		return errors.New("Cannot specify both --all-teams and --current-team")
	}

	if len(command.Teams) > 0 && command.AllTeams {
		return errors.New("Cannot specify both --all-teams and --team")
	}

	var (
		builds = make([]atc.Build, 0)
		teams  = make([]concourse.Team, 0)
	)

	page.Limit = command.Count
	page.Timestamps = command.Since != "" || command.Until != ""

	currentTeam := target.Team()
	client := target.Client()

	if command.pipelineFlag() {
		err = command.Pipeline.Validate()
		if err != nil {
			return err
		}

		var found bool
		builds, _, found, err = currentTeam.PipelineBuilds(
			string(command.Pipeline),
			page,
		)
		if err != nil {
			return err
		}

		if !found {
			displayhelpers.Failf("pipeline not found")
		}
	} else if command.jobFlag() {
		var found bool
		builds, _, found, err = currentTeam.JobBuilds(
			command.Job.PipelineName,
			command.Job.JobName,
			page,
		)
		if err != nil {
			return err
		}

		if !found {
			displayhelpers.Failf("pipeline/job not found")
		}
	} else if command.AllTeams {
		atcTeams, err := client.ListTeams()
		if err != nil {
			return err
		}

		for _, atcTeam := range atcTeams {
			teams = append(teams, client.Team(atcTeam.Name))
		}
	} else if len(command.Teams) > 0 || command.CurrentTeam {
		if command.CurrentTeam {
			teams = append(teams, currentTeam)
		}

		for _, teamName := range command.Teams {
			teams = append(teams, client.Team(teamName))
		}
	} else {
		builds, _, err = client.Builds(page)
		if err != nil {
			return err
		}
	}

	for _, team := range teams {
		teamBuilds, _, err := team.Builds(page)
		if err != nil {
			return err
		}

		builds = append(builds, teamBuilds...)
	}

	if command.Json {
		err = displayhelpers.JsonPrint(builds)
		if err != nil {
			return err
		}
		return nil
	}

	table := ui.Table{
		Headers: ui.TableRow{
			{Contents: "id", Color: color.New(color.Bold)},
			{Contents: "pipeline/job", Color: color.New(color.Bold)},
			{Contents: "build", Color: color.New(color.Bold)},
			{Contents: "status", Color: color.New(color.Bold)},
			{Contents: "start", Color: color.New(color.Bold)},
			{Contents: "end", Color: color.New(color.Bold)},
			{Contents: "duration", Color: color.New(color.Bold)},
			{Contents: "team", Color: color.New(color.Bold)},
		},
	}

	var rangeUntil int
	if command.Count < len(builds) {
		rangeUntil = command.Count
	} else {
		rangeUntil = len(builds)
	}

	for _, b := range builds[:rangeUntil] {
		startTimeCell, endTimeCell, durationCell := populateTimeCells(time.Unix(b.StartTime, 0), time.Unix(b.EndTime, 0))

		var pipelineJobCell, buildCell ui.TableCell
		if b.PipelineName == "" {
			pipelineJobCell.Contents = "one-off"
			buildCell.Contents = "n/a"
		} else {
			pipelineJobCell.Contents = fmt.Sprintf("%s/%s", b.PipelineName, b.JobName)
			buildCell.Contents = b.Name
		}

		var statusCell ui.TableCell
		statusCell.Contents = b.Status

		switch b.Status {
		case "pending":
			statusCell.Color = ui.PendingColor
		case "started":
			statusCell.Color = ui.StartedColor
		case "succeeded":
			statusCell.Color = ui.SucceededColor
		case "failed":
			statusCell.Color = ui.FailedColor
		case "errored":
			statusCell.Color = ui.ErroredColor
		case "aborted":
			statusCell.Color = ui.AbortedColor
		case "paused":
			statusCell.Color = ui.PausedColor
		}

		table.Data = append(table.Data, []ui.TableCell{
			{Contents: strconv.Itoa(b.ID)},
			pipelineJobCell,
			buildCell,
			statusCell,
			startTimeCell,
			endTimeCell,
			durationCell,
			{Contents: b.TeamName},
		})
	}

	return table.Render(os.Stdout, Fly.PrintTableHeaders)
}

func populateTimeCells(startTime time.Time, endTime time.Time) (ui.TableCell, ui.TableCell, ui.TableCell) {
	var startTimeCell ui.TableCell
	var endTimeCell ui.TableCell
	var durationCell ui.TableCell

	startTime = startTime.Local()
	endTime = endTime.Local()
	zeroTime := time.Unix(0, 0)

	if startTime == zeroTime {
		startTimeCell.Contents = "n/a"
	} else {
		startTimeCell.Contents = startTime.Format(timeDateLayout)
	}

	if endTime == zeroTime {
		endTimeCell.Contents = "n/a"
		durationCell.Contents = fmt.Sprintf("%v+", roundSecondsOffDuration(time.Since(startTime)))
	} else {
		endTimeCell.Contents = endTime.Format(timeDateLayout)
		durationCell.Contents = endTime.Sub(startTime).String()
	}

	if startTime == zeroTime && endTime == zeroTime {
		durationCell.Contents = "n/a"
	}

	return startTimeCell, endTimeCell, durationCell
}

func roundSecondsOffDuration(d time.Duration) time.Duration {
	return d - (d % time.Second)
}

func (command *BuildsCommand) jobFlag() bool {
	return command.Job.PipelineName != "" && command.Job.JobName != ""
}

func (command *BuildsCommand) pipelineFlag() bool {
	return command.Pipeline != ""
}
<commit_msg>change flag of fly builds --team from t to n because t is already used<commit_after>package commands

import (
	"errors"
	"fmt"
	"os"
	"strconv"
	"time"

	"github.com/concourse/concourse/atc"
	"github.com/concourse/concourse/fly/commands/internal/displayhelpers"
	"github.com/concourse/concourse/fly/commands/internal/flaghelpers"
	"github.com/concourse/concourse/fly/rc"
	"github.com/concourse/concourse/fly/ui"
	"github.com/concourse/concourse/go-concourse/concourse"
	"github.com/fatih/color"
)

const timeDateLayout = "2006-01-02@15:04:05-0700"
const inputTimeLayout = "2006-01-02 15:04:05"

type BuildsCommand struct {
	AllTeams    bool                     `short:"a" long:"all-teams" description:"Show builds for the all teams that user has access to"`
	Count       int                      `short:"c" long:"count" default:"50" description:"Number of builds you want to limit the return to"`
	CurrentTeam bool                     `long:"current-team" description:"Show builds for the currently targeted team"`
	Job         flaghelpers.JobFlag      `short:"j" long:"job" value-name:"PIPELINE/JOB" description:"Name of a job to get builds for"`
	Json        bool                     `long:"json" description:"Print command result as JSON"`
	Pipeline    flaghelpers.PipelineFlag `short:"p" long:"pipeline" description:"Name of a pipeline to get builds for"`
	Teams       []string                 `short:"n"  long:"team" description:"Show builds for these teams"`
	Since       string                   `long:"since" description:"Start of the range to filter builds"`
	Until       string                   `long:"until" description:"End of the range to filter builds"`
}

func (command *BuildsCommand) Execute([]string) error {
	target, err := rc.LoadTarget(Fly.Target, Fly.Verbose)
	if err != nil {
		return err
	}

	err = target.Validate()
	if err != nil {
		return err
	}

	var (
		timeSince time.Time
		timeUntil time.Time
		page      = concourse.Page{}
	)

	if command.Since != "" {
		timeSince, err = time.ParseInLocation(inputTimeLayout, command.Since, time.Now().Location())
		if err != nil {
			return errors.New("Since time should be in the format: " + inputTimeLayout)
		}
		page.Since = int(timeSince.Unix())
	}

	if command.Until != "" {
		timeUntil, err = time.ParseInLocation(inputTimeLayout, command.Until, time.Now().Location())
		if err != nil {
			return errors.New("Until time should be in the format: " + inputTimeLayout)
		}
		page.Until = int(timeUntil.Unix())
	}

	if timeSince.After(timeUntil) && command.Since != "" && command.Until != "" {
		return errors.New("Cannot have --since after --until")
	}

	if command.pipelineFlag() && command.jobFlag() {
		return errors.New("Cannot specify both --pipeline and --job")
	}

	if command.CurrentTeam && command.AllTeams {
		return errors.New("Cannot specify both --all-teams and --current-team")
	}

	if len(command.Teams) > 0 && command.AllTeams {
		return errors.New("Cannot specify both --all-teams and --team")
	}

	var (
		builds = make([]atc.Build, 0)
		teams  = make([]concourse.Team, 0)
	)

	page.Limit = command.Count
	page.Timestamps = command.Since != "" || command.Until != ""

	currentTeam := target.Team()
	client := target.Client()

	if command.pipelineFlag() {
		err = command.Pipeline.Validate()
		if err != nil {
			return err
		}

		var found bool
		builds, _, found, err = currentTeam.PipelineBuilds(
			string(command.Pipeline),
			page,
		)
		if err != nil {
			return err
		}

		if !found {
			displayhelpers.Failf("pipeline not found")
		}
	} else if command.jobFlag() {
		var found bool
		builds, _, found, err = currentTeam.JobBuilds(
			command.Job.PipelineName,
			command.Job.JobName,
			page,
		)
		if err != nil {
			return err
		}

		if !found {
			displayhelpers.Failf("pipeline/job not found")
		}
	} else if command.AllTeams {
		atcTeams, err := client.ListTeams()
		if err != nil {
			return err
		}

		for _, atcTeam := range atcTeams {
			teams = append(teams, client.Team(atcTeam.Name))
		}
	} else if len(command.Teams) > 0 || command.CurrentTeam {
		if command.CurrentTeam {
			teams = append(teams, currentTeam)
		}

		for _, teamName := range command.Teams {
			teams = append(teams, client.Team(teamName))
		}
	} else {
		builds, _, err = client.Builds(page)
		if err != nil {
			return err
		}
	}

	for _, team := range teams {
		teamBuilds, _, err := team.Builds(page)
		if err != nil {
			return err
		}

		builds = append(builds, teamBuilds...)
	}

	if command.Json {
		err = displayhelpers.JsonPrint(builds)
		if err != nil {
			return err
		}
		return nil
	}

	table := ui.Table{
		Headers: ui.TableRow{
			{Contents: "id", Color: color.New(color.Bold)},
			{Contents: "pipeline/job", Color: color.New(color.Bold)},
			{Contents: "build", Color: color.New(color.Bold)},
			{Contents: "status", Color: color.New(color.Bold)},
			{Contents: "start", Color: color.New(color.Bold)},
			{Contents: "end", Color: color.New(color.Bold)},
			{Contents: "duration", Color: color.New(color.Bold)},
			{Contents: "team", Color: color.New(color.Bold)},
		},
	}

	var rangeUntil int
	if command.Count < len(builds) {
		rangeUntil = command.Count
	} else {
		rangeUntil = len(builds)
	}

	for _, b := range builds[:rangeUntil] {
		startTimeCell, endTimeCell, durationCell := populateTimeCells(time.Unix(b.StartTime, 0), time.Unix(b.EndTime, 0))

		var pipelineJobCell, buildCell ui.TableCell
		if b.PipelineName == "" {
			pipelineJobCell.Contents = "one-off"
			buildCell.Contents = "n/a"
		} else {
			pipelineJobCell.Contents = fmt.Sprintf("%s/%s", b.PipelineName, b.JobName)
			buildCell.Contents = b.Name
		}

		var statusCell ui.TableCell
		statusCell.Contents = b.Status

		switch b.Status {
		case "pending":
			statusCell.Color = ui.PendingColor
		case "started":
			statusCell.Color = ui.StartedColor
		case "succeeded":
			statusCell.Color = ui.SucceededColor
		case "failed":
			statusCell.Color = ui.FailedColor
		case "errored":
			statusCell.Color = ui.ErroredColor
		case "aborted":
			statusCell.Color = ui.AbortedColor
		case "paused":
			statusCell.Color = ui.PausedColor
		}

		table.Data = append(table.Data, []ui.TableCell{
			{Contents: strconv.Itoa(b.ID)},
			pipelineJobCell,
			buildCell,
			statusCell,
			startTimeCell,
			endTimeCell,
			durationCell,
			{Contents: b.TeamName},
		})
	}

	return table.Render(os.Stdout, Fly.PrintTableHeaders)
}

func populateTimeCells(startTime time.Time, endTime time.Time) (ui.TableCell, ui.TableCell, ui.TableCell) {
	var startTimeCell ui.TableCell
	var endTimeCell ui.TableCell
	var durationCell ui.TableCell

	startTime = startTime.Local()
	endTime = endTime.Local()
	zeroTime := time.Unix(0, 0)

	if startTime == zeroTime {
		startTimeCell.Contents = "n/a"
	} else {
		startTimeCell.Contents = startTime.Format(timeDateLayout)
	}

	if endTime == zeroTime {
		endTimeCell.Contents = "n/a"
		durationCell.Contents = fmt.Sprintf("%v+", roundSecondsOffDuration(time.Since(startTime)))
	} else {
		endTimeCell.Contents = endTime.Format(timeDateLayout)
		durationCell.Contents = endTime.Sub(startTime).String()
	}

	if startTime == zeroTime && endTime == zeroTime {
		durationCell.Contents = "n/a"
	}

	return startTimeCell, endTimeCell, durationCell
}

func roundSecondsOffDuration(d time.Duration) time.Duration {
	return d - (d % time.Second)
}

func (command *BuildsCommand) jobFlag() bool {
	return command.Job.PipelineName != "" && command.Job.JobName != ""
}

func (command *BuildsCommand) pipelineFlag() bool {
	return command.Pipeline != ""
}
<|endoftext|>


<commit_before>package frames

import (
	"bytes"
	"errors"
	"io"
	"io/ioutil"

	"github.com/lucas-clemente/quic-go/protocol"
	"github.com/lucas-clemente/quic-go/qerr"
	"github.com/lucas-clemente/quic-go/utils"
)

// A StreamFrame of QUIC
type StreamFrame struct {
	FinBit         bool
	StreamID       protocol.StreamID
	Offset         protocol.ByteCount
	Data           []byte
	DataLenPresent bool
}

var (
	errInvalidStreamIDLen = errors.New("StreamFrame: Invalid StreamID length")
	errInvalidOffsetLen   = errors.New("StreamFrame: Invalid offset length")
)

// ParseStreamFrame reads a stream frame. The type byte must not have been read yet.
func ParseStreamFrame(r *bytes.Reader) (*StreamFrame, error) {
	frame := &StreamFrame{}

	typeByte, err := r.ReadByte()
	if err != nil {
		return nil, err
	}

	frame.FinBit = typeByte&0x40 > 0
	frame.DataLenPresent = typeByte&0x20 > 0
	offsetLen := typeByte & 0x1C >> 2
	if offsetLen != 0 {
		offsetLen++
	}
	streamIDLen := typeByte&0x03 + 1

	sid, err := utils.ReadUintN(r, streamIDLen)
	if err != nil {
		return nil, err
	}
	frame.StreamID = protocol.StreamID(sid)

	offset, err := utils.ReadUintN(r, offsetLen)
	if err != nil {
		return nil, err
	}
	frame.Offset = protocol.ByteCount(offset)

	var dataLen uint16
	if frame.DataLenPresent {
		dataLen, err = utils.ReadUint16(r)
		if err != nil {
			return nil, err
		}
	}

	if dataLen > uint16(protocol.MaxPacketSize) {
		return nil, qerr.Error(qerr.InvalidStreamData, "data len too large")
	}

	if dataLen == 0 {
		// The rest of the packet is data
		frame.Data, err = ioutil.ReadAll(r)
		if err != nil {
			return nil, err
		}
	} else {
		frame.Data = make([]byte, dataLen)
		if _, err := io.ReadFull(r, frame.Data); err != nil {
			return nil, err
		}
	}

	if !frame.FinBit && len(frame.Data) == 0 {
		return nil, qerr.EmptyStreamFrameNoFin
	}

	return frame, nil
}

// WriteStreamFrame writes a stream frame.
func (f *StreamFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error {
	if len(f.Data) == 0 && !f.FinBit {
		return errors.New("StreamFrame: attempting to write empty frame without FIN")
	}

	typeByte := uint8(0x80) // sets the leftmost bit to 1

	if f.FinBit {
		typeByte ^= 0x40
	}

	if f.DataLenPresent {
		typeByte ^= 0x20
	}

	offsetLength := f.getOffsetLength()

	if offsetLength > 0 {
		typeByte ^= (uint8(offsetLength) - 1) << 2
	}

	streamIDLen := f.calculateStreamIDLength()
	typeByte ^= streamIDLen - 1

	b.WriteByte(typeByte)

	switch streamIDLen {
	case 1:
		b.WriteByte(uint8(f.StreamID))
	case 2:
		utils.WriteUint16(b, uint16(f.StreamID))
	case 3:
		utils.WriteUint24(b, uint32(f.StreamID))
	case 4:
		utils.WriteUint32(b, uint32(f.StreamID))
	default:
		return errInvalidStreamIDLen
	}

	switch offsetLength {
	case 0:
	case 2:
		utils.WriteUint16(b, uint16(f.Offset))
	case 3:
		utils.WriteUint24(b, uint32(f.Offset))
	case 4:
		utils.WriteUint32(b, uint32(f.Offset))
	case 5:
		utils.WriteUint40(b, uint64(f.Offset))
	case 6:
		utils.WriteUint48(b, uint64(f.Offset))
	case 7:
		utils.WriteUint56(b, uint64(f.Offset))
	case 8:
		utils.WriteUint64(b, uint64(f.Offset))
	default:
		return errInvalidOffsetLen
	}

	if f.DataLenPresent {
		utils.WriteUint16(b, uint16(len(f.Data)))
	}

	b.Write(f.Data)

	return nil
}

func (f *StreamFrame) calculateStreamIDLength() uint8 {
	if f.StreamID < (1 << 8) {
		return 1
	} else if f.StreamID < (1 << 16) {
		return 2
	} else if f.StreamID < (1 << 24) {
		return 3
	}
	return 4
}

func (f *StreamFrame) getOffsetLength() protocol.ByteCount {
	if f.Offset == 0 {
		return 0
	}
	if f.Offset < (1 << 16) {
		return 2
	}
	if f.Offset < (1 << 24) {
		return 3
	}
	if f.Offset < (1 << 32) {
		return 4
	}
	if f.Offset < (1 << 40) {
		return 5
	}
	if f.Offset < (1 << 48) {
		return 6
	}
	if f.Offset < (1 << 56) {
		return 7
	}
	return 8
}

// MinLength returns the length of the header of a StreamFrame
// the total length of the StreamFrame is frame.MinLength() + frame.DataLen()
func (f *StreamFrame) MinLength(protocol.VersionNumber) (protocol.ByteCount, error) {
	length := protocol.ByteCount(1) + protocol.ByteCount(f.calculateStreamIDLength()) + f.getOffsetLength()
	if f.DataLenPresent {
		length += 2
	}

	return length, nil
}

// DataLen gives the length of data in bytes
func (f *StreamFrame) DataLen() protocol.ByteCount {
	return protocol.ByteCount(len(f.Data))
}
<commit_msg>improve reading of stream frames, increasing transfer speed by ~10%<commit_after>package frames

import (
	"bytes"
	"errors"

	"github.com/lucas-clemente/quic-go/protocol"
	"github.com/lucas-clemente/quic-go/qerr"
	"github.com/lucas-clemente/quic-go/utils"
)

// A StreamFrame of QUIC
type StreamFrame struct {
	FinBit         bool
	StreamID       protocol.StreamID
	Offset         protocol.ByteCount
	Data           []byte
	DataLenPresent bool
}

var (
	errInvalidStreamIDLen = errors.New("StreamFrame: Invalid StreamID length")
	errInvalidOffsetLen   = errors.New("StreamFrame: Invalid offset length")
)

// ParseStreamFrame reads a stream frame. The type byte must not have been read yet.
func ParseStreamFrame(r *bytes.Reader) (*StreamFrame, error) {
	frame := &StreamFrame{}

	typeByte, err := r.ReadByte()
	if err != nil {
		return nil, err
	}

	frame.FinBit = typeByte&0x40 > 0
	frame.DataLenPresent = typeByte&0x20 > 0
	offsetLen := typeByte & 0x1C >> 2
	if offsetLen != 0 {
		offsetLen++
	}
	streamIDLen := typeByte&0x03 + 1

	sid, err := utils.ReadUintN(r, streamIDLen)
	if err != nil {
		return nil, err
	}
	frame.StreamID = protocol.StreamID(sid)

	offset, err := utils.ReadUintN(r, offsetLen)
	if err != nil {
		return nil, err
	}
	frame.Offset = protocol.ByteCount(offset)

	var dataLen uint16
	if frame.DataLenPresent {
		dataLen, err = utils.ReadUint16(r)
		if err != nil {
			return nil, err
		}
	}

	if dataLen > uint16(protocol.MaxPacketSize) {
		return nil, qerr.Error(qerr.InvalidStreamData, "data len too large")
	}

	if dataLen == 0 {
		// The rest of the packet is data
		dataLen = uint16(r.Len())
	}
	if dataLen != 0 {
		frame.Data = make([]byte, dataLen)
		n, err := r.Read(frame.Data)
		if n != int(dataLen) {
			return nil, errors.New("BUG: StreamFrame could not read dataLen bytes")
		}
		if err != nil {
			return nil, err
		}
	}

	if !frame.FinBit && len(frame.Data) == 0 {
		return nil, qerr.EmptyStreamFrameNoFin
	}

	return frame, nil
}

// WriteStreamFrame writes a stream frame.
func (f *StreamFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error {
	if len(f.Data) == 0 && !f.FinBit {
		return errors.New("StreamFrame: attempting to write empty frame without FIN")
	}

	typeByte := uint8(0x80) // sets the leftmost bit to 1

	if f.FinBit {
		typeByte ^= 0x40
	}

	if f.DataLenPresent {
		typeByte ^= 0x20
	}

	offsetLength := f.getOffsetLength()

	if offsetLength > 0 {
		typeByte ^= (uint8(offsetLength) - 1) << 2
	}

	streamIDLen := f.calculateStreamIDLength()
	typeByte ^= streamIDLen - 1

	b.WriteByte(typeByte)

	switch streamIDLen {
	case 1:
		b.WriteByte(uint8(f.StreamID))
	case 2:
		utils.WriteUint16(b, uint16(f.StreamID))
	case 3:
		utils.WriteUint24(b, uint32(f.StreamID))
	case 4:
		utils.WriteUint32(b, uint32(f.StreamID))
	default:
		return errInvalidStreamIDLen
	}

	switch offsetLength {
	case 0:
	case 2:
		utils.WriteUint16(b, uint16(f.Offset))
	case 3:
		utils.WriteUint24(b, uint32(f.Offset))
	case 4:
		utils.WriteUint32(b, uint32(f.Offset))
	case 5:
		utils.WriteUint40(b, uint64(f.Offset))
	case 6:
		utils.WriteUint48(b, uint64(f.Offset))
	case 7:
		utils.WriteUint56(b, uint64(f.Offset))
	case 8:
		utils.WriteUint64(b, uint64(f.Offset))
	default:
		return errInvalidOffsetLen
	}

	if f.DataLenPresent {
		utils.WriteUint16(b, uint16(len(f.Data)))
	}

	b.Write(f.Data)

	return nil
}

func (f *StreamFrame) calculateStreamIDLength() uint8 {
	if f.StreamID < (1 << 8) {
		return 1
	} else if f.StreamID < (1 << 16) {
		return 2
	} else if f.StreamID < (1 << 24) {
		return 3
	}
	return 4
}

func (f *StreamFrame) getOffsetLength() protocol.ByteCount {
	if f.Offset == 0 {
		return 0
	}
	if f.Offset < (1 << 16) {
		return 2
	}
	if f.Offset < (1 << 24) {
		return 3
	}
	if f.Offset < (1 << 32) {
		return 4
	}
	if f.Offset < (1 << 40) {
		return 5
	}
	if f.Offset < (1 << 48) {
		return 6
	}
	if f.Offset < (1 << 56) {
		return 7
	}
	return 8
}

// MinLength returns the length of the header of a StreamFrame
// the total length of the StreamFrame is frame.MinLength() + frame.DataLen()
func (f *StreamFrame) MinLength(protocol.VersionNumber) (protocol.ByteCount, error) {
	length := protocol.ByteCount(1) + protocol.ByteCount(f.calculateStreamIDLength()) + f.getOffsetLength()
	if f.DataLenPresent {
		length += 2
	}

	return length, nil
}

// DataLen gives the length of data in bytes
func (f *StreamFrame) DataLen() protocol.ByteCount {
	return protocol.ByteCount(len(f.Data))
}
<|endoftext|>


<commit_before>package web

import (
	"net/http"
	"net/http/httptest"
	"net/url"
	"psychic-rat/sess"
	"testing"
)

func TestCSRFBlock(t *testing.T) {
	values := url.Values{"item": {"1"}}
	req := &http.Request{Method: "POST", PostForm: values}
	writer := httptest.NewRecorder()
	called := false
	csrfFunc := csrfProtect(func(w http.ResponseWriter, r *http.Request) {
		called = true
	})
	csrfFunc(writer, req)
	if called {
		t.Fatalf("protection failed to stop handler call")
	}
	if writer.Result().StatusCode != http.StatusForbidden {
		t.Fatalf("expected 403, got %s", writer.Result().Status)
	}
}

func TestCSRFPass(t *testing.T) {
	writer := httptest.NewRecorder()
	req := &http.Request{Method: "POST"}
	token, err := sess.NewSessionStore(req).SetCSRF(writer)
	if err != nil {
		t.Fatal(err)
	}
	req.PostForm = url.Values{"item": {"1"}, "csrf": {token}}
	called := false
	csrfFunc := csrfProtect(func(w http.ResponseWriter, r *http.Request) {
		called = true
	})
	csrfFunc(writer, req)
	if !called {
		t.Fatalf("handler not called")
	}
}

func TestCSRFIgnore(t *testing.T) {
	writer := httptest.NewRecorder()
	req := &http.Request{Method: "GET"}
	called := false
	csrfFunc := csrfProtect(func(w http.ResponseWriter, r *http.Request) {
		called = true
	})
	csrfFunc(writer, req)
	if !called {
		t.Fatalf("handler not called")
	}
}
<commit_msg>Fixed web tests<commit_after>package web

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"psychic-rat/types"
	"testing"
)

type mockUserHandler struct {
	csrfResult   bool
	tokenChecked string
}

func (m *mockUserHandler) GetLoggedInUser(*http.Request) (*types.User, error) {
	panic("not implemented")
}

func (m *mockUserHandler) VerifyUserCSRF(r *http.Request, token string) error {
	m.tokenChecked = token
	if m.csrfResult {
		return nil
	}
	return fmt.Errorf("CSRF failed")
}

func TestCSRFBlock(t *testing.T) {
	values := url.Values{"item": {"1"}}
	req := &http.Request{Method: "POST", PostForm: values}
	writer := httptest.NewRecorder()
	called := false
	Init(&mockUserHandler{csrfResult: false})
	csrfFunc := csrfProtect(func(w http.ResponseWriter, r *http.Request) {
		called = true
	})
	csrfFunc(writer, req)
	if called {
		t.Fatalf("protection failed to stop handler call")
	}
	if writer.Result().StatusCode != http.StatusForbidden {
		t.Fatalf("expected 403, got %s", writer.Result().Status)
	}
}

func TestCSRFPass(t *testing.T) {
	writer := httptest.NewRecorder()
	req := &http.Request{Method: "POST"}
	token := "1234"
	req.PostForm = url.Values{"item": {"1"}, "csrf": {token}}
	mock := &mockUserHandler{csrfResult: true}
	Init(mock)
	called := false
	csrfFunc := csrfProtect(func(w http.ResponseWriter, r *http.Request) {
		called = true
	})
	csrfFunc(writer, req)
	if !called {
		t.Fatalf("handler not called")
	}
	if mock.tokenChecked != token {
		t.Fatalf("expected token = %s in call to VerifyUserCSRF, got %s", token, mock.tokenChecked)
	}
}

func TestCSRFIgnore(t *testing.T) {
	writer := httptest.NewRecorder()
	req := &http.Request{Method: "GET"}
	called := false
	csrfFunc := csrfProtect(func(w http.ResponseWriter, r *http.Request) {
		called = true
	})
	csrfFunc(writer, req)
	if !called {
		t.Fatalf("handler not called")
	}
}
<|endoftext|>


<commit_before><commit_msg>Specify actual connection error<commit_after><|endoftext|>


<commit_before><commit_msg>Adding raw quotes (but they just work as regular single or double quotes) Also added definition of brackets (one day I would like to treat brackets text as a single token)<commit_after><|endoftext|>


<commit_before>// Copyright 2019 The Go Cloud 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
//
//     https://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 secrets provides an easy and portable way to encrypt and decrypt
// messages.
//
// Subpackages contain distinct implementations of secrets for various
// providers, including Cloud and on-prem solutions. For example, "localsecrets"
// supports encryption/decryption using a locally provided key. Your application
// should import one of these provider-specific subpackages and use its exported
// function(s) to create a *Keeper; do not use the NewKeeper function in this
// package. For example:
//
//  keeper := localsecrets.NewKeeper(myKey)
//  encrypted, err := keeper.Encrypt(ctx.Background(), []byte("text"))
//  ...
//
// Then, write your application code using the *Keeper type. You can easily
// reconfigure your initialization code to choose a different provider.
// You can develop your application locally using localsecrets, or deploy it to
// multiple Cloud providers. You may find http://github.com/google/wire useful
// for managing your initialization code.
package secrets // import "gocloud.dev/secrets"

import (
	"context"

	"gocloud.dev/secrets/driver"
)

// Keeper does encryption and decryption. To create a Keeper, use constructors
// found in provider-specific subpackages.
type Keeper struct {
	k driver.Keeper
}

// NewKeeper is intended for use by provider implementations.
var NewKeeper = newKeeper

// newKeeper creates a Keeper.
func newKeeper(k driver.Keeper) *Keeper {
	return &Keeper{k: k}
}

// Encrypt encrypts the plaintext and returns the cipher message.
func (k *Keeper) Encrypt(ctx context.Context, plaintext []byte) ([]byte, error) {
	b, err := k.k.Encrypt(ctx, plaintext)
	if err != nil {
		return nil, wrapError(k, err)
	}
	return b, nil
}

// Decrypt decrypts the ciphertext and returns the plaintext.
func (k *Keeper) Decrypt(ctx context.Context, ciphertext []byte) ([]byte, error) {
	b, err := k.k.Decrypt(ctx, ciphertext)
	if err != nil {
		return nil, wrapError(k, err)
	}
	return b, nil
}

// wrappedError is used to wrap all errors returned by drivers so that users are
// not given access to provider-specific errors.
type wrappedError struct {
	err error
	k   driver.Keeper
}

func wrapError(k driver.Keeper, err error) error {
	if err == nil {
		return nil
	}
	return &wrappedError{k: k, err: err}
}

func (w *wrappedError) Error() string {
	return "secrets: " + w.err.Error()
}
<commit_msg>secrets: add tracing (#1138)<commit_after>// Copyright 2019 The Go Cloud 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
//
//     https://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 secrets provides an easy and portable way to encrypt and decrypt
// messages.
//
// Subpackages contain distinct implementations of secrets for various
// providers, including Cloud and on-prem solutions. For example, "localsecrets"
// supports encryption/decryption using a locally provided key. Your application
// should import one of these provider-specific subpackages and use its exported
// function(s) to create a *Keeper; do not use the NewKeeper function in this
// package. For example:
//
//  keeper := localsecrets.NewKeeper(myKey)
//  encrypted, err := keeper.Encrypt(ctx.Background(), []byte("text"))
//  ...
//
// Then, write your application code using the *Keeper type. You can easily
// reconfigure your initialization code to choose a different provider.
// You can develop your application locally using localsecrets, or deploy it to
// multiple Cloud providers. You may find http://github.com/google/wire useful
// for managing your initialization code.
package secrets // import "gocloud.dev/secrets"

import (
	"context"

	"gocloud.dev/internal/trace"
	"gocloud.dev/secrets/driver"
)

// Keeper does encryption and decryption. To create a Keeper, use constructors
// found in provider-specific subpackages.
type Keeper struct {
	k driver.Keeper
}

// NewKeeper is intended for use by provider implementations.
var NewKeeper = newKeeper

// newKeeper creates a Keeper.
func newKeeper(k driver.Keeper) *Keeper {
	return &Keeper{k: k}
}

// Encrypt encrypts the plaintext and returns the cipher message.
func (k *Keeper) Encrypt(ctx context.Context, plaintext []byte) (ciphertext []byte, err error) {
	ctx = trace.StartSpan(ctx, "gocloud.dev/secrets.Encrypt")
	defer func() { trace.EndSpan(ctx, err) }()

	b, err := k.k.Encrypt(ctx, plaintext)
	if err != nil {
		return nil, wrapError(k, err)
	}
	return b, nil
}

// Decrypt decrypts the ciphertext and returns the plaintext.
func (k *Keeper) Decrypt(ctx context.Context, ciphertext []byte) (plaintext []byte, err error) {
	ctx = trace.StartSpan(ctx, "gocloud.dev/secrets.Decrypt")
	defer func() { trace.EndSpan(ctx, err) }()

	b, err := k.k.Decrypt(ctx, ciphertext)
	if err != nil {
		return nil, wrapError(k, err)
	}
	return b, nil
}

// wrappedError is used to wrap all errors returned by drivers so that users are
// not given access to provider-specific errors.
type wrappedError struct {
	err error
	k   driver.Keeper
}

func wrapError(k driver.Keeper, err error) error {
	if err == nil {
		return nil
	}
	return &wrappedError{k: k, err: err}
}

func (w *wrappedError) Error() string {
	return "secrets: " + w.err.Error()
}
<|endoftext|>


<commit_before>package osuapi

import "strconv"

// Languages
const (
	LanguageAny = iota
	LanguageOther
	LanguageEnglish
	LanguageJapanese
	LanguageChinese
	LanguageInstrumental
	LanguageKorean
	LanguageFrench
	LanguageGerman
	LanguageSwedish
	LanguageSpanish
	LanguageItalian
)

// Language is the language of a beatmap's song.
type Language int

var languageString = [...]string{
	"any",
	"other",
	"English",
	"Japanese",
	"Chinese",
	"instrumental",
	"Korean",
	"French",
	"German",
	"Swedish",
	"Spanish",
	"Italian",
}

func (l Language) String() string {
	if int(l) < len(languageString) {
		return languageString[l]
	}
	return strconv.Itoa(int(l))
}
<commit_msg>Make constants in language.go of type Language<commit_after>package osuapi

import "strconv"

// Languages
const (
	LanguageAny Language = iota
	LanguageOther
	LanguageEnglish
	LanguageJapanese
	LanguageChinese
	LanguageInstrumental
	LanguageKorean
	LanguageFrench
	LanguageGerman
	LanguageSwedish
	LanguageSpanish
	LanguageItalian
)

// Language is the language of a beatmap's song.
type Language int

var languageString = [...]string{
	"any",
	"other",
	"English",
	"Japanese",
	"Chinese",
	"instrumental",
	"Korean",
	"French",
	"German",
	"Swedish",
	"Spanish",
	"Italian",
}

func (l Language) String() string {
	if int(l) < len(languageString) {
		return languageString[l]
	}
	return strconv.Itoa(int(l))
}
<|endoftext|>


<commit_before>// Copyright 2014-2015 Apptimist, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"errors"
	"io"
	"os"
	"time"

	"github.com/apptimistco/asn/debug"
	"github.com/apptimistco/asn/debug/mutex"
)

// AckerF is an Acknowledgment handler for the given request and trailing Ack
// data.
type AckerF func(Req, error, *PDU) error

type acker struct {
	mutex.Mutex
	m map[Req]AckerF
}

func (acker *acker) Init() {
	acker.Mutex.Set("acker")
	if acker.m == nil {
		acker.m = make(map[Req]AckerF)
	}
}

func (acker *acker) Reset() {
	for k, _ := range acker.m {
		acker.m[k] = nil
	}
}

// Map a handler to the given request.
func (acker *acker) Map(req Req, f AckerF) {
	acker.Lock()
	defer acker.Unlock()
	acker.m[req] = f
}

// UnMap a handler to the given request.
func (acker *acker) UnMap(req Req) {
	acker.Lock()
	defer acker.Unlock()
	acker.m[req] = nil
}

// Rx processes recieved acks with registered handlers.
func (asn *asn) AckerRx(pdu *PDU) (err error) {
	var req Req
	if _, err = req.ReadFrom(pdu); err != nil {
		return
	}
	if _, err = (NBOReader{pdu}).ReadNBO(&asn.time.in); err != nil {
		return
	}
	if err = asn.ParseAckError(pdu); err == nil {
		asn.Trace(debug.Id(AckReqId), "rx", req, "ack")
	} else {
		asn.Trace(debug.Id(AckReqId), "rx", req, "nack", err)
	}
	asn.acker.Lock()
	f, ok := asn.acker.m[req]
	asn.acker.Unlock()
	if ok && f != nil {
		err = f(req, err, pdu)
	} else {
		pdu.Free()
		err = errors.New("unregistered Ack request")
		asn.Diag(err)
	}
	return
}

// Ack the given requester. If the argument is an error, the associate code is
// used in the negative reply with the error string. Otherwise, it's a
// successful Ack with any subsequent args appended as data.
// Only use this for page sized acks, anything larger should use
// NewAckSuccessPDUFile
func (asn *asn) Ack(req Req, argv ...interface{}) {
	var err error
	if len(argv) == 1 {
		switch t := argv[0].(type) {
		case *PDU:
			asn.Tx(t)
			return
		case error:
			err = t
			argv = argv[1:]
		case nil:
			argv = argv[1:]
		}
	}
	ack := NewPDUBuf()
	v := asn.version
	v.WriteTo(ack)
	AckReqId.Version(v).WriteTo(ack)
	req.WriteTo(ack)
	(NBOWriter{ack}).WriteNBO(asn.time.out)
	if err != nil {
		asn.Trace(debug.Id(AckReqId), "tx", req, "nack", err)
		ErrFromError(err).Version(v).WriteTo(ack)
		if len(argv) > 0 {
			AckOut(ack, argv...)
		} else {
			ack.Write([]byte(err.Error()))
		}
	} else {
		asn.Trace(debug.Id(AckReqId), "tx", req, "ack")
		Success.Version(v).WriteTo(ack)
		AckOut(ack, argv...)
	}
	asn.Tx(ack)
}

// AckOut is used by the above asn.Ack to write Ack content to the given
// writer. It is also used by asnsrv to print Ack content to Stdout.
func AckOut(w io.Writer, argv ...interface{}) {
	for _, v := range argv {
		switch t := v.(type) {
		case *PDU:
			if err := t.Open(); err == nil {
				var (
					v   Version
					id  Id
					req Req
					rxt time.Time
					ec  Err
				)
				v.ReadFrom(t)
				id.ReadFrom(t)
				req.ReadFrom(t)
				(NBOReader{t}).ReadNBO(&rxt)
				ec.ReadFrom(t)
				if ec != Success {
					w.Write([]byte("Error: "))
				}
				t.WriteTo(w)
			}
			t.Free()
		case WriteToer:
			t.WriteTo(w)
		case io.Reader:
			io.Copy(w, t)
		case []byte:
			w.Write(t)
		case string:
			io.WriteString(w, t)
		case func(io.Writer):
			t(w)
		case []*os.File:
			for i, f := range t {
				io.Copy(w, f)
				f.Close()
				t[i] = nil
			}
		case *Sum:
			w.Write([]byte(t.String()))
			w.Write([]byte("\n"))
		}
	}
}

// NewAckSuccessPDUFile creates a temp file preloaded with the asn success ack
// header and ready to write success data.
func (asn *asn) NewAckSuccessPDUFile(req Req) (ack *PDU, err error) {
	f := asn.repos.tmp.New()
	ack = NewPDUFile(f)
	f = nil
	v := asn.version
	v.WriteTo(ack)
	AckReqId.Version(v).WriteTo(ack)
	req.WriteTo(ack)
	(NBOWriter{ack}).WriteNBO(asn.time.out)
	Success.Version(v).WriteTo(ack)
	return
}

// ParseAckError returns a GO error, if any, derrived from the Ack and
// returns nil if Ack indicates success.
func (asn *asn) ParseAckError(ack *PDU) (err error) {
	var e Err
	e.ReadFrom(ack)
	e.Internal(asn.Version())
	err = e.ErrToError()
	if err == ErrFailure {
		var b [256]byte
		n, _ := ack.Read(b[:])
		err = errors.New(string(b[:n]))
	}
	return
}
<commit_msg>protect argv slice manipulation<commit_after>// Copyright 2014-2015 Apptimist, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"errors"
	"io"
	"os"
	"time"

	"github.com/apptimistco/asn/debug"
	"github.com/apptimistco/asn/debug/mutex"
)

// AckerF is an Acknowledgment handler for the given request and trailing Ack
// data.
type AckerF func(Req, error, *PDU) error

type acker struct {
	mutex.Mutex
	m map[Req]AckerF
}

func (acker *acker) Init() {
	acker.Mutex.Set("acker")
	if acker.m == nil {
		acker.m = make(map[Req]AckerF)
	}
}

func (acker *acker) Reset() {
	for k, _ := range acker.m {
		acker.m[k] = nil
	}
}

// Map a handler to the given request.
func (acker *acker) Map(req Req, f AckerF) {
	acker.Lock()
	defer acker.Unlock()
	acker.m[req] = f
}

// UnMap a handler to the given request.
func (acker *acker) UnMap(req Req) {
	acker.Lock()
	defer acker.Unlock()
	acker.m[req] = nil
}

// Rx processes recieved acks with registered handlers.
func (asn *asn) AckerRx(pdu *PDU) (err error) {
	var req Req
	if _, err = req.ReadFrom(pdu); err != nil {
		return
	}
	if _, err = (NBOReader{pdu}).ReadNBO(&asn.time.in); err != nil {
		return
	}
	if err = asn.ParseAckError(pdu); err == nil {
		asn.Trace(debug.Id(AckReqId), "rx", req, "ack")
	} else {
		asn.Trace(debug.Id(AckReqId), "rx", req, "nack", err)
	}
	asn.acker.Lock()
	f, ok := asn.acker.m[req]
	asn.acker.Unlock()
	if ok && f != nil {
		err = f(req, err, pdu)
	} else {
		pdu.Free()
		err = errors.New("unregistered Ack request")
		asn.Diag(err)
	}
	return
}

// Ack the given requester. If the argument is an error, the associate code is
// used in the negative reply with the error string. Otherwise, it's a
// successful Ack with any subsequent args appended as data.
// Only use this for page sized acks, anything larger should use
// NewAckSuccessPDUFile
func (asn *asn) Ack(req Req, argv ...interface{}) {
	var err error
	if len(argv) == 1 {
		switch t := argv[0].(type) {
		case *PDU:
			asn.Tx(t)
			return
		case error:
			err = t
			argv = argv[1:]
		case nil:
			if len(argv) > 1 {
				argv = argv[1:]
			} else {
				argv = argv[:0]
			}
		}
	}
	ack := NewPDUBuf()
	v := asn.version
	v.WriteTo(ack)
	AckReqId.Version(v).WriteTo(ack)
	req.WriteTo(ack)
	(NBOWriter{ack}).WriteNBO(asn.time.out)
	if err != nil {
		asn.Trace(debug.Id(AckReqId), "tx", req, "nack", err)
		ErrFromError(err).Version(v).WriteTo(ack)
		if len(argv) > 0 {
			AckOut(ack, argv...)
		} else {
			ack.Write([]byte(err.Error()))
		}
	} else {
		asn.Trace(debug.Id(AckReqId), "tx", req, "ack")
		Success.Version(v).WriteTo(ack)
		AckOut(ack, argv...)
	}
	asn.Tx(ack)
}

// AckOut is used by the above asn.Ack to write Ack content to the given
// writer. It is also used by asnsrv to print Ack content to Stdout.
func AckOut(w io.Writer, argv ...interface{}) {
	for _, v := range argv {
		switch t := v.(type) {
		case *PDU:
			if err := t.Open(); err == nil {
				var (
					v   Version
					id  Id
					req Req
					rxt time.Time
					ec  Err
				)
				v.ReadFrom(t)
				id.ReadFrom(t)
				req.ReadFrom(t)
				(NBOReader{t}).ReadNBO(&rxt)
				ec.ReadFrom(t)
				if ec != Success {
					w.Write([]byte("Error: "))
				}
				t.WriteTo(w)
			}
			t.Free()
		case WriteToer:
			t.WriteTo(w)
		case io.Reader:
			io.Copy(w, t)
		case []byte:
			w.Write(t)
		case string:
			io.WriteString(w, t)
		case func(io.Writer):
			t(w)
		case []*os.File:
			for i, f := range t {
				io.Copy(w, f)
				f.Close()
				t[i] = nil
			}
		case *Sum:
			w.Write([]byte(t.String()))
			w.Write([]byte("\n"))
		}
	}
}

// NewAckSuccessPDUFile creates a temp file preloaded with the asn success ack
// header and ready to write success data.
func (asn *asn) NewAckSuccessPDUFile(req Req) (ack *PDU, err error) {
	f := asn.repos.tmp.New()
	ack = NewPDUFile(f)
	f = nil
	v := asn.version
	v.WriteTo(ack)
	AckReqId.Version(v).WriteTo(ack)
	req.WriteTo(ack)
	(NBOWriter{ack}).WriteNBO(asn.time.out)
	Success.Version(v).WriteTo(ack)
	return
}

// ParseAckError returns a GO error, if any, derrived from the Ack and
// returns nil if Ack indicates success.
func (asn *asn) ParseAckError(ack *PDU) (err error) {
	var e Err
	e.ReadFrom(ack)
	e.Internal(asn.Version())
	err = e.ErrToError()
	if err == ErrFailure {
		var b [256]byte
		n, _ := ack.Read(b[:])
		err = errors.New(string(b[:n]))
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"time"

	"github.com/privacylab/talek/common"
	"github.com/privacylab/talek/libtalek"
)

var config = flag.String("config", "talek.conf", "Client configuration for talek")
var create = flag.Bool("create", false, "Create a new talek handle")
var share = flag.String("share", "", "Create a read-only version of the topic for sharing")
var handle = flag.String("topic", "talek.handle", "The talek handle to use")
var write = flag.String("write", "", "A message to append to the log (If not specified, the next item will be read.)")
var read = flag.Bool("read", false, "Read from the provided topic")
var verbose = flag.Bool("verbose", false, "Print diagnostic information")

// The CLI client will read or write a single item for talek
func main() {
	flag.Parse()

	// Config
	config := libtalek.ClientConfigFromFile(*config)
	if config == nil {
		fmt.Fprintln(os.Stderr, "Talek Client must be run with --config specifying where the server is.")
		os.Exit(1)
	}
	if config.Config == nil && *verbose {
		fmt.Fprintln(os.Stderr, "Common configuration will be fetched from frontend.")
	}

	topicdata, err := ioutil.ReadFile(*handle)
	if err != nil {
		panic(err)
	}
	topic := libtalek.Topic{}
	if *create {
		nt, newerr := libtalek.NewTopic()
		if newerr != nil {
			panic(err)
		}
		topic = *nt
	} else {
		if err = json.Unmarshal(topicdata, &topic); err != nil {
			panic(err)
		}
	}

	if len(*share) > 0 {
		handle := topic.Handle
		handleBytes, handleerr := json.Marshal(handle)
		if handleerr != nil {
			panic(handleerr)
		}
		ioutil.WriteFile(*share, handleBytes, 0640)
		fmt.Fprintf(os.Stderr, "Read-only topic written to %s\n", *share)
	}

	// Client-connected activity below.
	leaderRPC := common.NewFrontendRPC("RPC", config.FrontendAddr)

	client := libtalek.NewClient("Client", *config, leaderRPC)
	if client == nil {
		panic("could not create talek client")
	} else if *verbose {
		fmt.Fprintln(os.Stderr, "Connection to RPC established.")
	}

	if *read == false && len(*write) > 0 {
		if err = client.Publish(&topic, []byte(*write)); err != nil {
			fmt.Fprintf(os.Stderr, "Failed to publish: %s", err)
			panic(err)
		}
	} else if *read == false {
		fmt.Fprintf(os.Stderr, "No Read or Write operation requested. Closing.")
	} else {
		msgs := client.Poll(&topic.Handle)
		timeout := time.After(config.ReadInterval * 5)
		select {
		case next := <-msgs:
			fmt.Println(next)
			break
		case <-timeout:
			if *verbose {
				fmt.Fprintln(os.Stderr, "Timed out waiting for new message.")
			}
			return
		}
		client.Done(&topic.Handle)
	}
	updatedTopic, err := json.Marshal(topic)
	if err != nil {
		panic(err)
	}
	if err = ioutil.WriteFile(*handle, updatedTopic, 0600); err != nil {
		panic(err)
	}
}
<commit_msg>extract the read timeout multiple to a const<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"time"

	"github.com/privacylab/talek/common"
	"github.com/privacylab/talek/libtalek"
)

const readTimeoutMultiple = 5

var config = flag.String("config", "talek.conf", "Client configuration for talek")
var create = flag.Bool("create", false, "Create a new talek handle")
var share = flag.String("share", "", "Create a read-only version of the topic for sharing")
var handle = flag.String("topic", "talek.handle", "The talek handle to use")
var write = flag.String("write", "", "A message to append to the log (If not specified, the next item will be read.)")
var read = flag.Bool("read", false, "Read from the provided topic")
var verbose = flag.Bool("verbose", false, "Print diagnostic information")

// The CLI client will read or write a single item for talek
func main() {
	flag.Parse()

	// Config
	config := libtalek.ClientConfigFromFile(*config)
	if config == nil {
		fmt.Fprintln(os.Stderr, "Talek Client must be run with --config specifying where the server is.")
		os.Exit(1)
	}
	if config.Config == nil && *verbose {
		fmt.Fprintln(os.Stderr, "Common configuration will be fetched from frontend.")
	}

	topicdata, err := ioutil.ReadFile(*handle)
	if err != nil {
		panic(err)
	}
	topic := libtalek.Topic{}
	if *create {
		nt, newerr := libtalek.NewTopic()
		if newerr != nil {
			panic(err)
		}
		topic = *nt
	} else {
		if err = json.Unmarshal(topicdata, &topic); err != nil {
			panic(err)
		}
	}

	if len(*share) > 0 {
		handle := topic.Handle
		handleBytes, handleerr := json.Marshal(handle)
		if handleerr != nil {
			panic(handleerr)
		}
		ioutil.WriteFile(*share, handleBytes, 0640)
		fmt.Fprintf(os.Stderr, "Read-only topic written to %s\n", *share)
	}

	// Client-connected activity below.
	leaderRPC := common.NewFrontendRPC("RPC", config.FrontendAddr)

	client := libtalek.NewClient("Client", *config, leaderRPC)
	if client == nil {
		panic("could not create talek client")
	} else if *verbose {
		fmt.Fprintln(os.Stderr, "Connection to RPC established.")
	}

	if *read == false && len(*write) > 0 {
		if err = client.Publish(&topic, []byte(*write)); err != nil {
			fmt.Fprintf(os.Stderr, "Failed to publish: %s", err)
			panic(err)
		}
	} else if *read == false {
		fmt.Fprintf(os.Stderr, "No Read or Write operation requested. Closing.")
	} else {
		msgs := client.Poll(&topic.Handle)
		timeout := time.After(config.ReadInterval * readTimeoutMultiple)
		select {
		case next := <-msgs:
			fmt.Println(next)
			break
		case <-timeout:
			if *verbose {
				fmt.Fprintln(os.Stderr, "Timed out waiting for new message.")
			}
			return
		}
		client.Done(&topic.Handle)
	}
	updatedTopic, err := json.Marshal(topic)
	if err != nil {
		panic(err)
	}
	if err = ioutil.WriteFile(*handle, updatedTopic, 0600); err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>// (C) Copyright 2021 Hewlett Packard Enterprise Development LP
//
// 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 oneview

import (
	"github.com/HewlettPackard/oneview-golang/ov"
	"github.com/HewlettPackard/oneview-golang/utils"
	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)

func resourceEnclosure() *schema.Resource {
	return &schema.Resource{
		Create: resourceEnclosureCreate,
		Read:   resourceEnclosureRead,
		Update: resourceEnclosureUpdate,
		Delete: resourceEnclosureDelete,

		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"active_oa_preferred_ip": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"asset_tag": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"category": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"created": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"description": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"device_bay_count": {
				Type:     schema.TypeInt,
				Computed: true,
			},
			"device_bays": {
				Computed: true,
				Type:     schema.TypeSet,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"available_for_full_height_profile": {
							Type:     schema.TypeBool,
							Computed: true,
						},
						"available_for_half_height_profile": {
							Type:     schema.TypeBool,
							Computed: true,
						},
						"bay_number": {
							Type:     schema.TypeInt,
							Computed: true,
						},
						"category": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"covered_by_device": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"covered_by_profile": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"created": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"device_presence": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"device_uri": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"enclosure_uri": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"etag": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"model": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"modified": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"profile_uri": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"type": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"uri": {
							Type:     schema.TypeString,
							Computed: true,
						},
					},
				},
				Set: schema.HashString,
			},
			"etag": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"enclosure_group_uri": {
				Type:     schema.TypeString,
				Optional: true,
				Default:  "",
			},
			"enclosure_type": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"force_install_firmware": {
				Type:     schema.TypeBool,
				Optional: true,
			},
			"firmware_baseline_uri": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"force": {
				Type:     schema.TypeBool,
				Optional: true,
			},
			"fw_baseline_name": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"fw_baseline_uri": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"host_name": {
				Type:     schema.TypeString,
				Optional: true,
				Default:  "",
			},
			"user_name": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"password": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"initial_scope_uris": {
				Optional: true,
				Type:     schema.TypeSet,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
				Set: schema.HashString,
			},
			"interconnect_bay_count": {
				Type:     schema.TypeInt,
				Computed: true,
			},
			"interconnect_bays": {
				Computed: true,
				Type:     schema.TypeSet,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"interconnect_bay": {
							Type:     schema.TypeInt,
							Computed: true,
						},
						"logical_interconnect_group_uri": {
							Type:     schema.TypeString,
							Computed: true,
						},
					},
				},
				Set: schema.HashString,
			},
			"is_fw_managed": {
				Type:     schema.TypeBool,
				Computed: true,
			},
			"licensing_intent": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"modified": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"name": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"op": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"path": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"part_number": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"rack_name": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"refresh_state": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"serial_number": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"standby_oa_preferred_ip": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"state": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"state_reason": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"status": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"type": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"update_firmware_on": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"uri": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"uuid": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"value": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"vcm_domain_id": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"vcm_domain_name": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"vcm_mode": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"vcm_url": {
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func resourceEnclosureCreate(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	enclosureCreateMap := ov.EnclosureCreateMap{
		EnclosureGroupUri:    utils.NewNstring(d.Get("enclosure_group_uri").(string)),
		Hostname:             d.Get("host_name").(string),
		Username:             d.Get("user_name").(string),
		Password:             d.Get("password").(string),
		LicensingIntent:      d.Get("licensing_intent").(string),
		ForceInstallFirmware: d.Get("force_install_firmware").(bool),
		FirmwareBaselineUri:  d.Get("firmware_baseline_uri").(string),
		Force:                d.Get("force").(bool),
		UpdateFirmwareOn:     d.Get("update_firmware_on").(string),
	}

	rawInitialScopeUris := d.Get("initial_scope_uris").(*schema.Set).List()
	initialScopeUris := make([]string, len(rawInitialScopeUris))
	for i, raw := range rawInitialScopeUris {
		initialScopeUris[i] = raw.(string)
	}

	if val, ok := d.GetOk("initial_scope_uris"); ok {
		rawinitialScopeUris := val.(*schema.Set).List()
		initialScopeUris := make([]utils.Nstring, len(rawinitialScopeUris))
		for i, rawData := range rawinitialScopeUris {
			scope, _ := config.ovClient.GetScopeByName(rawData.(string))
			initialScopeUris[i] = utils.Nstring(scope.URI)
		}
		enclosureCreateMap.InitialScopeUris = initialScopeUris
	}

	enclosureError := config.ovClient.CreateEnclosure(enclosureCreateMap)
	d.SetId(d.Get("name").(string))
	if enclosureError != nil {
		d.SetId("")
		return enclosureError
	}
	return resourceEnclosureRead(d, meta)
}

func resourceEnclosureRead(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	enclosure, err := config.ovClient.GetEnclosureByName(d.Id())
	if err != nil || enclosure.URI.IsNil() {
		d.SetId("")
		return nil
	}
	d.Set("active_oa_preferred_ip", enclosure.ActiveOaPreferredIP)
	d.Set("asset_tag", enclosure.AssetTag)
	d.Set("category", enclosure.Category)
	d.Set("created", enclosure.Created)
	d.Set("description", enclosure.Description)
	d.Set("device_bay_count", enclosure.DeviceBayCount)
	d.Set("etag", enclosure.ETAG)
	if enclosure.EnclosureGroupUri.String() != "" {
		d.Set("enclosure_group_uri", enclosure.EnclosureGroupUri)
	}
	d.Set("enclosure_type", enclosure.EnclosureType)
	d.Set("fw_baseline_name", enclosure.FwBaselineName)
	d.Set("fw_baseline_uri", enclosure.FwBaselineUri.String())
	d.Set("interconnect_bay_count", enclosure.InterconnectBayCount)
	d.Set("interconnect_bays", enclosure.InterconnectBays)
	d.Set("is_fw_managed", enclosure.IsFwManaged)
	d.Set("licensing_intent", enclosure.LicensingIntent)
	d.Set("modified", enclosure.Modified)
	d.Set("name", enclosure.Name)
	d.Set("part_number", enclosure.PartNumber)
	d.Set("rack_name", enclosure.RackName)
	d.Set("refresh_state", enclosure.RefreshState)
	d.Set("serial_number", enclosure.SerialNumber)
	d.Set("standby_oa_preferred_ip", enclosure.StandbyOaPreferredIP)
	d.Set("state", enclosure.State)
	d.Set("state_reason", enclosure.StateReason)
	d.Set("status", enclosure.Status)
	d.Set("refresh_state", enclosure.RefreshState)
	d.Set("type", enclosure.Type)
	d.Set("uri", enclosure.URI.String())
	d.Set("uuid", enclosure.UUID)
	d.Set("vcm_domain_id", enclosure.VcmDomainId)
	d.Set("vcm_domain_name", enclosure.VcmDomainName)
	d.Set("vcm_mode", enclosure.VcmMode)
	d.Set("vcm_url", enclosure.VcmUrl)
	return nil
}

func getEnclosureByHostName(hostName string, meta interface{}) (bool, error) {
	config := meta.(*Config)
	enclosures, encErr := config.ovClient.GetEnclosures("", "", "", "", "")
	if encErr != nil {
		return false, encErr
	} else {
		for _, enclosure := range enclosures.Members {
			for _, bay := range enclosure.ManagerBays {
				if hostName == bay.IpAddress {
					return true, nil
				}
			}
		}
		return false, nil
	}
}

func resourceEnclosureUpdate(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	enclosure := ov.Enclosure{
		URI: utils.NewNstring(d.Get("uri").(string)),
	}

	if d.Get("host_name") != "" {
		result, err := getEnclosureByHostName(d.Get("host_name").(string), meta)
		if result {
			d.SetId(enclosure.Name)
			return resourceEnclosureRead(d, meta)
		} else if err != nil {
			return err
		}

	}

	err := config.ovClient.UpdateEnclosure(d.Get("op").(string), d.Get("path").(string), d.Get("value").(string), enclosure)
	if err != nil {
		return err
	}
	d.SetId(d.Get("name").(string))

	return resourceEnclosureRead(d, meta)
}

func resourceEnclosureDelete(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	err := config.ovClient.DeleteEnclosure(d.Get("name").(string))
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>fixed hound issues<commit_after>// (C) Copyright 2021 Hewlett Packard Enterprise Development LP
//
// 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 oneview

import (
	"github.com/HewlettPackard/oneview-golang/ov"
	"github.com/HewlettPackard/oneview-golang/utils"
	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)

func resourceEnclosure() *schema.Resource {
	return &schema.Resource{
		Create: resourceEnclosureCreate,
		Read:   resourceEnclosureRead,
		Update: resourceEnclosureUpdate,
		Delete: resourceEnclosureDelete,

		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"active_oa_preferred_ip": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"asset_tag": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"category": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"created": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"description": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"device_bay_count": {
				Type:     schema.TypeInt,
				Computed: true,
			},
			"device_bays": {
				Computed: true,
				Type:     schema.TypeSet,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"available_for_full_height_profile": {
							Type:     schema.TypeBool,
							Computed: true,
						},
						"available_for_half_height_profile": {
							Type:     schema.TypeBool,
							Computed: true,
						},
						"bay_number": {
							Type:     schema.TypeInt,
							Computed: true,
						},
						"category": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"covered_by_device": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"covered_by_profile": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"created": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"device_presence": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"device_uri": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"enclosure_uri": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"etag": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"model": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"modified": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"profile_uri": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"type": {
							Type:     schema.TypeString,
							Computed: true,
						},
						"uri": {
							Type:     schema.TypeString,
							Computed: true,
						},
					},
				},
				Set: schema.HashString,
			},
			"etag": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"enclosure_group_uri": {
				Type:     schema.TypeString,
				Optional: true,
				Default:  "",
			},
			"enclosure_type": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"force_install_firmware": {
				Type:     schema.TypeBool,
				Optional: true,
			},
			"firmware_baseline_uri": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"force": {
				Type:     schema.TypeBool,
				Optional: true,
			},
			"fw_baseline_name": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"fw_baseline_uri": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"host_name": {
				Type:     schema.TypeString,
				Optional: true,
				Default:  "",
			},
			"user_name": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"password": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"initial_scope_uris": {
				Optional: true,
				Type:     schema.TypeSet,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
				Set: schema.HashString,
			},
			"interconnect_bay_count": {
				Type:     schema.TypeInt,
				Computed: true,
			},
			"interconnect_bays": {
				Computed: true,
				Type:     schema.TypeSet,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"interconnect_bay": {
							Type:     schema.TypeInt,
							Computed: true,
						},
						"logical_interconnect_group_uri": {
							Type:     schema.TypeString,
							Computed: true,
						},
					},
				},
				Set: schema.HashString,
			},
			"is_fw_managed": {
				Type:     schema.TypeBool,
				Computed: true,
			},
			"licensing_intent": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"modified": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"name": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"op": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"path": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"part_number": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"rack_name": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"refresh_state": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"serial_number": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"standby_oa_preferred_ip": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"state": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"state_reason": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"status": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"type": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"update_firmware_on": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"uri": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"uuid": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"value": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"vcm_domain_id": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"vcm_domain_name": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"vcm_mode": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"vcm_url": {
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func resourceEnclosureCreate(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	enclosureCreateMap := ov.EnclosureCreateMap{
		EnclosureGroupUri:    utils.NewNstring(d.Get("enclosure_group_uri").(string)),
		Hostname:             d.Get("host_name").(string),
		Username:             d.Get("user_name").(string),
		Password:             d.Get("password").(string),
		LicensingIntent:      d.Get("licensing_intent").(string),
		ForceInstallFirmware: d.Get("force_install_firmware").(bool),
		FirmwareBaselineUri:  d.Get("firmware_baseline_uri").(string),
		Force:                d.Get("force").(bool),
		UpdateFirmwareOn:     d.Get("update_firmware_on").(string),
	}

	rawInitialScopeUris := d.Get("initial_scope_uris").(*schema.Set).List()
	initialScopeUris := make([]string, len(rawInitialScopeUris))
	for i, raw := range rawInitialScopeUris {
		initialScopeUris[i] = raw.(string)
	}

	if val, ok := d.GetOk("initial_scope_uris"); ok {
		rawinitialScopeUris := val.(*schema.Set).List()
		initialScopeUris := make([]utils.Nstring, len(rawinitialScopeUris))
		for i, rawData := range rawinitialScopeUris {
			scope, _ := config.ovClient.GetScopeByName(rawData.(string))
			initialScopeUris[i] = utils.Nstring(scope.URI)
		}
		enclosureCreateMap.InitialScopeUris = initialScopeUris
	}

	enclosureError := config.ovClient.CreateEnclosure(enclosureCreateMap)
	d.SetId(d.Get("name").(string))
	if enclosureError != nil {
		d.SetId("")
		return enclosureError
	}
	return resourceEnclosureRead(d, meta)
}

func resourceEnclosureRead(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	enclosure, err := config.ovClient.GetEnclosureByName(d.Id())
	if err != nil || enclosure.URI.IsNil() {
		d.SetId("")
		return nil
	}
	d.Set("active_oa_preferred_ip", enclosure.ActiveOaPreferredIP)
	d.Set("asset_tag", enclosure.AssetTag)
	d.Set("category", enclosure.Category)
	d.Set("created", enclosure.Created)
	d.Set("description", enclosure.Description)
	d.Set("device_bay_count", enclosure.DeviceBayCount)
	d.Set("etag", enclosure.ETAG)
	if enclosure.EnclosureGroupUri.String() != "" {
		d.Set("enclosure_group_uri", enclosure.EnclosureGroupUri)
	}
	d.Set("enclosure_type", enclosure.EnclosureType)
	d.Set("fw_baseline_name", enclosure.FwBaselineName)
	d.Set("fw_baseline_uri", enclosure.FwBaselineUri.String())
	d.Set("interconnect_bay_count", enclosure.InterconnectBayCount)
	d.Set("interconnect_bays", enclosure.InterconnectBays)
	d.Set("is_fw_managed", enclosure.IsFwManaged)
	d.Set("licensing_intent", enclosure.LicensingIntent)
	d.Set("modified", enclosure.Modified)
	d.Set("name", enclosure.Name)
	d.Set("part_number", enclosure.PartNumber)
	d.Set("rack_name", enclosure.RackName)
	d.Set("refresh_state", enclosure.RefreshState)
	d.Set("serial_number", enclosure.SerialNumber)
	d.Set("standby_oa_preferred_ip", enclosure.StandbyOaPreferredIP)
	d.Set("state", enclosure.State)
	d.Set("state_reason", enclosure.StateReason)
	d.Set("status", enclosure.Status)
	d.Set("refresh_state", enclosure.RefreshState)
	d.Set("type", enclosure.Type)
	d.Set("uri", enclosure.URI.String())
	d.Set("uuid", enclosure.UUID)
	d.Set("vcm_domain_id", enclosure.VcmDomainId)
	d.Set("vcm_domain_name", enclosure.VcmDomainName)
	d.Set("vcm_mode", enclosure.VcmMode)
	d.Set("vcm_url", enclosure.VcmUrl)
	return nil
}

func getEnclosureByHostName(hostName string, meta interface{}) (bool, error) {
	config := meta.(*Config)
	enclosures, encErr := config.ovClient.GetEnclosures("", "", "", "", "")
	if encErr != nil {
		return false, encErr
	}
	for _, enclosure := range enclosures.Members {
		for _, bay := range enclosure.ManagerBays {
			if hostName == bay.IpAddress {
				return true, nil
			}
		}
	}
	return false, nil
}

func resourceEnclosureUpdate(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	enclosure := ov.Enclosure{
		URI: utils.NewNstring(d.Get("uri").(string)),
	}

	if d.Get("host_name") != "" {
		result, err := getEnclosureByHostName(d.Get("host_name").(string), meta)
		if result {
			d.SetId(enclosure.Name)
			return resourceEnclosureRead(d, meta)
		} else if err != nil {
			return err
		}

	}

	err := config.ovClient.UpdateEnclosure(d.Get("op").(string), d.Get("path").(string), d.Get("value").(string), enclosure)
	if err != nil {
		return err
	}
	d.SetId(d.Get("name").(string))

	return resourceEnclosureRead(d, meta)
}

func resourceEnclosureDelete(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	err := config.ovClient.DeleteEnclosure(d.Get("name").(string))
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package telebot

import (
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/pkg/errors"
)

// Raw lets you call any method of Bot API manually.
func (b *Bot) Raw(method string, payload interface{}) ([]byte, error) {
	url := b.URL + "/bot" + b.Token + "/" + method

	var buf bytes.Buffer
	if err := json.NewEncoder(&buf).Encode(payload); err != nil {
		return []byte{}, wrapSystem(err)
	}

	resp, err := b.client.Post(url, "application/json", &buf)
	if err != nil {
		return []byte{}, errors.Wrap(err, "http.Post failed")
	}
	resp.Close = true
	defer resp.Body.Close()

	json, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return []byte{}, wrapSystem(err)
	}

	return json, nil
}

func addFileToWriter(writer *multipart.Writer,
	filename, field string, file interface{}) error {

	var reader io.Reader
	if r, ok := file.(io.Reader); ok {
		reader = r
	} else if path, ok := file.(string); ok {
		f, err := os.Open(path)
		if err != nil {
			return err
		}
		defer f.Close()
		reader = f
	} else {
		return errors.Errorf("File for field `%v` should be an io.ReadCloser or string", field)
	}

	part, err := writer.CreateFormFile(field, filename)
	if err != nil {
		return err
	}

	_, err = io.Copy(part, reader)
	return err
}

func (b *Bot) sendFiles(method string, files map[string]File,
	params map[string]string) ([]byte, error) {

	body := &bytes.Buffer{}
	rawFiles := map[string]interface{}{}

	for name, f := range files {
		switch {
		case f.InCloud():
			params[name] = f.FileID
		case f.FileURL != "":
			params[name] = f.FileURL
		case f.OnDisk():
			rawFiles[name] = f.FileLocal
		case f.FileReader != nil:
			rawFiles[name] = f.FileReader
		default:
			return nil, errors.Errorf("sendFiles: File for field %s doesn't exist", name)
		}
	}

	if len(rawFiles) == 0 {
		return b.Raw(method, params)
	}

	writer := multipart.NewWriter(body)

	for field, file := range rawFiles {
		if err := addFileToWriter(writer, params["file_name"], field, file); err != nil {
			return nil, wrapSystem(err)
		}
	}

	for field, value := range params {
		if err := writer.WriteField(field, value); err != nil {
			return nil, wrapSystem(err)
		}
	}

	if err := writer.Close(); err != nil {
		return nil, wrapSystem(err)
	}

	url := b.URL + "/bot" + b.Token + "/" + method
	req, err := http.NewRequest("POST", url, body)
	if err != nil {
		return nil, wrapSystem(err)
	}

	req.Header.Add("Content-Type", writer.FormDataContentType())

	resp, err := b.client.Do(req)
	if err != nil {
		return nil, errors.Wrap(err, "http.Post failed")
	}

	if resp.StatusCode == http.StatusInternalServerError {
		return nil, errors.New("api error: internal server error")
	}

	json, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, wrapSystem(err)
	}

	return json, nil
}

func (b *Bot) sendObject(f *File, what string, params map[string]string, files map[string]File) (*Message, error) {
	sendWhat := "send" + strings.Title(what)

	if what == "videoNote" {
		what = "video_note"
	}

	sendFiles := map[string]File{what: *f}
	for k, v := range files {
		sendFiles[k] = v
	}

	respJSON, err := b.sendFiles(sendWhat, sendFiles, params)
	if err != nil {
		return nil, err
	}

	return extractMsgResponse(respJSON)
}

func (b *Bot) getMe() (*User, error) {
	meJSON, err := b.Raw("getMe", nil)
	if err != nil {
		return nil, err
	}

	var botInfo struct {
		Ok          bool
		Result      *User
		Description string
	}

	err = json.Unmarshal(meJSON, &botInfo)
	if err != nil {
		return nil, errors.Wrap(err, "bad response json")
	}

	if !botInfo.Ok {
		return nil, errors.Errorf("api error: %s", botInfo.Description)
	}

	return botInfo.Result, nil

}

func (b *Bot) getUpdates(offset int, timeout time.Duration) (upd []Update, err error) {
	params := map[string]string{
		"offset":  strconv.Itoa(offset),
		"timeout": strconv.Itoa(int(timeout / time.Second)),
	}
	updatesJSON, errCommand := b.Raw("getUpdates", params)
	if errCommand != nil {
		err = errCommand
		return

	}
	var updatesReceived struct {
		Ok          bool
		Result      []Update
		Description string
	}

	err = json.Unmarshal(updatesJSON, &updatesReceived)
	if err != nil {
		err = errors.Wrap(err, "bad response json")
		return
	}

	if !updatesReceived.Ok {
		err = errors.Errorf("api error: %s", updatesReceived.Description)
		return
	}

	return updatesReceived.Result, nil
}
<commit_msg>Fix sendFiles reader leak<commit_after>package telebot

import (
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/pkg/errors"
)

// Raw lets you call any method of Bot API manually.
func (b *Bot) Raw(method string, payload interface{}) ([]byte, error) {
	url := b.URL + "/bot" + b.Token + "/" + method

	var buf bytes.Buffer
	if err := json.NewEncoder(&buf).Encode(payload); err != nil {
		return []byte{}, wrapSystem(err)
	}

	resp, err := b.client.Post(url, "application/json", &buf)
	if err != nil {
		return []byte{}, errors.Wrap(err, "http.Post failed")
	}
	resp.Close = true
	defer resp.Body.Close()

	json, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return []byte{}, wrapSystem(err)
	}

	return json, nil
}

func addFileToWriter(writer *multipart.Writer,
	filename, field string, file interface{}) error {

	var reader io.Reader
	if r, ok := file.(io.Reader); ok {
		reader = r
	} else if path, ok := file.(string); ok {
		f, err := os.Open(path)
		if err != nil {
			return err
		}
		defer f.Close()
		reader = f
	} else {
		return errors.Errorf("File for field `%v` should be an io.ReadCloser or string", field)
	}

	part, err := writer.CreateFormFile(field, filename)
	if err != nil {
		return err
	}

	_, err = io.Copy(part, reader)
	return err
}

func (b *Bot) sendFiles(method string, files map[string]File,
	params map[string]string) ([]byte, error) {

	body := &bytes.Buffer{}
	rawFiles := map[string]interface{}{}

	for name, f := range files {
		switch {
		case f.InCloud():
			params[name] = f.FileID
		case f.FileURL != "":
			params[name] = f.FileURL
		case f.OnDisk():
			rawFiles[name] = f.FileLocal
		case f.FileReader != nil:
			rawFiles[name] = f.FileReader
		default:
			return nil, errors.Errorf("sendFiles: File for field %s doesn't exist", name)
		}
	}

	if len(rawFiles) == 0 {
		return b.Raw(method, params)
	}

	writer := multipart.NewWriter(body)

	for field, file := range rawFiles {
		if err := addFileToWriter(writer, params["file_name"], field, file); err != nil {
			return nil, wrapSystem(err)
		}
	}

	for field, value := range params {
		if err := writer.WriteField(field, value); err != nil {
			return nil, wrapSystem(err)
		}
	}

	if err := writer.Close(); err != nil {
		return nil, wrapSystem(err)
	}

	url := b.URL + "/bot" + b.Token + "/" + method
	req, err := http.NewRequest("POST", url, body)
	if err != nil {
		return nil, wrapSystem(err)
	}

	req.Header.Add("Content-Type", writer.FormDataContentType())

	resp, err := b.client.Do(req)
	if err != nil {
		return nil, errors.Wrap(err, "http.Post failed")
	}
	defer resp.Body.Close()

	if resp.StatusCode == http.StatusInternalServerError {
		return nil, errors.New("api error: internal server error")
	}

	json, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, wrapSystem(err)
	}

	return json, nil
}

func (b *Bot) sendObject(f *File, what string, params map[string]string, files map[string]File) (*Message, error) {
	sendWhat := "send" + strings.Title(what)

	if what == "videoNote" {
		what = "video_note"
	}

	sendFiles := map[string]File{what: *f}
	for k, v := range files {
		sendFiles[k] = v
	}

	respJSON, err := b.sendFiles(sendWhat, sendFiles, params)
	if err != nil {
		return nil, err
	}

	return extractMsgResponse(respJSON)
}

func (b *Bot) getMe() (*User, error) {
	meJSON, err := b.Raw("getMe", nil)
	if err != nil {
		return nil, err
	}

	var botInfo struct {
		Ok          bool
		Result      *User
		Description string
	}

	err = json.Unmarshal(meJSON, &botInfo)
	if err != nil {
		return nil, errors.Wrap(err, "bad response json")
	}

	if !botInfo.Ok {
		return nil, errors.Errorf("api error: %s", botInfo.Description)
	}

	return botInfo.Result, nil

}

func (b *Bot) getUpdates(offset int, timeout time.Duration) (upd []Update, err error) {
	params := map[string]string{
		"offset":  strconv.Itoa(offset),
		"timeout": strconv.Itoa(int(timeout / time.Second)),
	}
	updatesJSON, errCommand := b.Raw("getUpdates", params)
	if errCommand != nil {
		err = errCommand
		return

	}
	var updatesReceived struct {
		Ok          bool
		Result      []Update
		Description string
	}

	err = json.Unmarshal(updatesJSON, &updatesReceived)
	if err != nil {
		err = errors.Wrap(err, "bad response json")
		return
	}

	if !updatesReceived.Ok {
		err = errors.Errorf("api error: %s", updatesReceived.Description)
		return
	}

	return updatesReceived.Result, nil
}
<|endoftext|>


<commit_before>package consulapi

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strconv"
	"time"
)

// QueryOptions are used to parameterize a query
type QueryOptions struct {
	// Providing a datacenter overwrites the DC provided
	// by the Config
	Datacenter string

	// AllowStale allows any Consul server (non-leader) to service
	// a read. This allows for lower latency and higher throughput
	AllowStale bool

	// RequireConsistent forces the read to be fully consistent.
	// This is more expensive but prevents ever performing a stale
	// read.
	RequireConsistent bool

	// WaitIndex is used to enable a blocking query. Waits
	// until the timeout or the next index is reached
	WaitIndex uint64

	// WaitTime is used to bound the duration of a wait.
	// Defaults to that of the Config, but can be overriden.
	WaitTime time.Duration

	// Token is used to provide a per-request ACL token
	// which overrides the agent's default token.
	Token string
}

// WriteOptions are used to parameterize a write
type WriteOptions struct {
	// Providing a datacenter overwrites the DC provided
	// by the Config
	Datacenter string

	// Token is used to provide a per-request ACL token
	// which overrides the agent's default token.
	Token string
}

// QueryMeta is used to return meta data about a query
type QueryMeta struct {
	// LastIndex. This can be used as a WaitIndex to perform
	// a blocking query
	LastIndex uint64

	// Time of last contact from the leader for the
	// server servicing the request
	LastContact time.Duration

	// Is there a known leader
	KnownLeader bool

	// How long did the request take
	RequestTime time.Duration
}

// WriteMeta is used to return meta data about a write
type WriteMeta struct {
	// How long did the request take
	RequestTime time.Duration
}

// Config is used to configure the creation of a client
type Config struct {
	// Address is the address of the Consul server
	Address string

	// Scheme is the URI scheme for the Consul server
	Scheme string

	// Datacenter to use. If not provided, the default agent datacenter is used.
	Datacenter string

	// HttpClient is the client to use. Default will be
	// used if not provided.
	HttpClient *http.Client

	// WaitTime limits how long a Watch will block. If not provided,
	// the agent default values will be used.
	WaitTime time.Duration

	// Token is used to provide a per-request ACL token
	// which overrides the agent's default token.
	Token string
}

// DefaultConfig returns a default configuration for the client
func DefaultConfig() *Config {
	return &Config{
		Address:    "127.0.0.1:8500",
		Scheme:     "http",
		HttpClient: http.DefaultClient,
	}
}

// Client provides a client to the Consul API
type Client struct {
	config Config
}

// NewClient returns a new client
func NewClient(config *Config) (*Client, error) {
	// bootstrap the config
	defConfig := DefaultConfig()

	if len(config.Address) == 0 {
		config.Address = defConfig.Address
	}

	if len(config.Scheme) == 0 {
		config.Scheme = defConfig.Scheme
	}

	if config.HttpClient == nil {
		config.HttpClient = defConfig.HttpClient
	}

	client := &Client{
		config: *config,
	}
	return client, nil
}

// request is used to help build up a request
type request struct {
	config *Config
	method string
	url    *url.URL
	params url.Values
	body   io.Reader
	obj    interface{}
}

// setQueryOptions is used to annotate the request with
// additional query options
func (r *request) setQueryOptions(q *QueryOptions) {
	if q == nil {
		return
	}
	if q.Datacenter != "" {
		r.params.Set("dc", q.Datacenter)
	}
	if q.AllowStale {
		r.params.Set("stale", "")
	}
	if q.RequireConsistent {
		r.params.Set("consistent", "")
	}
	if q.WaitIndex != 0 {
		r.params.Set("index", strconv.FormatUint(q.WaitIndex, 10))
	}
	if q.WaitTime != 0 {
		r.params.Set("wait", durToMsec(q.WaitTime))
	}
	if q.Token != "" {
		r.params.Set("token", q.Token)
	}
}

// durToMsec converts a duration to a millisecond specified string
func durToMsec(dur time.Duration) string {
	return fmt.Sprintf("%dms", dur/time.Millisecond)
}

// setWriteOptions is used to annotate the request with
// additional write options
func (r *request) setWriteOptions(q *WriteOptions) {
	if q == nil {
		return
	}
	if q.Datacenter != "" {
		r.params.Set("dc", q.Datacenter)
	}
	if q.Token != "" {
		r.params.Set("token", q.Token)
	}
}

// toHTTP converts the request to an HTTP request
func (r *request) toHTTP() (*http.Request, error) {
	// Encode the query parameters
	r.url.RawQuery = r.params.Encode()

	// Get the url sring
	urlRaw := r.url.String()

	// Check if we should encode the body
	if r.body == nil && r.obj != nil {
		if b, err := encodeBody(r.obj); err != nil {
			return nil, err
		} else {
			r.body = b
		}
	}

	// Create the HTTP request
	return http.NewRequest(r.method, urlRaw, r.body)
}

// newRequest is used to create a new request
func (c *Client) newRequest(method, path string) *request {
	r := &request{
		config: &c.config,
		method: method,
		url: &url.URL{
			Scheme: c.config.Scheme,
			Host:   c.config.Address,
			Path:   path,
		},
		params: make(map[string][]string),
	}
	if c.config.Datacenter != "" {
		r.params.Set("dc", c.config.Datacenter)
	}
	if c.config.WaitTime != 0 {
		r.params.Set("wait", durToMsec(r.config.WaitTime))
	}
	if c.config.Token != "" {
		r.params.Set("token", r.config.Token)
	}
	return r
}

// doRequest runs a request with our client
func (c *Client) doRequest(r *request) (time.Duration, *http.Response, error) {
	req, err := r.toHTTP()
	if err != nil {
		return 0, nil, err
	}
	start := time.Now()
	resp, err := c.config.HttpClient.Do(req)
	diff := time.Now().Sub(start)
	return diff, resp, err
}

// parseQueryMeta is used to help parse query meta-data
func parseQueryMeta(resp *http.Response, q *QueryMeta) error {
	header := resp.Header

	// Parse the X-Consul-Index
	index, err := strconv.ParseUint(header.Get("X-Consul-Index"), 10, 64)
	if err != nil {
		return fmt.Errorf("Failed to parse X-Consul-Index: %v", err)
	}
	q.LastIndex = index

	// Parse the X-Consul-LastContact
	last, err := strconv.ParseUint(header.Get("X-Consul-LastContact"), 10, 64)
	if err != nil {
		return fmt.Errorf("Failed to parse X-Consul-LastContact: %v", err)
	}
	q.LastContact = time.Duration(last) * time.Millisecond

	// Parse the X-Consul-KnownLeader
	switch header.Get("X-Consul-KnownLeader") {
	case "true":
		q.KnownLeader = true
	default:
		q.KnownLeader = false
	}
	return nil
}

// decodeBody is used to JSON decode a body
func decodeBody(resp *http.Response, out interface{}) error {
	dec := json.NewDecoder(resp.Body)
	return dec.Decode(out)
}

// encodeBody is used to encode a request body
func encodeBody(obj interface{}) (io.Reader, error) {
	buf := bytes.NewBuffer(nil)
	enc := json.NewEncoder(buf)
	if err := enc.Encode(obj); err != nil {
		return nil, err
	}
	return buf, nil
}

// requireOK is used to wrap doRequest and check for a 200
func requireOK(d time.Duration, resp *http.Response, e error) (time.Duration, *http.Response, error) {
	if e != nil {
		return d, resp, e
	}
	if resp.StatusCode != 200 {
		var buf bytes.Buffer
		io.Copy(&buf, resp.Body)
		return d, resp, fmt.Errorf("Unexpected response code: %d (%s)", resp.StatusCode, buf.Bytes())
	}
	return d, resp, e
}
<commit_msg>http basic auth support for http consul rest endpoint<commit_after>package consulapi

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strconv"
	"time"
)

// QueryOptions are used to parameterize a query
type QueryOptions struct {
	// Providing a datacenter overwrites the DC provided
	// by the Config
	Datacenter string

	// AllowStale allows any Consul server (non-leader) to service
	// a read. This allows for lower latency and higher throughput
	AllowStale bool

	// RequireConsistent forces the read to be fully consistent.
	// This is more expensive but prevents ever performing a stale
	// read.
	RequireConsistent bool

	// WaitIndex is used to enable a blocking query. Waits
	// until the timeout or the next index is reached
	WaitIndex uint64

	// WaitTime is used to bound the duration of a wait.
	// Defaults to that of the Config, but can be overriden.
	WaitTime time.Duration

	// Token is used to provide a per-request ACL token
	// which overrides the agent's default token.
	Token string
}

// WriteOptions are used to parameterize a write
type WriteOptions struct {
	// Providing a datacenter overwrites the DC provided
	// by the Config
	Datacenter string

	// Token is used to provide a per-request ACL token
	// which overrides the agent's default token.
	Token string
}

// QueryMeta is used to return meta data about a query
type QueryMeta struct {
	// LastIndex. This can be used as a WaitIndex to perform
	// a blocking query
	LastIndex uint64

	// Time of last contact from the leader for the
	// server servicing the request
	LastContact time.Duration

	// Is there a known leader
	KnownLeader bool

	// How long did the request take
	RequestTime time.Duration
}

// WriteMeta is used to return meta data about a write
type WriteMeta struct {
	// How long did the request take
	RequestTime time.Duration
}

// HttpBasicAuth is used to authenticate http client with HTTP Basic Authentication
type HttpBasicAuth struct {
	// Username to use for HTTP Basic Authentication
	Username string

	// Password to use for HTTP Basic Authentication
	Password string
}

// Config is used to configure the creation of a client
type Config struct {
	// Address is the address of the Consul server
	Address string

	// Scheme is the URI scheme for the Consul server
	Scheme string

	// Datacenter to use. If not provided, the default agent datacenter is used.
	Datacenter string

	// HttpClient is the client to use. Default will be
	// used if not provided.
	HttpClient *http.Client

	// HttpAuth is the auth info to use for http access.
	HttpAuth *HttpBasicAuth

	// WaitTime limits how long a Watch will block. If not provided,
	// the agent default values will be used.
	WaitTime time.Duration

	// Token is used to provide a per-request ACL token
	// which overrides the agent's default token.
	Token string
}

// DefaultConfig returns a default configuration for the client
func DefaultConfig() *Config {
	return &Config{
		Address:    "127.0.0.1:8500",
		Scheme:     "http",
		HttpClient: http.DefaultClient,
	}
}

// Client provides a client to the Consul API
type Client struct {
	config Config
}

// NewClient returns a new client
func NewClient(config *Config) (*Client, error) {
	// bootstrap the config
	defConfig := DefaultConfig()

	if len(config.Address) == 0 {
		config.Address = defConfig.Address
	}

	if len(config.Scheme) == 0 {
		config.Scheme = defConfig.Scheme
	}

	if config.HttpClient == nil {
		config.HttpClient = defConfig.HttpClient
	}

	client := &Client{
		config: *config,
	}
	return client, nil
}

// request is used to help build up a request
type request struct {
	config *Config
	method string
	url    *url.URL
	params url.Values
	body   io.Reader
	obj    interface{}
}

// setQueryOptions is used to annotate the request with
// additional query options
func (r *request) setQueryOptions(q *QueryOptions) {
	if q == nil {
		return
	}
	if q.Datacenter != "" {
		r.params.Set("dc", q.Datacenter)
	}
	if q.AllowStale {
		r.params.Set("stale", "")
	}
	if q.RequireConsistent {
		r.params.Set("consistent", "")
	}
	if q.WaitIndex != 0 {
		r.params.Set("index", strconv.FormatUint(q.WaitIndex, 10))
	}
	if q.WaitTime != 0 {
		r.params.Set("wait", durToMsec(q.WaitTime))
	}
	if q.Token != "" {
		r.params.Set("token", q.Token)
	}
}

// durToMsec converts a duration to a millisecond specified string
func durToMsec(dur time.Duration) string {
	return fmt.Sprintf("%dms", dur/time.Millisecond)
}

// setWriteOptions is used to annotate the request with
// additional write options
func (r *request) setWriteOptions(q *WriteOptions) {
	if q == nil {
		return
	}
	if q.Datacenter != "" {
		r.params.Set("dc", q.Datacenter)
	}
	if q.Token != "" {
		r.params.Set("token", q.Token)
	}
}

// toHTTP converts the request to an HTTP request
func (r *request) toHTTP() (*http.Request, error) {
	// Encode the query parameters
	r.url.RawQuery = r.params.Encode()

	// Get the url sring
	urlRaw := r.url.String()

	// Check if we should encode the body
	if r.body == nil && r.obj != nil {
		if b, err := encodeBody(r.obj); err != nil {
			return nil, err
		} else {
			r.body = b
		}
	}

	// Create the HTTP request
	req, err := http.NewRequest(r.method, urlRaw, r.body)

	// Setup auth
	if err == nil && r.config.HttpAuth != nil {
		r.config.HttpAuth.auth(req)
	}

	return req, err
}

func (a *HttpBasicAuth) auth(req *http.Request) {
	req.SetBasicAuth(a.Username, a.Password)
}

// newRequest is used to create a new request
func (c *Client) newRequest(method, path string) *request {
	r := &request{
		config: &c.config,
		method: method,
		url: &url.URL{
			Scheme: c.config.Scheme,
			Host:   c.config.Address,
			Path:   path,
		},
		params: make(map[string][]string),
	}
	if c.config.Datacenter != "" {
		r.params.Set("dc", c.config.Datacenter)
	}
	if c.config.WaitTime != 0 {
		r.params.Set("wait", durToMsec(r.config.WaitTime))
	}
	if c.config.Token != "" {
		r.params.Set("token", r.config.Token)
	}
	return r
}

// doRequest runs a request with our client
func (c *Client) doRequest(r *request) (time.Duration, *http.Response, error) {
	req, err := r.toHTTP()
	if err != nil {
		return 0, nil, err
	}
	start := time.Now()
	resp, err := c.config.HttpClient.Do(req)
	diff := time.Now().Sub(start)
	return diff, resp, err
}

// parseQueryMeta is used to help parse query meta-data
func parseQueryMeta(resp *http.Response, q *QueryMeta) error {
	header := resp.Header

	// Parse the X-Consul-Index
	index, err := strconv.ParseUint(header.Get("X-Consul-Index"), 10, 64)
	if err != nil {
		return fmt.Errorf("Failed to parse X-Consul-Index: %v", err)
	}
	q.LastIndex = index

	// Parse the X-Consul-LastContact
	last, err := strconv.ParseUint(header.Get("X-Consul-LastContact"), 10, 64)
	if err != nil {
		return fmt.Errorf("Failed to parse X-Consul-LastContact: %v", err)
	}
	q.LastContact = time.Duration(last) * time.Millisecond

	// Parse the X-Consul-KnownLeader
	switch header.Get("X-Consul-KnownLeader") {
	case "true":
		q.KnownLeader = true
	default:
		q.KnownLeader = false
	}
	return nil
}

// decodeBody is used to JSON decode a body
func decodeBody(resp *http.Response, out interface{}) error {
	dec := json.NewDecoder(resp.Body)
	return dec.Decode(out)
}

// encodeBody is used to encode a request body
func encodeBody(obj interface{}) (io.Reader, error) {
	buf := bytes.NewBuffer(nil)
	enc := json.NewEncoder(buf)
	if err := enc.Encode(obj); err != nil {
		return nil, err
	}
	return buf, nil
}

// requireOK is used to wrap doRequest and check for a 200
func requireOK(d time.Duration, resp *http.Response, e error) (time.Duration, *http.Response, error) {
	if e != nil {
		return d, resp, e
	}
	if resp.StatusCode != 200 {
		var buf bytes.Buffer
		io.Copy(&buf, resp.Body)
		return d, resp, fmt.Errorf("Unexpected response code: %d (%s)", resp.StatusCode, buf.Bytes())
	}
	return d, resp, e
}
<|endoftext|>