<commit_before>package device

import (
	"fmt"

	deviceConfig "github.com/lxc/lxd/lxd/device/config"
	"github.com/lxc/lxd/lxd/instance"
	"github.com/lxc/lxd/lxd/instance/instancetype"
	"github.com/lxc/lxd/lxd/network"
	"github.com/lxc/lxd/lxd/resources"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
)

type infinibandSRIOV struct {
	deviceCommon
}

// validateConfig checks the supplied config for correctness.
func (d *infinibandSRIOV) validateConfig(instConf instance.ConfigReader) error {
	if !instanceSupported(instConf.Type(), instancetype.Container) {
		return ErrUnsupportedDevType
	}

	requiredFields := []string{"parent"}
	optionalFields := []string{
		"name",
		"mtu",
		"hwaddr",
	}

	rules := nicValidationRules(requiredFields, optionalFields, instConf)
	rules["hwaddr"] = func(value string) error {
		if value == "" {
			return nil
		}

		return infinibandValidMAC(value)
	}

	err := d.config.Validate(rules)
	if err != nil {
		return err
	}

	return nil
}

// validateEnvironment checks the runtime environment for correctness.
func (d *infinibandSRIOV) validateEnvironment() error {
	if d.inst.Type() == instancetype.Container && d.config["name"] == "" {
		return fmt.Errorf("Requires name property to start")
	}

	if !shared.PathExists(fmt.Sprintf("/sys/class/net/%s", d.config["parent"])) {
		return fmt.Errorf("Parent device '%s' doesn't exist", d.config["parent"])
	}

	return nil
}

// Start is run when the device is added to a running instance or instance is starting up.
func (d *infinibandSRIOV) Start() (*deviceConfig.RunConfig, error) {
	err := d.validateEnvironment()
	if err != nil {
		return nil, err
	}

	saveData := make(map[string]string)

	// Load network interface info.
	nics, err := resources.GetNetwork()
	if err != nil {
		return nil, err
	}

	// Filter the network interfaces to just infiniband devices related to parent.
	ibDevs := infinibandDevices(nics, d.config["parent"])

	// We don't count the parent as an available VF.
	delete(ibDevs, d.config["parent"])

	// Load any interfaces already allocated to other devices.
	reservedDevices, err := network.SRIOVGetHostDevicesInUse(d.state)
	if err != nil {
		return nil, err
	}

	// Remove reserved devices from available list.
	for k := range reservedDevices {
		delete(ibDevs, k)
	}

	if len(ibDevs) < 1 {
		return nil, fmt.Errorf("All virtual functions on parent device are already in use")
	}

	// Get first VF device that is free.
	var vfDev *api.ResourcesNetworkCardPort
	for _, v := range ibDevs {
		vfDev = v
		break
	}

	saveData["host_name"] = vfDev.ID

	// Record hwaddr and mtu before potentially modifying them.
	err = networkSnapshotPhysicalNic(saveData["host_name"], saveData)
	if err != nil {
		return nil, err
	}

	// Set the MAC address.
	if d.config["hwaddr"] != "" {
		err := infinibandSetDevMAC(saveData["host_name"], d.config["hwaddr"])
		if err != nil {
			return nil, fmt.Errorf("Failed to set the MAC address: %s", err)
		}
	}

	// Set the MTU.
	if d.config["mtu"] != "" {
		err = network.InterfaceSetMTU(saveData["host_name"], d.config["mtu"])
		if err != nil {
			return nil, err
		}
	}

	runConf := deviceConfig.RunConfig{}

	// Configure runConf with infiniband setup instructions.
	err = infinibandAddDevices(d.state, d.inst.DevicesPath(), d.name, vfDev, &runConf)
	if err != nil {
		return nil, err
	}

	err = d.volatileSet(saveData)
	if err != nil {
		return nil, err
	}

	runConf.NetworkInterface = []deviceConfig.RunConfigItem{
		{Key: "type", Value: "phys"},
		{Key: "name", Value: d.config["name"]},
		{Key: "flags", Value: "up"},
		{Key: "link", Value: saveData["host_name"]},
	}

	return &runConf, nil
}

// Stop is run when the device is removed from the instance.
func (d *infinibandSRIOV) Stop() (*deviceConfig.RunConfig, error) {
	v := d.volatileGet()
	runConf := deviceConfig.RunConfig{
		PostHooks:        []func() error{d.postStop},
		NetworkInterface: []deviceConfig.RunConfigItem{{Key: "link", Value: v["host_name"]}},
	}

	err := unixDeviceRemove(d.inst.DevicesPath(), IBDevPrefix, d.name, "", &runConf)
	if err != nil {
		return nil, err
	}

	return &runConf, nil
}

// postStop is run after the device is removed from the instance.
func (d *infinibandSRIOV) postStop() error {
	defer d.volatileSet(map[string]string{
		"host_name":         "",
		"last_state.hwaddr": "",
		"last_state.mtu":    "",
	})

	// Remove infiniband host files for this device.
	err := unixDeviceDeleteFiles(d.state, d.inst.DevicesPath(), IBDevPrefix, d.name, "")
	if err != nil {
		return fmt.Errorf("Failed to delete files for device '%s': %v", d.name, err)
	}

	// Restore hwaddr and mtu.
	v := d.volatileGet()
	if v["host_name"] != "" {
		err := networkRestorePhysicalNic(v["host_name"], v)
		if err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>lxd/device/infiniband/sriov: SetMTU usage<commit_after>package device

import (
	"fmt"

	"github.com/pkg/errors"

	deviceConfig "github.com/lxc/lxd/lxd/device/config"
	"github.com/lxc/lxd/lxd/instance"
	"github.com/lxc/lxd/lxd/instance/instancetype"
	"github.com/lxc/lxd/lxd/ip"
	"github.com/lxc/lxd/lxd/network"
	"github.com/lxc/lxd/lxd/resources"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
)

type infinibandSRIOV struct {
	deviceCommon
}

// validateConfig checks the supplied config for correctness.
func (d *infinibandSRIOV) validateConfig(instConf instance.ConfigReader) error {
	if !instanceSupported(instConf.Type(), instancetype.Container) {
		return ErrUnsupportedDevType
	}

	requiredFields := []string{"parent"}
	optionalFields := []string{
		"name",
		"mtu",
		"hwaddr",
	}

	rules := nicValidationRules(requiredFields, optionalFields, instConf)
	rules["hwaddr"] = func(value string) error {
		if value == "" {
			return nil
		}

		return infinibandValidMAC(value)
	}

	err := d.config.Validate(rules)
	if err != nil {
		return err
	}

	return nil
}

// validateEnvironment checks the runtime environment for correctness.
func (d *infinibandSRIOV) validateEnvironment() error {
	if d.inst.Type() == instancetype.Container && d.config["name"] == "" {
		return fmt.Errorf("Requires name property to start")
	}

	if !shared.PathExists(fmt.Sprintf("/sys/class/net/%s", d.config["parent"])) {
		return fmt.Errorf("Parent device '%s' doesn't exist", d.config["parent"])
	}

	return nil
}

// Start is run when the device is added to a running instance or instance is starting up.
func (d *infinibandSRIOV) Start() (*deviceConfig.RunConfig, error) {
	err := d.validateEnvironment()
	if err != nil {
		return nil, err
	}

	saveData := make(map[string]string)

	// Load network interface info.
	nics, err := resources.GetNetwork()
	if err != nil {
		return nil, err
	}

	// Filter the network interfaces to just infiniband devices related to parent.
	ibDevs := infinibandDevices(nics, d.config["parent"])

	// We don't count the parent as an available VF.
	delete(ibDevs, d.config["parent"])

	// Load any interfaces already allocated to other devices.
	reservedDevices, err := network.SRIOVGetHostDevicesInUse(d.state)
	if err != nil {
		return nil, err
	}

	// Remove reserved devices from available list.
	for k := range reservedDevices {
		delete(ibDevs, k)
	}

	if len(ibDevs) < 1 {
		return nil, fmt.Errorf("All virtual functions on parent device are already in use")
	}

	// Get first VF device that is free.
	var vfDev *api.ResourcesNetworkCardPort
	for _, v := range ibDevs {
		vfDev = v
		break
	}

	saveData["host_name"] = vfDev.ID

	// Record hwaddr and mtu before potentially modifying them.
	err = networkSnapshotPhysicalNic(saveData["host_name"], saveData)
	if err != nil {
		return nil, err
	}

	// Set the MAC address.
	if d.config["hwaddr"] != "" {
		err := infinibandSetDevMAC(saveData["host_name"], d.config["hwaddr"])
		if err != nil {
			return nil, fmt.Errorf("Failed to set the MAC address: %s", err)
		}
	}

	// Set the MTU.
	if d.config["mtu"] != "" {
		link := &ip.Link{Name: saveData["host_name"]}
		err := link.SetMTU(d.config["mtu"])
		if err != nil {
			return nil, errors.Wrapf(err, "Failed setting MTU %q on %q", d.config["mtu"], saveData["host_name"])
		}
	}

	runConf := deviceConfig.RunConfig{}

	// Configure runConf with infiniband setup instructions.
	err = infinibandAddDevices(d.state, d.inst.DevicesPath(), d.name, vfDev, &runConf)
	if err != nil {
		return nil, err
	}

	err = d.volatileSet(saveData)
	if err != nil {
		return nil, err
	}

	runConf.NetworkInterface = []deviceConfig.RunConfigItem{
		{Key: "type", Value: "phys"},
		{Key: "name", Value: d.config["name"]},
		{Key: "flags", Value: "up"},
		{Key: "link", Value: saveData["host_name"]},
	}

	return &runConf, nil
}

// Stop is run when the device is removed from the instance.
func (d *infinibandSRIOV) Stop() (*deviceConfig.RunConfig, error) {
	v := d.volatileGet()
	runConf := deviceConfig.RunConfig{
		PostHooks:        []func() error{d.postStop},
		NetworkInterface: []deviceConfig.RunConfigItem{{Key: "link", Value: v["host_name"]}},
	}

	err := unixDeviceRemove(d.inst.DevicesPath(), IBDevPrefix, d.name, "", &runConf)
	if err != nil {
		return nil, err
	}

	return &runConf, nil
}

// postStop is run after the device is removed from the instance.
func (d *infinibandSRIOV) postStop() error {
	defer d.volatileSet(map[string]string{
		"host_name":         "",
		"last_state.hwaddr": "",
		"last_state.mtu":    "",
	})

	// Remove infiniband host files for this device.
	err := unixDeviceDeleteFiles(d.state, d.inst.DevicesPath(), IBDevPrefix, d.name, "")
	if err != nil {
		return fmt.Errorf("Failed to delete files for device '%s': %v", d.name, err)
	}

	// Restore hwaddr and mtu.
	v := d.volatileGet()
	if v["host_name"] != "" {
		err := networkRestorePhysicalNic(v["host_name"], v)
		if err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2015 Google Inc. 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.
*/
package gax

import (
	"testing"
	"time"

	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
)

func TestRandomizedDelays(t *testing.T) {
	max := 200 * time.Millisecond
	settings := []CallOption{
		WithRetryCodes([]codes.Code{codes.Unavailable, codes.DeadlineExceeded}),
		WithDelayTimeoutSettings(10*time.Millisecond, max, 1.5),
	}

	deadline := time.Now().Add(1 * time.Second)
	ctx, _ := context.WithDeadline(context.Background(), deadline)
	var invokeTime time.Time
	Invoke(ctx, func(childCtx context.Context) error {
		// Keep failing, make sure we never slept more than max (plus a fudge factor)
		if !invokeTime.IsZero() {
			if time.Since(invokeTime) > (max + 20*time.Millisecond) {
				t.Fatalf("Slept too long: %v", max)
			}
		}
		invokeTime = time.Now()
		// Workaround for `go vet`: https://github.com/grpc/grpc-go/issues/90
		errf := grpc.Errorf
		return errf(codes.Unavailable, "")
	}, settings...)
}
<commit_msg>bigtable: better messaging for possibly flaky test<commit_after>/*
Copyright 2015 Google Inc. 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.
*/
package gax

import (
	"testing"
	"time"

	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
)

func TestRandomizedDelays(t *testing.T) {
	max := 200 * time.Millisecond
	settings := []CallOption{
		WithRetryCodes([]codes.Code{codes.Unavailable, codes.DeadlineExceeded}),
		WithDelayTimeoutSettings(10*time.Millisecond, max, 1.5),
	}

	deadline := time.Now().Add(1 * time.Second)
	ctx, _ := context.WithDeadline(context.Background(), deadline)
	var invokeTime time.Time
	Invoke(ctx, func(childCtx context.Context) error {
		// Keep failing, make sure we never slept more than max (plus a fudge factor)
		if !invokeTime.IsZero() {
			if got, want := time.Since(invokeTime), max; got > (want + 20*time.Millisecond) {
				t.Fatalf("Slept too long. Got: %v, want: %v", got, max)
			}
		}
		invokeTime = time.Now()
		// Workaround for `go vet`: https://github.com/grpc/grpc-go/issues/90
		errf := grpc.Errorf
		return errf(codes.Unavailable, "")
	}, settings...)
}
<|endoftext|>


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

// Package metrics provides a bleve.store.KVStore implementation that
// wraps another, real KVStore implementation, and uses go-metrics to
// track runtime performance metrics.
package metrics

import (
	"fmt"
	"io"

	"github.com/blevesearch/bleve/index/store"
	"github.com/blevesearch/bleve/registry"

	"github.com/rcrowley/go-metrics"
)

const Name = "metrics"

func init() {
	registry.RegisterKVStore(Name, StoreConstructor)
}

func StoreConstructor(config map[string]interface{}) (store.KVStore, error) {
	name, ok := config["kvStoreName_actual"].(string)
	if !ok || name == "" {
		return nil, fmt.Errorf("metrics: missing kvStoreName_actual,"+
			" config: %#v", config)
	}

	if name == Name {
		return nil, fmt.Errorf("metrics: circular kvStoreName_actual")
	}

	ctr := registry.KVStoreConstructorByName(name)
	if ctr == nil {
		return nil, fmt.Errorf("metrics: no kv store constructor,"+
			" kvStoreName_actual: %s", name)
	}

	kvs, err := ctr(config)
	if err != nil {
		return nil, err
	}

	return NewBleveMetricsStore(kvs), nil
}

func NewBleveMetricsStore(o store.KVStore) *Store {
	return &Store{
		o: o,

		TimerReaderGet:         metrics.NewTimer(),
		TimerReaderIterator:    metrics.NewTimer(),
		TimerWriterGet:         metrics.NewTimer(),
		TimerWriterIterator:    metrics.NewTimer(),
		TimerWriterSet:         metrics.NewTimer(),
		TimerWriterDelete:      metrics.NewTimer(),
		TimerIteratorSeekFirst: metrics.NewTimer(),
		TimerIteratorSeek:      metrics.NewTimer(),
		TimerIteratorNext:      metrics.NewTimer(),
		TimerBatchMerge:        metrics.NewTimer(),
		TimerBatchExecute:      metrics.NewTimer(),
	}
}

// The following structs are wrappers around "real" bleve kvstore
// implementations.

type Store struct {
	o store.KVStore

	TimerReaderGet         metrics.Timer
	TimerReaderIterator    metrics.Timer
	TimerWriterGet         metrics.Timer
	TimerWriterIterator    metrics.Timer
	TimerWriterSet         metrics.Timer
	TimerWriterDelete      metrics.Timer
	TimerIteratorSeekFirst metrics.Timer
	TimerIteratorSeek      metrics.Timer
	TimerIteratorNext      metrics.Timer
	TimerBatchMerge        metrics.Timer
	TimerBatchExecute      metrics.Timer
}

type Reader struct {
	s *Store
	o store.KVReader
}

type Writer struct {
	s *Store
	o store.KVWriter
}

type Iterator struct {
	s *Store
	o store.KVIterator
}

type Batch struct {
	s *Store
	o store.KVBatch
}

func (s *Store) Close() error {
	return s.o.Close()
}

func (s *Store) Reader() (store.KVReader, error) {
	o, err := s.o.Reader()
	if err != nil {
		return nil, err
	}
	return &Reader{s: s, o: o}, nil
}

func (s *Store) Writer() (store.KVWriter, error) {
	o, err := s.o.Writer()
	if err != nil {
		return nil, err
	}
	return &Writer{s: s, o: o}, nil
}

func (s *Store) Actual() store.KVStore {
	return s.o
}

func (w *Reader) Get(key []byte) (v []byte, err error) {
	w.s.TimerReaderGet.Time(func() {
		v, err = w.o.Get(key)
	})
	return
}

func (w *Reader) Iterator(key []byte) (i store.KVIterator) {
	w.s.TimerReaderIterator.Time(func() {
		i = &Iterator{s: w.s, o: w.o.Iterator(key)}
	})
	return
}

func (w *Reader) Close() error {
	return w.o.Close()
}

func (w *Writer) Get(key []byte) (v []byte, err error) {
	w.s.TimerWriterGet.Time(func() {
		v, err = w.o.Get(key)
	})
	return
}

func (w *Writer) Iterator(key []byte) (i store.KVIterator) {
	w.s.TimerWriterIterator.Time(func() {
		i = &Iterator{s: w.s, o: w.o.Iterator(key)}
	})
	return
}

func (w *Writer) Close() error {
	return w.o.Close()
}

func (w *Writer) Set(key, val []byte) (err error) {
	w.s.TimerWriterSet.Time(func() {
		err = w.o.Set(key, val)
	})
	return
}

func (w *Writer) Delete(key []byte) (err error) {
	w.s.TimerWriterDelete.Time(func() {
		err = w.o.Delete(key)
	})
	return
}

func (w *Writer) NewBatch() store.KVBatch {
	return &Batch{s: w.s, o: w.o.NewBatch()}
}

func (w *Iterator) SeekFirst() {
	w.s.TimerIteratorSeekFirst.Time(func() {
		w.o.SeekFirst()
	})
}

func (w *Iterator) Seek(x []byte) {
	w.s.TimerIteratorSeek.Time(func() {
		w.o.Seek(x)
	})
}

func (w *Iterator) Next() {
	w.s.TimerIteratorNext.Time(func() {
		w.o.Next()
	})
}

func (w *Iterator) Current() ([]byte, []byte, bool) {
	return w.o.Current()
}

func (w *Iterator) Key() []byte {
	return w.o.Key()
}

func (w *Iterator) Value() []byte {
	return w.o.Value()
}

func (w *Iterator) Valid() bool {
	return w.o.Valid()
}

func (w *Iterator) Close() error {
	return w.o.Close()
}

func (w *Batch) Set(key, val []byte) {
	w.o.Set(key, val)
}

func (w *Batch) Delete(key []byte) {
	w.o.Delete(key)
}

func (w *Batch) Merge(key []byte, oper store.AssociativeMerge) {
	w.s.TimerBatchMerge.Time(func() {
		w.o.Merge(key, oper)
	})
}

func (w *Batch) Execute() (err error) {
	w.s.TimerBatchExecute.Time(func() {
		err = w.o.Execute()
	})
	return
}

func (w *Batch) Close() error {
	return w.o.Close()
}

// --------------------------------------------------------

func (s *Store) WriteJSON(w io.Writer) {
	w.Write([]byte(`{"TimerReaderGet":`))
	WriteTimerJSON(w, s.TimerReaderGet)
	w.Write([]byte(`,"TimerReaderIterator":`))
	WriteTimerJSON(w, s.TimerReaderIterator)
	w.Write([]byte(`,"TimerWriterGet":`))
	WriteTimerJSON(w, s.TimerWriterGet)
	w.Write([]byte(`,"TimerWriterIterator":`))
	WriteTimerJSON(w, s.TimerWriterIterator)
	w.Write([]byte(`,"TimerWriterSet":`))
	WriteTimerJSON(w, s.TimerWriterSet)
	w.Write([]byte(`,"TimerWriterDelete":`))
	WriteTimerJSON(w, s.TimerWriterDelete)
	w.Write([]byte(`,"TimerIteratorSeekFirst":`))
	WriteTimerJSON(w, s.TimerIteratorSeekFirst)
	w.Write([]byte(`,"TimerIteratorSeek":`))
	WriteTimerJSON(w, s.TimerIteratorSeek)
	w.Write([]byte(`,"TimerIteratorNext":`))
	WriteTimerJSON(w, s.TimerIteratorNext)
	w.Write([]byte(`,"TimerBatchMerge":`))
	WriteTimerJSON(w, s.TimerBatchMerge)
	w.Write([]byte(`,"TimerBatchExecute":`))
	WriteTimerJSON(w, s.TimerBatchExecute)
	w.Write([]byte(`}`))
}

func (s *Store) WriteCSVHeader(w io.Writer) {
	WriteTimerCSVHeader(w, "TimerReaderGet")
	WriteTimerCSVHeader(w, "TimerReaderIterator")
	WriteTimerCSVHeader(w, "TimerWriterGet")
	WriteTimerCSVHeader(w, "TimerWriterIterator")
	WriteTimerCSVHeader(w, "TimerWriterSet")
	WriteTimerCSVHeader(w, "TimerWriterDelete")
	WriteTimerCSVHeader(w, "TimerIteratorSeekFirst")
	WriteTimerCSVHeader(w, "TimerIteratorSeek")
	WriteTimerCSVHeader(w, "TimerIteratorNext")
	WriteTimerCSVHeader(w, "TimerBatchMerge")
	WriteTimerCSVHeader(w, "TimerBatchExecute")
}

func (s *Store) WriteCSV(w io.Writer) {
	WriteTimerCSV(w, s.TimerReaderGet)
	WriteTimerCSV(w, s.TimerReaderIterator)
	WriteTimerCSV(w, s.TimerWriterGet)
	WriteTimerCSV(w, s.TimerWriterIterator)
	WriteTimerCSV(w, s.TimerWriterSet)
	WriteTimerCSV(w, s.TimerWriterDelete)
	WriteTimerCSV(w, s.TimerIteratorSeekFirst)
	WriteTimerCSV(w, s.TimerIteratorSeek)
	WriteTimerCSV(w, s.TimerIteratorNext)
	WriteTimerCSV(w, s.TimerBatchMerge)
	WriteTimerCSV(w, s.TimerBatchExecute)
}

// --------------------------------------------------------

// NOTE: This is copy & pasted from cbft as otherwise there
// would be an import cycle.

var timerPercentiles = []float64{0.5, 0.75, 0.95, 0.99, 0.999}

func WriteTimerJSON(w io.Writer, timer metrics.Timer) {
	t := timer.Snapshot()
	p := t.Percentiles(timerPercentiles)

	fmt.Fprintf(w, `{"count":%9d,`, t.Count())
	fmt.Fprintf(w, `"min":%9d,`, t.Min())
	fmt.Fprintf(w, `"max":%9d,`, t.Max())
	fmt.Fprintf(w, `"mean":%12.2f,`, t.Mean())
	fmt.Fprintf(w, `"stddev":%12.2f,`, t.StdDev())
	fmt.Fprintf(w, `"percentiles":{`)
	fmt.Fprintf(w, `"median":%12.2f,`, p[0])
	fmt.Fprintf(w, `"75%%":%12.2f,`, p[1])
	fmt.Fprintf(w, `"95%%":%12.2f,`, p[2])
	fmt.Fprintf(w, `"99%%":%12.2f,`, p[3])
	fmt.Fprintf(w, `"99.9%%":%12.2f},`, p[4])
	fmt.Fprintf(w, `"rates":{`)
	fmt.Fprintf(w, `"1-min":%12.2f,`, t.Rate1())
	fmt.Fprintf(w, `"5-min":%12.2f,`, t.Rate5())
	fmt.Fprintf(w, `"15-min":%12.2f,`, t.Rate15())
	fmt.Fprintf(w, `"mean":%12.2f}}`, t.RateMean())
}

func WriteTimerCSVHeader(w io.Writer, prefix string) {
	fmt.Fprintf(w, "%s-count,", prefix)
	fmt.Fprintf(w, "%s-min,", prefix)
	fmt.Fprintf(w, "%s-max,", prefix)
	fmt.Fprintf(w, "%s-mean,", prefix)
	fmt.Fprintf(w, "%s-stddev,", prefix)
	fmt.Fprintf(w, "%s-percentile-50%%,", prefix)
	fmt.Fprintf(w, "%s-percentile-75%%,", prefix)
	fmt.Fprintf(w, "%s-percentile-95%%,", prefix)
	fmt.Fprintf(w, "%s-percentile-99%%,", prefix)
	fmt.Fprintf(w, "%s-percentile-99.9%%,", prefix)
	fmt.Fprintf(w, "%s-rate-1-min,", prefix)
	fmt.Fprintf(w, "%s-rate-5-min,", prefix)
	fmt.Fprintf(w, "%s-rate-15-min,", prefix)
	fmt.Fprintf(w, "%s-rate-mean", prefix)
}

func WriteTimerCSV(w io.Writer, timer metrics.Timer) {
	t := timer.Snapshot()
	p := t.Percentiles(timerPercentiles)

	fmt.Fprintf(w, `%d,`, t.Count())
	fmt.Fprintf(w, `%d,`, t.Min())
	fmt.Fprintf(w, `%d,`, t.Max())
	fmt.Fprintf(w, `%f,`, t.Mean())
	fmt.Fprintf(w, `%f,`, t.StdDev())
	fmt.Fprintf(w, `%f,`, p[0])
	fmt.Fprintf(w, `%f,`, p[1])
	fmt.Fprintf(w, `%f,`, p[2])
	fmt.Fprintf(w, `%f,`, p[3])
	fmt.Fprintf(w, `%f,`, p[4])
	fmt.Fprintf(w, `%f,`, t.Rate1())
	fmt.Fprintf(w, `%f,`, t.Rate5())
	fmt.Fprintf(w, `%f,`, t.Rate15())
	fmt.Fprintf(w, `%f`, t.RateMean())
}
<commit_msg>added debug tag for metrics KVStore<commit_after>//  Copyright (c) 2015 Couchbase, Inc.
//  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.

// +build debug

// Package metrics provides a bleve.store.KVStore implementation that
// wraps another, real KVStore implementation, and uses go-metrics to
// track runtime performance metrics.
package metrics

import (
	"fmt"
	"io"

	"github.com/blevesearch/bleve/index/store"
	"github.com/blevesearch/bleve/registry"

	"github.com/rcrowley/go-metrics"
)

const Name = "metrics"

func init() {
	registry.RegisterKVStore(Name, StoreConstructor)
}

func StoreConstructor(config map[string]interface{}) (store.KVStore, error) {
	name, ok := config["kvStoreName_actual"].(string)
	if !ok || name == "" {
		return nil, fmt.Errorf("metrics: missing kvStoreName_actual,"+
			" config: %#v", config)
	}

	if name == Name {
		return nil, fmt.Errorf("metrics: circular kvStoreName_actual")
	}

	ctr := registry.KVStoreConstructorByName(name)
	if ctr == nil {
		return nil, fmt.Errorf("metrics: no kv store constructor,"+
			" kvStoreName_actual: %s", name)
	}

	kvs, err := ctr(config)
	if err != nil {
		return nil, err
	}

	return NewBleveMetricsStore(kvs), nil
}

func NewBleveMetricsStore(o store.KVStore) *Store {
	return &Store{
		o: o,

		TimerReaderGet:         metrics.NewTimer(),
		TimerReaderIterator:    metrics.NewTimer(),
		TimerWriterGet:         metrics.NewTimer(),
		TimerWriterIterator:    metrics.NewTimer(),
		TimerWriterSet:         metrics.NewTimer(),
		TimerWriterDelete:      metrics.NewTimer(),
		TimerIteratorSeekFirst: metrics.NewTimer(),
		TimerIteratorSeek:      metrics.NewTimer(),
		TimerIteratorNext:      metrics.NewTimer(),
		TimerBatchMerge:        metrics.NewTimer(),
		TimerBatchExecute:      metrics.NewTimer(),
	}
}

// The following structs are wrappers around "real" bleve kvstore
// implementations.

type Store struct {
	o store.KVStore

	TimerReaderGet         metrics.Timer
	TimerReaderIterator    metrics.Timer
	TimerWriterGet         metrics.Timer
	TimerWriterIterator    metrics.Timer
	TimerWriterSet         metrics.Timer
	TimerWriterDelete      metrics.Timer
	TimerIteratorSeekFirst metrics.Timer
	TimerIteratorSeek      metrics.Timer
	TimerIteratorNext      metrics.Timer
	TimerBatchMerge        metrics.Timer
	TimerBatchExecute      metrics.Timer
}

type Reader struct {
	s *Store
	o store.KVReader
}

type Writer struct {
	s *Store
	o store.KVWriter
}

type Iterator struct {
	s *Store
	o store.KVIterator
}

type Batch struct {
	s *Store
	o store.KVBatch
}

func (s *Store) Close() error {
	return s.o.Close()
}

func (s *Store) Reader() (store.KVReader, error) {
	o, err := s.o.Reader()
	if err != nil {
		return nil, err
	}
	return &Reader{s: s, o: o}, nil
}

func (s *Store) Writer() (store.KVWriter, error) {
	o, err := s.o.Writer()
	if err != nil {
		return nil, err
	}
	return &Writer{s: s, o: o}, nil
}

func (s *Store) Actual() store.KVStore {
	return s.o
}

func (w *Reader) Get(key []byte) (v []byte, err error) {
	w.s.TimerReaderGet.Time(func() {
		v, err = w.o.Get(key)
	})
	return
}

func (w *Reader) Iterator(key []byte) (i store.KVIterator) {
	w.s.TimerReaderIterator.Time(func() {
		i = &Iterator{s: w.s, o: w.o.Iterator(key)}
	})
	return
}

func (w *Reader) Close() error {
	return w.o.Close()
}

func (w *Writer) Get(key []byte) (v []byte, err error) {
	w.s.TimerWriterGet.Time(func() {
		v, err = w.o.Get(key)
	})
	return
}

func (w *Writer) Iterator(key []byte) (i store.KVIterator) {
	w.s.TimerWriterIterator.Time(func() {
		i = &Iterator{s: w.s, o: w.o.Iterator(key)}
	})
	return
}

func (w *Writer) Close() error {
	return w.o.Close()
}

func (w *Writer) Set(key, val []byte) (err error) {
	w.s.TimerWriterSet.Time(func() {
		err = w.o.Set(key, val)
	})
	return
}

func (w *Writer) Delete(key []byte) (err error) {
	w.s.TimerWriterDelete.Time(func() {
		err = w.o.Delete(key)
	})
	return
}

func (w *Writer) NewBatch() store.KVBatch {
	return &Batch{s: w.s, o: w.o.NewBatch()}
}

func (w *Iterator) SeekFirst() {
	w.s.TimerIteratorSeekFirst.Time(func() {
		w.o.SeekFirst()
	})
}

func (w *Iterator) Seek(x []byte) {
	w.s.TimerIteratorSeek.Time(func() {
		w.o.Seek(x)
	})
}

func (w *Iterator) Next() {
	w.s.TimerIteratorNext.Time(func() {
		w.o.Next()
	})
}

func (w *Iterator) Current() ([]byte, []byte, bool) {
	return w.o.Current()
}

func (w *Iterator) Key() []byte {
	return w.o.Key()
}

func (w *Iterator) Value() []byte {
	return w.o.Value()
}

func (w *Iterator) Valid() bool {
	return w.o.Valid()
}

func (w *Iterator) Close() error {
	return w.o.Close()
}

func (w *Batch) Set(key, val []byte) {
	w.o.Set(key, val)
}

func (w *Batch) Delete(key []byte) {
	w.o.Delete(key)
}

func (w *Batch) Merge(key []byte, oper store.AssociativeMerge) {
	w.s.TimerBatchMerge.Time(func() {
		w.o.Merge(key, oper)
	})
}

func (w *Batch) Execute() (err error) {
	w.s.TimerBatchExecute.Time(func() {
		err = w.o.Execute()
	})
	return
}

func (w *Batch) Close() error {
	return w.o.Close()
}

// --------------------------------------------------------

func (s *Store) WriteJSON(w io.Writer) {
	w.Write([]byte(`{"TimerReaderGet":`))
	WriteTimerJSON(w, s.TimerReaderGet)
	w.Write([]byte(`,"TimerReaderIterator":`))
	WriteTimerJSON(w, s.TimerReaderIterator)
	w.Write([]byte(`,"TimerWriterGet":`))
	WriteTimerJSON(w, s.TimerWriterGet)
	w.Write([]byte(`,"TimerWriterIterator":`))
	WriteTimerJSON(w, s.TimerWriterIterator)
	w.Write([]byte(`,"TimerWriterSet":`))
	WriteTimerJSON(w, s.TimerWriterSet)
	w.Write([]byte(`,"TimerWriterDelete":`))
	WriteTimerJSON(w, s.TimerWriterDelete)
	w.Write([]byte(`,"TimerIteratorSeekFirst":`))
	WriteTimerJSON(w, s.TimerIteratorSeekFirst)
	w.Write([]byte(`,"TimerIteratorSeek":`))
	WriteTimerJSON(w, s.TimerIteratorSeek)
	w.Write([]byte(`,"TimerIteratorNext":`))
	WriteTimerJSON(w, s.TimerIteratorNext)
	w.Write([]byte(`,"TimerBatchMerge":`))
	WriteTimerJSON(w, s.TimerBatchMerge)
	w.Write([]byte(`,"TimerBatchExecute":`))
	WriteTimerJSON(w, s.TimerBatchExecute)
	w.Write([]byte(`}`))
}

func (s *Store) WriteCSVHeader(w io.Writer) {
	WriteTimerCSVHeader(w, "TimerReaderGet")
	WriteTimerCSVHeader(w, "TimerReaderIterator")
	WriteTimerCSVHeader(w, "TimerWriterGet")
	WriteTimerCSVHeader(w, "TimerWriterIterator")
	WriteTimerCSVHeader(w, "TimerWriterSet")
	WriteTimerCSVHeader(w, "TimerWriterDelete")
	WriteTimerCSVHeader(w, "TimerIteratorSeekFirst")
	WriteTimerCSVHeader(w, "TimerIteratorSeek")
	WriteTimerCSVHeader(w, "TimerIteratorNext")
	WriteTimerCSVHeader(w, "TimerBatchMerge")
	WriteTimerCSVHeader(w, "TimerBatchExecute")
}

func (s *Store) WriteCSV(w io.Writer) {
	WriteTimerCSV(w, s.TimerReaderGet)
	WriteTimerCSV(w, s.TimerReaderIterator)
	WriteTimerCSV(w, s.TimerWriterGet)
	WriteTimerCSV(w, s.TimerWriterIterator)
	WriteTimerCSV(w, s.TimerWriterSet)
	WriteTimerCSV(w, s.TimerWriterDelete)
	WriteTimerCSV(w, s.TimerIteratorSeekFirst)
	WriteTimerCSV(w, s.TimerIteratorSeek)
	WriteTimerCSV(w, s.TimerIteratorNext)
	WriteTimerCSV(w, s.TimerBatchMerge)
	WriteTimerCSV(w, s.TimerBatchExecute)
}

// --------------------------------------------------------

// NOTE: This is copy & pasted from cbft as otherwise there
// would be an import cycle.

var timerPercentiles = []float64{0.5, 0.75, 0.95, 0.99, 0.999}

func WriteTimerJSON(w io.Writer, timer metrics.Timer) {
	t := timer.Snapshot()
	p := t.Percentiles(timerPercentiles)

	fmt.Fprintf(w, `{"count":%9d,`, t.Count())
	fmt.Fprintf(w, `"min":%9d,`, t.Min())
	fmt.Fprintf(w, `"max":%9d,`, t.Max())
	fmt.Fprintf(w, `"mean":%12.2f,`, t.Mean())
	fmt.Fprintf(w, `"stddev":%12.2f,`, t.StdDev())
	fmt.Fprintf(w, `"percentiles":{`)
	fmt.Fprintf(w, `"median":%12.2f,`, p[0])
	fmt.Fprintf(w, `"75%%":%12.2f,`, p[1])
	fmt.Fprintf(w, `"95%%":%12.2f,`, p[2])
	fmt.Fprintf(w, `"99%%":%12.2f,`, p[3])
	fmt.Fprintf(w, `"99.9%%":%12.2f},`, p[4])
	fmt.Fprintf(w, `"rates":{`)
	fmt.Fprintf(w, `"1-min":%12.2f,`, t.Rate1())
	fmt.Fprintf(w, `"5-min":%12.2f,`, t.Rate5())
	fmt.Fprintf(w, `"15-min":%12.2f,`, t.Rate15())
	fmt.Fprintf(w, `"mean":%12.2f}}`, t.RateMean())
}

func WriteTimerCSVHeader(w io.Writer, prefix string) {
	fmt.Fprintf(w, "%s-count,", prefix)
	fmt.Fprintf(w, "%s-min,", prefix)
	fmt.Fprintf(w, "%s-max,", prefix)
	fmt.Fprintf(w, "%s-mean,", prefix)
	fmt.Fprintf(w, "%s-stddev,", prefix)
	fmt.Fprintf(w, "%s-percentile-50%%,", prefix)
	fmt.Fprintf(w, "%s-percentile-75%%,", prefix)
	fmt.Fprintf(w, "%s-percentile-95%%,", prefix)
	fmt.Fprintf(w, "%s-percentile-99%%,", prefix)
	fmt.Fprintf(w, "%s-percentile-99.9%%,", prefix)
	fmt.Fprintf(w, "%s-rate-1-min,", prefix)
	fmt.Fprintf(w, "%s-rate-5-min,", prefix)
	fmt.Fprintf(w, "%s-rate-15-min,", prefix)
	fmt.Fprintf(w, "%s-rate-mean", prefix)
}

func WriteTimerCSV(w io.Writer, timer metrics.Timer) {
	t := timer.Snapshot()
	p := t.Percentiles(timerPercentiles)

	fmt.Fprintf(w, `%d,`, t.Count())
	fmt.Fprintf(w, `%d,`, t.Min())
	fmt.Fprintf(w, `%d,`, t.Max())
	fmt.Fprintf(w, `%f,`, t.Mean())
	fmt.Fprintf(w, `%f,`, t.StdDev())
	fmt.Fprintf(w, `%f,`, p[0])
	fmt.Fprintf(w, `%f,`, p[1])
	fmt.Fprintf(w, `%f,`, p[2])
	fmt.Fprintf(w, `%f,`, p[3])
	fmt.Fprintf(w, `%f,`, p[4])
	fmt.Fprintf(w, `%f,`, t.Rate1())
	fmt.Fprintf(w, `%f,`, t.Rate5())
	fmt.Fprintf(w, `%f,`, t.Rate15())
	fmt.Fprintf(w, `%f`, t.RateMean())
}
<|endoftext|>


<commit_before>// Copyright 2015 henrylee2cn Author. 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.

package surfer

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"mime"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

// 基于Phantomjs的下载器实现，作为surfer的补充
// 效率较surfer会慢很多，但是因为模拟浏览器，破防性更好
// 支持UserAgent/TryTimes/RetryPause/自定义js
type (
	Phantom struct {
		PhantomjsFile string            //Phantomjs完整文件名
		TempJsDir     string            //临时js存放目录
		jsFileMap     map[string]string //已存在的js文件
	}
	Response struct {
		Cookies []string
		Body    string
	}
)

func NewPhantom(phantomjsFile, tempJsDir string) Surfer {
	phantom := &Phantom{
		PhantomjsFile: phantomjsFile,
		TempJsDir:     tempJsDir,
		jsFileMap:     make(map[string]string),
	}
	if !filepath.IsAbs(phantom.PhantomjsFile) {
		phantom.PhantomjsFile, _ = filepath.Abs(phantom.PhantomjsFile)
	}
	if !filepath.IsAbs(phantom.TempJsDir) {
		phantom.TempJsDir, _ = filepath.Abs(phantom.TempJsDir)
	}
	// 创建/打开目录
	err := os.MkdirAll(phantom.TempJsDir, 0777)
	if err != nil {
		log.Printf("[E] Surfer: %v\n", err)
		return phantom
	}
	phantom.createJsFile("js", js)
	return phantom
}

// 实现surfer下载器接口
func (self *Phantom) Download(req Request) (resp *http.Response, err error) {
	var encoding = "utf-8"
	if _, params, err := mime.ParseMediaType(req.GetHeader().Get("Content-Type")); err == nil {
		if cs, ok := params["charset"]; ok {
			encoding = strings.ToLower(strings.TrimSpace(cs))
		}
	}

	req.GetHeader().Del("Content-Type")

	param, err := NewParam(req)
	if err != nil {
		return nil, err
	}
	resp = param.writeback(resp)

	var args = []string{
		self.jsFileMap["js"],
		req.GetUrl(),
		param.header.Get("Cookie"),
		encoding,
		param.header.Get("User-Agent"),
		req.GetPostData(),
		strings.ToLower(param.method),
	}

	for i := 0; i < param.tryTimes; i++ {
		cmd := exec.Command(self.PhantomjsFile, args...)
		if resp.Body, err = cmd.StdoutPipe(); err != nil {
			time.Sleep(param.retryPause)
			continue
		}
		err = cmd.Start()
		if err != nil || resp.Body == nil {
			time.Sleep(param.retryPause)
			continue
		}
		var b []byte
		b, err = ioutil.ReadAll(resp.Body)
		if err != nil {
			time.Sleep(param.retryPause)
			continue
		}
		retResp := Response{}
		err = json.Unmarshal(b, &retResp)
		if err != nil {
			time.Sleep(param.retryPause)
			continue
		}
		resp.Header = param.header
		for _, cookie := range retResp.Cookies {
			resp.Header.Add("Set-Cookie", cookie)
		}
		resp.Body = ioutil.NopCloser(strings.NewReader(retResp.Body))
		break
	}

	if err == nil {
		resp.StatusCode = http.StatusOK
		resp.Status = http.StatusText(http.StatusOK)
	} else {
		resp.StatusCode = http.StatusBadGateway
		resp.Status = http.StatusText(http.StatusBadGateway)
	}
	return
}

//销毁js临时文件
func (self *Phantom) DestroyJsFiles() {
	p, _ := filepath.Split(self.TempJsDir)
	if p == "" {
		return
	}
	for _, filename := range self.jsFileMap {
		os.Remove(filename)
	}
	if len(WalkDir(p)) == 1 {
		os.Remove(p)
	}
}

func (self *Phantom) createJsFile(fileName, jsCode string) {
	fullFileName := filepath.Join(self.TempJsDir, fileName)
	// 创建并写入文件
	f, _ := os.Create(fullFileName)
	f.Write([]byte(jsCode))
	f.Close()
	self.jsFileMap[fileName] = fullFileName
}

/*
* system.args[0] == post.js
* system.args[1] == url
* system.args[2] == cookie
* system.args[3] == pageEncode
* system.args[4] == userAgent
* system.args[5] == postdata
* system.args[6] == method
 */
const js string = `
var system = require('system');
var page = require('webpage').create();
var url = system.args[1];
var cookie = system.args[2];
var pageEncode = system.args[3];
var userAgent = system.args[4];
var postdata = system.args[5];
var method = system.args[6];
page.onResourceRequested = function(requestData, request) {
    request.setHeader('Cookie', cookie)
};
phantom.outputEncoding = pageEncode;
page.settings.userAgent = userAgent;
page.open(url, method, postdata, function(status) {
   if (status !== 'success') {
        console.log('Unable to access network');
    } else {
        var cookies = new Array();
        for(var i in page.cookies) {
        	var cookie = page.cookies[i];
        	var c = cookie["name"] + "=" + cookie["value"];
        	for (var obj in cookie){
        		if(obj == 'name' || obj == 'value'){
        			continue;
        		}
				c +=  "; " +　obj + "=" +  cookie[obj];
    		}
			cookies[i] = c;
		}
        var resp = {
            "Cookies": cookies,
            "Body": page.content
        };
        console.log(JSON.stringify(resp));
    }
    phantom.exit();
});
`
<commit_msg>fix too many phantomjs process:Force the phantomjs process to be shutdown<commit_after>// Copyright 2015 henrylee2cn Author. 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.

package surfer

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"mime"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

// 基于Phantomjs的下载器实现，作为surfer的补充
// 效率较surfer会慢很多，但是因为模拟浏览器，破防性更好
// 支持UserAgent/TryTimes/RetryPause/自定义js
type (
	Phantom struct {
		PhantomjsFile string            //Phantomjs完整文件名
		TempJsDir     string            //临时js存放目录
		jsFileMap     map[string]string //已存在的js文件
	}
	Response struct {
		Cookies []string
		Body    string
	}
)

func NewPhantom(phantomjsFile, tempJsDir string) Surfer {
	phantom := &Phantom{
		PhantomjsFile: phantomjsFile,
		TempJsDir:     tempJsDir,
		jsFileMap:     make(map[string]string),
	}
	if !filepath.IsAbs(phantom.PhantomjsFile) {
		phantom.PhantomjsFile, _ = filepath.Abs(phantom.PhantomjsFile)
	}
	if !filepath.IsAbs(phantom.TempJsDir) {
		phantom.TempJsDir, _ = filepath.Abs(phantom.TempJsDir)
	}
	// 创建/打开目录
	err := os.MkdirAll(phantom.TempJsDir, 0777)
	if err != nil {
		log.Printf("[E] Surfer: %v\n", err)
		return phantom
	}
	phantom.createJsFile("js", js)
	return phantom
}

// 实现surfer下载器接口
func (self *Phantom) Download(req Request) (resp *http.Response, err error) {
	var encoding = "utf-8"
	if _, params, err := mime.ParseMediaType(req.GetHeader().Get("Content-Type")); err == nil {
		if cs, ok := params["charset"]; ok {
			encoding = strings.ToLower(strings.TrimSpace(cs))
		}
	}

	req.GetHeader().Del("Content-Type")

	param, err := NewParam(req)
	if err != nil {
		return nil, err
	}
	resp = param.writeback(resp)

	var args = []string{
		self.jsFileMap["js"],
		req.GetUrl(),
		param.header.Get("Cookie"),
		encoding,
		param.header.Get("User-Agent"),
		req.GetPostData(),
		strings.ToLower(param.method),
	}

	for i := 0; i < param.tryTimes; i++ {
		cmd := exec.Command(self.PhantomjsFile, args...)
		b, err := cmd.CombinedOutput()
		if cmd.Process != nil {
			cmd.Process.Kill()
		}
		if err != nil || len(b) == 0 {
			time.Sleep(param.retryPause)
			continue
		}
		retResp := Response{}
		err = json.Unmarshal(b, &retResp)
		if err != nil {
			time.Sleep(param.retryPause)
			continue
		}
		resp.Header = param.header
		for _, cookie := range retResp.Cookies {
			resp.Header.Add("Set-Cookie", cookie)
		}
		resp.Body = ioutil.NopCloser(strings.NewReader(retResp.Body))
		break
	}

	if err == nil {
		resp.StatusCode = http.StatusOK
		resp.Status = http.StatusText(http.StatusOK)
	} else {
		resp.StatusCode = http.StatusBadGateway
		resp.Status = http.StatusText(http.StatusBadGateway)
	}
	return
}

//销毁js临时文件
func (self *Phantom) DestroyJsFiles() {
	p, _ := filepath.Split(self.TempJsDir)
	if p == "" {
		return
	}
	for _, filename := range self.jsFileMap {
		os.Remove(filename)
	}
	if len(WalkDir(p)) == 1 {
		os.Remove(p)
	}
}

func (self *Phantom) createJsFile(fileName, jsCode string) {
	fullFileName := filepath.Join(self.TempJsDir, fileName)
	// 创建并写入文件
	f, _ := os.Create(fullFileName)
	f.Write([]byte(jsCode))
	f.Close()
	self.jsFileMap[fileName] = fullFileName
}

/*
* system.args[0] == post.js
* system.args[1] == url
* system.args[2] == cookie
* system.args[3] == pageEncode
* system.args[4] == userAgent
* system.args[5] == postdata
* system.args[6] == method
 */
const js string = `
var system = require('system');
var page = require('webpage').create();
var url = system.args[1];
var cookie = system.args[2];
var pageEncode = system.args[3];
var userAgent = system.args[4];
var postdata = system.args[5];
var method = system.args[6];
page.onResourceRequested = function(requestData, request) {
    request.setHeader('Cookie', cookie)
};
phantom.outputEncoding = pageEncode;
page.settings.userAgent = userAgent;
page.open(url, method, postdata, function(status) {
   if (status !== 'success') {
        console.log('Unable to access network');
    } else {
        var cookies = new Array();
        for(var i in page.cookies) {
        	var cookie = page.cookies[i];
        	var c = cookie["name"] + "=" + cookie["value"];
        	for (var obj in cookie){
        		if(obj == 'name' || obj == 'value'){
        			continue;
        		}
				c +=  "; " +　obj + "=" +  cookie[obj];
    		}
			cookies[i] = c;
		}
        var resp = {
            "Cookies": cookies,
            "Body": page.content
        };
        console.log(JSON.stringify(resp));
    }
    phantom.exit();
});
`
<|endoftext|>


<commit_before>// Copyright (c) 2015-2017 The usbtmc developers. All rights reserved.
// Project site: https://github.com/gotmc/usbtmc
// Use of this source code is governed by a MIT-style license that
// can be found in the LICENSE.txt file for the project.

package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"time"

	"github.com/gotmc/usbtmc"
)

var (
	readonly = flag.Bool("readonly", false, "Only read from the controller")
	debug    = flag.Int("debug", 0, "USB debugging control")
)

func main() {
	flag.Parse()

	start := time.Now()
	ctx := usbtmc.NewContext()
	defer ctx.Close()

	if *debug != 0 {
		ctx.Debug(*debug)
	}

	start = time.Now()
	fg, err := ctx.NewInstrument("USB0::2391::1031::MY44035349::INSTR")
	if err != nil {
		log.Fatalf("NewInstrument error: %s", err)
	}
	log.Printf("%.2fs to setup instrument\n", time.Since(start).Seconds())
	start = time.Now()
	fmt.Printf(
		"Found the Arb Wave Gen S/N %s by Vendor ID %d with Product ID %d\n",
		fg.Device.Descriptor.SerialNumber,
		fg.Device.Descriptor.Vendor,
		fg.Device.Descriptor.Product)
	// Send commands to waveform generator
	fg.Write([]byte("apply:sinusoid 2340, 0.1, 0.0")) // Write using byte slice
	io.WriteString(fg, "burst:internal:period 0.112") // WriteString using io's Writer interface
	fg.WriteString("burst:internal:period 0.112")     // WriteString
	fg.WriteString("burst:ncycles 131")
	fg.WriteString("burst:state on")
	fg.WriteString("*idn?")

	start = time.Now()
	var buf [1024]byte
	bytesRead, err := fg.Read(buf[:])
	log.Printf("%.2fs to read %d bytes\n", time.Since(start).Seconds(), bytesRead)
	if err != nil {
		log.Printf("Error reading: %s", err)
	}
	fmt.Printf("Read %d bytes = %s", bytesRead, buf[12:bytesRead])
	fmt.Printf("Last rune read = %x\n", buf[bytesRead-1:bytesRead])
	fmt.Printf("Last rune read = %q\n", buf[bytesRead-1:bytesRead])
	fmt.Printf("Read %d bytes = %v\n", bytesRead, buf[:12])

	// log.Print(fg.Write("freq 2340"))
	// log.Print(scope.Ask("*idn?"))
	defer fg.Close()
	fmt.Printf("Goodbye arbitrary waveform generator %s\n", fg.Device.Descriptor.SerialNumber)

}
<commit_msg>Close instrument in example lsusbtmc<commit_after>// Copyright (c) 2015-2017 The usbtmc developers. All rights reserved.
// Project site: https://github.com/gotmc/usbtmc
// Use of this source code is governed by a MIT-style license that
// can be found in the LICENSE.txt file for the project.

package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"time"

	"github.com/gotmc/usbtmc"
)

var (
	readonly = flag.Bool("readonly", false, "Only read from the controller")
	debug    = flag.Int("debug", 0, "USB debugging control")
)

func main() {
	flag.Parse()

	start := time.Now()
	ctx := usbtmc.NewContext()
	defer ctx.Close()

	if *debug != 0 {
		ctx.Debug(*debug)
	}

	start = time.Now()
	fg, err := ctx.NewInstrument("USB0::2391::1031::MY44035349::INSTR")
	if err != nil {
		log.Fatalf("NewInstrument error: %s", err)
	}
	defer fg.Close()
	log.Printf("%.2fs to setup instrument\n", time.Since(start).Seconds())
	start = time.Now()
	fmt.Printf(
		"Found the Arb Wave Gen S/N %s by Vendor ID %d with Product ID %d\n",
		fg.Device.Descriptor.SerialNumber,
		fg.Device.Descriptor.Vendor,
		fg.Device.Descriptor.Product)
	// Send commands to waveform generator
	fg.Write([]byte("apply:sinusoid 2340, 0.1, 0.0")) // Write using byte slice
	io.WriteString(fg, "burst:internal:period 0.112") // WriteString using io's Writer interface
	fg.WriteString("burst:internal:period 0.112")     // WriteString
	fg.WriteString("burst:ncycles 131")
	fg.WriteString("burst:state on")
	fg.WriteString("*idn?")

	start = time.Now()
	var buf [1024]byte
	bytesRead, err := fg.Read(buf[:])
	log.Printf("%.2fs to read %d bytes\n", time.Since(start).Seconds(), bytesRead)
	if err != nil {
		log.Printf("Error reading: %s", err)
	}
	fmt.Printf("Read %d bytes = %s", bytesRead, buf[12:bytesRead])
	fmt.Printf("Last rune read = %x\n", buf[bytesRead-1:bytesRead])
	fmt.Printf("Last rune read = %q\n", buf[bytesRead-1:bytesRead])
	fmt.Printf("Read %d bytes = %v\n", bytesRead, buf[:12])

	// log.Print(fg.Write("freq 2340"))
	// log.Print(scope.Ask("*idn?"))
	defer fg.Close()
	fmt.Printf("Goodbye arbitrary waveform generator %s\n", fg.Device.Descriptor.SerialNumber)

}
<|endoftext|>


<commit_before>// Copyright ©2015 The corridor 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 main

package corridor

import (
	"fmt"
	"math"
	"testing"

	"github.com/gonum/matrix/mat64"
)

// test Distance function
func TestDistance(t *testing.T) {

	// initialize test case
	t.Log("Distance Test: Expected Distance = 10")

	// initialize expected values
	var expValue float64 = 10.0

	// initialize test case variables
	var aSubs = []int{0, 10}
	var bSubs = []int{0, 20}

	// perform test case
	testCase := Distance(aSubs, bSubs)

	// log test result
	if testCase == expValue {
		t.Log("Distance Test: Computed Distance =", testCase)
	} else {
		t.Error("Distance Test: Computed Distance =", testCase)
	}
}

// test AllDistance function
func TestAllDistance(t *testing.T) {

	// initialize test case
	t.Log("AllDistance Test: Expected Matrix = {{3 3 3 [1.4142135623730951 1 1.4142135623730951 1 0 1 1.4142135623730951 1 1.4142135623730951]} 3 3}")

	// initialize expected value
	var expValueVector = []float64{
		math.Sqrt(2.0), 1.0, math.Sqrt(2.0),
		1.0, 0, 1.0,
		math.Sqrt(2.0), 1.0, math.Sqrt(2.0)}
	expValueMatrix := mat64.NewDense(3, 3, expValueVector)

	// initialize test case variables
	var aSubs = []int{1, 1}
	searchDomainMatrix := mat64.NewDense(3, 3, nil)

	// perform test case
	testCase := AllDistance(aSubs, searchDomainMatrix)

	// log test result
	if testCase.Equals(expValueMatrix) == true {
		t.Log("AllDistance Test: Computed Matrix =", *testCase)
	} else {
		t.Error("AllDistance Test: Computed Matrix =", *testCase)
	}
}

// test MinDistance function
func TestMinDistance(t *testing.T) {

	// initialize test case
	t.Log("MinDistance Test: Expected Value = 1.4142135623730951")

	// initialize expected value
	var expValue float64 = math.Sqrt(2.0)

	// initialize test case variables
	var pSubs = []int{0, 2}
	var aSubs = []int{0, 0}
	var bSubs = []int{2, 2}

	// perform test case
	testCase := MinDistance(pSubs, aSubs, bSubs)

	// log test result
	if testCase == expValue {
		t.Log("MinDistance Test: Computed Value =", testCase)
	} else {
		t.Error("AllDistance Test: Computed Value =", testCase)
	}
}

// test AllMinDistance function
func TestAllMinDistance(t *testing.T) {

	// initialize test case
	t.Log("AllMinDistance Test: Expected Matrix = {{3 3 3 [0 1 1.4142135623730951 1 0 1 1.4142135623730951 1 0]} 3 3}")

	// initialize expected value
	var expValueVector = []float64{
		0.0, (math.Sqrt(2.0) / 2.0), math.Sqrt(2.0),
		(math.Sqrt(2.0) / 2.0), 0.0, (math.Sqrt(2.0) / 2.0),
		math.Sqrt(2.0), (math.Sqrt(2.0) / 2.0), 0.0}
	expValueMatrix := mat64.NewDense(3, 3, expValueVector)

	// initialize test case variables
	var aSubs = []int{0, 0}
	var bSubs = []int{2, 2}
	searchDomainMatrix := mat64.NewDense(3, 3, nil)

	// peform test case
	testCase := AllMinDistance(aSubs, bSubs, searchDomainMatrix)

	// log test result
	if testCase.Equals(expValueMatrix) {
		t.Log("AllMinDistance Test: Computed Matrix =", testCase)
	} else {
		t.Error("AllMinDistance Test: Computed Matrix =", testCase)
	}
}

// test DistanceBands
func TestDistanceBands(t *testing.T) {

	// initialize test case
	t.Log("DistanceBands Test: Expected Matrix = {{3 3 3 [0 1 2 1 1 2 2 2 2]} 3 3}")

	// initialize expected value
	var expValueVector = []float64{
		0.0, 1.0, 2.0,
		1.0, 1.0, 2.0,
		2.0, 2.0, 2.0}
	expValueMatrix := mat64.NewDense(3, 3, expValueVector)

	// initialize test case variables
	var aSubs = []int{0, 0}
	var bandCount int = 2
	searchDomainMatrix := mat64.NewDense(3, 3, nil)

	// compute distance matrix !! dependent on AllDistance test result !!
	distanceMatrix := AllDistance(aSubs, searchDomainMatrix)

	// perform test case
	testCase := DistanceBands(bandCount, distanceMatrix)

	// log test result
	if testCase.Equals(expValueMatrix) {
		t.Log("DistanceBands Test: Computed Matrix =", *testCase)
	} else {
		t.Error("DistanceBands Test: Computed Matrix =", *testCase)
	}
}

// test BandMask
func TestBandMask(t *testing.T) {

	// initialize test case
	t.Log("DistanceBands Test: Expected Matrix = {{3 3 3 [0 0 0 0 1 0 0 0 0]} 3 3}")

	// initialize expected value
	var expValueVector = []float64{
		0.0, 0.0, 0.0,
		0.0, 1.0, 0.0,
		0.0, 0.0, 0.0}
	expValueMatrix := mat64.NewDense(3, 3, expValueVector)

	// initialize test case variables
	var aSubs = []int{0, 0}
	var bandCount int = 2
	var bandValue float64 = 1.0
	searchDomainMatrix := mat64.NewDense(3, 3, nil)

	// compute distance matrix !! dependent on AllDistance test result !!
	distanceMatrix := AllDistance(aSubs, searchDomainMatrix)

	// compute band matrix !! dependent on DistanceBands test result!!
	bandMatrix := DistanceBands(bandCount, distanceMatrix)

	// perform test case
	testCase := BandMask(bandValue, bandMatrix)

	// log test results
	if testCase.Equals(expValueMatrix) {
		t.Log("BandMask Test: Computed Matrix =", *testCase)
	} else {
		t.Error("BandMask Test: Computed Matrix =", *testCase)
	}
}

// test NonZeroSubs
func TestNonZeroSubs(t *testing.T) {

	// initialize test case
	t.Log("DistanceBands Test: Expected Vector = [[1 1]]")

	// initialize expected value
	expValueVector := make([][]int, 1)
	expValueVector[0] = []int{1, 1}

	// initialize test case variables
	var aSubs = []int{0, 0}
	var bandCount int = 2
	var bandValue float64 = 1.0
	searchDomainMatrix := mat64.NewDense(3, 3, nil)

	// compute distance matrix !! dependent on AllDistance test result !!
	distanceMatrix := AllDistance(aSubs, searchDomainMatrix)

	// compute band matrix !! dependent on DistanceBands test result!!
	bandMatrix := DistanceBands(bandCount, distanceMatrix)

	// compute band mask !! dependent on BandMask test result!!
	bandMask := BandMask(bandValue, bandMatrix)

	// perform test case
	testCase := NonZeroSubs(bandMask)

	// log test results
	if testCase[0][0] == expValueVector[0][0] && testCase[0][1] == expValueVector[0][1] {
		t.Log("NonZeroSubs Test: Computed Vector =", testCase)
	} else {
		t.Error("NonZeroSubs Test: Computed Vector =", testCase)
	}
}

// test FindSubs
func TestFindSubs(t *testing.T) {

	// initialize test case
	t.Log("DistanceBands Test: Expected Vector = [[0 0]]")

	// initialize expected value
	expValueVector := make([][]int, 1)
	expValueVector[0] = []int{0, 0}

	// initialize test case variables
	var inputValue float64 = 0.0
	var aSubs = []int{0, 0}
	searchDomainMatrix := mat64.NewDense(3, 3, nil)

	// compute distance matrix !! dependent on AllDistance test result !!
	distanceMatrix := AllDistance(aSubs, searchDomainMatrix)

	// DEBUG
	fmt.Println(distanceMatrix)

	// perform test case
	testCase := FindSubs(inputValue, distanceMatrix)

	// log test results
	if testCase[0][0] == expValueVector[0][0] && testCase[0][1] == expValueVector[0][1] {
		t.Log("FindSubs Test: Computed Vector =", testCase)
	} else {
		t.Error("FindSubs Test: Computed Vector =", testCase)
	}
}
<commit_msg>Added several new test suits to the lib test file. Fixed some minor documentation errors in previously committed test routines.<commit_after>// Copyright ©2015 The corridor 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 main

package corridor

import (
	"math"
	"testing"

	"github.com/gonum/matrix/mat64"
)

// test Distance function
func TestDistance(t *testing.T) {

	// initialize test case
	t.Log("Distance Test: Expected Distance = 10")

	// initialize expected values
	var expValue float64 = 10.0

	// initialize test case variables
	var aSubs = []int{0, 10}
	var bSubs = []int{0, 20}

	// perform test case
	testCase := Distance(aSubs, bSubs)

	// log test result
	if testCase == expValue {
		t.Log("Distance Test: Computed Distance =", testCase)
	} else {
		t.Error("Distance Test: Computed Distance =", testCase)
	}
}

// test AllDistance function
func TestAllDistance(t *testing.T) {

	// initialize test case
	t.Log("AllDistance Test: Expected Matrix = {{3 3 3 [1.4142135623730951 1 1.4142135623730951 1 0 1 1.4142135623730951 1 1.4142135623730951]} 3 3}")

	// initialize expected value
	var expValueVector = []float64{
		math.Sqrt(2.0), 1.0, math.Sqrt(2.0),
		1.0, 0, 1.0,
		math.Sqrt(2.0), 1.0, math.Sqrt(2.0)}
	expValueMatrix := mat64.NewDense(3, 3, expValueVector)

	// initialize test case variables
	var aSubs = []int{1, 1}
	searchDomainMatrix := mat64.NewDense(3, 3, nil)

	// perform test case
	testCase := AllDistance(aSubs, searchDomainMatrix)

	// log test result
	if testCase.Equals(expValueMatrix) == true {
		t.Log("AllDistance Test: Computed Matrix =", *testCase)
	} else {
		t.Error("AllDistance Test: Computed Matrix =", *testCase)
	}
}

// test MinDistance function
func TestMinDistance(t *testing.T) {

	// initialize test case
	t.Log("MinDistance Test: Expected Value = 1.4142135623730951")

	// initialize expected value
	var expValue float64 = math.Sqrt(2.0)

	// initialize test case variables
	var pSubs = []int{0, 2}
	var aSubs = []int{0, 0}
	var bSubs = []int{2, 2}

	// perform test case
	testCase := MinDistance(pSubs, aSubs, bSubs)

	// log test result
	if testCase == expValue {
		t.Log("MinDistance Test: Computed Value =", testCase)
	} else {
		t.Error("AllDistance Test: Computed Value =", testCase)
	}
}

// test AllMinDistance function
func TestAllMinDistance(t *testing.T) {

	// initialize test case
	t.Log("AllMinDistance Test: Expected Matrix = {{3 3 3 [0 1 1.4142135623730951 1 0 1 1.4142135623730951 1 0]} 3 3}")

	// initialize expected value
	var expValueVector = []float64{
		0.0, (math.Sqrt(2.0) / 2.0), math.Sqrt(2.0),
		(math.Sqrt(2.0) / 2.0), 0.0, (math.Sqrt(2.0) / 2.0),
		math.Sqrt(2.0), (math.Sqrt(2.0) / 2.0), 0.0}
	expValueMatrix := mat64.NewDense(3, 3, expValueVector)

	// initialize test case variables
	var aSubs = []int{0, 0}
	var bSubs = []int{2, 2}
	searchDomainMatrix := mat64.NewDense(3, 3, nil)

	// peform test case
	testCase := AllMinDistance(aSubs, bSubs, searchDomainMatrix)

	// log test result
	if testCase.Equals(expValueMatrix) {
		t.Log("AllMinDistance Test: Computed Matrix =", testCase)
	} else {
		t.Error("AllMinDistance Test: Computed Matrix =", testCase)
	}
}

// test DistanceBands
func TestDistanceBands(t *testing.T) {

	// initialize test case
	t.Log("DistanceBands Test: Expected Matrix = {{3 3 3 [0 1 2 1 1 2 2 2 2]} 3 3}")

	// initialize expected value
	var expValueVector = []float64{
		0.0, 1.0, 2.0,
		1.0, 1.0, 2.0,
		2.0, 2.0, 2.0}
	expValueMatrix := mat64.NewDense(3, 3, expValueVector)

	// initialize test case variables
	var aSubs = []int{0, 0}
	var bandCount int = 2
	searchDomainMatrix := mat64.NewDense(3, 3, nil)

	// compute distance matrix !! dependent on AllDistance test result !!
	distanceMatrix := AllDistance(aSubs, searchDomainMatrix)

	// perform test case
	testCase := DistanceBands(bandCount, distanceMatrix)

	// log test result
	if testCase.Equals(expValueMatrix) {
		t.Log("DistanceBands Test: Computed Matrix =", *testCase)
	} else {
		t.Error("DistanceBands Test: Computed Matrix =", *testCase)
	}
}

// test BandMask
func TestBandMask(t *testing.T) {

	// initialize test case
	t.Log("BandMask Test: Expected Matrix = {{3 3 3 [0 0 0 0 1 0 0 0 0]} 3 3}")

	// initialize expected value
	var expValueVector = []float64{
		0.0, 0.0, 0.0,
		0.0, 1.0, 0.0,
		0.0, 0.0, 0.0}
	expValueMatrix := mat64.NewDense(3, 3, expValueVector)

	// initialize test case variables
	var aSubs = []int{0, 0}
	var bandCount int = 2
	var bandValue float64 = 1.0
	searchDomainMatrix := mat64.NewDense(3, 3, nil)

	// compute distance matrix !! dependent on AllDistance test result !!
	distanceMatrix := AllDistance(aSubs, searchDomainMatrix)

	// compute band matrix !! dependent on DistanceBands test result!!
	bandMatrix := DistanceBands(bandCount, distanceMatrix)

	// perform test case
	testCase := BandMask(bandValue, bandMatrix)

	// log test results
	if testCase.Equals(expValueMatrix) {
		t.Log("BandMask Test: Computed Matrix =", *testCase)
	} else {
		t.Error("BandMask Test: Computed Matrix =", *testCase)
	}
}

// test NonZeroSubs
func TestNonZeroSubs(t *testing.T) {

	// initialize test case
	t.Log("NonZeroSubs Test: Expected Vector = [[1 1]]")

	// initialize expected value
	expValueVector := make([][]int, 1)
	expValueVector[0] = []int{1, 1}

	// initialize test case variables
	var aSubs = []int{0, 0}
	var bandCount int = 2
	var bandValue float64 = 1.0
	searchDomainMatrix := mat64.NewDense(3, 3, nil)

	// compute distance matrix !! dependent on AllDistance test result !!
	distanceMatrix := AllDistance(aSubs, searchDomainMatrix)

	// compute band matrix !! dependent on DistanceBands test result!!
	bandMatrix := DistanceBands(bandCount, distanceMatrix)

	// compute band mask !! dependent on BandMask test result!!
	bandMask := BandMask(bandValue, bandMatrix)

	// perform test case
	testCase := NonZeroSubs(bandMask)

	// log test results
	if testCase[0][0] == expValueVector[0][0] && testCase[0][1] == expValueVector[0][1] {
		t.Log("NonZeroSubs Test: Computed Vector =", testCase)
	} else {
		t.Error("NonZeroSubs Test: Computed Vector =", testCase)
	}
}

// test FindSubs
func TestFindSubs(t *testing.T) {

	// initialize test case
	t.Log("FindSubs Test: Expected Vector = [[0 0]]")

	// initialize expected value
	expValueVector := make([][]int, 1)
	expValueVector[0] = []int{0, 0}

	// initialize test case variables
	var inputValue float64 = 0.0
	var aSubs = []int{0, 0}
	searchDomainMatrix := mat64.NewDense(3, 3, nil)

	// compute distance matrix !! dependent on AllDistance test result !!
	distanceMatrix := AllDistance(aSubs, searchDomainMatrix)

	// perform test case
	testCase := FindSubs(inputValue, distanceMatrix)

	// log test results
	if testCase[0][0] == expValueVector[0][0] && testCase[0][1] == expValueVector[0][1] {
		t.Log("FindSubs Test: Computed Vector =", testCase)
	} else {
		t.Error("FindSubs Test: Computed Vector =", testCase)
	}
}

// test Orientation
func TestOrientation(t *testing.T) {

	// initialize test case
	t.Log("Orientation Test: Expected Vector = [1 1]")

	// initialize expected value
	var expValueVector = []int{1, 1}

	// initialize test case variables
	var aSubs = []int{0, 0}
	var bSubs = []int{2, 2}

	// perform test case
	testCase := Orientation(aSubs, bSubs)

	// log test results
	if testCase[0] == expValueVector[0] && testCase[1] == expValueVector[1] {
		t.Log("Orientation Test: Computed Vector =", testCase)
	} else {
		t.Error("Orientation Test: Computed Vector =", testCase)
	}
}

// test OrientationMask
func TestOrientationMask(t *testing.T) {

	// initialize test case
	t.Log("OrientationMask Test: Expected Matrix = {{3 3 3 [0 0 0 0 1 0 0 0 0]} 3 3}")

	// initialize expected value
	var expValueVector = []float64{
		0.0, 0.0, 0.0,
		0.0, 1.0, 0.0,
		0.0, 0.0, 0.0}
	expValueMatrix := mat64.NewDense(3, 3, expValueVector)

	// initialize test case variables
	var aSubs = []int{0, 0}
	var bSubs = []int{2, 2}
	searchDomainMatrix := mat64.NewDense(3, 3, nil)

	// perform test case
	testCase := OrientationMask(aSubs, bSubs, searchDomainMatrix)

	// log test results
	if testCase.Equals(expValueMatrix) {
		t.Log("OrientationMask Test: Computed Matrix =", testCase)
	} else {
		t.Error("OrientationMask Test: Computed Matrix =", testCase)
	}
}
<|endoftext|>


<commit_before>/*
 Copyright 2020 The GoPlus Authors (goplus.org)

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

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

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

package golang

import (
	"go/ast"
	"go/token"
	"reflect"
	"strings"

	"github.com/goplus/gop/exec.spec"
	"github.com/qiniu/x/log"
)

// -----------------------------------------------------------------------------

// Var represents a variable.
type Var struct {
	typ   reflect.Type
	name  string
	where *scopeCtx
}

// NewVar creates a variable instance.
func NewVar(typ reflect.Type, name string) *Var {
	c := name[0]
	if c >= '0' && c <= '9' {
		name = "_ret_" + name
	}
	return &Var{typ: typ, name: name}
}

// Type returns variable's type.
func (p *Var) Type() reflect.Type {
	return p.typ
}

// Name returns variable's name.
func (p *Var) Name() string {
	return p.name
}

// IsUnnamedOut returns if variable unnamed or not.
func (p *Var) IsUnnamedOut() bool {
	return strings.HasPrefix(p.name, "_ret_")
}

func (p *Var) setScope(where *scopeCtx) {
	if p.where != nil {
		panic("Var.setScope: variable already defined")
	}
	p.where = where
}

// -----------------------------------------------------------------------------

type scopeCtx struct {
	parentCtx *scopeCtx
	vlist     []exec.Var
	stmts     []ast.Stmt
	labels    []*Label // labels of current statement
}

func (p *scopeCtx) addVar(vars ...exec.Var) {
	for _, v := range vars {
		v.(*Var).setScope(p)
	}
	p.vlist = append(p.vlist, vars...)
}

func (p *scopeCtx) toGenDecl(b *Builder) *ast.GenDecl {
	n := len(p.vlist)
	if n == 0 {
		return nil
	}
	specs := make([]ast.Spec, 0, n)
	for _, item := range p.vlist {
		v := item.(*Var)
		spec := &ast.ValueSpec{
			Names: []*ast.Ident{Ident(v.name)},
			Type:  Type(b, v.typ),
		}
		specs = append(specs, spec)
	}
	return &ast.GenDecl{
		Tok:   token.VAR,
		Specs: specs,
	}
}

func (p *scopeCtx) getStmts(b *Builder) []ast.Stmt {
	if decl := p.toGenDecl(b); decl != nil {
		p.stmts[0] = &ast.DeclStmt{Decl: decl}
		return p.stmts
	}
	return p.stmts[1:]
}

func (p *scopeCtx) initStmts() {
	p.stmts = make([]ast.Stmt, 1, 8)
}

// -----------------------------------------------------------------------------

// DefineVar defines variables.
func (p *Builder) DefineVar(vars ...exec.Var) *Builder {
	var vlist []exec.Var
	for _, v := range vars {
		if pkgPath := v.Type().PkgPath(); pkgPath != "" {
			p.Import(pkgPath)
		} else if v.Name() == "_" {
			continue
		}
		pv := v.(*Var)
		if strings.HasPrefix(pv.name, "_") {
			pv.name = "_q" + pv.name
		}
		vlist = append(vlist, v)
	}
	p.addVar(vlist...)
	return p
}

// InCurrentCtx returns if a variable is in current context or not.
func (p *Builder) InCurrentCtx(v exec.Var) bool {
	return p.scopeCtx == v.(*Var).where
}

// Load instr
func (p *Builder) Load(idx int32) *Builder {
	p.rhs.Push(p.argIdent(idx))
	return p
}

// Addr instr
func (p *Builder) Addr(idx int32) *Builder {
	p.rhs.Push(p.argIdent(idx))
	return p
}

// Store instr
func (p *Builder) Store(idx int32) *Builder {
	p.lhs.Push(p.argIdent(idx))
	return p
}

func (p *Builder) argIdent(idx int32) *ast.Ident {
	i := len(p.cfun.in) + int(idx)
	if i == -1 {
		return Ident("_recv")
	}
	return Ident(toArg(i))
}

// LoadVar instr
func (p *Builder) LoadVar(v exec.Var) *Builder {
	p.rhs.Push(Ident(v.(*Var).name))
	return p
}

// StoreVar instr
func (p *Builder) StoreVar(v exec.Var) *Builder {
	p.lhs.Push(Ident(v.(*Var).name))
	return p
}

// AddrVar instr
func (p *Builder) AddrVar(v exec.Var) *Builder {
	p.rhs.Push(&ast.UnaryExpr{
		Op: token.AND,
		X:  Ident(v.(*Var).name),
	})
	return p
}

func (p *Builder) bigAddrOp(kind exec.Kind, op exec.AddrOperator) *Builder {
	if op == exec.OpAddrVal {
		return p
	}
	method := addropMethods[op]
	if method == "" {
		log.Panicln("bigAddrOp: unknown op -", op)
	}
	var expr ast.Expr
	var x = p.rhs.Pop()
	var val = p.rhs.Pop().(ast.Expr)
	switch v := x.(type) {
	case *ast.UnaryExpr:
		if v.Op != token.AND {
			log.Panicln("bigAddrOp: unknown x expr -", reflect.TypeOf(x))
		}
		bigOp := &ast.SelectorExpr{X: v.X, Sel: Ident(method)}
		expr = &ast.CallExpr{Fun: bigOp, Args: []ast.Expr{v.X, val}}
	default:
		log.Panicln("bigAddrOp: todo")
	}
	p.rhs.Push(&ast.ExprStmt{X: expr})
	return p
}

var addropMethods = [...]string{
	exec.OpAddAssign:    "Add",
	exec.OpSubAssign:    "Sub",
	exec.OpMulAssign:    "Mul",
	exec.OpQuoAssign:    "Quo",
	exec.OpModAssign:    "Mod",
	exec.OpAndAssign:    "And",
	exec.OpOrAssign:     "Or",
	exec.OpXorAssign:    "Xor",
	exec.OpAndNotAssign: "AndNot",
	exec.OpLshAssign:    "Lsh",
	exec.OpRshAssign:    "Rsh",
	exec.OpInc:          "Add",
	exec.OpDec:          "Sub",
}

// AddrOp instr
func (p *Builder) AddrOp(kind exec.Kind, op exec.AddrOperator) *Builder {
	if kind >= exec.BigInt {
		return p.bigAddrOp(kind, op)
	}
	if op == exec.OpAddrVal {
		p.rhs.Push(&ast.StarExpr{
			X: p.rhs.Pop().(ast.Expr),
		})
		return p
	}
	if op == exec.OpAssign {
		p.emitStmt(&ast.AssignStmt{
			Lhs: []ast.Expr{&ast.StarExpr{
				X: p.rhs.Pop().(ast.Expr),
			}},
			Tok: token.ASSIGN,
			Rhs: []ast.Expr{p.rhs.Pop().(ast.Expr)},
		})
		return p
	}
	var stmt ast.Stmt
	var x = p.rhs.Pop()
	var val = p.rhs.Pop().(ast.Expr)
	switch v := x.(type) {
	case *ast.UnaryExpr:
		if v.Op != token.AND {
			log.Panicln("AddrOp: unknown x expr -", reflect.TypeOf(x))
		}
		if op == exec.OpInc || op == exec.OpDec {
			stmt = &ast.IncDecStmt{X: v.X, TokPos: v.OpPos, Tok: addropTokens[op]}
		} else {
			stmt = &ast.AssignStmt{Lhs: []ast.Expr{v.X}, Tok: addropTokens[op], Rhs: []ast.Expr{val}}
		}
	default:
		log.Panicln("AddrOp: todo")
	}
	p.rhs.Push(stmt)
	return p
}

var addropTokens = [...]token.Token{
	exec.OpAddAssign:    token.ADD_ASSIGN,
	exec.OpSubAssign:    token.SUB_ASSIGN,
	exec.OpMulAssign:    token.MUL_ASSIGN,
	exec.OpQuoAssign:    token.QUO_ASSIGN,
	exec.OpModAssign:    token.REM_ASSIGN,
	exec.OpAndAssign:    token.AND_ASSIGN,
	exec.OpOrAssign:     token.OR_ASSIGN,
	exec.OpXorAssign:    token.XOR_ASSIGN,
	exec.OpAndNotAssign: token.AND_NOT_ASSIGN,
	exec.OpLshAssign:    token.SHL_ASSIGN,
	exec.OpRshAssign:    token.SHR_ASSIGN,
	exec.OpInc:          token.INC,
	exec.OpDec:          token.DEC,
}

// -----------------------------------------------------------------------------
<commit_msg>golang: support inc/dec for BigInt/BigRat/BigFloat<commit_after>/*
 Copyright 2020 The GoPlus Authors (goplus.org)

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

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

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

package golang

import (
	"go/ast"
	"go/token"
	"reflect"
	"strings"

	"github.com/goplus/gop/exec.spec"
	"github.com/qiniu/x/log"
)

// -----------------------------------------------------------------------------

// Var represents a variable.
type Var struct {
	typ   reflect.Type
	name  string
	where *scopeCtx
}

// NewVar creates a variable instance.
func NewVar(typ reflect.Type, name string) *Var {
	c := name[0]
	if c >= '0' && c <= '9' {
		name = "_ret_" + name
	}
	return &Var{typ: typ, name: name}
}

// Type returns variable's type.
func (p *Var) Type() reflect.Type {
	return p.typ
}

// Name returns variable's name.
func (p *Var) Name() string {
	return p.name
}

// IsUnnamedOut returns if variable unnamed or not.
func (p *Var) IsUnnamedOut() bool {
	return strings.HasPrefix(p.name, "_ret_")
}

func (p *Var) setScope(where *scopeCtx) {
	if p.where != nil {
		panic("Var.setScope: variable already defined")
	}
	p.where = where
}

// -----------------------------------------------------------------------------

type scopeCtx struct {
	parentCtx *scopeCtx
	vlist     []exec.Var
	stmts     []ast.Stmt
	labels    []*Label // labels of current statement
}

func (p *scopeCtx) addVar(vars ...exec.Var) {
	for _, v := range vars {
		v.(*Var).setScope(p)
	}
	p.vlist = append(p.vlist, vars...)
}

func (p *scopeCtx) toGenDecl(b *Builder) *ast.GenDecl {
	n := len(p.vlist)
	if n == 0 {
		return nil
	}
	specs := make([]ast.Spec, 0, n)
	for _, item := range p.vlist {
		v := item.(*Var)
		spec := &ast.ValueSpec{
			Names: []*ast.Ident{Ident(v.name)},
			Type:  Type(b, v.typ),
		}
		specs = append(specs, spec)
	}
	return &ast.GenDecl{
		Tok:   token.VAR,
		Specs: specs,
	}
}

func (p *scopeCtx) getStmts(b *Builder) []ast.Stmt {
	if decl := p.toGenDecl(b); decl != nil {
		p.stmts[0] = &ast.DeclStmt{Decl: decl}
		return p.stmts
	}
	return p.stmts[1:]
}

func (p *scopeCtx) initStmts() {
	p.stmts = make([]ast.Stmt, 1, 8)
}

// -----------------------------------------------------------------------------

// DefineVar defines variables.
func (p *Builder) DefineVar(vars ...exec.Var) *Builder {
	var vlist []exec.Var
	for _, v := range vars {
		if pkgPath := v.Type().PkgPath(); pkgPath != "" {
			p.Import(pkgPath)
		} else if v.Name() == "_" {
			continue
		}
		pv := v.(*Var)
		if strings.HasPrefix(pv.name, "_") {
			pv.name = "_q" + pv.name
		}
		vlist = append(vlist, v)
	}
	p.addVar(vlist...)
	return p
}

// InCurrentCtx returns if a variable is in current context or not.
func (p *Builder) InCurrentCtx(v exec.Var) bool {
	return p.scopeCtx == v.(*Var).where
}

// Load instr
func (p *Builder) Load(idx int32) *Builder {
	p.rhs.Push(p.argIdent(idx))
	return p
}

// Addr instr
func (p *Builder) Addr(idx int32) *Builder {
	p.rhs.Push(p.argIdent(idx))
	return p
}

// Store instr
func (p *Builder) Store(idx int32) *Builder {
	p.lhs.Push(p.argIdent(idx))
	return p
}

func (p *Builder) argIdent(idx int32) *ast.Ident {
	i := len(p.cfun.in) + int(idx)
	if i == -1 {
		return Ident("_recv")
	}
	return Ident(toArg(i))
}

// LoadVar instr
func (p *Builder) LoadVar(v exec.Var) *Builder {
	p.rhs.Push(Ident(v.(*Var).name))
	return p
}

// StoreVar instr
func (p *Builder) StoreVar(v exec.Var) *Builder {
	p.lhs.Push(Ident(v.(*Var).name))
	return p
}

// AddrVar instr
func (p *Builder) AddrVar(v exec.Var) *Builder {
	p.rhs.Push(&ast.UnaryExpr{
		Op: token.AND,
		X:  Ident(v.(*Var).name),
	})
	return p
}

func (p *Builder) bigAddrOp(kind exec.Kind, op exec.AddrOperator) *Builder {
	if op == exec.OpAddrVal {
		return p
	}
	method := addropMethods[op]
	if method == "" {
		log.Panicln("bigAddrOp: unknown op -", op)
	}
	var expr ast.Expr
	var x = p.rhs.Pop()
	var val = p.rhs.Pop().(ast.Expr)
	if op == exec.OpInc || op == exec.OpDec {
		pkg := p.Import("math/big")
		var fnName string
		switch kind {
		case exec.BigInt:
			fnName = "NewInt"
		case exec.BigRat:
			fnName = "NewRat"
		case exec.BigFloat:
			fnName = "NewFloat"
		}
		val = &ast.CallExpr{
			Fun: &ast.SelectorExpr{
				&ast.Ident{Name: pkg},
				&ast.Ident{Name: fnName},
			},
			Args: []ast.Expr{&ast.Ident{Name: "1"}},
		}
	}
	switch v := x.(type) {
	case *ast.UnaryExpr:
		if v.Op != token.AND {
			log.Panicln("bigAddrOp: unknown x expr -", reflect.TypeOf(x))
		}
		bigOp := &ast.SelectorExpr{X: v.X, Sel: Ident(method)}
		expr = &ast.CallExpr{Fun: bigOp, Args: []ast.Expr{v.X, val}}
	default:
		log.Panicln("bigAddrOp: todo")
	}
	p.rhs.Push(&ast.ExprStmt{X: expr})
	return p
}

var addropMethods = [...]string{
	exec.OpAddAssign:    "Add",
	exec.OpSubAssign:    "Sub",
	exec.OpMulAssign:    "Mul",
	exec.OpQuoAssign:    "Quo",
	exec.OpModAssign:    "Mod",
	exec.OpAndAssign:    "And",
	exec.OpOrAssign:     "Or",
	exec.OpXorAssign:    "Xor",
	exec.OpAndNotAssign: "AndNot",
	exec.OpLshAssign:    "Lsh",
	exec.OpRshAssign:    "Rsh",
	exec.OpInc:          "Add",
	exec.OpDec:          "Sub",
}

// AddrOp instr
func (p *Builder) AddrOp(kind exec.Kind, op exec.AddrOperator) *Builder {
	if kind >= exec.BigInt {
		return p.bigAddrOp(kind, op)
	}
	if op == exec.OpAddrVal {
		p.rhs.Push(&ast.StarExpr{
			X: p.rhs.Pop().(ast.Expr),
		})
		return p
	}
	if op == exec.OpAssign {
		p.emitStmt(&ast.AssignStmt{
			Lhs: []ast.Expr{&ast.StarExpr{
				X: p.rhs.Pop().(ast.Expr),
			}},
			Tok: token.ASSIGN,
			Rhs: []ast.Expr{p.rhs.Pop().(ast.Expr)},
		})
		return p
	}
	var stmt ast.Stmt
	var x = p.rhs.Pop()
	var val = p.rhs.Pop().(ast.Expr)
	switch v := x.(type) {
	case *ast.UnaryExpr:
		if v.Op != token.AND {
			log.Panicln("AddrOp: unknown x expr -", reflect.TypeOf(x))
		}
		if op == exec.OpInc || op == exec.OpDec {
			stmt = &ast.IncDecStmt{X: v.X, TokPos: v.OpPos, Tok: addropTokens[op]}
		} else {
			stmt = &ast.AssignStmt{Lhs: []ast.Expr{v.X}, Tok: addropTokens[op], Rhs: []ast.Expr{val}}
		}
	default:
		log.Panicln("AddrOp: todo")
	}
	p.rhs.Push(stmt)
	return p
}

var addropTokens = [...]token.Token{
	exec.OpAddAssign:    token.ADD_ASSIGN,
	exec.OpSubAssign:    token.SUB_ASSIGN,
	exec.OpMulAssign:    token.MUL_ASSIGN,
	exec.OpQuoAssign:    token.QUO_ASSIGN,
	exec.OpModAssign:    token.REM_ASSIGN,
	exec.OpAndAssign:    token.AND_ASSIGN,
	exec.OpOrAssign:     token.OR_ASSIGN,
	exec.OpXorAssign:    token.XOR_ASSIGN,
	exec.OpAndNotAssign: token.AND_NOT_ASSIGN,
	exec.OpLshAssign:    token.SHL_ASSIGN,
	exec.OpRshAssign:    token.SHR_ASSIGN,
	exec.OpInc:          token.INC,
	exec.OpDec:          token.DEC,
}

// -----------------------------------------------------------------------------
<|endoftext|>


<commit_before>package server

import (
	"html/template"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"
	"time"

	"github.com/Zamiell/isaac-racing-server/models"
	"github.com/didip/tollbooth"
	"github.com/didip/tollbooth_gin"
	"github.com/gin-contrib/sessions"
	"github.com/gin-contrib/sessions/cookie"
	"github.com/gin-gonic/gin"
)

const (
	HTTPSessionName  = "isaac.sid"
	HTTPWriteTimeout = 10 * time.Second
)

var (
	sessionStore cookie.Store
	GATrackingID string

	// HTTPClientWithTimeout specifies a timeout on the default http.Client structure
	HTTPClientWithTimeout = &http.Client{
		Timeout: HTTPWriteTimeout,
	}
)

/*
	Data structures
*/

type TemplateData struct {
	Title string

	// Races stuff
	RaceResults       []models.RaceHistory
	SingleRaceResults models.RaceHistory
	SingleRaceFormat  string
	ResultsRaces      []models.RaceHistory
	TotalRaceCount    int
	TotalPages        int
	PreviousPage      int
	NextPage          int
	RaceResultsRanked []models.RaceHistory
	RaceResultsAll    []models.RaceHistory

	// Profiles/profile stuff
	ResultsProfiles   []models.ProfilesRow
	ResultsProfile    models.ProfileData
	TotalProfileCount int
	UsersPerPage      int
	TotalTime         int
	MissingPlayer     string

	// Leaderboard stuff
	LeaderboardSeeded       []models.LeaderboardRowSeeded
	LeaderboardSeededSolo   []models.LeaderboardRowSeededSolo
	LeaderboardUnseeded     []models.LeaderboardRowUnseeded
	LeaderboardUnseededSolo []models.LeaderboardRowUnseededSolo
	LeaderboardDiversity    []models.LeaderboardRowDiversity
	//LeaderboardSeeded []models.LeaderboardRowSeeded

	// Hall of Fame stuff
	Season1r9  []models.SpeedRun
	Season1r14 []models.SpeedRun
	Season2r7  []models.SpeedRun
	Season3r7  []models.SpeedRun
	Season4r7  []models.SpeedRun
	Season5r7  []models.SpeedRun
	Season6r7  []models.SpeedRun
	Season7r7  []models.SpeedRun
	// Season8r7 []models.SpeedRun

	// Tournament Stuff
	CurrentTournament bool
	TournamentRaces   []models.TournamentRace
	TournamentInfos   TournamentStats
	AllTournaments    []TournamentInfo
}

/*
	Initialization function
*/

func httpInit() {
	// Create a new Gin HTTP router
	gin.SetMode(gin.ReleaseMode) // Comment this out to debug HTTP stuff
	httpRouter := gin.Default()

	// Read some HTTP server configuration values from environment variables
	// (they were loaded from the .env file in main.go)
	sessionSecret := os.Getenv("SESSION_SECRET")
	if len(sessionSecret) == 0 {
		logger.Info("The \"SESSION_SECRET\" environment variable is blank; aborting HTTP initialization.")
		return
	}
	domain := os.Getenv("DOMAIN")
	if len(domain) == 0 {
		logger.Info("The \"DOMAIN\" environment variable is blank; aborting HTTP initialization.")
		return
	}
	tlsCertFile := os.Getenv("TLS_CERT_FILE")
	tlsKeyFile := os.Getenv("TLS_KEY_FILE")
	useTLS := true
	if len(tlsCertFile) == 0 || len(tlsKeyFile) == 0 {
		useTLS = false
	}

	// Create a session store
	sessionStore = cookie.NewStore([]byte(sessionSecret))
	options := sessions.Options{
		Path:   "/",
		Domain: domain,
		MaxAge: 5, // 5 seconds
		// After getting a cookie via "/login", the client will immediately
		// establish a WebSocket connection via "/ws", so the cookie only needs
		// to exist for that time frame
		Secure: true,
		// Only send the cookie over HTTPS:
		// https://www.owasp.org/index.php/Testing_for_cookies_attributes_(OTG-SESS-002)
		HttpOnly: true,
		// Mitigate XSS attacks:
		// https://www.owasp.org/index.php/HttpOnly
	}
	if !useTLS {
		options.Secure = false
	}
	sessionStore.Options(options)
	httpRouter.Use(sessions.Sessions(HTTPSessionName, sessionStore))

	// Use the Tollbooth Gin middleware for rate limiting
	limiter := tollbooth.NewLimiter(1, nil) // Limit each user to 1 request per second
	// When a user requests "/", they will also request the CSS and images;
	// this middleware is smart enough to know that it is considered part of the first request
	// However, it is still not possible to spam download CSS or image files
	limiterMiddleware := tollbooth_gin.LimitHandler(limiter)
	httpRouter.Use(limiterMiddleware)

	/*
		This was used as an alterate to the Tollbooth middleware when it wasn't working

		// Use the gin-limiter middleware for rate-limiting
		// We only allow 60 request per minute, an average of 1 per second
		// This is because when a user requests "/", they will also request the CSS and images
		// Based on: https://github.com/julianshen/gin-limiter/blob/master/example/web.go
		limiterMiddleware := limiter.NewRateLimiter(time.Second*60, 60, func(c *gin.Context) (string, error) {
			// Local variables
			r := c.Request
			ip, _, _ := net.SplitHostPort(r.RemoteAddr)

			// Just use the IP address as the key
			return ip, nil
		}).Middleware()
		httpRouter.Use(limiterMiddleware)
	*/

	// Use a custom middleware for Google Analytics tracking
	GATrackingID = os.Getenv("GA_TRACKING_ID")
	if len(GATrackingID) != 0 {
		httpRouter.Use(httpMwGoogleAnalytics)
	}

	// Path handlers (for the WebSocket server)
	httpRouter.POST("/login", httpLogin)
	httpRouter.POST("/register", httpRegister)
	httpRouter.GET("/ws", httpWS)

	// Path handlers (for the website)
	httpRouter.GET("/", httpHome)
	httpRouter.GET("/news", httpNews)
	httpRouter.GET("/races", httpRaces)
	httpRouter.GET("/races/:page", httpRaces)
	httpRouter.GET("/race", httpRace)
	httpRouter.GET("/race/:raceid", httpRace)
	httpRouter.GET("/profiles", httpProfiles)
	httpRouter.GET("/profiles/:page", httpProfiles) // Handles extra pages for profiles
	httpRouter.GET("/profile", httpProfile)
	httpRouter.GET("/profile/:player", httpProfile) // Handles profile username
	httpRouter.GET("/tournaments", httpTournament)
	httpRouter.GET("/leaderboards", httpLeaderboards)
	httpRouter.GET("/info", httpInfo)
	httpRouter.GET("/download", httpDownload)
	httpRouter.GET("/halloffame", httpHallOfFame)
	httpRouter.Static("/public", path.Join(projectPath, "public"))

	// Figure out the port that we are using for the HTTP server
	var port int
	if useTLS {
		// We want all HTTP requests to be redirected to HTTPS
		// (but make an exception for Let's Encrypt)
		// The Gin router is using the default serve mux,
		// so we need to create a new fresh one for the HTTP handler
		HTTPServeMux := http.NewServeMux()
		letsEncryptPath := path.Join(projectPath, "letsencrypt")
		HTTPServeMux.Handle("/.well-known/acme-challenge/", http.FileServer(http.FileSystem(http.Dir(letsEncryptPath))))
		HTTPServeMux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			http.Redirect(w, req, "https://"+req.Host+req.URL.String(), http.StatusMovedPermanently)
		}))

		// ListenAndServe is blocking, so start listening on a new goroutine
		go func() {
			// Nothing before the colon implies 0.0.0.0
			if err := http.ListenAndServe(":80", HTTPServeMux); err != nil {
				logger.Fatal("http.ListenAndServe failed:", err)
			}
			logger.Fatal("http.ListenAndServe ended for port 80.", nil)
		}()

		// 443 is the default port for HTTPS
		port = 443
	} else {
		// 80 is the default port for HTTP
		port = 80
	}

	// Start listening and serving requests (which is blocking)
	logger.Info("Listening on port " + strconv.Itoa(port) + ".")
	if useTLS {
		if err := http.ListenAndServeTLS(
			":"+strconv.Itoa(port), // Nothing before the colon implies 0.0.0.0
			tlsCertFile,
			tlsKeyFile,
			httpRouter,
		); err != nil {
			logger.Fatal("http.ListenAndServeTLS failed:", err)
		}
		logger.Fatal("http.ListenAndServeTLS ended prematurely.", nil)
	} else {
		// Listen and serve (HTTP)
		if err := http.ListenAndServe(
			":"+strconv.Itoa(port), // Nothing before the colon implies 0.0.0.0
			httpRouter,
		); err != nil {
			logger.Fatal("http.ListenAndServe failed:", err)
		}
		logger.Fatal("http.ListenAndServe ended prematurely.", nil)
	}
}

/*
	HTTP miscellaneous subroutines
*/

func httpServeTemplate(w http.ResponseWriter, templateName string, data interface{}) {
	lp := path.Join(projectPath, "src", "views", "layout.tmpl")
	fp := path.Join(projectPath, "src", "views", templateName+".tmpl")

	// Return a 404 if the template doesn't exist
	info, err := os.Stat(fp)
	if err != nil {
		if os.IsNotExist(err) {
			logger.Debug("NOT FOUND")
			http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
			return
		}
	}

	// Return a 404 if the request is for a directory
	if info.IsDir() {
		http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		return
	}

	// Create the template
	tmpl, err := template.ParseFiles(lp, fp)
	if err != nil {
		logger.Error("Failed to create the template: " + err.Error())
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	// Execute the template and send it to the user
	if err := tmpl.ExecuteTemplate(w, "layout", data); err != nil {
		if strings.HasSuffix(err.Error(), ": write: broken pipe") ||
			strings.HasSuffix(err.Error(), ": client disconnected") ||
			strings.HasSuffix(err.Error(), ": http2: stream closed") ||
			strings.HasSuffix(err.Error(), ": write: connection timed out") {

			// Broken pipe errors can occur when the user presses the "Stop" button while the template is executing
			// We don't want to reporting these errors to Sentry
			// https://stackoverflow.com/questions/26853200/filter-out-broken-pipe-errors-from-template-execution
			// I don't know exactly what the other errors mean
			logger.Info("Ordinary error when executing the template: " + err.Error())
		} else {
			logger.Error("Failed to execute the template: " + err.Error())
		}
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
	}
}
<commit_msg>cleanup<commit_after>package server

import (
	"html/template"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"
	"time"

	"github.com/Zamiell/isaac-racing-server/models"
	"github.com/didip/tollbooth"
	"github.com/didip/tollbooth_gin"
	"github.com/gin-contrib/sessions"
	"github.com/gin-contrib/sessions/cookie"
	"github.com/gin-gonic/gin"
)

const (
	HTTPSessionName  = "isaac.sid"
	HTTPWriteTimeout = 10 * time.Second
)

var (
	sessionStore cookie.Store
	GATrackingID string

	// HTTPClientWithTimeout specifies a timeout on the default http.Client structure
	HTTPClientWithTimeout = &http.Client{
		Timeout: HTTPWriteTimeout,
	}
)

/*
	Data structures
*/

type TemplateData struct {
	Title string

	// Races stuff
	RaceResults       []models.RaceHistory
	SingleRaceResults models.RaceHistory
	SingleRaceFormat  string
	ResultsRaces      []models.RaceHistory
	TotalRaceCount    int
	TotalPages        int
	PreviousPage      int
	NextPage          int
	RaceResultsRanked []models.RaceHistory
	RaceResultsAll    []models.RaceHistory

	// Profiles/profile stuff
	ResultsProfiles   []models.ProfilesRow
	ResultsProfile    models.ProfileData
	TotalProfileCount int
	UsersPerPage      int
	TotalTime         int
	MissingPlayer     string

	// Leaderboard stuff
	LeaderboardSeeded       []models.LeaderboardRowSeeded
	LeaderboardSeededSolo   []models.LeaderboardRowSeededSolo
	LeaderboardUnseeded     []models.LeaderboardRowUnseeded
	LeaderboardUnseededSolo []models.LeaderboardRowUnseededSolo
	LeaderboardDiversity    []models.LeaderboardRowDiversity
	//LeaderboardSeeded []models.LeaderboardRowSeeded

	// Hall of Fame stuff
	Season1r9  []models.SpeedRun
	Season1r14 []models.SpeedRun
	Season2r7  []models.SpeedRun
	Season3r7  []models.SpeedRun
	Season4r7  []models.SpeedRun
	Season5r7  []models.SpeedRun
	Season6r7  []models.SpeedRun
	Season7r7  []models.SpeedRun
	// Season8r7 []models.SpeedRun

	// Tournament Stuff
	CurrentTournament bool
	TournamentRaces   []models.TournamentRace
	TournamentInfos   TournamentStats
	AllTournaments    []TournamentInfo
}

/*
	Initialization function
*/

func httpInit() {
	// Create a new Gin HTTP router
	gin.SetMode(gin.ReleaseMode) // Comment this out to debug HTTP stuff
	httpRouter := gin.Default()

	// Read some HTTP server configuration values from environment variables
	// (they were loaded from the .env file in main.go)
	sessionSecret := os.Getenv("SESSION_SECRET")
	if len(sessionSecret) == 0 {
		logger.Info("The \"SESSION_SECRET\" environment variable is blank; aborting HTTP initialization.")
		return
	}
	domain := os.Getenv("DOMAIN")
	if len(domain) == 0 {
		logger.Info("The \"DOMAIN\" environment variable is blank; aborting HTTP initialization.")
		return
	}
	tlsCertFile := os.Getenv("TLS_CERT_FILE")
	tlsKeyFile := os.Getenv("TLS_KEY_FILE")
	useTLS := true
	if len(tlsCertFile) == 0 || len(tlsKeyFile) == 0 {
		useTLS = false
	}

	// Create a session store
	sessionStore = cookie.NewStore([]byte(sessionSecret))
	options := sessions.Options{
		Path:   "/",
		Domain: domain,
		MaxAge: 5, // 5 seconds
		// After getting a cookie via "/login", the client will immediately
		// establish a WebSocket connection via "/ws", so the cookie only needs
		// to exist for that time frame
		Secure: true,
		// Only send the cookie over HTTPS:
		// https://www.owasp.org/index.php/Testing_for_cookies_attributes_(OTG-SESS-002)
		HttpOnly: true,
		// Mitigate XSS attacks:
		// https://www.owasp.org/index.php/HttpOnly
	}
	if !useTLS {
		options.Secure = false
	}
	sessionStore.Options(options)
	httpRouter.Use(sessions.Sessions(HTTPSessionName, sessionStore))

	// Use the Tollbooth Gin middleware for rate limiting
	limiter := tollbooth.NewLimiter(1, nil) // Limit each user to 1 request per second
	// When a user requests "/", they will also request the CSS and images;
	// this middleware is smart enough to know that it is considered part of the first request
	// However, it is still not possible to spam download CSS or image files
	limiterMiddleware := tollbooth_gin.LimitHandler(limiter)
	httpRouter.Use(limiterMiddleware)

	/*
		This was used as an alterate to the Tollbooth middleware when it wasn't working

		// Use the gin-limiter middleware for rate-limiting
		// We only allow 60 request per minute, an average of 1 per second
		// This is because when a user requests "/", they will also request the CSS and images
		// Based on: https://github.com/julianshen/gin-limiter/blob/master/example/web.go
		limiterMiddleware := limiter.NewRateLimiter(time.Second*60, 60, func(c *gin.Context) (string, error) {
			// Local variables
			r := c.Request
			ip, _, _ := net.SplitHostPort(r.RemoteAddr)

			// Just use the IP address as the key
			return ip, nil
		}).Middleware()
		httpRouter.Use(limiterMiddleware)
	*/

	// Use a custom middleware for Google Analytics tracking
	GATrackingID = os.Getenv("GA_TRACKING_ID")
	if len(GATrackingID) != 0 {
		httpRouter.Use(httpMwGoogleAnalytics)
	}

	// Path handlers (for the WebSocket server)
	httpRouter.POST("/login", httpLogin)
	httpRouter.POST("/register", httpRegister)
	httpRouter.GET("/ws", httpWS)

	// Path handlers (for the website)
	httpRouter.GET("/", httpHome)
	httpRouter.GET("/news", httpNews)
	httpRouter.GET("/races", httpRaces)
	httpRouter.GET("/races/:page", httpRaces)
	httpRouter.GET("/race", httpRace)
	httpRouter.GET("/race/:raceid", httpRace)
	httpRouter.GET("/profiles", httpProfiles)
	httpRouter.GET("/profiles/:page", httpProfiles) // Handles extra pages for profiles
	httpRouter.GET("/profile", httpProfile)
	httpRouter.GET("/profile/:player", httpProfile) // Handles profile username
	httpRouter.GET("/tournaments", httpTournament)
	httpRouter.GET("/leaderboards", httpLeaderboards)
	httpRouter.GET("/info", httpInfo)
	httpRouter.GET("/download", httpDownload)
	httpRouter.GET("/halloffame", httpHallOfFame)
	httpRouter.Static("/public", path.Join(projectPath, "public"))

	// Figure out the port that we are using for the HTTP server
	var port int
	if useTLS {
		// We want all HTTP requests to be redirected to HTTPS
		// (but make an exception for Let's Encrypt)
		// The Gin router is using the default serve mux,
		// so we need to create a new fresh one for the HTTP handler
		HTTPServeMux := http.NewServeMux()
		letsEncryptPath := path.Join(projectPath, "letsencrypt")
		HTTPServeMux.Handle("/.well-known/acme-challenge/", http.FileServer(http.FileSystem(http.Dir(letsEncryptPath))))
		HTTPServeMux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			http.Redirect(w, req, "https://"+req.Host+req.URL.String(), http.StatusMovedPermanently)
		}))

		// ListenAndServe is blocking, so start listening on a new goroutine
		go func() {
			// Nothing before the colon implies 0.0.0.0
			if err := http.ListenAndServe(":80", HTTPServeMux); err != nil {
				logger.Fatal("http.ListenAndServe failed:", err)
			}
			logger.Fatal("http.ListenAndServe ended for port 80.", nil)
		}()

		// 443 is the default port for HTTPS
		port = 443
	} else {
		// 80 is the default port for HTTP
		port = 80
	}

	// Start listening and serving requests (which is blocking)
	logger.Info("Listening on port " + strconv.Itoa(port) + ".")
	if useTLS {
		if err := http.ListenAndServeTLS(
			":"+strconv.Itoa(port), // Nothing before the colon implies 0.0.0.0
			tlsCertFile,
			tlsKeyFile,
			httpRouter,
		); err != nil {
			logger.Fatal("http.ListenAndServeTLS failed:", err)
		}
		logger.Fatal("http.ListenAndServeTLS ended prematurely.", nil)
	} else {
		// Listen and serve (HTTP)
		if err := http.ListenAndServe(
			":"+strconv.Itoa(port), // Nothing before the colon implies 0.0.0.0
			httpRouter,
		); err != nil {
			logger.Fatal("http.ListenAndServe failed:", err)
		}
		logger.Fatal("http.ListenAndServe ended prematurely.", nil)
	}
}

/*
	HTTP miscellaneous subroutines
*/

func httpServeTemplate(w http.ResponseWriter, templateName string, data interface{}) {
	lp := path.Join(projectPath, "src", "views", "layout.tmpl")
	fp := path.Join(projectPath, "src", "views", templateName+".tmpl")

	// Return a 404 if the template doesn't exist
	info, err := os.Stat(fp)
	if err != nil {
		if os.IsNotExist(err) {
			http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
			return
		}
	}

	// Return a 404 if the request is for a directory
	if info.IsDir() {
		http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		return
	}

	// Create the template
	tmpl, err := template.ParseFiles(lp, fp)
	if err != nil {
		logger.Error("Failed to create the template: " + err.Error())
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	// Execute the template and send it to the user
	if err := tmpl.ExecuteTemplate(w, "layout", data); err != nil {
		if strings.HasSuffix(err.Error(), ": write: broken pipe") ||
			strings.HasSuffix(err.Error(), ": client disconnected") ||
			strings.HasSuffix(err.Error(), ": http2: stream closed") ||
			strings.HasSuffix(err.Error(), ": write: connection timed out") {

			// Broken pipe errors can occur when the user presses the "Stop" button while the template is executing
			// We don't want to reporting these errors to Sentry
			// https://stackoverflow.com/questions/26853200/filter-out-broken-pipe-errors-from-template-execution
			// I don't know exactly what the other errors mean
			logger.Info("Ordinary error when executing the template: " + err.Error())
		} else {
			logger.Error("Failed to execute the template: " + err.Error())
		}
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
	}
}
<|endoftext|>