<commit_before>package internet

import (
	"net"
	"sync"

	"v2ray.com/core/common/errors"
	v2net "v2ray.com/core/common/net"
	"v2ray.com/core/common/retry"
)

var (
	transportListenerCache = make(map[TransportProtocol]ListenFunc)
)

func RegisterTransportListener(protocol TransportProtocol, listener ListenFunc) error {
	if _, found := transportListenerCache[protocol]; found {
		return errors.New("Internet|TCPHub: ", protocol, " listener already registered.")
	}
	transportListenerCache[protocol] = listener
	return nil
}

type ListenFunc func(address v2net.Address, port v2net.Port, options ListenOptions) (Listener, error)
type ListenOptions struct {
	Stream       *StreamConfig
	RecvOrigDest bool
}

type Listener interface {
	Accept() (Connection, error)
	Close() error
	Addr() net.Addr
}

type TCPHub struct {
	sync.Mutex
	listener     Listener
	connCallback ConnectionHandler
	accepting    bool
}

func ListenTCP(address v2net.Address, port v2net.Port, callback ConnectionHandler, settings *StreamConfig) (*TCPHub, error) {
	options := ListenOptions{
		Stream: settings,
	}
	protocol := settings.GetEffectiveProtocol()
	listenFunc := transportListenerCache[protocol]
	if listenFunc == nil {
		return nil, errors.New("Internet|TCPHub: ", protocol, " listener not registered.")
	}
	listener, err := listenFunc(address, port, options)
	if err != nil {
		return nil, errors.Base(err).Message("Internet|TCPHub: Failed to listen on address: ", address, ":", port)
	}

	hub := &TCPHub{
		listener:     listener,
		connCallback: callback,
	}

	go hub.start()
	return hub, nil
}

func (v *TCPHub) Close() {
	v.accepting = false
	v.listener.Close()
}

func (v *TCPHub) start() {
	v.accepting = true
	for v.accepting {
		var newConn Connection
		err := retry.ExponentialBackoff(10, 500).On(func() error {
			if !v.accepting {
				return nil
			}
			conn, err := v.listener.Accept()
			if err != nil {
				return errors.Base(err).Message("Internet|Listener: Failed to accept new TCP connection.")
			}
			newConn = conn
			return nil
		})
		if err == nil && newConn != nil {
			go v.connCallback(newConn)
		}
	}
}
<commit_msg>sync for tcp hub<commit_after>package internet

import (
	"net"

	"v2ray.com/core/common/errors"
	v2net "v2ray.com/core/common/net"
	"v2ray.com/core/common/retry"
)

var (
	transportListenerCache = make(map[TransportProtocol]ListenFunc)
)

func RegisterTransportListener(protocol TransportProtocol, listener ListenFunc) error {
	if _, found := transportListenerCache[protocol]; found {
		return errors.New("Internet|TCPHub: ", protocol, " listener already registered.")
	}
	transportListenerCache[protocol] = listener
	return nil
}

type ListenFunc func(address v2net.Address, port v2net.Port, options ListenOptions) (Listener, error)
type ListenOptions struct {
	Stream       *StreamConfig
	RecvOrigDest bool
}

type Listener interface {
	Accept() (Connection, error)
	Close() error
	Addr() net.Addr
}

type TCPHub struct {
	listener     Listener
	connCallback ConnectionHandler
	closed       chan bool
}

func ListenTCP(address v2net.Address, port v2net.Port, callback ConnectionHandler, settings *StreamConfig) (*TCPHub, error) {
	options := ListenOptions{
		Stream: settings,
	}
	protocol := settings.GetEffectiveProtocol()
	listenFunc := transportListenerCache[protocol]
	if listenFunc == nil {
		return nil, errors.New("Internet|TCPHub: ", protocol, " listener not registered.")
	}
	listener, err := listenFunc(address, port, options)
	if err != nil {
		return nil, errors.Base(err).Message("Internet|TCPHub: Failed to listen on address: ", address, ":", port)
	}

	hub := &TCPHub{
		listener:     listener,
		connCallback: callback,
	}

	go hub.start()
	return hub, nil
}

func (v *TCPHub) Close() {
	select {
	case <-v.closed:
		return
	default:
		v.listener.Close()
	}
}

func (v *TCPHub) start() {
	for {
		select {
		case <-v.closed:
			return
		default:
		}
		var newConn Connection
		err := retry.ExponentialBackoff(10, 500).On(func() error {
			select {
			case <-v.closed:
				return nil
			default:
				conn, err := v.listener.Accept()
				if err != nil {
					return errors.Base(err).Message("Internet|Listener: Failed to accept new TCP connection.")
				}
				newConn = conn
				return nil
			}
		})
		if err == nil && newConn != nil {
			go v.connCallback(newConn)
		}
	}
}
<|endoftext|>


<commit_before>package whclient

import (
	"net"
	"net/http"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"github.com/gorilla/websocket"
	"github.com/taskcluster/webhooktunnel/util"
	"github.com/taskcluster/webhooktunnel/wsmux"
)

type clientState int

const (
	stateRunning = iota
	stateBroken
	stateClosed
)

// Config ...
type Config struct {
	ID        string
	ProxyAddr string
	Token     string
	Retry     RetryConfig
	Logger    util.Logger
}

// Configurer is a function which can generate a Config object
// to be used by the client
type Configurer func() (Config, error)

// Client ...
type Client struct {
	m          sync.Mutex
	id         string
	proxyAddr  string
	token      string
	url        atomic.Value
	retry      RetryConfig
	logger     util.Logger
	configurer Configurer
	session    *wsmux.Session
	state      clientState
	closed     chan struct{}
	acceptErr  net.Error
}

func New(configurer Configurer) (*Client, error) {
	config, err := configurer()
	if err != nil {
		return nil, err
	}

	cl := &Client{configurer: configurer}
	cl.setConfig(config)
	cl.closed = make(chan struct{}, 1)
	conn, url, err := cl.connectWithRetry()
	if err != nil {
		return nil, err
	}
	cl.url.Store(url)
	cl.session = wsmux.Client(conn, wsmux.Config{})
	return cl, nil
}

func (c *Client) URL() string {
	return c.url.Load().(string)
}

func (c *Client) Accept() (net.Conn, error) {
	select {
	case <-c.closed:
		return nil, ErrClientClosed
	default:
	}

	c.m.Lock()
	defer c.m.Unlock()
	if c.state == stateBroken || c.state == stateClosed {
		return nil, c.acceptErr
	}

	stream, err := c.session.Accept()
	if err != nil {
		c.state = stateBroken
		c.acceptErr = ErrClientReconnecting
		go c.reconnect()
		return nil, c.acceptErr
	}
	return stream, nil
}

func (c *Client) Addr() net.Addr {
	return c.session.Addr()
}

// Close connection to the proxy
func (c *Client) Close() error {
	select {
	case <-c.closed:
		return nil
	default:
		close(c.closed)
		go func() {
			c.m.Lock()
			defer c.m.Unlock()
			c.acceptErr = ErrClientClosed
			_ = c.session.Close()
		}()
	}
	return nil
}

func (c *Client) setConfig(config Config) {
	c.id = config.ID
	c.proxyAddr = config.ProxyAddr
	c.token = config.Token

	c.retry = config.Retry.defaultValues()
	c.logger = config.Logger
	if c.logger == nil {
		c.logger = &util.NilLogger{}
	}
}

// connectWithRetry returns a websocket connection to the proxy
func (c *Client) connectWithRetry() (*websocket.Conn, string, error) {
	// if token is expired or not usable, get a new token from the authorizer
	if !util.IsTokenUsable(c.token) {
		config, err := c.configurer()
		if err != nil {
			return nil, "", ErrRetryFailed
		}
		c.setConfig(config)
	}

	// initial connection
	addr := strings.TrimSuffix(c.proxyAddr, "/") + "/register/" + c.id
	header := make(http.Header)
	header.Set("Authorization", "Bearer "+c.token)
	// initial attempt
	c.logger.Printf("trying to connect to %s", c.proxyAddr)
	conn, res, err := websocket.DefaultDialer.Dial(addr, header)
	if err != nil {
		if shouldRetry(res) {
			// retry connection and return result
			return c.retryConn()
		}
		c.logger.Printf("connection failed with error:%v, response:%v", err, res)
		if isAuthError(res) {
			return nil, "", ErrAuthFailed
		}
		return nil, "", ErrRetryFailed
	}
	c.logger.Printf("connected to %s ", c.proxyAddr)

	url := res.Header.Get("x-webhooktunnel-client-url")
	return conn, url, err
}

// retryConn is a utility function used by connectWithRetry to use exponential
// backoff to attempt reconnection
func (c *Client) retryConn() (*websocket.Conn, string, error) {
	addr := strings.TrimSuffix(c.proxyAddr, "/") + "/register/" + c.id
	header := make(http.Header)
	header.Set("Authorization", "Bearer "+c.token)

	currentDelay := c.retry.InitialDelay
	maxTimer := time.After(c.retry.MaxElapsedTime)
	backoff := time.After(currentDelay)

	for {
		select {
		case <-maxTimer:
			return nil, "", ErrRetryTimedOut
		case <-backoff:
			c.logger.Printf("trying to connect to %s", c.proxyAddr)
			conn, res, err := websocket.DefaultDialer.Dial(addr, header)
			if err == nil {
				url := res.Header.Get("x-webhooktunnel-client-url")
				return conn, url, nil
			}
			if !shouldRetry(res) {
				c.logger.Printf("connection to %s failed. could not connect", c.proxyAddr)
				return nil, "", ErrRetryFailed
			}
			c.logger.Printf("connection to %s failed. will retry", c.proxyAddr)

			currentDelay = c.retry.nextDelay(currentDelay)
			backoff = time.After(currentDelay)

		}
	}
}

// reconnect is used to repair broken connections
func (c *Client) reconnect() {
	c.m.Lock()
	defer c.m.Unlock()
	conn, url, err := c.connectWithRetry()
	if err != nil {
		// set error and return
		c.logger.Printf("unable to reconnect to %s", c.proxyAddr)
		c.acceptErr = ErrRetryFailed
		return
	}

	if c.session != nil {
		_ = c.session.Close()
		c.session = nil
	}

	sessionConfig := wsmux.Config{
		// Log:              c.logger,
		StreamBufferSize: 4 * 1024,
	}
	c.session = wsmux.Client(conn, sessionConfig)
	c.url.Store(url)
	c.state = stateRunning
	c.logger.Printf("state: running")
	c.acceptErr = nil

}

// simple utility to check if client should retry connection
func shouldRetry(r *http.Response) bool {
	// may be that proxy is down for changing secrets and therefore unreachable
	if r == nil {
		return true
	}
	if r.StatusCode/100 == 4 || r.StatusCode/100 == 2 {
		return false
	}
	return true
}

func isAuthError(r *http.Response) bool {
	if r == nil {
		return false
	}
	return r.StatusCode == 401
}
<commit_msg>added docs<commit_after>package whclient

import (
	"net"
	"net/http"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"github.com/gorilla/websocket"
	"github.com/taskcluster/webhooktunnel/util"
	"github.com/taskcluster/webhooktunnel/wsmux"
)

type clientState int

const (
	stateRunning = iota
	stateBroken
	stateClosed
)

// Config ...
type Config struct {
	ID        string
	ProxyAddr string
	Token     string
	Retry     RetryConfig
	Logger    util.Logger
}

// Configurer is a function which can generate a Config object
// to be used by the client
type Configurer func() (Config, error)

// Client used to connect to a proxy instance and serve content
// over the proxy. Client implements net.Listener.
type Client struct {
	m          sync.Mutex
	id         string
	proxyAddr  string
	token      string
	url        atomic.Value
	retry      RetryConfig
	logger     util.Logger
	configurer Configurer
	session    *wsmux.Session
	state      clientState
	closed     chan struct{}
	acceptErr  net.Error
}

// New creates a new Client instance.
func New(configurer Configurer) (*Client, error) {
	config, err := configurer()
	if err != nil {
		return nil, err
	}

	cl := &Client{configurer: configurer}
	cl.setConfig(config)
	cl.closed = make(chan struct{}, 1)
	conn, url, err := cl.connectWithRetry()
	if err != nil {
		return nil, err
	}
	cl.url.Store(url)
	cl.session = wsmux.Client(conn, wsmux.Config{})
	return cl, nil
}

// URL returns the url at which the proxy serves the client's
// endpoints
func (c *Client) URL() string {
	return c.url.Load().(string)
}

// Accept is used to accept multiplexed streams from the
// proxy as net.Conn.
func (c *Client) Accept() (net.Conn, error) {
	select {
	case <-c.closed:
		return nil, ErrClientClosed
	default:
	}

	c.m.Lock()
	defer c.m.Unlock()
	if c.state == stateBroken || c.state == stateClosed {
		return nil, c.acceptErr
	}

	stream, err := c.session.Accept()
	if err != nil {
		c.state = stateBroken
		c.acceptErr = ErrClientReconnecting
		go c.reconnect()
		return nil, c.acceptErr
	}
	return stream, nil
}

// Addr returns the net.Addr of the underlying wsmux session
func (c *Client) Addr() net.Addr {
	return c.session.Addr()
}

// Close connection to the proxy
func (c *Client) Close() error {
	select {
	case <-c.closed:
		return nil
	default:
		close(c.closed)
		go func() {
			c.m.Lock()
			defer c.m.Unlock()
			c.acceptErr = ErrClientClosed
			_ = c.session.Close()
		}()
	}
	return nil
}

func (c *Client) setConfig(config Config) {
	c.id = config.ID
	c.proxyAddr = config.ProxyAddr
	c.token = config.Token

	c.retry = config.Retry.defaultValues()
	c.logger = config.Logger
	if c.logger == nil {
		c.logger = &util.NilLogger{}
	}
}

// connectWithRetry returns a websocket connection to the proxy
func (c *Client) connectWithRetry() (*websocket.Conn, string, error) {
	// if token is expired or not usable, get a new token from the authorizer
	if !util.IsTokenUsable(c.token) {
		config, err := c.configurer()
		if err != nil {
			return nil, "", ErrRetryFailed
		}
		c.setConfig(config)
	}

	// initial connection
	addr := strings.TrimSuffix(c.proxyAddr, "/") + "/register/" + c.id
	header := make(http.Header)
	header.Set("Authorization", "Bearer "+c.token)
	// initial attempt
	c.logger.Printf("trying to connect to %s", c.proxyAddr)
	conn, res, err := websocket.DefaultDialer.Dial(addr, header)
	if err != nil {
		if shouldRetry(res) {
			// retry connection and return result
			return c.retryConn()
		}
		c.logger.Printf("connection failed with error:%v, response:%v", err, res)
		if isAuthError(res) {
			return nil, "", ErrAuthFailed
		}
		return nil, "", ErrRetryFailed
	}
	c.logger.Printf("connected to %s ", c.proxyAddr)

	url := res.Header.Get("x-webhooktunnel-client-url")
	return conn, url, err
}

// retryConn is a utility function used by connectWithRetry to use exponential
// backoff to attempt reconnection
func (c *Client) retryConn() (*websocket.Conn, string, error) {
	addr := strings.TrimSuffix(c.proxyAddr, "/") + "/register/" + c.id
	header := make(http.Header)
	header.Set("Authorization", "Bearer "+c.token)

	currentDelay := c.retry.InitialDelay
	maxTimer := time.After(c.retry.MaxElapsedTime)
	backoff := time.After(currentDelay)

	for {
		select {
		case <-maxTimer:
			return nil, "", ErrRetryTimedOut
		case <-backoff:
			c.logger.Printf("trying to connect to %s", c.proxyAddr)
			conn, res, err := websocket.DefaultDialer.Dial(addr, header)
			if err == nil {
				url := res.Header.Get("x-webhooktunnel-client-url")
				return conn, url, nil
			}
			if !shouldRetry(res) {
				c.logger.Printf("connection to %s failed. could not connect", c.proxyAddr)
				return nil, "", ErrRetryFailed
			}
			c.logger.Printf("connection to %s failed. will retry", c.proxyAddr)

			currentDelay = c.retry.nextDelay(currentDelay)
			backoff = time.After(currentDelay)

		}
	}
}

// reconnect is used to repair broken connections
func (c *Client) reconnect() {
	c.m.Lock()
	defer c.m.Unlock()
	conn, url, err := c.connectWithRetry()
	if err != nil {
		// set error and return
		c.logger.Printf("unable to reconnect to %s", c.proxyAddr)
		c.acceptErr = ErrRetryFailed
		return
	}

	if c.session != nil {
		_ = c.session.Close()
		c.session = nil
	}

	sessionConfig := wsmux.Config{
		// Log:              c.logger,
		StreamBufferSize: 4 * 1024,
	}
	c.session = wsmux.Client(conn, sessionConfig)
	c.url.Store(url)
	c.state = stateRunning
	c.logger.Printf("state: running")
	c.acceptErr = nil

}

// simple utility to check if client should retry connection
func shouldRetry(r *http.Response) bool {
	// may be that proxy is down for changing secrets and therefore unreachable
	if r == nil {
		return true
	}
	if r.StatusCode/100 == 4 || r.StatusCode/100 == 2 {
		return false
	}
	return true
}

func isAuthError(r *http.Response) bool {
	if r == nil {
		return false
	}
	return r.StatusCode == 401
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"
	"os/exec"
	"time"

	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/config"
	"github.com/ninjasphere/go-wireless/iwlib"
	"github.com/ninjasphere/gatt"
)

func StartHTTPServer(conn *ninja.Connection, wifi_manager *WifiManager, srv *gatt.Server, pairing_ui ConsolePairingUI) {

	http.HandleFunc("/get_visible_wifi_networks", func(w http.ResponseWriter, r *http.Request) {

		pairing_ui.DisplayIcon("wifi-searching.gif")

		// Before we search for wifi networks, disable any that are try-fail-ing
		wifi_manager.DisableAllNetworks()

		networks, err := iwlib.GetWirelessNetworks("wlan0")

		if err == nil {
			wifi_networks := make([]WifiNetwork, len(networks))
			for i, network := range networks {
				wifi_networks[i].SSID = network.SSID
			}

			out, err := json.Marshal(wifi_networks)
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

			io.WriteString(w, string(out))
		} else {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}

	})

	http.HandleFunc("/connect_wifi_network", func(w http.ResponseWriter, r *http.Request) {

		pairing_ui.DisplayIcon("wifi-connecting.gif")

		body, err := ioutil.ReadAll(r.Body)

		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		var wifi_creds WifiCredentials

		json.Unmarshal(body, &wifi_creds)

		logger.Infof("Got wifi credentials %v", wifi_creds)

		done := make(chan bool, 2)

		go func() {
			done <- wifi_manager.SetCredentials(&wifi_creds)
		}()

		select {
		case success := <-done:

			logger.Infof("Wifi success? %t", success)

			if success {
				pairing_ui.DisplayIcon("wifi-connected.gif")
				serial_number, err := exec.Command("/opt/ninjablocks/bin/sphere-serial").Output()
				if err != nil {
					http.Error(w, err.Error(), http.StatusInternalServerError)
					return
				}
				io.WriteString(w, "\""+string(serial_number)+"\"")
			} else {
				pairing_ui.DisplayIcon("wifi-failed.gif")
				http.Error(w, "Could not connect to specified WiFi network, is the key correct?", http.StatusBadRequest)
			}

		case <-time.After(time.Second * 15):
			pairing_ui.DisplayIcon("wifi-failed.gif")
			http.Error(w, "Could not connect to specified WiFi network, is it in range?", http.StatusBadRequest)
		}
	})

	http.HandleFunc("/close_ble_central", func(w http.ResponseWriter, r *http.Request) {
		err := srv.Close()

		if err == nil {
			io.WriteString(w, "true")
		} else {
			io.WriteString(w, "false")
		}
	})

	if !factoryReset {

		updateService := conn.GetServiceClient("$node/" + config.Serial() + "/updates")

		updateService.OnEvent("progress", func(progress *map[string]interface{}, topicKeys map[string]string) bool {
			lastUpdateProgress = *progress

			logger.Infof("Got update progress: %v", *progress)
			return true
		})

		http.HandleFunc("/start_update", func(w http.ResponseWriter, r *http.Request) {
			var response bool

			err := updateService.Call("start", nil, &response, time.Second*10)

			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

			if response {
				lastUpdateProgress = nil
			}

			out, err := json.Marshal(&response)

			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

			io.WriteString(w, string(out))
		})

		http.HandleFunc("/get_update_progress", func(w http.ResponseWriter, r *http.Request) {

			out, err := json.Marshal(&lastUpdateProgress)

			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

			io.WriteString(w, string(out))
		})

		http.HandleFunc("/get_wifi_ip", func(w http.ResponseWriter, r *http.Request) {

			ip, err := GetWlanAddress()
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

			out, err := json.Marshal(&ip)

			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

			io.WriteString(w, string(out))
		})

	}

	go func() {
		logger.Infof("Starting http server on port 8888")
		logger.Fatalf("Web server failed: %s", http.ListenAndServe(":8888", nil))
	}()

}
<commit_msg>Add /status to setup http server<commit_after>package main

import (
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"
	"os/exec"
	"time"

	"github.com/ninjasphere/gatt"
	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/config"
	"github.com/ninjasphere/go-wireless/iwlib"
)

func isPaired() bool {
	return config.HasString("siteId") && config.HasString("token") && config.HasString("userId") && config.HasString("nodeId")
}

func StartHTTPServer(conn *ninja.Connection, wifi_manager *WifiManager, srv *gatt.Server, pairing_ui ConsolePairingUI) {

	http.HandleFunc("/status", func(w http.ResponseWriter, r *http.Request) {

		config.MustRefresh()

		data := map[string]interface{}{
			"paired": isPaired(),
			"nodeId": config.MustString("nodeId"),
		}

		if ip, ipErr := GetWlanAddress(); ipErr == nil {
			data["wlanIp"] = ip
		}

		if isPaired() {
			data["siteId"] = config.MustString("siteId")
			data["userId"] = config.MustString("userId")
		}

		out, _ := json.Marshal(data)

		io.WriteString(w, string(out))
	})

	http.HandleFunc("/get_visible_wifi_networks", func(w http.ResponseWriter, r *http.Request) {

		pairing_ui.DisplayIcon("wifi-searching.gif")

		// Before we search for wifi networks, disable any that are try-fail-ing
		wifi_manager.DisableAllNetworks()

		networks, err := iwlib.GetWirelessNetworks("wlan0")

		if err == nil {
			wifi_networks := make([]WifiNetwork, len(networks))
			for i, network := range networks {
				wifi_networks[i].SSID = network.SSID
			}

			out, err := json.Marshal(wifi_networks)
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

			io.WriteString(w, string(out))
		} else {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}

	})

	http.HandleFunc("/connect_wifi_network", func(w http.ResponseWriter, r *http.Request) {

		pairing_ui.DisplayIcon("wifi-connecting.gif")

		body, err := ioutil.ReadAll(r.Body)

		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		var wifi_creds WifiCredentials

		json.Unmarshal(body, &wifi_creds)

		logger.Infof("Got wifi credentials %v", wifi_creds)

		done := make(chan bool, 2)

		go func() {
			done <- wifi_manager.SetCredentials(&wifi_creds)
		}()

		select {
		case success := <-done:

			logger.Infof("Wifi success? %t", success)

			if success {
				pairing_ui.DisplayIcon("wifi-connected.gif")
				serial_number, err := exec.Command("/opt/ninjablocks/bin/sphere-serial").Output()
				if err != nil {
					http.Error(w, err.Error(), http.StatusInternalServerError)
					return
				}
				io.WriteString(w, "\""+string(serial_number)+"\"")
			} else {
				pairing_ui.DisplayIcon("wifi-failed.gif")
				http.Error(w, "Could not connect to specified WiFi network, is the key correct?", http.StatusBadRequest)
			}

		case <-time.After(time.Second * 15):
			pairing_ui.DisplayIcon("wifi-failed.gif")
			http.Error(w, "Could not connect to specified WiFi network, is it in range?", http.StatusBadRequest)
		}
	})

	http.HandleFunc("/close_ble_central", func(w http.ResponseWriter, r *http.Request) {
		err := srv.Close()

		if err == nil {
			io.WriteString(w, "true")
		} else {
			io.WriteString(w, "false")
		}
	})

	if !factoryReset {

		updateService := conn.GetServiceClient("$node/" + config.Serial() + "/updates")

		updateService.OnEvent("progress", func(progress *map[string]interface{}, topicKeys map[string]string) bool {
			lastUpdateProgress = *progress

			logger.Infof("Got update progress: %v", *progress)
			return true
		})

		http.HandleFunc("/start_update", func(w http.ResponseWriter, r *http.Request) {
			var response bool

			err := updateService.Call("start", nil, &response, time.Second*10)

			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

			if response {
				lastUpdateProgress = nil
			}

			out, err := json.Marshal(&response)

			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

			io.WriteString(w, string(out))
		})

		http.HandleFunc("/get_update_progress", func(w http.ResponseWriter, r *http.Request) {

			out, err := json.Marshal(&lastUpdateProgress)

			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

			io.WriteString(w, string(out))
		})

		http.HandleFunc("/get_wifi_ip", func(w http.ResponseWriter, r *http.Request) {

			ip, err := GetWlanAddress()
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

			out, err := json.Marshal(&ip)

			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

			io.WriteString(w, string(out))
		})

	}

	go func() {
		logger.Infof("Starting http server on port 8888")
		logger.Fatalf("Web server failed: %s", http.ListenAndServe(":8888", nil))
	}()

}
<|endoftext|>


<commit_before>package workloads

import (
	"fmt"
	"sync"
	"time"

	"github.com/patrick-higgins/summstat"

	"github.com/pavel-paulau/blurr/databases"
)

type State struct {
	Operations, Records int64
	Errors map[string]int
	Events map[string]time.Time
	Latency map[string]*summstat.Stats
}

func (state *State) Init() {
	state.Errors = map[string]int{}
	state.Events = map[string]time.Time{}
	state.Latency = map[string]*summstat.Stats{}
	state.Latency["Create"] = summstat.NewStats()
	state.Latency["Read"] = summstat.NewStats()
	state.Latency["Update"] = summstat.NewStats()
	state.Latency["Delete"] = summstat.NewStats()
	state.Latency["Query"] = summstat.NewStats()
}

func (state *State) ReportThroughput(config Config, wg *sync.WaitGroup) {
	defer wg.Done()
	opsDone := int64(0)
	samples := 1
	fmt.Println("Benchmark started:")
	for state.Operations < config.Operations {
		time.Sleep(10 * time.Second)
		throughput := (state.Operations - opsDone) / 10
		opsDone = state.Operations
		fmt.Printf("%6v seconds: %10v ops/sec; total operations: %v; total errors: %v\n",
			samples*10, throughput, opsDone, state.Errors["total"])
		samples++
	}
}

func (state *State) MeasureLatency(database databases.Database,
	workload Workload, config Config, wg *sync.WaitGroup) {
	defer wg.Done()

	for state.Operations < config.Operations {
		if config.CreatePercentage > 0 {
			state.Operations++
			state.Records++
			key := workload.GenerateNewKey(state.Records)
			value := workload.GenerateValue(key, config.IndexableFields, config.ValueSize)
			t0 := time.Now()
			database.Create(key, value)
			t1 := time.Now()
			state.Latency["Create"].AddSample(summstat.Sample(t1.Sub(t0)))
		}
		if config.ReadPercentage > 0 {
			state.Operations++
			key := workload.GenerateExistingKey(state.Records)
			t0 := time.Now()
			database.Read(key)
			t1 := time.Now()
			state.Latency["Read"].AddSample(summstat.Sample(t1.Sub(t0)))
		}
		if config.UpdatePercentage > 0 {
			state.Operations++
			key := workload.GenerateExistingKey(state.Records)
			value := workload.GenerateValue(key, config.IndexableFields, config.ValueSize)
			t0 := time.Now()
			database.Update(key, value)
			t1 := time.Now()
			state.Latency["Update"].AddSample(summstat.Sample(t1.Sub(t0)))
		}
		if config.DeletePercentage > 0 {
			state.Operations++
			key := workload.GenerateKeyForRemoval()
			t0 := time.Now()
			database.Delete(key)
			t1 := time.Now()
			state.Latency["Delete"].AddSample(summstat.Sample(t1.Sub(t0)))
		}
		if config.QueryPercentage > 0 {
			state.Operations++
			fieldName, fieldValue, limit := workload.GenerateQuery(config.IndexableFields,
				state.Records)
			t0 := time.Now()
			database.Query(fieldName, fieldValue, limit)
			t1 := time.Now()
			state.Latency["Query"].AddSample(summstat.Sample(t1.Sub(t0)))
		}
		time.Sleep(time.Second)
	}
}

func (state *State) ReportSummary() {
	for _, op := range []string{"Create", "Read", "Update", "Delete", "Query"} {
		if state.Latency[op].Count() > 0 {
			fmt.Printf("%v latency:\n", op)
			perc80th := time.Duration(state.Latency[op].Percentile(0.8))
			perc90th := time.Duration(state.Latency[op].Percentile(0.9))
			perc95th := time.Duration(state.Latency[op].Percentile(0.95))
			mean := time.Duration(state.Latency[op].Mean())
			fmt.Printf("\t80th percentile: %v\n", perc80th)
			fmt.Printf("\t90th percentile: %v\n", perc90th)
			fmt.Printf("\t95th percentile: %v\n", perc95th)
			fmt.Printf("\tMean: %v\n", mean)
		}
	}
	if len(state.Errors) > 0 {
		fmt.Println("Errors:")
		fmt.Printf("\tCreate : %v\n", state.Errors["c"])
		fmt.Printf("\tRead   : %v\n", state.Errors["r"])
		fmt.Printf("\tUpdate : %v\n", state.Errors["u"])
		fmt.Printf("\tDelete : %v\n", state.Errors["d"])
		fmt.Printf("\tQuery  : %v\n", state.Errors["q"])
		fmt.Printf("\tTotal  : %v\n", state.Errors["total"])
	}
	fmt.Printf("Time elapsed:\n\t%v\n",
		state.Events["Finished"].Sub(state.Events["Started"]))
}
<commit_msg>fix formatting issues<commit_after>package workloads

import (
	"fmt"
	"sync"
	"time"

	"github.com/patrick-higgins/summstat"

	"github.com/pavel-paulau/blurr/databases"
)

type State struct {
	Operations, Records int64
	Errors              map[string]int
	Events              map[string]time.Time
	Latency             map[string]*summstat.Stats
}

func (state *State) Init() {
	state.Errors = map[string]int{}
	state.Events = map[string]time.Time{}
	state.Latency = map[string]*summstat.Stats{}
	state.Latency["Create"] = summstat.NewStats()
	state.Latency["Read"] = summstat.NewStats()
	state.Latency["Update"] = summstat.NewStats()
	state.Latency["Delete"] = summstat.NewStats()
	state.Latency["Query"] = summstat.NewStats()
}

func (state *State) ReportThroughput(config Config, wg *sync.WaitGroup) {
	defer wg.Done()
	opsDone := int64(0)
	samples := 1
	fmt.Println("Benchmark started:")
	for state.Operations < config.Operations {
		time.Sleep(10 * time.Second)
		throughput := (state.Operations - opsDone) / 10
		opsDone = state.Operations
		fmt.Printf("%6v seconds: %10v ops/sec; total operations: %v; total errors: %v\n",
			samples*10, throughput, opsDone, state.Errors["total"])
		samples++
	}
}

func (state *State) MeasureLatency(database databases.Database,
	workload Workload, config Config, wg *sync.WaitGroup) {
	defer wg.Done()

	for state.Operations < config.Operations {
		if config.CreatePercentage > 0 {
			state.Operations++
			state.Records++
			key := workload.GenerateNewKey(state.Records)
			value := workload.GenerateValue(key, config.IndexableFields, config.ValueSize)
			t0 := time.Now()
			database.Create(key, value)
			t1 := time.Now()
			state.Latency["Create"].AddSample(summstat.Sample(t1.Sub(t0)))
		}
		if config.ReadPercentage > 0 {
			state.Operations++
			key := workload.GenerateExistingKey(state.Records)
			t0 := time.Now()
			database.Read(key)
			t1 := time.Now()
			state.Latency["Read"].AddSample(summstat.Sample(t1.Sub(t0)))
		}
		if config.UpdatePercentage > 0 {
			state.Operations++
			key := workload.GenerateExistingKey(state.Records)
			value := workload.GenerateValue(key, config.IndexableFields, config.ValueSize)
			t0 := time.Now()
			database.Update(key, value)
			t1 := time.Now()
			state.Latency["Update"].AddSample(summstat.Sample(t1.Sub(t0)))
		}
		if config.DeletePercentage > 0 {
			state.Operations++
			key := workload.GenerateKeyForRemoval()
			t0 := time.Now()
			database.Delete(key)
			t1 := time.Now()
			state.Latency["Delete"].AddSample(summstat.Sample(t1.Sub(t0)))
		}
		if config.QueryPercentage > 0 {
			state.Operations++
			fieldName, fieldValue, limit := workload.GenerateQuery(config.IndexableFields,
				state.Records)
			t0 := time.Now()
			database.Query(fieldName, fieldValue, limit)
			t1 := time.Now()
			state.Latency["Query"].AddSample(summstat.Sample(t1.Sub(t0)))
		}
		time.Sleep(time.Second)
	}
}

func (state *State) ReportSummary() {
	for _, op := range []string{"Create", "Read", "Update", "Delete", "Query"} {
		if state.Latency[op].Count() > 0 {
			fmt.Printf("%v latency:\n", op)
			perc80th := time.Duration(state.Latency[op].Percentile(0.8))
			perc90th := time.Duration(state.Latency[op].Percentile(0.9))
			perc95th := time.Duration(state.Latency[op].Percentile(0.95))
			mean := time.Duration(state.Latency[op].Mean())
			fmt.Printf("\t80th percentile: %v\n", perc80th)
			fmt.Printf("\t90th percentile: %v\n", perc90th)
			fmt.Printf("\t95th percentile: %v\n", perc95th)
			fmt.Printf("\tMean: %v\n", mean)
		}
	}
	if len(state.Errors) > 0 {
		fmt.Println("Errors:")
		fmt.Printf("\tCreate : %v\n", state.Errors["c"])
		fmt.Printf("\tRead   : %v\n", state.Errors["r"])
		fmt.Printf("\tUpdate : %v\n", state.Errors["u"])
		fmt.Printf("\tDelete : %v\n", state.Errors["d"])
		fmt.Printf("\tQuery  : %v\n", state.Errors["q"])
		fmt.Printf("\tTotal  : %v\n", state.Errors["total"])
	}
	fmt.Printf("Time elapsed:\n\t%v\n",
		state.Events["Finished"].Sub(state.Events["Started"]))
}
<|endoftext|>


<commit_before>package class

// only used by jvm.go
func NewBootstrapMethod(code []byte, cl *ClassLoader) *Method {
	method := &Method{}
	method.class = &Class{name: "~jvmgo", classLoader: cl}
	method.name = "<bootstrap>"
	method.accessFlags = ACC_STATIC
	method.maxStack = 8
	method.maxLocals = 8
	method.code = code
	return method
}

func NewGarbageMethod() *Method {
	method := &Method{}
	method.class = &Class{name: "~jvmgo"}
	method.name = "<garbage>"
	method.accessFlags = ACC_STATIC
	method.maxStack = 8
	method.maxLocals = 8
	method.code = []byte{0xb1} // return
	return method
}

// todo
func hackClass(class *Class) {
	if class.name == "java/lang/ClassLoader" {
		loadLibrary := class.GetStaticMethod("loadLibrary", "(Ljava/lang/Class;Ljava/lang/String;Z)V")
		loadLibrary.code = []byte{0xb1} // return void
	}
}
<commit_msg>delete function: NewGarbageMethod()<commit_after>package class

// only used by jvm.go
func NewBootstrapMethod(code []byte, cl *ClassLoader) *Method {
	method := &Method{}
	method.class = &Class{name: "~jvmgo", classLoader: cl}
	method.name = "<bootstrap>"
	method.accessFlags = ACC_STATIC
	method.maxStack = 8
	method.maxLocals = 8
	method.code = code
	return method
}

// todo
func hackClass(class *Class) {
	if class.name == "java/lang/ClassLoader" {
		loadLibrary := class.GetStaticMethod("loadLibrary", "(Ljava/lang/Class;Ljava/lang/String;Z)V")
		loadLibrary.code = []byte{0xb1} // return void
	}
}
<|endoftext|>


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

package xdgbasedir

import (
	"os"
	"os/user"
	"path/filepath"
	"runtime"
	"strconv"
	"sync"
	"testing"

	"github.com/zchee/go-xdgbasedir/home"
)

func TestDataHome(t *testing.T) {
	var testDefaultDataHome string
	switch runtime.GOOS {
	case "windows":
		testDefaultDataHome = filepath.Join(home.Dir(), "AppData", "Local")
	default:
		testDefaultDataHome = filepath.Join(home.Dir(), ".local", "share")
	}

	tests := []struct {
		name string
		env  string
		want string
		mode mode
	}{
		{
			name: "set env based specification",
			env:  testDefaultDataHome,
			want: testDefaultDataHome,
		},
		{
			name: "set env based different from specification",
			env:  filepath.Join(string(filepath.Separator), "tmp", "xdg", ".local", "share"),
			want: filepath.Join(string(filepath.Separator), "tmp", "xdg", ".local", "share"),
		},
		{
			name: "empty env",
			env:  "",
			want: testDefaultDataHome,
		},
	}
	for _, tt := range tests {
		os.Setenv("XDG_DATA_HOME", tt.env)
		t.Run(tt.name, func(t *testing.T) {
			if got := DataHome(); got != tt.want {
				t.Errorf("DataHome() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestConfigHome(t *testing.T) {
	var testDefaultConfigHome string
	switch runtime.GOOS {
	case "windows":
		testDefaultConfigHome = filepath.Join(home.Dir(), "AppData", "Local")
	default:
		testDefaultConfigHome = filepath.Join(home.Dir(), ".config")
	}

	tests := []struct {
		name string
		env  string
		want string
	}{
		{
			name: "set env based specification",
			env:  testDefaultConfigHome,
			want: testDefaultConfigHome,
		},
		{
			name: "set env based different from specification",
			env:  filepath.Join(string(filepath.Separator), "tmp", "config"),
			want: filepath.Join(string(filepath.Separator), "tmp", "config"),
		},
		{
			name: "empty env",
			env:  "",
			want: testDefaultConfigHome,
		},
	}
	for _, tt := range tests {
		os.Setenv("XDG_CONFIG_HOME", tt.env)
		t.Run(tt.name, func(t *testing.T) {
			if got := ConfigHome(); got != tt.want {
				t.Errorf("ConfigHome() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestDataDirs(t *testing.T) {
	var testDefaultDataDirs string
	switch runtime.GOOS {
	case "windows":
		testDefaultDataDirs = filepath.Join(home.Dir(), "AppData", "Local")
	default:
		testDefaultDataDirs = filepath.Join("/usr", "local", "share") + string(filepath.ListSeparator) + filepath.Join("/usr", "share")
	}

	tests := []struct {
		name string
		env  string
		want string
	}{
		{
			name: "set env based specification",
			env:  testDefaultDataDirs,
			want: testDefaultDataDirs,
		},
		{
			name: "set env based different from specification",
			env:  filepath.Join("/opt", "local", "share"),
			want: filepath.Join("/opt", "local", "share"),
		},
		{
			name: "empty env",
			env:  "",
			want: testDefaultDataDirs,
		},
	}
	for _, tt := range tests {
		os.Setenv("XDG_DATA_DIRS", tt.env)
		t.Run(tt.name, func(t *testing.T) {
			if got := DataDirs(); got != tt.want {
				t.Errorf("DataDirs() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestConfigDirs(t *testing.T) {
	var testDefaultConfigDirs string
	switch runtime.GOOS {
	case "windows":
		testDefaultConfigDirs = filepath.Join(home.Dir(), "AppData", "Local")
	default:
		testDefaultConfigDirs = filepath.Join("/etc", "xdg")
	}

	tests := []struct {
		name string
		env  string
		want string
	}{
		{
			name: "set env based specification",
			env:  testDefaultConfigDirs,
			want: testDefaultConfigDirs,
		},
		{
			name: "set env based different from specification",
			env:  filepath.Join(string(filepath.Separator), "var", "etc", "xdg"),
			want: filepath.Join(string(filepath.Separator), "var", "etc", "xdg"),
		},
		{
			name: "empty env",
			env:  "",
			want: testDefaultConfigDirs,
		},
	}
	for _, tt := range tests {
		os.Setenv("XDG_CONFIG_DIRS", tt.env)
		t.Run(tt.name, func(t *testing.T) {
			if got := ConfigDirs(); got != tt.want {
				t.Errorf("ConfigDirs() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestCacheHome(t *testing.T) {
	var testDefaultCacheHome string
	switch runtime.GOOS {
	case "windows":
		testDefaultCacheHome = filepath.Join(home.Dir(), "AppData", "Local", "cache")
	default:
		testDefaultCacheHome = filepath.Join(home.Dir(), ".cache")
	}

	tests := []struct {
		name string
		env  string
		want string
	}{
		{
			name: "set env based specification",
			env:  testDefaultCacheHome,
			want: testDefaultCacheHome,
		},
		{
			name: "set env based different from specification",
			env:  filepath.Join(string(filepath.Separator), "tmp", "cache"),
			want: filepath.Join(string(filepath.Separator), "tmp", "cache"),
		},
		{
			name: "empty env",
			env:  "",
			want: testDefaultCacheHome,
		},
	}
	for _, tt := range tests {
		os.Setenv("XDG_CACHE_HOME", tt.env)
		t.Run(tt.name, func(t *testing.T) {
			if got := CacheHome(); got != tt.want {
				t.Errorf("CacheHome() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestRuntimeDir(t *testing.T) {
	var testDefaultRuntimeDir string
	switch runtime.GOOS {
	case "windows":
		testDefaultRuntimeDir = home.Dir()
	default:
		testDefaultRuntimeDir = filepath.Join("/run", "user", strconv.Itoa(os.Getuid()))
	}

	tests := []struct {
		name string
		env  string
		want string
	}{
		{
			name: "set env based specification",
			env:  testDefaultRuntimeDir,
			want: testDefaultRuntimeDir,
		},
		{
			name: "set env based different from specification",
			env:  filepath.Join(string(filepath.Separator), "tmp", "user", "1000"),
			want: filepath.Join(string(filepath.Separator), "tmp", "user", "1000"),
		},
		{
			name: "empty env",
			env:  "",
			want: testDefaultRuntimeDir,
		},
	}
	for _, tt := range tests {
		os.Setenv("XDG_RUNTIME_DIR", tt.env)
		t.Run(tt.name, func(t *testing.T) {
			if got := RuntimeDir(); got != tt.want {
				t.Errorf("RuntimeDir() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestNativeMode(t *testing.T) {
	// skip test if not darwin
	if runtime.GOOS != "darwin" {
		t.Skip("native mode for darwin only")
	}

	Mode = Native
	initOnce = sync.Once{}

	tests := []struct {
		name string
		fn   string
		want string
	}{
		{
			name: "DataHome",
			fn:   DataHome(),
			want: filepath.Join(home.Dir(), "Library", "Application Support"),
		},
		{
			name: "ConfigHome",
			fn:   ConfigHome(),
			want: filepath.Join(home.Dir(), "Library", "Preferences"),
		},
		{
			name: "DataDirs",
			fn:   DataDirs(),
			want: filepath.Join(home.Dir(), "Library", "Application Support"),
		},
		{
			name: "ConfigDirs",
			fn:   ConfigDirs(),
			want: filepath.Join(home.Dir(), "Library", "Preferences"),
		},
		{
			name: "CacheHome",
			fn:   CacheHome(),
			want: filepath.Join(home.Dir(), "Library", "Caches"),
		},
		{
			name: "RuntimeDir",
			fn:   RuntimeDir(),
			want: filepath.Join(home.Dir(), "Library", "Application Support"),
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := tt.fn; got != tt.want {
				t.Errorf("NativeMode() = %v, want %v", got, tt.want)
			}
		})
	}
}

func Test_expandUser(t *testing.T) {
	usr, err := user.Current()
	if err != nil {
		t.Fatal(err)
	}

	type args struct {
		s string
	}
	tests := []struct {
		name string
		args args
		want string
	}{
		{
			name: "have tilda",
			args: args{s: filepath.Join("~/tmp", ".config")},
			want: filepath.ToSlash(filepath.Join(usr.HomeDir, "tmp", ".config")),
		},
		{
			name: "tilda only",
			args: args{s: "~/"},
			want: filepath.ToSlash(usr.HomeDir),
		},
		{
			name: "no tilda with root",
			args: args{s: filepath.Join("/tmp", ".config")},
			want: filepath.Join("/tmp", ".config"),
		},
		{
			name: "no tilda with related",
			args: args{s: filepath.Join("test", "related")},
			want: filepath.Join("test", "related"),
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := expandUser(tt.args.s); got != tt.want {
				t.Errorf("expandUser(%v) = %v, want %v", tt.args.s, got, tt.want)
			}
		})
	}
}

func BenchmarkDataHome(b *testing.B) {
	for i := 0; i < b.N; i++ {
		DataHome()
	}
}

func BenchmarkConfigHome(b *testing.B) {
	for i := 0; i < b.N; i++ {
		ConfigHome()
	}
}

func BenchmarkDataDirs(b *testing.B) {
	for i := 0; i < b.N; i++ {
		DataDirs()
	}
}

func BenchmarkConfigDirs(b *testing.B) {
	for i := 0; i < b.N; i++ {
		ConfigHome()
	}
}

func BenchmarkCacheHome(b *testing.B) {
	for i := 0; i < b.N; i++ {
		CacheHome()
	}
}

func BenchmarkRuntimeDir(b *testing.B) {
	for i := 0; i < b.N; i++ {
		RuntimeDir()
	}
}

func Benchmark_expandUser(b *testing.B) {
	for i := 0; i < b.N; i++ {
		expandUser("")
	}
}
<commit_msg>xdgbasedir: remove filepath.Separator for root path on test<commit_after>// Copyright 2017 The go-xdgbasedir Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package xdgbasedir

import (
	"os"
	"os/user"
	"path/filepath"
	"runtime"
	"strconv"
	"sync"
	"testing"

	"github.com/zchee/go-xdgbasedir/home"
)

func TestDataHome(t *testing.T) {
	var testDefaultDataHome string
	switch runtime.GOOS {
	case "windows":
		testDefaultDataHome = filepath.Join(home.Dir(), "AppData", "Local")
	default:
		testDefaultDataHome = filepath.Join(home.Dir(), ".local", "share")
	}

	tests := []struct {
		name string
		env  string
		want string
		mode mode
	}{
		{
			name: "set env based specification",
			env:  testDefaultDataHome,
			want: testDefaultDataHome,
		},
		{
			name: "set env based different from specification",
			env:  filepath.Join("/tmp", "xdg", ".local", "share"),
			want: filepath.Join("/tmp", "xdg", ".local", "share"),
		},
		{
			name: "empty env",
			env:  "",
			want: testDefaultDataHome,
		},
	}
	for _, tt := range tests {
		os.Setenv("XDG_DATA_HOME", tt.env)
		t.Run(tt.name, func(t *testing.T) {
			if got := DataHome(); got != tt.want {
				t.Errorf("DataHome() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestConfigHome(t *testing.T) {
	var testDefaultConfigHome string
	switch runtime.GOOS {
	case "windows":
		testDefaultConfigHome = filepath.Join(home.Dir(), "AppData", "Local")
	default:
		testDefaultConfigHome = filepath.Join(home.Dir(), ".config")
	}

	tests := []struct {
		name string
		env  string
		want string
	}{
		{
			name: "set env based specification",
			env:  testDefaultConfigHome,
			want: testDefaultConfigHome,
		},
		{
			name: "set env based different from specification",
			env:  filepath.Join("/tmp", "config"),
			want: filepath.Join("/tmp", "config"),
		},
		{
			name: "empty env",
			env:  "",
			want: testDefaultConfigHome,
		},
	}
	for _, tt := range tests {
		os.Setenv("XDG_CONFIG_HOME", tt.env)
		t.Run(tt.name, func(t *testing.T) {
			if got := ConfigHome(); got != tt.want {
				t.Errorf("ConfigHome() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestDataDirs(t *testing.T) {
	var testDefaultDataDirs string
	switch runtime.GOOS {
	case "windows":
		testDefaultDataDirs = filepath.Join(home.Dir(), "AppData", "Local")
	default:
		testDefaultDataDirs = filepath.Join("/usr", "local", "share") + string(filepath.ListSeparator) + filepath.Join("/usr", "share")
	}

	tests := []struct {
		name string
		env  string
		want string
	}{
		{
			name: "set env based specification",
			env:  testDefaultDataDirs,
			want: testDefaultDataDirs,
		},
		{
			name: "set env based different from specification",
			env:  filepath.Join("/opt", "local", "share"),
			want: filepath.Join("/opt", "local", "share"),
		},
		{
			name: "empty env",
			env:  "",
			want: testDefaultDataDirs,
		},
	}
	for _, tt := range tests {
		os.Setenv("XDG_DATA_DIRS", tt.env)
		t.Run(tt.name, func(t *testing.T) {
			if got := DataDirs(); got != tt.want {
				t.Errorf("DataDirs() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestConfigDirs(t *testing.T) {
	var testDefaultConfigDirs string
	switch runtime.GOOS {
	case "windows":
		testDefaultConfigDirs = filepath.Join(home.Dir(), "AppData", "Local")
	default:
		testDefaultConfigDirs = filepath.Join("/etc", "xdg")
	}

	tests := []struct {
		name string
		env  string
		want string
	}{
		{
			name: "set env based specification",
			env:  testDefaultConfigDirs,
			want: testDefaultConfigDirs,
		},
		{
			name: "set env based different from specification",
			env:  filepath.Join("/var", "etc", "xdg"),
			want: filepath.Join("/var", "etc", "xdg"),
		},
		{
			name: "empty env",
			env:  "",
			want: testDefaultConfigDirs,
		},
	}
	for _, tt := range tests {
		os.Setenv("XDG_CONFIG_DIRS", tt.env)
		t.Run(tt.name, func(t *testing.T) {
			if got := ConfigDirs(); got != tt.want {
				t.Errorf("ConfigDirs() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestCacheHome(t *testing.T) {
	var testDefaultCacheHome string
	switch runtime.GOOS {
	case "windows":
		testDefaultCacheHome = filepath.Join(home.Dir(), "AppData", "Local", "cache")
	default:
		testDefaultCacheHome = filepath.Join(home.Dir(), ".cache")
	}

	tests := []struct {
		name string
		env  string
		want string
	}{
		{
			name: "set env based specification",
			env:  testDefaultCacheHome,
			want: testDefaultCacheHome,
		},
		{
			name: "set env based different from specification",
			env:  filepath.Join("/tmp", "cache"),
			want: filepath.Join("/tmp", "cache"),
		},
		{
			name: "empty env",
			env:  "",
			want: testDefaultCacheHome,
		},
	}
	for _, tt := range tests {
		os.Setenv("XDG_CACHE_HOME", tt.env)
		t.Run(tt.name, func(t *testing.T) {
			if got := CacheHome(); got != tt.want {
				t.Errorf("CacheHome() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestRuntimeDir(t *testing.T) {
	var testDefaultRuntimeDir string
	switch runtime.GOOS {
	case "windows":
		testDefaultRuntimeDir = home.Dir()
	default:
		testDefaultRuntimeDir = filepath.Join("/run", "user", strconv.Itoa(os.Getuid()))
	}

	tests := []struct {
		name string
		env  string
		want string
	}{
		{
			name: "set env based specification",
			env:  testDefaultRuntimeDir,
			want: testDefaultRuntimeDir,
		},
		{
			name: "set env based different from specification",
			env:  filepath.Join("/tmp", "user", "1000"),
			want: filepath.Join("/tmp", "user", "1000"),
		},
		{
			name: "empty env",
			env:  "",
			want: testDefaultRuntimeDir,
		},
	}
	for _, tt := range tests {
		os.Setenv("XDG_RUNTIME_DIR", tt.env)
		t.Run(tt.name, func(t *testing.T) {
			if got := RuntimeDir(); got != tt.want {
				t.Errorf("RuntimeDir() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestNativeMode(t *testing.T) {
	// skip test if not darwin
	if runtime.GOOS != "darwin" {
		t.Skip("native mode for darwin only")
	}

	Mode = Native
	initOnce = sync.Once{}

	tests := []struct {
		name string
		fn   string
		want string
	}{
		{
			name: "DataHome",
			fn:   DataHome(),
			want: filepath.Join(home.Dir(), "Library", "Application Support"),
		},
		{
			name: "ConfigHome",
			fn:   ConfigHome(),
			want: filepath.Join(home.Dir(), "Library", "Preferences"),
		},
		{
			name: "DataDirs",
			fn:   DataDirs(),
			want: filepath.Join(home.Dir(), "Library", "Application Support"),
		},
		{
			name: "ConfigDirs",
			fn:   ConfigDirs(),
			want: filepath.Join(home.Dir(), "Library", "Preferences"),
		},
		{
			name: "CacheHome",
			fn:   CacheHome(),
			want: filepath.Join(home.Dir(), "Library", "Caches"),
		},
		{
			name: "RuntimeDir",
			fn:   RuntimeDir(),
			want: filepath.Join(home.Dir(), "Library", "Application Support"),
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := tt.fn; got != tt.want {
				t.Errorf("NativeMode() = %v, want %v", got, tt.want)
			}
		})
	}
}

func Test_expandUser(t *testing.T) {
	usr, err := user.Current()
	if err != nil {
		t.Fatal(err)
	}

	type args struct {
		s string
	}
	tests := []struct {
		name string
		args args
		want string
	}{
		{
			name: "have tilda",
			args: args{s: filepath.Join("~/tmp", ".config")},
			want: filepath.ToSlash(filepath.Join(usr.HomeDir, "tmp", ".config")),
		},
		{
			name: "tilda only",
			args: args{s: "~/"},
			want: filepath.ToSlash(usr.HomeDir),
		},
		{
			name: "no tilda with root",
			args: args{s: filepath.Join("/tmp", ".config")},
			want: filepath.Join("/tmp", ".config"),
		},
		{
			name: "no tilda with related",
			args: args{s: filepath.Join("test", "related")},
			want: filepath.Join("test", "related"),
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := expandUser(tt.args.s); got != tt.want {
				t.Errorf("expandUser(%v) = %v, want %v", tt.args.s, got, tt.want)
			}
		})
	}
}

func BenchmarkDataHome(b *testing.B) {
	for i := 0; i < b.N; i++ {
		DataHome()
	}
}

func BenchmarkConfigHome(b *testing.B) {
	for i := 0; i < b.N; i++ {
		ConfigHome()
	}
}

func BenchmarkDataDirs(b *testing.B) {
	for i := 0; i < b.N; i++ {
		DataDirs()
	}
}

func BenchmarkConfigDirs(b *testing.B) {
	for i := 0; i < b.N; i++ {
		ConfigHome()
	}
}

func BenchmarkCacheHome(b *testing.B) {
	for i := 0; i < b.N; i++ {
		CacheHome()
	}
}

func BenchmarkRuntimeDir(b *testing.B) {
	for i := 0; i < b.N; i++ {
		RuntimeDir()
	}
}

func Benchmark_expandUser(b *testing.B) {
	for i := 0; i < b.N; i++ {
		expandUser("")
	}
}
<|endoftext|>


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

package flat

import "testing"

func TestSerialize(t *testing.T) {
	facts, err := Get()
	if err != nil {
		t.Errorf("unexpected error: %s", err)
	}
	factsD := Deserialize(facts)
	if factsD.Timestamp == 0 {
		t.Error("timestamp: expected non-zero timestamp")
	}
	if len(factsD.CPU) == 0 {
		t.Error("expected at least 1 cpu entries; got none")
	}
	for i := 0; i < len(factsD.CPU); i++ {
		if factsD.CPU[i].VendorID == "" {
			t.Errorf("VendorID: expected Vendor ID to not be empty, it was", i)
		}
		if factsD.CPU[i].Model == "" {
			t.Errorf("Model: expected model to not be empty; it was", i)
		}
		if factsD.CPU[i].CPUCores == 0 {
			t.Errorf("CPUCores %d: expected non-zero value; was 0", i)
		}
	}
	_, err = Serialize(factsD)
	if err != nil {
		t.Errorf("unexpected serialization error: %s", err)
		return
	}
}
<commit_msg>add benchmarks to cpu facts flatbuffer<commit_after>// Copyright 2016 Joel Scoble and The JoeFriday authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package flat

import (
	"testing"

	"github.com/mohae/joefriday/cpu/facts"
)

func TestSerialize(t *testing.T) {
	facts, err := Get()
	if err != nil {
		t.Errorf("unexpected error: %s", err)
	}
	factsD := Deserialize(facts)
	if factsD.Timestamp == 0 {
		t.Error("timestamp: expected non-zero timestamp")
	}
	if len(factsD.CPU) == 0 {
		t.Error("expected at least 1 cpu entries; got none")
	}
	for i := 0; i < len(factsD.CPU); i++ {
		if factsD.CPU[i].VendorID == "" {
			t.Errorf("%d: VendorID: expected Vendor ID to not be empty, it was", i)
		}
		if factsD.CPU[i].Model == "" {
			t.Errorf("%d: Model: expected model to not be empty; it was", i)
		}
		if factsD.CPU[i].CPUCores == 0 {
			t.Errorf("%d: CPUCores: expected non-zero value; was 0", i)
		}
	}
	_, err = Serialize(factsD)
	if err != nil {
		t.Errorf("unexpected serialization error: %s", err)
		return
	}
}

var inf *facts.Facts

func BenchmarkGet(b *testing.B) {
	var jsn []byte
	p, _ := New()
	for i := 0; i < b.N; i++ {
		jsn, _ = p.Get()
	}
	_ = jsn
}

func BenchmarkDeserialize(b *testing.B) {
	p, _ := New()
	infB, _ := p.Get()
	for i := 0; i < b.N; i++ {
		inf = Deserialize(infB)
	}
	_ = inf
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"sync"

	"github.com/google/go-github/github"

	"github.com/wsxiaoys/terminal"
	"github.com/wsxiaoys/terminal/color"
)

var (
	mut sync.Mutex
)

func NewPullRequestor(config *Config, client *github.Client) *PullRequestor {
	return &PullRequestor{config, client}
}

type PullRequestor struct {
	config *Config
	client *github.Client
}

func (self *PullRequestor) printPullRequest(repo string, pr github.PullRequest, wg *sync.WaitGroup) {
	comments, _, err := self.client.PullRequests.ListComments(self.config.Organization, repo, *pr.Number, nil)

	if err != nil {
		log.Fatal(err)
	}

	// Keep the mutex down here so we can make the list requests in parallel, but
	// not clobber stdout while printing stuff. A better approach would be to
	// throw this in a channel.
	mut.Lock()

	color.Printf("@w[%d] %s\n", *pr.Number, *pr.Title)

	if pr.Body == nil || len(*pr.Body) == 0 {
		color.Println("@r<no body>")
	} else {
		color.Printf("@b%s\n", (*pr.Body)[:120])
	}

	color.Printf("@{/}(%d comments)\n", len(comments))
	color.Printf("@g%s\n", (*pr.HTMLURL))
	color.Println()
	mut.Unlock()

	// Signals we're done printing this one element.
	wg.Done()
}

func (self *PullRequestor) PrintPullRequests(repo string) {
	prs, _, err := self.client.PullRequests.List(self.config.Organization, repo, nil)

	if err != nil {
		log.Fatal(err)
	}

	if len(prs) == 0 {
		return
	}

	terminal.Stdout.Color("y")

	// Get color codes here: https://github.com/wsxiaoys/terminal/blob/master/color/color.go
	color.Println("================================================================================")
	color.Printf("@{!m}**** Pull requests for [%s]", repo)
	color.Println()

	var wg sync.WaitGroup

	for _, pr := range prs {
		wg.Add(1)
		go self.printPullRequest(repo, pr, &wg)
	}

	// Wait for the threads to finish their stuff.
	wg.Wait()
}

func (self *PullRequestor) ListRepos() {
	// list all repositories for the authenticated user
	for _, repo := range self.config.Repositories {
		self.PrintPullRequests(repo)
	}
}
<commit_msg>Check for body length before slicing<commit_after>package main

import (
	"log"
	"sync"

	"github.com/google/go-github/github"

	"github.com/wsxiaoys/terminal"
	"github.com/wsxiaoys/terminal/color"
)

var (
	mut sync.Mutex
)

func NewPullRequestor(config *Config, client *github.Client) *PullRequestor {
	return &PullRequestor{config, client}
}

type PullRequestor struct {
	config *Config
	client *github.Client
}

func (self *PullRequestor) printPullRequest(repo string, pr github.PullRequest, wg *sync.WaitGroup) {
	comments, _, err := self.client.PullRequests.ListComments(self.config.Organization, repo, *pr.Number, nil)

	if err != nil {
		log.Fatal(err)
	}

	// Keep the mutex down here so we can make the list requests in parallel, but
	// not clobber stdout while printing stuff. A better approach would be to
	// throw this in a channel.
	mut.Lock()

	color.Printf("@w[%d] %s\n", *pr.Number, *pr.Title)

	if pr.Body == nil || len(*pr.Body) == 0 {
		color.Println("@r<no body>")
	} else {
		bodyLen := len(*pr.Body)
		if bodyLen > 120 {
			bodyLen = 120
		}

		color.Printf("@b%s\n", (*pr.Body)[:bodyLen])
	}

	color.Printf("@{/}(%d comments)\n", len(comments))
	color.Printf("@g%s\n", (*pr.HTMLURL))
	color.Println()
	mut.Unlock()

	// Signals we're done printing this one element.
	wg.Done()
}

func (self *PullRequestor) PrintPullRequests(repo string) {
	prs, _, err := self.client.PullRequests.List(self.config.Organization, repo, nil)

	if err != nil {
		log.Fatal(err)
	}

	if len(prs) == 0 {
		return
	}

	terminal.Stdout.Color("y")

	// Get color codes here: https://github.com/wsxiaoys/terminal/blob/master/color/color.go
	color.Println("================================================================================")
	color.Printf("@{!m}**** Pull requests for [%s]", repo)
	color.Println()

	var wg sync.WaitGroup

	for _, pr := range prs {
		wg.Add(1)
		go self.printPullRequest(repo, pr, &wg)
	}

	// Wait for the threads to finish their stuff.
	wg.Wait()
}

func (self *PullRequestor) ListRepos() {
	// list all repositories for the authenticated user
	for _, repo := range self.config.Repositories {
		self.PrintPullRequests(repo)
	}
}
<|endoftext|>


<commit_before>package xy

// Adopted from https://github.com/paulmach/orb/blob/master/simplify/douglas_peucker.go
// Original licence:
//
// The MIT License (MIT)
// Copyright (c) 2017 Paul Mach Permission is hereby granted, free of charge, to
// any person obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the following
// conditions: The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software. THE SOFTWARE
// IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// SimplifyFlatCoords uses the Douglas-Peucker algorithm to simplify a 2D
// flatCoords. It returns the indexes of the points. Note that the indexes are
// based on points, So acesss to x, y pair should be:
//
//  x := flatCoords[i*stride]
//  y := flatCoords[i*stride+1]
//
// Threshold is the distance between a point and the selected start
// and end line segment. It returns the indexes of the points.
func SimplifyFlatCoords(flatCoords []float64, threshold float64, stride int) []int {
	size := len(flatCoords) / stride
	if size < 3 {
		ret := make([]int, size)
		for i := 0; i < size; i++ {
			ret[i] = i
		}
		return ret
	}
	mask := make([]byte, size)
	// first and last always added
	mask[0] = 1
	mask[len(mask)-1] = 1

	found := dpWorker(flatCoords, threshold, mask, stride)
	indexMap := make([]int, 0, found)

	for i, v := range mask {
		if v == 1 {
			indexMap = append(indexMap, i)
		}
	}

	return indexMap
}

// dpWorker does the recursive threshold checks.
// Using a stack array with a stackLength variable resulted in
// 4x speed improvement over calling the function recursively.
func dpWorker(ls []float64, threshold float64, mask []byte, stride int) int {
	found := 2

	var stack []int
	stack = append(stack, 0, len(ls)/stride-1)

	l := len(stack)
	for l > 0 {
		start := stack[l-2]
		end := stack[l-1]

		// modify the line in place
		maxDist := 0.0
		maxIndex := 0
		a := ls[start*stride : start*stride+stride]
		b := ls[end*stride : end*stride+stride]

		for i := start + 1; i < end; i++ {
			p := ls[i*stride : i*stride+stride]
			dist := distanceFromSegmentSquared(a, b, p)
			if dist > maxDist {
				maxDist = dist
				maxIndex = i
			}
		}

		if maxDist > threshold*threshold {
			found++
			mask[maxIndex] = 1

			stack[l-1] = maxIndex
			stack = append(stack, maxIndex, end)
		} else {
			stack = stack[:l-2]
		}
		l = len(stack)
	}

	return found
}

// distanceFromSegmentSquared returns point's squared distance from the segment [a, b].
func distanceFromSegmentSquared(a, b, point []float64) float64 {
	x := a[0]
	y := a[1]
	dx := b[0] - x
	dy := b[1] - y

	if dx != 0 || dy != 0 {
		t := ((point[0]-x)*dx + (point[1]-y)*dy) / (dx*dx + dy*dy)

		if t > 1 {
			x = b[0]
			y = b[1]
		} else if t > 0 {
			x += dx * t
			y += dy * t
		}
	}

	dx = point[0] - x
	dy = point[1] - y

	return dx*dx + dy*dy
}
<commit_msg>remove outdated comment<commit_after>package xy

// Adopted from https://github.com/paulmach/orb/blob/master/simplify/douglas_peucker.go
// Original licence:
//
// The MIT License (MIT)
// Copyright (c) 2017 Paul Mach Permission is hereby granted, free of charge, to
// any person obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the following
// conditions: The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software. THE SOFTWARE
// IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// SimplifyFlatCoords uses the Douglas-Peucker algorithm to simplify a 2D
// flatCoords. It returns the indexes of the points. Note that the indexes are
// based on points, So acesss to x, y pair should be:
//
//  x := flatCoords[i*stride]
//  y := flatCoords[i*stride+1]
//
// Threshold is the distance between a point and the selected start
// and end line segment. It returns the indexes of the points.
func SimplifyFlatCoords(flatCoords []float64, threshold float64, stride int) []int {
	size := len(flatCoords) / stride
	if size < 3 {
		ret := make([]int, size)
		for i := 0; i < size; i++ {
			ret[i] = i
		}
		return ret
	}
	mask := make([]byte, size)
	// first and last always added
	mask[0] = 1
	mask[len(mask)-1] = 1

	found := dpWorker(flatCoords, threshold, mask, stride)
	indexMap := make([]int, 0, found)

	for i, v := range mask {
		if v == 1 {
			indexMap = append(indexMap, i)
		}
	}

	return indexMap
}

// dpWorker does the recursive threshold checks.
// Using a stack array with a stackLength variable resulted in
// 4x speed improvement over calling the function recursively.
func dpWorker(ls []float64, threshold float64, mask []byte, stride int) int {
	found := 2

	var stack []int
	stack = append(stack, 0, len(ls)/stride-1)

	l := len(stack)
	for l > 0 {
		start := stack[l-2]
		end := stack[l-1]

		maxDist := 0.0
		maxIndex := 0
		a := ls[start*stride : start*stride+stride]
		b := ls[end*stride : end*stride+stride]

		for i := start + 1; i < end; i++ {
			p := ls[i*stride : i*stride+stride]
			dist := distanceFromSegmentSquared(a, b, p)
			if dist > maxDist {
				maxDist = dist
				maxIndex = i
			}
		}

		if maxDist > threshold*threshold {
			found++
			mask[maxIndex] = 1

			stack[l-1] = maxIndex
			stack = append(stack, maxIndex, end)
		} else {
			stack = stack[:l-2]
		}
		l = len(stack)
	}

	return found
}

// distanceFromSegmentSquared returns point's squared distance from the segment [a, b].
func distanceFromSegmentSquared(a, b, point []float64) float64 {
	x := a[0]
	y := a[1]
	dx := b[0] - x
	dy := b[1] - y

	if dx != 0 || dy != 0 {
		t := ((point[0]-x)*dx + (point[1]-y)*dy) / (dx*dx + dy*dy)

		if t > 1 {
			x = b[0]
			y = b[1]
		} else if t > 0 {
			x += dx * t
			y += dy * t
		}
	}

	dx = point[0] - x
	dy = point[1] - y

	return dx*dx + dy*dy
}
<|endoftext|>


<commit_before>package postgis

import (
	"encoding/json"
	"fmt"

	"github.com/geodan/gost/src/sensorthings/entities"

	"database/sql"
	"errors"

	gostErrors "github.com/geodan/gost/src/errors"
	"github.com/geodan/gost/src/sensorthings/odata"
)

var lMapping = map[string]string{"location": "public.ST_AsGeoJSON(location.location)"}

// GetLocation retrieves the location for the given id from the database
func (gdb *GostDatabase) GetLocation(id interface{}, qo *odata.QueryOptions) (*entities.Location, error) {
	intID, ok := ToIntID(id)
	if !ok {
		return nil, gostErrors.NewRequestNotFound(errors.New("Location does not exist"))
	}

	sql := fmt.Sprintf("select "+CreateSelectString(&entities.Location{}, qo, "", "", lMapping)+" AS location from %s.location where id = %v", gdb.Schema, intID)
	return processLocation(gdb.Db, sql, qo)
}

// GetLocations retrieves all locations
func (gdb *GostDatabase) GetLocations(qo *odata.QueryOptions) ([]*entities.Location, int, error) {
	sql := fmt.Sprintf("select "+CreateSelectString(&entities.Location{}, qo, "", "", lMapping)+" AS location from %s.location order by id desc "+CreateTopSkipQueryString(qo), gdb.Schema)
	countSQL := fmt.Sprintf("select COUNT(*) FROM %s.location", gdb.Schema)
	return processLocations(gdb.Db, sql, qo, countSQL)
}

// GetLocationsByHistoricalLocation retrieves all locations linked to the given HistoricalLocation
func (gdb *GostDatabase) GetLocationsByHistoricalLocation(hlID interface{}, qo *odata.QueryOptions) ([]*entities.Location, int, error) {
	intID, ok := ToIntID(hlID)
	if !ok {
		return nil, 0, gostErrors.NewRequestNotFound(errors.New("HistoricaLocation does not exist"))
	}

	sql := fmt.Sprintf("select "+CreateSelectString(&entities.Location{}, qo, "location.", "", lMapping)+" AS location from %s.location inner join %s.historicallocation on historicallocation.location_id = location.id where historicallocation.id = %v order by id desc limit 1", gdb.Schema, gdb.Schema, intID)
	countSQL := fmt.Sprintf("select COUNT(*) FROM %s.location inner join %s.historicallocation on historicallocation.location_id = location.id where historicallocation.id = %v", gdb.Schema, gdb.Schema, intID)
	return processLocations(gdb.Db, sql, qo, countSQL)
}

// GetLocationByDatastreamID returns a location linked to an observation
func (gdb *GostDatabase) GetLocationByDatastreamID(datastreamID interface{}) (*entities.Location, error) {
	intID, ok := ToIntID(datastreamID)
	if !ok {
		return nil, gostErrors.NewRequestNotFound(errors.New("Datastream does not exist"))
	}

	sql := fmt.Sprintf("SELECT "+CreateSelectString(&entities.Location{}, nil, "location.", "", lMapping)+" FROM %s.location INNER JOIN %s.thing_to_location on location.id = thing_to_location.location_id INNER JOIN %s.datastream on thing_to_location.thing_id = datastream.thing_id WHERE datastream.id = %v ORDER BY location.id DESC LIMIT 1", gdb.Schema, gdb.Schema, gdb.Schema, intID)
	return processLocation(gdb.Db, sql, nil)
}

// GetLocationsByThing retrieves all locations linked to the given thing
func (gdb *GostDatabase) GetLocationsByThing(thingID interface{}, qo *odata.QueryOptions) ([]*entities.Location, int, error) {
	intID, ok := ToIntID(thingID)
	if !ok {
		return nil, 0, gostErrors.NewRequestNotFound(errors.New("Thing does not exist"))
	}

	sql := fmt.Sprintf("select "+CreateSelectString(&entities.Location{}, qo, "location.", "", lMapping)+" AS location from %s.location inner join %s.thing_to_location on thing_to_location.location_id = location.id where thing_to_location.thing_id = %v order by id desc limit 1", gdb.Schema, gdb.Schema, intID)
	countSQL := fmt.Sprintf("select COUNT(*) FROM %s.location inner join %s.thing_to_location on thing_to_location.location_id = location.id where thing_to_location.thing_id = %v", gdb.Schema, gdb.Schema, intID)
	return processLocations(gdb.Db, sql, qo, countSQL)
}

func processLocation(db *sql.DB, sql string, qo *odata.QueryOptions) (*entities.Location, error) {
	locations, _, err := processLocations(db, sql, qo, "")
	if err != nil {
		return nil, err
	}

	if len(locations) == 0 {
		return nil, gostErrors.NewRequestNotFound(errors.New("Location not found"))
	}

	return locations[0], nil
}

func processLocations(db *sql.DB, sql string, qo *odata.QueryOptions, countSQL string) ([]*entities.Location, int, error) {
	rows, err := db.Query(sql)
	defer rows.Close()
	if err != nil {
		return nil, 0, err
	}

	var locations = []*entities.Location{}
	for rows.Next() {
		var sensorID interface{}
		var encodingType int
		var name, description, location string

		var params []interface{}
		var qp []string
		if qo == nil || qo.QuerySelect == nil || len(qo.QuerySelect.Params) == 0 {
			s := &entities.Location{}
			qp = s.GetPropertyNames()
		} else {
			qp = qo.QuerySelect.Params
		}

		for _, p := range qp {
			if p == "id" {
				params = append(params, &sensorID)
			}
			if p == "encodingType" {
				params = append(params, &encodingType)
			}
			if p == "name" {
				params = append(params, &name)
			}
			if p == "description" {
				params = append(params, &description)
			}
			if p == "location" {
				params = append(params, &location)
			}
		}

		err = rows.Scan(params...)
		if err != nil {
			return nil, 0, err
		}

		locationMap, err := JSONToMap(&location)
		if err != nil {
			return nil, 0, err
		}

		l := entities.Location{}
		l.ID = sensorID
		l.Name = name
		l.Description = description
		l.Location = locationMap
		if encodingType != 0 {
			l.EncodingType = entities.EncodingValues[encodingType].Value
		}
		locations = append(locations, &l)
	}

	var count int
	if len(countSQL) > 0 {
		db.QueryRow(countSQL).Scan(&count)
	}

	return locations, count, nil
}

// PostLocation receives a posted location entity and adds it to the database
// returns the created Location including the generated id
func (gdb *GostDatabase) PostLocation(location *entities.Location) (*entities.Location, error) {
	var locationID int
	locationBytes, _ := json.Marshal(location.Location)
	encoding, _ := entities.CreateEncodingType(location.EncodingType)

	sql := fmt.Sprintf("INSERT INTO %s.location (name, description, encodingtype, location) VALUES ($1, $2, $3, ST_SetSRID(ST_GeomFromGeoJSON('%s'),4326)) RETURNING id", gdb.Schema, string(locationBytes[:]))
	err := gdb.Db.QueryRow(sql, location.Name, location.Description, encoding.Code).Scan(&locationID)
	if err != nil {
		return nil, err
	}

	location.ID = locationID
	return location, nil
}

// LocationExists checks if a location is present in the database based on a given id
func (gdb *GostDatabase) LocationExists(id interface{}) bool {
	return EntityExists(gdb, id, "location")
}

// PatchLocation updates a Location in the database
func (gdb *GostDatabase) PatchLocation(id interface{}, l *entities.Location) (*entities.Location, error) {
	var err error
	var ok bool
	var intID int
	updates := make(map[string]interface{})

	if intID, ok = ToIntID(id); !ok || !gdb.LocationExists(intID) {
		return nil, gostErrors.NewRequestNotFound(errors.New("Location does not exist"))
	}

	if len(l.Name) > 0 {
		updates["name"] = l.Name
	}

	if len(l.Description) > 0 {
		updates["description"] = l.Description
	}

	if len(l.Location) > 0 {
		locationBytes, _ := json.Marshal(l.Location)
		updates["location"] = fmt.Sprintf("ST_SetSRID(ST_GeomFromGeoJSON('%s'),4326)", string(locationBytes[:]))
	}

	if len(l.EncodingType) > 0 {
		encoding, _ := entities.CreateEncodingType(l.EncodingType)
		updates["encodingtype"] = encoding.Code
	}

	if err = gdb.updateEntityColumns("location", updates, intID); err != nil {
		return nil, err
	}

	ns, _ := gdb.GetLocation(intID, nil)
	return ns, nil
}

// DeleteLocation removes a given location from the database
func (gdb *GostDatabase) DeleteLocation(id interface{}) error {
	return DeleteEntity(gdb, id, "location")
}

// PutLocation receives a Location entity and changes it in the database
// returns the adapted Location
func (gdb *GostDatabase) PutLocation(id interface{}, location *entities.Location) (*entities.Location, error) {
	var intID int
	var ok bool
	if intID, ok = ToIntID(id); !ok || !gdb.LocationExists(intID) {
		return nil, gostErrors.NewRequestNotFound(errors.New("Location does not exist"))
	}

	locationBytes, _ := json.Marshal(location.Location)
	encoding, _ := entities.CreateEncodingType(location.EncodingType)

	sql := fmt.Sprintf("update %s.location set name=$1, description=$2, encodingtype=$3, location=ST_SetSRID(ST_GeomFromGeoJSON('%s'),4326) where id = $4", gdb.Schema, string(locationBytes[:]))
	_, err := gdb.Db.Exec(sql, location.Name, location.Description, encoding.Code, intID)
	if err != nil {
		return nil, err
	}

	nt, _ := gdb.GetLocation(intID, nil)
	return nt, nil
}

// LinkLocation links a thing with a location
// fails when a thing or location cannot be found for the given id's
func (gdb *GostDatabase) LinkLocation(thingID interface{}, locationID interface{}) error {
	tid, ok := ToIntID(thingID)
	if !ok || !gdb.ThingExists(tid) {
		return gostErrors.NewRequestNotFound(errors.New("Thing does not exist"))
	}

	lid, ok := ToIntID(locationID)
	if !ok || !gdb.LocationExists(lid) {
		return gostErrors.NewRequestNotFound(errors.New("Location does not exist"))
	}

	sql := fmt.Sprintf("INSERT INTO %s.thing_to_location (thing_id, location_id) VALUES ($1, $2)", gdb.Schema)
	_, err3 := gdb.Db.Exec(sql, tid, lid)
	if err3 != nil {
		return err3
	}

	return nil
}
<commit_msg>remove Location check encodingType == 0<commit_after>package postgis

import (
	"encoding/json"
	"fmt"

	"github.com/geodan/gost/src/sensorthings/entities"

	"database/sql"
	"errors"

	gostErrors "github.com/geodan/gost/src/errors"
	"github.com/geodan/gost/src/sensorthings/odata"
)

var lMapping = map[string]string{"location": "public.ST_AsGeoJSON(location.location)"}

// GetLocation retrieves the location for the given id from the database
func (gdb *GostDatabase) GetLocation(id interface{}, qo *odata.QueryOptions) (*entities.Location, error) {
	intID, ok := ToIntID(id)
	if !ok {
		return nil, gostErrors.NewRequestNotFound(errors.New("Location does not exist"))
	}

	sql := fmt.Sprintf("select "+CreateSelectString(&entities.Location{}, qo, "", "", lMapping)+" AS location from %s.location where id = %v", gdb.Schema, intID)
	return processLocation(gdb.Db, sql, qo)
}

// GetLocations retrieves all locations
func (gdb *GostDatabase) GetLocations(qo *odata.QueryOptions) ([]*entities.Location, int, error) {
	sql := fmt.Sprintf("select "+CreateSelectString(&entities.Location{}, qo, "", "", lMapping)+" AS location from %s.location order by id desc "+CreateTopSkipQueryString(qo), gdb.Schema)
	countSQL := fmt.Sprintf("select COUNT(*) FROM %s.location", gdb.Schema)
	return processLocations(gdb.Db, sql, qo, countSQL)
}

// GetLocationsByHistoricalLocation retrieves all locations linked to the given HistoricalLocation
func (gdb *GostDatabase) GetLocationsByHistoricalLocation(hlID interface{}, qo *odata.QueryOptions) ([]*entities.Location, int, error) {
	intID, ok := ToIntID(hlID)
	if !ok {
		return nil, 0, gostErrors.NewRequestNotFound(errors.New("HistoricaLocation does not exist"))
	}

	sql := fmt.Sprintf("select "+CreateSelectString(&entities.Location{}, qo, "location.", "", lMapping)+" AS location from %s.location inner join %s.historicallocation on historicallocation.location_id = location.id where historicallocation.id = %v order by id desc limit 1", gdb.Schema, gdb.Schema, intID)
	countSQL := fmt.Sprintf("select COUNT(*) FROM %s.location inner join %s.historicallocation on historicallocation.location_id = location.id where historicallocation.id = %v", gdb.Schema, gdb.Schema, intID)
	return processLocations(gdb.Db, sql, qo, countSQL)
}

// GetLocationByDatastreamID returns a location linked to an observation
func (gdb *GostDatabase) GetLocationByDatastreamID(datastreamID interface{}) (*entities.Location, error) {
	intID, ok := ToIntID(datastreamID)
	if !ok {
		return nil, gostErrors.NewRequestNotFound(errors.New("Datastream does not exist"))
	}

	sql := fmt.Sprintf("SELECT "+CreateSelectString(&entities.Location{}, nil, "location.", "", lMapping)+" FROM %s.location INNER JOIN %s.thing_to_location on location.id = thing_to_location.location_id INNER JOIN %s.datastream on thing_to_location.thing_id = datastream.thing_id WHERE datastream.id = %v ORDER BY location.id DESC LIMIT 1", gdb.Schema, gdb.Schema, gdb.Schema, intID)
	return processLocation(gdb.Db, sql, nil)
}

// GetLocationsByThing retrieves all locations linked to the given thing
func (gdb *GostDatabase) GetLocationsByThing(thingID interface{}, qo *odata.QueryOptions) ([]*entities.Location, int, error) {
	intID, ok := ToIntID(thingID)
	if !ok {
		return nil, 0, gostErrors.NewRequestNotFound(errors.New("Thing does not exist"))
	}

	sql := fmt.Sprintf("select "+CreateSelectString(&entities.Location{}, qo, "location.", "", lMapping)+" AS location from %s.location inner join %s.thing_to_location on thing_to_location.location_id = location.id where thing_to_location.thing_id = %v order by id desc limit 1", gdb.Schema, gdb.Schema, intID)
	countSQL := fmt.Sprintf("select COUNT(*) FROM %s.location inner join %s.thing_to_location on thing_to_location.location_id = location.id where thing_to_location.thing_id = %v", gdb.Schema, gdb.Schema, intID)
	return processLocations(gdb.Db, sql, qo, countSQL)
}

func processLocation(db *sql.DB, sql string, qo *odata.QueryOptions) (*entities.Location, error) {
	locations, _, err := processLocations(db, sql, qo, "")
	if err != nil {
		return nil, err
	}

	if len(locations) == 0 {
		return nil, gostErrors.NewRequestNotFound(errors.New("Location not found"))
	}

	return locations[0], nil
}

func processLocations(db *sql.DB, sql string, qo *odata.QueryOptions, countSQL string) ([]*entities.Location, int, error) {
	rows, err := db.Query(sql)
	defer rows.Close()
	if err != nil {
		return nil, 0, err
	}

	var locations = []*entities.Location{}
	for rows.Next() {
		var sensorID interface{}
		var encodingType int
		var name, description, location string

		var params []interface{}
		var qp []string
		if qo == nil || qo.QuerySelect == nil || len(qo.QuerySelect.Params) == 0 {
			s := &entities.Location{}
			qp = s.GetPropertyNames()
		} else {
			qp = qo.QuerySelect.Params
		}

		for _, p := range qp {
			if p == "id" {
				params = append(params, &sensorID)
			}
			if p == "encodingType" {
				params = append(params, &encodingType)
			}
			if p == "name" {
				params = append(params, &name)
			}
			if p == "description" {
				params = append(params, &description)
			}
			if p == "location" {
				params = append(params, &location)
			}
		}

		err = rows.Scan(params...)
		if err != nil {
			return nil, 0, err
		}

		locationMap, err := JSONToMap(&location)
		if err != nil {
			return nil, 0, err
		}

		l := entities.Location{}
		l.ID = sensorID
		l.Name = name
		l.Description = description
		l.Location = locationMap
		l.EncodingType = entities.EncodingValues[encodingType].Value
		locations = append(locations, &l)
	}

	var count int
	if len(countSQL) > 0 {
		db.QueryRow(countSQL).Scan(&count)
	}

	return locations, count, nil
}

// PostLocation receives a posted location entity and adds it to the database
// returns the created Location including the generated id
func (gdb *GostDatabase) PostLocation(location *entities.Location) (*entities.Location, error) {
	var locationID int
	locationBytes, _ := json.Marshal(location.Location)
	encoding, _ := entities.CreateEncodingType(location.EncodingType)

	sql := fmt.Sprintf("INSERT INTO %s.location (name, description, encodingtype, location) VALUES ($1, $2, $3, ST_SetSRID(ST_GeomFromGeoJSON('%s'),4326)) RETURNING id", gdb.Schema, string(locationBytes[:]))
	err := gdb.Db.QueryRow(sql, location.Name, location.Description, encoding.Code).Scan(&locationID)
	if err != nil {
		return nil, err
	}

	location.ID = locationID
	return location, nil
}

// LocationExists checks if a location is present in the database based on a given id
func (gdb *GostDatabase) LocationExists(id interface{}) bool {
	return EntityExists(gdb, id, "location")
}

// PatchLocation updates a Location in the database
func (gdb *GostDatabase) PatchLocation(id interface{}, l *entities.Location) (*entities.Location, error) {
	var err error
	var ok bool
	var intID int
	updates := make(map[string]interface{})

	if intID, ok = ToIntID(id); !ok || !gdb.LocationExists(intID) {
		return nil, gostErrors.NewRequestNotFound(errors.New("Location does not exist"))
	}

	if len(l.Name) > 0 {
		updates["name"] = l.Name
	}

	if len(l.Description) > 0 {
		updates["description"] = l.Description
	}

	if len(l.Location) > 0 {
		locationBytes, _ := json.Marshal(l.Location)
		updates["location"] = fmt.Sprintf("ST_SetSRID(ST_GeomFromGeoJSON('%s'),4326)", string(locationBytes[:]))
	}

	if len(l.EncodingType) > 0 {
		encoding, _ := entities.CreateEncodingType(l.EncodingType)
		updates["encodingtype"] = encoding.Code
	}

	if err = gdb.updateEntityColumns("location", updates, intID); err != nil {
		return nil, err
	}

	ns, _ := gdb.GetLocation(intID, nil)
	return ns, nil
}

// DeleteLocation removes a given location from the database
func (gdb *GostDatabase) DeleteLocation(id interface{}) error {
	return DeleteEntity(gdb, id, "location")
}

// PutLocation receives a Location entity and changes it in the database
// returns the adapted Location
func (gdb *GostDatabase) PutLocation(id interface{}, location *entities.Location) (*entities.Location, error) {
	var intID int
	var ok bool
	if intID, ok = ToIntID(id); !ok || !gdb.LocationExists(intID) {
		return nil, gostErrors.NewRequestNotFound(errors.New("Location does not exist"))
	}

	locationBytes, _ := json.Marshal(location.Location)
	encoding, _ := entities.CreateEncodingType(location.EncodingType)

	sql := fmt.Sprintf("update %s.location set name=$1, description=$2, encodingtype=$3, location=ST_SetSRID(ST_GeomFromGeoJSON('%s'),4326) where id = $4", gdb.Schema, string(locationBytes[:]))
	_, err := gdb.Db.Exec(sql, location.Name, location.Description, encoding.Code, intID)
	if err != nil {
		return nil, err
	}

	nt, _ := gdb.GetLocation(intID, nil)
	return nt, nil
}

// LinkLocation links a thing with a location
// fails when a thing or location cannot be found for the given id's
func (gdb *GostDatabase) LinkLocation(thingID interface{}, locationID interface{}) error {
	tid, ok := ToIntID(thingID)
	if !ok || !gdb.ThingExists(tid) {
		return gostErrors.NewRequestNotFound(errors.New("Thing does not exist"))
	}

	lid, ok := ToIntID(locationID)
	if !ok || !gdb.LocationExists(lid) {
		return gostErrors.NewRequestNotFound(errors.New("Location does not exist"))
	}

	sql := fmt.Sprintf("INSERT INTO %s.thing_to_location (thing_id, location_id) VALUES ($1, $2)", gdb.Schema)
	_, err3 := gdb.Db.Exec(sql, tid, lid)
	if err3 != nil {
		return err3
	}

	return nil
}
<|endoftext|>


<commit_before>// Package fluentd provides the log driver for forwarding server logs
// to fluentd endpoints.
package fluentd

import (
	"fmt"
	"math"
	"net"
	"strconv"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/docker/docker/daemon/logger"
	"github.com/docker/docker/daemon/logger/loggerutils"
	"github.com/docker/go-units"
	"github.com/fluent/fluent-logger-golang/fluent"
)

type fluentd struct {
	tag           string
	containerID   string
	containerName string
	writer        *fluent.Fluent
	extra         map[string]string
}

const (
	name = "fluentd"

	defaultHost        = "127.0.0.1"
	defaultPort        = 24224
	defaultBufferLimit = 1024 * 1024
	defaultTagPrefix   = "docker"

	// logger tries to reconnect 2**32 - 1 times
	// failed (and panic) after 204 years [ 1.5 ** (2**32 - 1) - 1 seconds]
	defaultRetryWait              = 1000
	defaultTimeout                = 3 * time.Second
	defaultMaxRetries             = math.MaxInt32
	defaultReconnectWaitIncreRate = 1.5

	addressKey      = "fluentd-address"
	bufferLimitKey  = "fluentd-buffer-limit"
	retryWaitKey    = "fluentd-retry-wait"
	maxRetriesKey   = "fluentd-max-retries"
	asyncConnectKey = "fluentd-async-connect"
)

func init() {
	if err := logger.RegisterLogDriver(name, New); err != nil {
		logrus.Fatal(err)
	}
	if err := logger.RegisterLogOptValidator(name, ValidateLogOpt); err != nil {
		logrus.Fatal(err)
	}
}

// New creates a fluentd logger using the configuration passed in on
// the context. The supported context configuration variable is
// fluentd-address.
func New(ctx logger.Context) (logger.Logger, error) {
	host, port, err := parseAddress(ctx.Config[addressKey])
	if err != nil {
		return nil, err
	}

	tag, err := loggerutils.ParseLogTag(ctx, loggerutils.DefaultTemplate)
	if err != nil {
		return nil, err
	}

	extra := ctx.ExtraAttributes(nil)

	bufferLimit := defaultBufferLimit
	if ctx.Config[bufferLimitKey] != "" {
		bl64, err := units.RAMInBytes(ctx.Config[bufferLimitKey])
		if err != nil {
			return nil, err
		}
		bufferLimit = int(bl64)
	}

	retryWait := defaultRetryWait
	if ctx.Config[retryWaitKey] != "" {
		rwd, err := time.ParseDuration(ctx.Config[retryWaitKey])
		if err != nil {
			return nil, err
		}
		retryWait = int(rwd.Seconds() * 1000)
	}

	maxRetries := defaultMaxRetries
	if ctx.Config[maxRetriesKey] != "" {
		mr64, err := strconv.ParseUint(ctx.Config[maxRetriesKey], 10, strconv.IntSize)
		if err != nil {
			return nil, err
		}
		maxRetries = int(mr64)
	}

	asyncConnect := false
	if ctx.Config[asyncConnectKey] != "" {
		if asyncConnect, err = strconv.ParseBool(ctx.Config[asyncConnectKey]); err != nil {
			return nil, err
		}
	}

	fluentConfig := fluent.Config{
		FluentPort:   port,
		FluentHost:   host,
		BufferLimit:  bufferLimit,
		RetryWait:    retryWait,
		MaxRetry:     maxRetries,
		AsyncConnect: asyncConnect,
	}

	logrus.WithField("container", ctx.ContainerID).WithField("config", fluentConfig).
		Debug("logging driver fluentd configured")

	log, err := fluent.New(fluentConfig)
	if err != nil {
		return nil, err
	}
	return &fluentd{
		tag:           tag,
		containerID:   ctx.ContainerID,
		containerName: ctx.ContainerName,
		writer:        log,
		extra:         extra,
	}, nil
}

func (f *fluentd) Log(msg *logger.Message) error {
	data := map[string]string{
		"container_id":   f.containerID,
		"container_name": f.containerName,
		"source":         msg.Source,
		"log":            string(msg.Line),
	}
	for k, v := range f.extra {
		data[k] = v
	}
	// fluent-logger-golang buffers logs from failures and disconnections,
	// and these are transferred again automatically.
	return f.writer.PostWithTime(f.tag, msg.Timestamp, data)
}

func (f *fluentd) Close() error {
	return f.writer.Close()
}

func (f *fluentd) Name() string {
	return name
}

// ValidateLogOpt looks for fluentd specific log option fluentd-address.
func ValidateLogOpt(cfg map[string]string) error {
	for key := range cfg {
		switch key {
		case "env":
		case "labels":
		case "tag":
		case addressKey:
		case bufferLimitKey:
		case retryWaitKey:
		case maxRetriesKey:
		case asyncConnectKey:
			// Accepted
		default:
			return fmt.Errorf("unknown log opt '%s' for fluentd log driver", key)
		}
	}

	if _, _, err := parseAddress(cfg["fluentd-address"]); err != nil {
		return err
	}

	return nil
}

func parseAddress(address string) (string, int, error) {
	if address == "" {
		return defaultHost, defaultPort, nil
	}

	host, port, err := net.SplitHostPort(address)
	if err != nil {
		if !strings.Contains(err.Error(), "missing port in address") {
			return "", 0, fmt.Errorf("invalid fluentd-address %s: %s", address, err)
		}
		return host, defaultPort, nil
	}

	portnum, err := strconv.Atoi(port)
	if err != nil {
		return "", 0, fmt.Errorf("invalid fluentd-address %s: %s", address, err)
	}
	return host, portnum, nil
}
<commit_msg>Fixes #21803 : Removing unused configuration in daemon/logger/fluentd<commit_after>// Package fluentd provides the log driver for forwarding server logs
// to fluentd endpoints.
package fluentd

import (
	"fmt"
	"math"
	"net"
	"strconv"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/docker/docker/daemon/logger"
	"github.com/docker/docker/daemon/logger/loggerutils"
	"github.com/docker/go-units"
	"github.com/fluent/fluent-logger-golang/fluent"
)

type fluentd struct {
	tag           string
	containerID   string
	containerName string
	writer        *fluent.Fluent
	extra         map[string]string
}

const (
	name = "fluentd"

	defaultHost        = "127.0.0.1"
	defaultPort        = 24224
	defaultBufferLimit = 1024 * 1024

	// logger tries to reconnect 2**32 - 1 times
	// failed (and panic) after 204 years [ 1.5 ** (2**32 - 1) - 1 seconds]
	defaultRetryWait  = 1000
	defaultMaxRetries = math.MaxInt32

	addressKey      = "fluentd-address"
	bufferLimitKey  = "fluentd-buffer-limit"
	retryWaitKey    = "fluentd-retry-wait"
	maxRetriesKey   = "fluentd-max-retries"
	asyncConnectKey = "fluentd-async-connect"
)

func init() {
	if err := logger.RegisterLogDriver(name, New); err != nil {
		logrus.Fatal(err)
	}
	if err := logger.RegisterLogOptValidator(name, ValidateLogOpt); err != nil {
		logrus.Fatal(err)
	}
}

// New creates a fluentd logger using the configuration passed in on
// the context. The supported context configuration variable is
// fluentd-address.
func New(ctx logger.Context) (logger.Logger, error) {
	host, port, err := parseAddress(ctx.Config[addressKey])
	if err != nil {
		return nil, err
	}

	tag, err := loggerutils.ParseLogTag(ctx, loggerutils.DefaultTemplate)
	if err != nil {
		return nil, err
	}

	extra := ctx.ExtraAttributes(nil)

	bufferLimit := defaultBufferLimit
	if ctx.Config[bufferLimitKey] != "" {
		bl64, err := units.RAMInBytes(ctx.Config[bufferLimitKey])
		if err != nil {
			return nil, err
		}
		bufferLimit = int(bl64)
	}

	retryWait := defaultRetryWait
	if ctx.Config[retryWaitKey] != "" {
		rwd, err := time.ParseDuration(ctx.Config[retryWaitKey])
		if err != nil {
			return nil, err
		}
		retryWait = int(rwd.Seconds() * 1000)
	}

	maxRetries := defaultMaxRetries
	if ctx.Config[maxRetriesKey] != "" {
		mr64, err := strconv.ParseUint(ctx.Config[maxRetriesKey], 10, strconv.IntSize)
		if err != nil {
			return nil, err
		}
		maxRetries = int(mr64)
	}

	asyncConnect := false
	if ctx.Config[asyncConnectKey] != "" {
		if asyncConnect, err = strconv.ParseBool(ctx.Config[asyncConnectKey]); err != nil {
			return nil, err
		}
	}

	fluentConfig := fluent.Config{
		FluentPort:   port,
		FluentHost:   host,
		BufferLimit:  bufferLimit,
		RetryWait:    retryWait,
		MaxRetry:     maxRetries,
		AsyncConnect: asyncConnect,
	}

	logrus.WithField("container", ctx.ContainerID).WithField("config", fluentConfig).
		Debug("logging driver fluentd configured")

	log, err := fluent.New(fluentConfig)
	if err != nil {
		return nil, err
	}
	return &fluentd{
		tag:           tag,
		containerID:   ctx.ContainerID,
		containerName: ctx.ContainerName,
		writer:        log,
		extra:         extra,
	}, nil
}

func (f *fluentd) Log(msg *logger.Message) error {
	data := map[string]string{
		"container_id":   f.containerID,
		"container_name": f.containerName,
		"source":         msg.Source,
		"log":            string(msg.Line),
	}
	for k, v := range f.extra {
		data[k] = v
	}
	// fluent-logger-golang buffers logs from failures and disconnections,
	// and these are transferred again automatically.
	return f.writer.PostWithTime(f.tag, msg.Timestamp, data)
}

func (f *fluentd) Close() error {
	return f.writer.Close()
}

func (f *fluentd) Name() string {
	return name
}

// ValidateLogOpt looks for fluentd specific log option fluentd-address.
func ValidateLogOpt(cfg map[string]string) error {
	for key := range cfg {
		switch key {
		case "env":
		case "labels":
		case "tag":
		case addressKey:
		case bufferLimitKey:
		case retryWaitKey:
		case maxRetriesKey:
		case asyncConnectKey:
			// Accepted
		default:
			return fmt.Errorf("unknown log opt '%s' for fluentd log driver", key)
		}
	}

	if _, _, err := parseAddress(cfg["fluentd-address"]); err != nil {
		return err
	}

	return nil
}

func parseAddress(address string) (string, int, error) {
	if address == "" {
		return defaultHost, defaultPort, nil
	}

	host, port, err := net.SplitHostPort(address)
	if err != nil {
		if !strings.Contains(err.Error(), "missing port in address") {
			return "", 0, fmt.Errorf("invalid fluentd-address %s: %s", address, err)
		}
		return host, defaultPort, nil
	}

	portnum, err := strconv.Atoi(port)
	if err != nil {
		return "", 0, fmt.Errorf("invalid fluentd-address %s: %s", address, err)
	}
	return host, portnum, nil
}
<|endoftext|>