<commit_before>// Copyright (c) 2020 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

// Package multierr allows combining one or more errors together.
//
// Overview
//
// Errors can be combined with the use of the Combine function.
//
// 	multierr.Combine(
// 		reader.Close(),
// 		writer.Close(),
// 		conn.Close(),
// 	)
//
// If only two errors are being combined, the Append function may be used
// instead.
//
// 	err = multierr.Append(reader.Close(), writer.Close())
//
// This makes it possible to record resource cleanup failures from deferred
// blocks with the help of named return values.
//
// 	func sendRequest(req Request) (err error) {
// 		conn, err := openConnection()
// 		if err != nil {
// 			return err
// 		}
// 		defer func() {
// 			err = multierr.Append(err, conn.Close())
// 		}()
// 		// ...
// 	}
//
// The underlying list of errors for a returned error object may be retrieved
// with the Errors function.
//
// 	errors := multierr.Errors(err)
// 	if len(errors) > 0 {
// 		fmt.Println("The following errors occurred:", errors)
// 	}
//
// Advanced Usage
//
// Errors returned by Combine and Append MAY implement the following
// interface.
//
// 	type errorGroup interface {
// 		// Returns a slice containing the underlying list of errors.
// 		//
// 		// This slice MUST NOT be modified by the caller.
// 		Errors() []error
// 	}
//
// Note that if you need access to list of errors behind a multierr error, you
// should prefer using the Errors function. That said, if you need cheap
// read-only access to the underlying errors slice, you can attempt to cast
// the error to this interface. You MUST handle the failure case gracefully
// because errors returned by Combine and Append are not guaranteed to
// implement this interface.
//
// 	var errors []error
// 	group, ok := err.(errorGroup)
// 	if ok {
// 		errors = group.Errors()
// 	} else {
// 		errors = []error{err}
// 	}
package multierr // import "go.uber.org/multierr"

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"strings"
	"sync"

	"go.uber.org/atomic"
)

var (
	// Separator for single-line error messages.
	_singlelineSeparator = []byte("; ")

	// Prefix for multi-line messages
	_multilinePrefix = []byte("the following errors occurred:")

	// Prefix for the first and following lines of an item in a list of
	// multi-line error messages.
	//
	// For example, if a single item is:
	//
	// 	foo
	// 	bar
	//
	// It will become,
	//
	// 	 -  foo
	// 	    bar
	_multilineSeparator = []byte("\n -  ")
	_multilineIndent    = []byte("    ")
)

// _bufferPool is a pool of bytes.Buffers.
var _bufferPool = sync.Pool{
	New: func() interface{} {
		return &bytes.Buffer{}
	},
}

type errorGroup interface {
	Errors() []error
}

// Errors returns a slice containing zero or more errors that the supplied
// error is composed of. If the error is nil, a nil slice is returned.
//
// 	err := multierr.Append(r.Close(), w.Close())
// 	errors := multierr.Errors(err)
//
// If the error is not composed of other errors, the returned slice contains
// just the error that was passed in.
//
// Callers of this function are free to modify the returned slice.
func Errors(err error) []error {
	if err == nil {
		return nil
	}

	// Note that we're casting to multiError, not errorGroup. Our contract is
	// that returned errors MAY implement errorGroup. Errors, however, only
	// has special behavior for multierr-specific error objects.
	//
	// This behavior can be expanded in the future but I think it's prudent to
	// start with as little as possible in terms of contract and possibility
	// of misuse.
	eg, ok := err.(*multiError)
	if !ok {
		return []error{err}
	}

	errors := eg.Errors()
	result := make([]error, len(errors))
	copy(result, errors)
	return result
}

// multiError is an error that holds one or more errors.
//
// An instance of this is guaranteed to be non-empty and flattened. That is,
// none of the errors inside multiError are other multiErrors.
//
// multiError formats to a semi-colon delimited list of error messages with
// %v and with a more readable multi-line format with %+v.
type multiError struct {
	copyNeeded atomic.Bool
	errors     []error
}

var _ errorGroup = (*multiError)(nil)

// Errors returns the list of underlying errors.
//
// This slice MUST NOT be modified.
func (merr *multiError) Errors() []error {
	if merr == nil {
		return nil
	}
	return merr.errors
}

// As attempts to find the first error in the error list that matches the type
// of the value that target points to.
//
// This function allows errors.As to traverse the values stored on the
// multierr error.
func (merr *multiError) As(target interface{}) bool {
	for _, err := range merr.Errors() {
		if errors.As(err, target) {
			return true
		}
	}
	return false
}

// Is attempts to match the provided error against errors in the error list.
//
// This function allows errors.Is to traverse the values stored on the
// multierr error.
func (merr *multiError) Is(target error) bool {
	for _, err := range merr.Errors() {
		if errors.Is(err, target) {
			return true
		}
	}
	return false
}

func (merr *multiError) Error() string {
	if merr == nil {
		return ""
	}

	buff := _bufferPool.Get().(*bytes.Buffer)
	buff.Reset()

	merr.writeSingleline(buff)

	result := buff.String()
	_bufferPool.Put(buff)
	return result
}

func (merr *multiError) Format(f fmt.State, c rune) {
	if c == 'v' && f.Flag('+') {
		merr.writeMultiline(f)
	} else {
		merr.writeSingleline(f)
	}
}

func (merr *multiError) writeSingleline(w io.Writer) {
	first := true
	for _, item := range merr.errors {
		if first {
			first = false
		} else {
			w.Write(_singlelineSeparator)
		}
		io.WriteString(w, item.Error())
	}
}

func (merr *multiError) writeMultiline(w io.Writer) {
	w.Write(_multilinePrefix)
	for _, item := range merr.errors {
		w.Write(_multilineSeparator)
		writePrefixLine(w, _multilineIndent, fmt.Sprintf("%+v", item))
	}
}

// Writes s to the writer with the given prefix added before each line after
// the first.
func writePrefixLine(w io.Writer, prefix []byte, s string) {
	first := true
	for len(s) > 0 {
		if first {
			first = false
		} else {
			w.Write(prefix)
		}

		idx := strings.IndexByte(s, '\n')
		if idx < 0 {
			idx = len(s) - 1
		}

		io.WriteString(w, s[:idx+1])
		s = s[idx+1:]
	}
}

type inspectResult struct {
	// Number of top-level non-nil errors
	Count int

	// Total number of errors including multiErrors
	Capacity int

	// Index of the first non-nil error in the list. Value is meaningless if
	// Count is zero.
	FirstErrorIdx int

	// Whether the list contains at least one multiError
	ContainsMultiError bool
}

// Inspects the given slice of errors so that we can efficiently allocate
// space for it.
func inspect(errors []error) (res inspectResult) {
	first := true
	for i, err := range errors {
		if err == nil {
			continue
		}

		res.Count++
		if first {
			first = false
			res.FirstErrorIdx = i
		}

		if merr, ok := err.(*multiError); ok {
			res.Capacity += len(merr.errors)
			res.ContainsMultiError = true
		} else {
			res.Capacity++
		}
	}
	return
}

// fromSlice converts the given list of errors into a single error.
func fromSlice(errors []error) error {
	res := inspect(errors)
	switch res.Count {
	case 0:
		return nil
	case 1:
		// only one non-nil entry
		return errors[res.FirstErrorIdx]
	case len(errors):
		if !res.ContainsMultiError {
			// already flat
			return &multiError{errors: errors}
		}
	}

	nonNilErrs := make([]error, 0, res.Capacity)
	for _, err := range errors[res.FirstErrorIdx:] {
		if err == nil {
			continue
		}

		if nested, ok := err.(*multiError); ok {
			nonNilErrs = append(nonNilErrs, nested.errors...)
		} else {
			nonNilErrs = append(nonNilErrs, err)
		}
	}

	return &multiError{errors: nonNilErrs}
}

// Combine combines the passed errors into a single error.
//
// If zero arguments were passed or if all items are nil, a nil error is
// returned.
//
// 	Combine(nil, nil)  // == nil
//
// If only a single error was passed, it is returned as-is.
//
// 	Combine(err)  // == err
//
// Combine skips over nil arguments so this function may be used to combine
// together errors from operations that fail independently of each other.
//
// 	multierr.Combine(
// 		reader.Close(),
// 		writer.Close(),
// 		pipe.Close(),
// 	)
//
// If any of the passed errors is a multierr error, it will be flattened along
// with the other errors.
//
// 	multierr.Combine(multierr.Combine(err1, err2), err3)
// 	// is the same as
// 	multierr.Combine(err1, err2, err3)
//
// The returned error formats into a readable multi-line error message if
// formatted with %+v.
//
// 	fmt.Sprintf("%+v", multierr.Combine(err1, err2))
func Combine(errors ...error) error {
	return fromSlice(errors)
}

// Append appends the given errors together. Either value may be nil.
//
// This function is a specialization of Combine for the common case where
// there are only two errors.
//
// 	err = multierr.Append(reader.Close(), writer.Close())
//
// The following pattern may also be used to record failure of deferred
// operations without losing information about the original error.
//
// 	func doSomething(..) (err error) {
// 		f := acquireResource()
// 		defer func() {
// 			err = multierr.Append(err, f.Close())
// 		}()
func Append(left error, right error) error {
	switch {
	case left == nil:
		return right
	case right == nil:
		return left
	}

	if _, ok := right.(*multiError); !ok {
		if l, ok := left.(*multiError); ok && !l.copyNeeded.Swap(true) {
			// Common case where the error on the left is constantly being
			// appended to.
			errs := append(l.errors, right)
			return &multiError{errors: errs}
		} else if !ok {
			// Both errors are single errors.
			return &multiError{errors: []error{left, right}}
		}
	}

	// Either right or both, left and right, are multiErrors. Rely on usual
	// expensive logic.
	errors := [2]error{left, right}
	return fromSlice(errors[0:])
}

// AppendInto appends an error into the destination of an error pointer and
// returns whether the error being appended was non-nil.
//
// 	var err error
// 	multierr.AppendInto(&err, r.Close())
// 	multierr.AppendInto(&err, w.Close())
//
// The above is equivalent to,
//
// 	err := multierr.Append(r.Close(), w.Close())
//
// As AppendInto reports whether the provided error was non-nil, it may be
// used to build a multierr error in a loop more ergonomically. For example:
//
// 	var err error
// 	for line := range lines {
// 		var item Item
// 		if multierr.AppendInto(&err, parse(line, &item)) {
// 			continue
// 		}
// 		items = append(items, item)
// 	}
//
// Compare this with a verison that relies solely on Append:
//
// 	var err error
// 	for line := range lines {
// 		var item Item
// 		if parseErr := parse(line, &item); parseErr != nil {
// 			err = multierr.Append(err, parseErr)
// 			continue
// 		}
// 		items = append(items, item)
// 	}
func AppendInto(into *error, err error) (errored bool) {
	if into == nil {
		// We panic if 'into' is nil. This is not documented above
		// because suggesting that the pointer must be non-nil may
		// confuse users into thinking that the error that it points
		// to must be non-nil.
		panic("misuse of multierr.AppendInto: into pointer must not be nil")
	}

	if err == nil {
		return false
	}
	*into = Append(*into, err)
	return true
}
<commit_msg>error.go: Fix typo of doc (#44)<commit_after>// Copyright (c) 2020 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

// Package multierr allows combining one or more errors together.
//
// Overview
//
// Errors can be combined with the use of the Combine function.
//
// 	multierr.Combine(
// 		reader.Close(),
// 		writer.Close(),
// 		conn.Close(),
// 	)
//
// If only two errors are being combined, the Append function may be used
// instead.
//
// 	err = multierr.Append(reader.Close(), writer.Close())
//
// This makes it possible to record resource cleanup failures from deferred
// blocks with the help of named return values.
//
// 	func sendRequest(req Request) (err error) {
// 		conn, err := openConnection()
// 		if err != nil {
// 			return err
// 		}
// 		defer func() {
// 			err = multierr.Append(err, conn.Close())
// 		}()
// 		// ...
// 	}
//
// The underlying list of errors for a returned error object may be retrieved
// with the Errors function.
//
// 	errors := multierr.Errors(err)
// 	if len(errors) > 0 {
// 		fmt.Println("The following errors occurred:", errors)
// 	}
//
// Advanced Usage
//
// Errors returned by Combine and Append MAY implement the following
// interface.
//
// 	type errorGroup interface {
// 		// Returns a slice containing the underlying list of errors.
// 		//
// 		// This slice MUST NOT be modified by the caller.
// 		Errors() []error
// 	}
//
// Note that if you need access to list of errors behind a multierr error, you
// should prefer using the Errors function. That said, if you need cheap
// read-only access to the underlying errors slice, you can attempt to cast
// the error to this interface. You MUST handle the failure case gracefully
// because errors returned by Combine and Append are not guaranteed to
// implement this interface.
//
// 	var errors []error
// 	group, ok := err.(errorGroup)
// 	if ok {
// 		errors = group.Errors()
// 	} else {
// 		errors = []error{err}
// 	}
package multierr // import "go.uber.org/multierr"

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"strings"
	"sync"

	"go.uber.org/atomic"
)

var (
	// Separator for single-line error messages.
	_singlelineSeparator = []byte("; ")

	// Prefix for multi-line messages
	_multilinePrefix = []byte("the following errors occurred:")

	// Prefix for the first and following lines of an item in a list of
	// multi-line error messages.
	//
	// For example, if a single item is:
	//
	// 	foo
	// 	bar
	//
	// It will become,
	//
	// 	 -  foo
	// 	    bar
	_multilineSeparator = []byte("\n -  ")
	_multilineIndent    = []byte("    ")
)

// _bufferPool is a pool of bytes.Buffers.
var _bufferPool = sync.Pool{
	New: func() interface{} {
		return &bytes.Buffer{}
	},
}

type errorGroup interface {
	Errors() []error
}

// Errors returns a slice containing zero or more errors that the supplied
// error is composed of. If the error is nil, a nil slice is returned.
//
// 	err := multierr.Append(r.Close(), w.Close())
// 	errors := multierr.Errors(err)
//
// If the error is not composed of other errors, the returned slice contains
// just the error that was passed in.
//
// Callers of this function are free to modify the returned slice.
func Errors(err error) []error {
	if err == nil {
		return nil
	}

	// Note that we're casting to multiError, not errorGroup. Our contract is
	// that returned errors MAY implement errorGroup. Errors, however, only
	// has special behavior for multierr-specific error objects.
	//
	// This behavior can be expanded in the future but I think it's prudent to
	// start with as little as possible in terms of contract and possibility
	// of misuse.
	eg, ok := err.(*multiError)
	if !ok {
		return []error{err}
	}

	errors := eg.Errors()
	result := make([]error, len(errors))
	copy(result, errors)
	return result
}

// multiError is an error that holds one or more errors.
//
// An instance of this is guaranteed to be non-empty and flattened. That is,
// none of the errors inside multiError are other multiErrors.
//
// multiError formats to a semi-colon delimited list of error messages with
// %v and with a more readable multi-line format with %+v.
type multiError struct {
	copyNeeded atomic.Bool
	errors     []error
}

var _ errorGroup = (*multiError)(nil)

// Errors returns the list of underlying errors.
//
// This slice MUST NOT be modified.
func (merr *multiError) Errors() []error {
	if merr == nil {
		return nil
	}
	return merr.errors
}

// As attempts to find the first error in the error list that matches the type
// of the value that target points to.
//
// This function allows errors.As to traverse the values stored on the
// multierr error.
func (merr *multiError) As(target interface{}) bool {
	for _, err := range merr.Errors() {
		if errors.As(err, target) {
			return true
		}
	}
	return false
}

// Is attempts to match the provided error against errors in the error list.
//
// This function allows errors.Is to traverse the values stored on the
// multierr error.
func (merr *multiError) Is(target error) bool {
	for _, err := range merr.Errors() {
		if errors.Is(err, target) {
			return true
		}
	}
	return false
}

func (merr *multiError) Error() string {
	if merr == nil {
		return ""
	}

	buff := _bufferPool.Get().(*bytes.Buffer)
	buff.Reset()

	merr.writeSingleline(buff)

	result := buff.String()
	_bufferPool.Put(buff)
	return result
}

func (merr *multiError) Format(f fmt.State, c rune) {
	if c == 'v' && f.Flag('+') {
		merr.writeMultiline(f)
	} else {
		merr.writeSingleline(f)
	}
}

func (merr *multiError) writeSingleline(w io.Writer) {
	first := true
	for _, item := range merr.errors {
		if first {
			first = false
		} else {
			w.Write(_singlelineSeparator)
		}
		io.WriteString(w, item.Error())
	}
}

func (merr *multiError) writeMultiline(w io.Writer) {
	w.Write(_multilinePrefix)
	for _, item := range merr.errors {
		w.Write(_multilineSeparator)
		writePrefixLine(w, _multilineIndent, fmt.Sprintf("%+v", item))
	}
}

// Writes s to the writer with the given prefix added before each line after
// the first.
func writePrefixLine(w io.Writer, prefix []byte, s string) {
	first := true
	for len(s) > 0 {
		if first {
			first = false
		} else {
			w.Write(prefix)
		}

		idx := strings.IndexByte(s, '\n')
		if idx < 0 {
			idx = len(s) - 1
		}

		io.WriteString(w, s[:idx+1])
		s = s[idx+1:]
	}
}

type inspectResult struct {
	// Number of top-level non-nil errors
	Count int

	// Total number of errors including multiErrors
	Capacity int

	// Index of the first non-nil error in the list. Value is meaningless if
	// Count is zero.
	FirstErrorIdx int

	// Whether the list contains at least one multiError
	ContainsMultiError bool
}

// Inspects the given slice of errors so that we can efficiently allocate
// space for it.
func inspect(errors []error) (res inspectResult) {
	first := true
	for i, err := range errors {
		if err == nil {
			continue
		}

		res.Count++
		if first {
			first = false
			res.FirstErrorIdx = i
		}

		if merr, ok := err.(*multiError); ok {
			res.Capacity += len(merr.errors)
			res.ContainsMultiError = true
		} else {
			res.Capacity++
		}
	}
	return
}

// fromSlice converts the given list of errors into a single error.
func fromSlice(errors []error) error {
	res := inspect(errors)
	switch res.Count {
	case 0:
		return nil
	case 1:
		// only one non-nil entry
		return errors[res.FirstErrorIdx]
	case len(errors):
		if !res.ContainsMultiError {
			// already flat
			return &multiError{errors: errors}
		}
	}

	nonNilErrs := make([]error, 0, res.Capacity)
	for _, err := range errors[res.FirstErrorIdx:] {
		if err == nil {
			continue
		}

		if nested, ok := err.(*multiError); ok {
			nonNilErrs = append(nonNilErrs, nested.errors...)
		} else {
			nonNilErrs = append(nonNilErrs, err)
		}
	}

	return &multiError{errors: nonNilErrs}
}

// Combine combines the passed errors into a single error.
//
// If zero arguments were passed or if all items are nil, a nil error is
// returned.
//
// 	Combine(nil, nil)  // == nil
//
// If only a single error was passed, it is returned as-is.
//
// 	Combine(err)  // == err
//
// Combine skips over nil arguments so this function may be used to combine
// together errors from operations that fail independently of each other.
//
// 	multierr.Combine(
// 		reader.Close(),
// 		writer.Close(),
// 		pipe.Close(),
// 	)
//
// If any of the passed errors is a multierr error, it will be flattened along
// with the other errors.
//
// 	multierr.Combine(multierr.Combine(err1, err2), err3)
// 	// is the same as
// 	multierr.Combine(err1, err2, err3)
//
// The returned error formats into a readable multi-line error message if
// formatted with %+v.
//
// 	fmt.Sprintf("%+v", multierr.Combine(err1, err2))
func Combine(errors ...error) error {
	return fromSlice(errors)
}

// Append appends the given errors together. Either value may be nil.
//
// This function is a specialization of Combine for the common case where
// there are only two errors.
//
// 	err = multierr.Append(reader.Close(), writer.Close())
//
// The following pattern may also be used to record failure of deferred
// operations without losing information about the original error.
//
// 	func doSomething(..) (err error) {
// 		f := acquireResource()
// 		defer func() {
// 			err = multierr.Append(err, f.Close())
// 		}()
func Append(left error, right error) error {
	switch {
	case left == nil:
		return right
	case right == nil:
		return left
	}

	if _, ok := right.(*multiError); !ok {
		if l, ok := left.(*multiError); ok && !l.copyNeeded.Swap(true) {
			// Common case where the error on the left is constantly being
			// appended to.
			errs := append(l.errors, right)
			return &multiError{errors: errs}
		} else if !ok {
			// Both errors are single errors.
			return &multiError{errors: []error{left, right}}
		}
	}

	// Either right or both, left and right, are multiErrors. Rely on usual
	// expensive logic.
	errors := [2]error{left, right}
	return fromSlice(errors[0:])
}

// AppendInto appends an error into the destination of an error pointer and
// returns whether the error being appended was non-nil.
//
// 	var err error
// 	multierr.AppendInto(&err, r.Close())
// 	multierr.AppendInto(&err, w.Close())
//
// The above is equivalent to,
//
// 	err := multierr.Append(r.Close(), w.Close())
//
// As AppendInto reports whether the provided error was non-nil, it may be
// used to build a multierr error in a loop more ergonomically. For example:
//
// 	var err error
// 	for line := range lines {
// 		var item Item
// 		if multierr.AppendInto(&err, parse(line, &item)) {
// 			continue
// 		}
// 		items = append(items, item)
// 	}
//
// Compare this with a version that relies solely on Append:
//
// 	var err error
// 	for line := range lines {
// 		var item Item
// 		if parseErr := parse(line, &item); parseErr != nil {
// 			err = multierr.Append(err, parseErr)
// 			continue
// 		}
// 		items = append(items, item)
// 	}
func AppendInto(into *error, err error) (errored bool) {
	if into == nil {
		// We panic if 'into' is nil. This is not documented above
		// because suggesting that the pointer must be non-nil may
		// confuse users into thinking that the error that it points
		// to must be non-nil.
		panic("misuse of multierr.AppendInto: into pointer must not be nil")
	}

	if err == nil {
		return false
	}
	*into = Append(*into, err)
	return true
}
<|endoftext|>


<commit_before>package client

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"os"
	"path/filepath"
	"text/template"
	"regexp"

	services "github.com/maximilien/softlayer-go/services"
	softlayer "github.com/maximilien/softlayer-go/softlayer"
)

const (
	SOFTLAYER_API_URL  = "api.softlayer.com/rest/v3"
	TEMPLATE_ROOT_PATH = "templates"
	SL_GO_NON_VERBOSE  = "SL_GO_NON_VERBOSE"
)

type SoftLayerClient struct {
	username string
	apiKey   string

	templatePath string

	HTTPClient *http.Client

	softLayerServices map[string]softlayer.Service

	nonVerbose bool
}

func NewSoftLayerClient(username, apiKey string) *SoftLayerClient {
	pwd, err := os.Getwd()
	if err != nil {
		panic(err) // this should be handled by the user
	}

	slc := &SoftLayerClient{
		username: username,
		apiKey:   apiKey,

		templatePath: filepath.Join(pwd, TEMPLATE_ROOT_PATH),

		HTTPClient: http.DefaultClient,
		nonVerbose: checkNonVerbose(),

		softLayerServices: map[string]softlayer.Service{},
	}

	slc.initSoftLayerServices()

	return slc
}

//softlayer.Client interface methods

func (slc *SoftLayerClient) GetService(serviceName string) (softlayer.Service, error) {
	slService, ok := slc.softLayerServices[serviceName]
	if !ok {
		return nil, errors.New(fmt.Sprintf("softlayer-go does not support service '%s'", serviceName))
	}

	return slService, nil
}

func (slc *SoftLayerClient) GetSoftLayer_Account_Service() (softlayer.SoftLayer_Account_Service, error) {
	slService, err := slc.GetService("SoftLayer_Account")
	if err != nil {
		return nil, err
	}

	return slService.(softlayer.SoftLayer_Account_Service), nil
}

func (slc *SoftLayerClient) GetSoftLayer_Virtual_Guest_Service() (softlayer.SoftLayer_Virtual_Guest_Service, error) {
	slService, err := slc.GetService("SoftLayer_Virtual_Guest")
	if err != nil {
		return nil, err
	}

	return slService.(softlayer.SoftLayer_Virtual_Guest_Service), nil
}

func (slc *SoftLayerClient) GetSoftLayer_Virtual_Disk_Image_Service() (softlayer.SoftLayer_Virtual_Disk_Image_Service, error) {
	slService, err := slc.GetService("SoftLayer_Virtual_Disk_Image")
	if err != nil {
		return nil, err
	}

	return slService.(softlayer.SoftLayer_Virtual_Disk_Image_Service), nil
}

func (slc *SoftLayerClient) GetSoftLayer_Security_Ssh_Key_Service() (softlayer.SoftLayer_Security_Ssh_Key_Service, error) {
	slService, err := slc.GetService("SoftLayer_Security_Ssh_Key")
	if err != nil {
		return nil, err
	}

	return slService.(softlayer.SoftLayer_Security_Ssh_Key_Service), nil
}

func (slc *SoftLayerClient) GetSoftLayer_Product_Package_Service() (softlayer.SoftLayer_Product_Package_Service, error) {
	slService, err := slc.GetService("SoftLayer_Product_Package")
	if err != nil {
		return nil, err
	}

	return slService.(softlayer.SoftLayer_Product_Package_Service), nil
}

func (slc *SoftLayerClient) GetSoftLayer_Virtual_Guest_Block_Device_Template_Group_Service() (softlayer.SoftLayer_Virtual_Guest_Block_Device_Template_Group_Service, error) {
	slService, err := slc.GetService("SoftLayer_Virtual_Guest_Block_Device_Template_Group")
	if err != nil {
		return nil, err
	}

	return slService.(softlayer.SoftLayer_Virtual_Guest_Block_Device_Template_Group_Service), nil
}

func (slc *SoftLayerClient) GetSoftLayer_Network_Storage_Service() (softlayer.SoftLayer_Network_Storage_Service, error) {
	slService, err := slc.GetService("SoftLayer_Network_Storage")
	if err != nil {
		return nil, err
	}

	return slService.(softlayer.SoftLayer_Network_Storage_Service), nil
}

func (slc *SoftLayerClient) GetSoftLayer_Network_Storage_Allowed_Host_Service() (softlayer.SoftLayer_Network_Storage_Allowed_Host_Service, error) {
	slService, err := slc.GetService("SoftLayer_Network_Storage_Allowed_Host")
	if err != nil {
		return nil, err
	}

	return slService.(softlayer.SoftLayer_Network_Storage_Allowed_Host_Service), nil
}

func (slc *SoftLayerClient) GetSoftLayer_Product_Order_Service() (softlayer.SoftLayer_Product_Order_Service, error) {
	slService, err := slc.GetService("SoftLayer_Product_Order")
	if err != nil {
		return nil, err
	}

	return slService.(softlayer.SoftLayer_Product_Order_Service), nil
}

func (slc *SoftLayerClient) GetSoftLayer_Billing_Item_Cancellation_Request_Service() (softlayer.SoftLayer_Billing_Item_Cancellation_Request_Service, error) {
	slService, err := slc.GetService("SoftLayer_Billing_Item_Cancellation_Request")
	if err != nil {
		return nil, err
	}

	return slService.(softlayer.SoftLayer_Billing_Item_Cancellation_Request_Service), nil
}

func (slc *SoftLayerClient) GetSoftLayer_Hardware_Service() (softlayer.SoftLayer_Hardware_Service, error) {
	slService, err := slc.GetService("SoftLayer_Hardware")
	if err != nil {
		return nil, err
	}

	return slService.(softlayer.SoftLayer_Hardware_Service), nil
}

//Public methods

func (slc *SoftLayerClient) DoRawHttpRequestWithObjectMask(path string, masks []string, requestType string, requestBody *bytes.Buffer) ([]byte, error) {
	url := fmt.Sprintf("https://%s:%s@%s/%s", slc.username, slc.apiKey, SOFTLAYER_API_URL, path)

	url += "?objectMask="
	for i := 0; i < len(masks); i++ {
		url += masks[i]
		if i != len(masks)-1 {
			url += ";"
		}
	}

	return slc.makeHttpRequest(url, requestType, requestBody)
}

func (slc *SoftLayerClient) DoRawHttpRequestWithObjectFilter(path string, filters string, requestType string, requestBody *bytes.Buffer) ([]byte, error) {
	url := fmt.Sprintf("https://%s:%s@%s/%s", slc.username, slc.apiKey, SOFTLAYER_API_URL, path)
	url += "?objectFilter=" + filters

	return slc.makeHttpRequest(url, requestType, requestBody)
}

func (slc *SoftLayerClient) DoRawHttpRequestWithObjectFilterAndObjectMask(path string, masks []string, filters string, requestType string, requestBody *bytes.Buffer) ([]byte, error) {
	url := fmt.Sprintf("https://%s:%s@%s/%s", slc.username, slc.apiKey, SOFTLAYER_API_URL, path)

	url += "?objectFilter=" + filters

	url += "&objectMask=filteredMask["
	for i := 0; i < len(masks); i++ {
		url += masks[i]
		if i != len(masks)-1 {
			url += ";"
		}
	}
	url += "]"

	return slc.makeHttpRequest(url, requestType, requestBody)
}

func (slc *SoftLayerClient) DoRawHttpRequest(path string, requestType string, requestBody *bytes.Buffer) ([]byte, error) {
	url := fmt.Sprintf("https://%s:%s@%s/%s", slc.username, slc.apiKey, SOFTLAYER_API_URL, path)
	return slc.makeHttpRequest(url, requestType, requestBody)
}

func (slc *SoftLayerClient) GenerateRequestBody(templateData interface{}) (*bytes.Buffer, error) {
	cwd, err := os.Getwd()
	if err != nil {
		return nil, err
	}

	bodyTemplate := template.Must(template.ParseFiles(filepath.Join(cwd, slc.templatePath)))
	body := new(bytes.Buffer)
	bodyTemplate.Execute(body, templateData)

	return body, nil
}

func (slc *SoftLayerClient) HasErrors(body map[string]interface{}) error {
	if errString, ok := body["error"]; !ok {
		return nil
	} else {
		return errors.New(errString.(string))
	}
}

func (slc *SoftLayerClient) CheckForHttpResponseErrors(data []byte) error {
	var decodedResponse map[string]interface{}
	err := json.Unmarshal(data, &decodedResponse)
	if err != nil {
		return err
	}

	if err := slc.HasErrors(decodedResponse); err != nil {
		return err
	}

	return nil
}

//Private methods

func (slc *SoftLayerClient) initSoftLayerServices() {
	slc.softLayerServices["SoftLayer_Account"] = services.NewSoftLayer_Account_Service(slc)
	slc.softLayerServices["SoftLayer_Virtual_Guest"] = services.NewSoftLayer_Virtual_Guest_Service(slc)
	slc.softLayerServices["SoftLayer_Virtual_Disk_Image"] = services.NewSoftLayer_Virtual_Disk_Image_Service(slc)
	slc.softLayerServices["SoftLayer_Security_Ssh_Key"] = services.NewSoftLayer_Security_Ssh_Key_Service(slc)
	slc.softLayerServices["SoftLayer_Product_Package"] = services.NewSoftLayer_Product_Package_Service(slc)
	slc.softLayerServices["SoftLayer_Network_Storage"] = services.NewSoftLayer_Network_Storage_Service(slc)
	slc.softLayerServices["SoftLayer_Network_Storage_Allowed_Host"] = services.NewSoftLayer_Network_Storage_Allowed_Host_Service(slc)
	slc.softLayerServices["SoftLayer_Product_Order"] = services.NewSoftLayer_Product_Order_Service(slc)
	slc.softLayerServices["SoftLayer_Billing_Item_Cancellation_Request"] = services.NewSoftLayer_Billing_Item_Cancellation_Request_Service(slc)
	slc.softLayerServices["SoftLayer_Virtual_Guest_Block_Device_Template_Group"] = services.NewSoftLayer_Virtual_Guest_Block_Device_Template_Group_Service(slc)
	slc.softLayerServices["SoftLayer_Hardware"] = services.NewSoftLayer_Hardware_Service(slc)
}

func hideCredentials(s string) string {
	hiddenStr := "\"password\":\"******\""
	r := regexp.MustCompile(`"password":"[^"]*"`)

	return r.ReplaceAllString(s, hiddenStr)
}

func (slc *SoftLayerClient) makeHttpRequest(url string, requestType string, requestBody *bytes.Buffer) ([]byte, error) {
	req, err := http.NewRequest(requestType, url, requestBody)
	if err != nil {
		return nil, err
	}

	bs, err := httputil.DumpRequest(req, true)
	if err != nil {
		return nil, err
	}

	if !slc.nonVerbose {
		fmt.Fprintf(os.Stderr, "\n---\n[softlayer-go] Request:\n%s\n", hideCredentials(string(bs)))
	}

	resp, err := slc.HTTPClient.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	bs, err = httputil.DumpResponse(resp, true)
	if err != nil {
		return nil, err
	}

	if !slc.nonVerbose {
		fmt.Fprintf(os.Stderr, "[softlayer-go] Response:\n%s\n", hideCredentials(string(bs)))
	}

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

	return responseBody, nil
}

//Private helper methods

func checkNonVerbose() bool {
	slGoNonVerbose := os.Getenv(SL_GO_NON_VERBOSE)
	switch slGoNonVerbose {
	case "yes":
		return true
	case "YES":
		return true
	case "true":
		return true
	case "TRUE":
		return true
	}

	return false
}
<commit_msg>formatting<commit_after>package client

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"os"
	"path/filepath"
	"regexp"
	"text/template"

	services "github.com/maximilien/softlayer-go/services"
	softlayer "github.com/maximilien/softlayer-go/softlayer"
)

const (
	SOFTLAYER_API_URL  = "api.softlayer.com/rest/v3"
	TEMPLATE_ROOT_PATH = "templates"
	SL_GO_NON_VERBOSE  = "SL_GO_NON_VERBOSE"
)

type SoftLayerClient struct {
	username string
	apiKey   string

	templatePath string

	HTTPClient *http.Client

	softLayerServices map[string]softlayer.Service

	nonVerbose bool
}

func NewSoftLayerClient(username, apiKey string) *SoftLayerClient {
	pwd, err := os.Getwd()
	if err != nil {
		panic(err) // this should be handled by the user
	}

	slc := &SoftLayerClient{
		username: username,
		apiKey:   apiKey,

		templatePath: filepath.Join(pwd, TEMPLATE_ROOT_PATH),

		HTTPClient: http.DefaultClient,
		nonVerbose: checkNonVerbose(),

		softLayerServices: map[string]softlayer.Service{},
	}

	slc.initSoftLayerServices()

	return slc
}

//softlayer.Client interface methods

func (slc *SoftLayerClient) GetService(serviceName string) (softlayer.Service, error) {
	slService, ok := slc.softLayerServices[serviceName]
	if !ok {
		return nil, errors.New(fmt.Sprintf("softlayer-go does not support service '%s'", serviceName))
	}

	return slService, nil
}

func (slc *SoftLayerClient) GetSoftLayer_Account_Service() (softlayer.SoftLayer_Account_Service, error) {
	slService, err := slc.GetService("SoftLayer_Account")
	if err != nil {
		return nil, err
	}

	return slService.(softlayer.SoftLayer_Account_Service), nil
}

func (slc *SoftLayerClient) GetSoftLayer_Virtual_Guest_Service() (softlayer.SoftLayer_Virtual_Guest_Service, error) {
	slService, err := slc.GetService("SoftLayer_Virtual_Guest")
	if err != nil {
		return nil, err
	}

	return slService.(softlayer.SoftLayer_Virtual_Guest_Service), nil
}

func (slc *SoftLayerClient) GetSoftLayer_Virtual_Disk_Image_Service() (softlayer.SoftLayer_Virtual_Disk_Image_Service, error) {
	slService, err := slc.GetService("SoftLayer_Virtual_Disk_Image")
	if err != nil {
		return nil, err
	}

	return slService.(softlayer.SoftLayer_Virtual_Disk_Image_Service), nil
}

func (slc *SoftLayerClient) GetSoftLayer_Security_Ssh_Key_Service() (softlayer.SoftLayer_Security_Ssh_Key_Service, error) {
	slService, err := slc.GetService("SoftLayer_Security_Ssh_Key")
	if err != nil {
		return nil, err
	}

	return slService.(softlayer.SoftLayer_Security_Ssh_Key_Service), nil
}

func (slc *SoftLayerClient) GetSoftLayer_Product_Package_Service() (softlayer.SoftLayer_Product_Package_Service, error) {
	slService, err := slc.GetService("SoftLayer_Product_Package")
	if err != nil {
		return nil, err
	}

	return slService.(softlayer.SoftLayer_Product_Package_Service), nil
}

func (slc *SoftLayerClient) GetSoftLayer_Virtual_Guest_Block_Device_Template_Group_Service() (softlayer.SoftLayer_Virtual_Guest_Block_Device_Template_Group_Service, error) {
	slService, err := slc.GetService("SoftLayer_Virtual_Guest_Block_Device_Template_Group")
	if err != nil {
		return nil, err
	}

	return slService.(softlayer.SoftLayer_Virtual_Guest_Block_Device_Template_Group_Service), nil
}

func (slc *SoftLayerClient) GetSoftLayer_Network_Storage_Service() (softlayer.SoftLayer_Network_Storage_Service, error) {
	slService, err := slc.GetService("SoftLayer_Network_Storage")
	if err != nil {
		return nil, err
	}

	return slService.(softlayer.SoftLayer_Network_Storage_Service), nil
}

func (slc *SoftLayerClient) GetSoftLayer_Network_Storage_Allowed_Host_Service() (softlayer.SoftLayer_Network_Storage_Allowed_Host_Service, error) {
	slService, err := slc.GetService("SoftLayer_Network_Storage_Allowed_Host")
	if err != nil {
		return nil, err
	}

	return slService.(softlayer.SoftLayer_Network_Storage_Allowed_Host_Service), nil
}

func (slc *SoftLayerClient) GetSoftLayer_Product_Order_Service() (softlayer.SoftLayer_Product_Order_Service, error) {
	slService, err := slc.GetService("SoftLayer_Product_Order")
	if err != nil {
		return nil, err
	}

	return slService.(softlayer.SoftLayer_Product_Order_Service), nil
}

func (slc *SoftLayerClient) GetSoftLayer_Billing_Item_Cancellation_Request_Service() (softlayer.SoftLayer_Billing_Item_Cancellation_Request_Service, error) {
	slService, err := slc.GetService("SoftLayer_Billing_Item_Cancellation_Request")
	if err != nil {
		return nil, err
	}

	return slService.(softlayer.SoftLayer_Billing_Item_Cancellation_Request_Service), nil
}

func (slc *SoftLayerClient) GetSoftLayer_Hardware_Service() (softlayer.SoftLayer_Hardware_Service, error) {
	slService, err := slc.GetService("SoftLayer_Hardware")
	if err != nil {
		return nil, err
	}

	return slService.(softlayer.SoftLayer_Hardware_Service), nil
}

//Public methods

func (slc *SoftLayerClient) DoRawHttpRequestWithObjectMask(path string, masks []string, requestType string, requestBody *bytes.Buffer) ([]byte, error) {
	url := fmt.Sprintf("https://%s:%s@%s/%s", slc.username, slc.apiKey, SOFTLAYER_API_URL, path)

	url += "?objectMask="
	for i := 0; i < len(masks); i++ {
		url += masks[i]
		if i != len(masks)-1 {
			url += ";"
		}
	}

	return slc.makeHttpRequest(url, requestType, requestBody)
}

func (slc *SoftLayerClient) DoRawHttpRequestWithObjectFilter(path string, filters string, requestType string, requestBody *bytes.Buffer) ([]byte, error) {
	url := fmt.Sprintf("https://%s:%s@%s/%s", slc.username, slc.apiKey, SOFTLAYER_API_URL, path)
	url += "?objectFilter=" + filters

	return slc.makeHttpRequest(url, requestType, requestBody)
}

func (slc *SoftLayerClient) DoRawHttpRequestWithObjectFilterAndObjectMask(path string, masks []string, filters string, requestType string, requestBody *bytes.Buffer) ([]byte, error) {
	url := fmt.Sprintf("https://%s:%s@%s/%s", slc.username, slc.apiKey, SOFTLAYER_API_URL, path)

	url += "?objectFilter=" + filters

	url += "&objectMask=filteredMask["
	for i := 0; i < len(masks); i++ {
		url += masks[i]
		if i != len(masks)-1 {
			url += ";"
		}
	}
	url += "]"

	return slc.makeHttpRequest(url, requestType, requestBody)
}

func (slc *SoftLayerClient) DoRawHttpRequest(path string, requestType string, requestBody *bytes.Buffer) ([]byte, error) {
	url := fmt.Sprintf("https://%s:%s@%s/%s", slc.username, slc.apiKey, SOFTLAYER_API_URL, path)
	return slc.makeHttpRequest(url, requestType, requestBody)
}

func (slc *SoftLayerClient) GenerateRequestBody(templateData interface{}) (*bytes.Buffer, error) {
	cwd, err := os.Getwd()
	if err != nil {
		return nil, err
	}

	bodyTemplate := template.Must(template.ParseFiles(filepath.Join(cwd, slc.templatePath)))
	body := new(bytes.Buffer)
	bodyTemplate.Execute(body, templateData)

	return body, nil
}

func (slc *SoftLayerClient) HasErrors(body map[string]interface{}) error {
	if errString, ok := body["error"]; !ok {
		return nil
	} else {
		return errors.New(errString.(string))
	}
}

func (slc *SoftLayerClient) CheckForHttpResponseErrors(data []byte) error {
	var decodedResponse map[string]interface{}
	err := json.Unmarshal(data, &decodedResponse)
	if err != nil {
		return err
	}

	if err := slc.HasErrors(decodedResponse); err != nil {
		return err
	}

	return nil
}

//Private methods

func (slc *SoftLayerClient) initSoftLayerServices() {
	slc.softLayerServices["SoftLayer_Account"] = services.NewSoftLayer_Account_Service(slc)
	slc.softLayerServices["SoftLayer_Virtual_Guest"] = services.NewSoftLayer_Virtual_Guest_Service(slc)
	slc.softLayerServices["SoftLayer_Virtual_Disk_Image"] = services.NewSoftLayer_Virtual_Disk_Image_Service(slc)
	slc.softLayerServices["SoftLayer_Security_Ssh_Key"] = services.NewSoftLayer_Security_Ssh_Key_Service(slc)
	slc.softLayerServices["SoftLayer_Product_Package"] = services.NewSoftLayer_Product_Package_Service(slc)
	slc.softLayerServices["SoftLayer_Network_Storage"] = services.NewSoftLayer_Network_Storage_Service(slc)
	slc.softLayerServices["SoftLayer_Network_Storage_Allowed_Host"] = services.NewSoftLayer_Network_Storage_Allowed_Host_Service(slc)
	slc.softLayerServices["SoftLayer_Product_Order"] = services.NewSoftLayer_Product_Order_Service(slc)
	slc.softLayerServices["SoftLayer_Billing_Item_Cancellation_Request"] = services.NewSoftLayer_Billing_Item_Cancellation_Request_Service(slc)
	slc.softLayerServices["SoftLayer_Virtual_Guest_Block_Device_Template_Group"] = services.NewSoftLayer_Virtual_Guest_Block_Device_Template_Group_Service(slc)
	slc.softLayerServices["SoftLayer_Hardware"] = services.NewSoftLayer_Hardware_Service(slc)
}

func hideCredentials(s string) string {
	hiddenStr := "\"password\":\"******\""
	r := regexp.MustCompile(`"password":"[^"]*"`)

	return r.ReplaceAllString(s, hiddenStr)
}

func (slc *SoftLayerClient) makeHttpRequest(url string, requestType string, requestBody *bytes.Buffer) ([]byte, error) {
	req, err := http.NewRequest(requestType, url, requestBody)
	if err != nil {
		return nil, err
	}

	bs, err := httputil.DumpRequest(req, true)
	if err != nil {
		return nil, err
	}

	if !slc.nonVerbose {
		fmt.Fprintf(os.Stderr, "\n---\n[softlayer-go] Request:\n%s\n", hideCredentials(string(bs)))
	}

	resp, err := slc.HTTPClient.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	bs, err = httputil.DumpResponse(resp, true)
	if err != nil {
		return nil, err
	}

	if !slc.nonVerbose {
		fmt.Fprintf(os.Stderr, "[softlayer-go] Response:\n%s\n", hideCredentials(string(bs)))
	}

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

	return responseBody, nil
}

//Private helper methods

func checkNonVerbose() bool {
	slGoNonVerbose := os.Getenv(SL_GO_NON_VERBOSE)
	switch slGoNonVerbose {
	case "yes":
		return true
	case "YES":
		return true
	case "true":
		return true
	case "TRUE":
		return true
	}

	return false
}
<|endoftext|>


<commit_before>package girc

import (
	"bytes"
	"strings"
)

const (
	prefix     byte = 0x3A // prefix or last argument
	prefixUser byte = 0x21 // username
	prefixHost byte = 0x40 // hostname
	space      byte = 0x20 // separator

	maxLength = 510 // maximum length is 510 (2 for line endings)
)

func cutsetFunc(r rune) bool {
	// Characters to trim from prefixes/messages.
	return r == '\r' || r == '\n'
}

// Prefix represents the sender of an IRC event, see RFC1459 section 2.3.1
// <servername> | <nick> [ '!' <user> ] [ '@' <host> ]
type Prefix struct {
	Name string // Nick or servername
	User string // Username
	Host string // Hostname
}

// ParsePrefix takes a string and attempts to create a Prefix struct.
func ParsePrefix(raw string) (p *Prefix) {
	p = new(Prefix)

	user := indexByte(raw, prefixUser)
	host := indexByte(raw, prefixHost)

	switch {
	case user > 0 && host > user:
		p.Name = raw[:user]
		p.User = raw[user+1 : host]
		p.Host = raw[host+1:]
	case user > 0:
		p.Name = raw[:user]
		p.User = raw[user+1:]
	case host > 0:
		p.Name = raw[:host]
		p.Host = raw[host+1:]
	default:
		p.Name = raw

	}

	return p
}

// Len calculates the length of the string representation of prefix
func (p *Prefix) Len() (length int) {
	length = len(p.Name)
	if len(p.User) > 0 {
		length = 1 + length + len(p.User)
	}
	if len(p.Host) > 0 {
		length = 1 + length + len(p.Host)
	}

	return
}

// Bytes returns a []byte representation of prefix
func (p *Prefix) Bytes() []byte {
	buffer := new(bytes.Buffer)
	p.writeTo(buffer)

	return buffer.Bytes()
}

// String returns a string representation of prefix
func (p *Prefix) String() (s string) {
	s = p.Name
	if len(p.User) > 0 {
		s = s + string(prefixUser) + p.User
	}
	if len(p.Host) > 0 {
		s = s + string(prefixHost) + p.Host
	}

	return
}

// IsHostmask returns true if prefix looks like a user hostmask
func (p *Prefix) IsHostmask() bool {
	return len(p.User) > 0 && len(p.Host) > 0
}

// IsServer returns true if this prefix looks like a server name.
func (p *Prefix) IsServer() bool {
	return len(p.User) <= 0 && len(p.Host) <= 0 // && indexByte(p.Name, '.') > 0
}

// writeTo is an utility function to write the prefix to the bytes.Buffer in Event.String()
func (p *Prefix) writeTo(buffer *bytes.Buffer) {
	buffer.WriteString(p.Name)
	if len(p.User) > 0 {
		buffer.WriteByte(prefixUser)
		buffer.WriteString(p.User)
	}
	if len(p.Host) > 0 {
		buffer.WriteByte(prefixHost)
		buffer.WriteString(p.Host)
	}

	return
}

// Event represents an IRC protocol message, see RFC1459 section 2.3.1
//
//    <message>  :: [':' <prefix> <SPACE>] <command> <params> <crlf>
//    <prefix>   :: <servername> | <nick> ['!' <user>] ['@' <host>]
//    <command>  :: <letter>{<letter>} | <number> <number> <number>
//    <SPACE>    :: ' '{' '}
//    <params>   :: <SPACE> [':' <trailing> | <middle> <params>]
//    <middle>   :: <Any *non-empty* sequence of octets not including SPACE or NUL
//                   or CR or LF, the first of which may not be ':'>
//    <trailing> :: <Any, possibly empty, sequence of octets not including NUL or
//                   CR or LF>
//    <crlf>     :: CR LF
type Event struct {
	*Prefix                // The source of the event
	Command       string   // the IRC command, e.g. JOIN, PRIVMSG, KILL
	Params        []string // parameters to the command. Commonly nickname, channel, etc
	Trailing      string   // any trailing data. e.g. with a PRIVMSG, this is the message text
	EmptyTrailing bool     // if true, trailing prefix (:) will be added even if Event.Trailing is empty
	Sensitive     bool     // if the message is sensitive (e.g. and should not be logged)
}

// ParseEvent takes a string and attempts to create a Event struct.
// Returns nil if the Event is invalid.
func ParseEvent(raw string) (e *Event) {
	// ignore empty events
	if raw = strings.TrimFunc(raw, cutsetFunc); len(raw) < 2 {
		return nil
	}

	i, j := 0, 0
	e = new(Event)

	if raw[0] == prefix {
		// prefix ends with a space
		i = indexByte(raw, space)

		// prefix string must not be empty if the indicator is present
		if i < 2 {
			return nil
		}

		e.Prefix = ParsePrefix(raw[1:i])

		i++ // skip space at the end of the prefix
	}

	// find end of command
	j = i + indexByte(raw[i:], space)

	// extract command
	if j < i {
		e.Command = strings.ToUpper(raw[i:])
		return e
	}

	e.Command = strings.ToUpper(raw[i:j])
	j++ // skip space after command

	// find prefix for trailer
	i = indexByte(raw[j:], prefix)

	if i < 0 || raw[j+i-1] != space {
		// no trailing argument
		e.Params = strings.Split(raw[j:], string(space))
		return e
	}

	// compensate for index on substring
	i = i + j

	// check if we need to parse arguments
	if i > j {
		e.Params = strings.Split(raw[j:i-1], string(space))
	}

	e.Trailing = raw[i+1:]

	// we need to re-encode the trailing argument even if it was empty
	if len(e.Trailing) <= 0 {
		e.EmptyTrailing = true
	}

	return e

}

// Len calculates the length of the string representation of event
func (e *Event) Len() (length int) {
	if e.Prefix != nil {
		length = e.Prefix.Len() + 2 // include prefix and trailing space
	}

	length = length + len(e.Command)

	if len(e.Params) > 0 {
		length = length + len(e.Params)

		for i := 0; i < len(e.Params); i++ {
			length = length + len(e.Params[i])
		}
	}

	if len(e.Trailing) > 0 || e.EmptyTrailing {
		length = length + len(e.Trailing) + 2 // include prefix and space
	}

	return
}

// Bytes returns a []byte representation of event
//
// per RFC2812 section 2.3, messages should not exceed 512 characters
// in length. this method forces that limit by discarding any characters
// exceeding the length limit.
func (e *Event) Bytes() []byte {
	buffer := new(bytes.Buffer)

	// event prefix
	if e.Prefix != nil {
		buffer.WriteByte(prefix)
		e.Prefix.writeTo(buffer)
		buffer.WriteByte(space)
	}

	// command is required
	buffer.WriteString(e.Command)

	// space separated list of arguments
	if len(e.Params) > 0 {
		buffer.WriteByte(space)
		buffer.WriteString(strings.Join(e.Params, string(space)))
	}

	if len(e.Trailing) > 0 || e.EmptyTrailing {
		buffer.WriteByte(space)
		buffer.WriteByte(prefix)
		buffer.WriteString(e.Trailing)
	}

	// we need the limit the buffer length
	if buffer.Len() > (maxLength) {
		buffer.Truncate(maxLength)
	}

	return buffer.Bytes()
}

// IsAction checks to see if the event is a PRIVMSG, and is an ACTION (/me)
func (e *Event) IsAction() bool {
	if len(e.Trailing) == 0 || e.Command != PRIVMSG {
		return false
	}

	if !strings.HasPrefix(e.Trailing, "\001ACTION") || !strings.HasSuffix(e.Trailing, "\001") {
		return false
	}

	return true
}

// StripAction strips the action encoding from a PRIVMSG ACTION (/me)
func (e *Event) StripAction() string {
	if !e.IsAction() || len(e.Trailing) < 9 {
		return e.Trailing
	}

	return e.Trailing[8 : len(e.Trailing)-1]
}

// String returns a string representation of this event
func (e *Event) String() string {
	return string(e.Bytes())
}

func indexByte(s string, c byte) int {
	return strings.IndexByte(s, c)
}

// contains '*', even though this isn't RFC compliant, it's commonly used
var validChannelPrefixes = [...]string{"&", "#", "+", "!", "*"}

// IsValidChannel checks if channel is an RFC complaint channel or not
func IsValidChannel(channel string) bool {
	if len(channel) < 1 || len(channel) > 50 {
		return false
	}

	var validprefix bool
	for i := 0; i < len(validChannelPrefixes); i++ {
		if string(channel[0]) == validChannelPrefixes[i] {
			validprefix = true
			break
		}
	}

	if !validprefix {
		return false
	}

	if strings.Contains(channel, " ") || strings.Contains(channel, ",") {
		return false
	}

	return true
}
<commit_msg>remove indexByte<commit_after>package girc

import (
	"bytes"
	"strings"
)

const (
	prefix     byte = 0x3A // prefix or last argument
	prefixUser byte = 0x21 // username
	prefixHost byte = 0x40 // hostname
	space      byte = 0x20 // separator

	maxLength = 510 // maximum length is 510 (2 for line endings)
)

func cutsetFunc(r rune) bool {
	// Characters to trim from prefixes/messages.
	return r == '\r' || r == '\n'
}

// Prefix represents the sender of an IRC event, see RFC1459 section 2.3.1
// <servername> | <nick> [ '!' <user> ] [ '@' <host> ]
type Prefix struct {
	Name string // Nick or servername
	User string // Username
	Host string // Hostname
}

// ParsePrefix takes a string and attempts to create a Prefix struct.
func ParsePrefix(raw string) (p *Prefix) {
	p = new(Prefix)

	user := strings.IndexByte(raw, prefixUser)
	host := strings.IndexByte(raw, prefixHost)

	switch {
	case user > 0 && host > user:
		p.Name = raw[:user]
		p.User = raw[user+1 : host]
		p.Host = raw[host+1:]
	case user > 0:
		p.Name = raw[:user]
		p.User = raw[user+1:]
	case host > 0:
		p.Name = raw[:host]
		p.Host = raw[host+1:]
	default:
		p.Name = raw

	}

	return p
}

// Len calculates the length of the string representation of prefix
func (p *Prefix) Len() (length int) {
	length = len(p.Name)
	if len(p.User) > 0 {
		length = 1 + length + len(p.User)
	}
	if len(p.Host) > 0 {
		length = 1 + length + len(p.Host)
	}

	return
}

// Bytes returns a []byte representation of prefix
func (p *Prefix) Bytes() []byte {
	buffer := new(bytes.Buffer)
	p.writeTo(buffer)

	return buffer.Bytes()
}

// String returns a string representation of prefix
func (p *Prefix) String() (s string) {
	s = p.Name
	if len(p.User) > 0 {
		s = s + string(prefixUser) + p.User
	}
	if len(p.Host) > 0 {
		s = s + string(prefixHost) + p.Host
	}

	return
}

// IsHostmask returns true if prefix looks like a user hostmask
func (p *Prefix) IsHostmask() bool {
	return len(p.User) > 0 && len(p.Host) > 0
}

// IsServer returns true if this prefix looks like a server name.
func (p *Prefix) IsServer() bool {
	return len(p.User) <= 0 && len(p.Host) <= 0 // && strings.IndexByte(p.Name, '.') > 0
}

// writeTo is an utility function to write the prefix to the bytes.Buffer in Event.String()
func (p *Prefix) writeTo(buffer *bytes.Buffer) {
	buffer.WriteString(p.Name)
	if len(p.User) > 0 {
		buffer.WriteByte(prefixUser)
		buffer.WriteString(p.User)
	}
	if len(p.Host) > 0 {
		buffer.WriteByte(prefixHost)
		buffer.WriteString(p.Host)
	}

	return
}

// Event represents an IRC protocol message, see RFC1459 section 2.3.1
//
//    <message>  :: [':' <prefix> <SPACE>] <command> <params> <crlf>
//    <prefix>   :: <servername> | <nick> ['!' <user>] ['@' <host>]
//    <command>  :: <letter>{<letter>} | <number> <number> <number>
//    <SPACE>    :: ' '{' '}
//    <params>   :: <SPACE> [':' <trailing> | <middle> <params>]
//    <middle>   :: <Any *non-empty* sequence of octets not including SPACE or NUL
//                   or CR or LF, the first of which may not be ':'>
//    <trailing> :: <Any, possibly empty, sequence of octets not including NUL or
//                   CR or LF>
//    <crlf>     :: CR LF
type Event struct {
	*Prefix                // The source of the event
	Command       string   // the IRC command, e.g. JOIN, PRIVMSG, KILL
	Params        []string // parameters to the command. Commonly nickname, channel, etc
	Trailing      string   // any trailing data. e.g. with a PRIVMSG, this is the message text
	EmptyTrailing bool     // if true, trailing prefix (:) will be added even if Event.Trailing is empty
	Sensitive     bool     // if the message is sensitive (e.g. and should not be logged)
}

// ParseEvent takes a string and attempts to create a Event struct.
// Returns nil if the Event is invalid.
func ParseEvent(raw string) (e *Event) {
	// ignore empty events
	if raw = strings.TrimFunc(raw, cutsetFunc); len(raw) < 2 {
		return nil
	}

	i, j := 0, 0
	e = new(Event)

	if raw[0] == prefix {
		// prefix ends with a space
		i = strings.IndexByte(raw, space)

		// prefix string must not be empty if the indicator is present
		if i < 2 {
			return nil
		}

		e.Prefix = ParsePrefix(raw[1:i])

		i++ // skip space at the end of the prefix
	}

	// find end of command
	j = i + strings.IndexByte(raw[i:], space)

	// extract command
	if j < i {
		e.Command = strings.ToUpper(raw[i:])
		return e
	}

	e.Command = strings.ToUpper(raw[i:j])
	j++ // skip space after command

	// find prefix for trailer
	i = strings.IndexByte(raw[j:], prefix)

	if i < 0 || raw[j+i-1] != space {
		// no trailing argument
		e.Params = strings.Split(raw[j:], string(space))
		return e
	}

	// compensate for index on substring
	i = i + j

	// check if we need to parse arguments
	if i > j {
		e.Params = strings.Split(raw[j:i-1], string(space))
	}

	e.Trailing = raw[i+1:]

	// we need to re-encode the trailing argument even if it was empty
	if len(e.Trailing) <= 0 {
		e.EmptyTrailing = true
	}

	return e

}

// Len calculates the length of the string representation of event
func (e *Event) Len() (length int) {
	if e.Prefix != nil {
		length = e.Prefix.Len() + 2 // include prefix and trailing space
	}

	length = length + len(e.Command)

	if len(e.Params) > 0 {
		length = length + len(e.Params)

		for i := 0; i < len(e.Params); i++ {
			length = length + len(e.Params[i])
		}
	}

	if len(e.Trailing) > 0 || e.EmptyTrailing {
		length = length + len(e.Trailing) + 2 // include prefix and space
	}

	return
}

// Bytes returns a []byte representation of event
//
// per RFC2812 section 2.3, messages should not exceed 512 characters
// in length. this method forces that limit by discarding any characters
// exceeding the length limit.
func (e *Event) Bytes() []byte {
	buffer := new(bytes.Buffer)

	// event prefix
	if e.Prefix != nil {
		buffer.WriteByte(prefix)
		e.Prefix.writeTo(buffer)
		buffer.WriteByte(space)
	}

	// command is required
	buffer.WriteString(e.Command)

	// space separated list of arguments
	if len(e.Params) > 0 {
		buffer.WriteByte(space)
		buffer.WriteString(strings.Join(e.Params, string(space)))
	}

	if len(e.Trailing) > 0 || e.EmptyTrailing {
		buffer.WriteByte(space)
		buffer.WriteByte(prefix)
		buffer.WriteString(e.Trailing)
	}

	// we need the limit the buffer length
	if buffer.Len() > (maxLength) {
		buffer.Truncate(maxLength)
	}

	return buffer.Bytes()
}

// IsAction checks to see if the event is a PRIVMSG, and is an ACTION (/me)
func (e *Event) IsAction() bool {
	if len(e.Trailing) == 0 || e.Command != PRIVMSG {
		return false
	}

	if !strings.HasPrefix(e.Trailing, "\001ACTION") || !strings.HasSuffix(e.Trailing, "\001") {
		return false
	}

	return true
}

// StripAction strips the action encoding from a PRIVMSG ACTION (/me)
func (e *Event) StripAction() string {
	if !e.IsAction() || len(e.Trailing) < 9 {
		return e.Trailing
	}

	return e.Trailing[8 : len(e.Trailing)-1]
}

// String returns a string representation of this event
func (e *Event) String() string {
	return string(e.Bytes())
}

// contains '*', even though this isn't RFC compliant, it's commonly used
var validChannelPrefixes = [...]string{"&", "#", "+", "!", "*"}

// IsValidChannel checks if channel is an RFC complaint channel or not
func IsValidChannel(channel string) bool {
	if len(channel) < 1 || len(channel) > 50 {
		return false
	}

	var validprefix bool
	for i := 0; i < len(validChannelPrefixes); i++ {
		if string(channel[0]) == validChannelPrefixes[i] {
			validprefix = true
			break
		}
	}

	if !validprefix {
		return false
	}

	if strings.Contains(channel, " ") || strings.Contains(channel, ",") {
		return false
	}

	return true
}
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"io/ioutil"
	"math/rand"
	"testing"

	"golang.org/x/sync/errgroup"

	"github.com/pachyderm/pachyderm/src/client"
	"github.com/pachyderm/pachyderm/src/client/pkg/require"
	"github.com/pachyderm/pachyderm/src/server/pkg/workload"
)

const (
	clients          = 12
	objectsPerClient = 500
	objectSize       = 1024 * 1024
)

func BenchmarkManyObjects(b *testing.B) {
	fmt.Println("running benchmark")
	b.Run("Put", func(b *testing.B) {
		for n := 0; n < b.N; n++ {
			var eg errgroup.Group
			for i := 0; i < clients; i++ {
				c := getPachClientInCluster(b)
				rand := rand.New(rand.NewSource(int64(i)))
				eg.Go(func() error {
					for j := 0; j < objectsPerClient; j++ {
						r := workload.NewReader(rand, objectSize)
						if n == 0 {
							if _, err := c.PutObject(r, fmt.Sprintf("%d.%d", i, j)); err != nil {
								return err
							}
						} else {
							if _, err := c.PutObject(r); err != nil {
								return err
							}
						}
					}
					return nil
				})
			}
			b.SetBytes(clients * objectsPerClient * objectSize)
			require.NoError(b, eg.Wait())
		}
	})
	b.Run("Get", func(b *testing.B) {
		for n := 0; n < b.N; n++ {
			var eg errgroup.Group
			for i := 0; i < clients; i++ {
				c := getPachClientInCluster(b)
				eg.Go(func() error {
					for j := 0; j < objectsPerClient; j++ {
						err := c.GetTag(fmt.Sprintf("%d.%d", i, j), ioutil.Discard)
						if err != nil {
							return err
						}
					}
					return nil
				})
			}
			b.SetBytes(clients * objectsPerClient * objectSize)
			require.NoError(b, eg.Wait())
		}
	})
	b.Run("CacheGet", func(b *testing.B) {
		for n := 0; n < b.N; n++ {
			var eg errgroup.Group
			for i := 0; i < clients; i++ {
				c := getPachClientInCluster(b)
				eg.Go(func() error {
					for j := 0; j < objectsPerClient; j++ {
						err := c.GetTag(fmt.Sprintf("%d.%d", i, j), ioutil.Discard)
						if err != nil {
							return err
						}
					}
					return nil
				})
			}
			b.SetBytes(clients * objectsPerClient * objectSize)
			require.NoError(b, eg.Wait())
		}
	})
}

func getPachClientInCluster(t testing.TB) *client.APIClient {
	client, err := client.NewInCluster()
	require.NoError(t, err)
	return client
}
<commit_msg>Fix loop variable capture bug.<commit_after>package server

import (
	"fmt"
	"io/ioutil"
	"math/rand"
	"testing"

	"golang.org/x/sync/errgroup"

	"github.com/pachyderm/pachyderm/src/client"
	"github.com/pachyderm/pachyderm/src/client/pkg/require"
	"github.com/pachyderm/pachyderm/src/server/pkg/workload"
)

const (
	clients          = 8
	objectsPerClient = 50
	objectSize       = 15 * 1024 * 1024
)

func BenchmarkManyObjects(b *testing.B) {
	b.Run("Put", func(b *testing.B) {
		for n := 0; n < b.N; n++ {
			var eg errgroup.Group
			for i := 0; i < clients; i++ {
				i := i
				c := getPachClientInCluster(b)
				rand := rand.New(rand.NewSource(int64(i)))
				eg.Go(func() error {
					for j := 0; j < objectsPerClient; j++ {
						r := workload.NewReader(rand, objectSize)
						if n == 0 {
							if _, err := c.PutObject(r, fmt.Sprintf("%d.%d", i, j)); err != nil {
								return err
							}
						} else {
							if _, err := c.PutObject(r); err != nil {
								return err
							}
						}
					}
					return nil
				})
			}
			b.SetBytes(clients * objectsPerClient * objectSize)
			require.NoError(b, eg.Wait())
		}
	})
	b.Run("Get", func(b *testing.B) {
		for n := 0; n < b.N; n++ {
			var eg errgroup.Group
			for i := 0; i < clients; i++ {
				i := i
				c := getPachClientInCluster(b)
				eg.Go(func() error {
					for j := 0; j < objectsPerClient; j++ {
						err := c.GetTag(fmt.Sprintf("%d.%d", i, j), ioutil.Discard)
						if err != nil {
							return err
						}
					}
					return nil
				})
			}
			b.SetBytes(clients * objectsPerClient * objectSize)
			require.NoError(b, eg.Wait())
		}
	})
	b.Run("CacheGet", func(b *testing.B) {
		for n := 0; n < b.N; n++ {
			var eg errgroup.Group
			for i := 0; i < clients; i++ {
				i := i
				c := getPachClientInCluster(b)
				eg.Go(func() error {
					for j := 0; j < objectsPerClient; j++ {
						err := c.GetTag(fmt.Sprintf("%d.%d", i, j), ioutil.Discard)
						if err != nil {
							return err
						}
					}
					return nil
				})
			}
			b.SetBytes(clients * objectsPerClient * objectSize)
			require.NoError(b, eg.Wait())
		}
	})
}

func getPachClientInCluster(t testing.TB) *client.APIClient {
	client, err := client.NewInCluster()
	require.NoError(t, err)
	return client
}
<|endoftext|>


<commit_before>package restTest

import (
	"encoding/json"
	"fmt"
	"math"
	"net/http"
	"sync"
)

const (
	// REST API url
	urlTemplate = "http://resttest.bench.co/transactions/%d.json"
	// Maximum number of transaction per page
	transactionsPerPage = 10
	// Maximum number of idle http connections
	maxIdleConnections = 100
	// default number of concurrent go routines to fetch pages
	DefaultConcurrency = 20
)

var (
	// number of concurrent go routines that fetch pages
	Concurrency = DefaultConcurrency
)

type Page struct {
	TotalCount   int
	Page         int
	Transactions []Transaction
}

func init() {
	http.DefaultTransport.(*http.Transport).MaxIdleConnsPerHost = maxIdleConnections
}

// Returns the page's fields in a string formatted as JSON
func (p Page) String() string {
	return fmt.Sprintf("{\n\tTotal Count: %d,\n\tPage: %d,\n\tTransactions: %v\n}",
		p.TotalCount, p.Page, p.Transactions)
}

// Fetches the page from the restTest API server and decodes it into Page
// Returns HTTPError if response status is not 200
func FetchPage(pageNumber int) (*Page, error) {
	return fetchPage(pageURL(pageNumber, urlTemplate))
}

// Returns page url from base url template and page number
// urlTemplate must specify where the pageNumber goes with %d.
func pageURL(n int, urlTemplate string) string {
	return fmt.Sprintf(urlTemplate, n)
}

// Calls HTTP GET to the passed url and decodes the response body into Page struct.
// returns HTTPError if response status is not 200
func fetchPage(url string) (*Page, error) {
	res, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	if res.StatusCode != http.StatusOK {
		return nil, HTTPError{res.Status, res.StatusCode}
	}

	page := new(Page)
	err = json.NewDecoder(res.Body).Decode(page)
	if err != nil {
		return nil, err
	}

	return page, nil
}

// Fetches all pages from the restTest API and
// puts the slice of transactions (max transactions per slice = 10)
// from each page over a channel. It closes the channel once all
// transactions are put to the channel.
//
// Panics if encounters an error
func FetchAllTransactions() chan []Transaction {
	ch := make(chan []Transaction)
	go fetchAllTransactions(ch, urlTemplate, Concurrency)
	return ch
}

// Fetches the first page to get total number of pages to fetch.
// Then launches a go routine to fetch each page. After the last
// transaction is put in the channel, it closes the channel.
//
// It only launches as many go routines as the passed concurrency flag
func fetchAllTransactions(ch chan []Transaction, urlTemplate string, concurrency int) {
	// Fetch the first page
	p, err := fetchPage(pageURL(1, urlTemplate))
	if err != nil {
		panic(err)
	}

	// Put the first page's transactions in the channel
	ch <- p.Transactions

	// Calculate the number of remaining pages to fetch
	pageCount := int(
		math.Floor(
			float64(
				(p.TotalCount-1)/transactionsPerPage,
			),
		) + 1,
	)

	// Close the channel if there are no more pages
	if pageCount < 2 {
		close(ch)
		return
	}

	// Initialize WaitGroup
	var wg sync.WaitGroup
	wg.Add(pageCount - 1)

	// If an error occurs in a child go routine,
	// send it over the channel and panic from the parent
	done := make(chan error, 1)
	// Semaphore to limit the number of go routines
	sem := make(chan bool, concurrency)

	for i := 2; i <= pageCount; i++ {
		sem <- true // increment semaphore
		go func(i int) {
			defer wg.Done()
			defer func() { <-sem }()

			// Fetch page
			p, err := fetchPage(pageURL(i, urlTemplate))
			if err != nil {
				done <- err
				return
			}

			// Put page's transactions in channel
			ch <- p.Transactions
		}(i)
	}

	// Wait for all go routines to finish then close the done channel
	go func() {
		wg.Wait()
		close(done)
	}()

	// Waits until an error or channel close.
	// Panics if error. Otherwise closes the transactions channel
	err = <-done
	if err != nil {
		panic(err)
	}
	close(ch)
}
<commit_msg>punctuation<commit_after>package restTest

import (
	"encoding/json"
	"fmt"
	"math"
	"net/http"
	"sync"
)

const (
	// REST API url
	urlTemplate = "http://resttest.bench.co/transactions/%d.json"
	// Maximum number of transaction per page
	transactionsPerPage = 10
	// Maximum number of idle http connections
	maxIdleConnections = 100
	// default number of concurrent go routines to fetch pages
	DefaultConcurrency = 20
)

var (
	// number of concurrent go routines that fetch pages
	Concurrency = DefaultConcurrency
)

type Page struct {
	TotalCount   int
	Page         int
	Transactions []Transaction
}

func init() {
	http.DefaultTransport.(*http.Transport).MaxIdleConnsPerHost = maxIdleConnections
}

// Returns the page's fields in a string formatted as JSON
func (p Page) String() string {
	return fmt.Sprintf("{\n\tTotal Count: %d,\n\tPage: %d,\n\tTransactions: %v\n}",
		p.TotalCount, p.Page, p.Transactions)
}

// Fetches the page from the restTest API server and decodes it into Page.
// Returns HTTPError if response status is not 200
func FetchPage(pageNumber int) (*Page, error) {
	return fetchPage(pageURL(pageNumber, urlTemplate))
}

// Returns page url from base url template and page number
// urlTemplate must specify where the pageNumber goes with %d.
func pageURL(n int, urlTemplate string) string {
	return fmt.Sprintf(urlTemplate, n)
}

// Calls HTTP GET to the passed url and decodes the response body into Page struct.
// returns HTTPError if response status is not 200
func fetchPage(url string) (*Page, error) {
	res, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	if res.StatusCode != http.StatusOK {
		return nil, HTTPError{res.Status, res.StatusCode}
	}

	page := new(Page)
	err = json.NewDecoder(res.Body).Decode(page)
	if err != nil {
		return nil, err
	}

	return page, nil
}

// Fetches all pages from the restTest API and
// puts the slice of transactions (max transactions per slice = 10)
// from each page over a channel. It closes the channel once all
// transactions are put to the channel.
//
// Panics if encounters an error
func FetchAllTransactions() chan []Transaction {
	ch := make(chan []Transaction)
	go fetchAllTransactions(ch, urlTemplate, Concurrency)
	return ch
}

// Fetches the first page to get total number of pages to fetch.
// Then launches a go routine to fetch each page. After the last
// transaction is put in the channel, it closes the channel.
//
// It only launches as many go routines as the passed concurrency flag
func fetchAllTransactions(ch chan []Transaction, urlTemplate string, concurrency int) {
	// Fetch the first page
	p, err := fetchPage(pageURL(1, urlTemplate))
	if err != nil {
		panic(err)
	}

	// Put the first page's transactions in the channel
	ch <- p.Transactions

	// Calculate the number of remaining pages to fetch
	pageCount := int(
		math.Floor(
			float64(
				(p.TotalCount-1)/transactionsPerPage,
			),
		) + 1,
	)

	// Close the channel if there are no more pages
	if pageCount < 2 {
		close(ch)
		return
	}

	// Initialize WaitGroup
	var wg sync.WaitGroup
	wg.Add(pageCount - 1)

	// If an error occurs in a child go routine,
	// send it over the channel and panic from the parent
	done := make(chan error, 1)
	// Semaphore to limit the number of go routines
	sem := make(chan bool, concurrency)

	for i := 2; i <= pageCount; i++ {
		sem <- true // increment semaphore
		go func(i int) {
			defer wg.Done()
			defer func() { <-sem }()

			// Fetch page
			p, err := fetchPage(pageURL(i, urlTemplate))
			if err != nil {
				done <- err
				return
			}

			// Put page's transactions in channel
			ch <- p.Transactions
		}(i)
	}

	// Wait for all go routines to finish then close the done channel
	go func() {
		wg.Wait()
		close(done)
	}()

	// Waits until an error or channel close.
	// Panics if error. Otherwise closes the transactions channel
	err = <-done
	if err != nil {
		panic(err)
	}
	close(ch)
}
<|endoftext|>


<commit_before>package rcon

import (
	"bytes"
	"testing"
)

func TestNewPacket(t *testing.T) {
	p := NewPacket(Auth, "password")

	if p.Header.Size != 22 {
		t.Error("Expected packet size 22, got ", p.Header.Size)
	}
	if p.Header.Type != Auth {
		t.Error("Expected packet type Auth(3), got ", p.Header.Type)
	}
	if p.Body != "password" {
		t.Error("Expected packet body \"password\", got ", p.Body)
	}
}

func TestPayload(t *testing.T) {
	p := NewPacket(Auth, "password")
	payload, _ := p.Payload()

	size := payload[0:4]
	typ := payload[8:12]
	body := payload[12 : len(payload)-2]
	padding := payload[len(payload)-2:]

	if !bytes.Equal(size, []byte{22, 0, 0, 0}) {
		t.Error("Expected payload [0:4] to be bytes [22 0 0 0], got ", payload[0:4])
	}
	if !bytes.Equal(typ, []byte{3, 0, 0, 0}) {
		t.Error("Expected payload [8:12] to be bytes [3 0 0 0], got ", typ)
	}
	if !bytes.Equal(body, []byte("password")) {
		t.Error("Expected payload body to be bytes \"password\", got ", body)
	}
	if !bytes.Equal(padding, []byte("\x00\x00")) {
		t.Error("Expected two bytes of null padding at end of payload, got ", padding)
	}
}
<commit_msg>Fix tests<commit_after>package rcon

import (
	"bytes"
	"testing"
)

func TestNewPacket(t *testing.T) {
	p := NewPacket(Auth, "password")

	if p.Size != 18 {
		t.Error("Expected packet size 18, got ", p.Size)
	}
	if p.Type != Auth {
		t.Error("Expected packet type Auth(3), got ", p.Type)
	}
	if p.Body != "password" {
		t.Error("Expected packet body \"password\", got ", p.Body)
	}
}

func TestPayload(t *testing.T) {
	p := NewPacket(Auth, "password")
	payload, _ := p.Payload()

	size := payload[0:4]
	typ := payload[8:12]
	body := payload[12 : len(payload)-2]
	padding := payload[len(payload)-2:]

	if !bytes.Equal(size, []byte{18, 0, 0, 0}) {
		t.Error("Expected payload [0:4] to be bytes [18 0 0 0], got ", payload[0:4])
	}
	if !bytes.Equal(typ, []byte{3, 0, 0, 0}) {
		t.Error("Expected payload [8:12] to be bytes [3 0 0 0], got ", typ)
	}
	if !bytes.Equal(body, []byte("password")) {
		t.Error("Expected payload body to be bytes \"password\", got ", body)
	}
	if !bytes.Equal(padding, []byte("\x00\x00")) {
		t.Error("Expected two bytes of null padding at end of payload, got ", padding)
	}
}
<|endoftext|>