<commit_before>/*
   Copyright 2020 Docker, 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 context

import (
	"context"

	"github.com/pkg/errors"
	"github.com/spf13/cobra"

	"github.com/docker/api/client"
	"github.com/docker/api/context/store"
)

type aciCreateOpts struct {
	description    string
	location       string
	subscriptionID string
	resourceGroup  string
}

func createAciCommand() *cobra.Command {
	var opts aciCreateOpts
	cmd := &cobra.Command{
		Use:   "aci CONTEXT [flags]",
		Short: "Create a context for Azure Container Instances",
		Args:  cobra.ExactArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			contextData, description, err := getAciContextData(cmd.Context(), opts)
			if err != nil {
				return nil
			}
			return createDockerContext(cmd.Context(), args[0], store.AciContextType, description, contextData)
		},
	}

	addDescriptionFlag(cmd, &opts.description)
	cmd.Flags().StringVar(&opts.location, "location", "eastus", "Location")
	cmd.Flags().StringVar(&opts.subscriptionID, "subscription-id", "", "Location")
	cmd.Flags().StringVar(&opts.resourceGroup, "resource-group", "", "Resource group")

	return cmd
}

func getAciContextData(ctx context.Context, opts aciCreateOpts) (interface{}, string, error) {
	cs, err := client.GetCloudService(ctx, store.AciContextType)
	if err != nil {
		return nil, "", errors.Wrap(err, "cannot connect to ACI backend")
	}
	return cs.CreateContextData(ctx, convertAciOpts(opts))
}

func convertAciOpts(opts aciCreateOpts) map[string]string {
	return map[string]string{
		"aciSubscriptionId": opts.subscriptionID,
		"aciResourceGroup":  opts.resourceGroup,
		"aciLocation":       opts.location,
		"description":       opts.description,
	}
}
<commit_msg>So much for golang error processing...<commit_after>/*
   Copyright 2020 Docker, 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 context

import (
	"context"

	"github.com/pkg/errors"
	"github.com/spf13/cobra"

	"github.com/docker/api/client"
	"github.com/docker/api/context/store"
)

type aciCreateOpts struct {
	description    string
	location       string
	subscriptionID string
	resourceGroup  string
}

func createAciCommand() *cobra.Command {
	var opts aciCreateOpts
	cmd := &cobra.Command{
		Use:   "aci CONTEXT [flags]",
		Short: "Create a context for Azure Container Instances",
		Args:  cobra.ExactArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			contextData, description, err := getAciContextData(cmd.Context(), opts)
			if err != nil {
				return err
			}
			return createDockerContext(cmd.Context(), args[0], store.AciContextType, description, contextData)
		},
	}

	addDescriptionFlag(cmd, &opts.description)
	cmd.Flags().StringVar(&opts.location, "location", "eastus", "Location")
	cmd.Flags().StringVar(&opts.subscriptionID, "subscription-id", "", "Location")
	cmd.Flags().StringVar(&opts.resourceGroup, "resource-group", "", "Resource group")

	return cmd
}

func getAciContextData(ctx context.Context, opts aciCreateOpts) (interface{}, string, error) {
	cs, err := client.GetCloudService(ctx, store.AciContextType)
	if err != nil {
		return nil, "", errors.Wrap(err, "cannot connect to ACI backend")
	}
	return cs.CreateContextData(ctx, convertAciOpts(opts))
}

func convertAciOpts(opts aciCreateOpts) map[string]string {
	return map[string]string{
		"aciSubscriptionId": opts.subscriptionID,
		"aciResourceGroup":  opts.resourceGroup,
		"aciLocation":       opts.location,
		"description":       opts.description,
	}
}
<|endoftext|>


<commit_before>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package cmd

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/vespa-engine/vespa/client/go/vespa"
)

func TestConfig(t *testing.T) {
	homeDir := filepath.Join(t.TempDir(), ".vespa")
	assertConfigCommandErr(t, "Error: invalid option or value: \"foo\": \"bar\"\n", homeDir, "config", "set", "foo", "bar")
	assertConfigCommand(t, "foo = <unset>\n", homeDir, "config", "get", "foo")
	assertConfigCommand(t, "target = local\n", homeDir, "config", "get", "target")
	assertConfigCommand(t, "", homeDir, "config", "set", "target", "hosted")
	assertConfigCommand(t, "target = hosted\n", homeDir, "config", "get", "target")
	assertConfigCommand(t, "", homeDir, "config", "set", "target", "cloud")
	assertConfigCommand(t, "target = cloud\n", homeDir, "config", "get", "target")
	assertConfigCommand(t, "", homeDir, "config", "set", "target", "http://127.0.0.1:8080")
	assertConfigCommand(t, "", homeDir, "config", "set", "target", "https://127.0.0.1")
	assertConfigCommand(t, "target = https://127.0.0.1\n", homeDir, "config", "get", "target")
	assertEnvConfigCommand(t, "api-key-file = /tmp/private.key\n", homeDir, map[string]string{"VESPA_CLI_API_KEY_FILE": "/tmp/private.key"}, "config", "get", "api-key-file")
	assertConfigCommand(t, "", homeDir, "config", "set", "api-key-file", "/tmp/private.key")
	assertConfigCommand(t, "api-key-file = /tmp/private.key\n", homeDir, "config", "get", "api-key-file")

	assertConfigCommandErr(t, "Error: invalid application: \"foo\"\n", homeDir, "config", "set", "application", "foo")
	assertConfigCommand(t, "application = <unset>\n", homeDir, "config", "get", "application")
	assertConfigCommand(t, "", homeDir, "config", "set", "application", "t1.a1.i1")
	assertConfigCommand(t, "application = t1.a1.i1\n", homeDir, "config", "get", "application")

	assertConfigCommand(t, "api-key-file = /tmp/private.key\napplication = t1.a1.i1\ncolor = auto\nquiet = false\ntarget = https://127.0.0.1\nwait = 0\n", homeDir, "config", "get")

	assertConfigCommand(t, "", homeDir, "config", "set", "wait", "60")
	assertConfigCommandErr(t, "Error: wait option must be an integer >= 0, got \"foo\"\n", homeDir, "config", "set", "wait", "foo")
	assertConfigCommand(t, "wait = 60\n", homeDir, "config", "get", "wait")

	assertConfigCommand(t, "", homeDir, "config", "set", "quiet", "true")
	assertConfigCommand(t, "", homeDir, "config", "set", "quiet", "false")
}

func assertConfigCommand(t *testing.T, expected, homeDir string, args ...string) {
	assertEnvConfigCommand(t, expected, homeDir, nil, args...)
}

func assertEnvConfigCommand(t *testing.T, expected, homeDir string, env map[string]string, args ...string) {
	out, _ := execute(command{homeDir: homeDir, env: env, args: args}, t, nil)
	assert.Equal(t, expected, out)
}

func assertConfigCommandErr(t *testing.T, expected, homeDir string, args ...string) {
	_, outErr := execute(command{homeDir: homeDir, args: args}, t, nil)
	assert.Equal(t, expected, outErr)
}

func withEnv(key, value string, fn func()) {
	orig, ok := os.LookupEnv(key)
	os.Setenv(key, value)
	fn()
	if ok {
		os.Setenv(key, orig)
	} else {
		os.Unsetenv(key)
	}
}

func TestUseAPIKey(t *testing.T) {
	homeDir := t.TempDir()
	c := Config{Home: homeDir}

	assert.False(t, c.UseAPIKey(vespa.PublicSystem, "t1"))

	c.Set(apiKeyFileFlag, "/tmp/foo")
	assert.True(t, c.UseAPIKey(vespa.PublicSystem, "t1"))
	c.Set(apiKeyFileFlag, "")

	withEnv("VESPA_CLI_API_KEY", "...", func() {
		require.Nil(t, c.load())
		assert.True(t, c.UseAPIKey(vespa.PublicSystem, "t1"))
	})

	// Test deprecated functionality
	authContent := `
{
    "version": 1,
    "providers": {
        "auth0": {
            "version": 1,
            "systems": {
                "public": {
					"access_token": "...",
					"scopes": ["openid", "offline_access"],
					"expires_at": "2030-01-01T01:01:01.000001+01:00"
				}
			}
		}
	}
}`
	withEnv("VESPA_CLI_CLOUD_SYSTEM", "public", func() {
		_, err := os.Create(filepath.Join(homeDir, "t2.api-key.pem"))
		require.Nil(t, err)
		assert.True(t, c.UseAPIKey(vespa.PublicSystem, "t2"))
		require.Nil(t, ioutil.WriteFile(filepath.Join(homeDir, "auth.json"), []byte(authContent), 0600))
		assert.False(t, c.UseAPIKey(vespa.PublicSystem, "t2"))
	})
}
<commit_msg>Ignore CI when running test<commit_after>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package cmd

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/vespa-engine/vespa/client/go/vespa"
)

func TestConfig(t *testing.T) {
	homeDir := filepath.Join(t.TempDir(), ".vespa")
	assertConfigCommandErr(t, "Error: invalid option or value: \"foo\": \"bar\"\n", homeDir, "config", "set", "foo", "bar")
	assertConfigCommand(t, "foo = <unset>\n", homeDir, "config", "get", "foo")
	assertConfigCommand(t, "target = local\n", homeDir, "config", "get", "target")
	assertConfigCommand(t, "", homeDir, "config", "set", "target", "hosted")
	assertConfigCommand(t, "target = hosted\n", homeDir, "config", "get", "target")
	assertConfigCommand(t, "", homeDir, "config", "set", "target", "cloud")
	assertConfigCommand(t, "target = cloud\n", homeDir, "config", "get", "target")
	assertConfigCommand(t, "", homeDir, "config", "set", "target", "http://127.0.0.1:8080")
	assertConfigCommand(t, "", homeDir, "config", "set", "target", "https://127.0.0.1")
	assertConfigCommand(t, "target = https://127.0.0.1\n", homeDir, "config", "get", "target")
	assertEnvConfigCommand(t, "api-key-file = /tmp/private.key\n", homeDir, map[string]string{"VESPA_CLI_API_KEY_FILE": "/tmp/private.key"}, "config", "get", "api-key-file")
	assertConfigCommand(t, "", homeDir, "config", "set", "api-key-file", "/tmp/private.key")
	assertConfigCommand(t, "api-key-file = /tmp/private.key\n", homeDir, "config", "get", "api-key-file")

	assertConfigCommandErr(t, "Error: invalid application: \"foo\"\n", homeDir, "config", "set", "application", "foo")
	assertConfigCommand(t, "application = <unset>\n", homeDir, "config", "get", "application")
	assertConfigCommand(t, "", homeDir, "config", "set", "application", "t1.a1.i1")
	assertConfigCommand(t, "application = t1.a1.i1\n", homeDir, "config", "get", "application")

	assertConfigCommand(t, "api-key-file = /tmp/private.key\napplication = t1.a1.i1\ncolor = auto\nquiet = false\ntarget = https://127.0.0.1\nwait = 0\n", homeDir, "config", "get")

	assertConfigCommand(t, "", homeDir, "config", "set", "wait", "60")
	assertConfigCommandErr(t, "Error: wait option must be an integer >= 0, got \"foo\"\n", homeDir, "config", "set", "wait", "foo")
	assertConfigCommand(t, "wait = 60\n", homeDir, "config", "get", "wait")

	assertConfigCommand(t, "", homeDir, "config", "set", "quiet", "true")
	assertConfigCommand(t, "", homeDir, "config", "set", "quiet", "false")
}

func assertConfigCommand(t *testing.T, expected, homeDir string, args ...string) {
	assertEnvConfigCommand(t, expected, homeDir, nil, args...)
}

func assertEnvConfigCommand(t *testing.T, expected, homeDir string, env map[string]string, args ...string) {
	out, _ := execute(command{homeDir: homeDir, env: env, args: args}, t, nil)
	assert.Equal(t, expected, out)
}

func assertConfigCommandErr(t *testing.T, expected, homeDir string, args ...string) {
	_, outErr := execute(command{homeDir: homeDir, args: args}, t, nil)
	assert.Equal(t, expected, outErr)
}

func withEnv(key, value string, fn func()) {
	orig, ok := os.LookupEnv(key)
	os.Setenv(key, value)
	fn()
	if ok {
		os.Setenv(key, orig)
	} else {
		os.Unsetenv(key)
	}
}

func TestUseAPIKey(t *testing.T) {
	homeDir := t.TempDir()
	c := Config{Home: homeDir}

	assert.False(t, c.UseAPIKey(vespa.PublicSystem, "t1"))

	c.Set(apiKeyFileFlag, "/tmp/foo")
	assert.True(t, c.UseAPIKey(vespa.PublicSystem, "t1"))
	c.Set(apiKeyFileFlag, "")

	withEnv("VESPA_CLI_API_KEY", "...", func() {
		require.Nil(t, c.load())
		assert.True(t, c.UseAPIKey(vespa.PublicSystem, "t1"))
	})

	// Test deprecated functionality
	authContent := `
{
    "version": 1,
    "providers": {
        "auth0": {
            "version": 1,
            "systems": {
                "public": {
					"access_token": "...",
					"scopes": ["openid", "offline_access"],
					"expires_at": "2030-01-01T01:01:01.000001+01:00"
				}
			}
		}
	}
}`
	withEnv("VESPA_CLI_CLOUD_SYSTEM", "public", func() {
		ci, ok := os.LookupEnv("CI")
		if ok {
			os.Unsetenv("CI") // Test depends on unset variable
		}
		_, err := os.Create(filepath.Join(homeDir, "t2.api-key.pem"))
		require.Nil(t, err)
		assert.True(t, c.UseAPIKey(vespa.PublicSystem, "t2"))
		require.Nil(t, ioutil.WriteFile(filepath.Join(homeDir, "auth.json"), []byte(authContent), 0600))
		assert.False(t, c.UseAPIKey(vespa.PublicSystem, "t2"))
		if ok {
			os.Setenv("CI", ci)
		}
	})
}
<|endoftext|>


<commit_before>package weavedns

import (
	"github.com/miekg/dns"
	"math"
	"net"
	"time"
)

// Portions of this code taken from github.com/armon/mdns

const (
	ipv4mdns    = "224.0.0.251" // link-local multicast address
	mdnsPort    = 5353          // mDNS assigned port
	mDNSTimeout = 200 * time.Millisecond
	MaxDuration = time.Duration(math.MaxInt64)
)

var (
	ipv4Addr = &net.UDPAddr{
		IP:   net.ParseIP(ipv4mdns),
		Port: mdnsPort,
	}
)

type ResponseA struct {
	Name string
	Addr net.IP
	Err  error
}

type responseInfo struct {
	timeout time.Time // if no answer by this time, give up
	ch      chan<- *ResponseA
}

// Represents one query that we have sent for one name.
// If we, internally, get several requests for the same name while we have
// a query in flight, then we don't want to send more queries out.
type inflightQuery struct {
	name          string
	id            uint16 // the DNS message ID
	responseInfos []*responseInfo
}

type MDNSClient struct {
	server    *dns.Server
	conn      *net.UDPConn
	addr      *net.UDPAddr
	inflight  map[string]*inflightQuery
	queryChan chan<- *MDNSInteraction
}

type mDNSQueryInfo struct {
	name       string
	querytype  uint16
	responseCh chan<- *ResponseA
}

func NewMDNSClient() (*MDNSClient, error) {
	conn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
	if err != nil {
		return nil, err
	}
	retval := &MDNSClient{
		conn:     conn,
		addr:     ipv4Addr,
		inflight: make(map[string]*inflightQuery)}
	return retval, nil
}

func (c *MDNSClient) Start(ifi *net.Interface) error {
	multicast, err := LinkLocalMulticastListener(ifi)
	if err != nil {
		return err
	}

	handleMDNS := func(w dns.ResponseWriter, r *dns.Msg) {
		//log.Println("client received:", r)
		// Only handle responses here
		if len(r.Answer) > 0 {
			c.ResponseCallback(r)
		}
	}

	c.server = &dns.Server{Listener: nil, PacketConn: multicast, Handler: dns.HandlerFunc(handleMDNS)}
	go c.server.ActivateAndServe()

	queryChan := make(chan *MDNSInteraction, 4)
	c.queryChan = queryChan
	go c.queryLoop(queryChan)

	return nil
}

func LinkLocalMulticastListener(ifi *net.Interface) (net.PacketConn, error) {
	conn, err := net.ListenMulticastUDP("udp", ifi, ipv4Addr)
	return conn, err
}

// ACTOR client API

const (
	CSendQuery       = iota
	CShutdown        = iota
	CMessageReceived = iota
)

type MDNSInteraction struct {
	code       int
	resultChan chan<- interface{}
	payload    interface{}
}

// Async
func (c *MDNSClient) Shutdown() {
	c.queryChan <- &MDNSInteraction{code: CShutdown}
}

// Async
func (c *MDNSClient) SendQuery(name string, querytype uint16, responseCh chan<- *ResponseA) {
	c.queryChan <- &MDNSInteraction{
		code:    CSendQuery,
		payload: mDNSQueryInfo{name, querytype, responseCh},
	}
}

// Async - called from dns library multiplexer
func (c *MDNSClient) ResponseCallback(r *dns.Msg) {
	c.queryChan <- &MDNSInteraction{code: CMessageReceived, payload: r}
}

// ACTOR server

func (c *MDNSClient) queryLoop(queryChan <-chan *MDNSInteraction) {
	timer := time.NewTimer(MaxDuration)
	run := func() {
		now := time.Now()
		after := MaxDuration
		for name, query := range c.inflight {
			// Count down from end of slice to beginning
			length := len(query.responseInfos)
			for i := length - 1; i >= 0; i-- {
				item := query.responseInfos[i]
				switch duration := item.timeout.Sub(now); {
				case duration <= 0: // timed out
					close(item.ch)
					// Swap item from the end of the slice
					length--
					if i < length {
						query.responseInfos[i] = query.responseInfos[length]
					}
				case duration < after:
					after = duration
				}
			}
			query.responseInfos = query.responseInfos[:length]
			if length == 0 {
				delete(c.inflight, name)
			}
		}
		timer.Reset(after)
	}

	terminate := false
	for !terminate {
		select {
		case query, ok := <-queryChan:
			if !ok {
				break
			}
			switch query.code {
			case CShutdown:
				c.server.Shutdown()
				terminate = true
			case CSendQuery:
				c.handleSendQuery(query.payload.(mDNSQueryInfo))
				run()
			case CMessageReceived:
				c.handleResponse(query.payload.(*dns.Msg))
				run()
			}
		case <-timer.C:
			run()
		}
	}

	// Close all response channels
	for _, query := range c.inflight {
		for _, item := range query.responseInfos {
			close(item.ch)
		}
	}
}

func (c *MDNSClient) handleSendQuery(q mDNSQueryInfo) error {
	query, found := c.inflight[q.name]
	if !found {
		m := new(dns.Msg)
		m.SetQuestion(q.name, q.querytype)
		m.RecursionDesired = false
		buf, err := m.Pack()
		if err != nil {
			q.responseCh <- &ResponseA{Err: err}
			close(q.responseCh)
			return err
		}
		query = &inflightQuery{
			name: q.name,
			id:   m.Id,
		}
		c.inflight[q.name] = query
		_, err = c.conn.WriteTo(buf, c.addr)
		if err != nil {
			q.responseCh <- &ResponseA{Err: err}
			close(q.responseCh)
			return err
		}
	}
	info := &responseInfo{
		ch:      q.responseCh,
		timeout: time.Now().Add(mDNSTimeout),
	}
	query.responseInfos = append(query.responseInfos, info)

	return nil
}

func (c *MDNSClient) handleResponse(r *dns.Msg) {
	for _, answer := range r.Answer {
		switch rr := answer.(type) {
		case *dns.A:
			if query, found := c.inflight[rr.Hdr.Name]; found {
				for _, resp := range query.responseInfos {
					resp.ch <- &ResponseA{Name: rr.Hdr.Name, Addr: rr.A}
				}
			} else {
				// We've received a response that didn't match a query
				// Do we want to cache it?
			}
		}
	}
}
<commit_msg>Clean up some code-review issues<commit_after>package weavedns

import (
	"github.com/miekg/dns"
	"math"
	"net"
	"time"
)

// Portions of this code taken from github.com/armon/mdns

const (
	ipv4mdns = "224.0.0.251" // link-local multicast address
	mdnsPort = 5353          // mDNS assigned port
	// We wait this long to hear responses from other mDNS servers on the network.
	// TODO: introduce caching so we don't have to wait this long on every call.
	mDNSTimeout = 250 * time.Millisecond
	MaxDuration = time.Duration(math.MaxInt64)
)

var (
	ipv4Addr = &net.UDPAddr{
		IP:   net.ParseIP(ipv4mdns),
		Port: mdnsPort,
	}
)

type ResponseA struct {
	Name string
	Addr net.IP
	Err  error
}

type responseInfo struct {
	timeout time.Time // if no answer by this time, give up
	ch      chan<- *ResponseA
}

// Represents one query that we have sent for one name.
// If we, internally, get several requests for the same name while we have
// a query in flight, then we don't want to send more queries out.
type inflightQuery struct {
	name          string
	id            uint16 // the DNS message ID
	responseInfos []*responseInfo
}

type MDNSClient struct {
	server    *dns.Server
	conn      *net.UDPConn
	addr      *net.UDPAddr
	inflight  map[string]*inflightQuery
	queryChan chan<- *MDNSInteraction
}

type mDNSQueryInfo struct {
	name       string
	querytype  uint16
	responseCh chan<- *ResponseA
}

func NewMDNSClient() (*MDNSClient, error) {
	conn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
	if err != nil {
		return nil, err
	}
	return &MDNSClient{
		conn:     conn,
		addr:     ipv4Addr,
		inflight: make(map[string]*inflightQuery)}, nil
}

func (c *MDNSClient) Start(ifi *net.Interface) error {
	multicast, err := LinkLocalMulticastListener(ifi)
	if err != nil {
		return err
	}

	handleMDNS := func(w dns.ResponseWriter, r *dns.Msg) {
		//log.Println("client received:", r)
		// Don't want to handle queries here, so filter anything out that isn't a response
		if len(r.Answer) > 0 {
			c.ResponseCallback(r)
		}
	}

	c.server = &dns.Server{Listener: nil, PacketConn: multicast, Handler: dns.HandlerFunc(handleMDNS)}
	go c.server.ActivateAndServe()

	queryChan := make(chan *MDNSInteraction, 4)
	c.queryChan = queryChan
	go c.queryLoop(queryChan)

	return nil
}

func LinkLocalMulticastListener(ifi *net.Interface) (net.PacketConn, error) {
	conn, err := net.ListenMulticastUDP("udp", ifi, ipv4Addr)
	return conn, err
}

// ACTOR client API

const (
	CSendQuery       = iota
	CShutdown        = iota
	CMessageReceived = iota
)

type MDNSInteraction struct {
	code       int
	resultChan chan<- interface{}
	payload    interface{}
}

// Async
func (c *MDNSClient) Shutdown() {
	c.queryChan <- &MDNSInteraction{code: CShutdown}
}

// Async
func (c *MDNSClient) SendQuery(name string, querytype uint16, responseCh chan<- *ResponseA) {
	c.queryChan <- &MDNSInteraction{
		code:    CSendQuery,
		payload: mDNSQueryInfo{name, querytype, responseCh},
	}
}

// Async - called from dns library multiplexer
func (c *MDNSClient) ResponseCallback(r *dns.Msg) {
	c.queryChan <- &MDNSInteraction{code: CMessageReceived, payload: r}
}

// ACTOR server

func (c *MDNSClient) queryLoop(queryChan <-chan *MDNSInteraction) {
	timer := time.NewTimer(MaxDuration)
	run := func() {
		now := time.Now()
		after := MaxDuration
		for name, query := range c.inflight {
			// Count down from end of slice to beginning
			length := len(query.responseInfos)
			for i := length - 1; i >= 0; i-- {
				item := query.responseInfos[i]
				switch duration := item.timeout.Sub(now); {
				case duration <= 0: // timed out
					close(item.ch)
					// Swap item from the end of the slice
					length--
					if i < length {
						query.responseInfos[i] = query.responseInfos[length]
					}
				case duration < after:
					after = duration
				}
			}
			query.responseInfos = query.responseInfos[:length]
			if length == 0 {
				delete(c.inflight, name)
			}
		}
		timer.Reset(after)
	}

	terminate := false
	for !terminate {
		select {
		case query, ok := <-queryChan:
			if !ok {
				break
			}
			switch query.code {
			case CShutdown:
				c.server.Shutdown()
				terminate = true
			case CSendQuery:
				c.handleSendQuery(query.payload.(mDNSQueryInfo))
				run()
			case CMessageReceived:
				c.handleResponse(query.payload.(*dns.Msg))
				run()
			}
		case <-timer.C:
			run()
		}
	}

	// Close all response channels
	for _, query := range c.inflight {
		for _, item := range query.responseInfos {
			close(item.ch)
		}
	}
}

func (c *MDNSClient) handleSendQuery(q mDNSQueryInfo) {
	query, found := c.inflight[q.name]
	if !found {
		m := new(dns.Msg)
		m.SetQuestion(q.name, q.querytype)
		m.RecursionDesired = false
		buf, err := m.Pack()
		if err != nil {
			q.responseCh <- &ResponseA{Err: err}
			close(q.responseCh)
			return
		}
		query = &inflightQuery{
			name: q.name,
			id:   m.Id,
		}
		c.inflight[q.name] = query
		_, err = c.conn.WriteTo(buf, c.addr)
		if err != nil {
			q.responseCh <- &ResponseA{Err: err}
			close(q.responseCh)
			return
		}
	}
	info := &responseInfo{
		ch:      q.responseCh,
		timeout: time.Now().Add(mDNSTimeout),
	}
	query.responseInfos = append(query.responseInfos, info)
}

func (c *MDNSClient) handleResponse(r *dns.Msg) {
	for _, answer := range r.Answer {
		switch rr := answer.(type) {
		case *dns.A:
			if query, found := c.inflight[rr.Hdr.Name]; found {
				for _, resp := range query.responseInfos {
					resp.ch <- &ResponseA{Name: rr.Hdr.Name, Addr: rr.A}
				}
			} else {
				// We've received a response that didn't match a query
				// Do we want to cache it?
			}
		}
	}
}
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"testing"
)

func TestParseUrlQuery(t *testing.T) {
	request := "http://127.0.0.1:3000/announce?info_hash=QtA%C0%81%8D%C5GV%02%150%5D%2B%91%80a%BB%02%9A&peer_id=-lt0D20-s%081%8ER%D7%C9%15X%DB%DD%D2&key=602bcd6f&compact=1&port=6963&uploaded=0&downloaded=0&left=5448254&event=started"

	result := decodeQueryURL(request)
	if result["uploaded"][0] != "0" {
		t.Fatalf("Expected 0, got %s", result["uploaded"])
	}
	if result["port"][0] != "6963" {
		t.Fatalf("Expected 0, got %s", result["port"])
	}
	if result["downloaded"][0] != "0" {
		t.Fatalf("Expected 0, got %s", result["downloaded"])
	}
	if result["compact"][0] != "1" {
		t.Fatalf("Expected 0, got %s", result["compact"])
	}

}

func TestParseTorrentGetRequest(t *testing.T) {
	request := "http://127.0.0.1:3000/announce?info_hash=QtA%C0%81%8D%C5GV%02%150%5D%2B%91%80a%BB%02%9A&peer_id=-lt0D20-s%081%8ER%D7%C9%15X%DB%DD%D2&key=602bcd6f&compact=1&port=6963&uploaded=0&downloaded=0&left=5448254&event=started"

	result := decodeQueryURL(request)
	fmt.Println(result)
	if result["uploaded"][0] != "0" {
		t.Fatalf("Expected 0, got %s", result["uploaded"])
	}
	if result["port"][0] != "6963" {
		t.Fatalf("Expected 0, got %s", result["port"])
	}
	if result["downloaded"][0] != "0" {
		t.Fatalf("Expected 0, got %s", result["downloaded"])
	}
	if result["compact"][0] != "1" {
		t.Fatalf("Expected 0, got %s", result["compact"])
	}
}

func TestParseInfoHash(t *testing.T) {
	expectedResult := "4925623525306625313825326325633425313825396325383925316325396559732563382566346725376225623359253137"
	result := ParseInfoHash("I%b5%0f%18%2c%c4%18%9c%89%1c%9eYs%c8%f4g%7b%b3Y%17")

	if result != expectedResult {
		t.Fatalf("Expected %s, got %s", expectedResult, result)
	}
}

func TestGetIntFailEmptyKey(t *testing.T) {
	urlValues := url.Parse("http://google.com/").Query()
	key := "testInt"

	expectedResult := 50
	result := GetInt(urlValues, key)

	if result == expectedResult {
		t.Fatalf("Expected %s, got %s", expectedResult, result)
	}
}

func TestGetInt(t *testing.T) {
	urlValues := url.Parse("http://google.com/?testInt=50").Query()
	key := "testInt"

	expectedResult := 50
	result := GetInt(urlValues, key)

	if result != expectedResult {
		t.Fatalf("Expected %s, got %s", expectedResult, result)
	}
}

<commit_msg>Update server_test.go<commit_after>package server

import (
	"fmt"
	"testing"
	"net/url"
)

func TestParseUrlQuery(t *testing.T) {
	request := "http://127.0.0.1:3000/announce?info_hash=QtA%C0%81%8D%C5GV%02%150%5D%2B%91%80a%BB%02%9A&peer_id=-lt0D20-s%081%8ER%D7%C9%15X%DB%DD%D2&key=602bcd6f&compact=1&port=6963&uploaded=0&downloaded=0&left=5448254&event=started"

	result := decodeQueryURL(request)
	if result["uploaded"][0] != "0" {
		t.Fatalf("Expected 0, got %s", result["uploaded"])
	}
	if result["port"][0] != "6963" {
		t.Fatalf("Expected 0, got %s", result["port"])
	}
	if result["downloaded"][0] != "0" {
		t.Fatalf("Expected 0, got %s", result["downloaded"])
	}
	if result["compact"][0] != "1" {
		t.Fatalf("Expected 0, got %s", result["compact"])
	}

}

func TestParseTorrentGetRequest(t *testing.T) {
	request := "http://127.0.0.1:3000/announce?info_hash=QtA%C0%81%8D%C5GV%02%150%5D%2B%91%80a%BB%02%9A&peer_id=-lt0D20-s%081%8ER%D7%C9%15X%DB%DD%D2&key=602bcd6f&compact=1&port=6963&uploaded=0&downloaded=0&left=5448254&event=started"

	result := decodeQueryURL(request)
	fmt.Println(result)
	if result["uploaded"][0] != "0" {
		t.Fatalf("Expected 0, got %s", result["uploaded"])
	}
	if result["port"][0] != "6963" {
		t.Fatalf("Expected 0, got %s", result["port"])
	}
	if result["downloaded"][0] != "0" {
		t.Fatalf("Expected 0, got %s", result["downloaded"])
	}
	if result["compact"][0] != "1" {
		t.Fatalf("Expected 0, got %s", result["compact"])
	}
}

func TestParseInfoHash(t *testing.T) {
	expectedResult := "4925623525306625313825326325633425313825396325383925316325396559732563382566346725376225623359253137"
	result := ParseInfoHash("I%b5%0f%18%2c%c4%18%9c%89%1c%9eYs%c8%f4g%7b%b3Y%17")

	if result != expectedResult {
		t.Fatalf("Expected %s, got %s", expectedResult, result)
	}
}

func TestGetIntFailEmptyKey(t *testing.T) {
	urlValues := url.Parse("http://google.com/").Query()
	key := "testInt"

	expectedResult := 50
	result := GetInt(urlValues, key)

	if result == expectedResult {
		t.Fatalf("Expected %s, got %s", expectedResult, result)
	}
}

func TestGetInt(t *testing.T) {
	urlValues := url.Parse("http://google.com/?testInt=50").Query()
	key := "testInt"

	expectedResult := 50
	result := GetInt(urlValues, key)

	if result != expectedResult {
		t.Fatalf("Expected %s, got %s", expectedResult, result)
	}
}

<|endoftext|>


<commit_before>/*package sphere_halo is essentially a redo of the implementation of
HaloProfiles found in the los package but with a different internal geometry
kernel. I've learned a few lessons since then about the right way to structure
this stuff and I'm going to try applying those lessons here.

Operating on a SphereHalo is relatively simple:

    hs := make([]SphereHalo, workers)
    h := &hs[0]
    h.Init(norms, origin, rMin, rMax, bins, n)

    // Read particle positions from disk. (Probably in a loop.)
    vecs := Read()

    h.Transform(vecs)
    intr := make([]bool, len(vecs))
    h.Intersect(vecs, intr)

    // Split the halo up into thread-specific workplaces.
    h.Split(hs)

   // Split into multiple thread here

   for i, vec := range vecs {
       if intr[i] { h.Insert(vec, ptRadius) }
    }

    // Do synchronization here

    h.Join(hs)
*/
package sphere_halo

import (
	"fmt"
	"math"

	"github.com/phil-mansfield/gotetra/los"
	"github.com/phil-mansfield/gotetra/math/mat"
	rgeom "github.com/phil-mansfield/gotetra/render/geom"
	"github.com/phil-mansfield/gotetra/los/geom"
)

// Type SphereHalo represents a halo which can have spheres inserted into it.
type SphereHalo struct {
	origin [3]float64
	rMin, rMax float64
	rings, bins, n int // bins = radial bins, n = number of lines per

	ringVecs [][2]float64
	ringPhis []float64
	dPhi float64

	rots []mat.Matrix32
	norms []geom.Vec
	profs []los.ProfileRing
}

// Init initializes a halo centered at origin with minimum and maximum radii
// given by rMin, and rMax. It will consist of a family of rings whose normals
// are given by the slice of vectors, norms. Each ring will consists of n
// lines of sight and will have bins radial bins.
func (h *SphereHalo) Init(
	norms []geom.Vec, origin [3]float64,
	rMin, rMax float64, bins, n int,
) {
	h.origin = origin
	h.rMin, h.rMax = rMin, rMax
	h.rings, h.bins, h.n = len(norms), bins, n
	h.norms = norms

	zAxis := &geom.Vec{0, 0, 1}

	h.profs = make([]los.ProfileRing, h.rings)
	h.rots = make([]mat.Matrix32, h.rings)

	for i := range h.profs {
		h.profs[i].Init(math.Log(h.rMin), math.Log(h.rMax), h.bins, h.n)
		h.rots[i].Init(make([]float32, 9), 3, 3)
		geom.EulerMatrixBetweenAt(&norms[i], zAxis, &h.rots[i])
	}

	h.ringPhis = make([]float64, h.n)
	h.ringVecs = make([][2]float64, h.n)
	for i := 0; i < h.n; i++ {
		h.ringPhis[i] = float64(i) / float64(n) * (2 * math.Pi)
		h.ringVecs[i][1], h.ringVecs[i][0] = math.Sincos(h.ringPhis[i])
	}
	h.dPhi = 1 / float64(n) * (2 * math.Pi)
}

// Split splits the halo h into copies and stores those copies in hs. The
// total mass stored in h and all those copies is equal to the total mass
// stored in h.
//
// Used for parallelization. But very expensive.
func (h *SphereHalo) Split(hs []SphereHalo) {
	for i := range hs {
		hi := &hs[i]
		if h.rings != hi.rings || h.bins != hi.bins || h.n != hi.n {
			hi.Init(h.norms, h.origin, h.rMin, h.rMax, h.bins, h.n)
		} else {
			hi.norms = h.norms
			hi.rots = h.rots
			hi.origin = h.origin
			hi.rMin, hi.rMax = h.rMin, h.rMax
		}
		for r := range h.profs {
			h.profs[r].Split(&hi.profs[r])
		}
	}
}

// Join joins h and all the halos in hs together into h. The mass stored in h
// at the end is equal to the total mass intially in h and all the halos in hs.
//
// Used for parallelization. But very expensive.
func (h *SphereHalo) Join(hs []SphereHalo) {
	for i := range hs {
		hi := &hs[i]
		if h.rings != hi.rings || h.bins != hi.bins || h.n != hi.n {
			panic(fmt.Sprintf("size of h != size of hs[%d]", i))
		}

		for r := range h.profs {
			h.profs[r].Join(&hi.profs[r])
		}
	}
}

// Intersect treats all the given vectors as spheres of radius r, and tests
// them for intersection with the halo. The results are written to the
// buffer intr.
//
// Intersect must be called after Transform is called on the vectors.
func (h *SphereHalo) Intersect(vecs []rgeom.Vec, r float64, intr []bool) {
	rMin, rMax := h.rMin + r, h.rMax + r
	rMin2, rMax2 := float32(rMin*rMin), float32(rMax*rMax)
	if rMin <= 0 { rMin2 = 0 }

	if len(intr) != len(vecs) { panic("len(intr) != len(vecs)") }

	x0, y0, z0 := float32(h.origin[0]), float32(h.origin[1]), float32(h.origin[2])
	for i, vec := range vecs {
		x, y, z := vec[0]-x0, vec[1]-y0, vec[2]-z0
		r2 := x*x + y*y + z*z
		intr[i] = r2 > rMin2 && r2 < rMax2
	}
}

// Transform translates all the given vectors so that they are in the local
// coordinate system of the halo.
func (h *SphereHalo) Transform(vecs []rgeom.Vec, totalWidth float64) {
	x0 := float32(h.origin[0])
	y0 := float32(h.origin[1])
	z0 := float32(h.origin[2])
	tw := float32(totalWidth)
	tw2 := tw / 2

	for i, vec := range vecs {
		x, y, z := vec[0], vec[1], vec[2]
		dx, dy, dz := x - x0, y - y0, z - z0

        if dx > tw2 {
            vecs[i][0] -= tw
        } else if dx < -tw2 {
            vecs[i][0] += tw
        }

        if dy > tw2 {
            vecs[i][1] -= tw
        } else if dy < -tw2 {
            vecs[i][1] += tw
        }

        if dz > tw2 {
            vecs[i][2] -= tw
        } else if dz < -tw2 {
            vecs[i][2] += tw
        }
	}
}

// Insert insreats a sphere with the given center and radius to all the rings
// of the halo.
func (h *SphereHalo) Insert(vec geom.Vec, radius, rho float64) {
	// transform into displacement from the center
	vec[0] -= float32(h.origin[0])
	vec[1] -= float32(h.origin[1])
	vec[2] -= float32(h.origin[2])

	for ring := 0; ring < h.rings; ring++ {
		// If this intersection check is the chief cost, we can throw some
		// more computational feometry at it until it's fixed. (3D spatial
		// indexing trees.)
		if h.sphereIntersectRing(vec, radius, ring) {
			h.insertToRing(vec, radius, rho, ring)
		}
	}
}

// sphereIntersecRing performs an intersection
func (h *SphereHalo) sphereIntersectRing(
	vec geom.Vec, radius float64, ring int,
) bool {
	norm := h.norms[ring]
	dot := float64(norm[0]*vec[0] + norm[1]*vec[1] + norm[2]*vec[2])
	return dot < radius && dot > -radius
}

// insertToRing inserts a sphere of the given center, radius, and density to
// one ring of the halo. This is where the magic happens.
func (h *SphereHalo) insertToRing(vec geom.Vec, radius, rho float64, ring int) {
	vec.Rotate(&h.rots[ring])

	// Properties of the projected circle.
	cx, cy, cz := float64(vec[0]), float64(vec[1]), float64(vec[2])
	projDist2 := cx*cx + cy*cy
	projRad2 := radius*radius - cz*cz
	if projRad2 > projDist2 {
		// Circle contains center.
		for i := 0; i < h.n; i++ {
			// b = impact parameter
			b := cx*h.ringVecs[i][0] + cy*h.ringVecs[i][1]
			rHi := oneValIntrDist(projDist2, projRad2, b)
			h.profs[ring].Insert(math.Inf(-1), math.Log(rHi), rho, i)
		}
	} else {
		// Circle does not contain center.
		alpha := halfAngularWidth(projDist2, projRad2)
		projPhi := math.Atan2(cy, cx)
		phiStart, phiEnd := projPhi-alpha, projPhi+alpha
		iLo1, iHi1, iLo2, iHi2 := h.idxRange(phiStart, phiEnd)

		for i := iLo1; i < iHi1; i++ {
			// b = impact parameter
			b := cx*h.ringVecs[i][0] + cy*h.ringVecs[i][1]
			rLo, rHi := twoValIntrDist(projDist2, projRad2, b)
			h.profs[ring].Insert(math.Log(rLo), math.Log(rHi), rho, i)
		}

		for i := iLo2; i < iHi2; i++ {
			b := cx*h.ringVecs[i][0] + cy*h.ringVecs[i][1]
			rLo, rHi := twoValIntrDist(projDist2, projRad2, b)
			h.profs[ring].Insert(math.Log(rLo), math.Log(rHi), rho, i)
		}
	}
}

// idxRange returns the range of indices spanned by the two given angles.
// Since it is possible that the indices map to non-contiguous potions of the
// LoS array, two sets of indices are returned and bot sets must be looped over.
//
// Upper indices are _exclusive_.
func (h *SphereHalo) idxRange(
	phiHi, phiLo float64,
) (iLo1, iHi1, iLo2, iHi2 int) {
	// An alternate approach involves doing some modulo calculations.
	// It is simpler, but slower.
	switch {
	case phiHi > 2*math.Pi:
		// phiHi wraps around.
		iLo1 = int(phiLo/h.dPhi)
		iHi1 = h.n
		iLo2 = 0
		iHi2 = int((phiHi - 2*math.Pi)/h.dPhi) + 1
		return iLo1, iHi1, iLo2, iHi2
	case phiLo < 0:
		// phiLo wraps around.
		iLo1 = int((phiLo + 2*math.Pi)/h.dPhi)
		iHi1 = h.n
		iLo2 = 0
		iHi2 = int(phiHi/h.dPhi) + 1
		return iLo1, iHi1, iLo2, iHi2
	default:
		// not wrapping around at all.
		iLo := int(phiLo/h.dPhi)
		iHi := int(phiLo/h.dPhi)+  1
		return iLo, iHi, 0, 0
	}
}

// angularWidth returns the angular width in radians of a circle of at a
// squared distance of dist2 and a squared radius of r2. It's assumed that
// the circle does not contain the origin.
func halfAngularWidth(dist2, r2 float64) float64 {
	return math.Asin(math.Sqrt(r2/dist2))
}

// twoValIntrDist returns both the intersection distances for a ray which
// passes through a circle at two points. dist2 is the squared distance
// between the origin of the ray and the center of the circle, rad2 is the
// squared radius of the circle, and b is the impact parameter of the
// ray and the center of the circle.
func twoValIntrDist(dist2, rad2, b float64) (lo, hi float64) {
	midDist := math.Sqrt(dist2 - rad2)
	diff := math.Sqrt(rad2 - b*b)
	return midDist-diff, midDist+diff
}

// twoValIntrDist returns both the intersection distances for a ray which
// passes through a circle at one point. dist2 is the squared distance
// between the origin of the ray and the center of the circle, rad2 is the
// squared radius of the circle, and b is the impact parameter of the
// ray and the center of the circle.
func oneValIntrDist(dist2, rad2, b float64) float64 {
	return math.Sqrt(rad2 - dist2) + math.Sqrt(rad2 - b*b)
}
<commit_msg>Bug fixes to sphere_halo's geometry code.<commit_after>/*package sphere_halo is essentially a redo of the implementation of
HaloProfiles found in the los package but with a different internal geometry
kernel. I've learned a few lessons since then about the right way to structure
this stuff and I'm going to try applying those lessons here.

Operating on a SphereHalo is relatively simple:

    hs := make([]SphereHalo, workers)
    h := &hs[0]
    h.Init(norms, origin, rMin, rMax, bins, n)

    // Read particle positions from disk. (Probably in a loop.)
    vecs := Read()

    h.Transform(vecs)
    intr := make([]bool, len(vecs))
    h.Intersect(vecs, intr)

    // Split the halo up into thread-specific workplaces.
    h.Split(hs)

   // Split into multiple thread here

   for i, vec := range vecs {
       if intr[i] { h.Insert(vec, ptRadius) }
    }

    // Do synchronization here

    h.Join(hs)
*/
package sphere_halo

import (
	"fmt"
	"math"

	"github.com/phil-mansfield/gotetra/los"
	"github.com/phil-mansfield/gotetra/math/mat"
	rgeom "github.com/phil-mansfield/gotetra/render/geom"
	"github.com/phil-mansfield/gotetra/los/geom"
)

// Type SphereHalo represents a halo which can have spheres inserted into it.
type SphereHalo struct {
	origin [3]float64
	rMin, rMax float64
	rings, bins, n int // bins = radial bins, n = number of lines per

	ringVecs [][2]float64
	ringPhis []float64
	dPhi float64

	rots []mat.Matrix32
	norms []geom.Vec
	profs []los.ProfileRing
}

// Init initializes a halo centered at origin with minimum and maximum radii
// given by rMin, and rMax. It will consist of a family of rings whose normals
// are given by the slice of vectors, norms. Each ring will consists of n
// lines of sight and will have bins radial bins.
func (h *SphereHalo) Init(
	norms []geom.Vec, origin [3]float64,
	rMin, rMax float64, bins, n int,
) {
	h.origin = origin
	h.rMin, h.rMax = rMin, rMax
	h.rings, h.bins, h.n = len(norms), bins, n
	h.norms = norms

	zAxis := &geom.Vec{0, 0, 1}

	h.profs = make([]los.ProfileRing, h.rings)
	h.rots = make([]mat.Matrix32, h.rings)

	for i := range h.profs {
		h.profs[i].Init(math.Log(h.rMin), math.Log(h.rMax), h.bins, h.n)
		h.rots[i].Init(make([]float32, 9), 3, 3)
		geom.EulerMatrixBetweenAt(&norms[i], zAxis, &h.rots[i])
	}

	h.ringPhis = make([]float64, h.n)
	h.ringVecs = make([][2]float64, h.n)
	for i := 0; i < h.n; i++ {
		h.ringPhis[i] = float64(i) / float64(n) * (2 * math.Pi)
		h.ringVecs[i][1], h.ringVecs[i][0] = math.Sincos(h.ringPhis[i])
	}
	h.dPhi = 1 / float64(n) * (2 * math.Pi)
}

// Split splits the halo h into copies and stores those copies in hs. The
// total mass stored in h and all those copies is equal to the total mass
// stored in h.
//
// Used for parallelization. But very expensive.
func (h *SphereHalo) Split(hs []SphereHalo) {
	for i := range hs {
		hi := &hs[i]
		if h.rings != hi.rings || h.bins != hi.bins || h.n != hi.n {
			hi.Init(h.norms, h.origin, h.rMin, h.rMax, h.bins, h.n)
		} else {
			hi.norms = h.norms
			hi.rots = h.rots
			hi.origin = h.origin
			hi.rMin, hi.rMax = h.rMin, h.rMax
		}
		for r := range h.profs {
			h.profs[r].Split(&hi.profs[r])
		}
	}
}

// Join joins h and all the halos in hs together into h. The mass stored in h
// at the end is equal to the total mass intially in h and all the halos in hs.
//
// Used for parallelization. But very expensive.
func (h *SphereHalo) Join(hs []SphereHalo) {
	for i := range hs {
		hi := &hs[i]
		if h.rings != hi.rings || h.bins != hi.bins || h.n != hi.n {
			panic(fmt.Sprintf("size of h != size of hs[%d]", i))
		}

		for r := range h.profs {
			h.profs[r].Join(&hi.profs[r])
		}
	}
}

// Intersect treats all the given vectors as spheres of radius r, and tests
// them for intersection with the halo. The results are written to the
// buffer intr.
//
// Intersect must be called after Transform is called on the vectors.
func (h *SphereHalo) Intersect(vecs []rgeom.Vec, r float64, intr []bool) {
	rMin, rMax := h.rMin + r, h.rMax + r
	rMin2, rMax2 := float32(rMin*rMin), float32(rMax*rMax)
	if rMin <= 0 { rMin2 = 0 }

	if len(intr) != len(vecs) { panic("len(intr) != len(vecs)") }

	x0, y0, z0 := float32(h.origin[0]), float32(h.origin[1]), float32(h.origin[2])
	for i, vec := range vecs {
		x, y, z := vec[0]-x0, vec[1]-y0, vec[2]-z0
		r2 := x*x + y*y + z*z
		intr[i] = r2 > rMin2 && r2 < rMax2
	}
}

// Transform translates all the given vectors so that they are in the local
// coordinate system of the halo.
func (h *SphereHalo) Transform(vecs []rgeom.Vec, totalWidth float64) {
	x0 := float32(h.origin[0])
	y0 := float32(h.origin[1])
	z0 := float32(h.origin[2])
	tw := float32(totalWidth)
	tw2 := tw / 2

	for i, vec := range vecs {
		x, y, z := vec[0], vec[1], vec[2]
		dx, dy, dz := x - x0, y - y0, z - z0

        if dx > tw2 {
            vecs[i][0] -= tw
        } else if dx < -tw2 {
            vecs[i][0] += tw
        }

        if dy > tw2 {
            vecs[i][1] -= tw
        } else if dy < -tw2 {
            vecs[i][1] += tw
        }

        if dz > tw2 {
            vecs[i][2] -= tw
        } else if dz < -tw2 {
            vecs[i][2] += tw
        }
	}
}

// Insert insreats a sphere with the given center and radius to all the rings
// of the halo.
func (h *SphereHalo) Insert(vec geom.Vec, radius, rho float64) {
	// transform into displacement from the center
	vec[0] -= float32(h.origin[0])
	vec[1] -= float32(h.origin[1])
	vec[2] -= float32(h.origin[2])

	for ring := 0; ring < h.rings; ring++ {
		// If this intersection check is the chief cost, we can throw some
		// more computational feometry at it until it's fixed. (3D spatial
		// indexing trees.)
		if h.sphereIntersectRing(vec, radius, ring) {
			h.insertToRing(vec, radius, rho, ring)
		}
	}
}

// sphereIntersecRing performs an intersection
func (h *SphereHalo) sphereIntersectRing(
	vec geom.Vec, radius float64, ring int,
) bool {
	norm := h.norms[ring]
	dot := float64(norm[0]*vec[0] + norm[1]*vec[1] + norm[2]*vec[2])
	return dot < radius && dot > -radius
}

// insertToRing inserts a sphere of the given center, radius, and density to
// one ring of the halo. This is where the magic happens.
func (h *SphereHalo) insertToRing(vec geom.Vec, radius, rho float64, ring int) {
	vec.Rotate(&h.rots[ring])

	// Properties of the projected circle.
	cx, cy, cz := float64(vec[0]), float64(vec[1]), float64(vec[2])
	projDist2 := cx*cx + cy*cy
	projRad2 := radius*radius - cz*cz
	if projRad2 > projDist2 {
		// Circle contains center.
		for i := 0; i < h.n; i++ {
			// b = impact parameter
			b := cy*h.ringVecs[i][0] - cx*h.ringVecs[i][1]
			dir := cx*h.ringVecs[i][0] + cy*h.ringVecs[i][1]
			rHi := oneValIntrDist(projDist2, projRad2, b, dir)
			h.profs[ring].Insert(math.Inf(-1), math.Log(rHi), rho, i)
		}
	} else {
		// Circle does not contain center.
		alpha := halfAngularWidth(projDist2, projRad2)
		projPhi := math.Atan2(cy, cx)
		phiStart, phiEnd := projPhi-alpha, projPhi+alpha
		iLo1, iHi1, iLo2, iHi2 := h.idxRange(phiStart, phiEnd)

		for i := iLo1; i < iHi1; i++ {
			// b = impact parameter
			b := cy*h.ringVecs[i][0] - cx*h.ringVecs[i][1]
			rLo, rHi := twoValIntrDist(projDist2, projRad2, b)
			if math.IsNaN(rLo) || math.IsNaN(rHi) { continue }
			h.profs[ring].Insert(math.Log(rLo), math.Log(rHi), rho, i)
		}

		for i := iLo2; i < iHi2; i++ {
			b := cy*h.ringVecs[i][0] - cx*h.ringVecs[i][1]
			rLo, rHi := twoValIntrDist(projDist2, projRad2, b)
			if math.IsNaN(rLo) || math.IsNaN(rHi) { continue }
			h.profs[ring].Insert(math.Log(rLo), math.Log(rHi), rho, i)
		}
	}
}

// idxRange returns the range of indices spanned by the two given angles.
// Since it is possible that the indices map to non-contiguous potions of the
// LoS array, two sets of indices are returned and bot sets must be looped over.
//
// Upper indices are _exclusive_.
func (h *SphereHalo) idxRange(
	phiLo, phiHi float64,
) (iLo1, iHi1, iLo2, iHi2 int) {
	// An alternate approach involves doing some modulo calculations.
	// It is simpler, but slower.
	switch {
	case phiHi > 2*math.Pi:
		// phiHi wraps around.
		iLo1 = int(phiLo/h.dPhi)
		iHi1 = h.n
		iLo2 = 0
		iHi2 = int((phiHi - 2*math.Pi)/h.dPhi) + 1
		return iLo1, iHi1, iLo2, iHi2
	case phiLo < 0:
		// phiLo wraps around.
		iLo1 = int((phiLo + 2*math.Pi)/h.dPhi)
		iHi1 = h.n
		iLo2 = 0
		iHi2 = int(phiHi/h.dPhi) + 1
		return iLo1, iHi1, iLo2, iHi2
	default:
		// not wrapping around at all.
		iLo := int(phiLo/h.dPhi)
		iHi := int(phiHi/h.dPhi)+  1
		return iLo, iHi, 0, 0
	}
}

// angularWidth returns the angular width in radians of a circle of at a
// squared distance of dist2 and a squared radius of r2. It's assumed that
// the circle does not contain the origin.
func halfAngularWidth(dist2, r2 float64) float64 {
	return math.Asin(math.Sqrt(r2/dist2))
}

// twoValIntrDist returns both the intersection distances for a ray which
// passes through a circle at two points. dist2 is the squared distance
// between the origin of the ray and the center of the circle, rad2 is the
// squared radius of the circle, and b is the impact parameter of the
// ray and the center of the circle.
func twoValIntrDist(dist2, rad2, b float64) (lo, hi float64) {
	b2 := b*b
	midDist := math.Sqrt(dist2 - b2)
	diff := math.Sqrt(rad2 - b2)
	return midDist-diff, midDist+diff
}

// twoValIntrDist returns both the intersection distances for a ray which
// passes through a circle at one point. dist2 is the squared distance
// between the origin of the ray and the center of the circle, rad2 is the
// squared radius of the circle, b is the impact parameter of the
// ray and the center of the circle, and dir is the dot product of the
// the circle's position vector and the normal vector of .
func oneValIntrDist(dist2, rad2, b, dir float64) float64 {
	b2 := b*b
	radMidDist := math.Sqrt(rad2 - b2)
	cMidDist := math.Sqrt(dist2 - b2)
	if dir > 0 {
		return radMidDist + cMidDist
	} else {
		return radMidDist - cMidDist
	}
}
<|endoftext|>


<commit_before>package openvswitch

import (
	"fmt"
	"net"
	"os/exec"
	"strconv"
	"strings"
	"sync"

	"github.com/lxc/lxd/shared"
)

// ovnBridgeMappingMutex locks access to read/write external-ids:ovn-bridge-mappings.
var ovnBridgeMappingMutex sync.Mutex

// NewOVS initialises new OVS wrapper.
func NewOVS() *OVS {
	return &OVS{}
}

// OVS command wrapper.
type OVS struct{}

// Installed returns true if OVS tools are installed.
func (o *OVS) Installed() bool {
	_, err := exec.LookPath("ovs-vsctl")
	return err == nil
}

// BridgeExists returns true if OVS bridge exists.
func (o *OVS) BridgeExists(bridgeName string) (bool, error) {
	_, err := shared.RunCommand("ovs-vsctl", "br-exists", bridgeName)
	if err != nil {
		runErr, ok := err.(shared.RunError)
		if ok {
			exitError, ok := runErr.Err.(*exec.ExitError)

			// ovs-vsctl manpage says that br-exists exits with code 2 if bridge doesn't exist.
			if ok && exitError.ExitCode() == 2 {
				return false, nil
			}
		}

		return false, err
	}

	return true, nil
}

// BridgeAdd adds an OVS bridge.
func (o *OVS) BridgeAdd(bridgeName string, mayExist bool) error {
	args := []string{}

	if mayExist {
		args = append(args, "--may-exist")
	}

	args = append(args, "add-br", bridgeName)

	_, err := shared.RunCommand("ovs-vsctl", args...)
	if err != nil {
		return err
	}

	return nil
}

// BridgeDelete deletes an OVS bridge.
func (o *OVS) BridgeDelete(bridgeName string) error {
	_, err := shared.RunCommand("ovs-vsctl", "del-br", bridgeName)
	if err != nil {
		return err
	}

	return nil
}

// BridgePortAdd adds a port to the bridge (if already attached does nothing).
func (o *OVS) BridgePortAdd(bridgeName string, portName string, mayExist bool) error {
	args := []string{}

	if mayExist {
		args = append(args, "--may-exist")
	}

	args = append(args, "add-port", bridgeName, portName)

	_, err := shared.RunCommand("ovs-vsctl", args...)
	if err != nil {
		return err
	}

	return nil
}

// BridgePortDelete deletes a port from the bridge (if already detached does nothing).
func (o *OVS) BridgePortDelete(bridgeName string, portName string) error {
	_, err := shared.RunCommand("ovs-vsctl", "--if-exists", "del-port", bridgeName, portName)
	if err != nil {
		return err
	}

	return nil
}

// BridgePortSet sets port options.
func (o *OVS) BridgePortSet(portName string, options ...string) error {
	_, err := shared.RunCommand("ovs-vsctl", append([]string{"set", "port", portName}, options...)...)
	if err != nil {
		return err
	}

	return nil
}

// InterfaceAssociateOVNSwitchPort removes any existing OVS ports associated to the specified ovnSwitchPortName
// and then associates the specified interfaceName to the OVN switch port.
func (o *OVS) InterfaceAssociateOVNSwitchPort(interfaceName string, ovnSwitchPortName OVNSwitchPort) error {
	// Clear existing ports that were formerly associated to ovnSwitchPortName.
	existingPorts, err := shared.RunCommand("ovs-vsctl", "--format=csv", "--no-headings", "--data=bare", "--colum=name", "find", "interface", fmt.Sprintf("external-ids:iface-id=%s", string(ovnSwitchPortName)))
	if err != nil {
		return err
	}

	existingPorts = strings.TrimSpace(existingPorts)
	if existingPorts != "" {
		for _, port := range strings.Split(existingPorts, "\n") {
			_, err = shared.RunCommand("ovs-vsctl", "del-port", port)
			if err != nil {
				return err
			}

			// Atempt to remove port, but don't fail if doesn't exist or can't be removed, at least
			// the OVS association has been successfully removed, so the new port being added next
			// won't fail to work properly.
			shared.RunCommand("ip", "link", "del", port)
		}
	}

	_, err = shared.RunCommand("ovs-vsctl", "set", "interface", interfaceName, fmt.Sprintf("external_ids:iface-id=%s", string(ovnSwitchPortName)))
	if err != nil {
		return err
	}

	return nil
}

// InterfaceAssociatedOVNSwitchPort returns the OVN switch port associated to the OVS interface.
func (o *OVS) InterfaceAssociatedOVNSwitchPort(interfaceName string) (OVNSwitchPort, error) {
	ovnSwitchPort, err := shared.RunCommand("ovs-vsctl", "get", "interface", interfaceName, "external_ids:iface-id")
	if err != nil {
		return "", err
	}

	return OVNSwitchPort(strings.TrimSpace(ovnSwitchPort)), nil
}

// ChassisID returns the local chassis ID.
func (o *OVS) ChassisID() (string, error) {
	// ovs-vsctl's get command doesn't support its --format flag, so we always get the output quoted.
	// However ovs-vsctl's find and list commands don't support retrieving a single column's map field.
	// And ovs-vsctl's JSON output is unfriendly towards statically typed languages as it mixes data types
	// in a slice. So stick with "get" command and use Go's strconv.Unquote to return the actual values.
	chassisID, err := shared.RunCommand("ovs-vsctl", "get", "open_vswitch", ".", "external_ids:system-id")
	if err != nil {
		return "", err
	}

	chassisID = strings.TrimSpace(chassisID)
	chassisID, err = strconv.Unquote(chassisID)
	if err != nil {
		return "", err
	}

	return chassisID, nil
}

// OVNEncapIP returns the enscapsulation IP used for OVN underlay tunnels.
func (o *OVS) OVNEncapIP() (net.IP, error) {
	// ovs-vsctl's get command doesn't support its --format flag, so we always get the output quoted.
	// However ovs-vsctl's find and list commands don't support retrieving a single column's map field.
	// And ovs-vsctl's JSON output is unfriendly towards statically typed languages as it mixes data types
	// in a slice. So stick with "get" command and use Go's strconv.Unquote to return the actual values.
	encapIPStr, err := shared.RunCommand("ovs-vsctl", "get", "open_vswitch", ".", "external_ids:ovn-encap-ip")
	if err != nil {
		return nil, err
	}

	encapIPStr = strings.TrimSpace(encapIPStr)
	encapIPStr, err = strconv.Unquote(encapIPStr)
	if err != nil {
		return nil, err
	}

	encapIP := net.ParseIP(encapIPStr)
	if encapIP == nil {
		return nil, fmt.Errorf("Invalid ovn-encap-ip address")
	}

	return encapIP, nil
}

// OVNBridgeMappings gets the current OVN bridge mappings.
func (o *OVS) OVNBridgeMappings(bridgeName string) ([]string, error) {
	// ovs-vsctl's get command doesn't support its --format flag, so we always get the output quoted.
	// However ovs-vsctl's find and list commands don't support retrieving a single column's map field.
	// And ovs-vsctl's JSON output is unfriendly towards statically typed languages as it mixes data types
	// in a slice. So stick with "get" command and use Go's strconv.Unquote to return the actual values.
	mappings, err := shared.RunCommand("ovs-vsctl", "--if-exists", "get", "open_vswitch", ".", "external-ids:ovn-bridge-mappings")
	if err != nil {
		return nil, err
	}

	mappings = strings.TrimSpace(mappings)
	if mappings == "" {
		return []string{}, nil
	}

	mappings, err = strconv.Unquote(mappings)
	if err != nil {
		return nil, err
	}

	return strings.SplitN(mappings, ",", -1), nil
}

// OVNBridgeMappingAdd appends an OVN bridge mapping between an OVS bridge and the logical provider name.
func (o *OVS) OVNBridgeMappingAdd(bridgeName string, providerName string) error {
	ovnBridgeMappingMutex.Lock()
	defer ovnBridgeMappingMutex.Unlock()

	mappings, err := o.OVNBridgeMappings(bridgeName)
	if err != nil {
		return err
	}

	newMapping := fmt.Sprintf("%s:%s", providerName, bridgeName)
	for _, mapping := range mappings {
		if mapping == newMapping {
			return nil // Mapping is already present, nothing to do.
		}
	}

	mappings = append(mappings, newMapping)

	// Set new mapping string back into OVS database.
	_, err = shared.RunCommand("ovs-vsctl", "set", "open_vswitch", ".", fmt.Sprintf("external-ids:ovn-bridge-mappings=%s", strings.Join(mappings, ",")))
	if err != nil {
		return err
	}

	return nil
}

// OVNBridgeMappingDelete deletes an OVN bridge mapping between an OVS bridge and the logical provider name.
func (o *OVS) OVNBridgeMappingDelete(bridgeName string, providerName string) error {
	ovnBridgeMappingMutex.Lock()
	defer ovnBridgeMappingMutex.Unlock()

	mappings, err := o.OVNBridgeMappings(bridgeName)
	if err != nil {
		return err
	}

	changed := false
	newMappings := make([]string, 0, len(mappings))
	matchMapping := fmt.Sprintf("%s:%s", providerName, bridgeName)
	for _, mapping := range mappings {
		if mapping != matchMapping {
			newMappings = append(newMappings, mapping)
		} else {
			changed = true
		}
	}

	if changed {
		if len(newMappings) < 1 {
			// Remove mapping key in OVS database.
			_, err = shared.RunCommand("ovs-vsctl", "remove", "open_vswitch", ".", "external-ids", "ovn-bridge-mappings")
			if err != nil {
				return err
			}
		} else {
			// Set updated mapping string back into OVS database.
			_, err = shared.RunCommand("ovs-vsctl", "set", "open_vswitch", ".", fmt.Sprintf("external-ids:ovn-bridge-mappings=%s", strings.Join(newMappings, ",")))
			if err != nil {
				return err
			}
		}
	}

	return nil
}

// BridgePortList returns a list of ports that are connected to the bridge.
func (o *OVS) BridgePortList(bridgeName string) ([]string, error) {
	// Clear existing ports that were formerly associated to ovnSwitchPortName.
	portString, err := shared.RunCommand("ovs-vsctl", "list-ports", bridgeName)
	if err != nil {
		return nil, err
	}

	ports := []string{}

	portString = strings.TrimSpace(portString)
	if portString != "" {
		for _, port := range strings.Split(portString, "\n") {
			ports = append(ports, strings.TrimSpace(port))
		}
	}

	return ports, nil
}
<commit_msg>lxd/network/openvswitch/ovs: Adds TCP flag constants<commit_after>package openvswitch

import (
	"fmt"
	"net"
	"os/exec"
	"strconv"
	"strings"
	"sync"

	"github.com/lxc/lxd/shared"
)

// ovnBridgeMappingMutex locks access to read/write external-ids:ovn-bridge-mappings.
var ovnBridgeMappingMutex sync.Mutex

// OVS TCP Flags from OVS lib/packets.h.
const (
	TCPFIN = 0x001
	TCPSYN = 0x002
	TCPRST = 0x004
	TCPPSH = 0x008
	TCPACK = 0x010
	TCPURG = 0x020
	TCPECE = 0x040
	TCPCWR = 0x080
	TCPNS  = 0x100
)

// NewOVS initialises new OVS wrapper.
func NewOVS() *OVS {
	return &OVS{}
}

// OVS command wrapper.
type OVS struct{}

// Installed returns true if OVS tools are installed.
func (o *OVS) Installed() bool {
	_, err := exec.LookPath("ovs-vsctl")
	return err == nil
}

// BridgeExists returns true if OVS bridge exists.
func (o *OVS) BridgeExists(bridgeName string) (bool, error) {
	_, err := shared.RunCommand("ovs-vsctl", "br-exists", bridgeName)
	if err != nil {
		runErr, ok := err.(shared.RunError)
		if ok {
			exitError, ok := runErr.Err.(*exec.ExitError)

			// ovs-vsctl manpage says that br-exists exits with code 2 if bridge doesn't exist.
			if ok && exitError.ExitCode() == 2 {
				return false, nil
			}
		}

		return false, err
	}

	return true, nil
}

// BridgeAdd adds an OVS bridge.
func (o *OVS) BridgeAdd(bridgeName string, mayExist bool) error {
	args := []string{}

	if mayExist {
		args = append(args, "--may-exist")
	}

	args = append(args, "add-br", bridgeName)

	_, err := shared.RunCommand("ovs-vsctl", args...)
	if err != nil {
		return err
	}

	return nil
}

// BridgeDelete deletes an OVS bridge.
func (o *OVS) BridgeDelete(bridgeName string) error {
	_, err := shared.RunCommand("ovs-vsctl", "del-br", bridgeName)
	if err != nil {
		return err
	}

	return nil
}

// BridgePortAdd adds a port to the bridge (if already attached does nothing).
func (o *OVS) BridgePortAdd(bridgeName string, portName string, mayExist bool) error {
	args := []string{}

	if mayExist {
		args = append(args, "--may-exist")
	}

	args = append(args, "add-port", bridgeName, portName)

	_, err := shared.RunCommand("ovs-vsctl", args...)
	if err != nil {
		return err
	}

	return nil
}

// BridgePortDelete deletes a port from the bridge (if already detached does nothing).
func (o *OVS) BridgePortDelete(bridgeName string, portName string) error {
	_, err := shared.RunCommand("ovs-vsctl", "--if-exists", "del-port", bridgeName, portName)
	if err != nil {
		return err
	}

	return nil
}

// BridgePortSet sets port options.
func (o *OVS) BridgePortSet(portName string, options ...string) error {
	_, err := shared.RunCommand("ovs-vsctl", append([]string{"set", "port", portName}, options...)...)
	if err != nil {
		return err
	}

	return nil
}

// InterfaceAssociateOVNSwitchPort removes any existing OVS ports associated to the specified ovnSwitchPortName
// and then associates the specified interfaceName to the OVN switch port.
func (o *OVS) InterfaceAssociateOVNSwitchPort(interfaceName string, ovnSwitchPortName OVNSwitchPort) error {
	// Clear existing ports that were formerly associated to ovnSwitchPortName.
	existingPorts, err := shared.RunCommand("ovs-vsctl", "--format=csv", "--no-headings", "--data=bare", "--colum=name", "find", "interface", fmt.Sprintf("external-ids:iface-id=%s", string(ovnSwitchPortName)))
	if err != nil {
		return err
	}

	existingPorts = strings.TrimSpace(existingPorts)
	if existingPorts != "" {
		for _, port := range strings.Split(existingPorts, "\n") {
			_, err = shared.RunCommand("ovs-vsctl", "del-port", port)
			if err != nil {
				return err
			}

			// Atempt to remove port, but don't fail if doesn't exist or can't be removed, at least
			// the OVS association has been successfully removed, so the new port being added next
			// won't fail to work properly.
			shared.RunCommand("ip", "link", "del", port)
		}
	}

	_, err = shared.RunCommand("ovs-vsctl", "set", "interface", interfaceName, fmt.Sprintf("external_ids:iface-id=%s", string(ovnSwitchPortName)))
	if err != nil {
		return err
	}

	return nil
}

// InterfaceAssociatedOVNSwitchPort returns the OVN switch port associated to the OVS interface.
func (o *OVS) InterfaceAssociatedOVNSwitchPort(interfaceName string) (OVNSwitchPort, error) {
	ovnSwitchPort, err := shared.RunCommand("ovs-vsctl", "get", "interface", interfaceName, "external_ids:iface-id")
	if err != nil {
		return "", err
	}

	return OVNSwitchPort(strings.TrimSpace(ovnSwitchPort)), nil
}

// ChassisID returns the local chassis ID.
func (o *OVS) ChassisID() (string, error) {
	// ovs-vsctl's get command doesn't support its --format flag, so we always get the output quoted.
	// However ovs-vsctl's find and list commands don't support retrieving a single column's map field.
	// And ovs-vsctl's JSON output is unfriendly towards statically typed languages as it mixes data types
	// in a slice. So stick with "get" command and use Go's strconv.Unquote to return the actual values.
	chassisID, err := shared.RunCommand("ovs-vsctl", "get", "open_vswitch", ".", "external_ids:system-id")
	if err != nil {
		return "", err
	}

	chassisID = strings.TrimSpace(chassisID)
	chassisID, err = strconv.Unquote(chassisID)
	if err != nil {
		return "", err
	}

	return chassisID, nil
}

// OVNEncapIP returns the enscapsulation IP used for OVN underlay tunnels.
func (o *OVS) OVNEncapIP() (net.IP, error) {
	// ovs-vsctl's get command doesn't support its --format flag, so we always get the output quoted.
	// However ovs-vsctl's find and list commands don't support retrieving a single column's map field.
	// And ovs-vsctl's JSON output is unfriendly towards statically typed languages as it mixes data types
	// in a slice. So stick with "get" command and use Go's strconv.Unquote to return the actual values.
	encapIPStr, err := shared.RunCommand("ovs-vsctl", "get", "open_vswitch", ".", "external_ids:ovn-encap-ip")
	if err != nil {
		return nil, err
	}

	encapIPStr = strings.TrimSpace(encapIPStr)
	encapIPStr, err = strconv.Unquote(encapIPStr)
	if err != nil {
		return nil, err
	}

	encapIP := net.ParseIP(encapIPStr)
	if encapIP == nil {
		return nil, fmt.Errorf("Invalid ovn-encap-ip address")
	}

	return encapIP, nil
}

// OVNBridgeMappings gets the current OVN bridge mappings.
func (o *OVS) OVNBridgeMappings(bridgeName string) ([]string, error) {
	// ovs-vsctl's get command doesn't support its --format flag, so we always get the output quoted.
	// However ovs-vsctl's find and list commands don't support retrieving a single column's map field.
	// And ovs-vsctl's JSON output is unfriendly towards statically typed languages as it mixes data types
	// in a slice. So stick with "get" command and use Go's strconv.Unquote to return the actual values.
	mappings, err := shared.RunCommand("ovs-vsctl", "--if-exists", "get", "open_vswitch", ".", "external-ids:ovn-bridge-mappings")
	if err != nil {
		return nil, err
	}

	mappings = strings.TrimSpace(mappings)
	if mappings == "" {
		return []string{}, nil
	}

	mappings, err = strconv.Unquote(mappings)
	if err != nil {
		return nil, err
	}

	return strings.SplitN(mappings, ",", -1), nil
}

// OVNBridgeMappingAdd appends an OVN bridge mapping between an OVS bridge and the logical provider name.
func (o *OVS) OVNBridgeMappingAdd(bridgeName string, providerName string) error {
	ovnBridgeMappingMutex.Lock()
	defer ovnBridgeMappingMutex.Unlock()

	mappings, err := o.OVNBridgeMappings(bridgeName)
	if err != nil {
		return err
	}

	newMapping := fmt.Sprintf("%s:%s", providerName, bridgeName)
	for _, mapping := range mappings {
		if mapping == newMapping {
			return nil // Mapping is already present, nothing to do.
		}
	}

	mappings = append(mappings, newMapping)

	// Set new mapping string back into OVS database.
	_, err = shared.RunCommand("ovs-vsctl", "set", "open_vswitch", ".", fmt.Sprintf("external-ids:ovn-bridge-mappings=%s", strings.Join(mappings, ",")))
	if err != nil {
		return err
	}

	return nil
}

// OVNBridgeMappingDelete deletes an OVN bridge mapping between an OVS bridge and the logical provider name.
func (o *OVS) OVNBridgeMappingDelete(bridgeName string, providerName string) error {
	ovnBridgeMappingMutex.Lock()
	defer ovnBridgeMappingMutex.Unlock()

	mappings, err := o.OVNBridgeMappings(bridgeName)
	if err != nil {
		return err
	}

	changed := false
	newMappings := make([]string, 0, len(mappings))
	matchMapping := fmt.Sprintf("%s:%s", providerName, bridgeName)
	for _, mapping := range mappings {
		if mapping != matchMapping {
			newMappings = append(newMappings, mapping)
		} else {
			changed = true
		}
	}

	if changed {
		if len(newMappings) < 1 {
			// Remove mapping key in OVS database.
			_, err = shared.RunCommand("ovs-vsctl", "remove", "open_vswitch", ".", "external-ids", "ovn-bridge-mappings")
			if err != nil {
				return err
			}
		} else {
			// Set updated mapping string back into OVS database.
			_, err = shared.RunCommand("ovs-vsctl", "set", "open_vswitch", ".", fmt.Sprintf("external-ids:ovn-bridge-mappings=%s", strings.Join(newMappings, ",")))
			if err != nil {
				return err
			}
		}
	}

	return nil
}

// BridgePortList returns a list of ports that are connected to the bridge.
func (o *OVS) BridgePortList(bridgeName string) ([]string, error) {
	// Clear existing ports that were formerly associated to ovnSwitchPortName.
	portString, err := shared.RunCommand("ovs-vsctl", "list-ports", bridgeName)
	if err != nil {
		return nil, err
	}

	ports := []string{}

	portString = strings.TrimSpace(portString)
	if portString != "" {
		for _, port := range strings.Split(portString, "\n") {
			ports = append(ports, strings.TrimSpace(port))
		}
	}

	return ports, nil
}
<|endoftext|>


<commit_before>package fasthttp

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"runtime"
	"sync/atomic"
	"testing"
	"time"
)

var defaultClientsCount = runtime.NumCPU()

func BenchmarkServerGet1ReqPerConn(b *testing.B) {
	benchmarkServerGet(b, defaultClientsCount, 1)
}

func BenchmarkServerGet2ReqPerConn(b *testing.B) {
	benchmarkServerGet(b, defaultClientsCount, 2)
}

func BenchmarkServerGet10ReqPerConn(b *testing.B) {
	benchmarkServerGet(b, defaultClientsCount, 10)
}

func BenchmarkServerGet10000ReqPerConn(b *testing.B) {
	benchmarkServerGet(b, defaultClientsCount, 10000)
}

func BenchmarkNetHTTPServerGet1ReqPerConn(b *testing.B) {
	benchmarkNetHTTPServerGet(b, defaultClientsCount, 1)
}

func BenchmarkNetHTTPServerGet2ReqPerConn(b *testing.B) {
	benchmarkNetHTTPServerGet(b, defaultClientsCount, 2)
}

func BenchmarkNetHTTPServerGet10ReqPerConn(b *testing.B) {
	benchmarkNetHTTPServerGet(b, defaultClientsCount, 10)
}

func BenchmarkNetHTTPServerGet10000ReqPerConn(b *testing.B) {
	benchmarkNetHTTPServerGet(b, defaultClientsCount, 10000)
}

func BenchmarkServerPost1ReqPerConn(b *testing.B) {
	benchmarkServerPost(b, defaultClientsCount, 1)
}

func BenchmarkServerPost2ReqPerConn(b *testing.B) {
	benchmarkServerPost(b, defaultClientsCount, 2)
}

func BenchmarkServerPost10ReqPerConn(b *testing.B) {
	benchmarkServerPost(b, defaultClientsCount, 10)
}

func BenchmarkServerPost10KReqPerConn(b *testing.B) {
	benchmarkServerPost(b, defaultClientsCount, 10000)
}

func BenchmarkNetHTTPServerPost1ReqPerConn(b *testing.B) {
	benchmarkNetHTTPServerPost(b, defaultClientsCount, 1)
}

func BenchmarkNetHTTPServerPost2ReqPerConn(b *testing.B) {
	benchmarkNetHTTPServerPost(b, defaultClientsCount, 2)
}

func BenchmarkNetHTTPServerPost10ReqPerConn(b *testing.B) {
	benchmarkNetHTTPServerPost(b, defaultClientsCount, 10)
}

func BenchmarkNetHTTPServerPost10KReqPerConn(b *testing.B) {
	benchmarkNetHTTPServerPost(b, defaultClientsCount, 10000)
}

func BenchmarkServerGet1ReqPerConn1KClients(b *testing.B) {
	benchmarkServerGet(b, 1000, 1)
}

func BenchmarkServerGet2ReqPerConn1KClients(b *testing.B) {
	benchmarkServerGet(b, 1000, 2)
}

func BenchmarkServerGet10ReqPerConn1KClients(b *testing.B) {
	benchmarkServerGet(b, 1000, 10)
}

func BenchmarkServerGet10KReqPerConn1KClients(b *testing.B) {
	benchmarkServerGet(b, 1000, 10000)
}

func BenchmarkNetHTTPServerGet1ReqPerConn1KClients(b *testing.B) {
	benchmarkNetHTTPServerGet(b, 1000, 1)
}

func BenchmarkNetHTTPServerGet2ReqPerConn1KClients(b *testing.B) {
	benchmarkNetHTTPServerGet(b, 1000, 2)
}

func BenchmarkNetHTTPServerGet10ReqPerConn1KClients(b *testing.B) {
	benchmarkNetHTTPServerGet(b, 1000, 10)
}

func BenchmarkNetHTTPServerGet10KReqPerConn1KClients(b *testing.B) {
	benchmarkNetHTTPServerGet(b, 1000, 10000)
}

func BenchmarkServerMaxConnsPerIP(b *testing.B) {
	clientsCount := 1000
	requestsPerConn := 10
	ch := make(chan struct{}, b.N)
	s := &Server{
		Handler: func(ctx *RequestCtx) {
			ctx.Success("foobar", []byte("123"))
			registerServedRequest(b, ch)
		},
		MaxConnsPerIP: clientsCount * 2,
	}
	req := "GET /foo HTTP/1.1\r\nHost: google.com\r\n\r\n"
	benchmarkServer(b, &testServer{s, clientsCount}, clientsCount, requestsPerConn, req)
	verifyRequestsServed(b, ch)
}

func BenchmarkServerTimeoutError(b *testing.B) {
	clientsCount := 1
	requestsPerConn := 10
	ch := make(chan struct{}, b.N)
	n := uint32(0)
	s := &Server{
		Handler: func(ctx *RequestCtx) {
			if atomic.AddUint32(&n, 1)&7 == 0 {
				ctx.TimeoutError("xxx")
				go func() {
					ctx.Success("foobar", []byte("123"))
				}()
			} else {
				ctx.Success("foobar", []byte("123"))
			}
			registerServedRequest(b, ch)
		},
	}
	req := "GET /foo HTTP/1.1\r\nHost: google.com\r\n\r\n"
	benchmarkServer(b, &testServer{s, clientsCount}, clientsCount, requestsPerConn, req)
	verifyRequestsServed(b, ch)
}

type fakeServerConn struct {
	net.TCPConn
	ln            *fakeListener
	requestsCount int
	closed        uint32
}

func (c *fakeServerConn) Read(b []byte) (int, error) {
	nn := 0
	for len(b) > len(c.ln.request) {
		if c.requestsCount == 0 {
			if nn == 0 {
				return 0, io.EOF
			}
			return nn, nil
		}
		n := copy(b, c.ln.request)
		b = b[n:]
		nn += n
		c.requestsCount--
	}
	if nn == 0 {
		panic("server has too small buffer")
	}
	return nn, nil
}

func (c *fakeServerConn) Write(b []byte) (int, error) {
	return len(b), nil
}

var fakeAddr = net.TCPAddr{
	IP:   []byte{1, 2, 3, 4},
	Port: 12345,
}

func (c *fakeServerConn) RemoteAddr() net.Addr {
	return &fakeAddr
}

func (c *fakeServerConn) Close() error {
	if atomic.AddUint32(&c.closed, 1) == 1 {
		c.ln.ch <- c
	}
	return nil
}

func (c *fakeServerConn) SetReadDeadline(t time.Time) error {
	return nil
}

func (c *fakeServerConn) SetWriteDeadline(t time.Time) error {
	return nil
}

type fakeListener struct {
	requestsCount   int
	requestsPerConn int
	request         []byte
	ch              chan *fakeServerConn
	done            chan struct{}
}

func (ln *fakeListener) Accept() (net.Conn, error) {
	if ln.requestsCount == 0 {
		for len(ln.ch) < cap(ln.ch) {
			time.Sleep(10 * time.Millisecond)
		}
		close(ln.done)
		return nil, io.EOF
	}
	requestsCount := ln.requestsPerConn
	if requestsCount > ln.requestsCount {
		requestsCount = ln.requestsCount
	}
	ln.requestsCount -= requestsCount

	c := <-ln.ch
	c.requestsCount = requestsCount
	c.closed = 0

	return c, nil
}

func (ln *fakeListener) Close() error {
	return nil
}

func (ln *fakeListener) Addr() net.Addr {
	return &fakeAddr
}

func newFakeListener(requestsCount, clientsCount, requestsPerConn int, request string) *fakeListener {
	ln := &fakeListener{
		requestsCount:   requestsCount,
		requestsPerConn: requestsPerConn,
		request:         []byte(request),
		ch:              make(chan *fakeServerConn, clientsCount),
		done:            make(chan struct{}),
	}
	for i := 0; i < clientsCount; i++ {
		ln.ch <- &fakeServerConn{
			ln: ln,
		}
	}
	return ln
}

var (
	fakeResponse = []byte("Hello, world!")
	getRequest   = "GET /foobar?baz HTTP/1.1\r\nHost: google.com\r\nUser-Agent: aaa/bbb/ccc/ddd/eee Firefox Chrome MSIE Opera\r\n" +
		"Referer: http://xxx.com/aaa?bbb=ccc\r\n\r\n"
	postRequest = fmt.Sprintf("POST /foobar?baz HTTP/1.1\r\nHost: google.com\r\nContent-Type: foo/bar\r\nContent-Length: %d\r\n"+
		"User-Agent: Opera Chrome MSIE Firefox and other/1.2.34\r\nReferer: http://google.com/aaaa/bbb/ccc\r\n\r\n%s",
		len(fakeResponse), fakeResponse)
)

func benchmarkServerGet(b *testing.B, clientsCount, requestsPerConn int) {
	ch := make(chan struct{}, b.N)
	s := &Server{
		Handler: func(ctx *RequestCtx) {
			if !ctx.Request.Header.IsMethodGet() {
				b.Fatalf("Unexpected request method: %s", ctx.Request.Header.Method)
			}
			ctx.Success("text/plain", fakeResponse)
			registerServedRequest(b, ch)
		},
	}
	benchmarkServer(b, &testServer{s, clientsCount}, clientsCount, requestsPerConn, getRequest)
	verifyRequestsServed(b, ch)
}

func benchmarkNetHTTPServerGet(b *testing.B, clientsCount, requestsPerConn int) {
	ch := make(chan struct{}, b.N)
	s := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			if req.Method != "GET" {
				b.Fatalf("Unexpected request method: %s", req.Method)
			}
			w.Header().Set("Content-Type", "text/plain")
			w.Write(fakeResponse)
			registerServedRequest(b, ch)
		}),
	}
	benchmarkServer(b, s, clientsCount, requestsPerConn, getRequest)
	verifyRequestsServed(b, ch)
}

func benchmarkServerPost(b *testing.B, clientsCount, requestsPerConn int) {
	ch := make(chan struct{}, b.N)
	s := &Server{
		Handler: func(ctx *RequestCtx) {
			if !ctx.Request.Header.IsMethodPost() {
				b.Fatalf("Unexpected request method: %s", ctx.Request.Header.Method)
			}
			body := ctx.Request.Body
			if !bytes.Equal(body, fakeResponse) {
				b.Fatalf("Unexpected body %q. Expected %q", body, fakeResponse)
			}
			ctx.Success("text/plain", body)
			registerServedRequest(b, ch)
		},
	}
	benchmarkServer(b, &testServer{s, clientsCount}, clientsCount, requestsPerConn, postRequest)
	verifyRequestsServed(b, ch)
}

func benchmarkNetHTTPServerPost(b *testing.B, clientsCount, requestsPerConn int) {
	ch := make(chan struct{}, b.N)
	s := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			if req.Method != "POST" {
				b.Fatalf("Unexpected request method: %s", req.Method)
			}
			body, err := ioutil.ReadAll(req.Body)
			if err != nil {
				b.Fatalf("Unexpected error: %s", err)
			}
			req.Body.Close()
			if !bytes.Equal(body, fakeResponse) {
				b.Fatalf("Unexpected body %q. Expected %q", body, fakeResponse)
			}
			w.Header().Set("Content-Type", "text/plain")
			w.Write(body)
			registerServedRequest(b, ch)
		}),
	}
	benchmarkServer(b, s, clientsCount, requestsPerConn, postRequest)
	verifyRequestsServed(b, ch)
}

func registerServedRequest(b *testing.B, ch chan<- struct{}) {
	select {
	case ch <- struct{}{}:
	default:
		b.Fatalf("More than %d requests served", cap(ch))
	}
}

func verifyRequestsServed(b *testing.B, ch <-chan struct{}) {
	requestsServed := 0
	for len(ch) > 0 {
		<-ch
		requestsServed++
	}
	requestsSent := b.N
	for requestsServed < requestsSent {
		select {
		case <-ch:
			requestsServed++
		case <-time.After(100 * time.Millisecond):
			b.Fatalf("Unexpected number of requests served %d. Expected %d", requestsServed, requestsSent)
		}
	}
}

type realServer interface {
	Serve(ln net.Listener) error
}

type testServer struct {
	*Server
	Concurrency int
}

func (s *testServer) Serve(ln net.Listener) error {
	if s.Concurrency < runtime.NumCPU() {
		s.Concurrency = runtime.NumCPU()
	}
	return s.Server.ServeConcurrency(ln, s.Concurrency)
}

func benchmarkServer(b *testing.B, s realServer, clientsCount, requestsPerConn int, request string) {
	ln := newFakeListener(b.N, clientsCount, requestsPerConn, request)
	ch := make(chan struct{})
	go func() {
		s.Serve(ln)
		ch <- struct{}{}
	}()

	<-ln.done

	select {
	case <-ch:
	case <-time.After(10 * time.Second):
		b.Fatalf("Server.Serve() didn't stop")
	}
}
<commit_msg>Properly handle the case when servers read data by small chunks<commit_after>package fasthttp

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"runtime"
	"sync/atomic"
	"testing"
	"time"
)

var defaultClientsCount = runtime.NumCPU()

func BenchmarkServerGet1ReqPerConn(b *testing.B) {
	benchmarkServerGet(b, defaultClientsCount, 1)
}

func BenchmarkServerGet2ReqPerConn(b *testing.B) {
	benchmarkServerGet(b, defaultClientsCount, 2)
}

func BenchmarkServerGet10ReqPerConn(b *testing.B) {
	benchmarkServerGet(b, defaultClientsCount, 10)
}

func BenchmarkServerGet10000ReqPerConn(b *testing.B) {
	benchmarkServerGet(b, defaultClientsCount, 10000)
}

func BenchmarkNetHTTPServerGet1ReqPerConn(b *testing.B) {
	benchmarkNetHTTPServerGet(b, defaultClientsCount, 1)
}

func BenchmarkNetHTTPServerGet2ReqPerConn(b *testing.B) {
	benchmarkNetHTTPServerGet(b, defaultClientsCount, 2)
}

func BenchmarkNetHTTPServerGet10ReqPerConn(b *testing.B) {
	benchmarkNetHTTPServerGet(b, defaultClientsCount, 10)
}

func BenchmarkNetHTTPServerGet10000ReqPerConn(b *testing.B) {
	benchmarkNetHTTPServerGet(b, defaultClientsCount, 10000)
}

func BenchmarkServerPost1ReqPerConn(b *testing.B) {
	benchmarkServerPost(b, defaultClientsCount, 1)
}

func BenchmarkServerPost2ReqPerConn(b *testing.B) {
	benchmarkServerPost(b, defaultClientsCount, 2)
}

func BenchmarkServerPost10ReqPerConn(b *testing.B) {
	benchmarkServerPost(b, defaultClientsCount, 10)
}

func BenchmarkServerPost10KReqPerConn(b *testing.B) {
	benchmarkServerPost(b, defaultClientsCount, 10000)
}

func BenchmarkNetHTTPServerPost1ReqPerConn(b *testing.B) {
	benchmarkNetHTTPServerPost(b, defaultClientsCount, 1)
}

func BenchmarkNetHTTPServerPost2ReqPerConn(b *testing.B) {
	benchmarkNetHTTPServerPost(b, defaultClientsCount, 2)
}

func BenchmarkNetHTTPServerPost10ReqPerConn(b *testing.B) {
	benchmarkNetHTTPServerPost(b, defaultClientsCount, 10)
}

func BenchmarkNetHTTPServerPost10KReqPerConn(b *testing.B) {
	benchmarkNetHTTPServerPost(b, defaultClientsCount, 10000)
}

func BenchmarkServerGet1ReqPerConn1KClients(b *testing.B) {
	benchmarkServerGet(b, 1000, 1)
}

func BenchmarkServerGet2ReqPerConn1KClients(b *testing.B) {
	benchmarkServerGet(b, 1000, 2)
}

func BenchmarkServerGet10ReqPerConn1KClients(b *testing.B) {
	benchmarkServerGet(b, 1000, 10)
}

func BenchmarkServerGet10KReqPerConn1KClients(b *testing.B) {
	benchmarkServerGet(b, 1000, 10000)
}

func BenchmarkNetHTTPServerGet1ReqPerConn1KClients(b *testing.B) {
	benchmarkNetHTTPServerGet(b, 1000, 1)
}

func BenchmarkNetHTTPServerGet2ReqPerConn1KClients(b *testing.B) {
	benchmarkNetHTTPServerGet(b, 1000, 2)
}

func BenchmarkNetHTTPServerGet10ReqPerConn1KClients(b *testing.B) {
	benchmarkNetHTTPServerGet(b, 1000, 10)
}

func BenchmarkNetHTTPServerGet10KReqPerConn1KClients(b *testing.B) {
	benchmarkNetHTTPServerGet(b, 1000, 10000)
}

func BenchmarkServerMaxConnsPerIP(b *testing.B) {
	clientsCount := 1000
	requestsPerConn := 10
	ch := make(chan struct{}, b.N)
	s := &Server{
		Handler: func(ctx *RequestCtx) {
			ctx.Success("foobar", []byte("123"))
			registerServedRequest(b, ch)
		},
		MaxConnsPerIP: clientsCount * 2,
	}
	req := "GET /foo HTTP/1.1\r\nHost: google.com\r\n\r\n"
	benchmarkServer(b, &testServer{s, clientsCount}, clientsCount, requestsPerConn, req)
	verifyRequestsServed(b, ch)
}

func BenchmarkServerTimeoutError(b *testing.B) {
	clientsCount := 1
	requestsPerConn := 10
	ch := make(chan struct{}, b.N)
	n := uint32(0)
	s := &Server{
		Handler: func(ctx *RequestCtx) {
			if atomic.AddUint32(&n, 1)&7 == 0 {
				ctx.TimeoutError("xxx")
				go func() {
					ctx.Success("foobar", []byte("123"))
				}()
			} else {
				ctx.Success("foobar", []byte("123"))
			}
			registerServedRequest(b, ch)
		},
	}
	req := "GET /foo HTTP/1.1\r\nHost: google.com\r\n\r\n"
	benchmarkServer(b, &testServer{s, clientsCount}, clientsCount, requestsPerConn, req)
	verifyRequestsServed(b, ch)
}

type fakeServerConn struct {
	net.TCPConn
	ln            *fakeListener
	requestsCount int
	pos           int
	closed        uint32
}

func (c *fakeServerConn) Read(b []byte) (int, error) {
	nn := 0
	reqLen := len(c.ln.request)
	for len(b) > 0 {
		if c.requestsCount == 0 {
			if nn == 0 {
				return 0, io.EOF
			}
			return nn, nil
		}
		pos := c.pos % reqLen
		n := copy(b, c.ln.request[pos:])
		b = b[n:]
		nn += n
		c.pos += n
		if n+pos == reqLen {
			c.requestsCount--
		}
	}
	return nn, nil
}

func (c *fakeServerConn) Write(b []byte) (int, error) {
	return len(b), nil
}

var fakeAddr = net.TCPAddr{
	IP:   []byte{1, 2, 3, 4},
	Port: 12345,
}

func (c *fakeServerConn) RemoteAddr() net.Addr {
	return &fakeAddr
}

func (c *fakeServerConn) Close() error {
	if atomic.AddUint32(&c.closed, 1) == 1 {
		c.ln.ch <- c
	}
	return nil
}

func (c *fakeServerConn) SetReadDeadline(t time.Time) error {
	return nil
}

func (c *fakeServerConn) SetWriteDeadline(t time.Time) error {
	return nil
}

type fakeListener struct {
	requestsCount   int
	requestsPerConn int
	request         []byte
	ch              chan *fakeServerConn
	done            chan struct{}
}

func (ln *fakeListener) Accept() (net.Conn, error) {
	if ln.requestsCount == 0 {
		for len(ln.ch) < cap(ln.ch) {
			time.Sleep(10 * time.Millisecond)
		}
		close(ln.done)
		return nil, io.EOF
	}
	requestsCount := ln.requestsPerConn
	if requestsCount > ln.requestsCount {
		requestsCount = ln.requestsCount
	}
	ln.requestsCount -= requestsCount

	c := <-ln.ch
	c.requestsCount = requestsCount
	c.closed = 0
	c.pos = 0

	return c, nil
}

func (ln *fakeListener) Close() error {
	return nil
}

func (ln *fakeListener) Addr() net.Addr {
	return &fakeAddr
}

func newFakeListener(requestsCount, clientsCount, requestsPerConn int, request string) *fakeListener {
	ln := &fakeListener{
		requestsCount:   requestsCount,
		requestsPerConn: requestsPerConn,
		request:         []byte(request),
		ch:              make(chan *fakeServerConn, clientsCount),
		done:            make(chan struct{}),
	}
	for i := 0; i < clientsCount; i++ {
		ln.ch <- &fakeServerConn{
			ln: ln,
		}
	}
	return ln
}

var (
	fakeResponse = []byte("Hello, world!")
	getRequest   = "GET /foobar?baz HTTP/1.1\r\nHost: google.com\r\nUser-Agent: aaa/bbb/ccc/ddd/eee Firefox Chrome MSIE Opera\r\n" +
		"Referer: http://xxx.com/aaa?bbb=ccc\r\n\r\n"
	postRequest = fmt.Sprintf("POST /foobar?baz HTTP/1.1\r\nHost: google.com\r\nContent-Type: foo/bar\r\nContent-Length: %d\r\n"+
		"User-Agent: Opera Chrome MSIE Firefox and other/1.2.34\r\nReferer: http://google.com/aaaa/bbb/ccc\r\n\r\n%s",
		len(fakeResponse), fakeResponse)
)

func benchmarkServerGet(b *testing.B, clientsCount, requestsPerConn int) {
	ch := make(chan struct{}, b.N)
	s := &Server{
		Handler: func(ctx *RequestCtx) {
			if !ctx.Request.Header.IsMethodGet() {
				b.Fatalf("Unexpected request method: %s", ctx.Request.Header.Method)
			}
			ctx.Success("text/plain", fakeResponse)
			registerServedRequest(b, ch)
		},
	}
	benchmarkServer(b, &testServer{s, clientsCount}, clientsCount, requestsPerConn, getRequest)
	verifyRequestsServed(b, ch)
}

func benchmarkNetHTTPServerGet(b *testing.B, clientsCount, requestsPerConn int) {
	ch := make(chan struct{}, b.N)
	s := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			if req.Method != "GET" {
				b.Fatalf("Unexpected request method: %s", req.Method)
			}
			w.Header().Set("Content-Type", "text/plain")
			w.Write(fakeResponse)
			registerServedRequest(b, ch)
		}),
	}
	benchmarkServer(b, s, clientsCount, requestsPerConn, getRequest)
	verifyRequestsServed(b, ch)
}

func benchmarkServerPost(b *testing.B, clientsCount, requestsPerConn int) {
	ch := make(chan struct{}, b.N)
	s := &Server{
		Handler: func(ctx *RequestCtx) {
			if !ctx.Request.Header.IsMethodPost() {
				b.Fatalf("Unexpected request method: %s", ctx.Request.Header.Method)
			}
			body := ctx.Request.Body
			if !bytes.Equal(body, fakeResponse) {
				b.Fatalf("Unexpected body %q. Expected %q", body, fakeResponse)
			}
			ctx.Success("text/plain", body)
			registerServedRequest(b, ch)
		},
	}
	benchmarkServer(b, &testServer{s, clientsCount}, clientsCount, requestsPerConn, postRequest)
	verifyRequestsServed(b, ch)
}

func benchmarkNetHTTPServerPost(b *testing.B, clientsCount, requestsPerConn int) {
	ch := make(chan struct{}, b.N)
	s := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			if req.Method != "POST" {
				b.Fatalf("Unexpected request method: %s", req.Method)
			}
			body, err := ioutil.ReadAll(req.Body)
			if err != nil {
				b.Fatalf("Unexpected error: %s", err)
			}
			req.Body.Close()
			if !bytes.Equal(body, fakeResponse) {
				b.Fatalf("Unexpected body %q. Expected %q", body, fakeResponse)
			}
			w.Header().Set("Content-Type", "text/plain")
			w.Write(body)
			registerServedRequest(b, ch)
		}),
	}
	benchmarkServer(b, s, clientsCount, requestsPerConn, postRequest)
	verifyRequestsServed(b, ch)
}

func registerServedRequest(b *testing.B, ch chan<- struct{}) {
	select {
	case ch <- struct{}{}:
	default:
		b.Fatalf("More than %d requests served", cap(ch))
	}
}

func verifyRequestsServed(b *testing.B, ch <-chan struct{}) {
	requestsServed := 0
	for len(ch) > 0 {
		<-ch
		requestsServed++
	}
	requestsSent := b.N
	for requestsServed < requestsSent {
		select {
		case <-ch:
			requestsServed++
		case <-time.After(100 * time.Millisecond):
			b.Fatalf("Unexpected number of requests served %d. Expected %d", requestsServed, requestsSent)
		}
	}
}

type realServer interface {
	Serve(ln net.Listener) error
}

type testServer struct {
	*Server
	Concurrency int
}

func (s *testServer) Serve(ln net.Listener) error {
	if s.Concurrency < runtime.NumCPU() {
		s.Concurrency = runtime.NumCPU()
	}
	return s.Server.ServeConcurrency(ln, s.Concurrency)
}

func benchmarkServer(b *testing.B, s realServer, clientsCount, requestsPerConn int, request string) {
	ln := newFakeListener(b.N, clientsCount, requestsPerConn, request)
	ch := make(chan struct{})
	go func() {
		s.Serve(ln)
		ch <- struct{}{}
	}()

	<-ln.done

	select {
	case <-ch:
	case <-time.After(10 * time.Second):
		b.Fatalf("Server.Serve() didn't stop")
	}
}
<|endoftext|>


<commit_before>package cloudpelican

// @author Robin Verlangen
// @todo Support bulk index requests
// Tool for logging data to CloudPelican directly from Go

// Imports
import (
    "net"
    "net/http"
    "net/url"
    "log"
    "sync"
    "time"
    "strconv"
    "os"
)

// Settings
var ENDPOINT string = "https://app.cloudpelican.com/api"
var TOKEN string = ""
var backendTimeout = time.Duration(5 * time.Second)
var debugMode bool = false
var maxBulkSize uint64 = uint64(100)
var hostname string

// Monitor drain status
var startCounter uint64 = uint64(0)
var startCounterMux sync.Mutex
var doneCounter uint64 = uint64(0)
var doneCounterMux sync.Mutex
var isDraining bool = false
var drained = make(chan bool);

// Log queue
var writeAheadBufferSize int = 1000
var writeAhead chan map[string]string = make(chan map[string]string, writeAheadBufferSize)
var writeAheadInit bool
var dropOnFullWriteAheadBuffer bool = true

// Set token
func SetToken(t string) {
    // Validate before setting
    validateToken(t)

    // Store
    TOKEN = t
}

// Set endpoint
func SetEndpoint(e string) {
    // Store
    ENDPOINT = e
}

// Set timeout
func SetBackendTimeout(to time.Duration) {
    backendTimeout = to
}

// Debug
func SetDebugMode(b bool) {
    debugMode = b
}

// Drain
func Drain() {
    isDraining = true
    if startCounter > doneCounter {
        // Wait for signal
        <- drained
    }
}

// Write a message
func LogMessage(msg string) bool {
    // Create fields map
    params := make(map[string]string)
    params["__token__"] = TOKEN
    params["msg"] = msg

    // Push to channel
    return requestAsync(params)
}

// Request async
func requestAsync(params map[string]string) bool {
    // Check amount of open items in the channel, if the channel is full, return false and drop this message
    if dropOnFullWriteAheadBuffer {
        var lwa int = len(writeAhead)
        if lwa == writeAheadBufferSize {
            log.Printf("Write ahead buffer is full and contains %d items. Dropping current log message", lwa)
        }
    }

    // Add counter
    startCounterMux.Lock()
    startCounter++
    startCounterMux.Unlock()

    // Do we have to start a writer?
    if writeAheadInit == false {
        writeAheadInit = true
        backendWriter()
    }

    // Insert into channel
    writeAhead <- params

    // OK
    return true
}

// Get hostname of this system
func getHostname() string {
    // Hostname
    name, err := os.Hostname()
    if err != nil {
        return ""
    }
    return name
}

// Backend writer
func backendWriter() {
    hostname = getHostname()
    go func() {
        // Client
        transport := &http.Transport{
            Dial: func(netw, addr string) (net.Conn, error) {
                    deadline := time.Now().Add(backendTimeout)
                    c, err := net.DialTimeout(netw, addr, time.Second)
                    if err != nil {
                            return nil, err
                    }
                    c.SetDeadline(deadline)
                    return c, nil
            }}
        httpclient := &http.Client{Transport: transport}

        // Wait for messages
        var urlParams url.Values
        var currentEventCount uint64 = uint64(0)
        for {
            // Read from channel
            var fields map[string]string
            fields = <- writeAhead

            // Populate url params
            if currentEventCount == 0 {
                urlParams = url.Values{}
            }
            for k, _ := range fields {
                if k == "__token__" {
                    // Token
                    urlParams.Add("t", fields[k]);
                } else {
                    // Field
                    urlParams.Add("f[" + strconv.FormatUint(currentEventCount, 10) + "][" + k + "]", fields[k]);
                }
            }

            // Host
            if len(hostname) > 0 {
                urlParams.Add("f[" + strconv.FormatUint(currentEventCount, 10) + "][host]", hostname);
            }

            // Increase current count
            currentEventCount++

            // Queue length
            var qLen = len(writeAhead)
            if qLen > 0 && currentEventCount < maxBulkSize {
                // There is more in the current queue, bulk request
                continue
            }

            // Assemble url
            var url string = ENDPOINT + "/push/bulk"

            // Make request
            if debugMode {
                log.Printf("Write ahead queue %d\n", qLen)
                log.Println(urlParams.Encode())
            }
            resp, err := httpclient.PostForm(url, urlParams)
            if err != nil {
                log.Printf("Error while forwarding data: %s\n", err)
            } else {
                defer resp.Body.Close()
            }

            // Done counter
            doneCounterMux.Lock()
            doneCounter += currentEventCount
            doneCounterMux.Unlock()

            // Reset event count
            currentEventCount = 0

            // Are we draining the system?
            if isDraining && doneCounter >= startCounter {
                // Flag the drained channel
                drained <- true
            }
        }
        log.Printf("Stopping backend writer")
    }()
}

// Timeout helper
func dialTimeout(network, addr string) (net.Conn, error) {
    return net.DialTimeout(network, addr, backendTimeout)
}

// Validate the token
func validateToken(t string) {
    if len(t) == 0 {
        log.Println("Please set a valid token with cloudpelican.SetToken(token string)")
    }
}<commit_msg>Dt timestamps<commit_after>package cloudpelican

// @author Robin Verlangen
// @todo Support bulk index requests
// Tool for logging data to CloudPelican directly from Go

// Imports
import (
    "net"
    "net/http"
    "net/url"
    "log"
    "sync"
    "time"
    "strconv"
    "os"
)

// Settings
var ENDPOINT string = "https://app.cloudpelican.com/api"
var TOKEN string = ""
var backendTimeout = time.Duration(5 * time.Second)
var debugMode bool = false
var maxBulkSize uint64 = uint64(100)
var hostname string

// Monitor drain status
var startCounter uint64 = uint64(0)
var startCounterMux sync.Mutex
var doneCounter uint64 = uint64(0)
var doneCounterMux sync.Mutex
var isDraining bool = false
var drained = make(chan bool);

// Log queue
var writeAheadBufferSize int = 1000
var writeAhead chan map[string]string = make(chan map[string]string, writeAheadBufferSize)
var writeAheadInit bool
var dropOnFullWriteAheadBuffer bool = true

// Set token
func SetToken(t string) {
    // Validate before setting
    validateToken(t)

    // Store
    TOKEN = t
}

// Set endpoint
func SetEndpoint(e string) {
    // Store
    ENDPOINT = e
}

// Set timeout
func SetBackendTimeout(to time.Duration) {
    backendTimeout = to
}

// Debug
func SetDebugMode(b bool) {
    debugMode = b
}

// Drain
func Drain() {
    isDraining = true
    if startCounter > doneCounter {
        // Wait for signal
        <- drained
    }
}

// Write a message
func LogMessage(msg string) bool {
    // Create fields map
    params := make(map[string]string)
    params["__token__"] = TOKEN
    params["msg"] = msg
    params["dt"] = getTimeString()

    // Push to channel
    return requestAsync(params)
}

// Current time
func getTimeString() string {
    now := time.Nanoseconds()
    localTime := time.SecondsToLocalTime(now/1e9)
    miliSeconds := (now % 1e9) / 1e6
    return fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d.%03d", localTime.Year, localTime.Month, localTime.Day, localTime.Hour, localTime.Minute, localTime.Second, miliSeconds)
}

// Request async
func requestAsync(params map[string]string) bool {
    // Check amount of open items in the channel, if the channel is full, return false and drop this message
    if dropOnFullWriteAheadBuffer {
        var lwa int = len(writeAhead)
        if lwa == writeAheadBufferSize {
            log.Printf("Write ahead buffer is full and contains %d items. Dropping current log message", lwa)
        }
    }

    // Add counter
    startCounterMux.Lock()
    startCounter++
    startCounterMux.Unlock()

    // Do we have to start a writer?
    if writeAheadInit == false {
        writeAheadInit = true
        backendWriter()
    }

    // Insert into channel
    writeAhead <- params

    // OK
    return true
}

// Get hostname of this system
func getHostname() string {
    // Hostname
    name, err := os.Hostname()
    if err != nil {
        return ""
    }
    return name
}

// Backend writer
func backendWriter() {
    hostname = getHostname()
    go func() {
        // Client
        transport := &http.Transport{
            Dial: func(netw, addr string) (net.Conn, error) {
                    deadline := time.Now().Add(backendTimeout)
                    c, err := net.DialTimeout(netw, addr, time.Second)
                    if err != nil {
                            return nil, err
                    }
                    c.SetDeadline(deadline)
                    return c, nil
            }}
        httpclient := &http.Client{Transport: transport}

        // Wait for messages
        var urlParams url.Values
        var currentEventCount uint64 = uint64(0)
        for {
            // Read from channel
            var fields map[string]string
            fields = <- writeAhead

            // Populate url params
            if currentEventCount == 0 {
                urlParams = url.Values{}
            }
            for k, _ := range fields {
                if k == "__token__" {
                    // Token
                    urlParams.Add("t", fields[k]);
                } else {
                    // Field
                    urlParams.Add("f[" + strconv.FormatUint(currentEventCount, 10) + "][" + k + "]", fields[k]);
                }
            }

            // Host
            if len(hostname) > 0 {
                urlParams.Add("f[" + strconv.FormatUint(currentEventCount, 10) + "][host]", hostname);
            }

            // Increase current count
            currentEventCount++

            // Queue length
            var qLen = len(writeAhead)
            if qLen > 0 && currentEventCount < maxBulkSize {
                // There is more in the current queue, bulk request
                continue
            }

            // Assemble url
            var url string = ENDPOINT + "/push/bulk"

            // Make request
            if debugMode {
                log.Printf("Write ahead queue %d\n", qLen)
                log.Println(urlParams.Encode())
            }
            resp, err := httpclient.PostForm(url, urlParams)
            if err != nil {
                log.Printf("Error while forwarding data: %s\n", err)
            } else {
                defer resp.Body.Close()
            }

            // Done counter
            doneCounterMux.Lock()
            doneCounter += currentEventCount
            doneCounterMux.Unlock()

            // Reset event count
            currentEventCount = 0

            // Are we draining the system?
            if isDraining && doneCounter >= startCounter {
                // Flag the drained channel
                drained <- true
            }
        }
        log.Printf("Stopping backend writer")
    }()
}

// Timeout helper
func dialTimeout(network, addr string) (net.Conn, error) {
    return net.DialTimeout(network, addr, backendTimeout)
}

// Validate the token
func validateToken(t string) {
    if len(t) == 0 {
        log.Println("Please set a valid token with cloudpelican.SetToken(token string)")
    }
}<|endoftext|>