<commit_before>/*
Copyright 2014 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// e2e.go runs the e2e test suite. No non-standard package dependencies; call with "go run".
package main

import (
	"flag"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

var (
	isup             = flag.Bool("isup", false, "Check to see if the e2e cluster is up, then exit.")
	build            = flag.Bool("build", false, "If true, build a new release. Otherwise, use whatever is there.")
	up               = flag.Bool("up", false, "If true, start the the e2e cluster. If cluster is already up, recreate it.")
	push             = flag.Bool("push", false, "If true, push to e2e cluster. Has no effect if -up is true.")
	pushup           = flag.Bool("pushup", false, "If true, push to e2e cluster if it's up, otherwise start the e2e cluster.")
	down             = flag.Bool("down", false, "If true, tear down the cluster before exiting.")
	test             = flag.Bool("test", false, "Run Ginkgo tests.")
	testArgs         = flag.String("test_args", "", "Space-separated list of arguments to pass to Ginkgo test runner.")
	root             = flag.String("root", absOrDie(filepath.Clean(filepath.Join(path.Base(os.Args[0]), ".."))), "Root directory of kubernetes repository.")
	verbose          = flag.Bool("v", false, "If true, print all command output.")
	checkVersionSkew = flag.Bool("check_version_skew", true, ""+
		"By default, verify that client and server have exact version match. "+
		"You can explicitly set to false if you're, e.g., testing client changes "+
		"for which the server version doesn't make a difference.")
	checkNodeCount = flag.Bool("check_node_count", true, ""+
		"By default, verify that the cluster has at least two nodes."+
		"You can explicitly set to false if you're, e.g., testing single-node clusters "+
		"for which the node count is supposed to be one.")

	ctlCmd = flag.String("ctl", "", "If nonempty, pass this as an argument, and call kubectl. Implies -v. (-test, -cfg, -ctl are mutually exclusive)")
)

const (
	minNodeCount = 2
)

func absOrDie(path string) string {
	out, err := filepath.Abs(path)
	if err != nil {
		panic(err)
	}
	return out
}

type TestResult struct {
	Pass int
	Fail int
}

type ResultsByTest map[string]TestResult

func main() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
	flag.Parse()

	if *isup {
		status := 1
		if IsUp() {
			status = 0
			log.Printf("Cluster is UP")
		} else {
			log.Printf("Cluster is DOWN")
		}
		os.Exit(status)
	}

	if *build {
		// The build-release script needs stdin to ask the user whether
		// it's OK to download the docker image.
		cmd := exec.Command(path.Join(*root, "hack/e2e-internal/build-release.sh"))
		cmd.Stdin = os.Stdin
		if !finishRunning("build-release", cmd) {
			log.Fatal("Error building. Aborting.")
		}
	}

	os.Setenv("KUBECTL", *root+`/cluster/kubectl.sh`+kubectlArgs())

	if *pushup {
		if IsUp() {
			log.Printf("e2e cluster is up, pushing.")
			*up = false
			*push = true
		} else {
			log.Printf("e2e cluster is down, creating.")
			*up = true
			*push = false
		}
	}
	if *up {
		if !Up() {
			log.Fatal("Error starting e2e cluster. Aborting.")
		}
	} else if *push {
		if !finishRunning("push", exec.Command(path.Join(*root, "hack/e2e-internal/e2e-push.sh"))) {
			log.Fatal("Error pushing e2e cluster. Aborting.")
		}
	}

	success := true
	switch {
	case *ctlCmd != "":
		ctlArgs := strings.Fields(*ctlCmd)
		os.Setenv("KUBE_CONFIG_FILE", "config-test.sh")
		success = finishRunning("'kubectl "+*ctlCmd+"'", exec.Command(path.Join(*root, "cluster/kubectl.sh"), ctlArgs...))
	case *test:
		success = Test()
	}

	if *down {
		TearDown()
	}

	if !success {
		os.Exit(1)
	}
}

func TearDown() bool {
	return finishRunning("teardown", exec.Command(path.Join(*root, "hack/e2e-internal/e2e-down.sh")))
}

// Up brings an e2e cluster up, recreating it if one is already running.
func Up() bool {
	if IsUp() {
		log.Printf("e2e cluster already running; will teardown")
		if res := TearDown(); !res {
			return false
		}
	}
	return finishRunning("up", exec.Command(path.Join(*root, "hack/e2e-internal/e2e-up.sh")))
}

// Ensure that the cluster is large engough to run the e2e tests.
func ValidateClusterSize() {
	// Check that there are at least minNodeCount nodes running
	cmd := exec.Command(path.Join(*root, "hack/e2e-internal/e2e-cluster-size.sh"))
	if *verbose {
		cmd.Stderr = os.Stderr
	}
	stdout, err := cmd.Output()
	if err != nil {
		log.Fatalf("Could not get nodes to validate cluster size (%s)", err)
	}

	numNodes, err := strconv.Atoi(strings.TrimSpace(string(stdout)))
	if err != nil {
		log.Fatalf("Could not count number of nodes to validate cluster size (%s)", err)
	}

	if numNodes < minNodeCount {
		log.Fatalf("Cluster size (%d) is too small to run e2e tests.  %d Nodes are required.", numNodes, minNodeCount)
	}
}

// Is the e2e cluster up?
func IsUp() bool {
	return finishRunning("get status", exec.Command(path.Join(*root, "hack/e2e-internal/e2e-status.sh")))
}

func Test() bool {
	if !IsUp() {
		log.Fatal("Testing requested, but e2e cluster not up!")
	}

	if *checkNodeCount {
		ValidateClusterSize()
	}

	return finishRunning("Ginkgo tests", exec.Command(filepath.Join(*root, "hack/ginkgo-e2e.sh"), strings.Fields(*testArgs)...))
}

func finishRunning(stepName string, cmd *exec.Cmd) bool {
	if *verbose {
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
	}
	log.Printf("Running: %v", stepName)
	defer func(start time.Time) {
		log.Printf("Step '%s' finished in %s", stepName, time.Since(start))
	}(time.Now())

	if err := cmd.Run(); err != nil {
		log.Printf("Error running %v: %v", stepName, err)
		return false
	}
	return true
}

// returns either "", or a list of args intended for appending with the
// kubectl command (beginning with a space).
func kubectlArgs() string {
	if *checkVersionSkew {
		return " --match-server-version"
	}
	return ""
}
<commit_msg>Federation build and e2e test integration<commit_after>/*
Copyright 2014 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// e2e.go runs the e2e test suite. No non-standard package dependencies; call with "go run".
package main

import (
	"flag"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

var (
	isup             = flag.Bool("isup", false, "Check to see if the e2e cluster is up, then exit.")
	build            = flag.Bool("build", false, "If true, build a new release. Otherwise, use whatever is there.")
	up               = flag.Bool("up", false, "If true, start the the e2e cluster. If cluster is already up, recreate it.")
	push             = flag.Bool("push", false, "If true, push to e2e cluster. Has no effect if -up is true.")
	pushup           = flag.Bool("pushup", false, "If true, push to e2e cluster if it's up, otherwise start the e2e cluster.")
	down             = flag.Bool("down", false, "If true, tear down the cluster before exiting.")
	test             = flag.Bool("test", false, "Run Ginkgo tests.")
	testArgs         = flag.String("test_args", "", "Space-separated list of arguments to pass to Ginkgo test runner.")
	root             = flag.String("root", absOrDie(filepath.Clean(filepath.Join(path.Base(os.Args[0]), ".."))), "Root directory of kubernetes repository.")
	verbose          = flag.Bool("v", false, "If true, print all command output.")
	checkVersionSkew = flag.Bool("check_version_skew", true, ""+
		"By default, verify that client and server have exact version match. "+
		"You can explicitly set to false if you're, e.g., testing client changes "+
		"for which the server version doesn't make a difference.")
	checkNodeCount = flag.Bool("check_node_count", true, ""+
		"By default, verify that the cluster has at least two nodes."+
		"You can explicitly set to false if you're, e.g., testing single-node clusters "+
		"for which the node count is supposed to be one.")

	ctlCmd = flag.String("ctl", "", "If nonempty, pass this as an argument, and call kubectl. Implies -v. (-test, -cfg, -ctl are mutually exclusive)")
)

const (
	minNodeCount = 2
)

func absOrDie(path string) string {
	out, err := filepath.Abs(path)
	if err != nil {
		panic(err)
	}
	return out
}

type TestResult struct {
	Pass int
	Fail int
}

type ResultsByTest map[string]TestResult

func main() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
	flag.Parse()

	if *isup {
		status := 1
		if IsUp() {
			status = 0
			log.Printf("Cluster is UP")
		} else {
			log.Printf("Cluster is DOWN")
		}
		os.Exit(status)
	}

	if *build {
		// The build-release script needs stdin to ask the user whether
		// it's OK to download the docker image.
		cmd := exec.Command(path.Join(*root, "hack/e2e-internal/build-release.sh"))
		cmd.Stdin = os.Stdin
		if !finishRunning("build-release", cmd) {
			log.Fatal("Error building. Aborting.")
		}
	}

	os.Setenv("KUBECTL", *root+`/cluster/kubectl.sh`+kubectlArgs())

	if *pushup {
		if IsUp() {
			log.Printf("e2e cluster is up, pushing.")
			*up = false
			*push = true
		} else {
			log.Printf("e2e cluster is down, creating.")
			*up = true
			*push = false
		}
	}
	if *up {
		if !Up() {
			log.Fatal("Error starting e2e cluster. Aborting.")
		}
	} else if *push {
		if !finishRunning("push", exec.Command(path.Join(*root, "hack/e2e-internal/e2e-push.sh"))) {
			log.Fatal("Error pushing e2e cluster. Aborting.")
		}
	}

	success := true
	switch {
	case *ctlCmd != "":
		ctlArgs := strings.Fields(*ctlCmd)
		os.Setenv("KUBE_CONFIG_FILE", "config-test.sh")
		success = finishRunning("'kubectl "+*ctlCmd+"'", exec.Command(path.Join(*root, "cluster/kubectl.sh"), ctlArgs...))
	case *test:
		success = Test()
	}

	if *down {
		TearDown()
	}

	if !success {
		os.Exit(1)
	}
}

func TearDown() bool {
	return finishRunning("teardown", exec.Command(path.Join(*root, "hack/e2e-internal/e2e-down.sh")))
}

// Up brings an e2e cluster up, recreating it if one is already running.
func Up() bool {
	if IsUp() {
		log.Printf("e2e cluster already running; will teardown")
		if res := TearDown(); !res {
			return false
		}
	}
	return finishRunning("up", exec.Command(path.Join(*root, "hack/e2e-internal/e2e-up.sh")))
}

// Ensure that the cluster is large engough to run the e2e tests.
func ValidateClusterSize() {
	if os.Getenv("FEDERATION") == "true" {
		//TODO(colhom): federated equivalent of  ValidateClusterSize
		return
	}
	// Check that there are at least minNodeCount nodes running
	cmd := exec.Command(path.Join(*root, "hack/e2e-internal/e2e-cluster-size.sh"))
	if *verbose {
		cmd.Stderr = os.Stderr
	}
	stdout, err := cmd.Output()
	if err != nil {
		log.Fatalf("Could not get nodes to validate cluster size (%s)", err)
	}

	numNodes, err := strconv.Atoi(strings.TrimSpace(string(stdout)))
	if err != nil {
		log.Fatalf("Could not count number of nodes to validate cluster size (%s)", err)
	}

	if numNodes < minNodeCount {
		log.Fatalf("Cluster size (%d) is too small to run e2e tests.  %d Nodes are required.", numNodes, minNodeCount)
	}
}

// Is the e2e cluster up?
func IsUp() bool {
	return finishRunning("get status", exec.Command(path.Join(*root, "hack/e2e-internal/e2e-status.sh")))
}

func Test() bool {
	if !IsUp() {
		log.Fatal("Testing requested, but e2e cluster not up!")
	}

	if *checkNodeCount {
		ValidateClusterSize()
	}

	if os.Getenv("FEDERATION") == "" {
		return finishRunning("Ginkgo tests", exec.Command(filepath.Join(*root, "hack/ginkgo-e2e.sh"), strings.Fields(*testArgs)...))
	} else {

		if *testArgs == "" {
			*testArgs = "--ginkgo.focus=\\[Feature:Federation\\]"
		}
		return finishRunning("Federated Ginkgo tests", exec.Command(filepath.Join(*root, "hack/federated-ginkgo-e2e.sh"), strings.Fields(*testArgs)...))
	}
}

func finishRunning(stepName string, cmd *exec.Cmd) bool {
	if *verbose {
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
	}
	log.Printf("Running: %v", stepName)
	defer func(start time.Time) {
		log.Printf("Step '%s' finished in %s", stepName, time.Since(start))
	}(time.Now())

	if err := cmd.Run(); err != nil {
		log.Printf("Error running %v: %v", stepName, err)
		return false
	}
	return true
}

// returns either "", or a list of args intended for appending with the
// kubectl command (beginning with a space).
func kubectlArgs() string {
	args := []string{""}
	if *checkVersionSkew {
		args = append(args, "--match-server-version")
	}
	return strings.Join(args, " ")
}
<|endoftext|>


<commit_before>// Package hadfield implements a basic subcommand system to complement the
// standard library's 'flag' package. To use, in your 'main' function you must
// call the Run function with a list of Commands and Templates.
package hadfield

import (
	"os"
	"fmt"
	"flag"
	"log"
)

// Run executes the correct subcommand. It delegates the subcommand 'help' to
// the help function below. If it can not find a subcommand with the name given
// it exits with an error.
func Run(cmds Commands, templates Templates) {
	flag.Usage = func() { usage(templates, cmds) }
	flag.Parse()
	log.SetFlags(0)

	args := flag.Args()

	if len(args) < 1 {
		usage(templates, cmds)
	}

	if args[0] == "help" {
		help(templates, cmds, args[1:])
		return
	}

	for _, cmd := range cmds {
		if cmd.Name() == args[0] && cmd.Callable() {
			cmd.Call(cmd, templates, args)
		}
	}

	fmt.Fprintf(os.Stderr, "unknown subcommand %q\n", args[0])
	os.Exit(2)
}

// usage prints a usage message and then exits.
func usage(templates Templates, cmds Commands) {
	templates.Usage.Render(os.Stderr, cmds)
	os.Exit(0)
}

// help controls the "help" pseudo-command. It will print the usage message if
// given an empty list of arguments. It prints the associated help text if given
// a signle argument. And otherwise exists with an error.
func help(templates Templates, cmds Commands, args []string) {
	if len(args) == 0 {
		templates.Usage.Render(os.Stdout, cmds.Data())
		return
	}
	if len(args) != 1 {
		fmt.Fprintf(os.Stderr, "help given too many arguments\n")
		os.Exit(2)
	}

	arg := args[0]

	for _, cmd := range cmds {
		if cmd.Name() == arg {
			templates.Help.Render(os.Stdout, cmd)
			return
		}
	}

	fmt.Fprintf(os.Stderr, "Unknown help topic %#q\n", arg)
	os.Exit(2)
}
<commit_msg>Made sure to always call Data() when rendering<commit_after>// Package hadfield implements a basic subcommand system to complement the
// standard library's 'flag' package. To use, in your 'main' function you must
// call the Run function with a list of Commands and Templates.
package hadfield

import (
	"os"
	"fmt"
	"flag"
	"log"
)

// Run executes the correct subcommand. It delegates the subcommand 'help' to
// the help function below. If it can not find a subcommand with the name given
// it exits with an error.
func Run(cmds Commands, templates Templates) {
	flag.Usage = func() { usage(templates, cmds) }
	flag.Parse()
	log.SetFlags(0)

	args := flag.Args()

	if len(args) < 1 {
		usage(templates, cmds)
	}

	if args[0] == "help" {
		help(templates, cmds, args[1:])
		return
	}

	for _, cmd := range cmds {
		if cmd.Name() == args[0] && cmd.Callable() {
			cmd.Call(cmd, templates, args)
		}
	}

	fmt.Fprintf(os.Stderr, "unknown subcommand %q\n", args[0])
	os.Exit(2)
}

// usage prints a usage message and then exits.
func usage(templates Templates, cmds Commands) {
	templates.Usage.Render(os.Stderr, cmds.Data())
	os.Exit(0)
}

// help controls the "help" pseudo-command. It will print the usage message if
// given an empty list of arguments. It prints the associated help text if given
// a signle argument. And otherwise exists with an error.
func help(templates Templates, cmds Commands, args []string) {
	if len(args) == 0 {
		templates.Usage.Render(os.Stdout, cmds.Data())
		return
	}
	if len(args) != 1 {
		fmt.Fprintf(os.Stderr, "help given too many arguments\n")
		os.Exit(2)
	}

	arg := args[0]

	for _, cmd := range cmds {
		if cmd.Name() == arg {
			templates.Help.Render(os.Stdout, cmd.Data())
			return
		}
	}

	fmt.Fprintf(os.Stderr, "Unknown help topic %#q\n", arg)
	os.Exit(2)
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 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 config

import (
	"bytes"
	"fmt"
	"reflect"
	"strconv"

	"github.com/go-validator/validator"
)

const (
	bucketPrimitive = iota
	bucketArray
	bucketObject
	bucketMap
	bucketSlice
)

type fieldInfo struct {
	FieldName    string
	DefaultValue string
	Required     bool
}

func getFieldInfo(field reflect.StructField) fieldInfo {
	return fieldInfo{
		FieldName:    field.Tag.Get("yaml"),
		DefaultValue: field.Tag.Get("default"),
	}
}

func derefType(t reflect.Type) reflect.Type {
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}

	return t
}

func convertValueFromStruct(value interface{}, targetType reflect.Type, fieldType reflect.Type, fieldValue reflect.Value) error {
	// The fieldType is probably a custom type here. We will try and set the fieldValue by
	// the custom type
	// TODO: refactor switch cases into isType functions
	// TODO(alsam) Fix overflows/negatives for unsigned types...
	switch fieldType.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		fieldValue.SetInt(int64(value.(int)))
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		fieldValue.SetUint(uint64(value.(int)))
	case reflect.Float32, reflect.Float64:
		fieldValue.SetFloat(value.(float64))
	case reflect.Bool:
		fieldValue.SetBool(value.(bool))
	case reflect.String:
		fieldValue.SetString(value.(string))
	default:
		return fmt.Errorf("can't convert %v to %v", reflect.TypeOf(value).String(), targetType)
	}
	return nil
}

func getBucket(t reflect.Type) int {
	kind := t.Kind()
	if kind == reflect.Ptr {
		kind = t.Elem().Kind()
	}

	switch kind {
	case reflect.Chan:
		fallthrough
	case reflect.Interface:
		fallthrough
	case reflect.Func:
		fallthrough
	case reflect.Map:
		return bucketMap
	case reflect.Array:
		return bucketArray
	case reflect.Slice:
		return bucketSlice
	case reflect.Struct:
		return bucketObject
	}
	return bucketPrimitive
}

type decoder struct {
	*Value
	m map[interface{}]struct{}
}

func (d *decoder) getGlobalProvider() Provider {
	if d.root == nil {
		return d.provider
	}

	return d.root
}

// Sets value to a primitive type.
func (d *decoder) scalar(childKey string, value reflect.Value, def string) error {
	valueType := value.Type()
	global := d.getGlobalProvider()
	var val interface{}

	if valueType.Kind() == reflect.Ptr {
		if v1 := global.Get(childKey); v1.HasValue() {
			val = v1.Value()
			if val != nil {
				// We cannot assign reflect.ValueOf(Val) to it as is to value.
				// value is a pointer, which currently points to non address.
				// We need to set a new reflect.Value with field type same as the field we are populating
				// before assigning the value (val) parsed from yaml
				if value.IsNil() {
					value.Set(reflect.New(valueType.Elem()))
				}

				return d.scalar(childKey, value.Elem(), def)
			}
		}

		return nil
	}

	// For primitive values, just get the value and set it into the field
	if v2 := global.Get(childKey); v2.HasValue() {
		val = v2.Value()
	} else if def != "" {
		val = def
	}

	if val != nil {
		// First try to convert primitive type values, if convertValue wasn't able
		// to convert to primitive,try converting the value as a struct value
		if ret, err := convertValue(val, valueType); ret != nil {
			if err != nil {
				return err
			}

			value.Set(reflect.ValueOf(ret))
		} else {
			return convertValueFromStruct(val, value.Type(), valueType, value)
		}
	}

	return nil
}

// Set value for a sequence type
// TODO(alsam) We stop populating sequence on a first nil value. Can we do better?
func (d *decoder) sequence(childKey string, value reflect.Value) error {
	valueType := value.Type()
	global := d.getGlobalProvider()
	destSlice := reflect.MakeSlice(valueType, 0, 4)

	// start looking for child values.
	elementType := derefType(valueType).Elem()
	childKey += _separator

	for ai := 0; ; ai++ {
		arrayKey := childKey + strconv.Itoa(ai)

		// Iterate until we find first missing value.
		if v2 := global.Get(arrayKey); v2.HasValue() {
			val := reflect.New(elementType).Elem()

			// Unmarshal current element.
			if err := d.unmarshal(arrayKey, val, ""); err != nil {
				return err
			}

			// Append element to the slice
			if destSlice.Len() <= ai {
				destSlice = reflect.Append(destSlice, reflect.Zero(elementType))
			}

			destSlice.Index(ai).Set(val)
		} else {
			break
		}
	}

	if destSlice.Len() > 0 {
		value.Set(destSlice)
	}

	return nil
}

// Set value for the array type
func (d *decoder) array(childKey string, value reflect.Value) error {
	valueType := value.Type()
	global := d.getGlobalProvider()

	// start looking for child values.
	elementType := derefType(valueType).Elem()
	childKey += _separator

	for ai := 0; ai < value.Len(); ai++ {
		arrayKey := childKey + strconv.Itoa(ai)

		// Iterate until we find first missing value.
		if v2 := global.Get(arrayKey); v2.HasValue() {
			val := reflect.New(elementType).Elem()

			// Unmarshal current element.
			if err := d.unmarshal(arrayKey, val, ""); err != nil {
				return err
			}

			value.Index(ai).Set(val)
		} else if value.Index(ai).Kind() == reflect.Struct {
			if err := d.valueStruct(arrayKey, value.Index(ai).Addr().Interface()); err != nil {
				return err
			}
		}
	}

	return nil
}

// Sets value to a map type.
func (d *decoder) mapping(childKey string, value reflect.Value, def string) error {
	valueType := value.Type()
	global := d.getGlobalProvider()

	v := global.Get(childKey)
	if !v.HasValue() || v.Value() == nil {
		return nil
	}

	val := v.Value()

	// We fallthrough for interface to maps
	if valueType.Kind() == reflect.Interface {
		value.Set(reflect.ValueOf(val))
		return nil
	}

	destMap := reflect.ValueOf(reflect.MakeMap(valueType).Interface())

	// child yamlNode parsed from yaml file is of type map[interface{}]interface{}
	// type casting here makes sure that we are iterating over a parsed map.
	if v, ok := val.(map[interface{}]interface{}); ok {
		childKey += _separator
		for key := range v {
			mapKey := childKey + fmt.Sprintf("%v", key)
			itemValue := reflect.New(valueType.Elem()).Elem()

			// Try to unmarshal value and save it in the map.
			if err := d.unmarshal(mapKey, itemValue, def); err != nil {
				return err
			}

			destMap.SetMapIndex(reflect.ValueOf(key), itemValue)
		}

		value.Set(destMap)
	}

	return nil
}

// Sets value to an object type.
func (d *decoder) object(childKey string, value reflect.Value) error {
	valueType := value.Type()
	global := d.getGlobalProvider()

	v2 := global.Get(childKey)

	if !v2.HasValue() && valueType.Kind() == reflect.Ptr {
		// in this case we will keep the pointer value as not defined.
		return nil
	}

	if !value.CanSet() {
		return nil
	}

	if valueType.Kind() != reflect.Ptr {
		value = value.Addr()
	}

	if value.IsNil() {
		tmp := reflect.New(value.Type().Elem())
		value.Set(tmp)
	}

	return d.valueStruct(childKey, value.Interface())
}

// Walk through the struct and start asking the providers for values at each key.
//
// - for individual values, we terminate
// - for array values, we start asking for indexes
// - for object values, we recurse.
func (d *decoder) valueStruct(key string, target interface{}) error {
	tarGet := reflect.Indirect(reflect.ValueOf(target))
	targetType := tarGet.Type()
	for i := 0; i < targetType.NumField(); i++ {
		field := targetType.Field(i)

		// Check for the private field
		if field.PkgPath != "" && !field.Anonymous {
			continue
		}

		fieldName := field.Name
		fieldInfo := getFieldInfo(field)
		if fieldInfo.FieldName != "" {
			fieldName = fieldInfo.FieldName
		}

		if key != "" {
			fieldName = key + _separator + fieldName
		}

		fieldValue := tarGet.Field(i)
		if fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil() {
			fieldValue.Set(reflect.New(fieldValue.Type()).Elem())
		}

		if err := d.unmarshal(fieldName, fieldValue, getFieldInfo(field).DefaultValue); err != nil {
			return err
		}
	}

	return validator.Validate(target)
}

// Dispatch un-marshalling functions based on the value type.
func (d *decoder) unmarshal(name string, value reflect.Value, def string) error {
	if value.Type().Comparable() {
		val := value.Interface()
		kind := value.Kind()
		if _, ok := d.m[val]; ok {
			if kind == reflect.Ptr && !value.IsNil() {
				buf := &bytes.Buffer{}
				for k := range d.m {
					fmt.Fprintf(buf, "%+v -> ", k)
				}

				fmt.Fprintf(buf, "%+v", value.Interface())
				return fmt.Errorf("cycles detected: %s", buf.String())
			}
		}

		d.m[val] = struct{}{}
	}

	switch getBucket(value.Type()) {
	case bucketPrimitive:
		return d.scalar(name, value, def)
	case bucketObject:
		return d.object(name, value)
	case bucketArray:
		return d.array(name, value)
	case bucketSlice:
		return d.sequence(name, value)
	case bucketMap:
		return d.mapping(name, value, def)
	}

	return nil
}
<commit_msg>Remove getBucket function to clean up unmarshal method (#394)<commit_after>// Copyright (c) 2017 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 config

import (
	"bytes"
	"fmt"
	"reflect"
	"strconv"

	"github.com/go-validator/validator"
)

type fieldInfo struct {
	FieldName    string
	DefaultValue string
	Required     bool
}

func getFieldInfo(field reflect.StructField) fieldInfo {
	return fieldInfo{
		FieldName:    field.Tag.Get("yaml"),
		DefaultValue: field.Tag.Get("default"),
	}
}

func derefType(t reflect.Type) reflect.Type {
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}

	return t
}

func convertValueFromStruct(value interface{}, targetType reflect.Type, fieldType reflect.Type, fieldValue reflect.Value) error {
	// The fieldType is probably a custom type here. We will try and set the fieldValue by
	// the custom type
	// TODO: refactor switch cases into isType functions
	// TODO(alsam) Fix overflows/negatives for unsigned types...
	switch fieldType.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		fieldValue.SetInt(int64(value.(int)))
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		fieldValue.SetUint(uint64(value.(int)))
	case reflect.Float32, reflect.Float64:
		fieldValue.SetFloat(value.(float64))
	case reflect.Bool:
		fieldValue.SetBool(value.(bool))
	case reflect.String:
		fieldValue.SetString(value.(string))
	default:
		return fmt.Errorf("can't convert %v to %v", reflect.TypeOf(value).String(), targetType)
	}
	return nil
}

type decoder struct {
	*Value
	m map[interface{}]struct{}
}

func (d *decoder) getGlobalProvider() Provider {
	if d.root == nil {
		return d.provider
	}

	return d.root
}

// Sets value to a primitive type.
func (d *decoder) scalar(childKey string, value reflect.Value, def string) error {
	valueType := value.Type()
	global := d.getGlobalProvider()
	var val interface{}

	// For primitive values, just get the value and set it into the field
	if v2 := global.Get(childKey); v2.HasValue() {
		val = v2.Value()
	} else if def != "" {
		val = def
	}

	if val != nil {
		// First try to convert primitive type values, if convertValue wasn't able
		// to convert to primitive,try converting the value as a struct value
		if ret, err := convertValue(val, valueType); ret != nil {
			if err != nil {
				return err
			}

			value.Set(reflect.ValueOf(ret))
		} else {
			return convertValueFromStruct(val, value.Type(), valueType, value)
		}
	}

	return nil
}

// Set value for a sequence type
// TODO(alsam) We stop populating sequence on a first nil value. Can we do better?
func (d *decoder) sequence(childKey string, value reflect.Value) error {
	valueType := value.Type()
	global := d.getGlobalProvider()
	destSlice := reflect.MakeSlice(valueType, 0, 4)

	// start looking for child values.
	elementType := derefType(valueType).Elem()
	childKey += _separator

	for ai := 0; ; ai++ {
		arrayKey := childKey + strconv.Itoa(ai)

		// Iterate until we find first missing value.
		if v2 := global.Get(arrayKey); v2.HasValue() {
			val := reflect.New(elementType).Elem()

			// Unmarshal current element.
			if err := d.unmarshal(arrayKey, val, ""); err != nil {
				return err
			}

			// Append element to the slice
			if destSlice.Len() <= ai {
				destSlice = reflect.Append(destSlice, reflect.Zero(elementType))
			}

			destSlice.Index(ai).Set(val)
		} else {
			break
		}
	}

	if destSlice.Len() > 0 {
		value.Set(destSlice)
	}

	return nil
}

// Set value for the array type
func (d *decoder) array(childKey string, value reflect.Value) error {
	valueType := value.Type()
	global := d.getGlobalProvider()

	// start looking for child values.
	elementType := derefType(valueType).Elem()
	childKey += _separator

	for ai := 0; ai < value.Len(); ai++ {
		arrayKey := childKey + strconv.Itoa(ai)

		// Iterate until we find first missing value.
		if v2 := global.Get(arrayKey); v2.HasValue() {
			val := reflect.New(elementType).Elem()

			// Unmarshal current element.
			if err := d.unmarshal(arrayKey, val, ""); err != nil {
				return err
			}

			value.Index(ai).Set(val)
		} else if value.Index(ai).Kind() == reflect.Struct {
			if err := d.valueStruct(arrayKey, value.Index(ai).Addr().Interface()); err != nil {
				return err
			}
		}
	}

	return nil
}

// Sets value to a map type.
func (d *decoder) mapping(childKey string, value reflect.Value, def string) error {
	valueType := value.Type()
	global := d.getGlobalProvider()

	v := global.Get(childKey)
	if !v.HasValue() || v.Value() == nil {
		return nil
	}

	val := v.Value()

	// We fallthrough for interface to maps
	if valueType.Kind() == reflect.Interface {
		value.Set(reflect.ValueOf(val))
		return nil
	}

	destMap := reflect.ValueOf(reflect.MakeMap(valueType).Interface())

	// child yamlNode parsed from yaml file is of type map[interface{}]interface{}
	// type casting here makes sure that we are iterating over a parsed map.
	if v, ok := val.(map[interface{}]interface{}); ok {
		childKey += _separator
		for key := range v {
			mapKey := childKey + fmt.Sprintf("%v", key)
			itemValue := reflect.New(valueType.Elem()).Elem()

			// Try to unmarshal value and save it in the map.
			if err := d.unmarshal(mapKey, itemValue, def); err != nil {
				return err
			}

			destMap.SetMapIndex(reflect.ValueOf(key), itemValue)
		}

		value.Set(destMap)
	}

	return nil
}

// Sets value to an object type.
func (d *decoder) object(childKey string, value reflect.Value) error {
	value = value.Addr()

	if value.IsNil() {
		tmp := reflect.New(value.Type().Elem())
		value.Set(tmp)
	}

	return d.valueStruct(childKey, value.Interface())
}

// Walk through the struct and start asking the providers for values at each key.
//
// - for individual values, we terminate
// - for array values, we start asking for indexes
// - for object values, we recurse.
func (d *decoder) valueStruct(key string, target interface{}) error {
	tarGet := reflect.Indirect(reflect.ValueOf(target))
	targetType := tarGet.Type()
	for i := 0; i < targetType.NumField(); i++ {
		field := targetType.Field(i)

		// Check for the private field
		if field.PkgPath != "" && !field.Anonymous {
			continue
		}

		fieldName := field.Name
		fieldInfo := getFieldInfo(field)
		if fieldInfo.FieldName != "" {
			fieldName = fieldInfo.FieldName
		}

		if key != "" {
			fieldName = key + _separator + fieldName
		}

		fieldValue := tarGet.Field(i)
		if fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil() {
			fieldValue.Set(reflect.New(fieldValue.Type()).Elem())
		}

		if err := d.unmarshal(fieldName, fieldValue, getFieldInfo(field).DefaultValue); err != nil {
			return err
		}
	}

	return validator.Validate(target)
}

// If there is no value with name - leave it nil, otherwise allocate memory and set the value.
func (d *decoder) pointer(name string, value reflect.Value, def string) error {
	if !d.getGlobalProvider().Get(name).HasValue() {
		return nil
	}

	if value.IsNil() {
		value.Set(reflect.New(value.Type().Elem()))
	}

	return d.unmarshal(name, value.Elem(), def)
}

// Check if a value is a pointer and decoder set it before.
// TODO(alsam) print only elements in the loop, not all elements.
func (d *decoder) checkCycles(value reflect.Value) error {
	if value.Type().Comparable() {
		val := value.Interface()
		kind := value.Kind()
		if _, ok := d.m[val]; ok {
			if kind == reflect.Ptr && !value.IsNil() {
				buf := &bytes.Buffer{}
				for k := range d.m {
					fmt.Fprintf(buf, "%+v -> ", k)
				}

				fmt.Fprintf(buf, "%+v", value.Interface())
				return fmt.Errorf("cycles detected: %s", buf.String())
			}
		}

		d.m[val] = struct{}{}
	}

	return nil
}

// Dispatch un-marshalling functions based on the value type.
func (d *decoder) unmarshal(name string, value reflect.Value, def string) error {
	if err := d.checkCycles(value); err != nil {
		return err
	}

	switch value.Kind() {
	case reflect.Invalid:
		return fmt.Errorf("invalid value type for key %s", name)
	case reflect.Ptr:
		return d.pointer(name, value, def)
	case reflect.Struct:
		return d.object(name, value)
	case reflect.Array:
		return d.array(name, value)
	case reflect.Slice:
		return d.sequence(name, value)
	case reflect.Map:
		return d.mapping(name, value, def)
	case reflect.Interface:
		return d.mapping(name, value, def)
	case reflect.Chan:
		return nil
	case reflect.Func:
		return nil
	default:
		return d.scalar(name, value, def)
	}
}
<|endoftext|>


<commit_before>/*
	Copyright (c) 2016 Christopher Young
	Distributable under the terms of The "BSD New"" License
	that can be found in the LICENSE file, herein included
	as part of this header.

	flowfast.go: Counts inputs from ADS1115, sends over a websocket.
*/

// A0 = -
// A1 = +
// 5.6K pullup on WHITE lead for FT-60.

package main

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"github.com/kidoman/embd"
	_ "github.com/kidoman/embd/host/all"
	_ "github.com/mattn/go-sqlite3"
	"github.com/op/go-logging"
	"github.com/paulbellamy/ratecounter"
	"golang.org/x/net/websocket"
	"math"
	"net/http"
	"os"
	"sync"
	"time"
)

const (
	GALLONS_PER_CLICK = 1 / 68000.0 // FT-60 K-factor: 68,000.
	SQLITE_DB_FILE    = "./test.db"
	LISTEN_ADDR       = ":8081"
)

type FlowStats struct {
	EvaluatedTime time.Time // Time when the counters were evaluated.
	Flow_Total    float64
	// units=gallons.
	Flow_LastSecond   float64
	Flow_LastMinute   float64
	Flow_MaxPerMinute float64
	// units=GPH.
	Flow_LastSecond_GPH      float64
	Flow_LastMinute_GPH      float64
	Flow_MaxPerMinute_GPH    float64
	Flow_LastHour_Actual_GPH float64
	// Rate counters.
	flow_total_raw   uint64
	flow_last_second *ratecounter.RateCounter
	flow_last_minute *ratecounter.RateCounter
	flow_last_hour   *ratecounter.RateCounter

	mu *sync.Mutex
}

type fuel_log struct {
	log_date_start time.Time
	log_date_end   time.Time
	flow           float64
}

var flow FlowStats

var logger = logging.MustGetLogger("flowfast")

func statusWebSocket(conn *websocket.Conn) {
	ticker := time.NewTicker(1 * time.Second)

	last_update := time.Now()
	for {
		<-ticker.C

		flow.mu.Lock()
		updateJSON, _ := json.Marshal(&flow) //TODO.
		flow.mu.Unlock()

		conn.Write(updateJSON)
		t := time.Now()
		logChan <- fuel_log{log_date_start: last_update, log_date_end: t, flow: flow.Flow_LastSecond}
		last_update = t
	}
}

func startWebListener() {
	http.HandleFunc("/",
		func(w http.ResponseWriter, req *http.Request) {
			s := websocket.Server{
				Handler: websocket.Handler(statusWebSocket)}
			s.ServeHTTP(w, req)
		})

	logger.Debugf("listening on %s.\n", LISTEN_ADDR)
	err := http.ListenAndServe(LISTEN_ADDR, nil)
	if err != nil {
		logger.Errorf("can't listen on socket: %s\n", err.Error())
		os.Exit(-1)
	}
}

var i2cbus embd.I2CBus

var inputChan chan float64

// Re-calculate stats every second.
func statsCalculator() {
	ticker := time.NewTicker(1 * time.Second)
	for {
		<-ticker.C
		flow.mu.Lock()

		flow.EvaluatedTime = time.Now()

		flow.Flow_Total = float64(flow.flow_total_raw) * GALLONS_PER_CLICK
		flow.Flow_LastSecond = float64(flow.flow_last_second.Rate()) * GALLONS_PER_CLICK
		flow.Flow_LastMinute = float64(flow.flow_last_minute.Rate()) * GALLONS_PER_CLICK
		flow.Flow_LastHour_Actual_GPH = float64(flow.flow_last_hour.Rate()) * GALLONS_PER_CLICK

		// Calculate maximums.
		if flow.Flow_LastMinute > flow.Flow_MaxPerMinute {
			flow.Flow_MaxPerMinute = flow.Flow_LastMinute
			flow.Flow_MaxPerMinute_GPH = flow.Flow_MaxPerMinute * float64(60.0) // Extrapolate.
		}

		// Extrapolate "GPH" numbers for the Second and Minute flow values.
		flow.Flow_LastSecond_GPH = flow.Flow_LastSecond * float64(3600.0)
		flow.Flow_LastMinute_GPH = flow.Flow_LastMinute * float64(60.0)

		flow.mu.Unlock()
	}
}

func processInput() {

	inputHigh := false

	for {
		mv := <-inputChan

		countCondition := false

		// 0V low.
		if math.Abs(mv-0.0) <= float64(1000.0) { // Low.
			inputHigh = false
		}

		// 5V high.
		if !inputHigh && math.Abs(mv-5000.0) <= float64(1000.0) { // High.
			inputHigh = true
			countCondition = true
			logger.Debugf("count! %f\n", mv)
		}

		if countCondition {
			flow.flow_total_raw++
			flow.flow_last_second.Incr(1)
			flow.flow_last_minute.Incr(1)
			flow.flow_last_hour.Incr(1)
		}
	}
}

// Ref: https://github.com/jrowberg/i2cdevlib/blob/master/Arduino/ADS1115/ADS1115.cpp
// Ref: https://github.com/jrowberg/i2cdevlib/blob/master/Arduino/ADS1115/ADS1115.h

func writeBitsW(bus embd.I2CBus, reg byte, bit_start, val_len uint, val uint16) {
	cur_val, err := bus.ReadWordFromReg(0x48, reg)
	if err != nil {
		logger.Errorf("ReadWordFromReg(): %s\n", err.Error())
		return
	}

	mask := uint16(((1 << val_len) - 1) << (bit_start - val_len + 1))
	val = val << (bit_start - val_len + 1)
	val &= mask
	cur_val &= ^(mask)
	cur_val |= val
	bus.WriteWordToReg(0x48, reg, cur_val)
}

func readADS1115() {
	inputChan = make(chan float64, 1024)

	go processInput()
	go statsCalculator()

	i2cbus = embd.NewI2CBus(1) //TODO: error checking.

	// Set up the device. ADS1115::setRate().
	writeBitsW(i2cbus, 0x01, 7, 3, 0x07)  // 3300 samples/sec.
	writeBitsW(i2cbus, 0x01, 8, 1, 0)     // MODE_CONTINUOUS.
	writeBitsW(i2cbus, 0x01, 11, 3, 0x00) // +/-6.144V. 3 mV div.
	writeBitsW(i2cbus, 0x01, 14, 3, 0x00) // setMultiplexer(MUX_P0_N1).

	for {
		v, err := i2cbus.ReadWordFromReg(0x48, 0x00)

		cv := int16(v >> 4)
		if v>>15 != 0 {
			cv = cv - 0xFFF
		}
		mv := float64(cv) * float64(3.0) // units=mV.
		if err != nil {
			logger.Errorf("ReadWordFromReg(): %s\n", err.Error())
		}

		inputChan <- mv
		time.Sleep(1 * time.Millisecond) // Oversampling.
	}

	return
}

var logChan chan fuel_log

// Logs fuel data to an SQLite database.
func dbLogger() {
	logChan = make(chan fuel_log, 1024)

	// Check if we need to create a new database.
	createDatabase := false
	if _, err := os.Stat(SQLITE_DB_FILE); os.IsNotExist(err) {
		createDatabase = true
		logger.Debugf("creating new database '%s'.\n", SQLITE_DB_FILE)
	}

	db, err := sql.Open("sqlite3", SQLITE_DB_FILE)
	if err != nil {
		logger.Errorf("sql.Open(): %s\n", err.Error())
	}
	defer db.Close()

	if createDatabase {
		createSmt := `
			CREATE TABLE fuel_flow (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, log_date_start INTEGER, log_date_end INTEGER, flow REAL);
		`

		_, err = db.Exec(createSmt)
		if err != nil {
			logger.Errorf("%q: %s\n", err, createSmt)
			return
		}
	}

	for {
		f := <-logChan
		//FIXME: Timestamps here are a hack.
		q := fmt.Sprintf("INSERT INTO fuel_flow(log_date_start, log_date_end, flow) values(%d, %d, %f)", f.log_date_start.Unix(), f.log_date_end.Unix(), f.flow)
		_, err = db.Exec(q)
		if err != nil {
			logger.Errorf("stmt.Exec(): %s\n", err.Error())
		}
	}
}

func main() {
	// Set up logging for stdout (colors).
	logBackend := logging.NewLogBackend(os.Stderr, "", 0)
	logFormat := logging.MustStringFormatter(`%{color}%{time:15:04:05.000} %{shortfunc} ▶ %{level:.4s} %{id:03x}%{color:reset} %{message}`)
	logBackendFormatter := logging.NewBackendFormatter(logBackend, logFormat)

	// Set up logging for file.
	logFileFp, err := os.OpenFile("/var/log/flowfast.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		logger.Errorf("Failed to open '%s': %s\n", "/var/log/flowfast.log", err.Error())
		return
	}
	defer logFileFp.Close()
	logFileBackend := logging.NewLogBackend(logFileFp, "", 0)
	logFileBackendFormatter := logging.NewBackendFormatter(logFileBackend, logFormat)
	logging.SetBackend(logBackendFormatter, logFileBackendFormatter)

	// Set up rate counters and mutex.
	flow.flow_last_second = ratecounter.NewRateCounter(1 * time.Second)
	flow.flow_last_minute = ratecounter.NewRateCounter(1 * time.Minute)
	flow.flow_last_hour = ratecounter.NewRateCounter(1 * time.Hour)
	flow.mu = &sync.Mutex{}

	go startWebListener()
	go dbLogger()
	go readADS1115()

	// Wait indefinitely.
	select {}
}
<commit_msg>Move database update functions to stats calculation goroutine.<commit_after>/*
	Copyright (c) 2016 Christopher Young
	Distributable under the terms of The "BSD New"" License
	that can be found in the LICENSE file, herein included
	as part of this header.

	flowfast.go: Counts inputs from ADS1115, sends over a websocket.
*/

// A0 = -
// A1 = +
// 5.6K pullup on WHITE lead for FT-60.

package main

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"github.com/kidoman/embd"
	_ "github.com/kidoman/embd/host/all"
	_ "github.com/mattn/go-sqlite3"
	"github.com/op/go-logging"
	"github.com/paulbellamy/ratecounter"
	"golang.org/x/net/websocket"
	"math"
	"net/http"
	"os"
	"sync"
	"time"
)

const (
	GALLONS_PER_CLICK = 1 / 68000.0 // FT-60 K-factor: 68,000.
	SQLITE_DB_FILE    = "./test.db"
	LISTEN_ADDR       = ":8081"
)

type FlowStats struct {
	EvaluatedTime time.Time // Time when the counters were evaluated.
	Flow_Total    float64
	// units=gallons.
	Flow_LastSecond   float64
	Flow_LastMinute   float64
	Flow_MaxPerMinute float64
	// units=GPH.
	Flow_LastSecond_GPH      float64
	Flow_LastMinute_GPH      float64
	Flow_MaxPerMinute_GPH    float64
	Flow_LastHour_Actual_GPH float64
	// Rate counters.
	flow_total_raw   uint64
	flow_last_second *ratecounter.RateCounter
	flow_last_minute *ratecounter.RateCounter
	flow_last_hour   *ratecounter.RateCounter

	mu *sync.Mutex
}

type fuel_log struct {
	log_date_start time.Time
	log_date_end   time.Time
	flow           float64
}

var flow FlowStats

var logger = logging.MustGetLogger("flowfast")

func statusWebSocket(conn *websocket.Conn) {
	ticker := time.NewTicker(1 * time.Second)

	for {
		<-ticker.C

		flow.mu.Lock()
		updateJSON, _ := json.Marshal(&flow) //TODO.
		flow.mu.Unlock()

		conn.Write(updateJSON)
	}
}

func startWebListener() {
	http.HandleFunc("/",
		func(w http.ResponseWriter, req *http.Request) {
			s := websocket.Server{
				Handler: websocket.Handler(statusWebSocket)}
			s.ServeHTTP(w, req)
		})

	logger.Debugf("listening on %s.\n", LISTEN_ADDR)
	err := http.ListenAndServe(LISTEN_ADDR, nil)
	if err != nil {
		logger.Errorf("can't listen on socket: %s\n", err.Error())
		os.Exit(-1)
	}
}

var i2cbus embd.I2CBus

var inputChan chan float64

// Re-calculate stats every second.
func statsCalculator() {
	ticker := time.NewTicker(1 * time.Second)
	last_update := time.Now()
	for {
		<-ticker.C
		flow.mu.Lock()

		flow.EvaluatedTime = time.Now()

		flow.Flow_Total = float64(flow.flow_total_raw) * GALLONS_PER_CLICK
		flow.Flow_LastSecond = float64(flow.flow_last_second.Rate()) * GALLONS_PER_CLICK
		flow.Flow_LastMinute = float64(flow.flow_last_minute.Rate()) * GALLONS_PER_CLICK
		flow.Flow_LastHour_Actual_GPH = float64(flow.flow_last_hour.Rate()) * GALLONS_PER_CLICK

		// Calculate maximums.
		if flow.Flow_LastMinute > flow.Flow_MaxPerMinute {
			flow.Flow_MaxPerMinute = flow.Flow_LastMinute
			flow.Flow_MaxPerMinute_GPH = flow.Flow_MaxPerMinute * float64(60.0) // Extrapolate.
		}

		// Extrapolate "GPH" numbers for the Second and Minute flow values.
		flow.Flow_LastSecond_GPH = flow.Flow_LastSecond * float64(3600.0)
		flow.Flow_LastMinute_GPH = flow.Flow_LastMinute * float64(60.0)

		// Update SQLite database.
		t := time.Now()
		logChan <- fuel_log{log_date_start: last_update, log_date_end: t, flow: flow.Flow_LastSecond}
		last_update = t

		flow.mu.Unlock()
	}
}

func processInput() {

	inputHigh := false

	for {
		mv := <-inputChan

		countCondition := false

		// 0V low.
		if math.Abs(mv-0.0) <= float64(1000.0) { // Low.
			inputHigh = false
		}

		// 5V high.
		if !inputHigh && math.Abs(mv-5000.0) <= float64(1000.0) { // High.
			inputHigh = true
			countCondition = true
			//		logger.Debugf("count! %f\n", mv)
		}

		if countCondition {
			flow.flow_total_raw++
			flow.flow_last_second.Incr(1)
			flow.flow_last_minute.Incr(1)
			flow.flow_last_hour.Incr(1)
		}
	}
}

// Ref: https://github.com/jrowberg/i2cdevlib/blob/master/Arduino/ADS1115/ADS1115.cpp
// Ref: https://github.com/jrowberg/i2cdevlib/blob/master/Arduino/ADS1115/ADS1115.h

func writeBitsW(bus embd.I2CBus, reg byte, bit_start, val_len uint, val uint16) {
	cur_val, err := bus.ReadWordFromReg(0x48, reg)
	if err != nil {
		logger.Errorf("ReadWordFromReg(): %s\n", err.Error())
		return
	}

	mask := uint16(((1 << val_len) - 1) << (bit_start - val_len + 1))
	val = val << (bit_start - val_len + 1)
	val &= mask
	cur_val &= ^(mask)
	cur_val |= val
	bus.WriteWordToReg(0x48, reg, cur_val)
}

func readADS1115() {
	inputChan = make(chan float64, 1024)

	go processInput()
	go statsCalculator()

	i2cbus = embd.NewI2CBus(1) //TODO: error checking.

	// Set up the device. ADS1115::setRate().
	writeBitsW(i2cbus, 0x01, 7, 3, 0x07)  // 3300 samples/sec.
	writeBitsW(i2cbus, 0x01, 8, 1, 0)     // MODE_CONTINUOUS.
	writeBitsW(i2cbus, 0x01, 11, 3, 0x00) // +/-6.144V. 3 mV div.
	writeBitsW(i2cbus, 0x01, 14, 3, 0x00) // setMultiplexer(MUX_P0_N1).

	for {
		v, err := i2cbus.ReadWordFromReg(0x48, 0x00)

		cv := int16(v >> 4)
		if v>>15 != 0 {
			cv = cv - 0xFFF
		}
		mv := float64(cv) * float64(3.0) // units=mV.
		if err != nil {
			logger.Errorf("ReadWordFromReg(): %s\n", err.Error())
		}

		inputChan <- mv
		time.Sleep(500 * time.Microsecond) // Oversampling.
	}

	return
}

var logChan chan fuel_log

// Logs fuel data to an SQLite database.
func dbLogger() {
	logChan = make(chan fuel_log, 1024)

	// Check if we need to create a new database.
	createDatabase := false
	if _, err := os.Stat(SQLITE_DB_FILE); os.IsNotExist(err) {
		createDatabase = true
		logger.Debugf("creating new database '%s'.\n", SQLITE_DB_FILE)
	}

	db, err := sql.Open("sqlite3", SQLITE_DB_FILE)
	if err != nil {
		logger.Errorf("sql.Open(): %s\n", err.Error())
	}
	defer db.Close()

	if createDatabase {
		createSmt := `
			CREATE TABLE fuel_flow (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, log_date_start INTEGER, log_date_end INTEGER, flow REAL);
		`

		_, err = db.Exec(createSmt)
		if err != nil {
			logger.Errorf("%q: %s\n", err, createSmt)
			return
		}
	}

	for {
		f := <-logChan
		//FIXME: Timestamps here are a hack.
		q := fmt.Sprintf("INSERT INTO fuel_flow(log_date_start, log_date_end, flow) values(%d, %d, %f)", f.log_date_start.Unix(), f.log_date_end.Unix(), f.flow)
		_, err = db.Exec(q)
		if err != nil {
			logger.Errorf("stmt.Exec(): %s\n", err.Error())
		}
	}
}

func main() {
	// Set up logging for stdout (colors).
	logBackend := logging.NewLogBackend(os.Stderr, "", 0)
	logFormat := logging.MustStringFormatter(`%{color}%{time:15:04:05.000} %{shortfunc} ▶ %{level:.4s} %{id:03x}%{color:reset} %{message}`)
	logBackendFormatter := logging.NewBackendFormatter(logBackend, logFormat)

	// Set up logging for file.
	logFileFp, err := os.OpenFile("/var/log/flowfast.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		logger.Errorf("Failed to open '%s': %s\n", "/var/log/flowfast.log", err.Error())
		return
	}
	defer logFileFp.Close()
	logFileBackend := logging.NewLogBackend(logFileFp, "", 0)
	logFileBackendFormatter := logging.NewBackendFormatter(logFileBackend, logFormat)
	logging.SetBackend(logBackendFormatter, logFileBackendFormatter)

	// Set up rate counters and mutex.
	flow.flow_last_second = ratecounter.NewRateCounter(1 * time.Second)
	flow.flow_last_minute = ratecounter.NewRateCounter(1 * time.Minute)
	flow.flow_last_hour = ratecounter.NewRateCounter(1 * time.Hour)
	flow.mu = &sync.Mutex{}

	go startWebListener()
	go dbLogger()
	go readADS1115()

	// Wait indefinitely.
	select {}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"text/tabwriter"
	"time"

	"github.com/Shopify/sarama"
	"github.com/cznic/sortutil"
	"github.com/wvanbergen/kazoo-go"
)

var (
	zookeeper        = flag.String("zookeeper", os.Getenv("ZOOKEEPER_PEERS"), "Zookeeper connection string. It can include a chroot.")
	zookeeperTimeout = flag.Int("zookeeper-timeout", 1000, "Zookeeper timeout in milliseconds.")
	topic            = flag.String("topic", os.Getenv("KAFKA_TOPIC"), "Kafka topic")
	group            = flag.String("group-id", os.Getenv("KAFKA_GROUP_ID"), "Kafka Consumer Group ID")
)

type ConsumerOffsets struct {
	Group      string
	Topic      string
	Partitions map[int32]*PartitionOffsets
}

func (co ConsumerOffsets) Print() {
	var ps sortutil.Int32Slice
	for k, _ := range co.Partitions {
		ps = append(ps, k)
	}
	ps.Sort()

	w := new(tabwriter.Writer)
	w.Init(os.Stdout, 0, 8, 4, '\t', 0)

	fmt.Fprintf(w, "Group ID\tTopic\tPartition\tOffset\tLog Size\tLag\tOwner\n")
	for _, p := range ps {
		s := co.Partitions[p].Newest - co.Partitions[p].Oldest
		l := co.Partitions[p].Newest - co.Partitions[p].Current
		fmt.Fprintf(w, "%s\t%s\t%d\t%d\t%d\t%d\t%s\n", co.Group, co.Topic, p, co.Partitions[p].Current, s, l, co.Partitions[p].Owner)
	}

	w.Flush()
}

type PartitionOffsets struct {
	Owner   string
	Oldest  int64
	Newest  int64
	Current int64
}

func main() {
	flag.Parse()

	checkFlags()

	// Prevent zk library froom outputing garbage
	log.SetOutput(ioutil.Discard)

	conf := kazoo.NewConfig()
	conf.Timeout = time.Duration(*zookeeperTimeout) * time.Millisecond

	zk, err := kazoo.NewKazooFromConnectionString(*zookeeper, conf)
	if err != nil {
		printErrorAndExit(69, "Failed to connect to Zookeeper: %v", err)
	}
	defer zk.Close()

	groups, err := zk.Consumergroups()
	if err != nil {
		printErrorAndExit(69, "Failed to get Kafka Consumer Groups from Zookeeper: %v", err)
	}

	g := groups.Find(*group)

	offsets, err := g.FetchAllOffsets()
	if err != nil {
		printErrorAndExit(69, "Failed to get offsets for Kafka Consumer Group %s: %v", group, err)
	}

	co := ConsumerOffsets{
		Group:      *group,
		Topic:      *topic,
		Partitions: make(map[int32]*PartitionOffsets),
	}

	for p, o := range offsets[*topic] {
		owner, err := g.PartitionOwner(*topic, p)
		if err != nil {
			printErrorAndExit(69, "Failed to get partition owner for Consumer Group %s topic %s partition %d: err", group, topic, p, err)
		}
		co.Partitions[p] = &PartitionOffsets{
			Owner:   owner.ID,
			Current: o,
		}
	}

	b, err := zk.BrokerList()
	if err != nil {
		printErrorAndExit(69, "Failed to get kafka broker list from zookeeper: %v", err)
	}

	k, err := sarama.NewClient(b, sarama.NewConfig())
	if err != nil {
		printErrorAndExit(69, "Failed to connect to kafka: %v", err)
	}
	defer k.Close()

	ps, err := k.Partitions(*topic)
	if err != nil {
		printErrorAndExit(69, "Failed to get partitions for topic %s from kafka: %v", topic, err)
	}

	for _, p := range ps {
		oldest, err := k.GetOffset(*topic, p, sarama.OffsetOldest)
		if err != nil {
			printErrorAndExit(69, "Failed to get oldest offset for topic %s, partition %d, from kafka: %v", topic, p, err)
		}
		co.Partitions[p].Oldest = oldest

		newest, err := k.GetOffset(*topic, p, sarama.OffsetNewest)
		if err != nil {
			printErrorAndExit(69, "Failed to get newest offset for topic %s, partition %d, from kafka: %v", topic, p, err)
		}
		co.Partitions[p].Newest = newest
	}

	co.Print()
}

func printUsageErrorAndExit(format string, values ...interface{}) {
	fmt.Fprintf(os.Stderr, "ERROR: %s\n", fmt.Sprintf(format, values...))
	fmt.Fprintln(os.Stderr)
	fmt.Fprintln(os.Stderr, "Available command line options:")
	flag.PrintDefaults()
	os.Exit(64)
}

func printErrorAndExit(code int, format string, values ...interface{}) {
	fmt.Fprintf(os.Stderr, "ERROR: %s\n", fmt.Sprintf(format, values...))
	fmt.Fprintln(os.Stderr)
	os.Exit(code)
}

func checkFlags() {
	var err string

	if *zookeeper == "" {
		err += "A zookeeper host string is required\n"
	}

	if *group == "" {
		err += "A consumer group id is required.\n"
	}

	if *topic == "" {
		err += "A topic name is required.\n"
	}

	if err != "" {
		printUsageErrorAndExit(err)
	}
}
<commit_msg>Handle unclaimed partitions<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"text/tabwriter"
	"time"

	"github.com/Shopify/sarama"
	"github.com/cznic/sortutil"
	"github.com/wvanbergen/kazoo-go"
)

var (
	zookeeper        = flag.String("zookeeper", os.Getenv("ZOOKEEPER_PEERS"), "Zookeeper connection string. It can include a chroot.")
	zookeeperTimeout = flag.Int("zookeeper-timeout", 1000, "Zookeeper timeout in milliseconds.")
	topic            = flag.String("topic", os.Getenv("KAFKA_TOPIC"), "Kafka topic")
	group            = flag.String("group-id", os.Getenv("KAFKA_GROUP_ID"), "Kafka Consumer Group ID")
)

type ConsumerOffsets struct {
	Group      string
	Topic      string
	Partitions map[int32]*PartitionOffsets
}

func (co ConsumerOffsets) Print() {
	var ps sortutil.Int32Slice
	for k, _ := range co.Partitions {
		ps = append(ps, k)
	}
	ps.Sort()

	w := new(tabwriter.Writer)
	w.Init(os.Stdout, 0, 8, 4, '\t', 0)

	fmt.Fprintf(w, "Group ID\tTopic\tPartition\tOffset\tLog Size\tLag\tOwner\n")
	for _, p := range ps {
		s := co.Partitions[p].Newest - co.Partitions[p].Oldest
		l := co.Partitions[p].Newest - co.Partitions[p].Current
		fmt.Fprintf(w, "%s\t%s\t%d\t%d\t%d\t%d\t%s\n", co.Group, co.Topic, p, co.Partitions[p].Current, s, l, co.Partitions[p].Owner)
	}

	w.Flush()
}

type PartitionOffsets struct {
	Owner   string
	Oldest  int64
	Newest  int64
	Current int64
}

func main() {
	flag.Parse()

	checkFlags()

	// Prevent zk library froom outputing garbage
	log.SetOutput(ioutil.Discard)

	conf := kazoo.NewConfig()
	conf.Timeout = time.Duration(*zookeeperTimeout) * time.Millisecond

	zk, err := kazoo.NewKazooFromConnectionString(*zookeeper, conf)
	if err != nil {
		printErrorAndExit(69, "Failed to connect to Zookeeper: %v", err)
	}
	defer zk.Close()

	groups, err := zk.Consumergroups()
	if err != nil {
		printErrorAndExit(69, "Failed to get Kafka Consumer Groups from Zookeeper: %v", err)
	}

	g := groups.Find(*group)

	offsets, err := g.FetchAllOffsets()
	if err != nil {
		printErrorAndExit(69, "Failed to get offsets for Kafka Consumer Group %s: %v", group, err)
	}

	co := ConsumerOffsets{
		Group:      *group,
		Topic:      *topic,
		Partitions: make(map[int32]*PartitionOffsets),
	}

	for p, o := range offsets[*topic] {
		id := "UNCLAIMED"
		if owner, err := g.PartitionOwner(*topic, p); err != nil {
			printErrorAndExit(69, "Failed to get partition owner for Consumer Group %s topic %s partition %d: err", group, topic, p, err)
		} else if owner != nil {
			id = owner.ID
		}

		co.Partitions[p] = &PartitionOffsets{
			Owner:   id,
			Current: o,
		}
	}

	b, err := zk.BrokerList()
	if err != nil {
		printErrorAndExit(69, "Failed to get kafka broker list from zookeeper: %v", err)
	}

	k, err := sarama.NewClient(b, sarama.NewConfig())
	if err != nil {
		printErrorAndExit(69, "Failed to connect to kafka: %v", err)
	}
	defer k.Close()

	ps, err := k.Partitions(*topic)
	if err != nil {
		printErrorAndExit(69, "Failed to get partitions for topic %s from kafka: %v", topic, err)
	}

	for _, p := range ps {
		oldest, err := k.GetOffset(*topic, p, sarama.OffsetOldest)
		if err != nil {
			printErrorAndExit(69, "Failed to get oldest offset for topic %s, partition %d, from kafka: %v", topic, p, err)
		}
		co.Partitions[p].Oldest = oldest

		newest, err := k.GetOffset(*topic, p, sarama.OffsetNewest)
		if err != nil {
			printErrorAndExit(69, "Failed to get newest offset for topic %s, partition %d, from kafka: %v", topic, p, err)
		}
		co.Partitions[p].Newest = newest
	}

	co.Print()
}

func printUsageErrorAndExit(format string, values ...interface{}) {
	fmt.Fprintf(os.Stderr, "ERROR: %s\n", fmt.Sprintf(format, values...))
	fmt.Fprintln(os.Stderr)
	fmt.Fprintln(os.Stderr, "Available command line options:")
	flag.PrintDefaults()
	os.Exit(64)
}

func printErrorAndExit(code int, format string, values ...interface{}) {
	fmt.Fprintf(os.Stderr, "ERROR: %s\n", fmt.Sprintf(format, values...))
	fmt.Fprintln(os.Stderr)
	os.Exit(code)
}

func checkFlags() {
	var err string

	if *zookeeper == "" {
		err += "A zookeeper host string is required\n"
	}

	if *group == "" {
		err += "A consumer group id is required.\n"
	}

	if *topic == "" {
		err += "A topic name is required.\n"
	}

	if err != "" {
		printUsageErrorAndExit(err)
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2015, 2016 Janoš Guljaš <janos@resenje.org>
// All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package logging

import (
	"fmt"
	"io"
	"os"
	"sync"
	"time"
)

// Handler is interface that declares what every logging handler
// must implement in order to be used with this library.
type Handler interface {
	// Handle should accept log record instance and process it.
	Handle(record *Record) error
	// HandleError should process errors that occurred during calls to Handle method
	HandleError(error) error
	// GetLevel should return level that this handler is processing.
	GetLevel() Level
	// Close should free handler resources (opened files, etc)
	Close() error
}

// NullHandler ignores all messages provided to it.
type NullHandler struct{}

// Handle ignores all messages.
func (handler *NullHandler) Handle(record *Record) error {
	return nil
}

// HandleError prints provided error to stderr.
func (handler *NullHandler) HandleError(err error) error {
	os.Stderr.WriteString(fmt.Sprintf("%s %s\n", time.Now(), err.Error()))
	return nil
}

// GetLevel returns current level for this handler.
func (handler *NullHandler) GetLevel() Level {
	return DEBUG
}

// Close does nothing for this handler.
func (handler *NullHandler) Close() error {
	return nil
}

// WriteHandler requires io.Writer and sends all messages to this writer.
type WriteHandler struct {
	NullHandler

	Level     Level
	Formatter Formatter
	Writer    io.Writer
	lock      sync.RWMutex
}

// Handle writes all provided log records to writer provided during creation.
func (handler *WriteHandler) Handle(record *Record) error {
	handler.lock.Lock()
	_, err := handler.Writer.Write([]byte(handler.Formatter.Format(record) + "\n"))
	handler.lock.Unlock()
	return err
}

// GetLevel returns current level for this handler.
func (handler *WriteHandler) GetLevel() Level {
	return handler.Level
}

// MemoryHandler stores all messages in memory.
// If needed, messages can be obtained from Messages field.
type MemoryHandler struct {
	NullHandler

	Level     Level
	Formatter Formatter
	Messages  []string
	lock      sync.RWMutex
}

// Handle appends message to Messages array.
func (handler *MemoryHandler) Handle(record *Record) error {
	handler.lock.Lock()
	handler.Messages = append(handler.Messages, handler.Formatter.Format(record))
	handler.lock.Unlock()
	return nil
}

// GetLevel returns current level for this handler.
func (handler *MemoryHandler) GetLevel() Level {
	return handler.Level
}
<commit_msg>Remove lock from WriteHandler<commit_after>// Copyright (c) 2015, 2016 Janoš Guljaš <janos@resenje.org>
// All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package logging

import (
	"fmt"
	"io"
	"os"
	"sync"
	"time"
)

// Handler is interface that declares what every logging handler
// must implement in order to be used with this library.
type Handler interface {
	// Handle should accept log record instance and process it.
	Handle(record *Record) error
	// HandleError should process errors that occurred during calls to Handle method
	HandleError(error) error
	// GetLevel should return level that this handler is processing.
	GetLevel() Level
	// Close should free handler resources (opened files, etc)
	Close() error
}

// NullHandler ignores all messages provided to it.
type NullHandler struct{}

// Handle ignores all messages.
func (handler *NullHandler) Handle(record *Record) error {
	return nil
}

// HandleError prints provided error to stderr.
func (handler *NullHandler) HandleError(err error) error {
	os.Stderr.WriteString(fmt.Sprintf("%s %s\n", time.Now(), err.Error()))
	return nil
}

// GetLevel returns current level for this handler.
func (handler *NullHandler) GetLevel() Level {
	return DEBUG
}

// Close does nothing for this handler.
func (handler *NullHandler) Close() error {
	return nil
}

// WriteHandler requires io.Writer and sends all messages to this writer.
type WriteHandler struct {
	NullHandler

	Level     Level
	Formatter Formatter
	Writer    io.Writer
}

// Handle writes all provided log records to writer provided during creation.
func (handler *WriteHandler) Handle(record *Record) error {
	_, err := handler.Writer.Write([]byte(handler.Formatter.Format(record) + "\n"))
	return err
}

// GetLevel returns current level for this handler.
func (handler *WriteHandler) GetLevel() Level {
	return handler.Level
}

// MemoryHandler stores all messages in memory.
// If needed, messages can be obtained from Messages field.
type MemoryHandler struct {
	NullHandler

	Level     Level
	Formatter Formatter
	Messages  []string
	lock      sync.RWMutex
}

// Handle appends message to Messages array.
func (handler *MemoryHandler) Handle(record *Record) error {
	handler.lock.Lock()
	handler.Messages = append(handler.Messages, handler.Formatter.Format(record))
	handler.lock.Unlock()
	return nil
}

// GetLevel returns current level for this handler.
func (handler *MemoryHandler) GetLevel() Level {
	return handler.Level
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"html/template"
	"io"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"time"
)

type Handler struct {
	tpls *template.Template
	log  *log.Logger
}

func newHandler(tpls *template.Template) *Handler {
	handler := new(Handler)
	handler.tpls = tpls
	handler.log = logger

	return handler
}

func (h *Handler) handle(w http.ResponseWriter, r *http.Request) {
	context := NewContext(w, r, h.tpls)
	h.log.Println(fmt.Sprintf("%s %s", r.Method, r.URL.String()))
	switch r.URL.String() {
	case "/":
		context.IsUpload = true
		h.Home(context)
	case "/upload/do":
		context.IsUpload = true
		h.UploadDo(context)
	case "/download":
		context.IsDownload = true
		h.Download(context)
	case "/download/do":
		context.IsDownload = true
		h.DownloadDo(context)
	case "/favicon.ico":
	default:
		h.NotFound(context)
	}
}

func (h *Handler) Home(ctx *Context) {
	ctx.Render("home.html")
}

func (h *Handler) NotFound(ctx *Context) {
	ctx.Response.Write([]byte("This page is not found."))
}

func (h *Handler) UploadDo(ctx *Context) {
	if ctx.Method != "POST" {
		ctx.Redirect("/")
		return
	}
	fmt.Println(ctx.Request.Form)

	tmpDate := ctx.Request.Form.Get("date")

	date, err := time.Parse("2006-01", tmpDate)
	if err != nil {
		ctx.FlushMessage = fmt.Sprintf("日期格式不正确: %s", err)
		ctx.Status = 400
		ctx.Render("home.html")
		return
	}

	tmpIsContainHead := ctx.Request.Form.Get(("isContainHead"))

	var isContainHead bool
	if tmpIsContainHead == "on" {
		isContainHead = true
	} else {
		isContainHead = false
	}

	ctx.Request.ParseMultipartForm(32 << 20)
	file, handler, err := ctx.Request.FormFile("uploadfile")
	if err != nil {
		fmt.Println(err)
		ctx.FlushMessage = "上传错误"
		return
	}

	fileext := filepath.Ext(handler.Filename)
	if check(fileext) == false {
		ctx.FlushMessage = "不允许的上传类型"
		return
	}

	updateDir := filepath.Join(currentDir, "data")

	filename := strconv.FormatInt(time.Now().Unix(), 10) + fileext

	f, _ := os.OpenFile(filepath.Join(updateDir, filename), os.O_CREATE|os.O_WRONLY, 0660)
	_, err = io.Copy(f, file)
	if err != nil {
		ctx.FlushMessage = "上传失败"
		return
	}
	filedir, _ := filepath.Abs(updateDir + filename)
	ctx.FlushMessage = filepath.Join(filename + "上传完成,服务器地址:" + filedir)

	fmt.Println(isContainHead, date, f)
}

func (h *Handler) Download(ctx *Context) {
	ctx.Render("download.html")
}

func (h *Handler) DownloadDo(ctx *Context) {

}

func check(name string) bool {
	ext := []string{".exe", ".js", ".png"}

	for _, v := range ext {
		if v == name {
			return false
		}
	}
	return true
}
<commit_msg>nothing<commit_after>package main

import (
	"fmt"
	"html/template"
	"io"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"time"
)

type Handler struct {
	tpls *template.Template
	log  *log.Logger
}

func newHandler(tpls *template.Template) *Handler {
	handler := new(Handler)
	handler.tpls = tpls
	handler.log = logger

	return handler
}

func (h *Handler) handle(w http.ResponseWriter, r *http.Request) {
	context := NewContext(w, r, h.tpls)
	h.log.Println(fmt.Sprintf("%s %s", r.Method, r.URL.String()))
	switch r.URL.String() {
	case "/":
		context.IsUpload = true
		h.Home(context)
	case "/upload/do":
		context.IsUpload = true
		h.UploadDo(context)
	case "/download":
		context.IsDownload = true
		h.Download(context)
	case "/download/do":
		context.IsDownload = true
		h.DownloadDo(context)
	case "/favicon.ico":
	default:
		h.NotFound(context)
	}
}

func (h *Handler) Home(ctx *Context) {
	ctx.Render("home.html")
}

func (h *Handler) NotFound(ctx *Context) {
	ctx.Response.Write([]byte("This page is not found."))
}

func (h *Handler) UploadDo(ctx *Context) {
	if ctx.Method != "POST" {
		ctx.Redirect("/")
		return
	}
	fmt.Println(ctx.Request.Form)

	tmpDate := ctx.Request.Form.Get("date")

	date, err := time.Parse("2006-01", tmpDate)
	if err != nil {
		ctx.FlushMessage = fmt.Sprintf("日期格式不正确: %s", err)
		ctx.Status = 400
		ctx.Render("home.html")
		return
	}

	tmpIsContainHead := ctx.Request.Form.Get("isContainHead")

	var isContainHead bool
	if tmpIsContainHead == "on" {
		isContainHead = true
	} else {
		isContainHead = false
	}

	ctx.Request.ParseMultipartForm(32 << 20)
	file, handler, err := ctx.Request.FormFile("uploadfile")
	if err != nil {
		ctx.FlushMessage = fmt.Sprintf("上传错误: %s", err)
		return
	}

	fileext := filepath.Ext(handler.Filename)
	if check(fileext) == false {
		ctx.FlushMessage = "不允许的上传类型"
		return
	}

	updateDir := filepath.Join(currentDir, "data")

	filename := strconv.FormatInt(time.Now().Unix(), 10) + fileext
	fullpath := filepath.Join(updateDir, filename)

	f, _ := os.OpenFile(fullpath, os.O_CREATE|os.O_WRONLY, 0660)
	_, err = io.Copy(f, file)
	if err != nil {
		ctx.FlushMessage = fmt.Sprintf("上传失败: %s", err)
		return
	}

	ctx.FlushMessage = filepath.Join(filename + "上传完成,服务器地址:" + fullpath)

	fmt.Println(isContainHead, date, f)
}

func (h *Handler) Download(ctx *Context) {
	ctx.Render("download.html")
}

func (h *Handler) DownloadDo(ctx *Context) {

}

func check(name string) bool {
	ext := []string{".xlsx"}

	for _, v := range ext {
		if v == name {
			return true
		}
	}
	return false
}
<|endoftext|>


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

/*
Package handlers is a collection of handlers for use with Go's net/http package.
*/
package handlers

import (
	"bufio"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"strings"
	"time"
	"unicode/utf8"
)

// MethodHandler is an http.Handler that dispatches to a handler whose key in the MethodHandler's
// map matches the name of the HTTP request's method, eg: GET
//
// If the request's method is OPTIONS and OPTIONS is not a key in the map then the handler
// responds with a status of 200 and sets the Allow header to a comma-separated list of
// available methods.
//
// If the request's method doesn't match any of its keys the handler responds with
// a status of 406, Method not allowed and sets the Allow header to a comma-separated list
// of available methods.
type MethodHandler map[string]http.Handler

func (h MethodHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	if handler, ok := h[req.Method]; ok {
		handler.ServeHTTP(w, req)
	} else {
		allow := []string{}
		for k := range h {
			allow = append(allow, k)
		}
		sort.Strings(allow)
		w.Header().Set("Allow", strings.Join(allow, ", "))
		if req.Method == "OPTIONS" {
			w.WriteHeader(http.StatusOK)
		} else {
			http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		}
	}
}

// loggingHandler is the http.Handler implementation for LoggingHandlerTo and its friends
type loggingHandler struct {
	writer  io.Writer
	handler http.Handler
}

// combinedLoggingHandler is the http.Handler implementation for LoggingHandlerTo and its friends
type combinedLoggingHandler struct {
	writer  io.Writer
	handler http.Handler
}

func (h loggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	t := time.Now()
	var logger loggingResponseWriter
	if _, ok := w.(http.Hijacker); ok {
		logger = &hijackLogger{responseLogger: responseLogger{w: w}}
	} else {
		logger = &responseLogger{w: w}
	}
	url := *req.URL
	h.handler.ServeHTTP(logger, req)
	writeLog(h.writer, req, url, t, logger.Status(), logger.Size())
}

func (h combinedLoggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	t := time.Now()
	var logger loggingResponseWriter
	if _, ok := w.(http.Hijacker); ok {
		logger = &hijackLogger{responseLogger: responseLogger{w: w}}
	} else {
		logger = &responseLogger{w: w}
	}
	url := *req.URL
	h.handler.ServeHTTP(logger, req)
	writeCombinedLog(h.writer, req, url, t, logger.Status(), logger.Size())
}

type loggingResponseWriter interface {
	http.ResponseWriter
	Status() int
	Size() int
}

// responseLogger is wrapper of http.ResponseWriter that keeps track of its HTTP status
// code and body size
type responseLogger struct {
	w      http.ResponseWriter
	status int
	size   int
}

func (l *responseLogger) Header() http.Header {
	return l.w.Header()
}

func (l *responseLogger) Write(b []byte) (int, error) {
	if l.status == 0 {
		// The status will be StatusOK if WriteHeader has not been called yet
		l.status = http.StatusOK
	}
	size, err := l.w.Write(b)
	l.size += size
	return size, err
}

func (l *responseLogger) WriteHeader(s int) {
	l.w.WriteHeader(s)
	l.status = s
}

func (l *responseLogger) Status() int {
	return l.status
}

func (l *responseLogger) Size() int {
	return l.size
}

type hijackLogger struct {
	responseLogger
}

func (l *hijackLogger) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	h := l.responseLogger.w.(http.Hijacker)
	conn, rw, err := h.Hijack()
	if err == nil && l.responseLogger.status == 0 {
		// The status will be StatusSwitchingProtocols if there was no error and WriteHeader has not been called yet
		l.responseLogger.status = http.StatusSwitchingProtocols
	}
	return conn, rw, err
}

const lowerhex = "0123456789abcdef"

func appendQuoted(buf []byte, s string) []byte {
	var runeTmp [utf8.UTFMax]byte
	for width := 0; len(s) > 0; s = s[width:] {
		r := rune(s[0])
		width = 1
		if r >= utf8.RuneSelf {
			r, width = utf8.DecodeRuneInString(s)
		}
		if width == 1 && r == utf8.RuneError {
			buf = append(buf, `\x`...)
			buf = append(buf, lowerhex[s[0]>>4])
			buf = append(buf, lowerhex[s[0]&0xF])
			continue
		}
		if r == rune('"') || r == '\\' { // always backslashed
			buf = append(buf, '\\')
			buf = append(buf, byte(r))
			continue
		}
		if strconv.IsPrint(r) {
			n := utf8.EncodeRune(runeTmp[:], r)
			buf = append(buf, runeTmp[:n]...)
			continue
		}
		switch r {
		case '\a':
			buf = append(buf, `\a`...)
		case '\b':
			buf = append(buf, `\b`...)
		case '\f':
			buf = append(buf, `\f`...)
		case '\n':
			buf = append(buf, `\n`...)
		case '\r':
			buf = append(buf, `\r`...)
		case '\t':
			buf = append(buf, `\t`...)
		case '\v':
			buf = append(buf, `\v`...)
		default:
			switch {
			case r < ' ':
				buf = append(buf, `\x`...)
				buf = append(buf, lowerhex[s[0]>>4])
				buf = append(buf, lowerhex[s[0]&0xF])
			case r > utf8.MaxRune:
				r = 0xFFFD
				fallthrough
			case r < 0x10000:
				buf = append(buf, `\u`...)
				for s := 12; s >= 0; s -= 4 {
					buf = append(buf, lowerhex[r>>uint(s)&0xF])
				}
			default:
				buf = append(buf, `\U`...)
				for s := 28; s >= 0; s -= 4 {
					buf = append(buf, lowerhex[r>>uint(s)&0xF])
				}
			}
		}
	}
	return buf

}

// buildCommonLogLine builds a log entry for req in Apache Common Log Format.
// ts is the timestamp with which the entry should be logged.
// status and size are used to provide the response HTTP status and size.
func buildCommonLogLine(req *http.Request, url url.URL, ts time.Time, status int, size int) []byte {
	username := "-"
	if url.User != nil {
		if name := url.User.Username(); name != "" {
			username = name
		}
	}

	host, _, err := net.SplitHostPort(req.RemoteAddr)

	if err != nil {
		host = req.RemoteAddr
	}

	uri := url.RequestURI()

	buf := make([]byte, 0, 3*(len(host)+len(username)+len(req.Method)+len(uri)+len(req.Proto)+50)/2)
	buf = append(buf, host...)
	buf = append(buf, " - "...)
	buf = append(buf, username...)
	buf = append(buf, " ["...)
	buf = append(buf, ts.Format("02/Jan/2006:15:04:05 -0700")...)
	buf = append(buf, `] "`...)
	buf = append(buf, req.Method...)
	buf = append(buf, " "...)
	buf = appendQuoted(buf, uri)
	buf = append(buf, " "...)
	buf = append(buf, req.Proto...)
	buf = append(buf, `" `...)
	buf = append(buf, strconv.Itoa(status)...)
	buf = append(buf, " "...)
	buf = append(buf, strconv.Itoa(size)...)
	return buf
}

// writeLog writes a log entry for req to w in Apache Common Log Format.
// ts is the timestamp with which the entry should be logged.
// status and size are used to provide the response HTTP status and size.
func writeLog(w io.Writer, req *http.Request, url url.URL, ts time.Time, status, size int) {
	buf := buildCommonLogLine(req, url, ts, status, size)
	buf = append(buf, '\n')
	w.Write(buf)
}

// writeCombinedLog writes a log entry for req to w in Apache Combined Log Format.
// ts is the timestamp with which the entry should be logged.
// status and size are used to provide the response HTTP status and size.
func writeCombinedLog(w io.Writer, req *http.Request, url url.URL, ts time.Time, status, size int) {
	buf := buildCommonLogLine(req, url, ts, status, size)
	buf = append(buf, ` "`...)
	buf = appendQuoted(buf, req.Referer())
	buf = append(buf, `" "`...)
	buf = appendQuoted(buf, req.UserAgent())
	buf = append(buf, '"', '\n')
	w.Write(buf)
}

// CombinedLoggingHandler return a http.Handler that wraps h and logs requests to out in
// Apache Combined Log Format.
//
// See http://httpd.apache.org/docs/2.2/logs.html#combined for a description of this format.
//
// LoggingHandler always sets the ident field of the log to -
func CombinedLoggingHandler(out io.Writer, h http.Handler) http.Handler {
	return combinedLoggingHandler{out, h}
}

// LoggingHandler return a http.Handler that wraps h and logs requests to out in
// Apache Common Log Format (CLF).
//
// See http://httpd.apache.org/docs/2.2/logs.html#common for a description of this format.
//
// LoggingHandler always sets the ident field of the log to -
func LoggingHandler(out io.Writer, h http.Handler) http.Handler {
	return loggingHandler{out, h}
}

// isContentType validates the Content-Type header
// is contentType. That is, its type and subtype match.
func isContentType(h http.Header, contentType string) bool {
	ct := h.Get("Content-Type")
	if i := strings.IndexRune(ct, ';'); i != -1 {
		ct = ct[0:i]
	}
	return ct == contentType
}

// ContentTypeHandler wraps and returns a http.Handler, validating the request content type
// is acompatible with the contentTypes list.
// It writes a HTTP 415 error if that fails.
//
// Only PUT, POST, and PATCH requests are considered.
func ContentTypeHandler(h http.Handler, contentTypes ...string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if !(r.Method == "PUT" || r.Method == "POST" || r.Method == "PATCH") {
			h.ServeHTTP(w, r)
			return
		}

		for _, ct := range contentTypes {
			if isContentType(r.Header, ct) {
				h.ServeHTTP(w, r)
				return
			}
		}
		http.Error(w, fmt.Sprintf("Unsupported content type %q; expected one of %q", r.Header.Get("Content-Type"), contentTypes), http.StatusUnsupportedMediaType)
	})
}

const (
	// HTTPMethodOverrideHeader is a commonly used
	// http header to override a request method.
	HTTPMethodOverrideHeader = "X-HTTP-Method-Override"
	// HTTPMethodOverrideFormKey is a commonly used
	// HTML form key to override a request method.
	HTTPMethodOverrideFormKey = "_method"
)

// HTTPMethodOverrideHandler wraps and returns a http.Handler which checks for the X-HTTP-Method-Override header
// or the _method form key, and overrides (if valid) request.Method with its value.
//
// This is especially useful for http clients that don't support many http verbs.
// It isn't secure to override e.g a GET to a POST, so only POST requests are considered.
// Likewise, the override method can only be a "write" method: PUT, PATCH or DELETE.
//
// Form method takes precedence over header method.
func HTTPMethodOverrideHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "POST" {
			om := r.FormValue(HTTPMethodOverrideFormKey)
			if om == "" {
				om = r.Header.Get(HTTPMethodOverrideHeader)
			}
			if om == "PUT" || om == "PATCH" || om == "DELETE" {
				r.Method = om
			}
		}
		h.ServeHTTP(w, r)
	})
}
<commit_msg>Fix issue #27.<commit_after>// Copyright 2013 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
Package handlers is a collection of handlers for use with Go's net/http package.
*/
package handlers

import (
	"bufio"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"strings"
	"time"
	"unicode/utf8"
)

// MethodHandler is an http.Handler that dispatches to a handler whose key in the MethodHandler's
// map matches the name of the HTTP request's method, eg: GET
//
// If the request's method is OPTIONS and OPTIONS is not a key in the map then the handler
// responds with a status of 200 and sets the Allow header to a comma-separated list of
// available methods.
//
// If the request's method doesn't match any of its keys the handler responds with
// a status of 405, Method not allowed and sets the Allow header to a comma-separated list
// of available methods.
type MethodHandler map[string]http.Handler

func (h MethodHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	if handler, ok := h[req.Method]; ok {
		handler.ServeHTTP(w, req)
	} else {
		allow := []string{}
		for k := range h {
			allow = append(allow, k)
		}
		sort.Strings(allow)
		w.Header().Set("Allow", strings.Join(allow, ", "))
		if req.Method == "OPTIONS" {
			w.WriteHeader(http.StatusOK)
		} else {
			http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		}
	}
}

// loggingHandler is the http.Handler implementation for LoggingHandlerTo and its friends
type loggingHandler struct {
	writer  io.Writer
	handler http.Handler
}

// combinedLoggingHandler is the http.Handler implementation for LoggingHandlerTo and its friends
type combinedLoggingHandler struct {
	writer  io.Writer
	handler http.Handler
}

func (h loggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	t := time.Now()
	var logger loggingResponseWriter
	if _, ok := w.(http.Hijacker); ok {
		logger = &hijackLogger{responseLogger: responseLogger{w: w}}
	} else {
		logger = &responseLogger{w: w}
	}
	url := *req.URL
	h.handler.ServeHTTP(logger, req)
	writeLog(h.writer, req, url, t, logger.Status(), logger.Size())
}

func (h combinedLoggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	t := time.Now()
	var logger loggingResponseWriter
	if _, ok := w.(http.Hijacker); ok {
		logger = &hijackLogger{responseLogger: responseLogger{w: w}}
	} else {
		logger = &responseLogger{w: w}
	}
	url := *req.URL
	h.handler.ServeHTTP(logger, req)
	writeCombinedLog(h.writer, req, url, t, logger.Status(), logger.Size())
}

type loggingResponseWriter interface {
	http.ResponseWriter
	Status() int
	Size() int
}

// responseLogger is wrapper of http.ResponseWriter that keeps track of its HTTP status
// code and body size
type responseLogger struct {
	w      http.ResponseWriter
	status int
	size   int
}

func (l *responseLogger) Header() http.Header {
	return l.w.Header()
}

func (l *responseLogger) Write(b []byte) (int, error) {
	if l.status == 0 {
		// The status will be StatusOK if WriteHeader has not been called yet
		l.status = http.StatusOK
	}
	size, err := l.w.Write(b)
	l.size += size
	return size, err
}

func (l *responseLogger) WriteHeader(s int) {
	l.w.WriteHeader(s)
	l.status = s
}

func (l *responseLogger) Status() int {
	return l.status
}

func (l *responseLogger) Size() int {
	return l.size
}

type hijackLogger struct {
	responseLogger
}

func (l *hijackLogger) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	h := l.responseLogger.w.(http.Hijacker)
	conn, rw, err := h.Hijack()
	if err == nil && l.responseLogger.status == 0 {
		// The status will be StatusSwitchingProtocols if there was no error and WriteHeader has not been called yet
		l.responseLogger.status = http.StatusSwitchingProtocols
	}
	return conn, rw, err
}

const lowerhex = "0123456789abcdef"

func appendQuoted(buf []byte, s string) []byte {
	var runeTmp [utf8.UTFMax]byte
	for width := 0; len(s) > 0; s = s[width:] {
		r := rune(s[0])
		width = 1
		if r >= utf8.RuneSelf {
			r, width = utf8.DecodeRuneInString(s)
		}
		if width == 1 && r == utf8.RuneError {
			buf = append(buf, `\x`...)
			buf = append(buf, lowerhex[s[0]>>4])
			buf = append(buf, lowerhex[s[0]&0xF])
			continue
		}
		if r == rune('"') || r == '\\' { // always backslashed
			buf = append(buf, '\\')
			buf = append(buf, byte(r))
			continue
		}
		if strconv.IsPrint(r) {
			n := utf8.EncodeRune(runeTmp[:], r)
			buf = append(buf, runeTmp[:n]...)
			continue
		}
		switch r {
		case '\a':
			buf = append(buf, `\a`...)
		case '\b':
			buf = append(buf, `\b`...)
		case '\f':
			buf = append(buf, `\f`...)
		case '\n':
			buf = append(buf, `\n`...)
		case '\r':
			buf = append(buf, `\r`...)
		case '\t':
			buf = append(buf, `\t`...)
		case '\v':
			buf = append(buf, `\v`...)
		default:
			switch {
			case r < ' ':
				buf = append(buf, `\x`...)
				buf = append(buf, lowerhex[s[0]>>4])
				buf = append(buf, lowerhex[s[0]&0xF])
			case r > utf8.MaxRune:
				r = 0xFFFD
				fallthrough
			case r < 0x10000:
				buf = append(buf, `\u`...)
				for s := 12; s >= 0; s -= 4 {
					buf = append(buf, lowerhex[r>>uint(s)&0xF])
				}
			default:
				buf = append(buf, `\U`...)
				for s := 28; s >= 0; s -= 4 {
					buf = append(buf, lowerhex[r>>uint(s)&0xF])
				}
			}
		}
	}
	return buf

}

// buildCommonLogLine builds a log entry for req in Apache Common Log Format.
// ts is the timestamp with which the entry should be logged.
// status and size are used to provide the response HTTP status and size.
func buildCommonLogLine(req *http.Request, url url.URL, ts time.Time, status int, size int) []byte {
	username := "-"
	if url.User != nil {
		if name := url.User.Username(); name != "" {
			username = name
		}
	}

	host, _, err := net.SplitHostPort(req.RemoteAddr)

	if err != nil {
		host = req.RemoteAddr
	}

	uri := url.RequestURI()

	buf := make([]byte, 0, 3*(len(host)+len(username)+len(req.Method)+len(uri)+len(req.Proto)+50)/2)
	buf = append(buf, host...)
	buf = append(buf, " - "...)
	buf = append(buf, username...)
	buf = append(buf, " ["...)
	buf = append(buf, ts.Format("02/Jan/2006:15:04:05 -0700")...)
	buf = append(buf, `] "`...)
	buf = append(buf, req.Method...)
	buf = append(buf, " "...)
	buf = appendQuoted(buf, uri)
	buf = append(buf, " "...)
	buf = append(buf, req.Proto...)
	buf = append(buf, `" `...)
	buf = append(buf, strconv.Itoa(status)...)
	buf = append(buf, " "...)
	buf = append(buf, strconv.Itoa(size)...)
	return buf
}

// writeLog writes a log entry for req to w in Apache Common Log Format.
// ts is the timestamp with which the entry should be logged.
// status and size are used to provide the response HTTP status and size.
func writeLog(w io.Writer, req *http.Request, url url.URL, ts time.Time, status, size int) {
	buf := buildCommonLogLine(req, url, ts, status, size)
	buf = append(buf, '\n')
	w.Write(buf)
}

// writeCombinedLog writes a log entry for req to w in Apache Combined Log Format.
// ts is the timestamp with which the entry should be logged.
// status and size are used to provide the response HTTP status and size.
func writeCombinedLog(w io.Writer, req *http.Request, url url.URL, ts time.Time, status, size int) {
	buf := buildCommonLogLine(req, url, ts, status, size)
	buf = append(buf, ` "`...)
	buf = appendQuoted(buf, req.Referer())
	buf = append(buf, `" "`...)
	buf = appendQuoted(buf, req.UserAgent())
	buf = append(buf, '"', '\n')
	w.Write(buf)
}

// CombinedLoggingHandler return a http.Handler that wraps h and logs requests to out in
// Apache Combined Log Format.
//
// See http://httpd.apache.org/docs/2.2/logs.html#combined for a description of this format.
//
// LoggingHandler always sets the ident field of the log to -
func CombinedLoggingHandler(out io.Writer, h http.Handler) http.Handler {
	return combinedLoggingHandler{out, h}
}

// LoggingHandler return a http.Handler that wraps h and logs requests to out in
// Apache Common Log Format (CLF).
//
// See http://httpd.apache.org/docs/2.2/logs.html#common for a description of this format.
//
// LoggingHandler always sets the ident field of the log to -
func LoggingHandler(out io.Writer, h http.Handler) http.Handler {
	return loggingHandler{out, h}
}

// isContentType validates the Content-Type header
// is contentType. That is, its type and subtype match.
func isContentType(h http.Header, contentType string) bool {
	ct := h.Get("Content-Type")
	if i := strings.IndexRune(ct, ';'); i != -1 {
		ct = ct[0:i]
	}
	return ct == contentType
}

// ContentTypeHandler wraps and returns a http.Handler, validating the request content type
// is acompatible with the contentTypes list.
// It writes a HTTP 415 error if that fails.
//
// Only PUT, POST, and PATCH requests are considered.
func ContentTypeHandler(h http.Handler, contentTypes ...string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if !(r.Method == "PUT" || r.Method == "POST" || r.Method == "PATCH") {
			h.ServeHTTP(w, r)
			return
		}

		for _, ct := range contentTypes {
			if isContentType(r.Header, ct) {
				h.ServeHTTP(w, r)
				return
			}
		}
		http.Error(w, fmt.Sprintf("Unsupported content type %q; expected one of %q", r.Header.Get("Content-Type"), contentTypes), http.StatusUnsupportedMediaType)
	})
}

const (
	// HTTPMethodOverrideHeader is a commonly used
	// http header to override a request method.
	HTTPMethodOverrideHeader = "X-HTTP-Method-Override"
	// HTTPMethodOverrideFormKey is a commonly used
	// HTML form key to override a request method.
	HTTPMethodOverrideFormKey = "_method"
)

// HTTPMethodOverrideHandler wraps and returns a http.Handler which checks for the X-HTTP-Method-Override header
// or the _method form key, and overrides (if valid) request.Method with its value.
//
// This is especially useful for http clients that don't support many http verbs.
// It isn't secure to override e.g a GET to a POST, so only POST requests are considered.
// Likewise, the override method can only be a "write" method: PUT, PATCH or DELETE.
//
// Form method takes precedence over header method.
func HTTPMethodOverrideHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "POST" {
			om := r.FormValue(HTTPMethodOverrideFormKey)
			if om == "" {
				om = r.Header.Get(HTTPMethodOverrideHeader)
			}
			if om == "PUT" || om == "PATCH" || om == "DELETE" {
				r.Method = om
			}
		}
		h.ServeHTTP(w, r)
	})
}
<|endoftext|>