<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.

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

package tchannel

import (
	"errors"
	"fmt"
	"net"
	"os"
	"path/filepath"
	"sync"
	"time"

	"golang.org/x/net/context"
)

var (
	errAlreadyListening  = errors.New("channel already listening")
	errInvalidStateForOp = errors.New("channel is in an invalid state for that operation")
)

const (
	ephemeralHostPort = "0.0.0.0:0"
)

// TraceReporterFactory is the interface of the method to generate TraceReporter instance.
type TraceReporterFactory func(*Channel) TraceReporter

// ChannelOptions are used to control parameters on a create a TChannel
type ChannelOptions struct {
	// Default Connection options
	DefaultConnectionOptions ConnectionOptions

	// The name of the process, for logging and reporting to peers
	ProcessName string

	// The logger to use for this channel
	Logger Logger

	// The reporter to use for reporting stats for this channel.
	StatsReporter StatsReporter

	// Trace reporter to use for this channel.
	TraceReporter TraceReporter

	// Trace reporter factory to generate trace reporter instance.
	TraceReporterFactory TraceReporterFactory
}

// ChannelState is the state of a channel.
type ChannelState int

const (
	// ChannelClient is a channel that can be used as a client.
	ChannelClient ChannelState = iota + 1

	// ChannelListening is a channel that is listening for new connnections.
	ChannelListening

	// ChannelStartClose is a channel that has received a Close request.
	// The channel is no longer listening, and all new incoming connections are rejected.
	ChannelStartClose

	// ChannelInboundClosed is a channel that has drained all incoming connections, but may
	// have outgoing connections. All incoming calls and new outgoing calls are rejected.
	ChannelInboundClosed

	// ChannelClosed is a channel that has closed completely.
	ChannelClosed
)

//go:generate stringer -type=ChannelState

// A Channel is a bi-directional connection to the peering and routing network.
// Applications can use a Channel to make service calls to remote peers via
// BeginCall, or to listen for incoming calls from peers.  Applications that
// want to receive requests should call one of Serve or ListenAndServe
// TODO(prashant): Shutdown all subchannels + peers when channel is closed.
type Channel struct {
	log                  Logger
	commonStatsTags      map[string]string
	statsReporter        StatsReporter
	traceReporter        TraceReporter
	traceReporterFactory TraceReporterFactory
	connectionOptions    ConnectionOptions
	handlers             *handlerMap
	peers                *PeerList
	subChannels          *subChannelMap

	// mutable contains all the members of Channel which are mutable.
	mutable struct {
		mut      sync.RWMutex // protects members of the mutable struct.
		state    ChannelState
		peerInfo LocalPeerInfo // May be ephemeral if this is a client only channel
		l        net.Listener  // May be nil if this is a client only channel
		conns    []*Connection
	}
}

// NewChannel creates a new Channel.  The new channel can be used to send outbound requests
// to peers, but will not listen or handling incoming requests until one of ListenAndServe
// or Serve is called. The local service name should be passed to serviceName.
func NewChannel(serviceName string, opts *ChannelOptions) (*Channel, error) {
	if opts == nil {
		opts = &ChannelOptions{}
	}

	logger := opts.Logger
	if logger == nil {
		logger = NullLogger
	}

	processName := opts.ProcessName
	if processName == "" {
		processName = fmt.Sprintf("%s[%d]", filepath.Base(os.Args[0]), os.Getpid())
	}

	statsReporter := opts.StatsReporter
	if statsReporter == nil {
		statsReporter = NullStatsReporter
	}

	ch := &Channel{
		connectionOptions: opts.DefaultConnectionOptions,
		log:               logger.WithFields(LogField{"service", serviceName}),
		statsReporter:     statsReporter,
		handlers:          &handlerMap{},
		subChannels:       &subChannelMap{},
	}

	traceReporter := opts.TraceReporter
	if opts.TraceReporterFactory != nil {
		traceReporter = opts.TraceReporterFactory(ch)
	}
	if traceReporter == nil {
		traceReporter = NullReporter
	}
	ch.traceReporter = traceReporter

	ch.mutable.peerInfo = LocalPeerInfo{
		PeerInfo: PeerInfo{
			ProcessName: processName,
			HostPort:    ephemeralHostPort,
		},
		ServiceName: serviceName,
	}
	ch.mutable.state = ChannelClient
	ch.peers = newPeerList(ch)
	ch.createCommonStats()
	return ch, nil
}

// Serve serves incoming requests using the provided listener.
// The local peer info is set synchronously, but the actual socket listening is done in
// a separate goroutine.
func (ch *Channel) Serve(l net.Listener) error {
	mutable := &ch.mutable
	mutable.mut.Lock()
	defer mutable.mut.Unlock()

	if mutable.l != nil {
		return errAlreadyListening
	}
	mutable.l = l

	if mutable.state != ChannelClient {
		return errInvalidStateForOp
	}
	mutable.state = ChannelListening

	mutable.peerInfo.HostPort = l.Addr().String()
	peerInfo := mutable.peerInfo
	ch.log.Debugf("%v (%v) listening on %v", peerInfo.ProcessName, peerInfo.ServiceName, peerInfo.HostPort)
	go ch.serve()
	return nil
}

// ListenAndServe listens on the given address and serves incoming requests.
// The port may be 0, in which case the channel will use an OS assigned port
// This method does not block as the handling of connections is done in a goroutine.
func (ch *Channel) ListenAndServe(hostPort string) error {
	mutable := &ch.mutable
	mutable.mut.RLock()

	if mutable.l != nil {
		mutable.mut.RUnlock()
		return errAlreadyListening
	}

	l, err := net.Listen("tcp", hostPort)
	if err != nil {
		mutable.mut.RUnlock()
		return err
	}

	mutable.mut.RUnlock()
	return ch.Serve(l)
}

// Registrar is the base interface for registering handlers on either the base
// Channel or the SubChannel
type Registrar interface {
	// ServiceName returns the service name that this Registrar is for.
	ServiceName() string

	// Register registers a handler for ServiceName and the given operation.
	Register(h Handler, operationName string)

	// Logger returns the logger for this Registrar.
	Logger() Logger

	// Peers returns the peer list for this Registrar.
	Peers() *PeerList
}

// Register registers a handler for a service+operation pair
func (ch *Channel) Register(h Handler, operationName string) {
	ch.handlers.register(h, ch.PeerInfo().ServiceName, operationName)
}

// PeerInfo returns the current peer info for the channel
func (ch *Channel) PeerInfo() LocalPeerInfo {
	ch.mutable.mut.RLock()
	defer ch.mutable.mut.RUnlock()

	return ch.mutable.peerInfo
}

func (ch *Channel) createCommonStats() {
	ch.commonStatsTags = map[string]string{
		"app":     ch.mutable.peerInfo.ProcessName,
		"service": ch.mutable.peerInfo.ServiceName,
	}
	host, err := os.Hostname()
	if err != nil {
		ch.log.Infof("channel failed to get host: %v", err)
		return
	}
	ch.commonStatsTags["host"] = host
	// TODO(prashant): Allow user to pass extra tags (such as cluster, version).
}

// GetSubChannel returns a SubChannel for the given service name. If the subchannel does not
// exist, it is created.
func (ch *Channel) GetSubChannel(serviceName string) *SubChannel {
	return ch.subChannels.getOrAdd(serviceName, ch)
}

// Peers returns the PeerList for the channel.
func (ch *Channel) Peers() *PeerList {
	return ch.peers
}

// BeginCall starts a new call to a remote peer, returning an OutboundCall that can
// be used to write the arguments of the call.
func (ch *Channel) BeginCall(ctx context.Context, hostPort, serviceName, operationName string, callOptions *CallOptions) (*OutboundCall, error) {
	p := ch.peers.GetOrAdd(hostPort)
	return p.BeginCall(ctx, serviceName, operationName, callOptions)
}

// serve runs the listener to accept and manage new incoming connections, blocking
// until the channel is closed.
func (ch *Channel) serve() {
	acceptBackoff := 0 * time.Millisecond

	for {
		netConn, err := ch.mutable.l.Accept()
		if err != nil {
			// Backoff from new accepts if this is a temporary error
			if ne, ok := err.(net.Error); ok && ne.Temporary() {
				if acceptBackoff == 0 {
					acceptBackoff = 5 * time.Millisecond
				} else {
					acceptBackoff *= 2
				}
				if max := 1 * time.Second; acceptBackoff > max {
					acceptBackoff = max
				}
				ch.log.Warnf("accept error: %v; retrying in %v", err, acceptBackoff)
				time.Sleep(acceptBackoff)
				continue
			} else {
				// Only log an error if this didn't happen due to a Close.
				if ch.State() >= ChannelStartClose {
					return
				}
				ch.log.Fatalf("unrecoverable accept error: %v; closing server", err)
				return
			}
		}

		acceptBackoff = 0

		// Register the connection in the peer once the channel is set up.
		events := connectionEvents{
			OnActive:           ch.incomingConnectionActive,
			OnCloseStateChange: ch.connectionCloseStateChange,
		}
		if _, err := ch.newInboundConnection(netConn, events, &ch.connectionOptions); err != nil {
			// Server is getting overloaded - begin rejecting new connections
			ch.log.Errorf("could not create new TChannelConnection for incoming conn: %v", err)
			netConn.Close()
			continue
		}
	}
}

// Ping sends a ping message to the given hostPort and waits for a response.
func (ch *Channel) Ping(ctx context.Context, hostPort string) error {
	peer := ch.Peers().GetOrAdd(hostPort)
	conn, err := peer.GetConnection(ctx)
	if err != nil {
		return err
	}

	return conn.ping(ctx)
}

// Logger returns the logger for this channel.
func (ch *Channel) Logger() Logger {
	return ch.log
}

// ServiceName returns the serviceName that this channel was created for.
func (ch *Channel) ServiceName() string {
	return ch.PeerInfo().ServiceName
}

// Connect connects the channel.
func (ch *Channel) Connect(ctx context.Context, hostPort string, connectionOptions *ConnectionOptions) (*Connection, error) {
	switch state := ch.State(); state {
	case ChannelClient, ChannelListening:
		break
	case ChannelStartClose:
		// We still allow outgoing connections during Close, but the connection has to immediately
		// be Closed after opening
	default:
		ch.log.Debugf("Connect rejecting new connection as state is %v", state)
		return nil, errInvalidStateForOp
	}

	events := connectionEvents{OnCloseStateChange: ch.connectionCloseStateChange}
	c, err := ch.newOutboundConnection(hostPort, events, connectionOptions)
	if err != nil {
		return nil, err
	}

	if err := c.sendInit(ctx); err != nil {
		return nil, err
	}

	ch.mutable.mut.Lock()
	ch.mutable.conns = append(ch.mutable.conns, c)
	chState := ch.mutable.state
	ch.mutable.mut.Unlock()

	// Any connections added after the channel is in StartClose should also be set to start close.
	if chState == ChannelStartClose {
		// TODO(prashant): If Connect is called, but no outgoing calls are made, then this connection
		// will block Close, as it will never get cleaned up.
		c.withStateLock(func() error {
			c.state = connectionStartClose
			return nil
		})
		c.log.Debugf("Channel is in start close, set connection to start close")
	}

	return c, err
}

// incomingConnectionActive adds a new active connection to our peer list.
func (ch *Channel) incomingConnectionActive(c *Connection) {
	c.log.Debugf("Add connection as an active peer for %v", c.remotePeerInfo.HostPort)
	p := ch.peers.GetOrAdd(c.remotePeerInfo.HostPort)
	p.AddConnection(c)

	ch.mutable.mut.Lock()
	ch.mutable.conns = append(ch.mutable.conns, c)
	ch.mutable.mut.Unlock()
}

// connectionCloseStateChange is called when a connection's close state changes.
func (ch *Channel) connectionCloseStateChange(c *Connection) {
	switch chState := ch.State(); chState {
	case ChannelStartClose, ChannelInboundClosed:
		ch.mutable.mut.RLock()
		minState := connectionClosed
		for _, c := range ch.mutable.conns {
			if s := c.readState(); s < minState {
				minState = s
			}
		}
		ch.mutable.mut.RUnlock()

		var updateTo ChannelState
		if minState >= connectionClosed {
			updateTo = ChannelClosed
		} else if minState >= connectionInboundClosed && chState == ChannelStartClose {
			updateTo = ChannelInboundClosed
		}

		if updateTo > 0 {
			ch.mutable.mut.Lock()
			ch.mutable.state = updateTo
			ch.mutable.mut.Unlock()
			chState = updateTo
		}

		c.log.Debugf("ConnectionCloseStateChange channel state = %v connection minState = %v",
			chState, minState)
	}
}

// Closed returns whether this channel has been closed with .Close()
func (ch *Channel) Closed() bool {
	return ch.State() == ChannelClosed
}

// State returns the current channel state.
func (ch *Channel) State() ChannelState {
	ch.mutable.mut.RLock()
	state := ch.mutable.state
	ch.mutable.mut.RUnlock()

	return state
}

// Close starts a graceful Close for the channel. This does not happen immediately:
// 1. This call closes the Listener and starts closing connections.
// 2. When all incoming connections are drainged, the connection blocks new outgoing calls.
// 3. When all connections are drainged, the channel's state is updated to Closed.
func (ch *Channel) Close() {
	ch.mutable.mut.Lock()

	if ch.mutable.l != nil {
		ch.mutable.l.Close()
	}

	ch.mutable.state = ChannelStartClose
	if len(ch.mutable.conns) == 0 {
		ch.mutable.state = ChannelClosed
	}
	ch.mutable.mut.Unlock()

	ch.peers.Close()
}
<commit_msg>Return error when no service name provided<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.

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

package tchannel

import (
	"errors"
	"fmt"
	"net"
	"os"
	"path/filepath"
	"sync"
	"time"

	"golang.org/x/net/context"
)

var (
	errAlreadyListening  = errors.New("channel already listening")
	errInvalidStateForOp = errors.New("channel is in an invalid state for that operation")

	// ErrNoServiceName is returned when no service name is provided when
	// creating a new channel.
	ErrNoServiceName = errors.New("no service name provided")
)

const (
	ephemeralHostPort = "0.0.0.0:0"
)

// TraceReporterFactory is the interface of the method to generate TraceReporter instance.
type TraceReporterFactory func(*Channel) TraceReporter

// ChannelOptions are used to control parameters on a create a TChannel
type ChannelOptions struct {
	// Default Connection options
	DefaultConnectionOptions ConnectionOptions

	// The name of the process, for logging and reporting to peers
	ProcessName string

	// The logger to use for this channel
	Logger Logger

	// The reporter to use for reporting stats for this channel.
	StatsReporter StatsReporter

	// Trace reporter to use for this channel.
	TraceReporter TraceReporter

	// Trace reporter factory to generate trace reporter instance.
	TraceReporterFactory TraceReporterFactory
}

// ChannelState is the state of a channel.
type ChannelState int

const (
	// ChannelClient is a channel that can be used as a client.
	ChannelClient ChannelState = iota + 1

	// ChannelListening is a channel that is listening for new connnections.
	ChannelListening

	// ChannelStartClose is a channel that has received a Close request.
	// The channel is no longer listening, and all new incoming connections are rejected.
	ChannelStartClose

	// ChannelInboundClosed is a channel that has drained all incoming connections, but may
	// have outgoing connections. All incoming calls and new outgoing calls are rejected.
	ChannelInboundClosed

	// ChannelClosed is a channel that has closed completely.
	ChannelClosed
)

//go:generate stringer -type=ChannelState

// A Channel is a bi-directional connection to the peering and routing network.
// Applications can use a Channel to make service calls to remote peers via
// BeginCall, or to listen for incoming calls from peers.  Applications that
// want to receive requests should call one of Serve or ListenAndServe
// TODO(prashant): Shutdown all subchannels + peers when channel is closed.
type Channel struct {
	log                  Logger
	commonStatsTags      map[string]string
	statsReporter        StatsReporter
	traceReporter        TraceReporter
	traceReporterFactory TraceReporterFactory
	connectionOptions    ConnectionOptions
	handlers             *handlerMap
	peers                *PeerList
	subChannels          *subChannelMap

	// mutable contains all the members of Channel which are mutable.
	mutable struct {
		mut      sync.RWMutex // protects members of the mutable struct.
		state    ChannelState
		peerInfo LocalPeerInfo // May be ephemeral if this is a client only channel
		l        net.Listener  // May be nil if this is a client only channel
		conns    []*Connection
	}
}

// NewChannel creates a new Channel.  The new channel can be used to send outbound requests
// to peers, but will not listen or handling incoming requests until one of ListenAndServe
// or Serve is called. The local service name should be passed to serviceName.
func NewChannel(serviceName string, opts *ChannelOptions) (*Channel, error) {
	if serviceName == "" {
		return nil, ErrNoServiceName
	}

	if opts == nil {
		opts = &ChannelOptions{}
	}

	logger := opts.Logger
	if logger == nil {
		logger = NullLogger
	}

	processName := opts.ProcessName
	if processName == "" {
		processName = fmt.Sprintf("%s[%d]", filepath.Base(os.Args[0]), os.Getpid())
	}

	statsReporter := opts.StatsReporter
	if statsReporter == nil {
		statsReporter = NullStatsReporter
	}

	ch := &Channel{
		connectionOptions: opts.DefaultConnectionOptions,
		log:               logger.WithFields(LogField{"service", serviceName}),
		statsReporter:     statsReporter,
		handlers:          &handlerMap{},
		subChannels:       &subChannelMap{},
	}

	traceReporter := opts.TraceReporter
	if opts.TraceReporterFactory != nil {
		traceReporter = opts.TraceReporterFactory(ch)
	}
	if traceReporter == nil {
		traceReporter = NullReporter
	}
	ch.traceReporter = traceReporter

	ch.mutable.peerInfo = LocalPeerInfo{
		PeerInfo: PeerInfo{
			ProcessName: processName,
			HostPort:    ephemeralHostPort,
		},
		ServiceName: serviceName,
	}
	ch.mutable.state = ChannelClient
	ch.peers = newPeerList(ch)
	ch.createCommonStats()
	return ch, nil
}

// Serve serves incoming requests using the provided listener.
// The local peer info is set synchronously, but the actual socket listening is done in
// a separate goroutine.
func (ch *Channel) Serve(l net.Listener) error {
	mutable := &ch.mutable
	mutable.mut.Lock()
	defer mutable.mut.Unlock()

	if mutable.l != nil {
		return errAlreadyListening
	}
	mutable.l = l

	if mutable.state != ChannelClient {
		return errInvalidStateForOp
	}
	mutable.state = ChannelListening

	mutable.peerInfo.HostPort = l.Addr().String()
	peerInfo := mutable.peerInfo
	ch.log.Debugf("%v (%v) listening on %v", peerInfo.ProcessName, peerInfo.ServiceName, peerInfo.HostPort)
	go ch.serve()
	return nil
}

// ListenAndServe listens on the given address and serves incoming requests.
// The port may be 0, in which case the channel will use an OS assigned port
// This method does not block as the handling of connections is done in a goroutine.
func (ch *Channel) ListenAndServe(hostPort string) error {
	mutable := &ch.mutable
	mutable.mut.RLock()

	if mutable.l != nil {
		mutable.mut.RUnlock()
		return errAlreadyListening
	}

	l, err := net.Listen("tcp", hostPort)
	if err != nil {
		mutable.mut.RUnlock()
		return err
	}

	mutable.mut.RUnlock()
	return ch.Serve(l)
}

// Registrar is the base interface for registering handlers on either the base
// Channel or the SubChannel
type Registrar interface {
	// ServiceName returns the service name that this Registrar is for.
	ServiceName() string

	// Register registers a handler for ServiceName and the given operation.
	Register(h Handler, operationName string)

	// Logger returns the logger for this Registrar.
	Logger() Logger

	// Peers returns the peer list for this Registrar.
	Peers() *PeerList
}

// Register registers a handler for a service+operation pair
func (ch *Channel) Register(h Handler, operationName string) {
	ch.handlers.register(h, ch.PeerInfo().ServiceName, operationName)
}

// PeerInfo returns the current peer info for the channel
func (ch *Channel) PeerInfo() LocalPeerInfo {
	ch.mutable.mut.RLock()
	defer ch.mutable.mut.RUnlock()

	return ch.mutable.peerInfo
}

func (ch *Channel) createCommonStats() {
	ch.commonStatsTags = map[string]string{
		"app":     ch.mutable.peerInfo.ProcessName,
		"service": ch.mutable.peerInfo.ServiceName,
	}
	host, err := os.Hostname()
	if err != nil {
		ch.log.Infof("channel failed to get host: %v", err)
		return
	}
	ch.commonStatsTags["host"] = host
	// TODO(prashant): Allow user to pass extra tags (such as cluster, version).
}

// GetSubChannel returns a SubChannel for the given service name. If the subchannel does not
// exist, it is created.
func (ch *Channel) GetSubChannel(serviceName string) *SubChannel {
	return ch.subChannels.getOrAdd(serviceName, ch)
}

// Peers returns the PeerList for the channel.
func (ch *Channel) Peers() *PeerList {
	return ch.peers
}

// BeginCall starts a new call to a remote peer, returning an OutboundCall that can
// be used to write the arguments of the call.
func (ch *Channel) BeginCall(ctx context.Context, hostPort, serviceName, operationName string, callOptions *CallOptions) (*OutboundCall, error) {
	p := ch.peers.GetOrAdd(hostPort)
	return p.BeginCall(ctx, serviceName, operationName, callOptions)
}

// serve runs the listener to accept and manage new incoming connections, blocking
// until the channel is closed.
func (ch *Channel) serve() {
	acceptBackoff := 0 * time.Millisecond

	for {
		netConn, err := ch.mutable.l.Accept()
		if err != nil {
			// Backoff from new accepts if this is a temporary error
			if ne, ok := err.(net.Error); ok && ne.Temporary() {
				if acceptBackoff == 0 {
					acceptBackoff = 5 * time.Millisecond
				} else {
					acceptBackoff *= 2
				}
				if max := 1 * time.Second; acceptBackoff > max {
					acceptBackoff = max
				}
				ch.log.Warnf("accept error: %v; retrying in %v", err, acceptBackoff)
				time.Sleep(acceptBackoff)
				continue
			} else {
				// Only log an error if this didn't happen due to a Close.
				if ch.State() >= ChannelStartClose {
					return
				}
				ch.log.Fatalf("unrecoverable accept error: %v; closing server", err)
				return
			}
		}

		acceptBackoff = 0

		// Register the connection in the peer once the channel is set up.
		events := connectionEvents{
			OnActive:           ch.incomingConnectionActive,
			OnCloseStateChange: ch.connectionCloseStateChange,
		}
		if _, err := ch.newInboundConnection(netConn, events, &ch.connectionOptions); err != nil {
			// Server is getting overloaded - begin rejecting new connections
			ch.log.Errorf("could not create new TChannelConnection for incoming conn: %v", err)
			netConn.Close()
			continue
		}
	}
}

// Ping sends a ping message to the given hostPort and waits for a response.
func (ch *Channel) Ping(ctx context.Context, hostPort string) error {
	peer := ch.Peers().GetOrAdd(hostPort)
	conn, err := peer.GetConnection(ctx)
	if err != nil {
		return err
	}

	return conn.ping(ctx)
}

// Logger returns the logger for this channel.
func (ch *Channel) Logger() Logger {
	return ch.log
}

// ServiceName returns the serviceName that this channel was created for.
func (ch *Channel) ServiceName() string {
	return ch.PeerInfo().ServiceName
}

// Connect connects the channel.
func (ch *Channel) Connect(ctx context.Context, hostPort string, connectionOptions *ConnectionOptions) (*Connection, error) {
	switch state := ch.State(); state {
	case ChannelClient, ChannelListening:
		break
	case ChannelStartClose:
		// We still allow outgoing connections during Close, but the connection has to immediately
		// be Closed after opening
	default:
		ch.log.Debugf("Connect rejecting new connection as state is %v", state)
		return nil, errInvalidStateForOp
	}

	events := connectionEvents{OnCloseStateChange: ch.connectionCloseStateChange}
	c, err := ch.newOutboundConnection(hostPort, events, connectionOptions)
	if err != nil {
		return nil, err
	}

	if err := c.sendInit(ctx); err != nil {
		return nil, err
	}

	ch.mutable.mut.Lock()
	ch.mutable.conns = append(ch.mutable.conns, c)
	chState := ch.mutable.state
	ch.mutable.mut.Unlock()

	// Any connections added after the channel is in StartClose should also be set to start close.
	if chState == ChannelStartClose {
		// TODO(prashant): If Connect is called, but no outgoing calls are made, then this connection
		// will block Close, as it will never get cleaned up.
		c.withStateLock(func() error {
			c.state = connectionStartClose
			return nil
		})
		c.log.Debugf("Channel is in start close, set connection to start close")
	}

	return c, err
}

// incomingConnectionActive adds a new active connection to our peer list.
func (ch *Channel) incomingConnectionActive(c *Connection) {
	c.log.Debugf("Add connection as an active peer for %v", c.remotePeerInfo.HostPort)
	p := ch.peers.GetOrAdd(c.remotePeerInfo.HostPort)
	p.AddConnection(c)

	ch.mutable.mut.Lock()
	ch.mutable.conns = append(ch.mutable.conns, c)
	ch.mutable.mut.Unlock()
}

// connectionCloseStateChange is called when a connection's close state changes.
func (ch *Channel) connectionCloseStateChange(c *Connection) {
	switch chState := ch.State(); chState {
	case ChannelStartClose, ChannelInboundClosed:
		ch.mutable.mut.RLock()
		minState := connectionClosed
		for _, c := range ch.mutable.conns {
			if s := c.readState(); s < minState {
				minState = s
			}
		}
		ch.mutable.mut.RUnlock()

		var updateTo ChannelState
		if minState >= connectionClosed {
			updateTo = ChannelClosed
		} else if minState >= connectionInboundClosed && chState == ChannelStartClose {
			updateTo = ChannelInboundClosed
		}

		if updateTo > 0 {
			ch.mutable.mut.Lock()
			ch.mutable.state = updateTo
			ch.mutable.mut.Unlock()
			chState = updateTo
		}

		c.log.Debugf("ConnectionCloseStateChange channel state = %v connection minState = %v",
			chState, minState)
	}
}

// Closed returns whether this channel has been closed with .Close()
func (ch *Channel) Closed() bool {
	return ch.State() == ChannelClosed
}

// State returns the current channel state.
func (ch *Channel) State() ChannelState {
	ch.mutable.mut.RLock()
	state := ch.mutable.state
	ch.mutable.mut.RUnlock()

	return state
}

// Close starts a graceful Close for the channel. This does not happen immediately:
// 1. This call closes the Listener and starts closing connections.
// 2. When all incoming connections are drainged, the connection blocks new outgoing calls.
// 3. When all connections are drainged, the channel's state is updated to Closed.
func (ch *Channel) Close() {
	ch.mutable.mut.Lock()

	if ch.mutable.l != nil {
		ch.mutable.l.Close()
	}

	ch.mutable.state = ChannelStartClose
	if len(ch.mutable.conns) == 0 {
		ch.mutable.state = ChannelClosed
	}
	ch.mutable.mut.Unlock()

	ch.peers.Close()
}
<|endoftext|>


<commit_before>package birc

import (
	"fmt"
	"net"
	"time"

	sirc "github.com/sorcix/irc"
)

// Config contains fields required to connect to the IRC server.
type Config struct {
	ChannelName string
	Server      string
	Username    string
	OAuthToken  string
}

// Channel represents a connected and active IRC channel.
type Channel struct {
	Config     *Config
	Connection net.Conn
	Digesters  []Digester
	reader     Decoder
	writer     Encoder
	done       chan *ChannelError
	data       chan *Message
}

// ChannelWriter represents a writer capable of sending messages to a channel.
type ChannelWriter interface {
	Send(message string) error
}

// ChannelError isa struct consisting of a reference to a channel and an error that
// occurred on that channel.
type ChannelError struct {
	Channel *Channel
	error   error
}

// Error returns a description of the error. Satisfies the Error interface.
func (c *ChannelError) Error() string {
	return c.error.Error()
}

// NewTwitchChannel creates an IRC channel with Twitch's default server and port.
func NewTwitchChannel(channelName, username, token string, digesters ...Digester) *Channel {
	config := &Config{
		ChannelName: channelName,
		Username:    username,
		OAuthToken:  token,
		Server:      DefaultTwitchServer,
	}

	return &Channel{Config: config, Digesters: digesters[:]}
}

// Connect establishes a connection to an IRC server.
func (c *Channel) Connect() error {
	conn, err := net.Dial("tcp", c.Config.Server)
	if err != nil {
		return err
	}

	c.Connection = conn
	c.reader = sirc.NewDecoder(conn)
	c.writer = sirc.NewEncoder(conn)
	c.data = make(chan *Message)
	c.done = make(chan *ChannelError)
	return nil
}

// SetWriter sets the channel's underlying writer. This is not threadsafe.
func (c *Channel) SetWriter(e Encoder) {
	c.writer = e
}

// Authenticate sends the PASS and NICK to authenticate against the server. It also sends
// the JOIN message in order to join the specified channel in the configuration.
func (c *Channel) Authenticate() error {
	for _, m := range []sirc.Message{
		sirc.Message{
			Command: sirc.PASS,
			Params:  []string{fmt.Sprintf("oauth:%s", c.Config.OAuthToken)},
		},
		sirc.Message{
			Command: sirc.NICK,
			Params:  []string{c.Config.Username},
		},
		sirc.Message{
			Command: sirc.JOIN,
			Params:  []string{fmt.Sprintf("#%s", c.Config.ChannelName)},
		},
	} {
		if err := c.writer.Encode(&m); err != nil {
			return err
		}
	}
	return nil
}

// Disconnect ends the current listener and closes the TCP connection.
func (c *Channel) Disconnect() {
	c.done <- nil
}

// Send writes a message to the channel.
func (c *Channel) Send(message string) error {
	m := &sirc.Message{
		Prefix: &sirc.Prefix{
			Name: c.Config.Username,
			User: c.Config.Username,
			Host: DefaultTwitchURI,
		},
		Command:  sirc.PRIVMSG,
		Params:   []string{fmt.Sprintf("#%s", c.Config.ChannelName)},
		Trailing: message,
	}
	if err := c.writer.Encode(m); err != nil {
		return err
	}
	return nil
}

// Listen enters a loop and starts decoding IRC messages from the connected channel.
// Decoded messages are pushed to the data channel and ultimately the digesters to be handled.
func (c *Channel) Listen() *ChannelError {
	// Close the connection when finished.
	defer c.Connection.Close()

	// quit channel is used to stop the pinging routine when disconnecting.
	quit := make(chan bool, 1)
	go c.startPinging(quit)

	err := c.startReceiving()

	// stop the pinging before exiting
	quit <- true
	return err
}

func (c *Channel) startReceiving() *ChannelError {
	for {
		select {
		case <-c.done:
			return nil
		default:
			c.Connection.SetDeadline(time.Now().Add(2 * time.Minute))
			m, err := c.reader.Decode()
			if err != nil {
				return &ChannelError{Channel: c, error: err}
			}
			if m.Prefix != nil {
				m := &Message{Name: m.Name, Username: m.User, Content: m.Trailing, Time: time.Now()}
				go c.handle(m)
			}
		}

	}
}

// startPinging will send a 'heartbeat' ping to the server to maintain a connection.
func (c *Channel) startPinging(quit chan bool) {
	for {
		select {
		case <-quit:
			return
		case <-time.After(180 * time.Second):
			ping := sirc.Message{
				Command: sirc.PING,
			}
			c.writer.Encode(&ping)
		}
	}
}

func (c *Channel) handle(m *Message) {
	for _, d := range c.Digesters {
		go d(*m, c)
	}
}
<commit_msg>Updated a few comments. Fixed a bug where the connection read deadline was less than the ping heartbeat, resulting in a closed connection on inactive channels.<commit_after>package birc

import (
	"fmt"
	"net"
	"time"

	sirc "github.com/sorcix/irc"
)

// Config contains fields required to connect to the IRC server.
type Config struct {
	ChannelName string
	Server      string
	Username    string
	OAuthToken  string
}

// Channel represents a connected and active IRC channel.
type Channel struct {
	Config     *Config
	Connection net.Conn
	Digesters  []Digester
	reader     Decoder
	writer     Encoder
	done       chan *ChannelError
	data       chan *Message
}

// ChannelWriter represents a writer capable of sending messages to a channel.
type ChannelWriter interface {
	Send(message string) error
}

// ChannelError is a struct consisting of a reference to a channel and an error that
// occurred on that channel.
type ChannelError struct {
	Channel *Channel
	error   error
}

// Error returns a description of the error. Satisfies the Error interface.
func (c *ChannelError) Error() string {
	return c.error.Error()
}

// NewTwitchChannel creates an IRC channel with Twitch's default server and port.
func NewTwitchChannel(channelName, username, token string, digesters ...Digester) *Channel {
	config := &Config{
		ChannelName: channelName,
		Username:    username,
		OAuthToken:  token,
		Server:      DefaultTwitchServer,
	}

	return &Channel{Config: config, Digesters: digesters[:]}
}

// Connect establishes a connection to an IRC server.
func (c *Channel) Connect() error {
	conn, err := net.Dial("tcp", c.Config.Server)
	if err != nil {
		return err
	}

	c.Connection = conn
	c.reader = sirc.NewDecoder(conn)
	c.writer = sirc.NewEncoder(conn)
	c.data = make(chan *Message)
	c.done = make(chan *ChannelError)
	return nil
}

// SetWriter sets the channel's underlying writer. This is not threadsafe.
func (c *Channel) SetWriter(e Encoder) {
	c.writer = e
}

// Authenticate sends the PASS and NICK to authenticate against the server. It also sends
// the JOIN message in order to join the specified channel in the configuration.
func (c *Channel) Authenticate() error {
	for _, m := range []sirc.Message{
		sirc.Message{
			Command: sirc.PASS,
			Params:  []string{fmt.Sprintf("oauth:%s", c.Config.OAuthToken)},
		},
		sirc.Message{
			Command: sirc.NICK,
			Params:  []string{c.Config.Username},
		},
		sirc.Message{
			Command: sirc.JOIN,
			Params:  []string{fmt.Sprintf("#%s", c.Config.ChannelName)},
		},
	} {
		if err := c.writer.Encode(&m); err != nil {
			return err
		}
	}
	return nil
}

// Disconnect ends the current listener and closes the TCP connection.
func (c *Channel) Disconnect() {
	c.done <- nil
}

// Send writes a message to the channel.
func (c *Channel) Send(message string) error {
	m := &sirc.Message{
		Prefix: &sirc.Prefix{
			Name: c.Config.Username,
			User: c.Config.Username,
			Host: DefaultTwitchURI,
		},
		Command:  sirc.PRIVMSG,
		Params:   []string{fmt.Sprintf("#%s", c.Config.ChannelName)},
		Trailing: message,
	}
	if err := c.writer.Encode(m); err != nil {
		return err
	}
	return nil
}

// Listen enters a loop and starts decoding IRC messages from the connected channel.
// Decoded messages are pushed to the digesters to be handled.
func (c *Channel) Listen() *ChannelError {
	// Close the connection when finished.
	defer c.Connection.Close()

	// quit channel is used to stop the pinging routine when disconnecting.
	quit := make(chan bool, 1)
	go c.startPinging(quit)

	err := c.startReceiving()

	// stop the pinging before exiting
	quit <- true
	return err
}

func (c *Channel) startReceiving() *ChannelError {
	for {
		select {
		case <-c.done:
			return nil
		default:
			c.Connection.SetDeadline(time.Now().Add(4 * time.Minute))
			m, err := c.reader.Decode()
			if err != nil {
				return &ChannelError{Channel: c, error: err}
			}
			if m.Prefix != nil {
				m := &Message{Name: m.Name, Username: m.User, Content: m.Trailing, Time: time.Now()}
				go c.handle(m)
			}
		}

	}
}

// startPinging will send a 'heartbeat' ping to the server to maintain a connection.
func (c *Channel) startPinging(quit chan bool) {
	for {
		select {
		case <-quit:
			return
		case <-time.After(180 * time.Second):
			ping := sirc.Message{
				Command: sirc.PING,
			}
			c.writer.Encode(&ping)
		}
	}
}

func (c *Channel) handle(m *Message) {
	for _, d := range c.Digesters {
		go d(*m, c)
	}
}
<|endoftext|>


<commit_before>package HumorChecker // import "cirello.io/HumorChecker"

import (
	"bufio"
	"bytes"
	"io"
)

// analysis is the complete sentiment calculation
type analysis struct {
	// positivityScore is the sum of the positive sentiment points of the
	// analyzed text.
	positivityScore int

	// negativityScore is the sum of the negativity sentiment points of the
	// analyzed text.
	negativityScore int

	// positivityComparative establishes a ratio of sentiment per positive
	// word
	positivityComparative float64

	// negativityComparative establishes a ratio of sentiment per negative
	// word
	negativityComparative float64

	// positiveWords is the list of positive words for a given sentiment.
	positiveWords []string

	// negativeWords is the list of negative words for a given sentiment.
	negativeWords []string
}

// Score is the result of sentiment calculation
type Score struct {
	// Score is the sum of the sentiment points of the analyzed text.
	// Negativity will render negative points only, and vice-versa.
	Score int

	// Comparative establishes a ratio of sentiment per word
	Comparative float64

	// Words is the list of words for a given sentiment.
	Words []string
}

// FullScore is the difference between positive and negative sentiment
type FullScore struct {
	// Score is the difference between positive and negative sentiment
	// scores.
	Score int

	// Comparative is the difference between positive and negative sentiment
	// comparative scores.
	Comparative float64

	// Positive score object
	Positive Score

	// Negative score object
	Negative Score
}

func keepLettersAndSpace(str string) io.Reader {
	var buf bytes.Buffer
	for _, v := range str {
		switch {
		case v >= 'A' && v <= 'Z':
			buf.WriteRune(v + 32)
		case v >= 'a' && v <= 'z' || v == ' ':
			buf.WriteRune(v)
		}
	}
	return &buf
}

func calculateScore(phrase string) analysis {
	var phits, nhits int
	var pwords, nwords []string
	var count int

	scanner := bufio.NewScanner(keepLettersAndSpace(phrase))
	scanner.Split(bufio.ScanWords)
	for scanner.Scan() {
		count++
		word := scanner.Text()
		if v, ok := afinn[word]; ok {
			if v > 0 {
				phits += v
				pwords = append(pwords, word)
			} else if v < 0 {
				nhits -= v
				nwords = append(nwords, word)
			}
		}
	}

	return analysis{
		positivityScore:       phits,
		positivityComparative: float64(phits / count),
		positiveWords:         pwords,
		negativityScore:       nhits,
		negativityComparative: float64(nhits / count),
		negativeWords:         nwords,
	}
}

func renderNegativeScore(a analysis) Score {
	return Score{
		Score:       a.negativityScore,
		Comparative: a.negativityComparative,
		Words:       a.negativeWords,
	}
}

// Negativity calculates the negative sentiment of a sentence
func Negativity(phrase string) Score {
	return renderNegativeScore(calculateScore(phrase))
}

func renderPositiveScore(a analysis) Score {
	return Score{
		Score:       a.positivityScore,
		Comparative: a.positivityComparative,
		Words:       a.positiveWords,
	}
}

// Positivity calculates the positive sentiment of a sentence
func Positivity(phrase string) Score {
	return renderPositiveScore(calculateScore(phrase))
}

// Analyze calculates overall sentiment
func Analyze(phrase string) FullScore {
	analysis := calculateScore(phrase)

	return FullScore{
		Score:       analysis.positivityScore - analysis.negativityScore,
		Comparative: analysis.positivityComparative - analysis.negativityComparative,
		Positive:    renderPositiveScore(analysis),
		Negative:    renderNegativeScore(analysis),
	}
}
<commit_msg>HumorChecker: fix documentation<commit_after>// Package HumorChecker implements sentiment analysis tool based on the [AFINN-111 wordlist](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6010).
package HumorChecker // import "cirello.io/HumorChecker"

import (
	"bufio"
	"bytes"
	"io"
)

// analysis is the complete sentiment calculation
type analysis struct {
	// positivityScore is the sum of the positive sentiment points of the
	// analyzed text.
	positivityScore int

	// negativityScore is the sum of the negativity sentiment points of the
	// analyzed text.
	negativityScore int

	// positivityComparative establishes a ratio of sentiment per positive
	// word
	positivityComparative float64

	// negativityComparative establishes a ratio of sentiment per negative
	// word
	negativityComparative float64

	// positiveWords is the list of positive words for a given sentiment.
	positiveWords []string

	// negativeWords is the list of negative words for a given sentiment.
	negativeWords []string
}

// Score is the result of sentiment calculation
type Score struct {
	// Score is the sum of the sentiment points of the analyzed text.
	// Negativity will render negative points only, and vice-versa.
	Score int

	// Comparative establishes a ratio of sentiment per word
	Comparative float64

	// Words is the list of words for a given sentiment.
	Words []string
}

// FullScore is the difference between positive and negative sentiment
type FullScore struct {
	// Score is the difference between positive and negative sentiment
	// scores.
	Score int

	// Comparative is the difference between positive and negative sentiment
	// comparative scores.
	Comparative float64

	// Positive score object
	Positive Score

	// Negative score object
	Negative Score
}

func keepLettersAndSpace(str string) io.Reader {
	var buf bytes.Buffer
	for _, v := range str {
		switch {
		case v >= 'A' && v <= 'Z':
			buf.WriteRune(v + 32)
		case v >= 'a' && v <= 'z' || v == ' ':
			buf.WriteRune(v)
		}
	}
	return &buf
}

func calculateScore(phrase string) analysis {
	var phits, nhits int
	var pwords, nwords []string
	var count int

	scanner := bufio.NewScanner(keepLettersAndSpace(phrase))
	scanner.Split(bufio.ScanWords)
	for scanner.Scan() {
		count++
		word := scanner.Text()
		if v, ok := afinn[word]; ok {
			if v > 0 {
				phits += v
				pwords = append(pwords, word)
			} else if v < 0 {
				nhits -= v
				nwords = append(nwords, word)
			}
		}
	}

	return analysis{
		positivityScore:       phits,
		positivityComparative: float64(phits / count),
		positiveWords:         pwords,
		negativityScore:       nhits,
		negativityComparative: float64(nhits / count),
		negativeWords:         nwords,
	}
}

func renderNegativeScore(a analysis) Score {
	return Score{
		Score:       a.negativityScore,
		Comparative: a.negativityComparative,
		Words:       a.negativeWords,
	}
}

// Negativity calculates the negative sentiment of a sentence
func Negativity(phrase string) Score {
	return renderNegativeScore(calculateScore(phrase))
}

func renderPositiveScore(a analysis) Score {
	return Score{
		Score:       a.positivityScore,
		Comparative: a.positivityComparative,
		Words:       a.positiveWords,
	}
}

// Positivity calculates the positive sentiment of a sentence
func Positivity(phrase string) Score {
	return renderPositiveScore(calculateScore(phrase))
}

// Analyze calculates overall sentiment
func Analyze(phrase string) FullScore {
	analysis := calculateScore(phrase)

	return FullScore{
		Score:       analysis.positivityScore - analysis.negativityScore,
		Comparative: analysis.positivityComparative - analysis.negativityComparative,
		Positive:    renderPositiveScore(analysis),
		Negative:    renderNegativeScore(analysis),
	}
}
<|endoftext|>


<commit_before>package mmm

import (
	"testing"
	"unsafe"
)

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

func TestSize_SizeOf_bool(t *testing.T) {
	var v bool
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for bool")
	}
}

func TestSize_SizeOf_int(t *testing.T) {
	var v int
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for int")
	}
}

func TestSize_SizeOf_int8(t *testing.T) {
	var v int8
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for int8")
	}
}

func TestSize_SizeOf_int16(t *testing.T) {
	var v int16
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for int16")
	}
}

func TestSize_SizeOf_int32(t *testing.T) {
	var v int32
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for int32")
	}
}

func TestSize_SizeOf_int64(t *testing.T) {
	var v int64
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for int64")
	}
}

func TestSize_SizeOf_uint(t *testing.T) {
	var v uint
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for uint")
	}
}

func TestSize_SizeOf_uint8(t *testing.T) {
	var v uint8
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for uint8")
	}
}

func TestSize_SizeOf_uint16(t *testing.T) {
	var v uint16
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for uint16")
	}
}

func TestSize_SizeOf_uint32(t *testing.T) {
	var v uint32
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for uint32")
	}
}

func TestSize_SizeOf_uint64(t *testing.T) {
	var v uint64
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for uint64")
	}
}

func TestSize_SizeOf_uintptr(t *testing.T) {
	var v uintptr
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for uintptr")
	}
}

//var f32 float32
//var f64 float64
//var c64 complex64
//var c128 complex128
//var iarr [42]int
//var ichan chan int
//var itf interface{}
//var imap map[int]int
//var iptr *int
//var islice []int
//var str string
//var srt struct{}
<commit_msg>added TestSize_SizeOf_float32<commit_after>package mmm

import (
	"testing"
	"unsafe"
)

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

func TestSize_SizeOf_bool(t *testing.T) {
	var v bool
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for bool")
	}
}

func TestSize_SizeOf_int(t *testing.T) {
	var v int
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for int")
	}
}

func TestSize_SizeOf_int8(t *testing.T) {
	var v int8
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for int8")
	}
}

func TestSize_SizeOf_int16(t *testing.T) {
	var v int16
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for int16")
	}
}

func TestSize_SizeOf_int32(t *testing.T) {
	var v int32
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for int32")
	}
}

func TestSize_SizeOf_int64(t *testing.T) {
	var v int64
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for int64")
	}
}

func TestSize_SizeOf_uint(t *testing.T) {
	var v uint
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for uint")
	}
}

func TestSize_SizeOf_uint8(t *testing.T) {
	var v uint8
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for uint8")
	}
}

func TestSize_SizeOf_uint16(t *testing.T) {
	var v uint16
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for uint16")
	}
}

func TestSize_SizeOf_uint32(t *testing.T) {
	var v uint32
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for uint32")
	}
}

func TestSize_SizeOf_uint64(t *testing.T) {
	var v uint64
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for uint64")
	}
}

func TestSize_SizeOf_uintptr(t *testing.T) {
	var v uintptr
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for uintptr")
	}
}

func TestSize_SizeOf_float32(t *testing.T) {
	var v float32
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for float32")
	}
}

//var f32 float32
//var f64 float64
//var c64 complex64
//var c128 complex128
//var iarr [42]int
//var ichan chan int
//var itf interface{}
//var imap map[int]int
//var iptr *int
//var islice []int
//var str string
//var srt struct{}
<|endoftext|>


<commit_before>package flake

import (
	"testing"
)

func BenchmarkGenerate(b *testing.B) {

	node, _ := NewNode(1)

	b.ReportAllocs()
	for n := 0; n < b.N; n++ {
		_, _ = node.Generate()
	}

}
<commit_msg>Fixed package name in test file<commit_after>package snowflake

import (
	"testing"
)

func BenchmarkGenerate(b *testing.B) {

	node, _ := NewNode(1)

	b.ReportAllocs()
	for n := 0; n < b.N; n++ {
		_, _ = node.Generate()
	}

}
<|endoftext|>


<commit_before>package api

import (
	"context"
	"github.com/ovh/cds/engine/api/authentication"
	"github.com/ovh/cds/engine/api/authentication/builtin"
	"github.com/ovh/cds/engine/api/test/assets"
	"github.com/ovh/cds/engine/api/workflow"
	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/cdsclient"
	"github.com/stretchr/testify/require"
	"net/url"
	"strings"
	"testing"
	"time"
)

func Test_websocketWrongFilters(t *testing.T) {
	api, tsURL, tsClose := newTestServer(t)
	defer tsClose()

	u, _ := assets.InsertAdminUser(t, api.mustDB())
	localConsumer, err := authentication.LoadConsumerByTypeAndUserID(context.TODO(), api.mustDB(), sdk.ConsumerLocal, u.ID, authentication.LoadConsumerOptions.WithAuthentifiedUser)
	require.NoError(t, err)

	_, jws, err := builtin.NewConsumer(context.TODO(), api.mustDB(), sdk.RandomString(10), sdk.RandomString(10), localConsumer, u.GetGroupIDs(),
		sdk.NewAuthConsumerScopeDetails(sdk.AuthConsumerScopeProject))

	chanMessageReceived := make(chan sdk.WebsocketEvent)
	chanMessageToSend := make(chan sdk.WebsocketFilter)

	client := cdsclient.New(cdsclient.Config{
		Host:                              tsURL,
		User:                              u.Username,
		InsecureSkipVerifyTLS:             true,
		BuitinConsumerAuthenticationToken: jws,
	})
	go client.WebsocketEventsListen(context.TODO(), chanMessageToSend, chanMessageReceived)

	// Subscribe to project without project key
	chanMessageToSend <- sdk.WebsocketFilter{
		Type:       sdk.WebsocketFilterTypeProject,
		ProjectKey: "",
	}
	response := <-chanMessageReceived
	require.Equal(t, "KO", response.Status)
	require.Equal(t, "wrong request", response.Error)

	// Subscribe to application without application name
	chanMessageToSend <- sdk.WebsocketFilter{
		Type:            sdk.WebsocketFilterTypeApplication,
		ProjectKey:      "Key",
		ApplicationName: "",
	}
	response = <-chanMessageReceived
	require.Equal(t, "KO", response.Status)
	require.Equal(t, "wrong request", response.Error)

	// Subscribe to application without project key
	chanMessageToSend <- sdk.WebsocketFilter{
		Type:            sdk.WebsocketFilterTypeApplication,
		ProjectKey:      "",
		ApplicationName: "App1",
	}
	response = <-chanMessageReceived
	require.Equal(t, "KO", response.Status)
	require.Equal(t, "wrong request", response.Error)

	// Subscribe to pipeline without pipeline name
	chanMessageToSend <- sdk.WebsocketFilter{
		Type:         sdk.WebsocketFilterTypeApplication,
		ProjectKey:   "Key",
		PipelineName: "",
	}
	response = <-chanMessageReceived
	require.Equal(t, "KO", response.Status)
	require.Equal(t, "wrong request", response.Error)

	// Subscribe to pipeline without project key
	chanMessageToSend <- sdk.WebsocketFilter{
		Type:         sdk.WebsocketFilterTypePipeline,
		ProjectKey:   "",
		PipelineName: "PipName",
	}
	response = <-chanMessageReceived
	require.Equal(t, "KO", response.Status)
	require.Equal(t, "wrong request", response.Error)

	// Subscribe to environment without environment name
	chanMessageToSend <- sdk.WebsocketFilter{
		Type:            sdk.WebsocketFilterTypeEnvironment,
		ProjectKey:      "Key",
		EnvironmentName: "",
	}
	response = <-chanMessageReceived
	require.Equal(t, "KO", response.Status)
	require.Equal(t, "wrong request", response.Error)

	// Subscribe to environment without project key
	chanMessageToSend <- sdk.WebsocketFilter{
		Type:            sdk.WebsocketFilterTypeEnvironment,
		ProjectKey:      "",
		EnvironmentName: "EnvNmae",
	}
	response = <-chanMessageReceived
	require.Equal(t, "KO", response.Status)
	require.Equal(t, "wrong request", response.Error)

	// Subscribe to workflow without workflow name
	chanMessageToSend <- sdk.WebsocketFilter{
		Type:         sdk.WebsocketFilterTypeWorkflow,
		ProjectKey:   "Key",
		WorkflowName: "",
	}
	response = <-chanMessageReceived
	require.Equal(t, "KO", response.Status)
	require.Equal(t, "wrong request", response.Error)

	// Subscribe to workflow without project key
	chanMessageToSend <- sdk.WebsocketFilter{
		Type:         sdk.WebsocketFilterTypeWorkflow,
		ProjectKey:   "",
		WorkflowName: "WorkflowName",
	}
	response = <-chanMessageReceived
	require.Equal(t, "KO", response.Status)
	require.Equal(t, "wrong request", response.Error)
}

func Test_websocketGetWorkflowEvent(t *testing.T) {
	api, tsURL, tsClose := newTestServer(t)
	defer tsClose()

	u, _ := assets.InsertAdminUser(t, api.mustDB())
	localConsumer, err := authentication.LoadConsumerByTypeAndUserID(context.TODO(), api.mustDB(), sdk.ConsumerLocal, u.ID, authentication.LoadConsumerOptions.WithAuthentifiedUser)
	require.NoError(t, err)

	key := sdk.RandomString(10)
	proj := assets.InsertTestProject(t, api.mustDB(), api.Cache, key, key)

	w := sdk.Workflow{
		Name:       "workflow1",
		ProjectID:  proj.ID,
		ProjectKey: proj.Key,
		WorkflowData: sdk.WorkflowData{
			Node: sdk.Node{
				Name: "root",
				Type: sdk.NodeTypeFork,
			},
		},
	}
	require.NoError(t, workflow.Insert(context.TODO(), api.mustDB(), api.Cache, *proj, &w))

	_, jws, err := builtin.NewConsumer(context.TODO(), api.mustDB(), sdk.RandomString(10), sdk.RandomString(10), localConsumer, u.GetGroupIDs(),
		sdk.NewAuthConsumerScopeDetails(sdk.AuthConsumerScopeProject))

	chanMessageReceived := make(chan sdk.WebsocketEvent)
	chanMessageToSend := make(chan sdk.WebsocketFilter)

	client := cdsclient.New(cdsclient.Config{
		Host:                              tsURL,
		User:                              u.Username,
		InsecureSkipVerifyTLS:             true,
		BuitinConsumerAuthenticationToken: jws,
	})
	go client.WebsocketEventsListen(context.TODO(), chanMessageToSend, chanMessageReceived)

	chanMessageToSend <- sdk.WebsocketFilter{
		Type:              sdk.WebsocketFilterTypeWorkflow,
		ProjectKey:        key,
		WorkflowName:      w.Name,
		WorkflowRunNumber: 1,
	}
	// Waiting websocket to update filter
	time.Sleep(1 * time.Second)

	api.websocketBroker.messages <- sdk.Event{ProjectKey: "blabla", WorkflowName: "toto", EventType: "sdk.EventRunWorkflow", WorkflowRunNum: 1}
	api.websocketBroker.messages <- sdk.Event{ProjectKey: proj.Key, WorkflowName: w.Name, EventType: "sdk.EventRunWorkflow", WorkflowRunNum: 1}
	response := <-chanMessageReceived
	require.Equal(t, "OK", response.Status)
	require.Equal(t, response.Event.EventType, "sdk.EventRunWorkflow")
	require.Equal(t, response.Event.ProjectKey, proj.Key)
	require.Equal(t, response.Event.WorkflowName, w.Name)

	assert.Equal(t, 0, len(chanMessageReceived))
}

func Test_websocketDeconnection(t *testing.T) {
	api, tsURL, tsClose := newTestServer(t)
	defer tsClose()

	u, _ := assets.InsertAdminUser(t, api.mustDB())
	localConsumer, err := authentication.LoadConsumerByTypeAndUserID(context.TODO(), api.mustDB(), sdk.ConsumerLocal, u.ID, authentication.LoadConsumerOptions.WithAuthentifiedUser)
	require.NoError(t, err)

	key := sdk.RandomString(10)
	proj := assets.InsertTestProject(t, api.mustDB(), api.Cache, key, key)

	w := sdk.Workflow{
		Name:       "workflow1",
		ProjectID:  proj.ID,
		ProjectKey: proj.Key,
		WorkflowData: sdk.WorkflowData{
			Node: sdk.Node{
				Name: "root",
				Type: sdk.NodeTypeFork,
			},
		},
	}
	require.NoError(t, workflow.Insert(context.TODO(), api.mustDB(), api.Cache, *proj, &w))

	_, jws, err := builtin.NewConsumer(context.TODO(), api.mustDB(), sdk.RandomString(10), sdk.RandomString(10), localConsumer, u.GetGroupIDs(),
		sdk.NewAuthConsumerScopeDetails(sdk.AuthConsumerScopeProject))

	// Open websocket
	client := cdsclient.New(cdsclient.Config{
		Host:                              tsURL,
		User:                              u.Username,
		InsecureSkipVerifyTLS:             true,
		BuitinConsumerAuthenticationToken: jws,
	})
	resp, err := client.AuthConsumerSignin(sdk.ConsumerBuiltin, sdk.AuthConsumerSigninRequest{"token": jws})
	require.NoError(t, err)
	token := resp.Token

	uHost, err := url.Parse(tsURL)
	require.NoError(t, err)
	urlWebsocket := url.URL{
		Scheme: strings.Replace(uHost.Scheme, "http", "ws", -1),
		Host:   uHost.Host,
		Path:   "/ws",
	}
	headers := make(map[string][]string)
	date := sdk.FormatDateRFC5322(time.Now())
	headers["Date"] = []string{date}
	headers["X-CDS-RemoteTime"] = []string{date}
	auth := "Bearer " + token
	headers["Authorization"] = []string{auth}
	con, _, err := client.HTTPWebsocketClient().Dial(urlWebsocket.String(), headers)
	require.NoError(t, err)
	defer con.Close() // nolint

	// Waiting the websocket add the client
	time.Sleep(1 * time.Second)

	// Send filter
	err = con.WriteJSON(sdk.WebsocketFilter{
		Type:         sdk.WebsocketFilterTypeWorkflow,
		ProjectKey:   key,
		WorkflowName: w.Name,
	})
	require.NoError(t, err)

	// Waiting websocket to update filter
	time.Sleep(1 * time.Second)

	// Send message to client
	go func() {
		for i := 0; i < 100; i++ {
			api.websocketBroker.messages <- sdk.Event{ProjectKey: proj.Key, WorkflowName: w.Name, EventType: "sdk.EventWorkflow"}
			time.Sleep(200 * time.Millisecond)
		}
	}()
	// Kill client
	con.Close()

	time.Sleep(1 * time.Second)

	require.Equal(t, len(api.websocketBroker.clients), 0)
}
<commit_msg>fix(api): websocket unit test (#5162)<commit_after>package api

import (
	"context"
	"github.com/ovh/cds/engine/api/authentication"
	"github.com/ovh/cds/engine/api/authentication/builtin"
	"github.com/ovh/cds/engine/api/test/assets"
	"github.com/ovh/cds/engine/api/workflow"
	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/cdsclient"
	"github.com/stretchr/testify/require"
	"net/url"
	"strings"
	"testing"
	"time"
)

func Test_websocketWrongFilters(t *testing.T) {
	api, tsURL, tsClose := newTestServer(t)
	defer tsClose()

	u, _ := assets.InsertAdminUser(t, api.mustDB())
	localConsumer, err := authentication.LoadConsumerByTypeAndUserID(context.TODO(), api.mustDB(), sdk.ConsumerLocal, u.ID, authentication.LoadConsumerOptions.WithAuthentifiedUser)
	require.NoError(t, err)

	_, jws, err := builtin.NewConsumer(context.TODO(), api.mustDB(), sdk.RandomString(10), sdk.RandomString(10), localConsumer, u.GetGroupIDs(),
		sdk.NewAuthConsumerScopeDetails(sdk.AuthConsumerScopeProject))

	chanMessageReceived := make(chan sdk.WebsocketEvent)
	chanMessageToSend := make(chan sdk.WebsocketFilter)

	client := cdsclient.New(cdsclient.Config{
		Host:                              tsURL,
		User:                              u.Username,
		InsecureSkipVerifyTLS:             true,
		BuitinConsumerAuthenticationToken: jws,
	})
	go client.WebsocketEventsListen(context.TODO(), chanMessageToSend, chanMessageReceived)

	// Subscribe to project without project key
	chanMessageToSend <- sdk.WebsocketFilter{
		Type:       sdk.WebsocketFilterTypeProject,
		ProjectKey: "",
	}
	response := <-chanMessageReceived
	require.Equal(t, "KO", response.Status)
	require.Equal(t, "wrong request", response.Error)

	// Subscribe to application without application name
	chanMessageToSend <- sdk.WebsocketFilter{
		Type:            sdk.WebsocketFilterTypeApplication,
		ProjectKey:      "Key",
		ApplicationName: "",
	}
	response = <-chanMessageReceived
	require.Equal(t, "KO", response.Status)
	require.Equal(t, "wrong request", response.Error)

	// Subscribe to application without project key
	chanMessageToSend <- sdk.WebsocketFilter{
		Type:            sdk.WebsocketFilterTypeApplication,
		ProjectKey:      "",
		ApplicationName: "App1",
	}
	response = <-chanMessageReceived
	require.Equal(t, "KO", response.Status)
	require.Equal(t, "wrong request", response.Error)

	// Subscribe to pipeline without pipeline name
	chanMessageToSend <- sdk.WebsocketFilter{
		Type:         sdk.WebsocketFilterTypeApplication,
		ProjectKey:   "Key",
		PipelineName: "",
	}
	response = <-chanMessageReceived
	require.Equal(t, "KO", response.Status)
	require.Equal(t, "wrong request", response.Error)

	// Subscribe to pipeline without project key
	chanMessageToSend <- sdk.WebsocketFilter{
		Type:         sdk.WebsocketFilterTypePipeline,
		ProjectKey:   "",
		PipelineName: "PipName",
	}
	response = <-chanMessageReceived
	require.Equal(t, "KO", response.Status)
	require.Equal(t, "wrong request", response.Error)

	// Subscribe to environment without environment name
	chanMessageToSend <- sdk.WebsocketFilter{
		Type:            sdk.WebsocketFilterTypeEnvironment,
		ProjectKey:      "Key",
		EnvironmentName: "",
	}
	response = <-chanMessageReceived
	require.Equal(t, "KO", response.Status)
	require.Equal(t, "wrong request", response.Error)

	// Subscribe to environment without project key
	chanMessageToSend <- sdk.WebsocketFilter{
		Type:            sdk.WebsocketFilterTypeEnvironment,
		ProjectKey:      "",
		EnvironmentName: "EnvNmae",
	}
	response = <-chanMessageReceived
	require.Equal(t, "KO", response.Status)
	require.Equal(t, "wrong request", response.Error)

	// Subscribe to workflow without workflow name
	chanMessageToSend <- sdk.WebsocketFilter{
		Type:         sdk.WebsocketFilterTypeWorkflow,
		ProjectKey:   "Key",
		WorkflowName: "",
	}
	response = <-chanMessageReceived
	require.Equal(t, "KO", response.Status)
	require.Equal(t, "wrong request", response.Error)

	// Subscribe to workflow without project key
	chanMessageToSend <- sdk.WebsocketFilter{
		Type:         sdk.WebsocketFilterTypeWorkflow,
		ProjectKey:   "",
		WorkflowName: "WorkflowName",
	}
	response = <-chanMessageReceived
	require.Equal(t, "KO", response.Status)
	require.Equal(t, "wrong request", response.Error)
}

func Test_websocketGetWorkflowEvent(t *testing.T) {
	api, tsURL, tsClose := newTestServer(t)
	defer tsClose()

	u, _ := assets.InsertAdminUser(t, api.mustDB())
	localConsumer, err := authentication.LoadConsumerByTypeAndUserID(context.TODO(), api.mustDB(), sdk.ConsumerLocal, u.ID, authentication.LoadConsumerOptions.WithAuthentifiedUser)
	require.NoError(t, err)

	key := sdk.RandomString(10)
	proj := assets.InsertTestProject(t, api.mustDB(), api.Cache, key, key)

	w := sdk.Workflow{
		Name:       "workflow1",
		ProjectID:  proj.ID,
		ProjectKey: proj.Key,
		WorkflowData: sdk.WorkflowData{
			Node: sdk.Node{
				Name: "root",
				Type: sdk.NodeTypeFork,
			},
		},
	}
	require.NoError(t, workflow.Insert(context.TODO(), api.mustDB(), api.Cache, *proj, &w))

	_, jws, err := builtin.NewConsumer(context.TODO(), api.mustDB(), sdk.RandomString(10), sdk.RandomString(10), localConsumer, u.GetGroupIDs(),
		sdk.NewAuthConsumerScopeDetails(sdk.AuthConsumerScopeProject))

	chanMessageReceived := make(chan sdk.WebsocketEvent)
	chanMessageToSend := make(chan sdk.WebsocketFilter)

	client := cdsclient.New(cdsclient.Config{
		Host:                              tsURL,
		User:                              u.Username,
		InsecureSkipVerifyTLS:             true,
		BuitinConsumerAuthenticationToken: jws,
	})
	go client.WebsocketEventsListen(context.TODO(), chanMessageToSend, chanMessageReceived)

	chanMessageToSend <- sdk.WebsocketFilter{
		Type:              sdk.WebsocketFilterTypeWorkflow,
		ProjectKey:        key,
		WorkflowName:      w.Name,
		WorkflowRunNumber: 1,
	}
	// Waiting websocket to update filter
	time.Sleep(1 * time.Second)

	api.websocketBroker.messages <- sdk.Event{ProjectKey: "blabla", WorkflowName: "toto", EventType: "sdk.EventRunWorkflow", WorkflowRunNum: 1}
	api.websocketBroker.messages <- sdk.Event{ProjectKey: proj.Key, WorkflowName: w.Name, EventType: "sdk.EventRunWorkflow", WorkflowRunNum: 1}
	response := <-chanMessageReceived
	require.Equal(t, "OK", response.Status)
	require.Equal(t, response.Event.EventType, "sdk.EventRunWorkflow")
	require.Equal(t, response.Event.ProjectKey, proj.Key)
	require.Equal(t, response.Event.WorkflowName, w.Name)
	require.Equal(t, 0, len(chanMessageReceived))
}

func Test_websocketDeconnection(t *testing.T) {
	api, tsURL, tsClose := newTestServer(t)
	defer tsClose()

	u, _ := assets.InsertAdminUser(t, api.mustDB())
	localConsumer, err := authentication.LoadConsumerByTypeAndUserID(context.TODO(), api.mustDB(), sdk.ConsumerLocal, u.ID, authentication.LoadConsumerOptions.WithAuthentifiedUser)
	require.NoError(t, err)

	key := sdk.RandomString(10)
	proj := assets.InsertTestProject(t, api.mustDB(), api.Cache, key, key)

	w := sdk.Workflow{
		Name:       "workflow1",
		ProjectID:  proj.ID,
		ProjectKey: proj.Key,
		WorkflowData: sdk.WorkflowData{
			Node: sdk.Node{
				Name: "root",
				Type: sdk.NodeTypeFork,
			},
		},
	}
	require.NoError(t, workflow.Insert(context.TODO(), api.mustDB(), api.Cache, *proj, &w))

	_, jws, err := builtin.NewConsumer(context.TODO(), api.mustDB(), sdk.RandomString(10), sdk.RandomString(10), localConsumer, u.GetGroupIDs(),
		sdk.NewAuthConsumerScopeDetails(sdk.AuthConsumerScopeProject))

	// Open websocket
	client := cdsclient.New(cdsclient.Config{
		Host:                              tsURL,
		User:                              u.Username,
		InsecureSkipVerifyTLS:             true,
		BuitinConsumerAuthenticationToken: jws,
	})
	resp, err := client.AuthConsumerSignin(sdk.ConsumerBuiltin, sdk.AuthConsumerSigninRequest{"token": jws})
	require.NoError(t, err)
	token := resp.Token

	uHost, err := url.Parse(tsURL)
	require.NoError(t, err)
	urlWebsocket := url.URL{
		Scheme: strings.Replace(uHost.Scheme, "http", "ws", -1),
		Host:   uHost.Host,
		Path:   "/ws",
	}
	headers := make(map[string][]string)
	date := sdk.FormatDateRFC5322(time.Now())
	headers["Date"] = []string{date}
	headers["X-CDS-RemoteTime"] = []string{date}
	auth := "Bearer " + token
	headers["Authorization"] = []string{auth}
	con, _, err := client.HTTPWebsocketClient().Dial(urlWebsocket.String(), headers)
	require.NoError(t, err)
	defer con.Close() // nolint

	// Waiting the websocket add the client
	time.Sleep(1 * time.Second)

	// Send filter
	err = con.WriteJSON(sdk.WebsocketFilter{
		Type:         sdk.WebsocketFilterTypeWorkflow,
		ProjectKey:   key,
		WorkflowName: w.Name,
	})
	require.NoError(t, err)

	// Waiting websocket to update filter
	time.Sleep(1 * time.Second)

	// Send message to client
	go func() {
		for i := 0; i < 100; i++ {
			api.websocketBroker.messages <- sdk.Event{ProjectKey: proj.Key, WorkflowName: w.Name, EventType: "sdk.EventWorkflow"}
			time.Sleep(200 * time.Millisecond)
		}
	}()
	// Kill client
	con.Close()

	time.Sleep(1 * time.Second)

	require.Equal(t, len(api.websocketBroker.clients), 0)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"net/http"

	log "github.com/Sirupsen/logrus"
)

const runURL = "http://golang.org/compile?output=json"

func init() {
	http.HandleFunc("/compile", compile)
}

func compile(w http.ResponseWriter, r *http.Request) {
	if err := passThru(w, r); err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, "Compile server error.")
	}
}

func passThru(w io.Writer, req *http.Request) error {
	defer req.Body.Close()
	r, err := http.Post(runUrl, req.Header.Get("Content-type"), req.Body)
	if err != nil {
		log.Errorf("making POST request: %v", err)
		return err
	}
	defer r.Body.Close()
	if _, err := io.Copy(w, r.Body); err != nil {
		log.Errorf("copying response Body: %v", err)
		return err
	}
	return nil
}
<commit_msg>fix golint<commit_after>package main

import (
	"fmt"
	"io"
	"net/http"

	log "github.com/Sirupsen/logrus"
)

const runURL = "http://golang.org/compile?output=json"

func init() {
	http.HandleFunc("/compile", compile)
}

func compile(w http.ResponseWriter, r *http.Request) {
	if err := passThru(w, r); err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, "Compile server error.")
	}
}

func passThru(w io.Writer, req *http.Request) error {
	defer req.Body.Close()
	r, err := http.Post(runURL, req.Header.Get("Content-type"), req.Body)
	if err != nil {
		log.Errorf("making POST request: %v", err)
		return err
	}
	defer r.Body.Close()
	if _, err := io.Copy(w, r.Body); err != nil {
		log.Errorf("copying response Body: %v", err)
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package hpcloud

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"strings"
)

/* Server flavours Smallest to Largest */
type Flavor int

const (
	XSmall = Flavor(100) + iota
	Small
	Medium
	Large
	XLarge
	DblXLarge
)

/* Available images */
type ServerImage int

const (
	UbuntuLucid10_04Kernel    = ServerImage(1235)
	UbuntuLucid10_04          = 1236
	UbuntuMaverick10_10Kernel = 1237
	UbuntuMaverick10_10       = 1238
	UbuntuNatty11_04Kernel    = 1239
	UbuntuNatty11_04          = 1240
	UbuntuOneiric11_10        = 5579
	UbuntuPrecise12_04        = 8419
	CentOS5_8Server64         = 54021
	CentOS6_2Server64Kernel   = 1356
	CentOS6_2Server64Ramdisk  = 1357
	CentOS6_2Server64         = 1358
	DebianSqueeze6_0_3Kernel  = 1359
	DebianSqueeze6_0_3Ramdisk = 1360
	DebianSqueeze6_0_3Server  = 1361
	Fedora16Server64          = 16291
	BitNamiDrupal7_14_0       = 22729
	BitNamiWebPack1_2_0       = 22731
	BitNamiDevPack1_0_0       = 4654
	ActiveStateStackatov1_2_6 = 14345
	ActiveStateStackatov2_2_2 = 59297
	ActiveStateStackatov2_2_3 = 60815
	EnterpriseDBPPAS9_1_2     = 9953
	EnterpriseDBPSQL9_1_3     = 9995
)

type Link struct {
	HREF string `json:"href"`
	Rel  string `json:"rel"`
}

/*
  Several embedded types are simply an ID string with a slice of Link
*/
type IDLink struct {
	Name  string `json:"name"`
	ID    string `json:"id"`
	Links []Link `json:"links"`
}

type Flavor_ struct {
	Name  string `json:"name"`
	ID    int64  `json:"id"`
	Links []Link `json:"links"`
}

type Flavors struct {
	F []Flavor_ `json:"flavors"`
}

type Image struct {
	I struct {
		Name     string            `json:"name"`
		ID       string            `json:"id"`
		Links    []Link            `json:"links"`
		Progress int               `json:"progress"`
		Metadata map[string]string `json:"metadata"`
		Status   string            `json:"status"`
		Updated  string            `json:"updated"`
	} `json:"image"`
}

type Images struct {
	I []IDLink `json:"images"`
}

/*
  This type describes the JSON data which should be sent to the create
  server resource.
*/
type Server struct {
	ConfigDrive    bool              `json:"config_drive"`
	FlavorRef      Flavor            `json:"flavorRef"`
	ImageRef       ServerImage       `json:"imageRef"`
	MaxCount       int               `json:"max_count"`
	MinCount       int               `json:"min_count"`
	Name           string            `json:"name"`
	Key            string            `json:"key_name"`
	Personality    string            `json:"personality"`
	UserData       string            `json:"user_data"`
	SecurityGroups []IDLink          `json:"security_groups"`
	Metadata       map[string]string `json:"metadata"`
}

/*
  This type describes the JSON response from a successful CreateServer
  call.
*/
type ServerResponse struct {
	S struct {
		Status         string            `json:"status"`
		Updated        string            `json:"update"`
		HostID         string            `json:"hostId"`
		UserID         string            `json:"user_id"`
		Name           string            `json:"name"`
		Links          []Link            `json:"links"`
		Addresses      interface{}       `json:"addresses"`
		TenantID       string            `json:"tenant_id"`
		Image          IDLink            `json:"image"`
		Created        string            `json:"created"`
		UUID           string            `json:"uuid"`
		AccessIPv4     string            `json:"accessIPv4"`
		AccessIPv6     string            `json:"accessIPv6"`
		KeyName        string            `json:"key_name"`
		AdminPass      string            `json:"adminPass"`
		Flavor         IDLink            `json:"flavor"`
		ConfigDrive    string            `json:"config_drive"`
		ID             int64             `json:"id"`
		SecurityGroups []IDLink          `json:"security_groups"`
		Metadata       map[string]string `json:"metadata"`
	} `json:"server"`
}

func (a Access) CreateServer(s Server) (*ServerResponse, error) {
	b, err := s.MarshalJSON()
	if err != nil {
		return nil, err
	}

	body, err := a.baseComputeRequest("servers", "POST",
		strings.NewReader(string(b)),
	)
	if err != nil {
		return nil, err
	}
	sr := &ServerResponse{}
	err = json.Unmarshal(body, sr)
	if err != nil {
		return nil, err
	}
	return sr, nil
}

func (a Access) DeleteServer(server_id string) error {
	_, err := a.baseComputeRequest(
		fmt.Sprintf("servers/%s", server_id),
		"DELETE", nil,
	)
	if err != nil {
		return err
	}
	return nil
}

func (a Access) RebootServer(server_id string) error {
	s := `{"reboot":{"type":"SOFT"}}`
	_, err := a.baseComputeRequest(
		fmt.Sprintf("servers/%s/action", server_id),
		"POST", strings.NewReader(s),
	)
	if err != nil {
		return err
	}
	return nil

}

func (a Access) ListFlavors() (*Flavors, error) {
	body, err := a.baseComputeRequest("flavors", "GET", nil)
	if err != nil {
		return nil, err
	}

	fl := &Flavors{}
	err = json.Unmarshal(body, fl)
	if err != nil {
		return nil, err
	}
	return fl, nil
}

func (a Access) ListImages() (*Images, error) {
	body, err := a.baseComputeRequest("images", "GET", nil)
	if err != nil {
		return nil, err
	}
	im := &Images{}
	err = json.Unmarshal(body, im)
	if err != nil {
		return nil, err
	}
	return im, nil
}

func (a Access) DeleteImage(image_id string) error {
	_, err := a.baseComputeRequest(
		fmt.Sprintf("images/%s", image_id), "DELETE", nil,
	)
	if err != nil {
		return err
	}
	return nil
}

func (a Access) ListImage(image_id string) (*Image, error) {
	body, err := a.baseComputeRequest(
		fmt.Sprintf("images/%s", image_id), "GET", nil,
	)
	if err != nil {
		return nil, err
	}
	i := &Image{}
	err = json.Unmarshal(body, i)
	if err != nil {
		return nil, err
	}
	return i, nil
}

func (a Access) baseComputeRequest(url, method string, b io.Reader) ([]byte, error) {
	path := fmt.Sprintf("%s%s/%s", COMPUTE_URL, a.TenantID, url)
	client := &http.Client{}
	req, err := http.NewRequest(method, path, b)
	if err != nil {
		return nil, err
	}
	req.Header.Add("X-Auth-Token", a.A.Token.ID)
	req.Header.Add("Content-type", "application/json")
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	switch resp.StatusCode {
	case http.StatusAccepted:
	case http.StatusNonAuthoritativeInfo:
	case http.StatusOK:
		return body, nil
	case http.StatusNotFound:
		nf := &NotFound{}
		err = json.Unmarshal(body, nf)
		if err != nil {
			return nil, err
		}
		return nil, errors.New(nf.NF.Message)
	default:
		br := &BadRequest{}
		err = json.Unmarshal(body, br)
		if err != nil {
			return nil, err
		}
		return nil, errors.New(br.B.Message)
	}
	panic("Unreachable")
}

func (s Server) MarshalJSON() ([]byte, error) {
	b := bytes.NewBufferString("")
	b.WriteString(`{"server":{`)
	/* The available images are 100-105, x-small to x-large. */
	if s.FlavorRef < 100 || s.FlavorRef > 105 {
		return []byte{},
			errors.New("Flavor Reference refers to a non-existant flavour.")
	} else {
		b.WriteString(fmt.Sprintf(`"flavorRef":%d`, s.FlavorRef))
	}
	if s.ImageRef == 0 {
		return []byte{},
			errors.New("An image name is required.")
	} else {
		b.WriteString(fmt.Sprintf(`,"imageRef":%d`, s.ImageRef))
	}
	if s.Name == "" {
		return []byte{},
			errors.New("A name is required")
	} else {
		b.WriteString(fmt.Sprintf(`,"name":"%s"`, s.Name))
	}

	/* Optional items */
	if s.Key != "" {
		b.WriteString(fmt.Sprintf(`,"key_name":"%s"`, s.Key))
	}
	if s.ConfigDrive {
		b.WriteString(`,"config_drive": true`)
	}
	if s.MinCount > 0 {
		b.WriteString(fmt.Sprintf(`,"min_count":%d`, s.MinCount))
	}
	if s.MaxCount > 0 {
		b.WriteString(fmt.Sprintf(`,"max_count":%d`, s.MaxCount))
	}
	if s.UserData != "" {
		/* user_data needs to be base64'd */
		newb := make([]byte, 0, len(s.UserData))
		base64.StdEncoding.Encode([]byte(s.UserData), newb)
		b.WriteString(fmt.Sprintf(`,"user_data": "%s",`, string(newb)))
	}
	if len(s.Personality) > 255 {
		return []byte{},
			errors.New("Server's personality cannot have >255 bytes.")
	} else if s.Personality != "" {
		b.WriteString(fmt.Sprintf(`,"personality":"%s",`, s.Personality))
	}
	if len(s.Metadata) > 0 {
		fmt.Println(len(s.Metadata))
		b.WriteString(`,"metadata":{`)
		cnt := 0
		for key, value := range s.Metadata {
			b.WriteString(fmt.Sprintf(`"%s": "%s"`, key, value))
			if cnt+1 != len(s.Metadata) {
				b.WriteString(",")
				cnt++
			} else {
				b.WriteString("}")
			}
		}
	}
	if len(s.SecurityGroups) > 0 {
		b.WriteString(`,"security_groups":[`)
		cnt := 0
		for _, sg := range s.SecurityGroups {
			b.WriteString(fmt.Sprintf(`{"name": "%s"}`, sg.Name))
			if cnt+1 != len(s.SecurityGroups) {
				b.WriteString(",")
				cnt++
			} else {
				b.WriteString("]")
			}
		}
	}
	b.WriteString("}}")
	return b.Bytes(), nil
}
<commit_msg>Comments.<commit_after>package hpcloud

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"strings"
)

/* Server flavours Smallest to Largest */
type Flavor int

const (
	XSmall = Flavor(100) + iota
	Small
	Medium
	Large
	XLarge
	DblXLarge
)

/* Available images */
type ServerImage int

const (
	UbuntuLucid10_04Kernel    = ServerImage(1235)
	UbuntuLucid10_04          = 1236
	UbuntuMaverick10_10Kernel = 1237
	UbuntuMaverick10_10       = 1238
	UbuntuNatty11_04Kernel    = 1239
	UbuntuNatty11_04          = 1240
	UbuntuOneiric11_10        = 5579
	UbuntuPrecise12_04        = 8419
	CentOS5_8Server64         = 54021
	CentOS6_2Server64Kernel   = 1356
	CentOS6_2Server64Ramdisk  = 1357
	CentOS6_2Server64         = 1358
	DebianSqueeze6_0_3Kernel  = 1359
	DebianSqueeze6_0_3Ramdisk = 1360
	DebianSqueeze6_0_3Server  = 1361
	Fedora16Server64          = 16291
	BitNamiDrupal7_14_0       = 22729
	BitNamiWebPack1_2_0       = 22731
	BitNamiDevPack1_0_0       = 4654
	ActiveStateStackatov1_2_6 = 14345
	ActiveStateStackatov2_2_2 = 59297
	ActiveStateStackatov2_2_3 = 60815
	EnterpriseDBPPAS9_1_2     = 9953
	EnterpriseDBPSQL9_1_3     = 9995
)

type Link struct {
	HREF string `json:"href"`
	Rel  string `json:"rel"`
}

/*
  Several embedded types are simply an ID string with a slice of Link
*/
type IDLink struct {
	Name  string `json:"name"`
	ID    string `json:"id"`
	Links []Link `json:"links"`
}

type Flavor_ struct {
	Name  string `json:"name"`
	ID    int64  `json:"id"`
	Links []Link `json:"links"`
}

type Flavors struct {
	F []Flavor_ `json:"flavors"`
}

type Image struct {
	I struct {
		Name     string            `json:"name"`
		ID       string            `json:"id"`
		Links    []Link            `json:"links"`
		Progress int               `json:"progress"`
		Metadata map[string]string `json:"metadata"`
		Status   string            `json:"status"`
		Updated  string            `json:"updated"`
	} `json:"image"`
}

type Images struct {
	I []IDLink `json:"images"`
}

/*
  This type describes the JSON data which should be sent to the create
  server resource.
*/
type Server struct {
	ConfigDrive    bool              `json:"config_drive"`
	FlavorRef      Flavor            `json:"flavorRef"`
	ImageRef       ServerImage       `json:"imageRef"`
	MaxCount       int               `json:"max_count"`
	MinCount       int               `json:"min_count"`
	Name           string            `json:"name"`
	Key            string            `json:"key_name"`
	Personality    string            `json:"personality"`
	UserData       string            `json:"user_data"`
	SecurityGroups []IDLink          `json:"security_groups"`
	Metadata       map[string]string `json:"metadata"`
}

/*
  This type describes the JSON response from a successful CreateServer
  call.
*/
type ServerResponse struct {
	S struct {
		Status         string            `json:"status"`
		Updated        string            `json:"update"`
		HostID         string            `json:"hostId"`
		UserID         string            `json:"user_id"`
		Name           string            `json:"name"`
		Links          []Link            `json:"links"`
		Addresses      interface{}       `json:"addresses"`
		TenantID       string            `json:"tenant_id"`
		Image          IDLink            `json:"image"`
		Created        string            `json:"created"`
		UUID           string            `json:"uuid"`
		AccessIPv4     string            `json:"accessIPv4"`
		AccessIPv6     string            `json:"accessIPv6"`
		KeyName        string            `json:"key_name"`
		AdminPass      string            `json:"adminPass"`
		Flavor         IDLink            `json:"flavor"`
		ConfigDrive    string            `json:"config_drive"`
		ID             int64             `json:"id"`
		SecurityGroups []IDLink          `json:"security_groups"`
		Metadata       map[string]string `json:"metadata"`
	} `json:"server"`
}

func (a Access) CreateServer(s Server) (*ServerResponse, error) {
	b, err := s.MarshalJSON()
	if err != nil {
		return nil, err
	}

	body, err := a.baseComputeRequest("servers", "POST",
		strings.NewReader(string(b)),
	)
	if err != nil {
		return nil, err
	}
	sr := &ServerResponse{}
	err = json.Unmarshal(body, sr)
	if err != nil {
		return nil, err
	}
	return sr, nil
}

func (a Access) DeleteServer(server_id string) error {
	_, err := a.baseComputeRequest(
		fmt.Sprintf("servers/%s", server_id),
		"DELETE", nil,
	)
	if err != nil {
		return err
	}
	return nil
}

func (a Access) RebootServer(server_id string) error {
	s := `{"reboot":{"type":"SOFT"}}`
	_, err := a.baseComputeRequest(
		fmt.Sprintf("servers/%s/action", server_id),
		"POST", strings.NewReader(s),
	)
	if err != nil {
		return err
	}
	return nil

}

func (a Access) ListFlavors() (*Flavors, error) {
	body, err := a.baseComputeRequest("flavors", "GET", nil)
	if err != nil {
		return nil, err
	}

	fl := &Flavors{}
	err = json.Unmarshal(body, fl)
	if err != nil {
		return nil, err
	}
	return fl, nil
}

func (a Access) ListImages() (*Images, error) {
	body, err := a.baseComputeRequest("images", "GET", nil)
	if err != nil {
		return nil, err
	}
	im := &Images{}
	err = json.Unmarshal(body, im)
	if err != nil {
		return nil, err
	}
	return im, nil
}

func (a Access) DeleteImage(image_id string) error {
	_, err := a.baseComputeRequest(
		fmt.Sprintf("images/%s", image_id), "DELETE", nil,
	)
	if err != nil {
		return err
	}
	return nil
}

func (a Access) ListImage(image_id string) (*Image, error) {
	body, err := a.baseComputeRequest(
		fmt.Sprintf("images/%s", image_id), "GET", nil,
	)
	if err != nil {
		return nil, err
	}
	i := &Image{}
	err = json.Unmarshal(body, i)
	if err != nil {
		return nil, err
	}
	return i, nil
}

func (a Access) baseComputeRequest(url, method string, b io.Reader) ([]byte, error) {
	path := fmt.Sprintf("%s%s/%s", COMPUTE_URL, a.TenantID, url)
	client := &http.Client{}
	req, err := http.NewRequest(method, path, b)
	if err != nil {
		return nil, err
	}
	req.Header.Add("X-Auth-Token", a.A.Token.ID)
	req.Header.Add("Content-type", "application/json")
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	switch resp.StatusCode {
	case http.StatusAccepted:
	case http.StatusNonAuthoritativeInfo:
	case http.StatusOK:
		return body, nil
	case http.StatusNotFound:
		nf := &NotFound{}
		err = json.Unmarshal(body, nf)
		if err != nil {
			return nil, err
		}
		return nil, errors.New(nf.NF.Message)
	default:
		br := &BadRequest{}
		err = json.Unmarshal(body, br)
		if err != nil {
			return nil, err
		}
		return nil, errors.New(br.B.Message)
	}
	panic("Unreachable")
}

func (s Server) MarshalJSON() ([]byte, error) {
	b := bytes.NewBufferString("")
	b.WriteString(`{"server":{`)
	/* The available images are 100-105, x-small to x-large. */
	if s.FlavorRef < 100 || s.FlavorRef > 105 {
		return []byte{},
			errors.New("Flavor Reference refers to a non-existant flavour.")
	} else {
		b.WriteString(fmt.Sprintf(`"flavorRef":%d`, s.FlavorRef))
	}
	if s.ImageRef == 0 {
		return []byte{},
			errors.New("An image name is required.")
	} else {
		b.WriteString(fmt.Sprintf(`,"imageRef":%d`, s.ImageRef))
	}
	if s.Name == "" {
		return []byte{},
			errors.New("A name is required")
	} else {
		b.WriteString(fmt.Sprintf(`,"name":"%s"`, s.Name))
	}

	/* Optional items */
	if s.Key != "" {
		b.WriteString(fmt.Sprintf(`,"key_name":"%s"`, s.Key))
	}
	if s.ConfigDrive {
		b.WriteString(`,"config_drive": true`)
	}
	if s.MinCount > 0 {
		b.WriteString(fmt.Sprintf(`,"min_count":%d`, s.MinCount))
	}
	if s.MaxCount > 0 {
		b.WriteString(fmt.Sprintf(`,"max_count":%d`, s.MaxCount))
	}
	if s.UserData != "" {
		/* user_data needs to be base64'd */
		newb := make([]byte, 0, len(s.UserData))
		base64.StdEncoding.Encode([]byte(s.UserData), newb)
		b.WriteString(fmt.Sprintf(`,"user_data": "%s",`, string(newb)))
	}
	/* The max size of a personality string is 255 bytes. */
	if len(s.Personality) > 255 {
		return []byte{},
			errors.New("Server's personality cannot have >255 bytes.")
	} else if s.Personality != "" {
		b.WriteString(fmt.Sprintf(`,"personality":"%s",`, s.Personality))
	}
	/* Ignore the metadata if there isn't any, it's optional. */
	if len(s.Metadata) > 0 {
		fmt.Println(len(s.Metadata))
		b.WriteString(`,"metadata":{`)
		cnt := 0
		for key, value := range s.Metadata {
			b.WriteString(fmt.Sprintf(`"%s": "%s"`, key, value))
			if cnt+1 != len(s.Metadata) {
				b.WriteString(",")
				cnt++
			} else {
				b.WriteString("}")
			}
		}
	}
	/* Ignore the Security Groups if there isn't any, it's optional. */
	if len(s.SecurityGroups) > 0 {
		b.WriteString(`,"security_groups":[`)
		cnt := 0
		for _, sg := range s.SecurityGroups {
			b.WriteString(fmt.Sprintf(`{"name": "%s"}`, sg.Name))
			if cnt+1 != len(s.SecurityGroups) {
				b.WriteString(",")
				cnt++
			} else {
				b.WriteString("]")
			}
		}
	}
	b.WriteString("}}")
	return b.Bytes(), nil
}
<|endoftext|>