<commit_before>package vm

import (
	"fmt"
	"strconv"
	"strings"
	"unicode"
)

var (
	stringClass *RString
)

// RString is the built in string class
type RString struct {
	*BaseClass
}

// StringObject represents string instances
// String object holds and manipulates a sequence of characters.
// String objects may be created using as string literals.
// Double or single quotations can be used for representation.
//
// ```ruby
// a = "Three"
// b = 'zero'
// c = '漢'
// d = 'Tiếng Việt'
// e = "😏"
// ```
//
// **Note:**
// Currently, manipulations are based upon Golang's Unicode manipulations.
//
// - Currently, UTF-8 encoding is assumed based upon Golang's string manipulation, but the encoding is not actually specified(TBD).
// - `String.new` is not supported.
type StringObject struct {
	Class *RString
	Value string
}

func (s *StringObject) toString() string {
	return "\"" + s.Value + "\""
}

func (s *StringObject) toJSON() string {
	return "\"" + s.Value + "\""
}

func (s *StringObject) returnClass() Class {
	if s.Class == nil {
		panic(fmt.Sprintf("String %s doesn't have class.", s.toString()))
	}

	return s.Class
}

func (s *StringObject) equal(e *StringObject) bool {
	return s.Value == e.Value
}

func initStringObject(value string) *StringObject {
	return &StringObject{Value: value, Class: stringClass}
}

var builtinStringInstanceMethods = []*BuiltInMethodObject{
	{
		// Returns the concatenation of self and another String
		//
		// ```Ruby
		// "first" + "-second" # => "first-second"
		// ```
		//
		// @return [String]
		Name: "+",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				leftValue := receiver.(*StringObject).Value
				right, ok := args[0].(*StringObject)

				if !ok {
					return wrongTypeError(stringClass)
				}

				rightValue := right.Value
				return &StringObject{Value: leftValue + rightValue, Class: stringClass}
			}
		},
	},
	{
		// Returns self multiplying another Integer
		//
		// ```Ruby
		// "string " * 2 # => "string string string "
		// ```
		//
		// @return [String]
		Name: "*",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				leftValue := receiver.(*StringObject).Value
				right, ok := args[0].(*IntegerObject)

				if !ok {
					return wrongTypeError(stringClass)
				}

				if right.Value < 0 {
					return newError("Second argument must be greater than or equal to 0 String#*")
				}

				var result string

				for i := 0; i < right.Value; i++ {
					result += leftValue
				}

				return &StringObject{Value: result, Class: stringClass}
			}
		},
	},
	{
		// Returns a Boolean if first string greater than second string
		//
		// ```Ruby
		// "a" < "b" # => true
		// ```
		//
		// @return [Boolean]
		Name: ">",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				leftValue := receiver.(*StringObject).Value
				right, ok := args[0].(*StringObject)

				if !ok {
					return wrongTypeError(stringClass)
				}

				rightValue := right.Value

				if leftValue > rightValue {
					return TRUE
				}

				return FALSE
			}
		},
	},
	{
		// Returns a Boolean if first string less than second string
		//
		// ```Ruby
		// "a" < "b" # => true
		// ```
		//
		// @return [Boolean]
		Name: "<",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				leftValue := receiver.(*StringObject).Value
				right, ok := args[0].(*StringObject)

				if !ok {
					return wrongTypeError(stringClass)
				}

				rightValue := right.Value

				if leftValue < rightValue {
					return TRUE
				}

				return FALSE
			}
		},
	},
	{
		// Returns a Boolean of compared two strings
		//
		// ```Ruby
		// "first" == "second" # => false
		// "two" == "two" # => true
		// ```
		//
		// @return [Boolean]
		Name: "==",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				leftValue := receiver.(*StringObject).Value
				right, ok := args[0].(*StringObject)

				if !ok {
					return wrongTypeError(stringClass)
				}

				rightValue := right.Value

				if leftValue == rightValue {
					return TRUE
				}

				return FALSE
			}
		},
	},
	{
		// Returns a Integer. If first string is less than second string returns -1, if equal to returns 0, if greater returns 1
		//
		//
		// ```Ruby
		// "abc" <=> "abcd" # => -1
		// "abc" <=> "abc" # => 0
		// "abcd" <=> "abc" # => 1
		// ```
		//
		// @return [Integer]
		Name: "<=>",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				leftValue := receiver.(*StringObject).Value
				right, ok := args[0].(*StringObject)

				if !ok {
					return wrongTypeError(stringClass)
				}

				rightValue := right.Value

				if leftValue < rightValue {
					return initIntegerObject(-1)
				}
				if leftValue > rightValue {
					return initIntegerObject(1)
				}

				return initIntegerObject(0)
			}
		},
	},
	{
		// Returns a Boolean of compared two strings
		//
		// ```Ruby
		// "first" != "second" # => true
		// "two" != "two" # => false
		// ```
		//
		// @return [Boolean]
		Name: "!=",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				leftValue := receiver.(*StringObject).Value
				right, ok := args[0].(*StringObject)

				if !ok {
					return wrongTypeError(stringClass)
				}

				rightValue := right.Value

				if leftValue != rightValue {
					return TRUE
				}

				return FALSE
			}
		},
	},
	{
		// Return a new String with the first character converted to uppercase but the rest of string converted to lowercase.
		//
		// ```Ruby
		// "test".capitalize # => "Test"
		// "tEST".capitalize # => "Test"
		// ```
		//
		// @return [String]
		Name: "capitalize",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := []byte(receiver.(*StringObject).Value)
				start := string(str[0])
				rest := string(str[1:])
				result := strings.ToUpper(start) + strings.ToLower(rest)

				return initStringObject(result)
			}
		},
	},
	{
		// Returns a new String with all characters is upcase
		//
		// ```Ruby
		// "very big".upcase # => "VERY BIG"
		// ```
		//
		// @return [String]
		Name: "upcase",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := receiver.(*StringObject).Value

				return initStringObject(strings.ToUpper(str))
			}
		},
	},
	{
		// Returns a new String with all characters is lowercase
		//
		// ```Ruby
		// "erROR".downcase # => "error"
		// ```
		//
		// @return [String]
		Name: "downcase",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := receiver.(*StringObject).Value

				return initStringObject(strings.ToLower(str))
			}
		},
	},
	{
		// Returns the character length of self
		// **Note:** the length is currently byte-based, instead of charcode-based.
		//
		// ```Ruby
		// "zero".size # => 4
		// "".size # => 0
		// ```
		//
		// @return [Integer]
		Name: "size",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := receiver.(*StringObject).Value

				return initIntegerObject(len(str))
			}
		},
	},
	{
		// Returns the character length of self
		// **Note:** the length is currently byte-based, instead of charcode-based.
		//
		// ```Ruby
		// "zero".size # => 4
		// "".size # => 0
		// ```
		//
		// @return [Integer]
		Name: "length",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := receiver.(*StringObject).Value

				return initIntegerObject(len(str))
			}
		},
	},
	{
		// Returns a new String with reverse order of self
		// **Note:** the length is currently byte-based, instead of charcode-based.
		//
		// ```Ruby
		// "reverse".reverse # => "esrever"
		// ```
		//
		// @return [String]
		Name: "reverse",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := receiver.(*StringObject).Value
				var revert string
				for i := len(str) - 1; i >= 0; i-- {
					revert += string(str[i])
				}

				return initStringObject(revert)
			}
		},
	},
	{
		// Returns a new String with self value
		//
		// ```Ruby
		// "string".to_s # => "string"
		// ```
		//
		// @return [String]
		Name: "to_s",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := receiver.(*StringObject).Value

				return initStringObject(str)
			}
		},
	},
	{
		// Returns the result of converting self to Integer
		//
		// ```Ruby
		// "123".to_i # => 123
		// "3d print".to_i # => 3
		// "some text".to_i # => 0
		// ```
		//
		// @return [Integer]
		Name: "to_i",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := receiver.(*StringObject).Value
				parsedStr, err := strconv.ParseInt(str, 10, 0)

				if err == nil {
					return initIntegerObject(int(parsedStr))
				}

				var digits string
				for _, char := range str {
					if unicode.IsDigit(char) {
						digits += string(char)
					} else {
						break
					}
				}

				if len(digits) > 0 {
					parsedStr, _ = strconv.ParseInt(digits, 10, 0)
					return initIntegerObject(int(parsedStr))
				}

				return initIntegerObject(0)
			}
		},
	},
	{
		// Checks if the specified string is included in the receiver
		//
		// ```Ruby
		// "Hello\nWorld".include("\n") # => true
		// ```
		//
		// @return [Bool]
		Name: "include",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				rcv := receiver.(*StringObject).Value
				arg := args[0].(*StringObject).Value

				if strings.Contains(rcv, arg) {
					return TRUE
				}

				return FALSE
			}
		},
	},
}

func initStringClass() {
	bc := &BaseClass{Name: "String", Methods: newEnvironment(), ClassMethods: newEnvironment(), Class: classClass, pseudoSuperClass: objectClass, superClass: objectClass}
	sc := &RString{BaseClass: bc}
	sc.setBuiltInMethods(builtinStringInstanceMethods, false)
	stringClass = sc
}
<commit_msg>Fix API document for String object<commit_after>package vm

import (
	"fmt"
	"strconv"
	"strings"
	"unicode"
)

var (
	stringClass *RString
)

// RString is the built in string class
type RString struct {
	*BaseClass
}

// StringObject represents string instances
// String object holds and manipulates a sequence of characters.
// String objects may be created using as string literals.
// Double or single quotations can be used for representation.
//
// ```ruby
// a = "Three"
// b = 'zero'
// c = '漢'
// d = 'Tiếng Việt'
// e = "😏"
// ```
//
// **Note:**
//
// - Currently, manipulations are based upon Golang's Unicode manipulations.
// - Currently, UTF-8 encoding is assumed based upon Golang's string manipulation, but the encoding is not actually specified(TBD).
// - `String.new` is not supported.
type StringObject struct {
	Class *RString
	Value string
}

func (s *StringObject) toString() string {
	return "\"" + s.Value + "\""
}

func (s *StringObject) toJSON() string {
	return "\"" + s.Value + "\""
}

func (s *StringObject) returnClass() Class {
	if s.Class == nil {
		panic(fmt.Sprintf("String %s doesn't have class.", s.toString()))
	}

	return s.Class
}

func (s *StringObject) equal(e *StringObject) bool {
	return s.Value == e.Value
}

func initStringObject(value string) *StringObject {
	return &StringObject{Value: value, Class: stringClass}
}

var builtinStringInstanceMethods = []*BuiltInMethodObject{
	{
		// Returns the concatenation of self and another String
		//
		// ```Ruby
		// "first" + "-second" # => "first-second"
		// ```
		//
		// @return [String]
		Name: "+",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				leftValue := receiver.(*StringObject).Value
				right, ok := args[0].(*StringObject)

				if !ok {
					return wrongTypeError(stringClass)
				}

				rightValue := right.Value
				return &StringObject{Value: leftValue + rightValue, Class: stringClass}
			}
		},
	},
	{
		// Returns self multiplying another Integer
		//
		// ```Ruby
		// "string " * 2 # => "string string string "
		// ```
		//
		// @return [String]
		Name: "*",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				leftValue := receiver.(*StringObject).Value
				right, ok := args[0].(*IntegerObject)

				if !ok {
					return wrongTypeError(stringClass)
				}

				if right.Value < 0 {
					return newError("Second argument must be greater than or equal to 0 String#*")
				}

				var result string

				for i := 0; i < right.Value; i++ {
					result += leftValue
				}

				return &StringObject{Value: result, Class: stringClass}
			}
		},
	},
	{
		// Returns a Boolean if first string greater than second string
		//
		// ```Ruby
		// "a" < "b" # => true
		// ```
		//
		// @return [Boolean]
		Name: ">",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				leftValue := receiver.(*StringObject).Value
				right, ok := args[0].(*StringObject)

				if !ok {
					return wrongTypeError(stringClass)
				}

				rightValue := right.Value

				if leftValue > rightValue {
					return TRUE
				}

				return FALSE
			}
		},
	},
	{
		// Returns a Boolean if first string less than second string
		//
		// ```Ruby
		// "a" < "b" # => true
		// ```
		//
		// @return [Boolean]
		Name: "<",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				leftValue := receiver.(*StringObject).Value
				right, ok := args[0].(*StringObject)

				if !ok {
					return wrongTypeError(stringClass)
				}

				rightValue := right.Value

				if leftValue < rightValue {
					return TRUE
				}

				return FALSE
			}
		},
	},
	{
		// Returns a Boolean of compared two strings
		//
		// ```Ruby
		// "first" == "second" # => false
		// "two" == "two" # => true
		// ```
		//
		// @return [Boolean]
		Name: "==",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				leftValue := receiver.(*StringObject).Value
				right, ok := args[0].(*StringObject)

				if !ok {
					return wrongTypeError(stringClass)
				}

				rightValue := right.Value

				if leftValue == rightValue {
					return TRUE
				}

				return FALSE
			}
		},
	},
	{
		// Returns a Integer. If first string is less than second string returns -1, if equal to returns 0, if greater returns 1
		//
		//
		// ```Ruby
		// "abc" <=> "abcd" # => -1
		// "abc" <=> "abc" # => 0
		// "abcd" <=> "abc" # => 1
		// ```
		//
		// @return [Integer]
		Name: "<=>",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				leftValue := receiver.(*StringObject).Value
				right, ok := args[0].(*StringObject)

				if !ok {
					return wrongTypeError(stringClass)
				}

				rightValue := right.Value

				if leftValue < rightValue {
					return initIntegerObject(-1)
				}
				if leftValue > rightValue {
					return initIntegerObject(1)
				}

				return initIntegerObject(0)
			}
		},
	},
	{
		// Returns a Boolean of compared two strings
		//
		// ```Ruby
		// "first" != "second" # => true
		// "two" != "two" # => false
		// ```
		//
		// @return [Boolean]
		Name: "!=",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				leftValue := receiver.(*StringObject).Value
				right, ok := args[0].(*StringObject)

				if !ok {
					return wrongTypeError(stringClass)
				}

				rightValue := right.Value

				if leftValue != rightValue {
					return TRUE
				}

				return FALSE
			}
		},
	},
	{
		// Return a new String with the first character converted to uppercase but the rest of string converted to lowercase.
		//
		// ```Ruby
		// "test".capitalize # => "Test"
		// "tEST".capitalize # => "Test"
		// ```
		//
		// @return [String]
		Name: "capitalize",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := []byte(receiver.(*StringObject).Value)
				start := string(str[0])
				rest := string(str[1:])
				result := strings.ToUpper(start) + strings.ToLower(rest)

				return initStringObject(result)
			}
		},
	},
	{
		// Returns a new String with all characters is upcase
		//
		// ```Ruby
		// "very big".upcase # => "VERY BIG"
		// ```
		//
		// @return [String]
		Name: "upcase",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := receiver.(*StringObject).Value

				return initStringObject(strings.ToUpper(str))
			}
		},
	},
	{
		// Returns a new String with all characters is lowercase
		//
		// ```Ruby
		// "erROR".downcase # => "error"
		// ```
		//
		// @return [String]
		Name: "downcase",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := receiver.(*StringObject).Value

				return initStringObject(strings.ToLower(str))
			}
		},
	},
	{
		// Returns the character length of self
		// **Note:** the length is currently byte-based, instead of charcode-based.
		//
		// ```Ruby
		// "zero".size # => 4
		// "".size # => 0
		// ```
		//
		// @return [Integer]
		Name: "size",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := receiver.(*StringObject).Value

				return initIntegerObject(len(str))
			}
		},
	},
	{
		// Returns the character length of self
		// **Note:** the length is currently byte-based, instead of charcode-based.
		//
		// ```Ruby
		// "zero".size # => 4
		// "".size # => 0
		// ```
		//
		// @return [Integer]
		Name: "length",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := receiver.(*StringObject).Value

				return initIntegerObject(len(str))
			}
		},
	},
	{
		// Returns a new String with reverse order of self
		// **Note:** the length is currently byte-based, instead of charcode-based.
		//
		// ```Ruby
		// "reverse".reverse # => "esrever"
		// ```
		//
		// @return [String]
		Name: "reverse",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := receiver.(*StringObject).Value
				var revert string
				for i := len(str) - 1; i >= 0; i-- {
					revert += string(str[i])
				}

				return initStringObject(revert)
			}
		},
	},
	{
		// Returns a new String with self value
		//
		// ```Ruby
		// "string".to_s # => "string"
		// ```
		//
		// @return [String]
		Name: "to_s",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := receiver.(*StringObject).Value

				return initStringObject(str)
			}
		},
	},
	{
		// Returns the result of converting self to Integer
		//
		// ```Ruby
		// "123".to_i # => 123
		// "3d print".to_i # => 3
		// "some text".to_i # => 0
		// ```
		//
		// @return [Integer]
		Name: "to_i",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := receiver.(*StringObject).Value
				parsedStr, err := strconv.ParseInt(str, 10, 0)

				if err == nil {
					return initIntegerObject(int(parsedStr))
				}

				var digits string
				for _, char := range str {
					if unicode.IsDigit(char) {
						digits += string(char)
					} else {
						break
					}
				}

				if len(digits) > 0 {
					parsedStr, _ = strconv.ParseInt(digits, 10, 0)
					return initIntegerObject(int(parsedStr))
				}

				return initIntegerObject(0)
			}
		},
	},
	{
		// Checks if the specified string is included in the receiver
		//
		// ```Ruby
		// "Hello\nWorld".include("\n") # => true
		// ```
		//
		// @return [Bool]
		Name: "include",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				rcv := receiver.(*StringObject).Value
				arg := args[0].(*StringObject).Value

				if strings.Contains(rcv, arg) {
					return TRUE
				}

				return FALSE
			}
		},
	},
}

func initStringClass() {
	bc := &BaseClass{Name: "String", Methods: newEnvironment(), ClassMethods: newEnvironment(), Class: classClass, pseudoSuperClass: objectClass, superClass: objectClass}
	sc := &RString{BaseClass: bc}
	sc.setBuiltInMethods(builtinStringInstanceMethods, false)
	stringClass = sc
}
<|endoftext|>


<commit_before>package parser

// line parsers are dispatch calls that parse a single unit of text into a
// Node object which contains the whole statement. Dockerfiles have varied
// (but not usually unique, see ONBUILD for a unique example) parsing rules
// per-command, and these unify the processing in a way that makes it
// manageable.

import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"unicode"
	"unicode/utf8"
)

var (
	errDockerfileNotStringArray = errors.New("When using JSON array syntax, arrays must be comprised of strings only.")
)

// ignore the current argument. This will still leave a command parsed, but
// will not incorporate the arguments into the ast.
func parseIgnore(rest string, d *Directive) (*Node, map[string]bool, error) {
	return &Node{}, nil, nil
}

// used for onbuild. Could potentially be used for anything that represents a
// statement with sub-statements.
//
// ONBUILD RUN foo bar -> (onbuild (run foo bar))
//
func parseSubCommand(rest string, d *Directive) (*Node, map[string]bool, error) {
	if rest == "" {
		return nil, nil, nil
	}

	_, child, err := ParseLine(rest, d)
	if err != nil {
		return nil, nil, err
	}

	return &Node{Children: []*Node{child}}, nil, nil
}

// helper to parse words (i.e space delimited or quoted strings) in a statement.
// The quotes are preserved as part of this function and they are stripped later
// as part of processWords().
func parseWords(rest string, d *Directive) []string {
	const (
		inSpaces = iota // looking for start of a word
		inWord
		inQuote
	)

	words := []string{}
	phase := inSpaces
	word := ""
	quote := '\000'
	blankOK := false
	var ch rune
	var chWidth int

	for pos := 0; pos <= len(rest); pos += chWidth {
		if pos != len(rest) {
			ch, chWidth = utf8.DecodeRuneInString(rest[pos:])
		}

		if phase == inSpaces { // Looking for start of word
			if pos == len(rest) { // end of input
				break
			}
			if unicode.IsSpace(ch) { // skip spaces
				continue
			}
			phase = inWord // found it, fall through
		}
		if (phase == inWord || phase == inQuote) && (pos == len(rest)) {
			if blankOK || len(word) > 0 {
				words = append(words, word)
			}
			break
		}
		if phase == inWord {
			if unicode.IsSpace(ch) {
				phase = inSpaces
				if blankOK || len(word) > 0 {
					words = append(words, word)
				}
				word = ""
				blankOK = false
				continue
			}
			if ch == '\'' || ch == '"' {
				quote = ch
				blankOK = true
				phase = inQuote
			}
			if ch == d.EscapeToken {
				if pos+chWidth == len(rest) {
					continue // just skip an escape token at end of line
				}
				// If we're not quoted and we see an escape token, then always just
				// add the escape token plus the char to the word, even if the char
				// is a quote.
				word += string(ch)
				pos += chWidth
				ch, chWidth = utf8.DecodeRuneInString(rest[pos:])
			}
			word += string(ch)
			continue
		}
		if phase == inQuote {
			if ch == quote {
				phase = inWord
			}
			// The escape token is special except for ' quotes - can't escape anything for '
			if ch == d.EscapeToken && quote != '\'' {
				if pos+chWidth == len(rest) {
					phase = inWord
					continue // just skip the escape token at end
				}
				pos += chWidth
				word += string(ch)
				ch, chWidth = utf8.DecodeRuneInString(rest[pos:])
			}
			word += string(ch)
		}
	}

	return words
}

// parse environment like statements. Note that this does *not* handle
// variable interpolation, which will be handled in the evaluator.
func parseNameVal(rest string, key string, d *Directive) (*Node, map[string]bool, error) {
	// This is kind of tricky because we need to support the old
	// variant:   KEY name value
	// as well as the new one:    KEY name=value ...
	// The trigger to know which one is being used will be whether we hit
	// a space or = first.  space ==> old, "=" ==> new

	words := parseWords(rest, d)
	if len(words) == 0 {
		return nil, nil, nil
	}

	var rootnode *Node

	// Old format (KEY name value)
	if !strings.Contains(words[0], "=") {
		node := &Node{}
		rootnode = node
		strs := tokenWhitespace.Split(rest, 2)

		if len(strs) < 2 {
			return nil, nil, fmt.Errorf(key + " must have two arguments")
		}

		node.Value = strs[0]
		node.Next = &Node{}
		node.Next.Value = strs[1]
	} else {
		var prevNode *Node
		for i, word := range words {
			if !strings.Contains(word, "=") {
				return nil, nil, fmt.Errorf("Syntax error - can't find = in %q. Must be of the form: name=value", word)
			}
			parts := strings.SplitN(word, "=", 2)

			name := &Node{}
			value := &Node{}

			name.Next = value
			name.Value = parts[0]
			value.Value = parts[1]

			if i == 0 {
				rootnode = name
			} else {
				prevNode.Next = name
			}
			prevNode = value
		}
	}

	return rootnode, nil, nil
}

func parseEnv(rest string, d *Directive) (*Node, map[string]bool, error) {
	return parseNameVal(rest, "ENV", d)
}

func parseLabel(rest string, d *Directive) (*Node, map[string]bool, error) {
	return parseNameVal(rest, "LABEL", d)
}

// parses a statement containing one or more keyword definition(s) and/or
// value assignments, like `name1 name2= name3="" name4=value`.
// Note that this is a stricter format than the old format of assignment,
// allowed by parseNameVal(), in a way that this only allows assignment of the
// form `keyword=[<value>]` like  `name2=`, `name3=""`, and `name4=value` above.
// In addition, a keyword definition alone is of the form `keyword` like `name1`
// above. And the assignments `name2=` and `name3=""` are equivalent and
// assign an empty value to the respective keywords.
func parseNameOrNameVal(rest string, d *Directive) (*Node, map[string]bool, error) {
	words := parseWords(rest, d)
	if len(words) == 0 {
		return nil, nil, nil
	}

	var (
		rootnode *Node
		prevNode *Node
	)
	for i, word := range words {
		node := &Node{}
		node.Value = word
		if i == 0 {
			rootnode = node
		} else {
			prevNode.Next = node
		}
		prevNode = node
	}

	return rootnode, nil, nil
}

// parses a whitespace-delimited set of arguments. The result is effectively a
// linked list of string arguments.
func parseStringsWhitespaceDelimited(rest string, d *Directive) (*Node, map[string]bool, error) {
	if rest == "" {
		return nil, nil, nil
	}

	node := &Node{}
	rootnode := node
	prevnode := node
	for _, str := range tokenWhitespace.Split(rest, -1) { // use regexp
		prevnode = node
		node.Value = str
		node.Next = &Node{}
		node = node.Next
	}

	// XXX to get around regexp.Split *always* providing an empty string at the
	// end due to how our loop is constructed, nil out the last node in the
	// chain.
	prevnode.Next = nil

	return rootnode, nil, nil
}

// parsestring just wraps the string in quotes and returns a working node.
func parseString(rest string, d *Directive) (*Node, map[string]bool, error) {
	if rest == "" {
		return nil, nil, nil
	}
	n := &Node{}
	n.Value = rest
	return n, nil, nil
}

// parseJSON converts JSON arrays to an AST.
func parseJSON(rest string, d *Directive) (*Node, map[string]bool, error) {
	rest = strings.TrimLeftFunc(rest, unicode.IsSpace)
	if !strings.HasPrefix(rest, "[") {
		return nil, nil, fmt.Errorf(`Error parsing "%s" as a JSON array`, rest)
	}

	var myJSON []interface{}
	if err := json.NewDecoder(strings.NewReader(rest)).Decode(&myJSON); err != nil {
		return nil, nil, err
	}

	var top, prev *Node
	for _, str := range myJSON {
		s, ok := str.(string)
		if !ok {
			return nil, nil, errDockerfileNotStringArray
		}

		node := &Node{Value: s}
		if prev == nil {
			top = node
		} else {
			prev.Next = node
		}
		prev = node
	}

	return top, map[string]bool{"json": true}, nil
}

// parseMaybeJSON determines if the argument appears to be a JSON array. If
// so, passes to parseJSON; if not, quotes the result and returns a single
// node.
func parseMaybeJSON(rest string, d *Directive) (*Node, map[string]bool, error) {
	if rest == "" {
		return nil, nil, nil
	}

	node, attrs, err := parseJSON(rest, d)

	if err == nil {
		return node, attrs, nil
	}
	if err == errDockerfileNotStringArray {
		return nil, nil, err
	}

	node = &Node{}
	node.Value = rest
	return node, nil, nil
}

// parseMaybeJSONToList determines if the argument appears to be a JSON array. If
// so, passes to parseJSON; if not, attempts to parse it as a whitespace
// delimited string.
func parseMaybeJSONToList(rest string, d *Directive) (*Node, map[string]bool, error) {
	node, attrs, err := parseJSON(rest, d)

	if err == nil {
		return node, attrs, nil
	}
	if err == errDockerfileNotStringArray {
		return nil, nil, err
	}

	return parseStringsWhitespaceDelimited(rest, d)
}

// The HEALTHCHECK command is like parseMaybeJSON, but has an extra type argument.
func parseHealthConfig(rest string, d *Directive) (*Node, map[string]bool, error) {
	// Find end of first argument
	var sep int
	for ; sep < len(rest); sep++ {
		if unicode.IsSpace(rune(rest[sep])) {
			break
		}
	}
	next := sep
	for ; next < len(rest); next++ {
		if !unicode.IsSpace(rune(rest[next])) {
			break
		}
	}

	if sep == 0 {
		return nil, nil, nil
	}

	typ := rest[:sep]
	cmd, attrs, err := parseMaybeJSON(rest[next:], d)
	if err != nil {
		return nil, nil, err
	}

	return &Node{Value: typ, Next: cmd, Attributes: attrs}, nil, err
}
<commit_msg>Fix exec form of HEALTHCHECK CMD<commit_after>package parser

// line parsers are dispatch calls that parse a single unit of text into a
// Node object which contains the whole statement. Dockerfiles have varied
// (but not usually unique, see ONBUILD for a unique example) parsing rules
// per-command, and these unify the processing in a way that makes it
// manageable.

import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"unicode"
	"unicode/utf8"
)

var (
	errDockerfileNotStringArray = errors.New("When using JSON array syntax, arrays must be comprised of strings only.")
)

// ignore the current argument. This will still leave a command parsed, but
// will not incorporate the arguments into the ast.
func parseIgnore(rest string, d *Directive) (*Node, map[string]bool, error) {
	return &Node{}, nil, nil
}

// used for onbuild. Could potentially be used for anything that represents a
// statement with sub-statements.
//
// ONBUILD RUN foo bar -> (onbuild (run foo bar))
//
func parseSubCommand(rest string, d *Directive) (*Node, map[string]bool, error) {
	if rest == "" {
		return nil, nil, nil
	}

	_, child, err := ParseLine(rest, d)
	if err != nil {
		return nil, nil, err
	}

	return &Node{Children: []*Node{child}}, nil, nil
}

// helper to parse words (i.e space delimited or quoted strings) in a statement.
// The quotes are preserved as part of this function and they are stripped later
// as part of processWords().
func parseWords(rest string, d *Directive) []string {
	const (
		inSpaces = iota // looking for start of a word
		inWord
		inQuote
	)

	words := []string{}
	phase := inSpaces
	word := ""
	quote := '\000'
	blankOK := false
	var ch rune
	var chWidth int

	for pos := 0; pos <= len(rest); pos += chWidth {
		if pos != len(rest) {
			ch, chWidth = utf8.DecodeRuneInString(rest[pos:])
		}

		if phase == inSpaces { // Looking for start of word
			if pos == len(rest) { // end of input
				break
			}
			if unicode.IsSpace(ch) { // skip spaces
				continue
			}
			phase = inWord // found it, fall through
		}
		if (phase == inWord || phase == inQuote) && (pos == len(rest)) {
			if blankOK || len(word) > 0 {
				words = append(words, word)
			}
			break
		}
		if phase == inWord {
			if unicode.IsSpace(ch) {
				phase = inSpaces
				if blankOK || len(word) > 0 {
					words = append(words, word)
				}
				word = ""
				blankOK = false
				continue
			}
			if ch == '\'' || ch == '"' {
				quote = ch
				blankOK = true
				phase = inQuote
			}
			if ch == d.EscapeToken {
				if pos+chWidth == len(rest) {
					continue // just skip an escape token at end of line
				}
				// If we're not quoted and we see an escape token, then always just
				// add the escape token plus the char to the word, even if the char
				// is a quote.
				word += string(ch)
				pos += chWidth
				ch, chWidth = utf8.DecodeRuneInString(rest[pos:])
			}
			word += string(ch)
			continue
		}
		if phase == inQuote {
			if ch == quote {
				phase = inWord
			}
			// The escape token is special except for ' quotes - can't escape anything for '
			if ch == d.EscapeToken && quote != '\'' {
				if pos+chWidth == len(rest) {
					phase = inWord
					continue // just skip the escape token at end
				}
				pos += chWidth
				word += string(ch)
				ch, chWidth = utf8.DecodeRuneInString(rest[pos:])
			}
			word += string(ch)
		}
	}

	return words
}

// parse environment like statements. Note that this does *not* handle
// variable interpolation, which will be handled in the evaluator.
func parseNameVal(rest string, key string, d *Directive) (*Node, map[string]bool, error) {
	// This is kind of tricky because we need to support the old
	// variant:   KEY name value
	// as well as the new one:    KEY name=value ...
	// The trigger to know which one is being used will be whether we hit
	// a space or = first.  space ==> old, "=" ==> new

	words := parseWords(rest, d)
	if len(words) == 0 {
		return nil, nil, nil
	}

	var rootnode *Node

	// Old format (KEY name value)
	if !strings.Contains(words[0], "=") {
		node := &Node{}
		rootnode = node
		strs := tokenWhitespace.Split(rest, 2)

		if len(strs) < 2 {
			return nil, nil, fmt.Errorf(key + " must have two arguments")
		}

		node.Value = strs[0]
		node.Next = &Node{}
		node.Next.Value = strs[1]
	} else {
		var prevNode *Node
		for i, word := range words {
			if !strings.Contains(word, "=") {
				return nil, nil, fmt.Errorf("Syntax error - can't find = in %q. Must be of the form: name=value", word)
			}
			parts := strings.SplitN(word, "=", 2)

			name := &Node{}
			value := &Node{}

			name.Next = value
			name.Value = parts[0]
			value.Value = parts[1]

			if i == 0 {
				rootnode = name
			} else {
				prevNode.Next = name
			}
			prevNode = value
		}
	}

	return rootnode, nil, nil
}

func parseEnv(rest string, d *Directive) (*Node, map[string]bool, error) {
	return parseNameVal(rest, "ENV", d)
}

func parseLabel(rest string, d *Directive) (*Node, map[string]bool, error) {
	return parseNameVal(rest, "LABEL", d)
}

// parses a statement containing one or more keyword definition(s) and/or
// value assignments, like `name1 name2= name3="" name4=value`.
// Note that this is a stricter format than the old format of assignment,
// allowed by parseNameVal(), in a way that this only allows assignment of the
// form `keyword=[<value>]` like  `name2=`, `name3=""`, and `name4=value` above.
// In addition, a keyword definition alone is of the form `keyword` like `name1`
// above. And the assignments `name2=` and `name3=""` are equivalent and
// assign an empty value to the respective keywords.
func parseNameOrNameVal(rest string, d *Directive) (*Node, map[string]bool, error) {
	words := parseWords(rest, d)
	if len(words) == 0 {
		return nil, nil, nil
	}

	var (
		rootnode *Node
		prevNode *Node
	)
	for i, word := range words {
		node := &Node{}
		node.Value = word
		if i == 0 {
			rootnode = node
		} else {
			prevNode.Next = node
		}
		prevNode = node
	}

	return rootnode, nil, nil
}

// parses a whitespace-delimited set of arguments. The result is effectively a
// linked list of string arguments.
func parseStringsWhitespaceDelimited(rest string, d *Directive) (*Node, map[string]bool, error) {
	if rest == "" {
		return nil, nil, nil
	}

	node := &Node{}
	rootnode := node
	prevnode := node
	for _, str := range tokenWhitespace.Split(rest, -1) { // use regexp
		prevnode = node
		node.Value = str
		node.Next = &Node{}
		node = node.Next
	}

	// XXX to get around regexp.Split *always* providing an empty string at the
	// end due to how our loop is constructed, nil out the last node in the
	// chain.
	prevnode.Next = nil

	return rootnode, nil, nil
}

// parsestring just wraps the string in quotes and returns a working node.
func parseString(rest string, d *Directive) (*Node, map[string]bool, error) {
	if rest == "" {
		return nil, nil, nil
	}
	n := &Node{}
	n.Value = rest
	return n, nil, nil
}

// parseJSON converts JSON arrays to an AST.
func parseJSON(rest string, d *Directive) (*Node, map[string]bool, error) {
	rest = strings.TrimLeftFunc(rest, unicode.IsSpace)
	if !strings.HasPrefix(rest, "[") {
		return nil, nil, fmt.Errorf(`Error parsing "%s" as a JSON array`, rest)
	}

	var myJSON []interface{}
	if err := json.NewDecoder(strings.NewReader(rest)).Decode(&myJSON); err != nil {
		return nil, nil, err
	}

	var top, prev *Node
	for _, str := range myJSON {
		s, ok := str.(string)
		if !ok {
			return nil, nil, errDockerfileNotStringArray
		}

		node := &Node{Value: s}
		if prev == nil {
			top = node
		} else {
			prev.Next = node
		}
		prev = node
	}

	return top, map[string]bool{"json": true}, nil
}

// parseMaybeJSON determines if the argument appears to be a JSON array. If
// so, passes to parseJSON; if not, quotes the result and returns a single
// node.
func parseMaybeJSON(rest string, d *Directive) (*Node, map[string]bool, error) {
	if rest == "" {
		return nil, nil, nil
	}

	node, attrs, err := parseJSON(rest, d)

	if err == nil {
		return node, attrs, nil
	}
	if err == errDockerfileNotStringArray {
		return nil, nil, err
	}

	node = &Node{}
	node.Value = rest
	return node, nil, nil
}

// parseMaybeJSONToList determines if the argument appears to be a JSON array. If
// so, passes to parseJSON; if not, attempts to parse it as a whitespace
// delimited string.
func parseMaybeJSONToList(rest string, d *Directive) (*Node, map[string]bool, error) {
	node, attrs, err := parseJSON(rest, d)

	if err == nil {
		return node, attrs, nil
	}
	if err == errDockerfileNotStringArray {
		return nil, nil, err
	}

	return parseStringsWhitespaceDelimited(rest, d)
}

// The HEALTHCHECK command is like parseMaybeJSON, but has an extra type argument.
func parseHealthConfig(rest string, d *Directive) (*Node, map[string]bool, error) {
	// Find end of first argument
	var sep int
	for ; sep < len(rest); sep++ {
		if unicode.IsSpace(rune(rest[sep])) {
			break
		}
	}
	next := sep
	for ; next < len(rest); next++ {
		if !unicode.IsSpace(rune(rest[next])) {
			break
		}
	}

	if sep == 0 {
		return nil, nil, nil
	}

	typ := rest[:sep]
	cmd, attrs, err := parseMaybeJSON(rest[next:], d)
	if err != nil {
		return nil, nil, err
	}

	return &Node{Value: typ, Next: cmd}, attrs, err
}
<|endoftext|>


<commit_before>package provider

import (
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	fsnotify "gopkg.in/fsnotify.v1"

	"github.com/BurntSushi/toml"
	"github.com/Nitro/sidecar/catalog"
	"github.com/Nitro/sidecar/service"
	"github.com/containous/traefik/log"
	"github.com/containous/traefik/safe"
	"github.com/containous/traefik/types"
)

const (
	method               = "drr"
	sticky               = false
	maxConnAmount        = 300
	maxConnExtractorFunc = "client.ip"
	circuitBreaker       = "NetworkErrorRatio() > 0.5"
	weight               = 0
)

var _ Provider = (*Sidecar)(nil)

// Sidecar holds configurations of the Sidecar provider
type Sidecar struct {
	BaseProvider      `mapstructure:",squash"`
	Endpoint          string `description:"Sidecar URL"`
	Frontend          string `description:"Configuration file for frontend"`
	configurationChan chan<- types.ConfigMessage
	RefreshConn       time.Duration `description:"How often to refresh the connection to Sidecar backend"`
}

type callback func(map[string][]*service.Service, error)

// Provide allows the provider to provide configurations to traefik
// using the given configuration channel.
func (provider *Sidecar) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error {
	provider.configurationChan = configurationChan
	if provider.Watch {
		safe.Go(func() { provider.sidecarWatcher() })

		watcher, err := fsnotify.NewWatcher()
		if err != nil {
			log.Errorln("Error creating file watcher", err)
			return err
		}

		file, err := os.Open(provider.Frontend)
		if err != nil {
			log.Errorln("Error opening file", err)
			return err
		}
		defer file.Close()
		pool.Go(func(stop chan bool) {
			defer watcher.Close()
			for {
				select {
				case <-stop:
					return
				case event := <-watcher.Events:
					if strings.Contains(event.Name, file.Name()) {
						log.Debug("Sidecar Frontend File event:", event)
						states, errState := provider.fetchState()
						if errState != nil {
							log.Errorln("Error reloading Sidecar config", errState)
						}
						provider.loadSidecarConfig(states.ByService())
					}
				case errWatcher := <-watcher.Errors:
					log.Errorln("Watcher event error", errWatcher)
				}
			}
		})
		err = watcher.Add(filepath.Dir(file.Name()))
		if err != nil {
			log.Error("Error adding file watcher", err)
			return err
		}
	}
	states, err := provider.fetchState()
	if err != nil {
		log.Fatalln("Error reloading Sidecar config", err)
	}
	err = provider.loadSidecarConfig(states.ByService())
	if err != nil {
		return err
	}
	return nil
}

func (provider *Sidecar) constructConfig(sidecarStates map[string][]*service.Service) (*types.Configuration, error) {
	sidecarConfig := types.Configuration{}
	log.Infoln("loading sidecar config")
	sidecarConfig.Backends = provider.makeBackends(sidecarStates)
	log.Infoln("loading frontend config from file: ", provider.Frontend)
	var err error
	sidecarConfig.Frontends, err = provider.makeFrontend()
	if err != nil {
		return nil, err
	}
	return &sidecarConfig, nil
}

func (provider *Sidecar) loadSidecarConfig(sidecarStates map[string][]*service.Service) error {
	conf, err := provider.constructConfig(sidecarStates)
	if err != nil {
		return err
	}
	provider.configurationChan <- types.ConfigMessage{
		ProviderName:  "sidecar",
		Configuration: conf,
	}
	return nil
}

func (provider *Sidecar) sidecarWatcher() error {
	//set timeout to be just a bot more than connection refresh interval
	client := &http.Client{Timeout: (provider.RefreshConn + 3) * time.Second}
	resp, err := client.Get(provider.Endpoint + "/watch")
	if err != nil {
		return err
	}
	go catalog.DecodeStream(resp.Body, provider.callbackLoader)
	log.Infof("Using %s Sidecar connection refresh interval", provider.RefreshConn)
	provider.recycleConn(resp, client)
	return nil
}

func (provider *Sidecar) recycleConn(resp *http.Response, client *http.Client) {
	var err error
	for { //use refresh interval to occasionally reconnect to Sidecar in case the stream connection is lost
		time.Sleep(provider.RefreshConn * time.Second)
		resp, err = client.Get(provider.Endpoint + "/watch")
		if err != nil {
			log.Errorf("Error connecting to Sidecar: %s, Error: %s", provider.Endpoint, err)
			continue
		}
		go catalog.DecodeStream(resp.Body, provider.callbackLoader)
	}
}

func (provider *Sidecar) callbackLoader(sidecarStates map[string][]*service.Service, err error) error {
	if err != nil {
		log.Errorln("Error decoding stream ", err)
		return err
	}
	provider.loadSidecarConfig(sidecarStates)
	return nil
}

func (provider *Sidecar) makeFrontend() (map[string]*types.Frontend, error) {
	configuration := new(types.Configuration)
	if _, err := toml.DecodeFile(provider.Frontend, configuration); err != nil {
		log.Errorf("Error reading file: %s", err)
		return nil, err
	}
	return configuration.Frontends, nil
}

func (provider *Sidecar) makeBackends(sidecarStates map[string][]*service.Service) map[string]*types.Backend {
	sidecarBacks := make(map[string]*types.Backend)
	for serviceName, services := range sidecarStates {
		newServers := make(map[string]types.Server)
		newBackend := &types.Backend{LoadBalancer: &types.LoadBalancer{Method: method, Sticky: sticky},
			MaxConn:        &types.MaxConn{Amount: maxConnAmount, ExtractorFunc: maxConnExtractorFunc},
			CircuitBreaker: &types.CircuitBreaker{Expression: circuitBreaker},
			Servers:        newServers}
		for _, serv := range services {
			if serv.IsAlive() {
				for i := 0; i < len(serv.Ports); i++ {
					ipAddr, err := net.LookupIP(serv.Hostname)
					if err != nil {
						log.Errorln("Error resolving Ip address, ", err)
						newBackend.Servers[serv.Hostname] = types.Server{URL: "http://" + serv.Hostname + ":" + strconv.FormatInt(serv.Ports[i].Port, 10), Weight: weight}
					} else {
						newBackend.Servers[serv.Hostname] = types.Server{URL: "http://" + ipAddr[0].String() + ":" + strconv.FormatInt(serv.Ports[i].Port, 10), Weight: weight}
					}
				}
			}
		}
		sidecarBacks[serviceName] = newBackend
	}
	return sidecarBacks
}

func (provider *Sidecar) fetchState() (*catalog.ServicesState, error) {
	client := &http.Client{Timeout: 5 * time.Second}
	resp, err := client.Get(provider.Endpoint + "/state.json")
	if err != nil {
		return nil, err
	}

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

	state, err := catalog.Decode(bytes)
	if err != nil {
		return nil, err
	}
	return state, nil
}
<commit_msg>Update sidecar.go<commit_after>package provider

import (
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	fsnotify "gopkg.in/fsnotify.v1"

	"github.com/BurntSushi/toml"
	"github.com/Nitro/sidecar/catalog"
	"github.com/Nitro/sidecar/service"
	"github.com/containous/traefik/log"
	"github.com/containous/traefik/safe"
	"github.com/containous/traefik/types"
)

const (
	method               = "drr"
	sticky               = false
	maxConnAmount        = 300
	maxConnExtractorFunc = "client.ip"
	circuitBreaker       = "ResponseCodeRatio(500, 600, 0, 600) > 0.3"
	weight               = 0
)

var _ Provider = (*Sidecar)(nil)

// Sidecar holds configurations of the Sidecar provider
type Sidecar struct {
	BaseProvider      `mapstructure:",squash"`
	Endpoint          string `description:"Sidecar URL"`
	Frontend          string `description:"Configuration file for frontend"`
	configurationChan chan<- types.ConfigMessage
	RefreshConn       time.Duration `description:"How often to refresh the connection to Sidecar backend"`
}

type callback func(map[string][]*service.Service, error)

// Provide allows the provider to provide configurations to traefik
// using the given configuration channel.
func (provider *Sidecar) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error {
	provider.configurationChan = configurationChan
	if provider.Watch {
		safe.Go(func() { provider.sidecarWatcher() })

		watcher, err := fsnotify.NewWatcher()
		if err != nil {
			log.Errorln("Error creating file watcher", err)
			return err
		}

		file, err := os.Open(provider.Frontend)
		if err != nil {
			log.Errorln("Error opening file", err)
			return err
		}
		defer file.Close()
		pool.Go(func(stop chan bool) {
			defer watcher.Close()
			for {
				select {
				case <-stop:
					return
				case event := <-watcher.Events:
					if strings.Contains(event.Name, file.Name()) {
						log.Debug("Sidecar Frontend File event:", event)
						states, errState := provider.fetchState()
						if errState != nil {
							log.Errorln("Error reloading Sidecar config", errState)
						}
						provider.loadSidecarConfig(states.ByService())
					}
				case errWatcher := <-watcher.Errors:
					log.Errorln("Watcher event error", errWatcher)
				}
			}
		})
		err = watcher.Add(filepath.Dir(file.Name()))
		if err != nil {
			log.Error("Error adding file watcher", err)
			return err
		}
	}
	states, err := provider.fetchState()
	if err != nil {
		log.Fatalln("Error reloading Sidecar config", err)
	}
	err = provider.loadSidecarConfig(states.ByService())
	if err != nil {
		return err
	}
	return nil
}

func (provider *Sidecar) constructConfig(sidecarStates map[string][]*service.Service) (*types.Configuration, error) {
	sidecarConfig := types.Configuration{}
	log.Infoln("loading sidecar config")
	sidecarConfig.Backends = provider.makeBackends(sidecarStates)
	log.Infoln("loading frontend config from file: ", provider.Frontend)
	var err error
	sidecarConfig.Frontends, err = provider.makeFrontend()
	if err != nil {
		return nil, err
	}
	return &sidecarConfig, nil
}

func (provider *Sidecar) loadSidecarConfig(sidecarStates map[string][]*service.Service) error {
	conf, err := provider.constructConfig(sidecarStates)
	if err != nil {
		return err
	}
	provider.configurationChan <- types.ConfigMessage{
		ProviderName:  "sidecar",
		Configuration: conf,
	}
	return nil
}

func (provider *Sidecar) sidecarWatcher() error {
	//set timeout to be just a bot more than connection refresh interval
	client := &http.Client{Timeout: (provider.RefreshConn + 3) * time.Second}
	resp, err := client.Get(provider.Endpoint + "/watch")
	if err != nil {
		return err
	}
	go catalog.DecodeStream(resp.Body, provider.callbackLoader)
	log.Infof("Using %s Sidecar connection refresh interval", provider.RefreshConn)
	provider.recycleConn(resp, client)
	return nil
}

func (provider *Sidecar) recycleConn(resp *http.Response, client *http.Client) {
	var err error
	for { //use refresh interval to occasionally reconnect to Sidecar in case the stream connection is lost
		time.Sleep(provider.RefreshConn * time.Second)
		resp, err = client.Get(provider.Endpoint + "/watch")
		if err != nil {
			log.Errorf("Error connecting to Sidecar: %s, Error: %s", provider.Endpoint, err)
			continue
		}
		go catalog.DecodeStream(resp.Body, provider.callbackLoader)
	}
}

func (provider *Sidecar) callbackLoader(sidecarStates map[string][]*service.Service, err error) error {
	if err != nil {
		log.Errorln("Error decoding stream ", err)
		return err
	}
	provider.loadSidecarConfig(sidecarStates)
	return nil
}

func (provider *Sidecar) makeFrontend() (map[string]*types.Frontend, error) {
	configuration := new(types.Configuration)
	if _, err := toml.DecodeFile(provider.Frontend, configuration); err != nil {
		log.Errorf("Error reading file: %s", err)
		return nil, err
	}
	return configuration.Frontends, nil
}

func (provider *Sidecar) makeBackends(sidecarStates map[string][]*service.Service) map[string]*types.Backend {
	sidecarBacks := make(map[string]*types.Backend)
	for serviceName, services := range sidecarStates {
		newServers := make(map[string]types.Server)
		newBackend := &types.Backend{LoadBalancer: &types.LoadBalancer{Method: method, Sticky: sticky},
			MaxConn:        &types.MaxConn{Amount: maxConnAmount, ExtractorFunc: maxConnExtractorFunc},
			CircuitBreaker: &types.CircuitBreaker{Expression: circuitBreaker},
			Servers:        newServers}
		for _, serv := range services {
			if serv.IsAlive() {
				for i := 0; i < len(serv.Ports); i++ {
					ipAddr, err := net.LookupIP(serv.Hostname)
					if err != nil {
						log.Errorln("Error resolving Ip address, ", err)
						newBackend.Servers[serv.Hostname] = types.Server{URL: "http://" + serv.Hostname + ":" + strconv.FormatInt(serv.Ports[i].Port, 10), Weight: weight}
					} else {
						newBackend.Servers[serv.Hostname] = types.Server{URL: "http://" + ipAddr[0].String() + ":" + strconv.FormatInt(serv.Ports[i].Port, 10), Weight: weight}
					}
				}
			}
		}
		sidecarBacks[serviceName] = newBackend
	}
	return sidecarBacks
}

func (provider *Sidecar) fetchState() (*catalog.ServicesState, error) {
	client := &http.Client{Timeout: 5 * time.Second}
	resp, err := client.Get(provider.Endpoint + "/state.json")
	if err != nil {
		return nil, err
	}

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

	state, err := catalog.Decode(bytes)
	if err != nil {
		return nil, err
	}
	return state, nil
}
<|endoftext|>


<commit_before>package swarm

import (
	"fmt"

	conn "github.com/jbenet/go-ipfs/p2p/net/conn"
	lgbl "github.com/jbenet/go-ipfs/util/eventlog/loggables"

	context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
	ps "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-peerstream"
	multierr "github.com/jbenet/go-ipfs/util/multierr"
)

// Open listeners for each network the swarm should listen on
func (s *Swarm) listen(addrs []ma.Multiaddr) error {

	for _, addr := range addrs {
		if !AddrUsable(addr) {
			return fmt.Errorf("cannot use addr: %s", addr)
		}
	}

	retErr := multierr.New()

	// listen on every address
	for i, addr := range addrs {
		err := s.setupListener(addr)
		if err != nil {
			if retErr.Errors == nil {
				retErr.Errors = make([]error, len(addrs))
			}
			retErr.Errors[i] = err
			log.Errorf("Failed to listen on: %s - %s", addr, err)
		}
	}

	if retErr.Errors != nil {
		return retErr
	}
	return nil
}

// Listen for new connections on the given multiaddr
func (s *Swarm) setupListener(maddr ma.Multiaddr) error {

	// TODO rethink how this has to work. (jbenet)
	//
	// resolved, err := resolveUnspecifiedAddresses([]ma.Multiaddr{maddr})
	// if err != nil {
	// 	return err
	// }
	// for _, a := range resolved {
	// 	s.peers.AddAddress(s.local, a)
	// }

	sk := s.peers.PrivKey(s.local)
	if sk == nil {
		// may be fine for sk to be nil, just log a warning.
		log.Warning("Listener not given PrivateKey, so WILL NOT SECURE conns.")
	}
	list, err := conn.Listen(s.cg.Context(), maddr, s.local, sk)
	if err != nil {
		return err
	}

	// AddListener to the peerstream Listener. this will begin accepting connections
	// and streams!
	_, err = s.swarm.AddListener(list)
	return err
}

// connHandler is called by the StreamSwarm whenever a new connection is added
// here we configure it slightly. Note that this is sequential, so if anything
// will take a while do it in a goroutine.
// See https://godoc.org/github.com/jbenet/go-peerstream for more information
func (s *Swarm) connHandler(c *ps.Conn) *Conn {
	ctx := context.Background()
	// this context is for running the handshake, which -- when receiveing connections
	// -- we have no bound on beyond what the transport protocol bounds it at.
	// note that setup + the handshake are bounded by underlying io.
	// (i.e. if TCP or UDP disconnects (or the swarm closes), we're done.
	// Q: why not have a shorter handshake? think about an HTTP server on really slow conns.
	// as long as the conn is live (TCP says its online), it tries its best. we follow suit.)

	sc, err := s.newConnSetup(ctx, c)
	if err != nil {
		log.Error(err)
		log.Event(ctx, "newConnHandlerDisconnect", lgbl.NetConn(c.NetConn()), lgbl.Error(err))
		c.Close() // boom. close it.
		return nil
	}

	return sc
}
<commit_msg>p2p/net/swarm/listener: consume peerstream listeners<commit_after>package swarm

import (
	"fmt"

	conn "github.com/jbenet/go-ipfs/p2p/net/conn"
	lgbl "github.com/jbenet/go-ipfs/util/eventlog/loggables"

	context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
	ps "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-peerstream"
	multierr "github.com/jbenet/go-ipfs/util/multierr"
)

// Open listeners for each network the swarm should listen on
func (s *Swarm) listen(addrs []ma.Multiaddr) error {

	for _, addr := range addrs {
		if !AddrUsable(addr) {
			return fmt.Errorf("cannot use addr: %s", addr)
		}
	}

	retErr := multierr.New()

	// listen on every address
	for i, addr := range addrs {
		err := s.setupListener(addr)
		if err != nil {
			if retErr.Errors == nil {
				retErr.Errors = make([]error, len(addrs))
			}
			retErr.Errors[i] = err
			log.Errorf("Failed to listen on: %s - %s", addr, err)
		}
	}

	if retErr.Errors != nil {
		return retErr
	}
	return nil
}

// Listen for new connections on the given multiaddr
func (s *Swarm) setupListener(maddr ma.Multiaddr) error {

	// TODO rethink how this has to work. (jbenet)
	//
	// resolved, err := resolveUnspecifiedAddresses([]ma.Multiaddr{maddr})
	// if err != nil {
	// 	return err
	// }
	// for _, a := range resolved {
	// 	s.peers.AddAddress(s.local, a)
	// }

	sk := s.peers.PrivKey(s.local)
	if sk == nil {
		// may be fine for sk to be nil, just log a warning.
		log.Warning("Listener not given PrivateKey, so WILL NOT SECURE conns.")
	}
	list, err := conn.Listen(s.cg.Context(), maddr, s.local, sk)
	if err != nil {
		return err
	}

	// AddListener to the peerstream Listener. this will begin accepting connections
	// and streams!
	sl, err := s.swarm.AddListener(list)
	if err != nil {
		return err
	}

	// go consume peerstream's listen accept errors. note, these ARE errors.
	// they may be killing the listener, and if we get _any_ we should be
	// fixing this in our conn.Listener (to ignore them or handle them
	// differently.)
	go func(ctx context.Context, sl *ps.Listener) {
		for {
			select {
			case err, more := <-sl.AcceptErrors():
				if !more {
					return
				}
				log.Info(err)
			case <-ctx.Done():
				return
			}
		}
	}(s.cg.Context(), sl)

	return nil
}

// connHandler is called by the StreamSwarm whenever a new connection is added
// here we configure it slightly. Note that this is sequential, so if anything
// will take a while do it in a goroutine.
// See https://godoc.org/github.com/jbenet/go-peerstream for more information
func (s *Swarm) connHandler(c *ps.Conn) *Conn {
	ctx := context.Background()
	// this context is for running the handshake, which -- when receiveing connections
	// -- we have no bound on beyond what the transport protocol bounds it at.
	// note that setup + the handshake are bounded by underlying io.
	// (i.e. if TCP or UDP disconnects (or the swarm closes), we're done.
	// Q: why not have a shorter handshake? think about an HTTP server on really slow conns.
	// as long as the conn is live (TCP says its online), it tries its best. we follow suit.)

	sc, err := s.newConnSetup(ctx, c)
	if err != nil {
		log.Error(err)
		log.Event(ctx, "newConnHandlerDisconnect", lgbl.NetConn(c.NetConn()), lgbl.Error(err))
		c.Close() // boom. close it.
		return nil
	}

	return sc
}
<|endoftext|>


<commit_before>package parser

import (
	"monkey/ast"
	"monkey/token"
)

func (p *Parser) parseReturnStatement() *ast.ReturnStatement {
	stmt := &ast.ReturnStatement{Token: p.curToken}
	p.nextToken()
	stmt.ReturnValue = p.parseExpressionStatement().Expression

	return stmt
}

func (p *Parser) parseLetStatement() *ast.LetStatement {
	stmt := &ast.LetStatement{Token: p.curToken}

	if !p.expectPeek(token.IDENT) {
		return nil
	}

	stmt.Name = &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}

	if !p.expectPeek(token.ASSIGN) {
		return nil
	}
	p.nextToken()
	stmt.Value = p.parseExpressionStatement().Expression

	return stmt
}
<commit_msg>allow nil returns<commit_after>package parser

import (
	"monkey/ast"
	"monkey/token"
)

func (p *Parser) parseReturnStatement() *ast.ReturnStatement {
	stmt := &ast.ReturnStatement{Token: p.curToken}
	if p.peekTokenIs(token.SEMICOLON) {
		p.nextToken()
		return stmt
	}
	p.nextToken()
	stmt.ReturnValue = p.parseExpressionStatement().Expression

	return stmt
}

func (p *Parser) parseLetStatement() *ast.LetStatement {
	stmt := &ast.LetStatement{Token: p.curToken}

	if !p.expectPeek(token.IDENT) {
		return nil
	}
	stmt.Name = &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}

	if !p.expectPeek(token.ASSIGN) {
		return nil
	}
	p.nextToken()
	stmt.Value = p.parseExpressionStatement().Expression

	return stmt
}
<|endoftext|>


<commit_before>package http2

import (
	"bytes"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io"
	"mime"
	"mime/multipart"
	"net"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"

	"github.com/xgfone/go-tools/types"
)

var (
	xmlHeaderBytes = []byte(xml.Header)

	decodeMap = make(map[string]func(*http.Request, int64, interface{}) error, 4)
)

// RegisterDecode registers the decode function for Content-Type ctype.
//
// ctype is the Content-Type to decode.
//
// f is the decoder for the Content-Type ctype. The decoder has three arguments.
// The first is the request, the second is the max size of the request body,
// and the last is the decoded values, which is a pointer or map in general.
//
// If giving force as true, it will override the registered content-type decoder.
func RegisterDecode(ctype string, f func(*http.Request, int64, interface{}) error,
	force ...bool) error {
	if _, ok := decodeMap[ctype]; ok {
		if len(force) == 0 || !force[0] {
			return fmt.Errorf("the Content-Type '%s' has existed", ctype)
		}
	}
	decodeMap[ctype] = f
	return nil
}

func init() {
	RegisterDecode(ApplicationJSON, DecodeJSON)
	RegisterDecode(ApplicationXML, DecodeXML)
}

func filteFlag(s string) string {
	for i, c := range s {
		if c == ' ' || c == ';' {
			return s[:i]
		}
	}
	return s
}

// SetHeader sets the response header.
func SetHeader(w http.ResponseWriter, key, value string) {
	w.Header().Set(key, value)
}

// SetContentType is equal to SetContentTypes(w, []string{value}).
func SetContentType(w http.ResponseWriter, value string) {
	w.Header().Set(ContentType, value)
}

// GetContentType returns the Content-Type of the request body.
func GetContentType(r *http.Request) string {
	return filteFlag(r.Header.Get("Content-Type"))
}

func detectContentType(filename string) (t string) {
	if t = mime.TypeByExtension(filepath.Ext(filename)); t == "" {
		t = OctetStream
	}
	return
}

// AcceptedLanguages returns an array of accepted languages denoted by
// the Accept-Language header sent by the browser
// NOTE: some stupid browsers send in locales lowercase when all the rest send
// it properly
func AcceptedLanguages(r *http.Request) (languages []string) {
	var accepted string
	if accepted = r.Header.Get(AcceptedLanguage); accepted == blank {
		return
	}

	options := strings.Split(accepted, ",")
	l := len(options)
	languages = make([]string, l)
	for i := 0; i < l; i++ {
		locale := strings.SplitN(options[i], ";", 2)
		languages[i] = strings.Trim(locale[0], " ")
	}

	return
}

// Attachment is a helper method for returning an attachement file
// to be downloaded, if you with to open inline see function Inline
func Attachment(w http.ResponseWriter, r io.Reader, filename string) (err error) {
	SetHeader(w, ContentDisposition, "attachment;filename="+filename)
	SetContentType(w, detectContentType(filename))
	w.WriteHeader(http.StatusOK)

	_, err = io.Copy(w, r)
	return
}

// Inline is a helper method for returning a file inline to
// be rendered/opened by the browser
func Inline(w http.ResponseWriter, r io.Reader, filename string) (err error) {
	SetHeader(w, ContentDisposition, "inline;filename="+filename)
	SetContentType(w, detectContentType(filename))
	w.WriteHeader(http.StatusOK)

	_, err = io.Copy(w, r)
	return
}

// ClientIP implements a best effort algorithm to return the real client IP,
// it parses X-Real-IP and X-Forwarded-For in order to work properly
// with reverse-proxies such us: nginx or haproxy.
func ClientIP(r *http.Request) (clientIP string) {
	var values []string

	if values, _ = r.Header[XRealIP]; len(values) > 0 {

		clientIP = strings.TrimSpace(values[0])
		if clientIP != blank {
			return
		}
	}

	if values, _ = r.Header[XForwardedFor]; len(values) > 0 {
		clientIP = values[0]

		if index := strings.IndexByte(clientIP, ','); index >= 0 {
			clientIP = clientIP[0:index]
		}

		clientIP = strings.TrimSpace(clientIP)
		if clientIP != blank {
			return
		}
	}

	clientIP, _, _ = net.SplitHostPort(strings.TrimSpace(r.RemoteAddr))

	return
}

// FromReader reads the content from the reader, then renders it to the response.
func FromReader(w http.ResponseWriter, status int, contentType string,
	reader io.Reader) error {
	SetContentType(w, contentType)
	w.WriteHeader(status)
	_, err := io.Copy(w, reader)
	return err
}

// Error renders an error into the response.
//
// If the status is not gived, the default is 500.
func Error(w http.ResponseWriter, err error, status ...int) error {
	if len(status) > 0 {
		return String(w, status[0], "%s", err)
	}
	return String(w, http.StatusInternalServerError, "%s", err)
}

// String renders the format string into the response.
func String(w http.ResponseWriter, status int, format string,
	args ...interface{}) error {
	SetContentType(w, TextPlainCharsetUTF8)
	w.WriteHeader(status)
	_, err := fmt.Fprintf(w, format, args...)
	return err
}

// Bytes renders the content into the response with a Content-Type and code.
func Bytes(w http.ResponseWriter, status int, contentType string,
	content []byte) error {
	SetContentType(w, contentType)
	w.WriteHeader(status)
	_, err := w.Write(content)
	return err
}

// JSON marshals provided interface + returns JSON + status code
func JSON(w http.ResponseWriter, status int, i interface{}) error {
	b, err := json.Marshal(i)
	if err != nil {
		return err
	}

	return JSONBytes(w, status, b)
}

// JSONBytes returns provided JSON response with status code
func JSONBytes(w http.ResponseWriter, status int, b []byte) (err error) {
	return Bytes(w, status, ApplicationJSONCharsetUTF8, b)
}

// JSONP sends a JSONP response with status code and uses `callback` to
// construct the JSONP payload.
func JSONP(w http.ResponseWriter, status int, i interface{}, callback string) error {
	b, err := json.Marshal(i)
	if err != nil {
		return err
	}

	SetContentType(w, ApplicationJavaScriptCharsetUTF8)
	w.WriteHeader(status)

	if _, err = w.Write([]byte(callback + "(")); err == nil {
		if _, err = w.Write(b); err == nil {
			_, err = w.Write([]byte(");"))
		}
	}

	return err
}

// XML marshals provided interface + returns XML + status code
func XML(w http.ResponseWriter, status int, i interface{}) error {
	b, err := xml.Marshal(i)
	if err != nil {
		return err
	}

	return XMLBytes(w, status, b)
}

// XMLBytes returns provided XML response with status code
func XMLBytes(w http.ResponseWriter, status int, b []byte) (err error) {
	SetContentType(w, ApplicationXMLCharsetUTF8)
	w.WriteHeader(status)

	if _, err = w.Write(xmlHeaderBytes); err == nil {
		_, err = w.Write(b)
	}

	return
}

// DecodeJSON decodes the request body into the provided struct and limits
// the request size via an io.LimitReader using the maxMemory param.
//
// The Content-Type e.g. "application/json" and http method are not checked.
func DecodeJSON(r *http.Request, maxMemory int64, v interface{}) (err error) {
	return json.NewDecoder(io.LimitReader(r.Body, maxMemory)).Decode(v)
}

// DecodeXML decodes the request body into the provided struct and limits
// the request size via an io.LimitReader using the maxMemory param.
//
// The Content-Type e.g. "application/xml" and http method are not checked.
func DecodeXML(r *http.Request, maxMemory int64, v interface{}) (err error) {
	return xml.NewDecoder(io.LimitReader(r.Body, maxMemory)).Decode(v)
}

// Decode takes the request and attempts to discover it's content type via
// the http headers and then decode the request body into the provided struct.
// Example if header was "application/json" would decode using
// json.NewDecoder(io.LimitReader(r.Body, maxMemory)).Decode(v).
//
// Notice: At present it only supports to decode JSON and XML.
func Decode(r *http.Request, maxMemory int64, v interface{}) (err error) {
	ct := GetContentType(r)
	if f := decodeMap[ct]; f != nil {
		return f(r, maxMemory, v)
	}
	return fmt.Errorf("no decode of Content-Type %s", ct)
}

// SaveUploadedFile uploads the form file to the specific file dst.
func SaveUploadedFile(file *multipart.FileHeader, dst string) error {
	src, err := file.Open()
	if err != nil {
		return err
	}
	defer src.Close()

	out, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer out.Close()

	_, err = io.Copy(out, src)
	return err
}

// GetBody returns the body of the HTTP request.
func GetBody(r *http.Request) (body []byte, err error) {
	buf := bytes.NewBuffer(nil)
	if _, err = io.CopyN(buf, r.Body, r.ContentLength); err != nil {
		return
	}
	return buf.Bytes(), nil
}

// GetQuerys returns the query values by the key.
//
// If the key does not exist, return nil.
func GetQuerys(values url.Values, key string) []string {
	return values[key]
}

// GetQuery return the first query value by the key.
//
// If the key does not exist, return "".
func GetQuery(values url.Values, key string) string {
	if vs := GetQuerys(values, key); len(vs) > 0 {
		return vs[0]
	}
	return ""
}

// GetQueryInt gets the first query value and converts it to int.
//
// If the key does not exist. return 0, not an error.
func GetQueryInt(values url.Values, key string) (int, error) {
	if v := GetQuery(values, key); v != "" {
		return types.ToInt(v)
	}
	return 0, nil
}

// GetQueryInt64 gets the first query value and converts it to int64.
//
// If the key does not exist. return 0, not an error.
func GetQueryInt64(values url.Values, key string) (int64, error) {
	if v := GetQuery(values, key); v != "" {
		return types.ToInt64(v)
	}
	return 0, nil
}

// GetQueryFloat64 gets the first query value and converts it to float64.
//
// If the key does not exist. return 0, not an error.
func GetQueryFloat64(values url.Values, key string) (float64, error) {
	if v := GetQuery(values, key); v != "" {
		return types.ToFloat64(v)
	}
	return 0, nil
}

// GetQueryBool gets the first query value and converts it to bool.
//
// If the key does not exist. return false, not an error.
func GetQueryBool(values url.Values, key string) (bool, error) {
	if v := GetQuery(values, key); v != "" {
		return types.ToBool(v)
	}
	return false, nil
}

// GetQueryStringSlice returns the query string slice by the key.
//
// The format supports two kinds: (1) more than one key, (2) the value separated
// by the comma. For example:
//
//     // URL: /path/to?key1=v1&key1=v2&key1=v3,v4,v5&key2=value2
//     GetQueryStringSlice(r.URL.Query(), "key1") // => [v1, v2, v3, v4, v5]
//
// If the key does not exist or it value is empty, return an empty string slice.
func GetQueryStringSlice(values url.Values, key string) []string {
	if qs := GetQuerys(values, key); len(qs) > 0 {
		vs := make([]string, 0, len(qs)*2)
		for _, q := range qs {
			for _, v := range strings.Split(q, ",") {
				if v != "" {
					vs = append(vs, v)
				}
			}
		}
	}

	return []string{}
}
<commit_msg>Add Some GetQueryXXXDefault functions<commit_after>package http2

import (
	"bytes"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io"
	"mime"
	"mime/multipart"
	"net"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"

	"github.com/xgfone/go-tools/types"
)

var (
	xmlHeaderBytes = []byte(xml.Header)

	decodeMap = make(map[string]func(*http.Request, int64, interface{}) error, 4)
)

// RegisterDecode registers the decode function for Content-Type ctype.
//
// ctype is the Content-Type to decode.
//
// f is the decoder for the Content-Type ctype. The decoder has three arguments.
// The first is the request, the second is the max size of the request body,
// and the last is the decoded values, which is a pointer or map in general.
//
// If giving force as true, it will override the registered content-type decoder.
func RegisterDecode(ctype string, f func(*http.Request, int64, interface{}) error,
	force ...bool) error {
	if _, ok := decodeMap[ctype]; ok {
		if len(force) == 0 || !force[0] {
			return fmt.Errorf("the Content-Type '%s' has existed", ctype)
		}
	}
	decodeMap[ctype] = f
	return nil
}

func init() {
	RegisterDecode(ApplicationJSON, DecodeJSON)
	RegisterDecode(ApplicationXML, DecodeXML)
}

func filteFlag(s string) string {
	for i, c := range s {
		if c == ' ' || c == ';' {
			return s[:i]
		}
	}
	return s
}

// SetHeader sets the response header.
func SetHeader(w http.ResponseWriter, key, value string) {
	w.Header().Set(key, value)
}

// SetContentType is equal to SetContentTypes(w, []string{value}).
func SetContentType(w http.ResponseWriter, value string) {
	w.Header().Set(ContentType, value)
}

// GetContentType returns the Content-Type of the request body.
func GetContentType(r *http.Request) string {
	return filteFlag(r.Header.Get("Content-Type"))
}

func detectContentType(filename string) (t string) {
	if t = mime.TypeByExtension(filepath.Ext(filename)); t == "" {
		t = OctetStream
	}
	return
}

// AcceptedLanguages returns an array of accepted languages denoted by
// the Accept-Language header sent by the browser
// NOTE: some stupid browsers send in locales lowercase when all the rest send
// it properly
func AcceptedLanguages(r *http.Request) (languages []string) {
	var accepted string
	if accepted = r.Header.Get(AcceptedLanguage); accepted == blank {
		return
	}

	options := strings.Split(accepted, ",")
	l := len(options)
	languages = make([]string, l)
	for i := 0; i < l; i++ {
		locale := strings.SplitN(options[i], ";", 2)
		languages[i] = strings.Trim(locale[0], " ")
	}

	return
}

// Attachment is a helper method for returning an attachement file
// to be downloaded, if you with to open inline see function Inline
func Attachment(w http.ResponseWriter, r io.Reader, filename string) (err error) {
	SetHeader(w, ContentDisposition, "attachment;filename="+filename)
	SetContentType(w, detectContentType(filename))
	w.WriteHeader(http.StatusOK)

	_, err = io.Copy(w, r)
	return
}

// Inline is a helper method for returning a file inline to
// be rendered/opened by the browser
func Inline(w http.ResponseWriter, r io.Reader, filename string) (err error) {
	SetHeader(w, ContentDisposition, "inline;filename="+filename)
	SetContentType(w, detectContentType(filename))
	w.WriteHeader(http.StatusOK)

	_, err = io.Copy(w, r)
	return
}

// ClientIP implements a best effort algorithm to return the real client IP,
// it parses X-Real-IP and X-Forwarded-For in order to work properly
// with reverse-proxies such us: nginx or haproxy.
func ClientIP(r *http.Request) (clientIP string) {
	var values []string

	if values, _ = r.Header[XRealIP]; len(values) > 0 {

		clientIP = strings.TrimSpace(values[0])
		if clientIP != blank {
			return
		}
	}

	if values, _ = r.Header[XForwardedFor]; len(values) > 0 {
		clientIP = values[0]

		if index := strings.IndexByte(clientIP, ','); index >= 0 {
			clientIP = clientIP[0:index]
		}

		clientIP = strings.TrimSpace(clientIP)
		if clientIP != blank {
			return
		}
	}

	clientIP, _, _ = net.SplitHostPort(strings.TrimSpace(r.RemoteAddr))

	return
}

// FromReader reads the content from the reader, then renders it to the response.
func FromReader(w http.ResponseWriter, status int, contentType string,
	reader io.Reader) error {
	SetContentType(w, contentType)
	w.WriteHeader(status)
	_, err := io.Copy(w, reader)
	return err
}

// Error renders an error into the response.
//
// If the status is not gived, the default is 500.
func Error(w http.ResponseWriter, err error, status ...int) error {
	if len(status) > 0 {
		return String(w, status[0], "%s", err)
	}
	return String(w, http.StatusInternalServerError, "%s", err)
}

// String renders the format string into the response.
func String(w http.ResponseWriter, status int, format string,
	args ...interface{}) error {
	SetContentType(w, TextPlainCharsetUTF8)
	w.WriteHeader(status)
	_, err := fmt.Fprintf(w, format, args...)
	return err
}

// Bytes renders the content into the response with a Content-Type and code.
func Bytes(w http.ResponseWriter, status int, contentType string,
	content []byte) error {
	SetContentType(w, contentType)
	w.WriteHeader(status)
	_, err := w.Write(content)
	return err
}

// JSON marshals provided interface + returns JSON + status code
func JSON(w http.ResponseWriter, status int, i interface{}) error {
	b, err := json.Marshal(i)
	if err != nil {
		return err
	}

	return JSONBytes(w, status, b)
}

// JSONBytes returns provided JSON response with status code
func JSONBytes(w http.ResponseWriter, status int, b []byte) (err error) {
	return Bytes(w, status, ApplicationJSONCharsetUTF8, b)
}

// JSONP sends a JSONP response with status code and uses `callback` to
// construct the JSONP payload.
func JSONP(w http.ResponseWriter, status int, i interface{}, callback string) error {
	b, err := json.Marshal(i)
	if err != nil {
		return err
	}

	SetContentType(w, ApplicationJavaScriptCharsetUTF8)
	w.WriteHeader(status)

	if _, err = w.Write([]byte(callback + "(")); err == nil {
		if _, err = w.Write(b); err == nil {
			_, err = w.Write([]byte(");"))
		}
	}

	return err
}

// XML marshals provided interface + returns XML + status code
func XML(w http.ResponseWriter, status int, i interface{}) error {
	b, err := xml.Marshal(i)
	if err != nil {
		return err
	}

	return XMLBytes(w, status, b)
}

// XMLBytes returns provided XML response with status code
func XMLBytes(w http.ResponseWriter, status int, b []byte) (err error) {
	SetContentType(w, ApplicationXMLCharsetUTF8)
	w.WriteHeader(status)

	if _, err = w.Write(xmlHeaderBytes); err == nil {
		_, err = w.Write(b)
	}

	return
}

// DecodeJSON decodes the request body into the provided struct and limits
// the request size via an io.LimitReader using the maxMemory param.
//
// The Content-Type e.g. "application/json" and http method are not checked.
func DecodeJSON(r *http.Request, maxMemory int64, v interface{}) (err error) {
	return json.NewDecoder(io.LimitReader(r.Body, maxMemory)).Decode(v)
}

// DecodeXML decodes the request body into the provided struct and limits
// the request size via an io.LimitReader using the maxMemory param.
//
// The Content-Type e.g. "application/xml" and http method are not checked.
func DecodeXML(r *http.Request, maxMemory int64, v interface{}) (err error) {
	return xml.NewDecoder(io.LimitReader(r.Body, maxMemory)).Decode(v)
}

// Decode takes the request and attempts to discover it's content type via
// the http headers and then decode the request body into the provided struct.
// Example if header was "application/json" would decode using
// json.NewDecoder(io.LimitReader(r.Body, maxMemory)).Decode(v).
//
// Notice: At present it only supports to decode JSON and XML.
func Decode(r *http.Request, maxMemory int64, v interface{}) (err error) {
	ct := GetContentType(r)
	if f := decodeMap[ct]; f != nil {
		return f(r, maxMemory, v)
	}
	return fmt.Errorf("no decode of Content-Type %s", ct)
}

// SaveUploadedFile uploads the form file to the specific file dst.
func SaveUploadedFile(file *multipart.FileHeader, dst string) error {
	src, err := file.Open()
	if err != nil {
		return err
	}
	defer src.Close()

	out, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer out.Close()

	_, err = io.Copy(out, src)
	return err
}

// GetBody returns the body of the HTTP request.
func GetBody(r *http.Request) (body []byte, err error) {
	buf := bytes.NewBuffer(nil)
	if _, err = io.CopyN(buf, r.Body, r.ContentLength); err != nil {
		return
	}
	return buf.Bytes(), nil
}

// GetQuerys returns the query values by the key.
//
// If the key does not exist, return nil.
func GetQuerys(values url.Values, key string) []string {
	return values[key]
}

// GetQuery return the first query value by the key.
//
// If the key does not exist, return "".
func GetQuery(values url.Values, key string) string {
	if vs := GetQuerys(values, key); len(vs) > 0 {
		return vs[0]
	}
	return ""
}

// GetQueryDefault is the same as GetQuery, but return the default if the key
// does not exist.
func GetQueryDefault(values url.Values, key, _default string) string {
	if v := GetQuery(values, key); v != "" {
		return v
	}
	return _default
}

// GetQueryInt gets the first query value and converts it to int.
//
// If the key does not exist. return 0, not an error.
func GetQueryInt(values url.Values, key string) (int, error) {
	if v := GetQuery(values, key); v != "" {
		return types.ToInt(v)
	}
	return 0, nil
}

// GetQueryIntDefault is the same as GetQueryInt, but return the default
// if the key does not exist.
func GetQueryIntDefault(values url.Values, key string, _default int) (int, error) {
	if v, err := GetQueryInt(values, key); err != nil {
		return 0, err
	} else if v != 0 {
		return v, nil
	}
	return _default, nil
}

// GetQueryInt64 gets the first query value and converts it to int64.
//
// If the key does not exist. return 0, not an error.
func GetQueryInt64(values url.Values, key string) (int64, error) {
	if v := GetQuery(values, key); v != "" {
		return types.ToInt64(v)
	}
	return 0, nil
}

// GetQueryInt64Default is the same as GetQueryInt64, but return the default
// if the key does not exist.
func GetQueryInt64Default(values url.Values, key string, _default int64) (int64, error) {
	if v, err := GetQueryInt64(values, key); err != nil {
		return 0, err
	} else if v != 0 {
		return v, nil
	}
	return _default, nil
}

// GetQueryFloat64 gets the first query value and converts it to float64.
//
// If the key does not exist. return 0, not an error.
func GetQueryFloat64(values url.Values, key string) (float64, error) {
	if v := GetQuery(values, key); v != "" {
		return types.ToFloat64(v)
	}
	return 0, nil
}

// GetQueryFloat64Default is the same as GetQueryFloat64, but return the default
// if the key does not exist.
func GetQueryFloat64Default(values url.Values, key string, _default float64) (float64, error) {
	if v, err := GetQueryFloat64(values, key); err != nil {
		return 0, err
	} else if v != 0 {
		return v, nil
	}
	return _default, nil
}

// GetQueryBool gets the first query value and converts it to bool.
//
// For "t", "T", "1", "true", "True", "TRUE", it's true
// For "f", "F", "0", "false", "False", "FALSE", "", it's false.
//
// If the key does not exist. return false, not an error.
func GetQueryBool(values url.Values, key string) (bool, error) {
	if v := GetQuery(values, key); v != "" {
		return types.ToBool(v)
	}
	return false, nil
}

// GetQueryStringSlice returns the query string slice by the key.
//
// The format supports two kinds: (1) more than one key, (2) the value separated
// by the comma. For example:
//
//     // URL: /path/to?key1=v1&key1=v2&key1=v3,v4,v5&key2=value2
//     GetQueryStringSlice(r.URL.Query(), "key1") // => [v1, v2, v3, v4, v5]
//
// If the key does not exist or it value is empty, return an empty string slice.
func GetQueryStringSlice(values url.Values, key string) []string {
	if qs := GetQuerys(values, key); len(qs) > 0 {
		vs := make([]string, 0, len(qs)*2)
		for _, q := range qs {
			for _, v := range strings.Split(q, ",") {
				if v != "" {
					vs = append(vs, v)
				}
			}
		}
	}

	return []string{}
}
<|endoftext|>