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

// Package server contains the `pilosa server` subcommand which runs Pilosa
// itself. The purpose of this package is to define an easily tested Command
// object which handles interpreting configuration and setting up all the
// objects that Pilosa needs.

package server

import (
	"fmt"
	"io"
	"log"
	"math/rand"
	"net"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"
	"time"

	"crypto/tls"

	"github.com/pilosa/pilosa"
	"github.com/pilosa/pilosa/boltdb"
	"github.com/pilosa/pilosa/gcnotify"
	"github.com/pilosa/pilosa/gopsutil"
	"github.com/pilosa/pilosa/gossip"
	"github.com/pilosa/pilosa/statik"
	"github.com/pilosa/pilosa/statsd"
	"github.com/pkg/errors"
)

func init() {
	rand.Seed(time.Now().UTC().UnixNano())
}

type loggerLogger interface {
	pilosa.Logger
	Logger() *log.Logger
}

// Command represents the state of the pilosa server command.
type Command struct {
	Server *pilosa.Server

	// Configuration.
	Config *Config

	// Gossip transport
	GossipTransport *gossip.Transport

	// Standard input/output
	*pilosa.CmdIO

	// Started will be closed once Command.Run is finished.
	Started chan struct{}
	// Done will be closed when Command.Close() is called
	Done chan struct{}

	// Passed to the Gossip implementation.
	logOutput io.Writer
	logger    loggerLogger
}

// NewCommand returns a new instance of Main.
func NewCommand(stdin io.Reader, stdout, stderr io.Writer) *Command {
	return &Command{
		Config: NewConfig(),

		CmdIO: pilosa.NewCmdIO(stdin, stdout, stderr),

		Started: make(chan struct{}),
		Done:    make(chan struct{}),
	}
}

// Start starts the pilosa server - it returns once the server is running.
func (m *Command) Start() (err error) {
	defer close(m.Started)
	prefix := "~" + string(filepath.Separator)
	if strings.HasPrefix(m.Config.DataDir, prefix) {
		HomeDir := os.Getenv("HOME")
		if HomeDir == "" {
			return errors.New("data directory not specified and no home dir available")
		}
		m.Config.DataDir = filepath.Join(HomeDir, strings.TrimPrefix(m.Config.DataDir, prefix))
	}

	// SetupServer
	err = m.SetupServer()
	if err != nil {
		return err
	}

	// SetupNetworking
	err = m.SetupNetworking()
	if err != nil {
		return err
	}

	// Initialize server.
	if err = m.Server.Open(); err != nil {
		return fmt.Errorf("server.Open: %v", err)
	}

	m.logger.Printf("Listening as %s\n", m.Server.URI)

	return nil
}

// Wait waits for the server to be closed or interrupted.
func (m *Command) Wait() error {
	// First SIGKILL causes server to shut down gracefully.
	c := make(chan os.Signal, 2)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
	select {
	case sig := <-c:
		m.logger.Printf("Received %s; gracefully shutting down...\n", sig.String())

		// Second signal causes a hard shutdown.
		go func() { <-c; os.Exit(1) }()
		return errors.Wrap(m.Close(), "closing command")
	case <-m.Done:
		m.logger.Printf("Server closed externally")
		return nil
	}
}

// setupLogger sets up the logger based on the configuration.
func (m *Command) setupLogger() error {
	var err error
	if m.Config.LogPath == "" {
		m.logOutput = m.Stderr
	} else {
		m.logOutput, err = os.OpenFile(m.Config.LogPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
		if err != nil {
			return errors.Wrap(err, "opening file")
		}
	}

	if m.Config.Verbose {
		m.logger = pilosa.NewVerboseLogger(m.logOutput)
	} else {
		m.logger = pilosa.NewStandardLogger(m.logOutput)
	}
	return nil
}

// SetupServer uses the cluster configuration to set up this server.
func (m *Command) SetupServer() error {
	err := m.setupLogger()
	if err != nil {
		return errors.Wrap(err, "setting up logger")
	}

	handler := pilosa.NewHandler()
	handler.Logger = m.logger
	handler.FileSystem = &statik.FileSystem{}
	handler.API = pilosa.NewAPI()
	handler.API.Logger = m.logger

	uri, err := pilosa.AddressWithDefaults(m.Config.Bind)
	if err != nil {
		return errors.Wrap(err, "processing bind address")
	}

	// Setup TLS
	var TLSConfig *tls.Config
	if uri.Scheme() == "https" {
		if m.Config.TLS.CertificatePath == "" {
			return errors.New("certificate path is required for TLS sockets")
		}
		if m.Config.TLS.CertificateKeyPath == "" {
			return errors.New("certificate key path is required for TLS sockets")
		}
		cert, err := tls.LoadX509KeyPair(m.Config.TLS.CertificatePath, m.Config.TLS.CertificateKeyPath)
		if err != nil {
			return errors.Wrap(err, "load x509 key pair")
		}
		TLSConfig = &tls.Config{
			Certificates:       []tls.Certificate{cert},
			InsecureSkipVerify: m.Config.TLS.SkipVerify,
		}
	}

	diagnosticsInterval := time.Duration(0)
	if m.Config.Metric.Diagnostics {
		diagnosticsInterval = time.Duration(DefaultDiagnosticsInterval)
	}

	statsClient, err := NewStatsClient(m.Config.Metric.Service, m.Config.Metric.Host)
	if err != nil {
		return errors.Wrap(err, "new stats client")
	}

	ln, err := getListener(*uri, TLSConfig)
	if err != nil {
		return errors.Wrap(err, "getting listener")
	}

	c := GetHTTPClient(TLSConfig)
	handler.API.RemoteClient = c

	m.Server, err = pilosa.NewServer(
		pilosa.OptServerAntiEntropyInterval(time.Duration(m.Config.AntiEntropy.Interval)),
		pilosa.OptServerLongQueryTime(time.Duration(m.Config.Cluster.LongQueryTime)),
		pilosa.OptServerDataDir(m.Config.DataDir),
		pilosa.OptServerReplicaN(m.Config.Cluster.ReplicaN),
		pilosa.OptServerMaxWritesPerRequest(m.Config.MaxWritesPerRequest),
		pilosa.OptServerMetricInterval(time.Duration(m.Config.Metric.PollInterval)),
		pilosa.OptServerDiagnosticsInterval(diagnosticsInterval),

		pilosa.OptServerLogger(m.logger),
		pilosa.OptServerAttrStoreFunc(boltdb.NewAttrStore),
		pilosa.OptServerHandler(handler),
		pilosa.OptServerSystemInfo(gopsutil.NewSystemInfo()),
		pilosa.OptServerGCNotifier(gcnotify.NewActiveGCNotifier()),
		pilosa.OptServerStatsClient(statsClient),
		pilosa.OptServerListener(ln),
		pilosa.OptServerURI(uri),
		pilosa.OptServerRemoteClient(c),
	)

	return errors.Wrap(err, "new server")
}

func GetHTTPClient(t *tls.Config) *http.Client {
	transport := &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		DialContext: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
			DualStack: true,
		}).DialContext,
		MaxIdleConns:          1000,
		MaxIdleConnsPerHost:   200,
		IdleConnTimeout:       90 * time.Second,
		TLSHandshakeTimeout:   10 * time.Second,
		ExpectContinueTimeout: 1 * time.Second,
	}
	if t != nil {
		transport.TLSClientConfig = t
	}
	return &http.Client{Transport: transport}
}

// SetupNetworking sets up internode communication based on the configuration.
func (m *Command) SetupNetworking() error {

	m.Server.NodeID = m.Server.LoadNodeID()

	if m.Config.Cluster.Disabled {
		m.Server.Cluster.Static = true
		m.Server.Cluster.Coordinator = m.Server.NodeID
		for _, address := range m.Config.Cluster.Hosts {
			uri, err := pilosa.NewURIFromAddress(address)
			if err != nil {
				return err
			}
			m.Server.Cluster.Nodes = append(m.Server.Cluster.Nodes, &pilosa.Node{
				URI: *uri,
			})
		}

		m.Server.Broadcaster = pilosa.NopBroadcaster
		m.Server.Cluster.MemberSet = pilosa.NewStaticMemberSet(m.Server.Cluster.Nodes)
		m.Server.BroadcastReceiver = pilosa.NopBroadcastReceiver
		m.Server.Gossiper = pilosa.NopGossiper
		return nil
	}

	gossipPort, err := strconv.Atoi(m.Config.Gossip.Port)
	if err != nil {
		return err
	}

	// get the host portion of addr to use for binding
	gossipHost := m.Server.URI.Host()
	var transport *gossip.Transport
	if m.GossipTransport != nil {
		transport = m.GossipTransport
	} else {
		transport, err = gossip.NewTransport(gossipHost, gossipPort, m.logger.Logger())
		if err != nil {
			return err
		}
	}

	// Set Coordinator.
	if m.Config.Cluster.Coordinator || len(m.Config.Gossip.Seeds) == 0 {
		m.Server.Cluster.Coordinator = m.Server.NodeID
		m.Server.Cluster.Node.IsCoordinator = true
	}

	gossipEventReceiver := gossip.NewGossipEventReceiver(m.logger)
	m.Server.Cluster.EventReceiver = gossipEventReceiver
	gossipMemberSet, err := gossip.NewGossipMemberSet(m.Server.NodeID, m.Server.URI.Host(), m.Config.Gossip, gossipEventReceiver, m.Server, gossip.WithLogger(m.logger.Logger()), gossip.WithTransport(transport))
	if err != nil {
		return err
	}
	m.Server.Cluster.MemberSet = gossipMemberSet
	m.Server.Broadcaster = m.Server
	m.Server.BroadcastReceiver = gossipMemberSet
	m.Server.Gossiper = gossipMemberSet
	return nil
}

// Close shuts down the server.
func (m *Command) Close() error {
	var logErr error
	serveErr := m.Server.Close()
	if closer, ok := m.logOutput.(io.Closer); ok {
		logErr = closer.Close()
	}
	close(m.Done)
	if serveErr != nil && logErr != nil {
		return fmt.Errorf("closing server: '%v', closing logs: '%v'", serveErr, logErr)
	} else if logErr != nil {
		return logErr
	}
	return serveErr
}

// NewStatsClient creates a stats client from the config
func NewStatsClient(name string, host string) (pilosa.StatsClient, error) {
	switch name {
	case "expvar":
		return pilosa.NewExpvarStatsClient(), nil
	case "statsd":
		return statsd.NewStatsClient(host)
	case "nop", "none":
		return pilosa.NopStatsClient, nil
	default:
		return nil, errors.Errorf("'%v' not a valid stats client, choose from [expvar, statsd, none].")
	}
}

// getListener gets a net.Listener based on the config.
func getListener(uri pilosa.URI, tlsconf *tls.Config) (ln net.Listener, err error) {
	// If bind URI has the https scheme, enable TLS
	if uri.Scheme() == "https" && tlsconf != nil {
		ln, err = tls.Listen("tcp", uri.HostPort(), tlsconf)
		if err != nil {
			return nil, errors.Wrap(err, "tls.Listener")
		}
	} else if uri.Scheme() == "http" {
		// Open HTTP listener to determine port (if specified as :0).
		ln, err = net.Listen("tcp", uri.HostPort())
		if err != nil {
			return nil, errors.Wrap(err, "net.Listen")
		}
	} else {
		return nil, errors.Errorf("unsupported scheme: %s", uri.Scheme())
	}

	return ln, nil
}
<commit_msg>Remove ~ expanding code from Command.Start<commit_after>// Copyright 2017 Pilosa Corp.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package server contains the `pilosa server` subcommand which runs Pilosa
// itself. The purpose of this package is to define an easily tested Command
// object which handles interpreting configuration and setting up all the
// objects that Pilosa needs.

package server

import (
	"fmt"
	"io"
	"log"
	"math/rand"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strconv"
	"syscall"
	"time"

	"crypto/tls"

	"github.com/pilosa/pilosa"
	"github.com/pilosa/pilosa/boltdb"
	"github.com/pilosa/pilosa/gcnotify"
	"github.com/pilosa/pilosa/gopsutil"
	"github.com/pilosa/pilosa/gossip"
	"github.com/pilosa/pilosa/statik"
	"github.com/pilosa/pilosa/statsd"
	"github.com/pkg/errors"
)

func init() {
	rand.Seed(time.Now().UTC().UnixNano())
}

type loggerLogger interface {
	pilosa.Logger
	Logger() *log.Logger
}

// Command represents the state of the pilosa server command.
type Command struct {
	Server *pilosa.Server

	// Configuration.
	Config *Config

	// Gossip transport
	GossipTransport *gossip.Transport

	// Standard input/output
	*pilosa.CmdIO

	// Started will be closed once Command.Run is finished.
	Started chan struct{}
	// Done will be closed when Command.Close() is called
	Done chan struct{}

	// Passed to the Gossip implementation.
	logOutput io.Writer
	logger    loggerLogger
}

// NewCommand returns a new instance of Main.
func NewCommand(stdin io.Reader, stdout, stderr io.Writer) *Command {
	return &Command{
		Config: NewConfig(),

		CmdIO: pilosa.NewCmdIO(stdin, stdout, stderr),

		Started: make(chan struct{}),
		Done:    make(chan struct{}),
	}
}

// Start starts the pilosa server - it returns once the server is running.
func (m *Command) Start() (err error) {
	defer close(m.Started)

	// SetupServer
	err = m.SetupServer()
	if err != nil {
		return err
	}

	// SetupNetworking
	err = m.SetupNetworking()
	if err != nil {
		return err
	}

	// Initialize server.
	if err = m.Server.Open(); err != nil {
		return fmt.Errorf("server.Open: %v", err)
	}

	m.logger.Printf("Listening as %s\n", m.Server.URI)

	return nil
}

// Wait waits for the server to be closed or interrupted.
func (m *Command) Wait() error {
	// First SIGKILL causes server to shut down gracefully.
	c := make(chan os.Signal, 2)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
	select {
	case sig := <-c:
		m.logger.Printf("Received %s; gracefully shutting down...\n", sig.String())

		// Second signal causes a hard shutdown.
		go func() { <-c; os.Exit(1) }()
		return errors.Wrap(m.Close(), "closing command")
	case <-m.Done:
		m.logger.Printf("Server closed externally")
		return nil
	}
}

// setupLogger sets up the logger based on the configuration.
func (m *Command) setupLogger() error {
	var err error
	if m.Config.LogPath == "" {
		m.logOutput = m.Stderr
	} else {
		m.logOutput, err = os.OpenFile(m.Config.LogPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
		if err != nil {
			return errors.Wrap(err, "opening file")
		}
	}

	if m.Config.Verbose {
		m.logger = pilosa.NewVerboseLogger(m.logOutput)
	} else {
		m.logger = pilosa.NewStandardLogger(m.logOutput)
	}
	return nil
}

// SetupServer uses the cluster configuration to set up this server.
func (m *Command) SetupServer() error {
	err := m.setupLogger()
	if err != nil {
		return errors.Wrap(err, "setting up logger")
	}

	handler := pilosa.NewHandler()
	handler.Logger = m.logger
	handler.FileSystem = &statik.FileSystem{}
	handler.API = pilosa.NewAPI()
	handler.API.Logger = m.logger

	uri, err := pilosa.AddressWithDefaults(m.Config.Bind)
	if err != nil {
		return errors.Wrap(err, "processing bind address")
	}

	// Setup TLS
	var TLSConfig *tls.Config
	if uri.Scheme() == "https" {
		if m.Config.TLS.CertificatePath == "" {
			return errors.New("certificate path is required for TLS sockets")
		}
		if m.Config.TLS.CertificateKeyPath == "" {
			return errors.New("certificate key path is required for TLS sockets")
		}
		cert, err := tls.LoadX509KeyPair(m.Config.TLS.CertificatePath, m.Config.TLS.CertificateKeyPath)
		if err != nil {
			return errors.Wrap(err, "load x509 key pair")
		}
		TLSConfig = &tls.Config{
			Certificates:       []tls.Certificate{cert},
			InsecureSkipVerify: m.Config.TLS.SkipVerify,
		}
	}

	diagnosticsInterval := time.Duration(0)
	if m.Config.Metric.Diagnostics {
		diagnosticsInterval = time.Duration(DefaultDiagnosticsInterval)
	}

	statsClient, err := NewStatsClient(m.Config.Metric.Service, m.Config.Metric.Host)
	if err != nil {
		return errors.Wrap(err, "new stats client")
	}

	ln, err := getListener(*uri, TLSConfig)
	if err != nil {
		return errors.Wrap(err, "getting listener")
	}

	c := GetHTTPClient(TLSConfig)
	handler.API.RemoteClient = c

	m.Server, err = pilosa.NewServer(
		pilosa.OptServerAntiEntropyInterval(time.Duration(m.Config.AntiEntropy.Interval)),
		pilosa.OptServerLongQueryTime(time.Duration(m.Config.Cluster.LongQueryTime)),
		pilosa.OptServerDataDir(m.Config.DataDir),
		pilosa.OptServerReplicaN(m.Config.Cluster.ReplicaN),
		pilosa.OptServerMaxWritesPerRequest(m.Config.MaxWritesPerRequest),
		pilosa.OptServerMetricInterval(time.Duration(m.Config.Metric.PollInterval)),
		pilosa.OptServerDiagnosticsInterval(diagnosticsInterval),

		pilosa.OptServerLogger(m.logger),
		pilosa.OptServerAttrStoreFunc(boltdb.NewAttrStore),
		pilosa.OptServerHandler(handler),
		pilosa.OptServerSystemInfo(gopsutil.NewSystemInfo()),
		pilosa.OptServerGCNotifier(gcnotify.NewActiveGCNotifier()),
		pilosa.OptServerStatsClient(statsClient),
		pilosa.OptServerListener(ln),
		pilosa.OptServerURI(uri),
		pilosa.OptServerRemoteClient(c),
	)

	return errors.Wrap(err, "new server")
}

func GetHTTPClient(t *tls.Config) *http.Client {
	transport := &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		DialContext: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
			DualStack: true,
		}).DialContext,
		MaxIdleConns:          1000,
		MaxIdleConnsPerHost:   200,
		IdleConnTimeout:       90 * time.Second,
		TLSHandshakeTimeout:   10 * time.Second,
		ExpectContinueTimeout: 1 * time.Second,
	}
	if t != nil {
		transport.TLSClientConfig = t
	}
	return &http.Client{Transport: transport}
}

// SetupNetworking sets up internode communication based on the configuration.
func (m *Command) SetupNetworking() error {

	m.Server.NodeID = m.Server.LoadNodeID()

	if m.Config.Cluster.Disabled {
		m.Server.Cluster.Static = true
		m.Server.Cluster.Coordinator = m.Server.NodeID
		for _, address := range m.Config.Cluster.Hosts {
			uri, err := pilosa.NewURIFromAddress(address)
			if err != nil {
				return err
			}
			m.Server.Cluster.Nodes = append(m.Server.Cluster.Nodes, &pilosa.Node{
				URI: *uri,
			})
		}

		m.Server.Broadcaster = pilosa.NopBroadcaster
		m.Server.Cluster.MemberSet = pilosa.NewStaticMemberSet(m.Server.Cluster.Nodes)
		m.Server.BroadcastReceiver = pilosa.NopBroadcastReceiver
		m.Server.Gossiper = pilosa.NopGossiper
		return nil
	}

	gossipPort, err := strconv.Atoi(m.Config.Gossip.Port)
	if err != nil {
		return err
	}

	// get the host portion of addr to use for binding
	gossipHost := m.Server.URI.Host()
	var transport *gossip.Transport
	if m.GossipTransport != nil {
		transport = m.GossipTransport
	} else {
		transport, err = gossip.NewTransport(gossipHost, gossipPort, m.logger.Logger())
		if err != nil {
			return err
		}
	}

	// Set Coordinator.
	if m.Config.Cluster.Coordinator || len(m.Config.Gossip.Seeds) == 0 {
		m.Server.Cluster.Coordinator = m.Server.NodeID
		m.Server.Cluster.Node.IsCoordinator = true
	}

	gossipEventReceiver := gossip.NewGossipEventReceiver(m.logger)
	m.Server.Cluster.EventReceiver = gossipEventReceiver
	gossipMemberSet, err := gossip.NewGossipMemberSet(m.Server.NodeID, m.Server.URI.Host(), m.Config.Gossip, gossipEventReceiver, m.Server, gossip.WithLogger(m.logger.Logger()), gossip.WithTransport(transport))
	if err != nil {
		return err
	}
	m.Server.Cluster.MemberSet = gossipMemberSet
	m.Server.Broadcaster = m.Server
	m.Server.BroadcastReceiver = gossipMemberSet
	m.Server.Gossiper = gossipMemberSet
	return nil
}

// Close shuts down the server.
func (m *Command) Close() error {
	var logErr error
	serveErr := m.Server.Close()
	if closer, ok := m.logOutput.(io.Closer); ok {
		logErr = closer.Close()
	}
	close(m.Done)
	if serveErr != nil && logErr != nil {
		return fmt.Errorf("closing server: '%v', closing logs: '%v'", serveErr, logErr)
	} else if logErr != nil {
		return logErr
	}
	return serveErr
}

// NewStatsClient creates a stats client from the config
func NewStatsClient(name string, host string) (pilosa.StatsClient, error) {
	switch name {
	case "expvar":
		return pilosa.NewExpvarStatsClient(), nil
	case "statsd":
		return statsd.NewStatsClient(host)
	case "nop", "none":
		return pilosa.NopStatsClient, nil
	default:
		return nil, errors.Errorf("'%v' not a valid stats client, choose from [expvar, statsd, none].")
	}
}

// getListener gets a net.Listener based on the config.
func getListener(uri pilosa.URI, tlsconf *tls.Config) (ln net.Listener, err error) {
	// If bind URI has the https scheme, enable TLS
	if uri.Scheme() == "https" && tlsconf != nil {
		ln, err = tls.Listen("tcp", uri.HostPort(), tlsconf)
		if err != nil {
			return nil, errors.Wrap(err, "tls.Listener")
		}
	} else if uri.Scheme() == "http" {
		// Open HTTP listener to determine port (if specified as :0).
		ln, err = net.Listen("tcp", uri.HostPort())
		if err != nil {
			return nil, errors.Wrap(err, "net.Listen")
		}
	} else {
		return nil, errors.Errorf("unsupported scheme: %s", uri.Scheme())
	}

	return ln, nil
}
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"os"
	"sync"
	"time"

	"github.com/micro/cli"
	"github.com/micro/go-micro"
	"github.com/micro/go-micro/network/router"
	"github.com/micro/go-micro/registry/gossip"
	"github.com/micro/go-micro/server"
	"github.com/micro/go-micro/transport"
	"github.com/micro/go-micro/transport/grpc"
	"github.com/micro/go-micro/util/log"
)

var (
	// Name of the server microservice
	Name = "go.micro.server"
	// Address is the router microservice bind address
	Address = ":8083"
	// Router is the router address a.k.a. gossip address
	Router = ":9093"
	// Network is the router network address
	Network = ":9094"
)

// srv is micro server
type srv struct {
	// router is micro router
	router router.Router
	// network is micro network server
	network server.Server
}

// newServer creates new micro server and returns it
func newServer(s micro.Service, r router.Router) *srv {
	// NOTE: this will end up being QUIC transport once it gets stable
	t := grpc.NewTransport(transport.Addrs(Network))
	n := server.NewServer(server.Transport(t))

	return &srv{
		router:  r,
		network: n,
	}
}

// start starts the micro server.
func (s *srv) start() error {
	log.Log("[server] starting micro server")

	return s.router.Advertise()
}

// stop stops the micro server.
func (s *srv) stop() error {
	log.Log("[server] attempting to stop server")

	// stop the router
	if err := s.router.Stop(); err != nil {
		return fmt.Errorf("failed to stop router: %v", err)
	}

	log.Log("[server] router successfully stopped")

	return nil
}

// run runs the micro server
func run(ctx *cli.Context, srvOpts ...micro.Option) {
	// Init plugins
	for _, p := range Plugins() {
		p.Init(ctx)
	}

	if len(ctx.GlobalString("server_name")) > 0 {
		Name = ctx.GlobalString("server_name")
	}
	if len(ctx.String("address")) > 0 {
		Address = ctx.String("address")
	}
	if len(ctx.String("router")) > 0 {
		Router = ctx.String("router")
	}
	if len(ctx.String("network")) > 0 {
		Network = ctx.String("network")
	}

	// Initialise service
	service := micro.NewService(
		micro.Name(Name),
		micro.Address(Address),
		micro.RegisterTTL(time.Duration(ctx.GlobalInt("register_ttl"))*time.Second),
		micro.RegisterInterval(time.Duration(ctx.GlobalInt("register_interval"))*time.Second),
	)

	// create new router
	r := router.NewRouter(
		router.ID(service.Server().Options().Id),
		router.Address(Router),
		router.Advertise(Network),
		router.Registry(service.Client().Options().Registry),
		router.Network(gossip.NewRegistry(gossip.Address(Router), gossip.Advertise(Router))),
	)

	// create new server and start it
	s := newServer(service, r)

	// channel to collect errors
	errChan := make(chan error, 2)

	// WaitGroup to track goroutines
	var wg sync.WaitGroup

	// Start the micro server
	wg.Add(1)
	go func() {
		defer wg.Done()
		errChan <- s.start()
	}()

	// Start the micro server service
	wg.Add(1)
	go func() {
		defer wg.Done()
		errChan <- service.Run()
	}()

	// we block here until either service or server fails
	if err := <-errChan; err != nil {
		log.Logf("[server] error running the server: %v", err)
	}

	// stop the server
	if err := s.stop(); err != nil {
		log.Logf("[server] error stopping server: %v", err)
		os.Exit(1)
	}

	// wait for all the goroutines to stop
	wg.Wait()

	log.Logf("[server] successfully stopped")
}

func Commands(options ...micro.Option) []cli.Command {
	command := cli.Command{
		Name:  "server",
		Usage: "Run the micro network server",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:   "address",
				Usage:  "Set the micro server address :8083",
				EnvVar: "MICRO_SERVER_ADDRESS",
			},
			cli.StringFlag{
				Name:   "router",
				Usage:  "Set the micro router address :9093",
				EnvVar: "MICRO_ROUTER_ADDRESS",
			},
			cli.StringFlag{
				Name:   "network",
				Usage:  "Set the micro network address :9094",
				EnvVar: "MICRO_NETWORK_ADDRESS",
			},
		},
		Action: func(ctx *cli.Context) {
			run(ctx, options...)
		},
	}

	for _, p := range Plugins() {
		if cmds := p.Commands(); len(cmds) > 0 {
			command.Subcommands = append(command.Subcommands, cmds...)
		}

		if flags := p.Flags(); len(flags) > 0 {
			command.Flags = append(command.Flags, flags...)
		}
	}

	return []cli.Command{command}
}
<commit_msg>Removed Network registry as its no longer used by router for now.<commit_after>package server

import (
	"fmt"
	"os"
	"sync"
	"time"

	"github.com/micro/cli"
	"github.com/micro/go-micro"
	"github.com/micro/go-micro/network/router"
	"github.com/micro/go-micro/server"
	"github.com/micro/go-micro/transport"
	"github.com/micro/go-micro/transport/grpc"
	"github.com/micro/go-micro/util/log"
)

var (
	// Name of the server microservice
	Name = "go.micro.server"
	// Address is the router microservice bind address
	Address = ":8083"
	// Router is the router address a.k.a. gossip address
	Router = ":9093"
	// Network is the router network id
	Network = "local"
)

// srv is micro server
type srv struct {
	// router is micro router
	router router.Router
	// network is micro network server
	network server.Server
}

// newServer creates new micro server and returns it
func newServer(s micro.Service, r router.Router) *srv {
	// NOTE: this will end up being QUIC transport once it gets stable
	t := grpc.NewTransport(transport.Addrs(Network))
	n := server.NewServer(server.Transport(t))

	return &srv{
		router:  r,
		network: n,
	}
}

// start starts the micro server.
func (s *srv) start() error {
	log.Log("[server] starting micro server")

	return s.router.Advertise()
}

// stop stops the micro server.
func (s *srv) stop() error {
	log.Log("[server] attempting to stop server")

	// stop the router
	if err := s.router.Stop(); err != nil {
		return fmt.Errorf("failed to stop router: %v", err)
	}

	log.Log("[server] router successfully stopped")

	return nil
}

// run runs the micro server
func run(ctx *cli.Context, srvOpts ...micro.Option) {
	// Init plugins
	for _, p := range Plugins() {
		p.Init(ctx)
	}

	if len(ctx.GlobalString("server_name")) > 0 {
		Name = ctx.GlobalString("server_name")
	}
	if len(ctx.String("address")) > 0 {
		Address = ctx.String("address")
	}
	if len(ctx.String("router")) > 0 {
		Router = ctx.String("router")
	}
	if len(ctx.String("network")) > 0 {
		Network = ctx.String("network")
	}

	// Initialise service
	service := micro.NewService(
		micro.Name(Name),
		micro.Address(Address),
		micro.RegisterTTL(time.Duration(ctx.GlobalInt("register_ttl"))*time.Second),
		micro.RegisterInterval(time.Duration(ctx.GlobalInt("register_interval"))*time.Second),
	)

	// create new router
	r := router.NewRouter(
		router.ID(service.Server().Options().Id),
		router.Address(Router),
		router.Network(Network),
		router.Registry(service.Client().Options().Registry),
	)

	// create new server and start it
	s := newServer(service, r)

	// channel to collect errors
	errChan := make(chan error, 2)

	// WaitGroup to track goroutines
	var wg sync.WaitGroup

	// Start the micro server
	wg.Add(1)
	go func() {
		defer wg.Done()
		errChan <- s.start()
	}()

	// Start the micro server service
	wg.Add(1)
	go func() {
		defer wg.Done()
		errChan <- service.Run()
	}()

	// we block here until either service or server fails
	if err := <-errChan; err != nil {
		log.Logf("[server] error running the server: %v", err)
	}

	// stop the server
	if err := s.stop(); err != nil {
		log.Logf("[server] error stopping server: %v", err)
		os.Exit(1)
	}

	// wait for all the goroutines to stop
	wg.Wait()

	log.Logf("[server] successfully stopped")
}

func Commands(options ...micro.Option) []cli.Command {
	command := cli.Command{
		Name:  "server",
		Usage: "Run the micro network server",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:   "address",
				Usage:  "Set the micro server address :8083",
				EnvVar: "MICRO_SERVER_ADDRESS",
			},
			cli.StringFlag{
				Name:   "router",
				Usage:  "Set the micro router address :9093",
				EnvVar: "MICRO_ROUTER_ADDRESS",
			},
			cli.StringFlag{
				Name:   "network",
				Usage:  "Set the micro network address :9094",
				EnvVar: "MICRO_NETWORK_ADDRESS",
			},
		},
		Action: func(ctx *cli.Context) {
			run(ctx, options...)
		},
	}

	for _, p := range Plugins() {
		if cmds := p.Commands(); len(cmds) > 0 {
			command.Subcommands = append(command.Subcommands, cmds...)
		}

		if flags := p.Flags(); len(flags) > 0 {
			command.Flags = append(command.Flags, flags...)
		}
	}

	return []cli.Command{command}
}
<|endoftext|>


<commit_before>// An IMAP server.
package server

import (
	"crypto/tls"
	"errors"
	"io"
	"log"
	"net"
	"os"

	"github.com/emersion/go-imap"
	"github.com/emersion/go-imap/backend"
	"github.com/emersion/go-imap/responses"
	"github.com/emersion/go-sasl"
)

// A command handler.
type Handler interface {
	imap.Parser

	// Handle this command for a given connection.
	//
	// By default, after this function has returned a status response is sent. To
	// prevent this behavior handlers can use ErrStatusResp or ErrNoStatusResp.
	Handle(conn Conn) error
}

// A connection upgrader. If a Handler is also an Upgrader, the connection will
// be upgraded after the Handler succeeds.
//
// This should only be used by libraries implementing an IMAP extension (e.g.
// COMPRESS).
type Upgrader interface {
	// Upgrade the connection. This method should call conn.Upgrade().
	Upgrade(conn Conn) error
}

// A function that creates handlers.
type HandlerFactory func() Handler

// A function that creates SASL servers.
type SaslServerFactory func(conn Conn) sasl.Server

// An IMAP extension.
type Extension interface {
	// Get capabilities provided by this extension for a given connection.
	Capabilities(c Conn) []string
	// Get the command handler factory for the provided command name.
	Command(name string) HandlerFactory
}

// An extension that provides additional features to each connection.
type ConnExtension interface {
	Extension

	// This function will be called when a client connects to the server. It can
	// be used to add new features to the default Conn interface by implementing
	// new methods.
	NewConn(c Conn) Conn
}

type errStatusResp struct {
	resp *imap.StatusResp
}

func (err *errStatusResp) Error() string {
	return ""
}

// ErrStatusResp can be returned by a Handler to replace the default status
// response. The response tag must be empty.
//
// To disable the default status response, use ErrNoStatusResp instead.
func ErrStatusResp(res *imap.StatusResp) error {
	return &errStatusResp{res}
}

// ErrNoStatusResp can be returned by a Handler to prevent the default status
// response from being sent.
func ErrNoStatusResp() error {
	return &errStatusResp{nil}
}

// An IMAP server.
type Server struct {
	listener net.Listener
	conns    []Conn

	commands   map[string]HandlerFactory
	auths      map[string]SaslServerFactory
	extensions []Extension

	// TCP address to listen on.
	Addr string
	// This server's TLS configuration.
	TLSConfig *tls.Config
	// This server's backend.
	Backend backend.Backend
	// Backend updates that will be sent to connected clients.
	Updates <-chan interface{}
	// Allow authentication over unencrypted connections.
	AllowInsecureAuth bool
	// An io.Writer to which all network activity will be mirrored.
	Debug io.Writer
	// ErrorLog specifies an optional logger for errors accepting
	// connections and unexpected behavior from handlers.
	// If nil, logging goes to os.Stderr via the log package's
	// standard logger.
	ErrorLog *log.Logger
}

// Create a new IMAP server from an existing listener.
func New(bkd backend.Backend) *Server {
	s := &Server{
		Backend:  bkd,
		ErrorLog: log.New(os.Stderr, "imap/server: ", log.LstdFlags),
	}

	s.auths = map[string]SaslServerFactory{
		"PLAIN": func(conn Conn) sasl.Server {
			return sasl.NewPlainServer(func(identity, username, password string) error {
				if identity != "" && identity != username {
					return errors.New("Identities not supported")
				}

				user, err := bkd.Login(username, password)
				if err != nil {
					return err
				}

				ctx := conn.Context()
				ctx.State = imap.AuthenticatedState
				ctx.User = user
				return nil
			})
		},
	}

	s.commands = map[string]HandlerFactory{
		imap.Noop:       func() Handler { return &Noop{} },
		imap.Capability: func() Handler { return &Capability{} },
		imap.Logout:     func() Handler { return &Logout{} },

		imap.StartTLS:     func() Handler { return &StartTLS{} },
		imap.Login:        func() Handler { return &Login{} },
		imap.Authenticate: func() Handler { return &Authenticate{} },

		imap.Select: func() Handler { return &Select{} },
		imap.Examine: func() Handler {
			hdlr := &Select{}
			hdlr.ReadOnly = true
			return hdlr
		},
		imap.Create:      func() Handler { return &Create{} },
		imap.Delete:      func() Handler { return &Delete{} },
		imap.Rename:      func() Handler { return &Rename{} },
		imap.Subscribe:   func() Handler { return &Subscribe{} },
		imap.Unsubscribe: func() Handler { return &Unsubscribe{} },
		imap.List:        func() Handler { return &List{} },
		imap.Lsub: func() Handler {
			hdlr := &List{}
			hdlr.Subscribed = true
			return hdlr
		},
		imap.Status: func() Handler { return &Status{} },
		imap.Append: func() Handler { return &Append{} },

		imap.Check:   func() Handler { return &Check{} },
		imap.Close:   func() Handler { return &Close{} },
		imap.Expunge: func() Handler { return &Expunge{} },
		imap.Search:  func() Handler { return &Search{} },
		imap.Fetch:   func() Handler { return &Fetch{} },
		imap.Store:   func() Handler { return &Store{} },
		imap.Copy:    func() Handler { return &Copy{} },
		imap.Uid:     func() Handler { return &Uid{} },
	}

	return s
}

// Serve accepts incoming connections on the Listener l.
func (s *Server) Serve(l net.Listener) error {
	s.listener = l
	defer s.Close()

	go s.listenUpdates()

	for {
		c, err := l.Accept()
		if err != nil {
			return err
		}

		var conn Conn = newConn(s, c)
		if s.Debug != nil {
			conn.conn().SetDebug(s.Debug)
		}

		for _, ext := range s.extensions {
			if ext, ok := ext.(ConnExtension); ok {
				conn = ext.NewConn(conn)
			}
		}

		go s.handleConn(conn)
	}
}

// ListenAndServe listens on the TCP network address s.Addr and then calls Serve
// to handle requests on incoming connections.
//
// If s.Addr is blank, ":imap" is used.
func (s *Server) ListenAndServe() error {
	addr := s.Addr
	if addr == "" {
		addr = ":imap"
	}

	l, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}

	return s.Serve(l)
}

// ListenAndServeTLS listens on the TCP network address s.Addr and then calls
// Serve to handle requests on incoming TLS connections.
//
// If s.Addr is blank, ":imaps" is used.
func (s *Server) ListenAndServeTLS() error {
	addr := s.Addr
	if addr == "" {
		addr = ":imaps"
	}

	l, err := tls.Listen("tcp", addr, s.TLSConfig)
	if err != nil {
		return err
	}

	return s.Serve(l)
}

func (s *Server) handleConn(conn Conn) error {
	s.conns = append(s.conns, conn)
	defer (func() {
		conn.Close()

		for i, c := range s.conns {
			if c == conn {
				s.conns = append(s.conns[:i], s.conns[i+1:]...)
				break
			}
		}
	})()

	// Send greeting
	if err := conn.greet(); err != nil {
		return err
	}

	for {
		if conn.Context().State == imap.LogoutState {
			return nil
		}

		var res *imap.StatusResp
		var up Upgrader

		conn.conn().Wait()
		fields, err := conn.conn().ReadLine()
		if err == io.EOF || conn.Context().State == imap.LogoutState {
			return nil
		}
		if err != nil {
			if imap.IsParseError(err) {
				res = &imap.StatusResp{
					Type: imap.StatusBad,
					Info: err.Error(),
				}
			} else {
				s.ErrorLog.Println("cannot read command: ", err)
				return err
			}
		} else {
			cmd := &imap.Command{}
			if err := cmd.Parse(fields); err != nil {
				res = &imap.StatusResp{
					Tag:  cmd.Tag,
					Type: imap.StatusBad,
					Info: err.Error(),
				}
			} else {
				var err error
				res, up, err = s.handleCommand(cmd, conn)
				if err != nil {
					res = &imap.StatusResp{
						Tag:  cmd.Tag,
						Type: imap.StatusBad,
						Info: err.Error(),
					}
				}
			}
		}

		if res != nil {
			conn.locker().Unlock()

			if err := conn.WriteResp(res); err != nil {
				s.ErrorLog.Println("cannot write response: ", err)
				conn.locker().Lock()
				continue
			}

			if up != nil && res.Type == imap.StatusOk {
				if err := up.Upgrade(conn); err != nil {
					s.ErrorLog.Println("cannot upgrade connection: ", err)
					return err
				}
			}

			conn.locker().Lock()
		}
	}
}

// Get a command handler factory for the provided command name.
func (s *Server) Command(name string) HandlerFactory {
	// Extensions can override builtin commands
	for _, ext := range s.extensions {
		if h := ext.Command(name); h != nil {
			return h
		}
	}

	return s.commands[name]
}

func (s *Server) commandHandler(cmd *imap.Command) (hdlr Handler, err error) {
	newHandler := s.Command(cmd.Name)
	if newHandler == nil {
		err = errors.New("Unknown command")
		return
	}

	hdlr = newHandler()
	err = hdlr.Parse(cmd.Arguments)
	return
}

func (s *Server) handleCommand(cmd *imap.Command, conn Conn) (res *imap.StatusResp, up Upgrader, err error) {
	hdlr, err := s.commandHandler(cmd)
	if err != nil {
		return
	}

	conn.locker().Unlock()
	defer conn.locker().Lock()

	hdlrErr := hdlr.Handle(conn)
	if statusErr, ok := hdlrErr.(*errStatusResp); ok {
		res = statusErr.resp
	} else if hdlrErr != nil {
		res = &imap.StatusResp{
			Type: imap.StatusNo,
			Info: hdlrErr.Error(),
		}
	} else {
		res = &imap.StatusResp{
			Type: imap.StatusOk,
		}
	}

	if res != nil {
		res.Tag = cmd.Tag

		if res.Type == imap.StatusOk && res.Info == "" {
			res.Info = cmd.Name + " completed"
		}
	}

	up, _ = hdlr.(Upgrader)
	return
}

func (s *Server) listenUpdates() (err error) {
	updater, ok := s.Backend.(backend.Updater)
	if !ok {
		return
	}
	s.Updates = updater.Updates()

	for {
		item := <-s.Updates

		var (
			update *backend.Update
			res imap.WriterTo
		)

		switch item := item.(type) {
		case *backend.StatusUpdate:
			update = &item.Update
			res = item.StatusResp
		case *backend.MailboxUpdate:
			update = &item.Update
			res = &responses.Select{Mailbox: item.MailboxStatus}
		case *backend.MessageUpdate:
			update = &item.Update

			ch := make(chan *imap.Message, 1)
			ch <- item.Message
			close(ch)

			res = &responses.Fetch{Messages: ch}
		case *backend.ExpungeUpdate:
			update = &item.Update

			ch := make(chan uint32, 1)
			ch <- item.SeqNum
			close(ch)

			res = &responses.Expunge{SeqNums: ch}
		default:
			s.ErrorLog.Printf("unhandled update: %T\n", item)
		}

		if update == nil || res == nil {
			continue
		}

		sends := make(chan struct{})
		wait := 0
		for _, conn := range s.conns {
			ctx := conn.Context()

			if update.Username != "" && (ctx.User == nil || ctx.User.Username() != update.Username) {
				continue
			}
			if update.Mailbox != "" && (ctx.Mailbox == nil || ctx.Mailbox.Name() != update.Mailbox) {
				continue
			}
			if *conn.silent() {
				// If silent is set, do not send message updates
				if _, ok := res.(*responses.Fetch); ok {
					continue
				}
			}

			go func() {
				done := make(chan struct{})
				conn.Context().Responses <- &response{
					response: res,
					done: done,
				}
				<-done
				sends <- struct{}{}
			}()

			wait++
		}

		if wait > 0 {
			go func() {
				for done := 0; done < wait; done++ {
					<-sends
				}
				close(sends)

				backend.DoneUpdate(update)
			}()
		} else {
			backend.DoneUpdate(update)
		}
	}
}

// Stops listening and closes all current connections.
func (s *Server) Close() error {
	if err := s.listener.Close(); err != nil {
		return err
	}

	for _, conn := range s.conns {
		conn.Close()
	}

	return nil
}

// Enable some IMAP extensions on this server.
//
// This function should not be called directly, it must only be used by
// libraries implementing extensions of the IMAP protocol.
func (s *Server) Enable(extensions ...Extension) {
	s.extensions = append(s.extensions, extensions...)
}

// Enable an authentication mechanism on this server.
//
// This function should not be called directly, it must only be used by
// libraries implementing extensions of the IMAP protocol.
func (s *Server) EnableAuth(name string, f SaslServerFactory) {
	s.auths[name] = f
}
<commit_msg>server: adds Server.ForEachConn()<commit_after>// An IMAP server.
package server

import (
	"crypto/tls"
	"errors"
	"io"
	"log"
	"net"
	"os"

	"github.com/emersion/go-imap"
	"github.com/emersion/go-imap/backend"
	"github.com/emersion/go-imap/responses"
	"github.com/emersion/go-sasl"
)

// A command handler.
type Handler interface {
	imap.Parser

	// Handle this command for a given connection.
	//
	// By default, after this function has returned a status response is sent. To
	// prevent this behavior handlers can use ErrStatusResp or ErrNoStatusResp.
	Handle(conn Conn) error
}

// A connection upgrader. If a Handler is also an Upgrader, the connection will
// be upgraded after the Handler succeeds.
//
// This should only be used by libraries implementing an IMAP extension (e.g.
// COMPRESS).
type Upgrader interface {
	// Upgrade the connection. This method should call conn.Upgrade().
	Upgrade(conn Conn) error
}

// A function that creates handlers.
type HandlerFactory func() Handler

// A function that creates SASL servers.
type SaslServerFactory func(conn Conn) sasl.Server

// An IMAP extension.
type Extension interface {
	// Get capabilities provided by this extension for a given connection.
	Capabilities(c Conn) []string
	// Get the command handler factory for the provided command name.
	Command(name string) HandlerFactory
}

// An extension that provides additional features to each connection.
type ConnExtension interface {
	Extension

	// This function will be called when a client connects to the server. It can
	// be used to add new features to the default Conn interface by implementing
	// new methods.
	NewConn(c Conn) Conn
}

type errStatusResp struct {
	resp *imap.StatusResp
}

func (err *errStatusResp) Error() string {
	return ""
}

// ErrStatusResp can be returned by a Handler to replace the default status
// response. The response tag must be empty.
//
// To disable the default status response, use ErrNoStatusResp instead.
func ErrStatusResp(res *imap.StatusResp) error {
	return &errStatusResp{res}
}

// ErrNoStatusResp can be returned by a Handler to prevent the default status
// response from being sent.
func ErrNoStatusResp() error {
	return &errStatusResp{nil}
}

// An IMAP server.
type Server struct {
	listener net.Listener
	conns    []Conn

	commands   map[string]HandlerFactory
	auths      map[string]SaslServerFactory
	extensions []Extension

	// TCP address to listen on.
	Addr string
	// This server's TLS configuration.
	TLSConfig *tls.Config
	// This server's backend.
	Backend backend.Backend
	// Backend updates that will be sent to connected clients.
	Updates <-chan interface{}
	// Allow authentication over unencrypted connections.
	AllowInsecureAuth bool
	// An io.Writer to which all network activity will be mirrored.
	Debug io.Writer
	// ErrorLog specifies an optional logger for errors accepting
	// connections and unexpected behavior from handlers.
	// If nil, logging goes to os.Stderr via the log package's
	// standard logger.
	ErrorLog *log.Logger
}

// Create a new IMAP server from an existing listener.
func New(bkd backend.Backend) *Server {
	s := &Server{
		Backend:  bkd,
		ErrorLog: log.New(os.Stderr, "imap/server: ", log.LstdFlags),
	}

	s.auths = map[string]SaslServerFactory{
		"PLAIN": func(conn Conn) sasl.Server {
			return sasl.NewPlainServer(func(identity, username, password string) error {
				if identity != "" && identity != username {
					return errors.New("Identities not supported")
				}

				user, err := bkd.Login(username, password)
				if err != nil {
					return err
				}

				ctx := conn.Context()
				ctx.State = imap.AuthenticatedState
				ctx.User = user
				return nil
			})
		},
	}

	s.commands = map[string]HandlerFactory{
		imap.Noop:       func() Handler { return &Noop{} },
		imap.Capability: func() Handler { return &Capability{} },
		imap.Logout:     func() Handler { return &Logout{} },

		imap.StartTLS:     func() Handler { return &StartTLS{} },
		imap.Login:        func() Handler { return &Login{} },
		imap.Authenticate: func() Handler { return &Authenticate{} },

		imap.Select: func() Handler { return &Select{} },
		imap.Examine: func() Handler {
			hdlr := &Select{}
			hdlr.ReadOnly = true
			return hdlr
		},
		imap.Create:      func() Handler { return &Create{} },
		imap.Delete:      func() Handler { return &Delete{} },
		imap.Rename:      func() Handler { return &Rename{} },
		imap.Subscribe:   func() Handler { return &Subscribe{} },
		imap.Unsubscribe: func() Handler { return &Unsubscribe{} },
		imap.List:        func() Handler { return &List{} },
		imap.Lsub: func() Handler {
			hdlr := &List{}
			hdlr.Subscribed = true
			return hdlr
		},
		imap.Status: func() Handler { return &Status{} },
		imap.Append: func() Handler { return &Append{} },

		imap.Check:   func() Handler { return &Check{} },
		imap.Close:   func() Handler { return &Close{} },
		imap.Expunge: func() Handler { return &Expunge{} },
		imap.Search:  func() Handler { return &Search{} },
		imap.Fetch:   func() Handler { return &Fetch{} },
		imap.Store:   func() Handler { return &Store{} },
		imap.Copy:    func() Handler { return &Copy{} },
		imap.Uid:     func() Handler { return &Uid{} },
	}

	return s
}

// Serve accepts incoming connections on the Listener l.
func (s *Server) Serve(l net.Listener) error {
	s.listener = l
	defer s.Close()

	go s.listenUpdates()

	for {
		c, err := l.Accept()
		if err != nil {
			return err
		}

		var conn Conn = newConn(s, c)
		if s.Debug != nil {
			conn.conn().SetDebug(s.Debug)
		}

		for _, ext := range s.extensions {
			if ext, ok := ext.(ConnExtension); ok {
				conn = ext.NewConn(conn)
			}
		}

		go s.handleConn(conn)
	}
}

// ListenAndServe listens on the TCP network address s.Addr and then calls Serve
// to handle requests on incoming connections.
//
// If s.Addr is blank, ":imap" is used.
func (s *Server) ListenAndServe() error {
	addr := s.Addr
	if addr == "" {
		addr = ":imap"
	}

	l, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}

	return s.Serve(l)
}

// ListenAndServeTLS listens on the TCP network address s.Addr and then calls
// Serve to handle requests on incoming TLS connections.
//
// If s.Addr is blank, ":imaps" is used.
func (s *Server) ListenAndServeTLS() error {
	addr := s.Addr
	if addr == "" {
		addr = ":imaps"
	}

	l, err := tls.Listen("tcp", addr, s.TLSConfig)
	if err != nil {
		return err
	}

	return s.Serve(l)
}

func (s *Server) handleConn(conn Conn) error {
	s.conns = append(s.conns, conn)
	defer (func() {
		conn.Close()

		for i, c := range s.conns {
			if c == conn {
				s.conns = append(s.conns[:i], s.conns[i+1:]...)
				break
			}
		}
	})()

	// Send greeting
	if err := conn.greet(); err != nil {
		return err
	}

	for {
		if conn.Context().State == imap.LogoutState {
			return nil
		}

		var res *imap.StatusResp
		var up Upgrader

		conn.conn().Wait()
		fields, err := conn.conn().ReadLine()
		if err == io.EOF || conn.Context().State == imap.LogoutState {
			return nil
		}
		if err != nil {
			if imap.IsParseError(err) {
				res = &imap.StatusResp{
					Type: imap.StatusBad,
					Info: err.Error(),
				}
			} else {
				s.ErrorLog.Println("cannot read command: ", err)
				return err
			}
		} else {
			cmd := &imap.Command{}
			if err := cmd.Parse(fields); err != nil {
				res = &imap.StatusResp{
					Tag:  cmd.Tag,
					Type: imap.StatusBad,
					Info: err.Error(),
				}
			} else {
				var err error
				res, up, err = s.handleCommand(cmd, conn)
				if err != nil {
					res = &imap.StatusResp{
						Tag:  cmd.Tag,
						Type: imap.StatusBad,
						Info: err.Error(),
					}
				}
			}
		}

		if res != nil {
			conn.locker().Unlock()

			if err := conn.WriteResp(res); err != nil {
				s.ErrorLog.Println("cannot write response: ", err)
				conn.locker().Lock()
				continue
			}

			if up != nil && res.Type == imap.StatusOk {
				if err := up.Upgrade(conn); err != nil {
					s.ErrorLog.Println("cannot upgrade connection: ", err)
					return err
				}
			}

			conn.locker().Lock()
		}
	}
}

// Get a command handler factory for the provided command name.
func (s *Server) Command(name string) HandlerFactory {
	// Extensions can override builtin commands
	for _, ext := range s.extensions {
		if h := ext.Command(name); h != nil {
			return h
		}
	}

	return s.commands[name]
}

func (s *Server) commandHandler(cmd *imap.Command) (hdlr Handler, err error) {
	newHandler := s.Command(cmd.Name)
	if newHandler == nil {
		err = errors.New("Unknown command")
		return
	}

	hdlr = newHandler()
	err = hdlr.Parse(cmd.Arguments)
	return
}

func (s *Server) handleCommand(cmd *imap.Command, conn Conn) (res *imap.StatusResp, up Upgrader, err error) {
	hdlr, err := s.commandHandler(cmd)
	if err != nil {
		return
	}

	conn.locker().Unlock()
	defer conn.locker().Lock()

	hdlrErr := hdlr.Handle(conn)
	if statusErr, ok := hdlrErr.(*errStatusResp); ok {
		res = statusErr.resp
	} else if hdlrErr != nil {
		res = &imap.StatusResp{
			Type: imap.StatusNo,
			Info: hdlrErr.Error(),
		}
	} else {
		res = &imap.StatusResp{
			Type: imap.StatusOk,
		}
	}

	if res != nil {
		res.Tag = cmd.Tag

		if res.Type == imap.StatusOk && res.Info == "" {
			res.Info = cmd.Name + " completed"
		}
	}

	up, _ = hdlr.(Upgrader)
	return
}

func (s *Server) listenUpdates() (err error) {
	updater, ok := s.Backend.(backend.Updater)
	if !ok {
		return
	}
	s.Updates = updater.Updates()

	for {
		item := <-s.Updates

		var (
			update *backend.Update
			res imap.WriterTo
		)

		switch item := item.(type) {
		case *backend.StatusUpdate:
			update = &item.Update
			res = item.StatusResp
		case *backend.MailboxUpdate:
			update = &item.Update
			res = &responses.Select{Mailbox: item.MailboxStatus}
		case *backend.MessageUpdate:
			update = &item.Update

			ch := make(chan *imap.Message, 1)
			ch <- item.Message
			close(ch)

			res = &responses.Fetch{Messages: ch}
		case *backend.ExpungeUpdate:
			update = &item.Update

			ch := make(chan uint32, 1)
			ch <- item.SeqNum
			close(ch)

			res = &responses.Expunge{SeqNums: ch}
		default:
			s.ErrorLog.Printf("unhandled update: %T\n", item)
		}

		if update == nil || res == nil {
			continue
		}

		sends := make(chan struct{})
		wait := 0
		for _, conn := range s.conns {
			ctx := conn.Context()

			if update.Username != "" && (ctx.User == nil || ctx.User.Username() != update.Username) {
				continue
			}
			if update.Mailbox != "" && (ctx.Mailbox == nil || ctx.Mailbox.Name() != update.Mailbox) {
				continue
			}
			if *conn.silent() {
				// If silent is set, do not send message updates
				if _, ok := res.(*responses.Fetch); ok {
					continue
				}
			}

			go func() {
				done := make(chan struct{})
				conn.Context().Responses <- &response{
					response: res,
					done: done,
				}
				<-done
				sends <- struct{}{}
			}()

			wait++
		}

		if wait > 0 {
			go func() {
				for done := 0; done < wait; done++ {
					<-sends
				}
				close(sends)

				backend.DoneUpdate(update)
			}()
		} else {
			backend.DoneUpdate(update)
		}
	}
}

// ForEachConn iterates through all opened connections.
func (s *Server) ForEachConn(f func(Conn)) {
	for _, conn := range s.conns {
		f(conn)
	}
}

// Stops listening and closes all current connections.
func (s *Server) Close() error {
	if err := s.listener.Close(); err != nil {
		return err
	}

	for _, conn := range s.conns {
		conn.Close()
	}

	return nil
}

// Enable some IMAP extensions on this server.
//
// This function should not be called directly, it must only be used by
// libraries implementing extensions of the IMAP protocol.
func (s *Server) Enable(extensions ...Extension) {
	s.extensions = append(s.extensions, extensions...)
}

// Enable an authentication mechanism on this server.
//
// This function should not be called directly, it must only be used by
// libraries implementing extensions of the IMAP protocol.
func (s *Server) EnableAuth(name string, f SaslServerFactory) {
	s.auths[name] = f
}
<|endoftext|>


<commit_before>package device

import (
	"context"
	"errors"
	"sync"
	"time"

	"github.com/funjack/golaunch"
	"github.com/funjack/launchcontrol/protocol"
)

var (
	// ErrNotSupported is returned when the request operation is not
	// supported by the active scriptplayer.
	ErrNotSupported = errors.New("operation is not supported")
	// ErrNotPlaying is returned when the operation is only supported while
	// a script is playing.
	ErrNotPlaying = errors.New("not playing")
)

// ConnectionTimeout is the default timeout used per Launch connecting attempt.
var ConnectionTimeout = time.Second * 10

// LaunchManager is responsible for connecting and communicating with the
// Launch.
type LaunchManager struct {
	sync.Mutex

	launch  golaunch.Launch
	player  protocol.Player
	tracers map[chan protocol.Action]bool

	isPlaying   bool
	isConnected bool
}

// NewLaunchManager creates a new manager for the given Launch.
func NewLaunchManager(l golaunch.Launch) *LaunchManager {
	lm := &LaunchManager{
		launch:  l,
		tracers: make(map[chan protocol.Action]bool),
	}
	lm.launch.HandleDisconnect(func() {
		lm.Lock()
		defer lm.Unlock()

		// TODO implement nice reconnect handling
		lm.isConnected = false
		lm.player.Stop()
		lm.isPlaying = false
	})

	return lm
}

// SetScriptPlayer switches the active ScriptPlayer. Any active script will be
// stopped.
func (m *LaunchManager) SetScriptPlayer(p protocol.Player) error {
	m.Lock()
	defer m.Unlock()

	if m.isPlaying {
		if err := m.player.Stop(); err != nil {
			return err
		}
	}
	m.player = p
	return nil
}

// connect will check if the manager has an active connection with the Launch
// or else tries to (re)connect.
func (m *LaunchManager) connect() error {
	if m.isConnected {
		return nil
	}
	ctx, cancel := context.WithTimeout(context.Background(), ConnectionTimeout)
	defer cancel()
	if err := m.launch.Connect(ctx); err != nil {
		return err
	}

	m.isConnected = true
	return nil
}

// Play will start playback using the loaded scriptplayer to the connected
// Launch.
func (m *LaunchManager) Play() error {
	m.Lock()
	defer m.Unlock()

	if m.isPlaying {
		return nil
	}

	if err := m.connect(); err != nil {
		return err
	}

	if m.player != nil {
		m.isPlaying = true
		go func() {
			for a := range m.player.Play() {
				m.launch.Move(a.Position, a.Speed)
				for t := range m.tracers {
					select {
					case t <- a:
					default:
						close(t)
						m.Lock()
						delete(m.tracers, t)
						m.Unlock()
					}
				}
			}
			m.Lock()
			m.isPlaying = false
			m.Unlock()
		}()
	}
	return nil
}

// Stop will halt playback and reset the scriptplayer.
func (m *LaunchManager) Stop() error {
	m.Lock()
	defer m.Unlock()

	if m.isPlaying {
		return m.player.Stop()
	}
	return nil
}

// Pause will halt playback but keep the current position.
func (m *LaunchManager) Pause() error {
	m.Lock()
	defer m.Unlock()

	if m.isPlaying {
		if pp, ok := m.player.(protocol.Pausable); ok {
			return pp.Pause()
		}
		return ErrNotSupported
	}
	return ErrNotPlaying
}

// Resume starts playback from the paused position.
func (m *LaunchManager) Resume() error {
	m.Lock()
	defer m.Unlock()

	if m.isPlaying {
		if pp, ok := m.player.(protocol.Pausable); ok {
			return pp.Resume()
		}
		return ErrNotSupported
	}
	return ErrNotPlaying
}

// Skip jumps playback position to the specified time.
func (m *LaunchManager) Skip(p time.Duration) error {
	m.Lock()
	defer m.Unlock()

	if m.isPlaying {
		if pp, ok := m.player.(protocol.Skippable); ok {
			return pp.Skip(p)
		}
		return ErrNotSupported
	}
	return ErrNotPlaying
}

// Dump will return the full loaded script.
func (m *LaunchManager) Dump() (protocol.TimedActions, error) {
	m.Lock()
	defer m.Unlock()

	if pp, ok := m.player.(protocol.Dumpable); ok {
		return pp.Dump()
	}
	return nil, ErrNotSupported
}

// Trace returns a channel that receives the same actions as are send to the
// Launch.
func (m *LaunchManager) Trace() <-chan protocol.Action {
	m.Lock()
	defer m.Unlock()
	t := make(chan protocol.Action)
	m.tracers[t] = true
	return t
}
<commit_msg>Bugfix UI websocket disconnect on new script<commit_after>package device

import (
	"context"
	"errors"
	"sync"
	"time"

	"github.com/funjack/golaunch"
	"github.com/funjack/launchcontrol/protocol"
)

var (
	// ErrNotSupported is returned when the request operation is not
	// supported by the active scriptplayer.
	ErrNotSupported = errors.New("operation is not supported")
	// ErrNotPlaying is returned when the operation is only supported while
	// a script is playing.
	ErrNotPlaying = errors.New("not playing")
)

// ConnectionTimeout is the default timeout used per Launch connecting attempt.
var ConnectionTimeout = time.Second * 10

// LaunchManager is responsible for connecting and communicating with the
// Launch.
type LaunchManager struct {
	sync.Mutex

	launch  golaunch.Launch
	player  protocol.Player
	tracers map[chan protocol.Action]bool

	isPlaying   bool
	isConnected bool
}

// NewLaunchManager creates a new manager for the given Launch.
func NewLaunchManager(l golaunch.Launch) *LaunchManager {
	lm := &LaunchManager{
		launch:  l,
		tracers: make(map[chan protocol.Action]bool),
	}
	lm.launch.HandleDisconnect(func() {
		lm.Lock()
		defer lm.Unlock()

		// TODO implement nice reconnect handling
		lm.isConnected = false
		lm.player.Stop()
		lm.isPlaying = false
	})

	return lm
}

// SetScriptPlayer switches the active ScriptPlayer. Any active script will be
// stopped.
func (m *LaunchManager) SetScriptPlayer(p protocol.Player) error {
	m.Lock()
	defer m.Unlock()

	if m.isPlaying {
		if err := m.player.Stop(); err != nil {
			return err
		}
	}
	m.player = p
	return nil
}

// connect will check if the manager has an active connection with the Launch
// or else tries to (re)connect.
func (m *LaunchManager) connect() error {
	if m.isConnected {
		return nil
	}
	ctx, cancel := context.WithTimeout(context.Background(), ConnectionTimeout)
	defer cancel()
	if err := m.launch.Connect(ctx); err != nil {
		return err
	}

	m.isConnected = true
	return nil
}

// Play will start playback using the loaded scriptplayer to the connected
// Launch.
func (m *LaunchManager) Play() error {
	m.Lock()
	defer m.Unlock()

	if m.isPlaying {
		return nil
	}

	if err := m.connect(); err != nil {
		return err
	}

	if m.player != nil {
		m.isPlaying = true
		go func() {
			for a := range m.player.Play() {
				m.launch.Move(a.Position, a.Speed)
				for t := range m.tracers {
					select {
					case t <- a:
					default:
						close(t)
						m.Lock()
						delete(m.tracers, t)
						m.Unlock()
					}
				}
			}
			m.Lock()
			m.isPlaying = false
			m.Unlock()
		}()
	}
	return nil
}

// Stop will halt playback and reset the scriptplayer.
func (m *LaunchManager) Stop() error {
	m.Lock()
	defer m.Unlock()

	if m.isPlaying {
		return m.player.Stop()
	}
	return nil
}

// Pause will halt playback but keep the current position.
func (m *LaunchManager) Pause() error {
	m.Lock()
	defer m.Unlock()

	if m.isPlaying {
		if pp, ok := m.player.(protocol.Pausable); ok {
			return pp.Pause()
		}
		return ErrNotSupported
	}
	return ErrNotPlaying
}

// Resume starts playback from the paused position.
func (m *LaunchManager) Resume() error {
	m.Lock()
	defer m.Unlock()

	if m.isPlaying {
		if pp, ok := m.player.(protocol.Pausable); ok {
			return pp.Resume()
		}
		return ErrNotSupported
	}
	return ErrNotPlaying
}

// Skip jumps playback position to the specified time.
func (m *LaunchManager) Skip(p time.Duration) error {
	m.Lock()
	defer m.Unlock()

	if m.isPlaying {
		if pp, ok := m.player.(protocol.Skippable); ok {
			return pp.Skip(p)
		}
		return ErrNotSupported
	}
	return ErrNotPlaying
}

// Dump will return the full loaded script.
func (m *LaunchManager) Dump() (protocol.TimedActions, error) {
	m.Lock()
	defer m.Unlock()

	if pp, ok := m.player.(protocol.Dumpable); ok {
		return pp.Dump()
	}
	return nil, ErrNotSupported
}

// Trace returns a channel that receives the same actions as are send to the
// Launch.
func (m *LaunchManager) Trace() <-chan protocol.Action {
	m.Lock()
	defer m.Unlock()
	t := make(chan protocol.Action, 8)
	m.tracers[t] = true
	return t
}
<|endoftext|>


<commit_before>package dht

import (
	"context"
	"fmt"
	"github.com/libp2p/go-libp2p-core/peer"
	"time"

	multierror "github.com/hashicorp/go-multierror"
	process "github.com/jbenet/goprocess"
	processctx "github.com/jbenet/goprocess/context"
	kbucket "github.com/libp2p/go-libp2p-kbucket"
	"github.com/multiformats/go-multiaddr"
	_ "github.com/multiformats/go-multiaddr-dns"
)

var DefaultBootstrapPeers []multiaddr.Multiaddr

// Minimum number of peers in the routing table. If we drop below this and we
// see a new peer, we trigger a bootstrap round.
var minRTRefreshThreshold = 10

// timeout for pinging one peer
const peerPingTimeout = 10 * time.Second

func init() {
	for _, s := range []string{
		"/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN",
		"/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa",
		"/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb",
		"/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt",
		"/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", // mars.i.ipfs.io
	} {
		ma, err := multiaddr.NewMultiaddr(s)
		if err != nil {
			panic(err)
		}
		DefaultBootstrapPeers = append(DefaultBootstrapPeers, ma)
	}
}

// startSelfLookup starts a go-routine that listens for requests to trigger a self walk on a dedicated channel
// and then sends the error status back on the error channel sent along with the request.
// if multiple callers "simultaneously" ask for a self walk, it performs ONLY one self walk and sends the same error status to all of them.
func (dht *IpfsDHT) startSelfLookup() error {
	dht.proc.Go(func(proc process.Process) {
		ctx := processctx.OnClosingContext(proc)
		for {
			var waiting []chan<- error
			select {
			case res := <-dht.triggerSelfLookup:
				if res != nil {
					waiting = append(waiting, res)
				}
			case <-ctx.Done():
				return
			}

			// batch multiple refresh requests if they're all waiting at the same time.
			waiting = append(waiting, collectWaitingChannels(dht.triggerSelfLookup)...)

			// Do a self walk
			queryCtx, cancel := context.WithTimeout(ctx, dht.rtRefreshQueryTimeout)
			_, err := dht.GetClosestPeers(queryCtx, string(dht.self))
			if err == kbucket.ErrLookupFailure {
				err = nil
			} else if err != nil {
				err = fmt.Errorf("failed to query self during routing table refresh: %s", err)
			}
			cancel()

			// send back the error status
			for _, w := range waiting {
				w <- err
				close(w)
			}
			if err != nil {
				logger.Warning(err)
			}
		}
	})

	return nil
}

// Start the refresh worker.
func (dht *IpfsDHT) startRefreshing() error {
	// scan the RT table periodically & do a random walk for cpl's that haven't been queried since the given period
	dht.proc.Go(func(proc process.Process) {
		ctx := processctx.OnClosingContext(proc)

		refreshTicker := time.NewTicker(dht.rtRefreshInterval)
		defer refreshTicker.Stop()

		// refresh if option is set
		if dht.autoRefresh {
			dht.doRefresh(ctx)
		} else {
			// disable the "auto-refresh" ticker so that no more ticks are sent to this channel
			refreshTicker.Stop()
		}

		for {
			var waiting []chan<- error
			select {
			case <-refreshTicker.C:
			case res := <-dht.triggerRtRefresh:
				if res != nil {
					waiting = append(waiting, res)
				}
			case <-ctx.Done():
				return
			}

			// Batch multiple refresh requests if they're all waiting at the same time.
			waiting = append(waiting, collectWaitingChannels(dht.triggerRtRefresh)...)

			err := dht.doRefresh(ctx)
			for _, w := range waiting {
				w <- err
				close(w)
			}
			if err != nil {
				logger.Warning(err)
			}

			// ping Routing Table peers that haven't been hear of/from in the interval they should have been.
			for _, ps := range dht.routingTable.GetPeerInfos() {
				// ping the peer if it's due for a ping and evict it if the ping fails
				if time.Since(ps.LastSuccessfulOutboundQuery) > dht.maxLastSuccessfulOutboundThreshold {
					livelinessCtx, cancel := context.WithTimeout(ctx, peerPingTimeout)
					if err := dht.host.Connect(livelinessCtx, peer.AddrInfo{ID: ps.Id}); err != nil {
						logger.Debugf("failed to ping peer=%s, got error=%s, evicting it from the RT", ps.Id, err)
						dht.routingTable.RemovePeer(ps.Id)
					}
					cancel()
				}
			}

		}
	})

	return nil
}

func collectWaitingChannels(source chan chan<- error) []chan<- error {
	var waiting []chan<- error
	for {
		select {
		case res := <-source:
			if res != nil {
				waiting = append(waiting, res)
			}
		default:
			return waiting
		}
	}
}

func (dht *IpfsDHT) doRefresh(ctx context.Context) error {
	var merr error

	// wait for the self walk result
	selfWalkres := make(chan error, 1)

	select {
	case dht.triggerSelfLookup <- selfWalkres:
	case <-ctx.Done():
		return ctx.Err()
	}

	select {
	case err := <-selfWalkres:
		if err != nil {
			merr = multierror.Append(merr, err)
		}
	case <-ctx.Done():
		return ctx.Err()
	}

	if err := dht.refreshCpls(ctx); err != nil {
		merr = multierror.Append(merr, err)
	}
	return merr
}

// refreshCpls scans the routing table, and does a random walk for cpl's that haven't been queried since the given period
func (dht *IpfsDHT) refreshCpls(ctx context.Context) error {
	doQuery := func(cpl uint, target string, f func(context.Context) error) error {
		logger.Infof("starting refreshing cpl %d to %s (routing table size was %d)",
			cpl, target, dht.routingTable.Size())
		defer func() {
			logger.Infof("finished refreshing cpl %d to %s (routing table size is now %d)",
				cpl, target, dht.routingTable.Size())
		}()
		queryCtx, cancel := context.WithTimeout(ctx, dht.rtRefreshQueryTimeout)
		defer cancel()
		err := f(queryCtx)
		if err == context.DeadlineExceeded && queryCtx.Err() == context.DeadlineExceeded && ctx.Err() == nil {
			return nil
		}
		return err
	}

	trackedCpls := dht.routingTable.GetTrackedCplsForRefresh()

	var merr error
	for _, tcpl := range trackedCpls {
		if time.Since(tcpl.LastRefreshAt) <= dht.rtRefreshInterval {
			continue
		}

		// gen rand peer with the cpl
		randPeer, err := dht.routingTable.GenRandPeerID(tcpl.Cpl)
		if err != nil {
			logger.Errorf("failed to generate peerID for cpl %d, err: %s", tcpl.Cpl, err)
			continue
		}

		// walk to the generated peer
		walkFnc := func(c context.Context) error {
			_, err := dht.GetClosestPeers(c, string(randPeer))
			return err
		}

		if err := doQuery(tcpl.Cpl, randPeer.String(), walkFnc); err != nil {
			merr = multierror.Append(
				merr,
				fmt.Errorf("failed to do a random walk for cpl %d: %s", tcpl.Cpl, err),
			)
		}
	}
	return merr
}

// Bootstrap tells the DHT to get into a bootstrapped state satisfying the
// IpfsRouter interface.
//
// This just calls `RefreshRoutingTable`.
func (dht *IpfsDHT) Bootstrap(_ context.Context) error {
	dht.RefreshRoutingTable()
	return nil
}

// RefreshRoutingTable tells the DHT to refresh it's routing tables.
//
// The returned channel will block until the refresh finishes, then yield the
// error and close. The channel is buffered and safe to ignore.
func (dht *IpfsDHT) RefreshRoutingTable() <-chan error {
	res := make(chan error, 1)
	dht.triggerRtRefresh <- res
	return res
}
<commit_msg>refreshing the routing table after the dht context is cancelled returns an error instead of hanging<commit_after>package dht

import (
	"context"
	"fmt"
	"github.com/libp2p/go-libp2p-core/peer"
	"time"

	multierror "github.com/hashicorp/go-multierror"
	process "github.com/jbenet/goprocess"
	processctx "github.com/jbenet/goprocess/context"
	kbucket "github.com/libp2p/go-libp2p-kbucket"
	"github.com/multiformats/go-multiaddr"
	_ "github.com/multiformats/go-multiaddr-dns"
)

var DefaultBootstrapPeers []multiaddr.Multiaddr

// Minimum number of peers in the routing table. If we drop below this and we
// see a new peer, we trigger a bootstrap round.
var minRTRefreshThreshold = 10

// timeout for pinging one peer
const peerPingTimeout = 10 * time.Second

func init() {
	for _, s := range []string{
		"/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN",
		"/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa",
		"/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb",
		"/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt",
		"/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", // mars.i.ipfs.io
	} {
		ma, err := multiaddr.NewMultiaddr(s)
		if err != nil {
			panic(err)
		}
		DefaultBootstrapPeers = append(DefaultBootstrapPeers, ma)
	}
}

// startSelfLookup starts a go-routine that listens for requests to trigger a self walk on a dedicated channel
// and then sends the error status back on the error channel sent along with the request.
// if multiple callers "simultaneously" ask for a self walk, it performs ONLY one self walk and sends the same error status to all of them.
func (dht *IpfsDHT) startSelfLookup() error {
	dht.proc.Go(func(proc process.Process) {
		ctx := processctx.OnClosingContext(proc)
		for {
			var waiting []chan<- error
			select {
			case res := <-dht.triggerSelfLookup:
				if res != nil {
					waiting = append(waiting, res)
				}
			case <-ctx.Done():
				return
			}

			// batch multiple refresh requests if they're all waiting at the same time.
			waiting = append(waiting, collectWaitingChannels(dht.triggerSelfLookup)...)

			// Do a self walk
			queryCtx, cancel := context.WithTimeout(ctx, dht.rtRefreshQueryTimeout)
			_, err := dht.GetClosestPeers(queryCtx, string(dht.self))
			if err == kbucket.ErrLookupFailure {
				err = nil
			} else if err != nil {
				err = fmt.Errorf("failed to query self during routing table refresh: %s", err)
			}
			cancel()

			// send back the error status
			for _, w := range waiting {
				w <- err
				close(w)
			}
			if err != nil {
				logger.Warning(err)
			}
		}
	})

	return nil
}

// Start the refresh worker.
func (dht *IpfsDHT) startRefreshing() error {
	// scan the RT table periodically & do a random walk for cpl's that haven't been queried since the given period
	dht.proc.Go(func(proc process.Process) {
		ctx := processctx.OnClosingContext(proc)

		refreshTicker := time.NewTicker(dht.rtRefreshInterval)
		defer refreshTicker.Stop()

		// refresh if option is set
		if dht.autoRefresh {
			dht.doRefresh(ctx)
		} else {
			// disable the "auto-refresh" ticker so that no more ticks are sent to this channel
			refreshTicker.Stop()
		}

		for {
			var waiting []chan<- error
			select {
			case <-refreshTicker.C:
			case res := <-dht.triggerRtRefresh:
				if res != nil {
					waiting = append(waiting, res)
				}
			case <-ctx.Done():
				return
			}

			// Batch multiple refresh requests if they're all waiting at the same time.
			waiting = append(waiting, collectWaitingChannels(dht.triggerRtRefresh)...)

			err := dht.doRefresh(ctx)
			for _, w := range waiting {
				w <- err
				close(w)
			}
			if err != nil {
				logger.Warning(err)
			}

			// ping Routing Table peers that haven't been hear of/from in the interval they should have been.
			for _, ps := range dht.routingTable.GetPeerInfos() {
				// ping the peer if it's due for a ping and evict it if the ping fails
				if time.Since(ps.LastSuccessfulOutboundQuery) > dht.maxLastSuccessfulOutboundThreshold {
					livelinessCtx, cancel := context.WithTimeout(ctx, peerPingTimeout)
					if err := dht.host.Connect(livelinessCtx, peer.AddrInfo{ID: ps.Id}); err != nil {
						logger.Debugf("failed to ping peer=%s, got error=%s, evicting it from the RT", ps.Id, err)
						dht.routingTable.RemovePeer(ps.Id)
					}
					cancel()
				}
			}

		}
	})

	return nil
}

func collectWaitingChannels(source chan chan<- error) []chan<- error {
	var waiting []chan<- error
	for {
		select {
		case res := <-source:
			if res != nil {
				waiting = append(waiting, res)
			}
		default:
			return waiting
		}
	}
}

func (dht *IpfsDHT) doRefresh(ctx context.Context) error {
	var merr error

	// wait for the self walk result
	selfWalkres := make(chan error, 1)

	select {
	case dht.triggerSelfLookup <- selfWalkres:
	case <-ctx.Done():
		return ctx.Err()
	}

	select {
	case err := <-selfWalkres:
		if err != nil {
			merr = multierror.Append(merr, err)
		}
	case <-ctx.Done():
		return ctx.Err()
	}

	if err := dht.refreshCpls(ctx); err != nil {
		merr = multierror.Append(merr, err)
	}
	return merr
}

// refreshCpls scans the routing table, and does a random walk for cpl's that haven't been queried since the given period
func (dht *IpfsDHT) refreshCpls(ctx context.Context) error {
	doQuery := func(cpl uint, target string, f func(context.Context) error) error {
		logger.Infof("starting refreshing cpl %d to %s (routing table size was %d)",
			cpl, target, dht.routingTable.Size())
		defer func() {
			logger.Infof("finished refreshing cpl %d to %s (routing table size is now %d)",
				cpl, target, dht.routingTable.Size())
		}()
		queryCtx, cancel := context.WithTimeout(ctx, dht.rtRefreshQueryTimeout)
		defer cancel()
		err := f(queryCtx)
		if err == context.DeadlineExceeded && queryCtx.Err() == context.DeadlineExceeded && ctx.Err() == nil {
			return nil
		}
		return err
	}

	trackedCpls := dht.routingTable.GetTrackedCplsForRefresh()

	var merr error
	for _, tcpl := range trackedCpls {
		if time.Since(tcpl.LastRefreshAt) <= dht.rtRefreshInterval {
			continue
		}

		// gen rand peer with the cpl
		randPeer, err := dht.routingTable.GenRandPeerID(tcpl.Cpl)
		if err != nil {
			logger.Errorf("failed to generate peerID for cpl %d, err: %s", tcpl.Cpl, err)
			continue
		}

		// walk to the generated peer
		walkFnc := func(c context.Context) error {
			_, err := dht.GetClosestPeers(c, string(randPeer))
			return err
		}

		if err := doQuery(tcpl.Cpl, randPeer.String(), walkFnc); err != nil {
			merr = multierror.Append(
				merr,
				fmt.Errorf("failed to do a random walk for cpl %d: %s", tcpl.Cpl, err),
			)
		}
	}
	return merr
}

// Bootstrap tells the DHT to get into a bootstrapped state satisfying the
// IpfsRouter interface.
//
// This just calls `RefreshRoutingTable`.
func (dht *IpfsDHT) Bootstrap(_ context.Context) error {
	dht.RefreshRoutingTable()
	return nil
}

// RefreshRoutingTable tells the DHT to refresh it's routing tables.
//
// The returned channel will block until the refresh finishes, then yield the
// error and close. The channel is buffered and safe to ignore.
func (dht *IpfsDHT) RefreshRoutingTable() <-chan error {
	res := make(chan error, 1)
	select {
	case dht.triggerRtRefresh <- res:
	case <-dht.ctx.Done():
		res <- dht.ctx.Err()
	}
	return res
}
<|endoftext|>


<commit_before>// Package splitmysqldump was copied from
// github.com/ripienaar/mysql-dump-split
// but it's in ruby and I don't like ruby
// plus it's a pain to run the ruby command
// and get a nice list of files parsed so I
// can run it in multiple goroutines
package splitmysqldump

import (
	"bufio"
	"bytes"
	"os"
	"regexp"
	"strings"
)

// ParsedFiles contains all the
// files that were created when
// running the Run function
type ParsedFiles struct {
	Files []string
}

// NewParsedFiles handles the work of
// creating a ParsedFiles struct
func NewParsedFiles() *ParsedFiles {
	return &ParsedFiles{Files: make([]string, 0)}
}

// Run is the function that is used
// to bind all of the other functions
// together
func Run(file string) *ParsedFiles {
	return findTables(openFile(file))
}

// openFile takes a string and trys to open
// it based on the current directory. If the
// current file is not availabel for opening
// panic since we can't do anything with the file
func openFile(file string) *os.File {
	f, err := os.Open(file)
	if err != nil {
		panic(err)
	}

	return f
}

// This returns all the regexes we need
func compileRegex() *regexp.Regexp {
	var regexSlice *regexp.Regexp

	regexSlice = regexp.MustCompile("^-- Table structure for table .(.+).")

	return regexSlice
}

// bufio.ReadLine does not gurantee that
// the entire line is returned to ensure
// that we do not break anything when parsing
// the file this ensures entire lines are
// returned when it is called
func getFullLine(r *bufio.Reader) []byte {
	var isPrefix = true
	var err error
	var line []byte
	var buffer bytes.Buffer

	for isPrefix && err == nil {
		line, isPrefix, err = r.ReadLine()
		buffer.Write(line)
	}

	return buffer.Bytes()
}

func makeNewFile(file string) *os.File {
	f, err := os.Create(file)
	if err != nil {
		panic(err)
	}

	return f
}

// Parse the regex above and return only the
// name of the table that we are parsing
func getTableName(name []byte) string {
	sections := strings.Split(string(name), "`")
	return sections[1]
}

// findTables parses the passed in file
func findTables(file *os.File) *ParsedFiles {
	var tableFile *os.File
	createdFiles := NewParsedFiles()
	inTable := false

	regexSlice := compileRegex()

	reader := bufio.NewReader(file)
	// Buffered makes sure we have more bytes
	// that can be read.
	for _, err := reader.Peek(1); err == nil; {
		line := getFullLine(reader)

		if x := regexSlice.Find(line); x != nil {
			if inTable {
				tableFile.Close()
			}
			tableName := getTableName(x)
			createdFiles.Files = append(createdFiles.Files, tableName)
			tableFile = makeNewFile(tableName)
			inTable = true
		}

		tableFile.Write(append(line, []byte("\n")...))

		_, err = reader.Peek(1)
	}

	return createdFiles
}
<commit_msg>Remove regex<commit_after>// Package splitmysqldump was copied from
// github.com/ripienaar/mysql-dump-split
// but it's in ruby and I don't like ruby
// plus it's a pain to run the ruby command
// and get a nice list of files parsed so I
// can run it in multiple goroutines
package splitmysqldump

import (
	"bufio"
	"bytes"
	"os"
	"strings"
)

// ParsedFiles contains all the
// files that were created when
// running the Run function
type ParsedFiles struct {
	Files []string
}

// NewParsedFiles handles the work of
// creating a ParsedFiles struct
func NewParsedFiles() *ParsedFiles {
	return &ParsedFiles{Files: make([]string, 0)}
}

// Run is the function that is used
// to bind all of the other functions
// together
func Run(file string) *ParsedFiles {
	return findTables(openFile(file))
}

// openFile takes a string and trys to open
// it based on the current directory. If the
// current file is not availabel for opening
// panic since we can't do anything with the file
func openFile(file string) *os.File {
	f, err := os.Open(file)
	if err != nil {
		panic(err)
	}

	return f
}

// bufio.ReadLine does not gurantee that
// the entire line is returned to ensure
// that we do not break anything when parsing
// the file this ensures entire lines are
// returned when it is called
func getFullLine(r *bufio.Reader) []byte {
	var isPrefix = true
	var err error
	var line []byte
	var buffer bytes.Buffer

	for isPrefix && err == nil {
		line, isPrefix, err = r.ReadLine()
		buffer.Write(line)
	}

	return buffer.Bytes()
}

func makeNewFile(file string) *os.File {
	f, err := os.Create(file)
	if err != nil {
		panic(err)
	}

	return f
}

// Parse the regex above and return only the
// name of the table that we are parsing
func getTableName(name []byte) string {
	sections := strings.Split(string(name), "`")
	return sections[1]
}

// findTables parses the passed in file
func findTables(file *os.File) *ParsedFiles {
	var tableFile *os.File
	var buffer bytes.Buffer
	createdFiles := NewParsedFiles()
	inTable := false

	reader := bufio.NewReader(file)
	// Buffered makes sure we have more bytes
	// that can be read.
	for _, err := reader.Peek(1); err == nil; {
		line := getFullLine(reader)

		if contains := bytes.Contains(line, []byte("-- Table structure for table")); contains {
			if inTable {
				tableFile.Close()
			}
			tableName := getTableName(line)
			createdFiles.Files = append(createdFiles.Files, tableName)
			tableFile = makeNewFile(tableName)
			inTable = true
		}

		buffer.Write(line)
		buffer.Write([]byte("\n"))

		tableFile.Write(buffer.Bytes())
		buffer.Reset()

		_, err = reader.Peek(1)
	}

	return createdFiles
}
<|endoftext|>


<commit_before>package google

func canonicalizeServiceScope(scope string) string {
	// This is a convenience map of short names used by the gcloud tool
	// to the GCE auth endpoints they alias to.
	scopeMap := map[string]string{
		"bigquery":              "https://www.googleapis.com/auth/bigquery",
		"cloud-platform":        "https://www.googleapis.com/auth/cloud-platform",
		"cloud-source-repos":    "https://www.googleapis.com/auth/source.full_control",
		"cloud-source-repos-ro": "https://www.googleapis.com/auth/source.read_only",
		"compute-ro":            "https://www.googleapis.com/auth/compute.readonly",
		"compute-rw":            "https://www.googleapis.com/auth/compute",
		"datastore":             "https://www.googleapis.com/auth/datastore",
		"logging-write":         "https://www.googleapis.com/auth/logging.write",
		"monitoring":            "https://www.googleapis.com/auth/monitoring",
		"monitoring-write":      "https://www.googleapis.com/auth/monitoring.write",
		"pubsub":                "https://www.googleapis.com/auth/pubsub",
		"service-control":       "https://www.googleapis.com/auth/servicecontrol",
		"service-management":    "https://www.googleapis.com/auth/service.management.readonly",
		"sql":                   "https://www.googleapis.com/auth/sqlservice",
		"sql-admin":             "https://www.googleapis.com/auth/sqlservice.admin",
		"storage-full":          "https://www.googleapis.com/auth/devstorage.full_control",
		"storage-ro":            "https://www.googleapis.com/auth/devstorage.read_only",
		"storage-rw":            "https://www.googleapis.com/auth/devstorage.read_write",
		"taskqueue":             "https://www.googleapis.com/auth/taskqueue",
		"useraccounts-ro":       "https://www.googleapis.com/auth/cloud.useraccounts.readonly",
		"useraccounts-rw":       "https://www.googleapis.com/auth/cloud.useraccounts",
		"userinfo-email":        "https://www.googleapis.com/auth/userinfo.email",
	}

	if matchedURL, ok := scopeMap[scope]; ok {
		return matchedURL
	}

	return scope
}
<commit_msg>Missing short name in the service scope (Google compute instance) (#14633)<commit_after>package google

func canonicalizeServiceScope(scope string) string {
	// This is a convenience map of short names used by the gcloud tool
	// to the GCE auth endpoints they alias to.
	scopeMap := map[string]string{
		"bigquery":              "https://www.googleapis.com/auth/bigquery",
		"cloud-platform":        "https://www.googleapis.com/auth/cloud-platform",
		"cloud-source-repos":    "https://www.googleapis.com/auth/source.full_control",
		"cloud-source-repos-ro": "https://www.googleapis.com/auth/source.read_only",
		"compute-ro":            "https://www.googleapis.com/auth/compute.readonly",
		"compute-rw":            "https://www.googleapis.com/auth/compute",
		"datastore":             "https://www.googleapis.com/auth/datastore",
		"logging-write":         "https://www.googleapis.com/auth/logging.write",
		"monitoring":            "https://www.googleapis.com/auth/monitoring",
		"monitoring-write":      "https://www.googleapis.com/auth/monitoring.write",
		"pubsub":                "https://www.googleapis.com/auth/pubsub",
		"service-control":       "https://www.googleapis.com/auth/servicecontrol",
		"service-management":    "https://www.googleapis.com/auth/service.management.readonly",
		"sql":                   "https://www.googleapis.com/auth/sqlservice",
		"sql-admin":             "https://www.googleapis.com/auth/sqlservice.admin",
		"storage-full":          "https://www.googleapis.com/auth/devstorage.full_control",
		"storage-ro":            "https://www.googleapis.com/auth/devstorage.read_only",
		"storage-rw":            "https://www.googleapis.com/auth/devstorage.read_write",
		"taskqueue":             "https://www.googleapis.com/auth/taskqueue",
		"trace-append":          "https://www.googleapis.com/auth/trace.append",
		"trace-ro":              "https://www.googleapis.com/auth/trace.readonly",
		"useraccounts-ro":       "https://www.googleapis.com/auth/cloud.useraccounts.readonly",
		"useraccounts-rw":       "https://www.googleapis.com/auth/cloud.useraccounts",
		"userinfo-email":        "https://www.googleapis.com/auth/userinfo.email",
	}

	if matchedURL, ok := scopeMap[scope]; ok {
		return matchedURL
	}

	return scope
}
<|endoftext|>


<commit_before>package main

// The part of the BAPS3 connector code responsible for updating the
// internal state.

import (
	"fmt"
	"strconv"
	"time"

	"github.com/UniversityRadioYork/baps3-go"
)

// serviceState is the struct of all known state for a BAPS3 service.
// TODO(CaptainHayashi): possibly segregate more by feature, so elements not
// relevant to the current feature set aren't allocated?
type serviceState struct {
	// Core
	features map[baps3.Feature]struct{}
	state    string

	// TimeReport
	time time.Duration

	// FileLoad
	file string
}

// initServiceState creates a new, blank, serviceState.
func initServiceState() (s *serviceState) {
	s = new(serviceState)
	s.features = make(map[baps3.Feature]struct{})

	return
}

// update updates a serviceState according to an incoming service response.
func (s *serviceState) update(res baps3.Message) (err error) {
	switch res.Word() {
	case baps3.RsFeatures:
		err = s.updateFeaturesFromMessage(res)
	case baps3.RsFile:
		err = s.updateFileFromMessage(res)
	case baps3.RsState:
		err = s.updateStateFromMessage(res)
	case baps3.RsTime:
		err = s.updateTimeFromMessage(res)
	}

	return
}

func (s *serviceState) updateFeaturesFromMessage(res baps3.Message) (err error) {
	feats := make(map[baps3.Feature]struct{})

	for i := 0; ; i++ {
		if fstring, e := res.Arg(i); e == nil {
			feat := baps3.LookupFeature(fstring)
			if feat == baps3.FtUnknown {
				err = fmt.Errorf("unknown feature: %q", fstring)
				break
			}
			feats[feat] = struct{}{}
		} else {
			// e != nil means we've run out of arguments.
			break
		}
	}

	s.features = feats
	return
}

func (s *serviceState) updateFileFromMessage(res baps3.Message) (err error) {
	// Expecting only one argument
	if _, err := res.Arg(1); err == nil {
		return fmt.Errorf("too many arguments in %q", res)
	}

	file, err := res.Arg(0)
	if err != nil {
		s.file = ""
		return
	}

	s.file = file

	return
}

func (s *serviceState) updateStateFromMessage(res baps3.Message) (err error) {
	// Expecting only one argument
	if _, err := res.Arg(1); err == nil {
		return fmt.Errorf("too many arguments in %q", res)
	}

	state, err := res.Arg(0)
	if err != nil {
		// TODO(CaptainHayashi): "Ready" is currently the most
		// valid fallback value in the spec.  Does the spec
		// need an 'I don't know what the state is' value?
		s.state = "Ready"
		return
	}

	s.state = state

	return
}

func (s *serviceState) updateTimeFromMessage(res baps3.Message) (err error) {
	// Expecting only one argument
	if _, err := res.Arg(1); err == nil {
		return fmt.Errorf("too many arguments in %q", res)
	}

	usecs, err := res.Arg(0)
	if err != nil {
		return
	}

	usec, err := strconv.Atoi(usecs)
	if err != nil {
		return
	}

	s.time = time.Duration(usec) * time.Microsecond

	return
}

// hasFeature returns whether the connected server advertises the given feature.
func (s *serviceState) hasFeature(f baps3.Feature) bool {
	_, ok := s.features[f]
	return ok
}
<commit_msg>Set initial state to Ready, for now.<commit_after>package main

// The part of the BAPS3 connector code responsible for updating the
// internal state.

import (
	"fmt"
	"strconv"
	"time"

	"github.com/UniversityRadioYork/baps3-go"
)

// serviceState is the struct of all known state for a BAPS3 service.
// TODO(CaptainHayashi): possibly segregate more by feature, so elements not
// relevant to the current feature set aren't allocated?
type serviceState struct {
	// Core
	features map[baps3.Feature]struct{}
	state    string

	// TimeReport
	time time.Duration

	// FileLoad
	file string
}

// initServiceState creates a new, blank, serviceState.
func initServiceState() (s *serviceState) {
	s = new(serviceState)
	s.features = make(map[baps3.Feature]struct{})
	s.state = "Ready"

	return
}

// update updates a serviceState according to an incoming service response.
func (s *serviceState) update(res baps3.Message) (err error) {
	switch res.Word() {
	case baps3.RsFeatures:
		err = s.updateFeaturesFromMessage(res)
	case baps3.RsFile:
		err = s.updateFileFromMessage(res)
	case baps3.RsState:
		err = s.updateStateFromMessage(res)
	case baps3.RsTime:
		err = s.updateTimeFromMessage(res)
	}

	return
}

func (s *serviceState) updateFeaturesFromMessage(res baps3.Message) (err error) {
	feats := make(map[baps3.Feature]struct{})

	for i := 0; ; i++ {
		if fstring, e := res.Arg(i); e == nil {
			feat := baps3.LookupFeature(fstring)
			if feat == baps3.FtUnknown {
				err = fmt.Errorf("unknown feature: %q", fstring)
				break
			}
			feats[feat] = struct{}{}
		} else {
			// e != nil means we've run out of arguments.
			break
		}
	}

	s.features = feats
	return
}

func (s *serviceState) updateFileFromMessage(res baps3.Message) (err error) {
	// Expecting only one argument
	if _, err := res.Arg(1); err == nil {
		return fmt.Errorf("too many arguments in %q", res)
	}

	file, err := res.Arg(0)
	if err != nil {
		s.file = ""
		return
	}

	s.file = file

	return
}

func (s *serviceState) updateStateFromMessage(res baps3.Message) (err error) {
	// Expecting only one argument
	if _, err := res.Arg(1); err == nil {
		return fmt.Errorf("too many arguments in %q", res)
	}

	state, err := res.Arg(0)
	if err != nil {
		// TODO(CaptainHayashi): "Ready" is currently the most
		// valid fallback value in the spec.  Does the spec
		// need an 'I don't know what the state is' value?
		s.state = "Ready"
		return
	}

	s.state = state

	return
}

func (s *serviceState) updateTimeFromMessage(res baps3.Message) (err error) {
	// Expecting only one argument
	if _, err := res.Arg(1); err == nil {
		return fmt.Errorf("too many arguments in %q", res)
	}

	usecs, err := res.Arg(0)
	if err != nil {
		return
	}

	usec, err := strconv.Atoi(usecs)
	if err != nil {
		return
	}

	s.time = time.Duration(usec) * time.Microsecond

	return
}

// hasFeature returns whether the connected server advertises the given feature.
func (s *serviceState) hasFeature(f baps3.Feature) bool {
	_, ok := s.features[f]
	return ok
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/kataras/iris"
)

// default memory provider
func main() {

	iris.Get("/set", func(c *iris.Context) {

		//set session values
		c.Session().Set("name", "iris")

		//test if setted here
		c.Write("All ok session setted to: %s", c.Session().GetString("name"))
	})

	iris.Get("/get", func(c *iris.Context) {
		name := c.Session().GetString("name")

		c.Write("The name on the /set was: %s", name)
	})

	iris.Get("/delete", func(c *iris.Context) {
		//get the session for this context

		c.Session().Delete("name")

	})

	iris.Get("/clear", func(c *iris.Context) {

		// removes all entries
		c.Session().Clear()
	})

	iris.Get("/destroy", func(c *iris.Context) {
		//destroy, removes the entire session and cookie
		c.SessionDestroy()
	})

	iris.Listen(":8080")
}
<commit_msg>update sessions example<commit_after>package main

import (
	"github.com/kataras/iris"
)

// default memory provider
func main() {

	iris.Get("/set", func(c *iris.Context) {

		//set session values
		c.Session().Set("name", "iris")

		//test if setted here
		c.Write("All ok session setted to: %s", c.Session().GetString("name"))
	})

	iris.Get("/get", func(c *iris.Context) {
		name := c.Session().GetString("name")

		c.Write("The name on the /set was: %s", name)
	})

	iris.Get("/delete", func(c *iris.Context) {
		//get the session for this context

		c.Session().Delete("name")

	})

	iris.Get("/clear", func(c *iris.Context) {

		// removes all entries
		c.Session().Clear()
	})

	iris.Get("/destroy", func(c *iris.Context) {
		//destroy, removes the entire session and cookie
		c.SessionDestroy()
		c.Log("You have to refresh the page to completely remove the session (on browsers), so the name should NOT be empty NOW, is it?\n Name: %s", c.Session().GetString("name"))
		c.Redirect("/destroyed")
	})

	iris.Get("/destroyed", func(c *iris.Context) {
		c.Write("The name should be empty, is it?\n Name: %s", c.Session().GetString("name"))
	})

	iris.Listen(":8080")
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"github.com/golang/glog"
	"github.com/signalfuse/signalfxproxy/config"
	"github.com/signalfuse/signalfxproxy/core"
	"github.com/signalfuse/signalfxproxy/forwarder"
	"github.com/signalfuse/signalfxproxy/listener"
)

func main() {
	configFileName := flag.String("configfile", "sf/sfdbproxy.conf", "Name of the db proxy configuration file.")
	flag.Parse()

	glog.Infof("Looking for config file %s\n", *configFileName)

	loadedConfig, err := config.LoadConfig(*configFileName)
	if err != nil {
		glog.Fatalln("Unable to load config: ", err)
		return
	}

	glog.Infof("Config is %s\n", loadedConfig)
	allForwarders := []core.DatapointStreamingAPI{}
	allStatKeepers := []core.StatKeeper{}
	for _, forwardConfig := range loadedConfig.ForwardTo {
		loader, ok := forwarder.AllForwarderLoaders[forwardConfig.Type]
		if !ok {
			glog.Fatalf("Unknown loader type: %s", forwardConfig.Type)
			return
		}
		forwarder, err := loader(forwardConfig)
		if err != nil {
			glog.Fatalf("Unable to loader forwarder: %s", err)
			return
		}
		allForwarders = append(allForwarders, forwarder)
		allStatKeepers = append(allStatKeepers, forwarder)
	}
	multiplexer, err := forwarder.NewStreamingDatapointDemultiplexer(allForwarders)
	if err != nil {
		glog.Fatalf("Unable to loader multiplexer: %s", err)
		return
	}
	allStatKeepers = append(allStatKeepers, multiplexer)

	allListeners := make([]listener.DatapointListener, len(loadedConfig.ListenFrom))
	for _, forwardConfig := range loadedConfig.ListenFrom {
		loader, ok := listener.AllListenerLoaders[forwardConfig.Type]
		if !ok {
			glog.Fatalf("Unknown loader type: %s", forwardConfig.Type)
			return
		}
		listener, err := loader(multiplexer, forwardConfig)
		if err != nil {
			glog.Fatalf("Unable to loader listener: %s", err)
			return
		}
		if listener == nil {
			glog.Fatalf("Got nil listener from %s", forwardConfig.Type)
		}
		allListeners = append(allListeners, listener)
		allStatKeepers = append(allStatKeepers, listener)
	}

	glog.Infof("Setup done.  Blocking!\n")
	if loadedConfig.StatsDelayDuration != nil {
		//		go core.DrainStatsThread(*loadedConfig.StatsDelayDuration, allForwarders, allStatKeepers)
	}

	// TODO: Replace with something more graceful that allows us to shutdown?
	select {}
}
<commit_msg>Include stat tracking<commit_after>package main

import (
	"flag"
	"github.com/golang/glog"
	"github.com/signalfuse/signalfxproxy/config"
	"github.com/signalfuse/signalfxproxy/core"
	"github.com/signalfuse/signalfxproxy/forwarder"
	"github.com/signalfuse/signalfxproxy/listener"
)

func main() {
	configFileName := flag.String("configfile", "sf/sfdbproxy.conf", "Name of the db proxy configuration file.")
	flag.Parse()

	glog.Infof("Looking for config file %s\n", *configFileName)

	loadedConfig, err := config.LoadConfig(*configFileName)
	if err != nil {
		glog.Fatalln("Unable to load config: ", err)
		return
	}

	glog.Infof("Config is %s\n", loadedConfig)
	allForwarders := []core.DatapointStreamingAPI{}
	allStatKeepers := []core.StatKeeper{}
	for _, forwardConfig := range loadedConfig.ForwardTo {
		loader, ok := forwarder.AllForwarderLoaders[forwardConfig.Type]
		if !ok {
			glog.Fatalf("Unknown loader type: %s", forwardConfig.Type)
			return
		}
		forwarder, err := loader(forwardConfig)
		if err != nil {
			glog.Fatalf("Unable to loader forwarder: %s", err)
			return
		}
		allForwarders = append(allForwarders, forwarder)
		allStatKeepers = append(allStatKeepers, forwarder)
	}
	multiplexer, err := forwarder.NewStreamingDatapointDemultiplexer(allForwarders)
	if err != nil {
		glog.Fatalf("Unable to loader multiplexer: %s", err)
		return
	}
	allStatKeepers = append(allStatKeepers, multiplexer)

	allListeners := make([]listener.DatapointListener, len(loadedConfig.ListenFrom))
	for _, forwardConfig := range loadedConfig.ListenFrom {
		loader, ok := listener.AllListenerLoaders[forwardConfig.Type]
		if !ok {
			glog.Fatalf("Unknown loader type: %s", forwardConfig.Type)
			return
		}
		listener, err := loader(multiplexer, forwardConfig)
		if err != nil {
			glog.Fatalf("Unable to loader listener: %s", err)
			return
		}
		if listener == nil {
			glog.Fatalf("Got nil listener from %s", forwardConfig.Type)
		}
		allListeners = append(allListeners, listener)
		allStatKeepers = append(allStatKeepers, listener)
	}

	glog.Infof("Setup done.  Blocking!\n")
	stopChannel := make(chan bool, 2)
	if loadedConfig.StatsDelayDuration != nil {
		go core.DrainStatsThread(*loadedConfig.StatsDelayDuration, allForwarders, allStatKeepers, stopChannel)
	}

	// TODO: Replace with something more graceful that allows us to shutdown?
	select {}
}
<|endoftext|>