<commit_before>package main

import (
	"errors"
	"fmt"
	"os"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/NebulousLabs/Sia/api"
	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/modules/consensus"
	"github.com/NebulousLabs/Sia/modules/explorer"
	"github.com/NebulousLabs/Sia/modules/gateway"
	"github.com/NebulousLabs/Sia/modules/host"
	"github.com/NebulousLabs/Sia/modules/miner"
	"github.com/NebulousLabs/Sia/modules/renter"
	"github.com/NebulousLabs/Sia/modules/transactionpool"
	"github.com/NebulousLabs/Sia/modules/wallet"
	"github.com/NebulousLabs/Sia/profile"

	"github.com/bgentry/speakeasy"
	"github.com/spf13/cobra"
)

// verifyAPISecurity checks that the security values are consistent with a
// sane, secure system.
func verifyAPISecurity(config Config) error {
	// Make sure that only the loopback address is allowed unless the
	// --disable-api-security flag has been used.
	if !config.Siad.AllowAPIBind {
		addr := modules.NetAddress(config.Siad.APIaddr)
		if !addr.IsLoopback() {
			if addr.Host() == "" {
				return fmt.Errorf("a blank host will listen on all interfaces, did you mean localhost:%v?\nyou must pass --disable-api-security to bind Siad to a non-localhost address", addr.Port())
			}
			return errors.New("you must pass --disable-api-security to bind Siad to a non-localhost address")
		}
		return nil
	}

	// If the --disable-api-security flag is used, enforce that
	// --authenticate-api must also be used.
	if config.Siad.AllowAPIBind && !config.Siad.AuthenticateAPI {
		return errors.New("cannot use --disable-api-security without setting an api password")
	}
	return nil
}

// processNetAddr adds a ':' to a bare integer, so that it is a proper port
// number.
func processNetAddr(addr string) string {
	_, err := strconv.Atoi(addr)
	if err == nil {
		return ":" + addr
	}
	return addr
}

// processModules makes the modules string lowercase to make checking if a
// module in the string easier, and returns an error if the string contains an
// invalid module character.
func processModules(modules string) (string, error) {
	modules = strings.ToLower(modules)
	validModules := "cghmrtwe"
	invalidModules := modules
	for _, m := range validModules {
		invalidModules = strings.Replace(invalidModules, string(m), "", 1)
	}
	if len(invalidModules) > 0 {
		return "", errors.New("Unable to parse --modules flag, unrecognized or duplicate modules: " + invalidModules)
	}
	return modules, nil
}

// processProfileFlags checks that the flags given for profiling are valid.
func processProfileFlags(profile string) (string, error) {
	profile = strings.ToLower(profile)
	validProfiles := "cmt"

	invalidProfiles := profile
	for _, p := range validProfiles {
		invalidProfiles = strings.Replace(invalidProfiles, string(p), "", 1)
	}
	if len(invalidProfiles) > 0 {
		return "", errors.New("Unable to parse --profile flags, unrecognized or duplicate flags: " + invalidProfiles)
	}
	return profile, nil
}

// processConfig checks the configuration values and performs cleanup on
// incorrect-but-allowed values.
func processConfig(config Config) (Config, error) {
	var err1, err2 error
	config.Siad.APIaddr = processNetAddr(config.Siad.APIaddr)
	config.Siad.RPCaddr = processNetAddr(config.Siad.RPCaddr)
	config.Siad.HostAddr = processNetAddr(config.Siad.HostAddr)
	config.Siad.Modules, err1 = processModules(config.Siad.Modules)
	config.Siad.Profile, err2 = processProfileFlags(config.Siad.Profile)
	err3 := verifyAPISecurity(config)
	err := build.JoinErrors([]error{err1, err2, err3}, ", and ")
	if err != nil {
		return Config{}, err
	}
	return config, nil
}

// startDaemon uses the config parameters to initialize Sia modules and start
// siad.
func startDaemon(config Config) (err error) {
	// Prompt user for API password.
	if config.Siad.AuthenticateAPI {
		config.APIPassword, err = speakeasy.Ask("Enter API password: ")
		if err != nil {
			return err
		}
		if config.APIPassword == "" {
			return errors.New("password cannot be blank")
		}
	}

	// Process the config variables after they are parsed by cobra.
	config, err = processConfig(config)
	if err != nil {
		return err
	}

	// Print a startup message.
	fmt.Println("Loading...")
	loadStart := time.Now()

	// Create the server and start serving daemon routes immediately.
	fmt.Printf("(0/%d) Loading siad...\n", len(config.Siad.Modules))
	srv, err := NewServer(config.Siad.APIaddr, config.Siad.RequiredUserAgent, config.APIPassword)
	if err != nil {
		return err
	}

	servErrs := make(chan error)
	go func() {
		servErrs <- srv.Serve()
	}()

	// Initialize the Sia modules
	i := 0
	var g modules.Gateway
	if strings.Contains(config.Siad.Modules, "g") {
		i++
		fmt.Printf("(%d/%d) Loading gateway...\n", i, len(config.Siad.Modules))
		g, err = gateway.New(config.Siad.RPCaddr, !config.Siad.NoBootstrap, filepath.Join(config.Siad.SiaDir, modules.GatewayDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing gateway...")
			err := g.Close()
			if err != nil {
				fmt.Println("Error during gateway shutdown:", err)
			}
		}()
	}
	var cs modules.ConsensusSet
	if strings.Contains(config.Siad.Modules, "c") {
		i++
		fmt.Printf("(%d/%d) Loading consensus...\n", i, len(config.Siad.Modules))
		cs, err = consensus.New(g, !config.Siad.NoBootstrap, filepath.Join(config.Siad.SiaDir, modules.ConsensusDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing consensus set...")
			err := cs.Close()
			if err != nil {
				fmt.Println("Error during consensus set shutdown:", err)
			}
		}()
	}
	var e modules.Explorer
	if strings.Contains(config.Siad.Modules, "e") {
		i++
		fmt.Printf("(%d/%d) Loading explorer...\n", i, len(config.Siad.Modules))
		e, err = explorer.New(cs, filepath.Join(config.Siad.SiaDir, modules.ExplorerDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing explorer...")
			err := e.Close()
			if err != nil {
				fmt.Println("Error during explorer shutdown:", err)
			}
		}()
	}
	var tpool modules.TransactionPool
	if strings.Contains(config.Siad.Modules, "t") {
		i++
		fmt.Printf("(%d/%d) Loading transaction pool...\n", i, len(config.Siad.Modules))
		tpool, err = transactionpool.New(cs, g, filepath.Join(config.Siad.SiaDir, modules.TransactionPoolDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing transaction pool...")
			err := tpool.Close()
			if err != nil {
				fmt.Println("Error during transaction pool shutdown:", err)
			}
		}()
	}
	var w modules.Wallet
	if strings.Contains(config.Siad.Modules, "w") {
		i++
		fmt.Printf("(%d/%d) Loading wallet...\n", i, len(config.Siad.Modules))
		w, err = wallet.New(cs, tpool, filepath.Join(config.Siad.SiaDir, modules.WalletDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing wallet...")
			err := w.Close()
			if err != nil {
				fmt.Println("Error during wallet shutdown:", err)
			}
		}()
	}
	var m modules.Miner
	if strings.Contains(config.Siad.Modules, "m") {
		i++
		fmt.Printf("(%d/%d) Loading miner...\n", i, len(config.Siad.Modules))
		m, err = miner.New(cs, tpool, w, filepath.Join(config.Siad.SiaDir, modules.MinerDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing miner...")
			err := m.Close()
			if err != nil {
				fmt.Println("Error during miner shutdown:", err)
			}
		}()
	}
	var h modules.Host
	if strings.Contains(config.Siad.Modules, "h") {
		i++
		fmt.Printf("(%d/%d) Loading host...\n", i, len(config.Siad.Modules))
		h, err = host.New(cs, tpool, w, config.Siad.HostAddr, filepath.Join(config.Siad.SiaDir, modules.HostDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing host...")
			err := h.Close()
			if err != nil {
				fmt.Println("Error during host shutdown:", err)
			}
		}()
	}
	var r modules.Renter
	if strings.Contains(config.Siad.Modules, "r") {
		i++
		fmt.Printf("(%d/%d) Loading renter...\n", i, len(config.Siad.Modules))
		r, err = renter.New(g, cs, w, tpool, filepath.Join(config.Siad.SiaDir, modules.RenterDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing renter...")
			err := r.Close()
			if err != nil {
				fmt.Println("Error during renter shutdown:", err)
			}
		}()
	}

	// Create the Sia API
	a := api.New(
		config.Siad.RequiredUserAgent,
		config.APIPassword,
		cs,
		e,
		g,
		h,
		m,
		r,
		tpool,
		w,
	)

	// connect the API to the server
	srv.mux.Handle("/", a)

	// stop the server if a kill signal is caught
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, os.Interrupt, os.Kill)
	go func() {
		<-sigChan
		fmt.Println("\rCaught stop signal, quitting...")
		srv.Close()
	}()

	// Print a 'startup complete' message.
	startupTime := time.Since(loadStart)
	fmt.Println("Finished loading in", startupTime.Seconds(), "seconds")

	err = <-servErrs
	if err != nil {
		build.Critical(err)
	}

	return nil
}

// startDaemonCmd is a passthrough function for startDaemon.
func startDaemonCmd(cmd *cobra.Command, _ []string) {
	var profileCPU, profileMem, profileTrace bool

	profileCPU = strings.Contains(globalConfig.Siad.Profile, "c")
	profileMem = strings.Contains(globalConfig.Siad.Profile, "m")
	profileTrace = strings.Contains(globalConfig.Siad.Profile, "t")

	if build.DEBUG {
		profileCPU = true
		profileMem = true
	}

	if profileCPU || profileMem || profileTrace {
		go profile.StartContinuousProfile(globalConfig.Siad.ProfileDir, profileCPU, profileMem, profileTrace)
	}

	// Start siad. startDaemon will only return when it is shutting down.
	err := startDaemon(globalConfig)
	if err != nil {
		die(err)
	}

	// Daemon seems to have closed cleanly. Print a 'closed' mesasge.
	fmt.Println("Shutdown complete.")
}
<commit_msg>siad: align closing and loading consensus messages<commit_after>package main

import (
	"errors"
	"fmt"
	"os"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/NebulousLabs/Sia/api"
	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/modules/consensus"
	"github.com/NebulousLabs/Sia/modules/explorer"
	"github.com/NebulousLabs/Sia/modules/gateway"
	"github.com/NebulousLabs/Sia/modules/host"
	"github.com/NebulousLabs/Sia/modules/miner"
	"github.com/NebulousLabs/Sia/modules/renter"
	"github.com/NebulousLabs/Sia/modules/transactionpool"
	"github.com/NebulousLabs/Sia/modules/wallet"
	"github.com/NebulousLabs/Sia/profile"

	"github.com/bgentry/speakeasy"
	"github.com/spf13/cobra"
)

// verifyAPISecurity checks that the security values are consistent with a
// sane, secure system.
func verifyAPISecurity(config Config) error {
	// Make sure that only the loopback address is allowed unless the
	// --disable-api-security flag has been used.
	if !config.Siad.AllowAPIBind {
		addr := modules.NetAddress(config.Siad.APIaddr)
		if !addr.IsLoopback() {
			if addr.Host() == "" {
				return fmt.Errorf("a blank host will listen on all interfaces, did you mean localhost:%v?\nyou must pass --disable-api-security to bind Siad to a non-localhost address", addr.Port())
			}
			return errors.New("you must pass --disable-api-security to bind Siad to a non-localhost address")
		}
		return nil
	}

	// If the --disable-api-security flag is used, enforce that
	// --authenticate-api must also be used.
	if config.Siad.AllowAPIBind && !config.Siad.AuthenticateAPI {
		return errors.New("cannot use --disable-api-security without setting an api password")
	}
	return nil
}

// processNetAddr adds a ':' to a bare integer, so that it is a proper port
// number.
func processNetAddr(addr string) string {
	_, err := strconv.Atoi(addr)
	if err == nil {
		return ":" + addr
	}
	return addr
}

// processModules makes the modules string lowercase to make checking if a
// module in the string easier, and returns an error if the string contains an
// invalid module character.
func processModules(modules string) (string, error) {
	modules = strings.ToLower(modules)
	validModules := "cghmrtwe"
	invalidModules := modules
	for _, m := range validModules {
		invalidModules = strings.Replace(invalidModules, string(m), "", 1)
	}
	if len(invalidModules) > 0 {
		return "", errors.New("Unable to parse --modules flag, unrecognized or duplicate modules: " + invalidModules)
	}
	return modules, nil
}

// processProfileFlags checks that the flags given for profiling are valid.
func processProfileFlags(profile string) (string, error) {
	profile = strings.ToLower(profile)
	validProfiles := "cmt"

	invalidProfiles := profile
	for _, p := range validProfiles {
		invalidProfiles = strings.Replace(invalidProfiles, string(p), "", 1)
	}
	if len(invalidProfiles) > 0 {
		return "", errors.New("Unable to parse --profile flags, unrecognized or duplicate flags: " + invalidProfiles)
	}
	return profile, nil
}

// processConfig checks the configuration values and performs cleanup on
// incorrect-but-allowed values.
func processConfig(config Config) (Config, error) {
	var err1, err2 error
	config.Siad.APIaddr = processNetAddr(config.Siad.APIaddr)
	config.Siad.RPCaddr = processNetAddr(config.Siad.RPCaddr)
	config.Siad.HostAddr = processNetAddr(config.Siad.HostAddr)
	config.Siad.Modules, err1 = processModules(config.Siad.Modules)
	config.Siad.Profile, err2 = processProfileFlags(config.Siad.Profile)
	err3 := verifyAPISecurity(config)
	err := build.JoinErrors([]error{err1, err2, err3}, ", and ")
	if err != nil {
		return Config{}, err
	}
	return config, nil
}

// startDaemon uses the config parameters to initialize Sia modules and start
// siad.
func startDaemon(config Config) (err error) {
	// Prompt user for API password.
	if config.Siad.AuthenticateAPI {
		config.APIPassword, err = speakeasy.Ask("Enter API password: ")
		if err != nil {
			return err
		}
		if config.APIPassword == "" {
			return errors.New("password cannot be blank")
		}
	}

	// Process the config variables after they are parsed by cobra.
	config, err = processConfig(config)
	if err != nil {
		return err
	}

	// Print a startup message.
	fmt.Println("Loading...")
	loadStart := time.Now()

	// Create the server and start serving daemon routes immediately.
	fmt.Printf("(0/%d) Loading siad...\n", len(config.Siad.Modules))
	srv, err := NewServer(config.Siad.APIaddr, config.Siad.RequiredUserAgent, config.APIPassword)
	if err != nil {
		return err
	}

	servErrs := make(chan error)
	go func() {
		servErrs <- srv.Serve()
	}()

	// Initialize the Sia modules
	i := 0
	var g modules.Gateway
	if strings.Contains(config.Siad.Modules, "g") {
		i++
		fmt.Printf("(%d/%d) Loading gateway...\n", i, len(config.Siad.Modules))
		g, err = gateway.New(config.Siad.RPCaddr, !config.Siad.NoBootstrap, filepath.Join(config.Siad.SiaDir, modules.GatewayDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing gateway...")
			err := g.Close()
			if err != nil {
				fmt.Println("Error during gateway shutdown:", err)
			}
		}()
	}
	var cs modules.ConsensusSet
	if strings.Contains(config.Siad.Modules, "c") {
		i++
		fmt.Printf("(%d/%d) Loading consensus...\n", i, len(config.Siad.Modules))
		cs, err = consensus.New(g, !config.Siad.NoBootstrap, filepath.Join(config.Siad.SiaDir, modules.ConsensusDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing consensus...")
			err := cs.Close()
			if err != nil {
				fmt.Println("Error during consensus set shutdown:", err)
			}
		}()
	}
	var e modules.Explorer
	if strings.Contains(config.Siad.Modules, "e") {
		i++
		fmt.Printf("(%d/%d) Loading explorer...\n", i, len(config.Siad.Modules))
		e, err = explorer.New(cs, filepath.Join(config.Siad.SiaDir, modules.ExplorerDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing explorer...")
			err := e.Close()
			if err != nil {
				fmt.Println("Error during explorer shutdown:", err)
			}
		}()
	}
	var tpool modules.TransactionPool
	if strings.Contains(config.Siad.Modules, "t") {
		i++
		fmt.Printf("(%d/%d) Loading transaction pool...\n", i, len(config.Siad.Modules))
		tpool, err = transactionpool.New(cs, g, filepath.Join(config.Siad.SiaDir, modules.TransactionPoolDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing transaction pool...")
			err := tpool.Close()
			if err != nil {
				fmt.Println("Error during transaction pool shutdown:", err)
			}
		}()
	}
	var w modules.Wallet
	if strings.Contains(config.Siad.Modules, "w") {
		i++
		fmt.Printf("(%d/%d) Loading wallet...\n", i, len(config.Siad.Modules))
		w, err = wallet.New(cs, tpool, filepath.Join(config.Siad.SiaDir, modules.WalletDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing wallet...")
			err := w.Close()
			if err != nil {
				fmt.Println("Error during wallet shutdown:", err)
			}
		}()
	}
	var m modules.Miner
	if strings.Contains(config.Siad.Modules, "m") {
		i++
		fmt.Printf("(%d/%d) Loading miner...\n", i, len(config.Siad.Modules))
		m, err = miner.New(cs, tpool, w, filepath.Join(config.Siad.SiaDir, modules.MinerDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing miner...")
			err := m.Close()
			if err != nil {
				fmt.Println("Error during miner shutdown:", err)
			}
		}()
	}
	var h modules.Host
	if strings.Contains(config.Siad.Modules, "h") {
		i++
		fmt.Printf("(%d/%d) Loading host...\n", i, len(config.Siad.Modules))
		h, err = host.New(cs, tpool, w, config.Siad.HostAddr, filepath.Join(config.Siad.SiaDir, modules.HostDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing host...")
			err := h.Close()
			if err != nil {
				fmt.Println("Error during host shutdown:", err)
			}
		}()
	}
	var r modules.Renter
	if strings.Contains(config.Siad.Modules, "r") {
		i++
		fmt.Printf("(%d/%d) Loading renter...\n", i, len(config.Siad.Modules))
		r, err = renter.New(g, cs, w, tpool, filepath.Join(config.Siad.SiaDir, modules.RenterDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing renter...")
			err := r.Close()
			if err != nil {
				fmt.Println("Error during renter shutdown:", err)
			}
		}()
	}

	// Create the Sia API
	a := api.New(
		config.Siad.RequiredUserAgent,
		config.APIPassword,
		cs,
		e,
		g,
		h,
		m,
		r,
		tpool,
		w,
	)

	// connect the API to the server
	srv.mux.Handle("/", a)

	// stop the server if a kill signal is caught
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, os.Interrupt, os.Kill)
	go func() {
		<-sigChan
		fmt.Println("\rCaught stop signal, quitting...")
		srv.Close()
	}()

	// Print a 'startup complete' message.
	startupTime := time.Since(loadStart)
	fmt.Println("Finished loading in", startupTime.Seconds(), "seconds")

	err = <-servErrs
	if err != nil {
		build.Critical(err)
	}

	return nil
}

// startDaemonCmd is a passthrough function for startDaemon.
func startDaemonCmd(cmd *cobra.Command, _ []string) {
	var profileCPU, profileMem, profileTrace bool

	profileCPU = strings.Contains(globalConfig.Siad.Profile, "c")
	profileMem = strings.Contains(globalConfig.Siad.Profile, "m")
	profileTrace = strings.Contains(globalConfig.Siad.Profile, "t")

	if build.DEBUG {
		profileCPU = true
		profileMem = true
	}

	if profileCPU || profileMem || profileTrace {
		go profile.StartContinuousProfile(globalConfig.Siad.ProfileDir, profileCPU, profileMem, profileTrace)
	}

	// Start siad. startDaemon will only return when it is shutting down.
	err := startDaemon(globalConfig)
	if err != nil {
		die(err)
	}

	// Daemon seems to have closed cleanly. Print a 'closed' mesasge.
	fmt.Println("Shutdown complete.")
}
<|endoftext|>


<commit_before>// +build !js

package vnet

import (
	"net"
	"testing"

	"github.com/pion/logging"
	"github.com/stretchr/testify/assert"
)

func TestNetNative(t *testing.T) {
	log := logging.NewDefaultLoggerFactory().NewLogger("test")

	t.Run("Interfaces", func(t *testing.T) {
		nw := NewNet(nil)
		assert.False(t, nw.IsVirtual(), "should be false")
		interfaces, err := nw.Interfaces()
		assert.NoError(t, err, "should succeed")
		log.Debugf("interfaces: %+v\n", interfaces)
		for _, ifc := range interfaces {
			if ifc.Name == "lo0" {
				_, err := ifc.Addrs()
				assert.NoError(t, err, "should succeed")
			}

			if addrs, err := ifc.Addrs(); err == nil {
				for _, addr := range addrs {
					log.Debugf("[%d] %s:%s",
						ifc.Index,
						addr.Network(),
						addr.String())
				}
			}
		}
	})

	t.Run("ResolveUDPAddr", func(t *testing.T) {
		nw := NewNet(nil)

		udpAddr, err := nw.ResolveUDPAddr("udp", "localhost:1234")
		if !assert.NoError(t, err, "should succeed") {
			return
		}
		assert.Equal(t, "127.0.0.1", udpAddr.IP.String(), "should match")
		assert.Equal(t, 1234, udpAddr.Port, "should match")
	})

	t.Run("ListenPacket", func(t *testing.T) {
		nw := NewNet(nil)

		conn, err := nw.ListenPacket("udp", "127.0.0.1:0")
		if !assert.NoError(t, err, "should succeed") {
			return
		}

		udpConn, ok := conn.(*net.UDPConn)
		assert.True(t, ok, "should succeed")
		log.Debugf("udpConn: %+v", udpConn)

		laddr := conn.LocalAddr().String()
		log.Debugf("laddr: %s", laddr)
	})

	t.Run("ListenUDP random port", func(t *testing.T) {
		nw := NewNet(nil)

		srcAddr := &net.UDPAddr{
			IP: net.ParseIP("127.0.0.1"),
		}
		conn, err := nw.ListenUDP("udp", srcAddr)
		assert.NoError(t, err, "should succeed")

		laddr := conn.LocalAddr().String()
		log.Debugf("laddr: %s", laddr)

		assert.NoError(t, conn.Close(), "should succeed")
	})

	t.Run("Dial (UDP)", func(t *testing.T) {
		nw := NewNet(nil)

		conn, err := nw.Dial("udp", "127.0.0.1:1234")
		assert.NoError(t, err, "should succeed")

		laddr := conn.LocalAddr()
		log.Debugf("laddr: %s", laddr.String())

		raddr := conn.RemoteAddr()
		log.Debugf("raddr: %s", raddr.String())

		assert.Equal(t, "127.0.0.1", laddr.(*net.UDPAddr).IP.String(), "should match")
		assert.True(t, laddr.(*net.UDPAddr).Port != 0, "should match")
		assert.Equal(t, "127.0.0.1:1234", raddr.String(), "should match")
		assert.NoError(t, conn.Close(), "should succeed")
	})

	t.Run("DialUDP", func(t *testing.T) {
		nw := NewNet(nil)

		locAddr := &net.UDPAddr{
			IP:   net.IPv4(127, 0, 0, 1),
			Port: 0,
		}

		remAddr := &net.UDPAddr{
			IP:   net.IPv4(127, 0, 0, 1),
			Port: 1234,
		}

		conn, err := nw.DialUDP("udp", locAddr, remAddr)
		assert.NoError(t, err, "should succeed")

		laddr := conn.LocalAddr()
		log.Debugf("laddr: %s", laddr.String())

		raddr := conn.RemoteAddr()
		log.Debugf("raddr: %s", raddr.String())

		assert.Equal(t, "127.0.0.1", laddr.(*net.UDPAddr).IP.String(), "should match")
		assert.True(t, laddr.(*net.UDPAddr).Port != 0, "should match")
		assert.Equal(t, "127.0.0.1:1234", raddr.String(), "should match")
		assert.NoError(t, conn.Close(), "should succeed")
	})

	t.Run("UDPLoopback", func(t *testing.T) {
		nw := NewNet(nil)

		conn, err := nw.ListenPacket("udp", "127.0.0.1:0")
		assert.NoError(t, err, "should succeed")
		laddr := conn.LocalAddr()
		msg := "PING!"
		n, err := conn.WriteTo([]byte(msg), laddr)
		assert.NoError(t, err, "should succeed")
		assert.Equal(t, len(msg), n, "should match")

		buf := make([]byte, 1000)
		n, addr, err := conn.ReadFrom(buf)
		assert.NoError(t, err, "should succeed")
		assert.Equal(t, len(msg), n, "should match")
		assert.Equal(t, msg, string(buf[:n]), "should match")
		assert.Equal(t, laddr.(*net.UDPAddr).String(), addr.(*net.UDPAddr).String(), "should match")
		assert.NoError(t, conn.Close(), "should succeed")
	})

	t.Run("Dialer", func(t *testing.T) {
		nw := NewNet(nil)

		dialer := nw.CreateDialer(&net.Dialer{
			LocalAddr: &net.UDPAddr{
				IP:   net.ParseIP("127.0.0.1"),
				Port: 0,
			},
		})

		conn, err := dialer.Dial("udp", "127.0.0.1:1234")
		assert.NoError(t, err, "should succeed")

		laddr := conn.LocalAddr()
		log.Debugf("laddr: %s", laddr.String())

		raddr := conn.RemoteAddr()
		log.Debugf("raddr: %s", raddr.String())

		assert.Equal(t, "127.0.0.1", laddr.(*net.UDPAddr).IP.String(), "should match")
		assert.True(t, laddr.(*net.UDPAddr).Port != 0, "should match")
		assert.Equal(t, "127.0.0.1:1234", raddr.String(), "should match")
		assert.NoError(t, conn.Close(), "should succeed")
	})

	t.Run("Unexpected operations", func(t *testing.T) {
		// For portability of test, find a name of loopack interface name first
		var loName string
		ifs, err := net.Interfaces()
		assert.NoError(t, err, "should succeed")
		for _, ifc := range ifs {
			if ifc.Flags&net.FlagLoopback != 0 {
				loName = ifc.Name
				break
			}
		}

		nw := NewNet(nil)

		if len(loName) > 0 {
			// InterfaceByName
			ifc, err2 := nw.InterfaceByName(loName)
			assert.NoError(t, err2, "should succeed")
			assert.Equal(t, loName, ifc.Name, "should match")

			// getInterface
			_, err2 = nw.getInterface(loName)
			assert.Error(t, err2, "should fail")
		}

		_, err = nw.InterfaceByName("foo0")
		assert.Error(t, err, "should fail")

		// setRouter
		err = nw.setRouter(nil)
		assert.Error(t, err, "should fail")

		// onInboundChunk (shouldn't crash)
		nw.onInboundChunk(nil)

		// getStaticIPs
		ips := nw.getStaticIPs()
		assert.Nil(t, ips, "should be nil")
	})
}
<commit_msg>Fix TestNetNative on Debian<commit_after>// +build !js

package vnet

import (
	"net"
	"testing"

	"github.com/pion/logging"
	"github.com/stretchr/testify/assert"
)

func TestNetNative(t *testing.T) {
	log := logging.NewDefaultLoggerFactory().NewLogger("test")

	t.Run("Interfaces", func(t *testing.T) {
		nw := NewNet(nil)
		assert.False(t, nw.IsVirtual(), "should be false")
		interfaces, err := nw.Interfaces()
		assert.NoError(t, err, "should succeed")
		log.Debugf("interfaces: %+v\n", interfaces)
		for _, ifc := range interfaces {
			if ifc.Name == "lo0" {
				_, err := ifc.Addrs()
				assert.NoError(t, err, "should succeed")
			}

			if addrs, err := ifc.Addrs(); err == nil {
				for _, addr := range addrs {
					log.Debugf("[%d] %s:%s",
						ifc.Index,
						addr.Network(),
						addr.String())
				}
			}
		}
	})

	t.Run("ResolveUDPAddr", func(t *testing.T) {
		nw := NewNet(nil)

		udpAddr, err := nw.ResolveUDPAddr("udp", "localhost:1234")
		if !assert.NoError(t, err, "should succeed") {
			return
		}
		assert.Contains(t, []string{"127.0.0.1", "127.0.1.1"}, udpAddr.IP.String(), "should match")
		assert.Equal(t, 1234, udpAddr.Port, "should match")
	})

	t.Run("ListenPacket", func(t *testing.T) {
		nw := NewNet(nil)

		conn, err := nw.ListenPacket("udp", "127.0.0.1:0")
		if !assert.NoError(t, err, "should succeed") {
			return
		}

		udpConn, ok := conn.(*net.UDPConn)
		assert.True(t, ok, "should succeed")
		log.Debugf("udpConn: %+v", udpConn)

		laddr := conn.LocalAddr().String()
		log.Debugf("laddr: %s", laddr)
	})

	t.Run("ListenUDP random port", func(t *testing.T) {
		nw := NewNet(nil)

		srcAddr := &net.UDPAddr{
			IP: net.ParseIP("127.0.0.1"),
		}
		conn, err := nw.ListenUDP("udp", srcAddr)
		assert.NoError(t, err, "should succeed")

		laddr := conn.LocalAddr().String()
		log.Debugf("laddr: %s", laddr)

		assert.NoError(t, conn.Close(), "should succeed")
	})

	t.Run("Dial (UDP)", func(t *testing.T) {
		nw := NewNet(nil)

		conn, err := nw.Dial("udp", "127.0.0.1:1234")
		assert.NoError(t, err, "should succeed")

		laddr := conn.LocalAddr()
		log.Debugf("laddr: %s", laddr.String())

		raddr := conn.RemoteAddr()
		log.Debugf("raddr: %s", raddr.String())

		assert.Equal(t, "127.0.0.1", laddr.(*net.UDPAddr).IP.String(), "should match")
		assert.True(t, laddr.(*net.UDPAddr).Port != 0, "should match")
		assert.Equal(t, "127.0.0.1:1234", raddr.String(), "should match")
		assert.NoError(t, conn.Close(), "should succeed")
	})

	t.Run("DialUDP", func(t *testing.T) {
		nw := NewNet(nil)

		locAddr := &net.UDPAddr{
			IP:   net.IPv4(127, 0, 0, 1),
			Port: 0,
		}

		remAddr := &net.UDPAddr{
			IP:   net.IPv4(127, 0, 0, 1),
			Port: 1234,
		}

		conn, err := nw.DialUDP("udp", locAddr, remAddr)
		assert.NoError(t, err, "should succeed")

		laddr := conn.LocalAddr()
		log.Debugf("laddr: %s", laddr.String())

		raddr := conn.RemoteAddr()
		log.Debugf("raddr: %s", raddr.String())

		assert.Equal(t, "127.0.0.1", laddr.(*net.UDPAddr).IP.String(), "should match")
		assert.True(t, laddr.(*net.UDPAddr).Port != 0, "should match")
		assert.Equal(t, "127.0.0.1:1234", raddr.String(), "should match")
		assert.NoError(t, conn.Close(), "should succeed")
	})

	t.Run("UDPLoopback", func(t *testing.T) {
		nw := NewNet(nil)

		conn, err := nw.ListenPacket("udp", "127.0.0.1:0")
		assert.NoError(t, err, "should succeed")
		laddr := conn.LocalAddr()
		msg := "PING!"
		n, err := conn.WriteTo([]byte(msg), laddr)
		assert.NoError(t, err, "should succeed")
		assert.Equal(t, len(msg), n, "should match")

		buf := make([]byte, 1000)
		n, addr, err := conn.ReadFrom(buf)
		assert.NoError(t, err, "should succeed")
		assert.Equal(t, len(msg), n, "should match")
		assert.Equal(t, msg, string(buf[:n]), "should match")
		assert.Equal(t, laddr.(*net.UDPAddr).String(), addr.(*net.UDPAddr).String(), "should match")
		assert.NoError(t, conn.Close(), "should succeed")
	})

	t.Run("Dialer", func(t *testing.T) {
		nw := NewNet(nil)

		dialer := nw.CreateDialer(&net.Dialer{
			LocalAddr: &net.UDPAddr{
				IP:   net.ParseIP("127.0.0.1"),
				Port: 0,
			},
		})

		conn, err := dialer.Dial("udp", "127.0.0.1:1234")
		assert.NoError(t, err, "should succeed")

		laddr := conn.LocalAddr()
		log.Debugf("laddr: %s", laddr.String())

		raddr := conn.RemoteAddr()
		log.Debugf("raddr: %s", raddr.String())

		assert.Equal(t, "127.0.0.1", laddr.(*net.UDPAddr).IP.String(), "should match")
		assert.True(t, laddr.(*net.UDPAddr).Port != 0, "should match")
		assert.Equal(t, "127.0.0.1:1234", raddr.String(), "should match")
		assert.NoError(t, conn.Close(), "should succeed")
	})

	t.Run("Unexpected operations", func(t *testing.T) {
		// For portability of test, find a name of loopack interface name first
		var loName string
		ifs, err := net.Interfaces()
		assert.NoError(t, err, "should succeed")
		for _, ifc := range ifs {
			if ifc.Flags&net.FlagLoopback != 0 {
				loName = ifc.Name
				break
			}
		}

		nw := NewNet(nil)

		if len(loName) > 0 {
			// InterfaceByName
			ifc, err2 := nw.InterfaceByName(loName)
			assert.NoError(t, err2, "should succeed")
			assert.Equal(t, loName, ifc.Name, "should match")

			// getInterface
			_, err2 = nw.getInterface(loName)
			assert.Error(t, err2, "should fail")
		}

		_, err = nw.InterfaceByName("foo0")
		assert.Error(t, err, "should fail")

		// setRouter
		err = nw.setRouter(nil)
		assert.Error(t, err, "should fail")

		// onInboundChunk (shouldn't crash)
		nw.onInboundChunk(nil)

		// getStaticIPs
		ips := nw.getStaticIPs()
		assert.Nil(t, ips, "should be nil")
	})
}
<|endoftext|>


<commit_before>// Copyright 2021 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     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.

//go:build ebitencbackend
// +build ebitencbackend

// TODO: This implementation is very similar to github.com/hajimehoshi/oto/v2's player_notjs.go
// Unify them if possible.

package cbackend

import (
	"io"
	"runtime"
	"sync"
	"sync/atomic"
)

type playerState int

const (
	playerPaused playerState = iota
	playerPlay
	playerClosed
)

type players struct {
	players map[*playerImpl]struct{}
	buf     []float32
	cond    *sync.Cond
}

func newPlayers() *players {
	p := &players{
		cond: sync.NewCond(&sync.Mutex{}),
	}
	go p.loop()
	return p
}

func (ps *players) shouldWait() bool {
	for p := range ps.players {
		if p.canReadSourceToBuffer() {
			return false
		}
	}
	return true
}

func (ps *players) wait() {
	ps.cond.L.Lock()
	defer ps.cond.L.Unlock()

	for ps.shouldWait() {
		ps.cond.Wait()
	}
}

func (ps *players) loop() {
	var players []*playerImpl
	for {
		ps.wait()

		ps.cond.L.Lock()
		players = players[:0]
		for p := range ps.players {
			players = append(players, p)
		}
		ps.cond.L.Unlock()

		for _, p := range players {
			p.readSourceToBuffer()
		}
	}
}

func (ps *players) addPlayer(player *playerImpl) {
	ps.cond.L.Lock()
	defer ps.cond.L.Unlock()

	if ps.players == nil {
		ps.players = map[*playerImpl]struct{}{}
	}
	ps.players[player] = struct{}{}
	ps.cond.Signal()
}

func (ps *players) removePlayer(player *playerImpl) {
	ps.cond.L.Lock()
	defer ps.cond.L.Unlock()

	delete(ps.players, player)
	ps.cond.Signal()
}

func (ps *players) read(buf []float32) {
	ps.cond.L.Lock()
	players := make([]*playerImpl, 0, len(ps.players))
	for p := range ps.players {
		players = append(players, p)
	}
	ps.cond.L.Unlock()

	for i := range buf {
		buf[i] = 0
	}
	for _, p := range players {
		p.readBufferAndAdd(buf)
	}
	ps.cond.Signal()
}

type Player struct {
	p *playerImpl
}

type playerImpl struct {
	context *Context
	src     io.Reader
	volume  float64
	err     atomic.Value
	state   playerState
	tmpbuf  []byte
	buf     []byte
	eof     bool

	m sync.Mutex
}

func newPlayer(context *Context, src io.Reader) *Player {
	p := &Player{
		p: &playerImpl{
			context: context,
			src:     src,
			volume:  1,
		},
	}
	runtime.SetFinalizer(p, (*Player).Close)
	return p
}

func (p *Player) Err() error {
	return p.p.Err()
}

func (p *playerImpl) Err() error {
	if err := p.err.Load(); err != nil {
		return err.(error)
	}
	return nil
}

func (p *Player) Play() {
	p.p.Play()
}

func (p *playerImpl) Play() {
	ch := make(chan struct{})
	go func() {
		p.m.Lock()
		defer p.m.Unlock()

		close(ch)
		p.playImpl()
	}()
	<-ch
}

func (p *playerImpl) ensureTmpBuf() []byte {
	if p.tmpbuf == nil {
		p.tmpbuf = make([]byte, p.context.bufferSize())
	}
	return p.tmpbuf
}

func (p *playerImpl) playImpl() {
	if p.err.Load() != nil {
		return
	}
	if p.state != playerPaused {
		return
	}

	if !p.eof {
		buf := p.ensureTmpBuf()
		for len(p.buf) < p.context.bufferSize() {
			n, err := p.src.Read(buf)
			if err != nil && err != io.EOF {
				p.setErrorImpl(err)
				return
			}
			p.buf = append(p.buf, buf[:n]...)
			if err == io.EOF {
				if len(p.buf) == 0 {
					p.eof = true
				}
				break
			}
		}
	}

	if !p.eof || len(p.buf) > 0 {
		p.state = playerPlay
	}

	p.m.Unlock()
	p.context.players.addPlayer(p)
	p.m.Lock()
}

func (p *Player) Pause() {
	p.p.Pause()
}

func (p *playerImpl) Pause() {
	p.m.Lock()
	defer p.m.Unlock()

	if p.state != playerPlay {
		return
	}
	p.state = playerPaused
}

func (p *Player) Reset() {
	p.p.Reset()
}

func (p *playerImpl) Reset() {
	p.m.Lock()
	defer p.m.Unlock()
	p.resetImpl()
}

func (p *playerImpl) resetImpl() {
	if p.state == playerClosed {
		return
	}
	p.state = playerPaused
	p.buf = p.buf[:0]
	p.eof = false
}

func (p *Player) IsPlaying() bool {
	return p.p.IsPlaying()
}

func (p *playerImpl) IsPlaying() bool {
	p.m.Lock()
	defer p.m.Unlock()
	return p.state == playerPlay
}

func (p *Player) Volume() float64 {
	return p.p.Volume()
}

func (p *playerImpl) Volume() float64 {
	p.m.Lock()
	defer p.m.Unlock()
	return p.volume
}

func (p *Player) SetVolume(volume float64) {
	p.p.SetVolume(volume)
}

func (p *playerImpl) SetVolume(volume float64) {
	p.m.Lock()
	defer p.m.Unlock()
	p.volume = volume
}

func (p *Player) UnplayedBufferSize() int {
	return p.p.UnplayedBufferSize()
}

func (p *playerImpl) UnplayedBufferSize() int {
	p.m.Lock()
	defer p.m.Unlock()
	return len(p.buf)
}

func (p *Player) Close() error {
	runtime.SetFinalizer(p, nil)
	return p.p.Close()
}

func (p *playerImpl) Close() error {
	p.m.Lock()
	defer p.m.Unlock()
	return p.closeImpl()
}

func (p *playerImpl) closeImpl() error {
	p.m.Unlock()
	p.context.players.removePlayer(p)
	p.m.Lock()

	if p.state == playerClosed {
		return nil
	}
	p.state = playerClosed
	p.buf = nil
	if err := p.err.Load(); err != nil {
		return err.(error)
	}
	return nil
}

func (p *playerImpl) readBufferAndAdd(buf []float32) int {
	p.m.Lock()
	defer p.m.Unlock()

	if p.state != playerPlay {
		return 0
	}

	bitDepthInBytes := p.context.bitDepthInBytes
	n := len(p.buf) / bitDepthInBytes
	if n > len(buf) {
		n = len(buf)
	}
	volume := float32(p.volume)
	src := p.buf[:n*bitDepthInBytes]

	for i := 0; i < n; i++ {
		var v float32
		switch bitDepthInBytes {
		case 1:
			v8 := src[i]
			v = float32(v8-(1<<7)) / (1 << 7)
		case 2:
			v16 := int16(src[2*i]) | (int16(src[2*i+1]) << 8)
			v = float32(v16) / (1 << 15)
		}
		buf[i] += v * volume
	}

	copy(p.buf, p.buf[n*bitDepthInBytes:])
	p.buf = p.buf[:len(p.buf)-n*bitDepthInBytes]

	return n
}

func (p *playerImpl) canReadSourceToBuffer() bool {
	p.m.Lock()
	defer p.m.Unlock()

	if p.eof {
		return false
	}
	return len(p.buf) < p.context.bufferSize()
}

func (p *playerImpl) readSourceToBuffer() {
	p.m.Lock()
	defer p.m.Unlock()

	if p.err.Load() != nil {
		return
	}
	if p.state == playerClosed {
		return
	}

	if len(p.buf) >= p.context.bufferSize() {
		return
	}

	buf := p.ensureTmpBuf()
	n, err := p.src.Read(buf)

	if err != nil && err != io.EOF {
		p.setErrorImpl(err)
		return
	}

	p.buf = append(p.buf, buf[:n]...)
	if err == io.EOF && len(p.buf) == 0 {
		p.state = playerPaused
		p.eof = true
	}
}

func (p *playerImpl) setErrorImpl(err error) {
	if p.err.Load() == nil {
		p.err.Store(err)
	}
	p.closeImpl()
}
<commit_msg>audio/internal/cbackend: bug fix: use CompareAndSwap<commit_after>// Copyright 2021 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     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.

//go:build ebitencbackend
// +build ebitencbackend

// TODO: This implementation is very similar to github.com/hajimehoshi/oto/v2's player_notjs.go
// Unify them if possible.

package cbackend

import (
	"io"
	"runtime"
	"sync"
	"sync/atomic"
)

type playerState int

const (
	playerPaused playerState = iota
	playerPlay
	playerClosed
)

type players struct {
	players map[*playerImpl]struct{}
	buf     []float32
	cond    *sync.Cond
}

func newPlayers() *players {
	p := &players{
		cond: sync.NewCond(&sync.Mutex{}),
	}
	go p.loop()
	return p
}

func (ps *players) shouldWait() bool {
	for p := range ps.players {
		if p.canReadSourceToBuffer() {
			return false
		}
	}
	return true
}

func (ps *players) wait() {
	ps.cond.L.Lock()
	defer ps.cond.L.Unlock()

	for ps.shouldWait() {
		ps.cond.Wait()
	}
}

func (ps *players) loop() {
	var players []*playerImpl
	for {
		ps.wait()

		ps.cond.L.Lock()
		players = players[:0]
		for p := range ps.players {
			players = append(players, p)
		}
		ps.cond.L.Unlock()

		for _, p := range players {
			p.readSourceToBuffer()
		}
	}
}

func (ps *players) addPlayer(player *playerImpl) {
	ps.cond.L.Lock()
	defer ps.cond.L.Unlock()

	if ps.players == nil {
		ps.players = map[*playerImpl]struct{}{}
	}
	ps.players[player] = struct{}{}
	ps.cond.Signal()
}

func (ps *players) removePlayer(player *playerImpl) {
	ps.cond.L.Lock()
	defer ps.cond.L.Unlock()

	delete(ps.players, player)
	ps.cond.Signal()
}

func (ps *players) read(buf []float32) {
	ps.cond.L.Lock()
	players := make([]*playerImpl, 0, len(ps.players))
	for p := range ps.players {
		players = append(players, p)
	}
	ps.cond.L.Unlock()

	for i := range buf {
		buf[i] = 0
	}
	for _, p := range players {
		p.readBufferAndAdd(buf)
	}
	ps.cond.Signal()
}

type Player struct {
	p *playerImpl
}

type playerImpl struct {
	context *Context
	src     io.Reader
	volume  float64
	err     atomic.Value
	state   playerState
	tmpbuf  []byte
	buf     []byte
	eof     bool

	m sync.Mutex
}

func newPlayer(context *Context, src io.Reader) *Player {
	p := &Player{
		p: &playerImpl{
			context: context,
			src:     src,
			volume:  1,
		},
	}
	runtime.SetFinalizer(p, (*Player).Close)
	return p
}

func (p *Player) Err() error {
	return p.p.Err()
}

func (p *playerImpl) Err() error {
	if err := p.err.Load(); err != nil {
		return err.(error)
	}
	return nil
}

func (p *Player) Play() {
	p.p.Play()
}

func (p *playerImpl) Play() {
	ch := make(chan struct{})
	go func() {
		p.m.Lock()
		defer p.m.Unlock()

		close(ch)
		p.playImpl()
	}()
	<-ch
}

func (p *playerImpl) ensureTmpBuf() []byte {
	if p.tmpbuf == nil {
		p.tmpbuf = make([]byte, p.context.bufferSize())
	}
	return p.tmpbuf
}

func (p *playerImpl) playImpl() {
	if p.err.Load() != nil {
		return
	}
	if p.state != playerPaused {
		return
	}

	if !p.eof {
		buf := p.ensureTmpBuf()
		for len(p.buf) < p.context.bufferSize() {
			n, err := p.src.Read(buf)
			if err != nil && err != io.EOF {
				p.setErrorImpl(err)
				return
			}
			p.buf = append(p.buf, buf[:n]...)
			if err == io.EOF {
				if len(p.buf) == 0 {
					p.eof = true
				}
				break
			}
		}
	}

	if !p.eof || len(p.buf) > 0 {
		p.state = playerPlay
	}

	p.m.Unlock()
	p.context.players.addPlayer(p)
	p.m.Lock()
}

func (p *Player) Pause() {
	p.p.Pause()
}

func (p *playerImpl) Pause() {
	p.m.Lock()
	defer p.m.Unlock()

	if p.state != playerPlay {
		return
	}
	p.state = playerPaused
}

func (p *Player) Reset() {
	p.p.Reset()
}

func (p *playerImpl) Reset() {
	p.m.Lock()
	defer p.m.Unlock()
	p.resetImpl()
}

func (p *playerImpl) resetImpl() {
	if p.state == playerClosed {
		return
	}
	p.state = playerPaused
	p.buf = p.buf[:0]
	p.eof = false
}

func (p *Player) IsPlaying() bool {
	return p.p.IsPlaying()
}

func (p *playerImpl) IsPlaying() bool {
	p.m.Lock()
	defer p.m.Unlock()
	return p.state == playerPlay
}

func (p *Player) Volume() float64 {
	return p.p.Volume()
}

func (p *playerImpl) Volume() float64 {
	p.m.Lock()
	defer p.m.Unlock()
	return p.volume
}

func (p *Player) SetVolume(volume float64) {
	p.p.SetVolume(volume)
}

func (p *playerImpl) SetVolume(volume float64) {
	p.m.Lock()
	defer p.m.Unlock()
	p.volume = volume
}

func (p *Player) UnplayedBufferSize() int {
	return p.p.UnplayedBufferSize()
}

func (p *playerImpl) UnplayedBufferSize() int {
	p.m.Lock()
	defer p.m.Unlock()
	return len(p.buf)
}

func (p *Player) Close() error {
	runtime.SetFinalizer(p, nil)
	return p.p.Close()
}

func (p *playerImpl) Close() error {
	p.m.Lock()
	defer p.m.Unlock()
	return p.closeImpl()
}

func (p *playerImpl) closeImpl() error {
	p.m.Unlock()
	p.context.players.removePlayer(p)
	p.m.Lock()

	if p.state == playerClosed {
		return nil
	}
	p.state = playerClosed
	p.buf = nil
	if err := p.err.Load(); err != nil {
		return err.(error)
	}
	return nil
}

func (p *playerImpl) readBufferAndAdd(buf []float32) int {
	p.m.Lock()
	defer p.m.Unlock()

	if p.state != playerPlay {
		return 0
	}

	bitDepthInBytes := p.context.bitDepthInBytes
	n := len(p.buf) / bitDepthInBytes
	if n > len(buf) {
		n = len(buf)
	}
	volume := float32(p.volume)
	src := p.buf[:n*bitDepthInBytes]

	for i := 0; i < n; i++ {
		var v float32
		switch bitDepthInBytes {
		case 1:
			v8 := src[i]
			v = float32(v8-(1<<7)) / (1 << 7)
		case 2:
			v16 := int16(src[2*i]) | (int16(src[2*i+1]) << 8)
			v = float32(v16) / (1 << 15)
		}
		buf[i] += v * volume
	}

	copy(p.buf, p.buf[n*bitDepthInBytes:])
	p.buf = p.buf[:len(p.buf)-n*bitDepthInBytes]

	return n
}

func (p *playerImpl) canReadSourceToBuffer() bool {
	p.m.Lock()
	defer p.m.Unlock()

	if p.eof {
		return false
	}
	return len(p.buf) < p.context.bufferSize()
}

func (p *playerImpl) readSourceToBuffer() {
	p.m.Lock()
	defer p.m.Unlock()

	if p.err.Load() != nil {
		return
	}
	if p.state == playerClosed {
		return
	}

	if len(p.buf) >= p.context.bufferSize() {
		return
	}

	buf := p.ensureTmpBuf()
	n, err := p.src.Read(buf)

	if err != nil && err != io.EOF {
		p.setErrorImpl(err)
		return
	}

	p.buf = append(p.buf, buf[:n]...)
	if err == io.EOF && len(p.buf) == 0 {
		p.state = playerPaused
		p.eof = true
	}
}

func (p *playerImpl) setErrorImpl(err error) {
	p.err.CompareAndSwap(nil, err)
	p.closeImpl()
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/arn"
	"github.com/aws/aws-sdk-go/service/ssm"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/helper/validation"
)

func resourceAwsSsmParameter() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsSsmParameterPut,
		Read:   resourceAwsSsmParameterRead,
		Update: resourceAwsSsmParameterPut,
		Delete: resourceAwsSsmParameterDelete,
		Exists: resourceAwsSmmParameterExists,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"description": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"type": {
				Type:     schema.TypeString,
				Required: true,
				ValidateFunc: validation.StringInSlice([]string{
					ssm.ParameterTypeString,
					ssm.ParameterTypeStringList,
					ssm.ParameterTypeSecureString,
				}, false),
			},
			"value": {
				Type:      schema.TypeString,
				Required:  true,
				Sensitive: true,
			},
			"arn": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"key_id": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"overwrite": {
				Type:     schema.TypeBool,
				Optional: true,
			},
			"allowed_pattern": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"tags": tagsSchema(),
		},
	}
}

func resourceAwsSmmParameterExists(d *schema.ResourceData, meta interface{}) (bool, error) {
	ssmconn := meta.(*AWSClient).ssmconn

	resp, err := ssmconn.GetParameters(&ssm.GetParametersInput{
		Names:          []*string{aws.String(d.Id())},
		WithDecryption: aws.Bool(true),
	})

	if err != nil {
		return false, err
	}
	return len(resp.InvalidParameters) == 0, nil
}

func resourceAwsSsmParameterRead(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[DEBUG] Reading SSM Parameter: %s", d.Id())

	resp, err := ssmconn.GetParameters(&ssm.GetParametersInput{
		Names:          []*string{aws.String(d.Id())},
		WithDecryption: aws.Bool(true),
	})
	if err != nil {
		return fmt.Errorf("error getting SSM parameter: %s", err)
	}
	if len(resp.Parameters) == 0 {
		log.Printf("[WARN] SSM Param %q not found, removing from state", d.Id())
		d.SetId("")
		return nil
	}

	param := resp.Parameters[0]
	d.Set("name", param.Name)
	d.Set("type", param.Type)
	d.Set("value", param.Value)

	describeParamsInput := &ssm.DescribeParametersInput{
		Filters: []*ssm.ParametersFilter{
			&ssm.ParametersFilter{
				Key:    aws.String("Name"),
				Values: []*string{aws.String(d.Get("name").(string))},
			},
		},
	}
	detailedParameters := []*ssm.ParameterMetadata{}
	err = ssmconn.DescribeParametersPages(describeParamsInput,
		func(page *ssm.DescribeParametersOutput, lastPage bool) bool {
			detailedParameters = append(detailedParameters, page.Parameters...)
			return !lastPage
		})
	if err != nil {
		return fmt.Errorf("error describing SSM parameter: %s", err)
	}
	if len(detailedParameters) == 0 {
		log.Printf("[WARN] SSM Param %q not found, removing from state", d.Id())
		d.SetId("")
		return nil
	}

	detail := detailedParameters[0]
	d.Set("key_id", detail.KeyId)
	d.Set("description", detail.Description)
	d.Set("allowed_pattern", detail.AllowedPattern)

	if tagList, err := ssmconn.ListTagsForResource(&ssm.ListTagsForResourceInput{
		ResourceId:   aws.String(d.Get("name").(string)),
		ResourceType: aws.String("Parameter"),
	}); err != nil {
		return fmt.Errorf("Failed to get SSM parameter tags for %s: %s", d.Get("name"), err)
	} else {
		d.Set("tags", tagsToMapSSM(tagList.TagList))
	}

	arn := arn.ARN{
		Partition: meta.(*AWSClient).partition,
		Region:    meta.(*AWSClient).region,
		Service:   "ssm",
		AccountID: meta.(*AWSClient).accountid,
		Resource:  fmt.Sprintf("parameter/%s", strings.TrimPrefix(d.Id(), "/")),
	}
	d.Set("arn", arn.String())

	return nil
}

func resourceAwsSsmParameterDelete(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[INFO] Deleting SSM Parameter: %s", d.Id())

	_, err := ssmconn.DeleteParameter(&ssm.DeleteParameterInput{
		Name: aws.String(d.Get("name").(string)),
	})
	if err != nil {
		return err
	}

	return nil
}

func resourceAwsSsmParameterPut(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[INFO] Creating SSM Parameter: %s", d.Get("name").(string))

	paramInput := &ssm.PutParameterInput{
		Name:           aws.String(d.Get("name").(string)),
		Type:           aws.String(d.Get("type").(string)),
		Value:          aws.String(d.Get("value").(string)),
		Overwrite:      aws.Bool(shouldUpdateSsmParameter(d)),
		AllowedPattern: aws.String(d.Get("allowed_pattern").(string)),
	}

	if d.HasChange("description") {
		_, n := d.GetChange("description")
		paramInput.Description = aws.String(n.(string))
	}

	if keyID, ok := d.GetOk("key_id"); ok {
		log.Printf("[DEBUG] Setting key_id for SSM Parameter %v: %s", d.Get("name"), keyID)
		paramInput.SetKeyId(keyID.(string))
	}

	log.Printf("[DEBUG] Waiting for SSM Parameter %v to be updated", d.Get("name"))
	if _, err := ssmconn.PutParameter(paramInput); err != nil {
		return fmt.Errorf("error creating SSM parameter: %s", err)
	}

	if err := setTagsSSM(ssmconn, d, d.Get("name").(string), "Parameter"); err != nil {
		return fmt.Errorf("error creating SSM parameter tags: %s", err)
	}

	d.SetId(d.Get("name").(string))

	return resourceAwsSsmParameterRead(d, meta)
}

func shouldUpdateSsmParameter(d *schema.ResourceData) bool {
	// If the user has specified a preference, return their preference
	if value, ok := d.GetOkExists("overwrite"); ok {
		return value.(bool)
	}

	// Since the user has not specified a preference, obey lifecycle rules
	// if it is not a new resource, otherwise overwrite should be set to false.
	return !d.IsNewResource()
}
<commit_msg>Use GetParameter instead of GetParameters in SSM parameter resource<commit_after>package aws

import (
	"fmt"
	"log"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/arn"
	"github.com/aws/aws-sdk-go/service/ssm"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/helper/validation"
)

func resourceAwsSsmParameter() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsSsmParameterPut,
		Read:   resourceAwsSsmParameterRead,
		Update: resourceAwsSsmParameterPut,
		Delete: resourceAwsSsmParameterDelete,
		Exists: resourceAwsSmmParameterExists,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"description": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"type": {
				Type:     schema.TypeString,
				Required: true,
				ValidateFunc: validation.StringInSlice([]string{
					ssm.ParameterTypeString,
					ssm.ParameterTypeStringList,
					ssm.ParameterTypeSecureString,
				}, false),
			},
			"value": {
				Type:      schema.TypeString,
				Required:  true,
				Sensitive: true,
			},
			"arn": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"key_id": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"overwrite": {
				Type:     schema.TypeBool,
				Optional: true,
			},
			"allowed_pattern": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"tags": tagsSchema(),
		},
	}
}

func resourceAwsSmmParameterExists(d *schema.ResourceData, meta interface{}) (bool, error) {
	ssmconn := meta.(*AWSClient).ssmconn
	_, err := ssmconn.GetParameter(&ssm.GetParameterInput{
		Name:           aws.String(d.Id()),
		WithDecryption: aws.Bool(false),
	})
	if err != nil {
		if isAWSErr(err, ssm.ErrCodeParameterNotFound, "") {
			return false, nil
		}
		return false, err
	}

	return true, nil
}

func resourceAwsSsmParameterRead(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[DEBUG] Reading SSM Parameter: %s", d.Id())

	resp, err := ssmconn.GetParameter(&ssm.GetParameterInput{
		Name:           aws.String(d.Id()),
		WithDecryption: aws.Bool(true),
	})
	if err != nil {
		return fmt.Errorf("error getting SSM parameter: %s", err)
	}

	param := resp.Parameter
	d.Set("name", param.Name)
	d.Set("type", param.Type)
	d.Set("value", param.Value)

	describeParamsInput := &ssm.DescribeParametersInput{
		Filters: []*ssm.ParametersFilter{
			&ssm.ParametersFilter{
				Key:    aws.String("Name"),
				Values: []*string{aws.String(d.Get("name").(string))},
			},
		},
		MaxResults: aws.Int64(50),
	}
	detailedParameters := []*ssm.ParameterMetadata{}
	err = ssmconn.DescribeParametersPages(describeParamsInput,
		func(page *ssm.DescribeParametersOutput, lastPage bool) bool {
			detailedParameters = append(detailedParameters, page.Parameters...)
			return !lastPage
		})
	if err != nil {
		return fmt.Errorf("error describing SSM parameter: %s", err)
	}
	if len(detailedParameters) == 0 {
		log.Printf("[WARN] SSM Param %q not found, removing from state", d.Id())
		d.SetId("")
		return nil
	}

	detail := detailedParameters[0]
	d.Set("key_id", detail.KeyId)
	d.Set("description", detail.Description)
	d.Set("allowed_pattern", detail.AllowedPattern)

	if tagList, err := ssmconn.ListTagsForResource(&ssm.ListTagsForResourceInput{
		ResourceId:   aws.String(d.Get("name").(string)),
		ResourceType: aws.String("Parameter"),
	}); err != nil {
		return fmt.Errorf("Failed to get SSM parameter tags for %s: %s", d.Get("name"), err)
	} else {
		d.Set("tags", tagsToMapSSM(tagList.TagList))
	}

	arn := arn.ARN{
		Partition: meta.(*AWSClient).partition,
		Region:    meta.(*AWSClient).region,
		Service:   "ssm",
		AccountID: meta.(*AWSClient).accountid,
		Resource:  fmt.Sprintf("parameter/%s", strings.TrimPrefix(d.Id(), "/")),
	}
	d.Set("arn", arn.String())

	return nil
}

func resourceAwsSsmParameterDelete(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[INFO] Deleting SSM Parameter: %s", d.Id())

	_, err := ssmconn.DeleteParameter(&ssm.DeleteParameterInput{
		Name: aws.String(d.Get("name").(string)),
	})
	if err != nil {
		return err
	}

	return nil
}

func resourceAwsSsmParameterPut(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[INFO] Creating SSM Parameter: %s", d.Get("name").(string))

	paramInput := &ssm.PutParameterInput{
		Name:           aws.String(d.Get("name").(string)),
		Type:           aws.String(d.Get("type").(string)),
		Value:          aws.String(d.Get("value").(string)),
		Overwrite:      aws.Bool(shouldUpdateSsmParameter(d)),
		AllowedPattern: aws.String(d.Get("allowed_pattern").(string)),
	}

	if d.HasChange("description") {
		_, n := d.GetChange("description")
		paramInput.Description = aws.String(n.(string))
	}

	if keyID, ok := d.GetOk("key_id"); ok {
		log.Printf("[DEBUG] Setting key_id for SSM Parameter %v: %s", d.Get("name"), keyID)
		paramInput.SetKeyId(keyID.(string))
	}

	log.Printf("[DEBUG] Waiting for SSM Parameter %v to be updated", d.Get("name"))
	if _, err := ssmconn.PutParameter(paramInput); err != nil {
		return fmt.Errorf("error creating SSM parameter: %s", err)
	}

	if err := setTagsSSM(ssmconn, d, d.Get("name").(string), "Parameter"); err != nil {
		return fmt.Errorf("error creating SSM parameter tags: %s", err)
	}

	d.SetId(d.Get("name").(string))

	return resourceAwsSsmParameterRead(d, meta)
}

func shouldUpdateSsmParameter(d *schema.ResourceData) bool {
	// If the user has specified a preference, return their preference
	if value, ok := d.GetOkExists("overwrite"); ok {
		return value.(bool)
	}

	// Since the user has not specified a preference, obey lifecycle rules
	// if it is not a new resource, otherwise overwrite should be set to false.
	return !d.IsNewResource()
}
<|endoftext|>


<commit_before>package vegeta

import (
	"strconv"
	"time"

	"github.com/streadway/quantile"
)

type (
	// Metrics holds metrics computed out of a slice of Results which are used
	// in some of the Reporters
	Metrics struct {
		// Latencies holds computed request latency metrics.
		Latencies LatencyMetrics `json:"latencies"`
		// BytesIn holds computed incoming byte metrics.
		BytesIn ByteMetrics `json:"bytes_in"`
		// BytesOut holds computed outgoing byte metrics.
		BytesOut ByteMetrics `json:"bytes_out"`
		// First is the earliest timestamp in a Result set.
		Earliest time.Time `json:"earliest"`
		// Latest is the latest timestamp in a Result set.
		Latest time.Time `json:"latest"`
		// End is the latest timestamp in a Result set plus its latency.
		End time.Time `json:"end"`
		// Duration is the duration of the attack.
		Duration time.Duration `json:"duration"`
		// Wait is the extra time waiting for responses from targets.
		Wait time.Duration `json:"wait"`
		// Requests is the total number of requests executed.
		Requests uint64 `json:"requests"`
		// Rate is the rate of requests per second.
		Rate float64 `json:"rate"`
		// Success is the percentage of non-error responses.
		Success float64 `json:"success"`
		// StatusCodes is a histogram of the responses' status codes.
		StatusCodes map[string]int `json:"status_codes"`
		// Errors is a set of unique errors returned by the targets during the attack.
		Errors []string `json:"errors"`

		errors    map[string]struct{}
		success   uint64
		latencies *quantile.Estimator
	}

	// LatencyMetrics holds computed request latency metrics.
	LatencyMetrics struct {
		// Total is the total latency sum of all requests in an attack.
		Total time.Duration `json:"total"`
		// Mean is the mean request latency.
		Mean time.Duration `json:"mean"`
		// P50 is the 50th percentile request latency.
		P50 time.Duration `json:"50th"`
		// P95 is the 95th percentile request latency.
		P95 time.Duration `json:"95th"`
		// P99 is the 99th percentile request latency.
		P99 time.Duration `json:"99th"`
		// Max is the maximum observed request latency.
		Max time.Duration `json:"max"`
	}

	// ByteMetrics holds computed byte flow metrics.
	ByteMetrics struct {
		// Total is the total number of flowing bytes in an attack.
		Total uint64 `json:"total"`
		// Mean is the mean number of flowing bytes per hit.
		Mean float64 `json:"mean"`
	}
)

// Add implements the Add method of the Report interface by adding the given
// Result to Metrics.
func (m *Metrics) Add(r *Result) {
	if m.StatusCodes == nil {
		m.StatusCodes = map[string]int{}
	}

	if m.errors == nil {
		m.Errors = []string{}
		m.errors = map[string]struct{}{}
	}

	if m.latencies == nil {
		m.latencies = quantile.New(
			quantile.Known(0.50, 0.01),
			quantile.Known(0.95, 0.001),
			quantile.Known(0.99, 0.0005),
		)
	}

	m.Requests++
	m.StatusCodes[strconv.Itoa(int(r.Code))]++
	m.Latencies.Total += r.Latency
	m.BytesOut.Total += r.BytesOut
	m.BytesIn.Total += r.BytesIn

	m.latencies.Add(float64(r.Latency))

	if m.Earliest.IsZero() || m.Earliest.After(r.Timestamp) {
		m.Earliest = r.Timestamp
	}

	if r.Timestamp.After(m.Latest) {
		m.Latest = r.Timestamp
	}

	if end := r.End(); end.After(m.End) {
		m.End = end
	}

	if r.Latency > m.Latencies.Max {
		m.Latencies.Max = r.Latency
	}

	if r.Code >= 200 && r.Code < 400 {
		m.success++
	}

	if r.Error != "" {
		if _, ok := m.errors[r.Error]; !ok {
			m.errors[r.Error] = struct{}{}
			m.Errors = append(m.Errors, r.Error)
		}
	}
}

// Close implements the Close method of the Report interface by computing
// derived summary metrics which don't need to be run on every Add call.
func (m *Metrics) Close() {
	m.Duration = m.Latest.Sub(m.Earliest)
	m.Rate = float64(m.Requests) / m.Duration.Seconds()
	m.Wait = m.End.Sub(m.Latest)
	m.BytesIn.Mean = float64(m.BytesIn.Total) / float64(m.Requests)
	m.BytesOut.Mean = float64(m.BytesOut.Total) / float64(m.Requests)
	m.Success = float64(m.success) / float64(m.Requests)
	m.Latencies.Mean = time.Duration(float64(m.Latencies.Total) / float64(m.Requests))
	m.Latencies.P50 = time.Duration(m.latencies.Get(0.50))
	m.Latencies.P95 = time.Duration(m.latencies.Get(0.95))
	m.Latencies.P99 = time.Duration(m.latencies.Get(0.99))
}
<commit_msg>metrics: Lazy init on Close method when required<commit_after>package vegeta

import (
	"strconv"
	"time"

	"github.com/streadway/quantile"
)

type (
	// Metrics holds metrics computed out of a slice of Results which are used
	// in some of the Reporters
	Metrics struct {
		// Latencies holds computed request latency metrics.
		Latencies LatencyMetrics `json:"latencies"`
		// BytesIn holds computed incoming byte metrics.
		BytesIn ByteMetrics `json:"bytes_in"`
		// BytesOut holds computed outgoing byte metrics.
		BytesOut ByteMetrics `json:"bytes_out"`
		// First is the earliest timestamp in a Result set.
		Earliest time.Time `json:"earliest"`
		// Latest is the latest timestamp in a Result set.
		Latest time.Time `json:"latest"`
		// End is the latest timestamp in a Result set plus its latency.
		End time.Time `json:"end"`
		// Duration is the duration of the attack.
		Duration time.Duration `json:"duration"`
		// Wait is the extra time waiting for responses from targets.
		Wait time.Duration `json:"wait"`
		// Requests is the total number of requests executed.
		Requests uint64 `json:"requests"`
		// Rate is the rate of requests per second.
		Rate float64 `json:"rate"`
		// Success is the percentage of non-error responses.
		Success float64 `json:"success"`
		// StatusCodes is a histogram of the responses' status codes.
		StatusCodes map[string]int `json:"status_codes"`
		// Errors is a set of unique errors returned by the targets during the attack.
		Errors []string `json:"errors"`

		errors    map[string]struct{}
		success   uint64
		latencies *quantile.Estimator
	}

	// LatencyMetrics holds computed request latency metrics.
	LatencyMetrics struct {
		// Total is the total latency sum of all requests in an attack.
		Total time.Duration `json:"total"`
		// Mean is the mean request latency.
		Mean time.Duration `json:"mean"`
		// P50 is the 50th percentile request latency.
		P50 time.Duration `json:"50th"`
		// P95 is the 95th percentile request latency.
		P95 time.Duration `json:"95th"`
		// P99 is the 99th percentile request latency.
		P99 time.Duration `json:"99th"`
		// Max is the maximum observed request latency.
		Max time.Duration `json:"max"`
	}

	// ByteMetrics holds computed byte flow metrics.
	ByteMetrics struct {
		// Total is the total number of flowing bytes in an attack.
		Total uint64 `json:"total"`
		// Mean is the mean number of flowing bytes per hit.
		Mean float64 `json:"mean"`
	}
)

// Add implements the Add method of the Report interface by adding the given
// Result to Metrics.
func (m *Metrics) Add(r *Result) {
	m.init()

	m.Requests++
	m.StatusCodes[strconv.Itoa(int(r.Code))]++
	m.Latencies.Total += r.Latency
	m.BytesOut.Total += r.BytesOut
	m.BytesIn.Total += r.BytesIn

	m.latencies.Add(float64(r.Latency))

	if m.Earliest.IsZero() || m.Earliest.After(r.Timestamp) {
		m.Earliest = r.Timestamp
	}

	if r.Timestamp.After(m.Latest) {
		m.Latest = r.Timestamp
	}

	if end := r.End(); end.After(m.End) {
		m.End = end
	}

	if r.Latency > m.Latencies.Max {
		m.Latencies.Max = r.Latency
	}

	if r.Code >= 200 && r.Code < 400 {
		m.success++
	}

	if r.Error != "" {
		if _, ok := m.errors[r.Error]; !ok {
			m.errors[r.Error] = struct{}{}
			m.Errors = append(m.Errors, r.Error)
		}
	}
}

// Close implements the Close method of the Report interface by computing
// derived summary metrics which don't need to be run on every Add call.
func (m *Metrics) Close() {
	m.init()
	m.Duration = m.Latest.Sub(m.Earliest)
	m.Rate = float64(m.Requests) / m.Duration.Seconds()
	m.Wait = m.End.Sub(m.Latest)
	m.BytesIn.Mean = float64(m.BytesIn.Total) / float64(m.Requests)
	m.BytesOut.Mean = float64(m.BytesOut.Total) / float64(m.Requests)
	m.Success = float64(m.success) / float64(m.Requests)
	m.Latencies.Mean = time.Duration(float64(m.Latencies.Total) / float64(m.Requests))
	m.Latencies.P50 = time.Duration(m.latencies.Get(0.50))
	m.Latencies.P95 = time.Duration(m.latencies.Get(0.95))
	m.Latencies.P99 = time.Duration(m.latencies.Get(0.99))
}

func (m *Metrics) init() {
	if m.StatusCodes == nil {
		m.StatusCodes = map[string]int{}
	}

	if m.errors == nil {
		m.errors = map[string]struct{}{}
	}

	if m.latencies == nil {
		m.latencies = quantile.New(
			quantile.Known(0.50, 0.01),
			quantile.Known(0.95, 0.001),
			quantile.Known(0.99, 0.0005),
		)
	}
}
<|endoftext|>


<commit_before>// Copyright (C) 2015-2016 Nippon Telegraph and Telephone Corporation.
//
// 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 server

import (
	"fmt"
	"github.com/osrg/gobgp/config"
	"github.com/osrg/gobgp/packet/bgp"
	"github.com/osrg/gobgp/packet/bmp"
	"github.com/osrg/gobgp/table"
	log "github.com/sirupsen/logrus"
	"net"
	"strconv"
	"time"
)

type ribout map[string][]*table.Path

func newribout() ribout {
	return make(map[string][]*table.Path)
}

// return true if we need to send the path to the BMP server
func (r ribout) update(p *table.Path) bool {
	key := p.GetNlri().String() // TODO expose (*Path).getPrefix()
	l := r[key]
	if p.IsWithdraw {
		if len(l) == 0 {
			return false
		}
		n := make([]*table.Path, 0, len(l))
		for _, q := range l {
			if p.GetSource() == q.GetSource() {
				continue
			}
			n = append(n, q)
		}
		if len(n) == 0 {
			delete(r, key)
		} else {
			r[key] = n
		}
		return true
	}

	if len(l) == 0 {
		r[key] = []*table.Path{p}
		return true
	}

	doAppend := true
	for idx, q := range l {
		if p.GetSource() == q.GetSource() {
			// if we have sent the same path, don't send it again
			if p.Equal(q) {
				return false
			}
			l[idx] = p
			doAppend = false
		}
	}
	if doAppend {
		r[key] = append(r[key], p)
	}
	return true
}

func (b *bmpClient) tryConnect() *net.TCPConn {
	interval := 1
	for {
		log.WithFields(log.Fields{"Topic": "bmp"}).Debugf("Connecting BMP server:%s", b.host)
		conn, err := net.Dial("tcp", b.host)
		if err != nil {
			select {
			case <-b.dead:
				return nil
			default:
			}
			time.Sleep(time.Duration(interval) * time.Second)
			if interval < 30 {
				interval *= 2
			}
		} else {
			log.WithFields(log.Fields{"Topic": "bmp"}).Infof("BMP server is connected:%s", b.host)
			return conn.(*net.TCPConn)
		}
	}
}

func (b *bmpClient) Stop() {
	close(b.dead)
}

func (b *bmpClient) loop() {
	for {
		conn := b.tryConnect()
		if conn == nil {
			break
		}

		if func() bool {
			ops := []WatchOption{WatchPeerState(true)}
			if b.c.RouteMonitoringPolicy == config.BMP_ROUTE_MONITORING_POLICY_TYPE_BOTH {
				log.WithFields(
					log.Fields{"Topic": "bmp"},
				).Warn("both option for route-monitoring-policy is obsoleted")
			}
			if b.c.RouteMonitoringPolicy == config.BMP_ROUTE_MONITORING_POLICY_TYPE_PRE_POLICY || b.c.RouteMonitoringPolicy == config.BMP_ROUTE_MONITORING_POLICY_TYPE_ALL {
				ops = append(ops, WatchUpdate(true))
			}
			if b.c.RouteMonitoringPolicy == config.BMP_ROUTE_MONITORING_POLICY_TYPE_POST_POLICY || b.c.RouteMonitoringPolicy == config.BMP_ROUTE_MONITORING_POLICY_TYPE_ALL {
				ops = append(ops, WatchPostUpdate(true))
			}
			if b.c.RouteMonitoringPolicy == config.BMP_ROUTE_MONITORING_POLICY_TYPE_LOCAL_RIB || b.c.RouteMonitoringPolicy == config.BMP_ROUTE_MONITORING_POLICY_TYPE_ALL {
				ops = append(ops, WatchBestPath(true))
			}
			if b.c.RouteMirroringEnabled {
				ops = append(ops, WatchMessage(false))
			}
			w := b.s.Watch(ops...)
			defer w.Stop()

			var tickerCh <-chan time.Time
			if b.c.StatisticsTimeout == 0 {
				log.WithFields(log.Fields{"Topic": "bmp"}).Debug("statistics reports disabled")
			} else {
				t := time.NewTicker(time.Duration(b.c.StatisticsTimeout) * time.Second)
				defer t.Stop()
				tickerCh = t.C
			}

			write := func(msg *bmp.BMPMessage) error {
				buf, _ := msg.Serialize()
				_, err := conn.Write(buf)
				if err != nil {
					log.Warnf("failed to write to bmp server %s", b.host)
				}
				return err
			}

			if err := write(bmp.NewBMPInitiation([]bmp.BMPInfoTLVInterface{})); err != nil {
				return false
			}

			for {
				select {
				case ev := <-w.Event():
					switch msg := ev.(type) {
					case *WatchEventUpdate:
						info := &table.PeerInfo{
							Address: msg.PeerAddress,
							AS:      msg.PeerAS,
							ID:      msg.PeerID,
						}
						if msg.Payload == nil {
							pathList := make([]*table.Path, 0, len(msg.PathList))
							for _, p := range msg.PathList {
								if b.ribout.update(p) {
									pathList = append(pathList, p)
								}
							}
							for _, u := range table.CreateUpdateMsgFromPaths(pathList) {
								payload, _ := u.Serialize()
								if err := write(bmpPeerRoute(bmp.BMP_PEER_TYPE_GLOBAL, msg.PostPolicy, 0, info, msg.Timestamp.Unix(), payload)); err != nil {
									return false
								}
							}
						} else {
							if err := write(bmpPeerRoute(bmp.BMP_PEER_TYPE_GLOBAL, msg.PostPolicy, 0, info, msg.Timestamp.Unix(), msg.Payload)); err != nil {
								return false
							}
						}
					case *WatchEventBestPath:
						info := &table.PeerInfo{
							Address: net.ParseIP("0.0.0.0").To4(),
							AS:      b.s.bgpConfig.Global.Config.As,
							ID:      net.ParseIP(b.s.bgpConfig.Global.Config.RouterId).To4(),
						}
						for _, p := range msg.PathList {
							u := table.CreateUpdateMsgFromPaths([]*table.Path{p})[0]
							if payload, err := u.Serialize(); err != nil {
								return false
							} else if err = write(bmpPeerRoute(bmp.BMP_PEER_TYPE_LOCAL_RIB, false, 0, info, p.GetTimestamp().Unix(), payload)); err != nil {
								return false
							}
						}
					case *WatchEventPeerState:
						info := &table.PeerInfo{
							Address: msg.PeerAddress,
							AS:      msg.PeerAS,
							ID:      msg.PeerID,
						}
						if msg.State == bgp.BGP_FSM_ESTABLISHED {
							if err := write(bmpPeerUp(msg.LocalAddress.String(), msg.LocalPort, msg.PeerPort, msg.SentOpen, msg.RecvOpen, bmp.BMP_PEER_TYPE_GLOBAL, false, 0, info, msg.Timestamp.Unix())); err != nil {
								return false
							}
						} else {
							if err := write(bmpPeerDown(bmp.BMP_PEER_DOWN_REASON_UNKNOWN, bmp.BMP_PEER_TYPE_GLOBAL, false, 0, info, msg.Timestamp.Unix())); err != nil {
								return false
							}
						}
					case *WatchEventMessage:
						info := &table.PeerInfo{
							Address: msg.PeerAddress,
							AS:      msg.PeerAS,
							ID:      msg.PeerID,
						}
						if err := write(bmpPeerRouteMirroring(bmp.BMP_PEER_TYPE_GLOBAL, 0, info, msg.Timestamp.Unix(), msg.Message)); err != nil {
							return false
						}
					}
				case <-tickerCh:
					neighborList := b.s.GetNeighbor("", true)
					for _, n := range neighborList {
						if n.State.SessionState != config.SESSION_STATE_ESTABLISHED {
							continue
						}
						if err := write(bmpPeerStats(bmp.BMP_PEER_TYPE_GLOBAL, 0, 0, n)); err != nil {
							return false
						}
					}
				case <-b.dead:
					term := bmp.NewBMPTermination([]bmp.BMPTermTLVInterface{
						bmp.NewBMPTermTLV16(bmp.BMP_TERM_TLV_TYPE_REASON, bmp.BMP_TERM_REASON_PERMANENTLY_ADMIN),
					})
					if err := write(term); err != nil {
						return false
					}
					conn.Close()
					return true
				}
			}
		}() {
			return
		}
	}
}

type bmpClient struct {
	s      *BgpServer
	dead   chan struct{}
	host   string
	c      *config.BmpServerConfig
	ribout ribout
}

func bmpPeerUp(laddr string, lport, rport uint16, sent, recv *bgp.BGPMessage, t uint8, policy bool, pd uint64, peeri *table.PeerInfo, timestamp int64) *bmp.BMPMessage {
	var flags uint8 = 0
	if policy {
		flags |= bmp.BMP_PEER_FLAG_POST_POLICY
	}
	ph := bmp.NewBMPPeerHeader(t, flags, pd, peeri.Address.String(), peeri.AS, peeri.ID.String(), float64(timestamp))
	return bmp.NewBMPPeerUpNotification(*ph, laddr, lport, rport, sent, recv)
}

func bmpPeerDown(reason uint8, t uint8, policy bool, pd uint64, peeri *table.PeerInfo, timestamp int64) *bmp.BMPMessage {
	var flags uint8 = 0
	if policy {
		flags |= bmp.BMP_PEER_FLAG_POST_POLICY
	}
	ph := bmp.NewBMPPeerHeader(t, flags, pd, peeri.Address.String(), peeri.AS, peeri.ID.String(), float64(timestamp))
	return bmp.NewBMPPeerDownNotification(*ph, reason, nil, []byte{})
}

func bmpPeerRoute(t uint8, policy bool, pd uint64, peeri *table.PeerInfo, timestamp int64, payload []byte) *bmp.BMPMessage {
	var flags uint8 = 0
	if policy {
		flags |= bmp.BMP_PEER_FLAG_POST_POLICY
	}
	ph := bmp.NewBMPPeerHeader(t, flags, pd, peeri.Address.String(), peeri.AS, peeri.ID.String(), float64(timestamp))
	m := bmp.NewBMPRouteMonitoring(*ph, nil)
	body := m.Body.(*bmp.BMPRouteMonitoring)
	body.BGPUpdatePayload = payload
	return m
}

func bmpPeerStats(peerType uint8, peerDist uint64, timestamp int64, neighConf *config.Neighbor) *bmp.BMPMessage {
	var peerFlags uint8 = 0
	ph := bmp.NewBMPPeerHeader(peerType, peerFlags, peerDist, neighConf.State.NeighborAddress, neighConf.State.PeerAs, neighConf.State.RemoteRouterId, float64(timestamp))
	return bmp.NewBMPStatisticsReport(
		*ph,
		[]bmp.BMPStatsTLVInterface{
			bmp.NewBMPStatsTLV64(bmp.BMP_STAT_TYPE_ADJ_RIB_IN, uint64(neighConf.State.AdjTable.Accepted)),
			bmp.NewBMPStatsTLV64(bmp.BMP_STAT_TYPE_LOC_RIB, uint64(neighConf.State.AdjTable.Advertised+neighConf.State.AdjTable.Filtered)),
			bmp.NewBMPStatsTLV32(bmp.BMP_STAT_TYPE_WITHDRAW_UPDATE, neighConf.State.Messages.Received.WithdrawUpdate),
			bmp.NewBMPStatsTLV32(bmp.BMP_STAT_TYPE_WITHDRAW_PREFIX, neighConf.State.Messages.Received.WithdrawPrefix),
		},
	)
}

func bmpPeerRouteMirroring(peerType uint8, peerDist uint64, peerInfo *table.PeerInfo, timestamp int64, msg *bgp.BGPMessage) *bmp.BMPMessage {
	var peerFlags uint8 = 0
	ph := bmp.NewBMPPeerHeader(peerType, peerFlags, peerDist, peerInfo.Address.String(), peerInfo.AS, peerInfo.ID.String(), float64(timestamp))
	return bmp.NewBMPRouteMirroring(
		*ph,
		[]bmp.BMPRouteMirrTLVInterface{
			// RFC7854: BGP Message TLV MUST occur last in the list of TLVs
			bmp.NewBMPRouteMirrTLVBGPMsg(bmp.BMP_ROUTE_MIRRORING_TLV_TYPE_BGP_MSG, msg),
		},
	)
}

func (b *bmpClientManager) addServer(c *config.BmpServerConfig) error {
	host := net.JoinHostPort(c.Address, strconv.Itoa(int(c.Port)))
	if _, y := b.clientMap[host]; y {
		return fmt.Errorf("bmp client %s is already configured", host)
	}
	b.clientMap[host] = &bmpClient{
		s:      b.s,
		dead:   make(chan struct{}),
		host:   host,
		c:      c,
		ribout: newribout(),
	}
	go b.clientMap[host].loop()
	return nil
}

func (b *bmpClientManager) deleteServer(c *config.BmpServerConfig) error {
	host := net.JoinHostPort(c.Address, strconv.Itoa(int(c.Port)))
	if c, y := b.clientMap[host]; !y {
		return fmt.Errorf("bmp client %s isn't found", host)
	} else {
		c.Stop()
		delete(b.clientMap, host)
	}
	return nil
}

type bmpClientManager struct {
	s         *BgpServer
	clientMap map[string]*bmpClient
}

func newBmpClientManager(s *BgpServer) *bmpClientManager {
	return &bmpClientManager{
		s:         s,
		clientMap: make(map[string]*bmpClient),
	}
}
<commit_msg>bmp: set A flag in Per-Peer Header<commit_after>// Copyright (C) 2015-2016 Nippon Telegraph and Telephone Corporation.
//
// 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 server

import (
	"fmt"
	"github.com/osrg/gobgp/config"
	"github.com/osrg/gobgp/packet/bgp"
	"github.com/osrg/gobgp/packet/bmp"
	"github.com/osrg/gobgp/table"
	log "github.com/sirupsen/logrus"
	"net"
	"strconv"
	"time"
)

type ribout map[string][]*table.Path

func newribout() ribout {
	return make(map[string][]*table.Path)
}

// return true if we need to send the path to the BMP server
func (r ribout) update(p *table.Path) bool {
	key := p.GetNlri().String() // TODO expose (*Path).getPrefix()
	l := r[key]
	if p.IsWithdraw {
		if len(l) == 0 {
			return false
		}
		n := make([]*table.Path, 0, len(l))
		for _, q := range l {
			if p.GetSource() == q.GetSource() {
				continue
			}
			n = append(n, q)
		}
		if len(n) == 0 {
			delete(r, key)
		} else {
			r[key] = n
		}
		return true
	}

	if len(l) == 0 {
		r[key] = []*table.Path{p}
		return true
	}

	doAppend := true
	for idx, q := range l {
		if p.GetSource() == q.GetSource() {
			// if we have sent the same path, don't send it again
			if p.Equal(q) {
				return false
			}
			l[idx] = p
			doAppend = false
		}
	}
	if doAppend {
		r[key] = append(r[key], p)
	}
	return true
}

func (b *bmpClient) tryConnect() *net.TCPConn {
	interval := 1
	for {
		log.WithFields(log.Fields{"Topic": "bmp"}).Debugf("Connecting BMP server:%s", b.host)
		conn, err := net.Dial("tcp", b.host)
		if err != nil {
			select {
			case <-b.dead:
				return nil
			default:
			}
			time.Sleep(time.Duration(interval) * time.Second)
			if interval < 30 {
				interval *= 2
			}
		} else {
			log.WithFields(log.Fields{"Topic": "bmp"}).Infof("BMP server is connected:%s", b.host)
			return conn.(*net.TCPConn)
		}
	}
}

func (b *bmpClient) Stop() {
	close(b.dead)
}

func (b *bmpClient) loop() {
	for {
		conn := b.tryConnect()
		if conn == nil {
			break
		}

		if func() bool {
			ops := []WatchOption{WatchPeerState(true)}
			if b.c.RouteMonitoringPolicy == config.BMP_ROUTE_MONITORING_POLICY_TYPE_BOTH {
				log.WithFields(
					log.Fields{"Topic": "bmp"},
				).Warn("both option for route-monitoring-policy is obsoleted")
			}
			if b.c.RouteMonitoringPolicy == config.BMP_ROUTE_MONITORING_POLICY_TYPE_PRE_POLICY || b.c.RouteMonitoringPolicy == config.BMP_ROUTE_MONITORING_POLICY_TYPE_ALL {
				ops = append(ops, WatchUpdate(true))
			}
			if b.c.RouteMonitoringPolicy == config.BMP_ROUTE_MONITORING_POLICY_TYPE_POST_POLICY || b.c.RouteMonitoringPolicy == config.BMP_ROUTE_MONITORING_POLICY_TYPE_ALL {
				ops = append(ops, WatchPostUpdate(true))
			}
			if b.c.RouteMonitoringPolicy == config.BMP_ROUTE_MONITORING_POLICY_TYPE_LOCAL_RIB || b.c.RouteMonitoringPolicy == config.BMP_ROUTE_MONITORING_POLICY_TYPE_ALL {
				ops = append(ops, WatchBestPath(true))
			}
			if b.c.RouteMirroringEnabled {
				ops = append(ops, WatchMessage(false))
			}
			w := b.s.Watch(ops...)
			defer w.Stop()

			var tickerCh <-chan time.Time
			if b.c.StatisticsTimeout == 0 {
				log.WithFields(log.Fields{"Topic": "bmp"}).Debug("statistics reports disabled")
			} else {
				t := time.NewTicker(time.Duration(b.c.StatisticsTimeout) * time.Second)
				defer t.Stop()
				tickerCh = t.C
			}

			write := func(msg *bmp.BMPMessage) error {
				buf, _ := msg.Serialize()
				_, err := conn.Write(buf)
				if err != nil {
					log.Warnf("failed to write to bmp server %s", b.host)
				}
				return err
			}

			if err := write(bmp.NewBMPInitiation([]bmp.BMPInfoTLVInterface{})); err != nil {
				return false
			}

			for {
				select {
				case ev := <-w.Event():
					switch msg := ev.(type) {
					case *WatchEventUpdate:
						info := &table.PeerInfo{
							Address: msg.PeerAddress,
							AS:      msg.PeerAS,
							ID:      msg.PeerID,
						}
						if msg.Payload == nil {
							pathList := make([]*table.Path, 0, len(msg.PathList))
							for _, p := range msg.PathList {
								if b.ribout.update(p) {
									pathList = append(pathList, p)
								}
							}
							for _, u := range table.CreateUpdateMsgFromPaths(pathList) {
								payload, _ := u.Serialize()
								if err := write(bmpPeerRoute(bmp.BMP_PEER_TYPE_GLOBAL, msg.PostPolicy, 0, true, info, msg.Timestamp.Unix(), payload)); err != nil {
									return false
								}
							}
						} else {
							if err := write(bmpPeerRoute(bmp.BMP_PEER_TYPE_GLOBAL, msg.PostPolicy, 0, msg.FourBytesAs, info, msg.Timestamp.Unix(), msg.Payload)); err != nil {
								return false
							}
						}
					case *WatchEventBestPath:
						info := &table.PeerInfo{
							Address: net.ParseIP("0.0.0.0").To4(),
							AS:      b.s.bgpConfig.Global.Config.As,
							ID:      net.ParseIP(b.s.bgpConfig.Global.Config.RouterId).To4(),
						}
						for _, p := range msg.PathList {
							u := table.CreateUpdateMsgFromPaths([]*table.Path{p})[0]
							if payload, err := u.Serialize(); err != nil {
								return false
							} else if err = write(bmpPeerRoute(bmp.BMP_PEER_TYPE_LOCAL_RIB, false, 0, true, info, p.GetTimestamp().Unix(), payload)); err != nil {
								return false
							}
						}
					case *WatchEventPeerState:
						info := &table.PeerInfo{
							Address: msg.PeerAddress,
							AS:      msg.PeerAS,
							ID:      msg.PeerID,
						}
						if msg.State == bgp.BGP_FSM_ESTABLISHED {
							if err := write(bmpPeerUp(msg.LocalAddress.String(), msg.LocalPort, msg.PeerPort, msg.SentOpen, msg.RecvOpen, bmp.BMP_PEER_TYPE_GLOBAL, false, 0, info, msg.Timestamp.Unix())); err != nil {
								return false
							}
						} else {
							if err := write(bmpPeerDown(bmp.BMP_PEER_DOWN_REASON_UNKNOWN, bmp.BMP_PEER_TYPE_GLOBAL, false, 0, info, msg.Timestamp.Unix())); err != nil {
								return false
							}
						}
					case *WatchEventMessage:
						info := &table.PeerInfo{
							Address: msg.PeerAddress,
							AS:      msg.PeerAS,
							ID:      msg.PeerID,
						}
						if err := write(bmpPeerRouteMirroring(bmp.BMP_PEER_TYPE_GLOBAL, 0, info, msg.Timestamp.Unix(), msg.Message)); err != nil {
							return false
						}
					}
				case <-tickerCh:
					neighborList := b.s.GetNeighbor("", true)
					for _, n := range neighborList {
						if n.State.SessionState != config.SESSION_STATE_ESTABLISHED {
							continue
						}
						if err := write(bmpPeerStats(bmp.BMP_PEER_TYPE_GLOBAL, 0, 0, n)); err != nil {
							return false
						}
					}
				case <-b.dead:
					term := bmp.NewBMPTermination([]bmp.BMPTermTLVInterface{
						bmp.NewBMPTermTLV16(bmp.BMP_TERM_TLV_TYPE_REASON, bmp.BMP_TERM_REASON_PERMANENTLY_ADMIN),
					})
					if err := write(term); err != nil {
						return false
					}
					conn.Close()
					return true
				}
			}
		}() {
			return
		}
	}
}

type bmpClient struct {
	s      *BgpServer
	dead   chan struct{}
	host   string
	c      *config.BmpServerConfig
	ribout ribout
}

func bmpPeerUp(laddr string, lport, rport uint16, sent, recv *bgp.BGPMessage, t uint8, policy bool, pd uint64, peeri *table.PeerInfo, timestamp int64) *bmp.BMPMessage {
	var flags uint8 = 0
	if policy {
		flags |= bmp.BMP_PEER_FLAG_POST_POLICY
	}
	ph := bmp.NewBMPPeerHeader(t, flags, pd, peeri.Address.String(), peeri.AS, peeri.ID.String(), float64(timestamp))
	return bmp.NewBMPPeerUpNotification(*ph, laddr, lport, rport, sent, recv)
}

func bmpPeerDown(reason uint8, t uint8, policy bool, pd uint64, peeri *table.PeerInfo, timestamp int64) *bmp.BMPMessage {
	var flags uint8 = 0
	if policy {
		flags |= bmp.BMP_PEER_FLAG_POST_POLICY
	}
	ph := bmp.NewBMPPeerHeader(t, flags, pd, peeri.Address.String(), peeri.AS, peeri.ID.String(), float64(timestamp))
	return bmp.NewBMPPeerDownNotification(*ph, reason, nil, []byte{})
}

func bmpPeerRoute(t uint8, policy bool, pd uint64, fourBytesAs bool, peeri *table.PeerInfo, timestamp int64, payload []byte) *bmp.BMPMessage {
	var flags uint8 = 0
	if policy {
		flags |= bmp.BMP_PEER_FLAG_POST_POLICY
	}
	if !fourBytesAs {
		flags |= bmp.BMP_PEER_FLAG_TWO_AS
	}
	ph := bmp.NewBMPPeerHeader(t, flags, pd, peeri.Address.String(), peeri.AS, peeri.ID.String(), float64(timestamp))
	m := bmp.NewBMPRouteMonitoring(*ph, nil)
	body := m.Body.(*bmp.BMPRouteMonitoring)
	body.BGPUpdatePayload = payload
	return m
}

func bmpPeerStats(peerType uint8, peerDist uint64, timestamp int64, neighConf *config.Neighbor) *bmp.BMPMessage {
	var peerFlags uint8 = 0
	ph := bmp.NewBMPPeerHeader(peerType, peerFlags, peerDist, neighConf.State.NeighborAddress, neighConf.State.PeerAs, neighConf.State.RemoteRouterId, float64(timestamp))
	return bmp.NewBMPStatisticsReport(
		*ph,
		[]bmp.BMPStatsTLVInterface{
			bmp.NewBMPStatsTLV64(bmp.BMP_STAT_TYPE_ADJ_RIB_IN, uint64(neighConf.State.AdjTable.Accepted)),
			bmp.NewBMPStatsTLV64(bmp.BMP_STAT_TYPE_LOC_RIB, uint64(neighConf.State.AdjTable.Advertised+neighConf.State.AdjTable.Filtered)),
			bmp.NewBMPStatsTLV32(bmp.BMP_STAT_TYPE_WITHDRAW_UPDATE, neighConf.State.Messages.Received.WithdrawUpdate),
			bmp.NewBMPStatsTLV32(bmp.BMP_STAT_TYPE_WITHDRAW_PREFIX, neighConf.State.Messages.Received.WithdrawPrefix),
		},
	)
}

func bmpPeerRouteMirroring(peerType uint8, peerDist uint64, peerInfo *table.PeerInfo, timestamp int64, msg *bgp.BGPMessage) *bmp.BMPMessage {
	var peerFlags uint8 = 0
	ph := bmp.NewBMPPeerHeader(peerType, peerFlags, peerDist, peerInfo.Address.String(), peerInfo.AS, peerInfo.ID.String(), float64(timestamp))
	return bmp.NewBMPRouteMirroring(
		*ph,
		[]bmp.BMPRouteMirrTLVInterface{
			// RFC7854: BGP Message TLV MUST occur last in the list of TLVs
			bmp.NewBMPRouteMirrTLVBGPMsg(bmp.BMP_ROUTE_MIRRORING_TLV_TYPE_BGP_MSG, msg),
		},
	)
}

func (b *bmpClientManager) addServer(c *config.BmpServerConfig) error {
	host := net.JoinHostPort(c.Address, strconv.Itoa(int(c.Port)))
	if _, y := b.clientMap[host]; y {
		return fmt.Errorf("bmp client %s is already configured", host)
	}
	b.clientMap[host] = &bmpClient{
		s:      b.s,
		dead:   make(chan struct{}),
		host:   host,
		c:      c,
		ribout: newribout(),
	}
	go b.clientMap[host].loop()
	return nil
}

func (b *bmpClientManager) deleteServer(c *config.BmpServerConfig) error {
	host := net.JoinHostPort(c.Address, strconv.Itoa(int(c.Port)))
	if c, y := b.clientMap[host]; !y {
		return fmt.Errorf("bmp client %s isn't found", host)
	} else {
		c.Stop()
		delete(b.clientMap, host)
	}
	return nil
}

type bmpClientManager struct {
	s         *BgpServer
	clientMap map[string]*bmpClient
}

func newBmpClientManager(s *BgpServer) *bmpClientManager {
	return &bmpClientManager{
		s:         s,
		clientMap: make(map[string]*bmpClient),
	}
}
<|endoftext|>