<commit_before>package steam

import (
	"bytes"
	"compress/gzip"
	"crypto/rand"
	"encoding/binary"
	"fmt"
	"github.com/Philipp15b/go-steam/cryptoutil"
	. "github.com/Philipp15b/go-steam/internal"
	. "github.com/Philipp15b/go-steam/internal/protobuf"
	. "github.com/Philipp15b/go-steam/internal/steamlang"
	. "github.com/Philipp15b/go-steam/steamid"
	"hash/crc32"
	"io/ioutil"
	"log"
	"net"
	"sync"
	"sync/atomic"
	"time"
)

// Represents a client to the Steam network.
// Always poll events from the channel returned by Events() or receiving messages will stop.
// All access, unless otherwise noted, should be threadsafe.
//
// When a FatalErrorEvent is emitted, the connection is automatically closed. The same client can be used to reconnect.
// Other errors don't have any effect.
type Client struct {
	// these need to be 64 bit aligned for sync/atomic on 32bit
	sessionId    int32
	_            uint32
	steamId      uint64
	currentJobId uint64

	Auth          *Auth
	Social        *Social
	Web           *Web
	Notifications *Notifications
	Trading       *Trading
	GC            *GameCoordinator

	events        chan interface{}
	handlers      []PacketHandler
	handlersMutex sync.RWMutex

	tempSessionKey []byte

	ConnectionTimeout time.Duration

	mutex     sync.RWMutex // guarding conn and writeChan
	conn      connection
	writeChan chan IMsg
	writeBuf  *bytes.Buffer
	heartbeat *time.Ticker
}

type PacketHandler interface {
	HandlePacket(*Packet)
}

func NewClient() *Client {
	client := &Client{
		events:    make(chan interface{}, 3),
		writeChan: make(chan IMsg, 5),
		writeBuf:  new(bytes.Buffer),
	}
	client.Auth = &Auth{client: client}
	client.RegisterPacketHandler(client.Auth)
	client.Social = newSocial(client)
	client.RegisterPacketHandler(client.Social)
	client.Web = &Web{client: client}
	client.RegisterPacketHandler(client.Web)
	client.Notifications = newNotifications(client)
	client.RegisterPacketHandler(client.Notifications)
	client.Trading = &Trading{client: client}
	client.RegisterPacketHandler(client.Trading)
	client.GC = newGC(client)
	client.RegisterPacketHandler(client.GC)
	return client
}

// Get the event channel. By convention all events are pointers, except for errors.
// It is never closed.
func (c *Client) Events() <-chan interface{} {
	return c.events
}

func (c *Client) Emit(event interface{}) {
	c.events <- event
}

// When this event is emitted by the Client, the connection is automatically closed.
// This may be caused by a network error, for example.
type FatalErrorEvent error

// Emits a FatalErrorEvent formatted with fmt.Errorf and disconnects.
func (c *Client) Fatalf(format string, a ...interface{}) {
	c.Emit(FatalErrorEvent(fmt.Errorf(format, a...)))
	c.Disconnect()
}

// Emits an error formatted with fmt.Errorf.
func (c *Client) Errorf(format string, a ...interface{}) {
	c.Emit(fmt.Errorf(format, a...))
}

// Registers a PacketHandler that receives all incoming packets.
func (c *Client) RegisterPacketHandler(handler PacketHandler) {
	c.handlersMutex.Lock()
	defer c.handlersMutex.Unlock()
	c.handlers = append(c.handlers, handler)
}

func (c *Client) GetNextJobId() JobId {
	return JobId(atomic.AddUint64(&c.currentJobId, 1))
}

func (c *Client) SteamId() SteamId {
	return SteamId(atomic.LoadUint64(&c.steamId))
}

func (c *Client) SessionId() int32 {
	return atomic.LoadInt32(&c.sessionId)
}

func (c *Client) Connected() bool {
	c.mutex.RLock()
	defer c.mutex.RUnlock()
	return c.conn != nil
}

// Connects to a random server of the included list of connection managers and returns the address.
// If this client is already connected, it is disconnected first.
//
// You will receive a ServerListEvent after logging in which contains a new list of servers of which you
// should choose one yourself and connect with ConnectTo since the included list may not always be up to date.
func (c *Client) Connect() *PortAddr {
	server := GetRandomCM()
	c.ConnectTo(server)
	return server
}

// Connects to a specific server.
// If this client is already connected, it is disconnected first.
func (c *Client) ConnectTo(addr *PortAddr) {
	c.Disconnect()

	conn, err := dialTCP(addr.ToTCPAddr())
	if err != nil {
		log.Fatal(err)
	}
	c.conn = conn

	go c.readLoop()
	go c.writeLoop()
}

func (c *Client) Disconnect() {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	if c.conn == nil {
		return
	}

	c.conn.Close()
	c.conn = nil
	if c.heartbeat != nil {
		c.heartbeat.Stop()
	}
	close(c.writeChan)
}

// Adds a message to the send queue. Modifications to the given message after
// writing are not allowed (possible race conditions).
//
// Writes to this client when not connected are ignored.
func (c *Client) Write(msg IMsg) {
	if cm, ok := msg.(IClientMsg); ok {
		cm.SetSessionId(c.SessionId())
		cm.SetSteamId(c.SteamId())
	}
	c.mutex.RLock()
	defer c.mutex.RUnlock()
	if c.conn == nil {
		return
	}
	c.writeChan <- msg
}

func (c *Client) readLoop() {
	for {
		// This *should* be atomic on most platforms, but the Go spec doesn't guarantee it
		c.mutex.RLock()
		conn := c.conn
		c.mutex.RUnlock()
		if conn == nil {
			return
		}
		packet, err := conn.Read()

		if err != nil {
			c.Fatalf("Error reading from the connection: %v", err)
			return
		}
		c.handlePacket(packet)
	}
}

func (c *Client) writeLoop() {
	defer c.Disconnect()
	for {
		c.mutex.RLock()
		conn := c.conn
		c.mutex.RUnlock()
		if conn == nil {
			return
		}

		msg, ok := <-c.writeChan
		if !ok {
			return
		}

		err := msg.Serialize(c.writeBuf)
		if err != nil {
			c.writeBuf.Reset()
			c.Errorf("Error serializing message %v: %v", msg, err)
			return
		}

		err = conn.Write(c.writeBuf.Bytes())

		c.writeBuf.Reset()

		if err != nil {
			c.Errorf("Error writing message %v: %v", msg, err)
			return
		}
	}
}

func (c *Client) heartbeatLoop(seconds time.Duration) {
	if c.heartbeat != nil {
		c.heartbeat.Stop()
	}
	c.heartbeat = time.NewTicker(seconds * time.Second)
	for {
		_, ok := <-c.heartbeat.C
		if !ok {
			break
		}
		c.Write(NewClientMsgProtobuf(EMsg_ClientHeartBeat, new(CMsgClientHeartBeat)))
	}
	c.heartbeat = nil
}

func (c *Client) handlePacket(packet *Packet) {
	switch packet.EMsg {
	case EMsg_ChannelEncryptRequest:
		c.handleChannelEncryptRequest(packet)
	case EMsg_ChannelEncryptResult:
		c.handleChannelEncryptResult(packet)
	case EMsg_Multi:
		c.handleMulti(packet)
	}

	c.handlersMutex.RLock()
	defer c.handlersMutex.RUnlock()
	for _, handler := range c.handlers {
		handler.HandlePacket(packet)
	}
}

func (c *Client) handleChannelEncryptRequest(packet *Packet) {
	body := NewMsgChannelEncryptRequest()
	packet.ReadMsg(body)

	if body.Universe != EUniverse_Public {
		c.Fatalf("Invalid univserse %v!", body.Universe)
	}

	c.tempSessionKey = make([]byte, 32)
	rand.Read(c.tempSessionKey)
	encryptedKey := cryptoutil.RSAEncrypt(GetPublicKey(EUniverse_Public), c.tempSessionKey)

	payload := new(bytes.Buffer)
	payload.Write(encryptedKey)
	binary.Write(payload, binary.LittleEndian, crc32.ChecksumIEEE(encryptedKey))
	payload.WriteByte(0)
	payload.WriteByte(0)
	payload.WriteByte(0)
	payload.WriteByte(0)

	c.Write(NewMsg(NewMsgChannelEncryptResponse(), payload.Bytes()))
}

type ConnectedEvent struct{}

func (c *Client) handleChannelEncryptResult(packet *Packet) {
	body := NewMsgChannelEncryptResult()
	packet.ReadMsg(body)

	if body.Result != EResult_OK {
		c.Fatalf("Encryption failed: %v", body.Result)
		return
	}
	c.conn.SetEncryptionKey(c.tempSessionKey)
	c.tempSessionKey = nil

	c.Emit(new(ConnectedEvent))
}

func (c *Client) handleMulti(packet *Packet) {
	body := new(CMsgMulti)
	packet.ReadProtoMsg(body)

	payload := body.GetMessageBody()

	if body.GetSizeUnzipped() > 0 {
		r, err := gzip.NewReader(bytes.NewReader(payload))
		if err != nil {
			c.Errorf("handleMulti: Error while decompressing: %v", err)
			return
		}

		payload, err = ioutil.ReadAll(r)
		if err != nil {
			c.Errorf("handleMulti: Error while decompressing: %v", err)
			return
		}
	}

	pr := bytes.NewReader(payload)
	for pr.Len() > 0 {
		var length uint32
		binary.Read(pr, binary.LittleEndian, &length)
		packetData := make([]byte, length)
		pr.Read(packetData)
		p, err := NewPacket(packetData)
		if err != nil {
			c.Errorf("Error reading packet in Multi msg %v: %v", packet, err)
			continue
		}
		c.handlePacket(p)
	}
}
<commit_msg>Add ClientCMListEvent<commit_after>package steam

import (
	"bytes"
	"compress/gzip"
	"crypto/rand"
	"encoding/binary"
	"fmt"
	"github.com/Philipp15b/go-steam/cryptoutil"
	. "github.com/Philipp15b/go-steam/internal"
	. "github.com/Philipp15b/go-steam/internal/protobuf"
	. "github.com/Philipp15b/go-steam/internal/steamlang"
	. "github.com/Philipp15b/go-steam/steamid"
	"hash/crc32"
	"io/ioutil"
	"log"
	"net"
	"sync"
	"sync/atomic"
	"time"
)

// Represents a client to the Steam network.
// Always poll events from the channel returned by Events() or receiving messages will stop.
// All access, unless otherwise noted, should be threadsafe.
//
// When a FatalErrorEvent is emitted, the connection is automatically closed. The same client can be used to reconnect.
// Other errors don't have any effect.
type Client struct {
	// these need to be 64 bit aligned for sync/atomic on 32bit
	sessionId    int32
	_            uint32
	steamId      uint64
	currentJobId uint64

	Auth          *Auth
	Social        *Social
	Web           *Web
	Notifications *Notifications
	Trading       *Trading
	GC            *GameCoordinator

	events        chan interface{}
	handlers      []PacketHandler
	handlersMutex sync.RWMutex

	tempSessionKey []byte

	ConnectionTimeout time.Duration

	mutex     sync.RWMutex // guarding conn and writeChan
	conn      connection
	writeChan chan IMsg
	writeBuf  *bytes.Buffer
	heartbeat *time.Ticker
}

type PacketHandler interface {
	HandlePacket(*Packet)
}

func NewClient() *Client {
	client := &Client{
		events:    make(chan interface{}, 3),
		writeChan: make(chan IMsg, 5),
		writeBuf:  new(bytes.Buffer),
	}
	client.Auth = &Auth{client: client}
	client.RegisterPacketHandler(client.Auth)
	client.Social = newSocial(client)
	client.RegisterPacketHandler(client.Social)
	client.Web = &Web{client: client}
	client.RegisterPacketHandler(client.Web)
	client.Notifications = newNotifications(client)
	client.RegisterPacketHandler(client.Notifications)
	client.Trading = &Trading{client: client}
	client.RegisterPacketHandler(client.Trading)
	client.GC = newGC(client)
	client.RegisterPacketHandler(client.GC)
	return client
}

// Get the event channel. By convention all events are pointers, except for errors.
// It is never closed.
func (c *Client) Events() <-chan interface{} {
	return c.events
}

func (c *Client) Emit(event interface{}) {
	c.events <- event
}

// When this event is emitted by the Client, the connection is automatically closed.
// This may be caused by a network error, for example.
type FatalErrorEvent error

// Emits a FatalErrorEvent formatted with fmt.Errorf and disconnects.
func (c *Client) Fatalf(format string, a ...interface{}) {
	c.Emit(FatalErrorEvent(fmt.Errorf(format, a...)))
	c.Disconnect()
}

// Emits an error formatted with fmt.Errorf.
func (c *Client) Errorf(format string, a ...interface{}) {
	c.Emit(fmt.Errorf(format, a...))
}

// Registers a PacketHandler that receives all incoming packets.
func (c *Client) RegisterPacketHandler(handler PacketHandler) {
	c.handlersMutex.Lock()
	defer c.handlersMutex.Unlock()
	c.handlers = append(c.handlers, handler)
}

func (c *Client) GetNextJobId() JobId {
	return JobId(atomic.AddUint64(&c.currentJobId, 1))
}

func (c *Client) SteamId() SteamId {
	return SteamId(atomic.LoadUint64(&c.steamId))
}

func (c *Client) SessionId() int32 {
	return atomic.LoadInt32(&c.sessionId)
}

func (c *Client) Connected() bool {
	c.mutex.RLock()
	defer c.mutex.RUnlock()
	return c.conn != nil
}

// Connects to a random server of the included list of connection managers and returns the address.
// If this client is already connected, it is disconnected first.
//
// You will receive a ServerListEvent after logging in which contains a new list of servers of which you
// should choose one yourself and connect with ConnectTo since the included list may not always be up to date.
func (c *Client) Connect() *PortAddr {
	server := GetRandomCM()
	c.ConnectTo(server)
	return server
}

// Connects to a specific server.
// If this client is already connected, it is disconnected first.
func (c *Client) ConnectTo(addr *PortAddr) {
	c.Disconnect()

	conn, err := dialTCP(addr.ToTCPAddr())
	if err != nil {
		log.Fatal(err)
	}
	c.conn = conn

	go c.readLoop()
	go c.writeLoop()
}

func (c *Client) Disconnect() {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	if c.conn == nil {
		return
	}

	c.conn.Close()
	c.conn = nil
	if c.heartbeat != nil {
		c.heartbeat.Stop()
	}
	close(c.writeChan)
}

// Adds a message to the send queue. Modifications to the given message after
// writing are not allowed (possible race conditions).
//
// Writes to this client when not connected are ignored.
func (c *Client) Write(msg IMsg) {
	if cm, ok := msg.(IClientMsg); ok {
		cm.SetSessionId(c.SessionId())
		cm.SetSteamId(c.SteamId())
	}
	c.mutex.RLock()
	defer c.mutex.RUnlock()
	if c.conn == nil {
		return
	}
	c.writeChan <- msg
}

func (c *Client) readLoop() {
	for {
		// This *should* be atomic on most platforms, but the Go spec doesn't guarantee it
		c.mutex.RLock()
		conn := c.conn
		c.mutex.RUnlock()
		if conn == nil {
			return
		}
		packet, err := conn.Read()

		if err != nil {
			c.Fatalf("Error reading from the connection: %v", err)
			return
		}
		c.handlePacket(packet)
	}
}

func (c *Client) writeLoop() {
	defer c.Disconnect()
	for {
		c.mutex.RLock()
		conn := c.conn
		c.mutex.RUnlock()
		if conn == nil {
			return
		}

		msg, ok := <-c.writeChan
		if !ok {
			return
		}

		err := msg.Serialize(c.writeBuf)
		if err != nil {
			c.writeBuf.Reset()
			c.Errorf("Error serializing message %v: %v", msg, err)
			return
		}

		err = conn.Write(c.writeBuf.Bytes())

		c.writeBuf.Reset()

		if err != nil {
			c.Errorf("Error writing message %v: %v", msg, err)
			return
		}
	}
}

func (c *Client) heartbeatLoop(seconds time.Duration) {
	if c.heartbeat != nil {
		c.heartbeat.Stop()
	}
	c.heartbeat = time.NewTicker(seconds * time.Second)
	for {
		_, ok := <-c.heartbeat.C
		if !ok {
			break
		}
		c.Write(NewClientMsgProtobuf(EMsg_ClientHeartBeat, new(CMsgClientHeartBeat)))
	}
	c.heartbeat = nil
}

func (c *Client) handlePacket(packet *Packet) {
	switch packet.EMsg {
	case EMsg_ChannelEncryptRequest:
		c.handleChannelEncryptRequest(packet)
	case EMsg_ChannelEncryptResult:
		c.handleChannelEncryptResult(packet)
	case EMsg_Multi:
		c.handleMulti(packet)
	case EMsg_ClientCMList:
		c.handleClientCMList(packet)
	}

	c.handlersMutex.RLock()
	defer c.handlersMutex.RUnlock()
	for _, handler := range c.handlers {
		handler.HandlePacket(packet)
	}
}

func (c *Client) handleChannelEncryptRequest(packet *Packet) {
	body := NewMsgChannelEncryptRequest()
	packet.ReadMsg(body)

	if body.Universe != EUniverse_Public {
		c.Fatalf("Invalid univserse %v!", body.Universe)
	}

	c.tempSessionKey = make([]byte, 32)
	rand.Read(c.tempSessionKey)
	encryptedKey := cryptoutil.RSAEncrypt(GetPublicKey(EUniverse_Public), c.tempSessionKey)

	payload := new(bytes.Buffer)
	payload.Write(encryptedKey)
	binary.Write(payload, binary.LittleEndian, crc32.ChecksumIEEE(encryptedKey))
	payload.WriteByte(0)
	payload.WriteByte(0)
	payload.WriteByte(0)
	payload.WriteByte(0)

	c.Write(NewMsg(NewMsgChannelEncryptResponse(), payload.Bytes()))
}

type ConnectedEvent struct{}

func (c *Client) handleChannelEncryptResult(packet *Packet) {
	body := NewMsgChannelEncryptResult()
	packet.ReadMsg(body)

	if body.Result != EResult_OK {
		c.Fatalf("Encryption failed: %v", body.Result)
		return
	}
	c.conn.SetEncryptionKey(c.tempSessionKey)
	c.tempSessionKey = nil

	c.Emit(new(ConnectedEvent))
}

func (c *Client) handleMulti(packet *Packet) {
	body := new(CMsgMulti)
	packet.ReadProtoMsg(body)

	payload := body.GetMessageBody()

	if body.GetSizeUnzipped() > 0 {
		r, err := gzip.NewReader(bytes.NewReader(payload))
		if err != nil {
			c.Errorf("handleMulti: Error while decompressing: %v", err)
			return
		}

		payload, err = ioutil.ReadAll(r)
		if err != nil {
			c.Errorf("handleMulti: Error while decompressing: %v", err)
			return
		}
	}

	pr := bytes.NewReader(payload)
	for pr.Len() > 0 {
		var length uint32
		binary.Read(pr, binary.LittleEndian, &length)
		packetData := make([]byte, length)
		pr.Read(packetData)
		p, err := NewPacket(packetData)
		if err != nil {
			c.Errorf("Error reading packet in Multi msg %v: %v", packet, err)
			continue
		}
		c.handlePacket(p)
	}
}

// A list of connection manager addresses to connect to in the future.
// You should always save them and then select one of these
// instead of the builtin ones for the next connection.
type ClientCMListEvent struct {
	Addresses []*PortAddr
}

func (c *Client) handleClientCMList(packet *Packet) {
	body := new(CMsgClientCMList)
	packet.ReadProtoMsg(body)

	l := make([]*PortAddr, 0)
	for i, ip := range body.GetCmAddresses() {
		l = append(l, &PortAddr{
			readIp(ip),
			uint16(body.GetCmPorts()[i]),
		})
	}

	c.Emit(&ClientCMListEvent{l})
}

func readIp(ip uint32) net.IP {
	r := make(net.IP, 4)
	r[3] = byte(ip)
	r[2] = byte(ip >> 8)
	r[1] = byte(ip >> 16)
	r[0] = byte(ip >> 24)
	return r
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"strings"
	"time"

	"github.com/jasonpuglisi/ircutil"
)

// main requests a server and user which it uses establish a connection. It
// runs a loop to keep the client alive until it is no longer active.
func main() {
	// Set config and debug flags, then parse command line arguments.
	configPtr := flag.String("config", "config.json", "configuration file")
	debugPtr := flag.Bool("debug", false, "debugging mode")
	flag.Parse()

	// Get configuration from filename.
	config, err := getConfig(*configPtr)
	if err != nil {
		fmt.Printf("Error opening %s, make sure the file exists.\n%s\n",
			*configPtr, err)
		return
	}

	// Declare slice to store clients.
	var clients []*ircutil.Client

	// Loop through all clients in config to establish their connections.
	for i := range config.Clients {
		client := &config.Clients[i]

		// Get server from config and reference it in client.
		server, err := getServer(config, client.ServerID)
		if err != nil {
			fmt.Printf("Error getting server %s, make sure it exists in %s.\n%s\n",
				client.ServerID, *configPtr, err)
			return
		}
		client.Server = server

		// Get user from config and reference it in client.
		user, err := getUser(config, client.UserID)
		if err != nil {
			fmt.Printf("Error getting user %s, make sure it exists in %s.\n%s\n",
				client.UserID, *configPtr, err)
			return
		}
		client.User = user

		// Set debugging mode, ready function, and done channel for client.
		client.Debug = *debugPtr
		client.Ready = Init
		client.Done = make(chan bool, 1)

		// Establish a connection with the created client.
		err = ircutil.EstablishConnection(client)
		if err != nil {
			fmt.Printf("Error establishing connection with %s/%s, make sure its "+
				"settings are valid in %s.\n%s\n", client.ServerID, client.UserID,
				*configPtr, err)
			return
		}

		// Add client to client slice.
		clients = append(clients, client)

		// Sleep for a second so we don't hit the same server too fast.
		if i < len(config.Clients)-1 {
			time.Sleep(time.Second)
		}
	}

	// Loop until all clients are no longer active.
	for _, c := range clients {
		<-c.Done
	}
}

// Init is executed after the client it connected and registered to the server.
func Init(client *ircutil.Client) {
	// Join all of a client's channels.
	for i := range client.Channels {
		c := strings.Split(client.Channels[i], " ")
		pass := ""
		if len(c) > 1 {
			pass = c[1]
		}
		ircutil.SendJoin(client, c[0], pass)

		// Sleep for half a second so we don't join channels too fast.
		if i < len(client.Channels)-1 {
			time.Sleep(time.Millisecond * 500)
		}
	}
}
<commit_msg>Seed RNG, set initial nick in internal state, authenticate with nickserv, and update user modes<commit_after>package main

import (
	"flag"
	"fmt"
	"math/rand"
	"strings"
	"time"

	"github.com/jasonpuglisi/ircutil"
)

// main requests a server and user which it uses establish a connection. It
// runs a loop to keep the client alive until it is no longer active.
func main() {
	// Set config and debug flags, then parse command line arguments.
	configPtr := flag.String("config", "config.json", "configuration file")
	debugPtr := flag.Bool("debug", false, "debugging mode")
	flag.Parse()

	// Get configuration from filename.
	config, err := getConfig(*configPtr)
	if err != nil {
		fmt.Printf("Error opening %s, make sure the file exists.\n%s\n",
			*configPtr, err)
		return
	}

	// Seed random number generator.
	rand.Seed(time.Now().UnixNano())

	// Declare slice to store clients.
	var clients []*ircutil.Client

	// Loop through all clients in config to establish their connections.
	for i := range config.Clients {
		client := &config.Clients[i]

		// Get server from config and reference it in client.
		server, err := getServer(config, client.ServerID)
		if err != nil {
			fmt.Printf("Error getting server %s, make sure it exists in %s.\n%s\n",
				client.ServerID, *configPtr, err)
			return
		}
		client.Server = server

		// Get user from config and reference it in client.
		user, err := getUser(config, client.UserID)
		if err != nil {
			fmt.Printf("Error getting user %s, make sure it exists in %s.\n%s\n",
				client.UserID, *configPtr, err)
			return
		}
		client.User = user

		// Set debugging mode, ready function, and done channel for client.
		client.Debug = *debugPtr
		client.Ready = Init
		client.Done = make(chan bool, 1)
		client.Nick = client.User.Nick

		// Establish a connection with the created client.
		err = ircutil.EstablishConnection(client)
		if err != nil {
			fmt.Printf("Error establishing connection with %s/%s, make sure its "+
				"settings are valid in %s.\n%s\n", client.ServerID, client.UserID,
				*configPtr, err)
			return
		}

		// Add client to client slice.
		clients = append(clients, client)

		// Sleep for a second so we don't hit the same server too fast.
		if i < len(config.Clients)-1 {
			time.Sleep(time.Second)
		}
	}

	// Loop until all clients are no longer active.
	for _, c := range clients {
		<-c.Done
	}
}

// Init is executed after the client it connected and registered to the server.
func Init(client *ircutil.Client) {
	// Authenticate with Nickserv if a password is specified.
	if client.Nick == client.User.Nick && len(client.Authentication.Nickserv) >
		0 {
		ircutil.SendNickservPass(client, client.Authentication.Nickserv)
	}

	// Set user modes if specified.
	if len(client.Modes) > 0 {
		ircutil.SendModeUser(client, client.Modes)
	}

	// Join all of a client's channels.
	for i := range client.Channels {
		c := strings.Split(client.Channels[i], " ")
		pass := ""
		if len(c) > 1 {
			pass = c[1]
		}
		ircutil.SendJoin(client, c[0], pass)

		// Sleep for half a second so we don't join channels too fast.
		if i < len(client.Channels)-1 {
			time.Sleep(time.Millisecond * 500)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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.

// More information about Google Directions API is available on
// https://developers.google.com/maps/documentation/directions/

package maps // import "google.golang.org/maps"

import (
	"encoding/base64"
	"fmt"
	"net/http"
	"net/url"

	"google.golang.org/maps/internal"
)

// Client may be used to make requests to the Google Maps WebService APIs
type Client struct {
	httpClient *http.Client
	apiKey     string
	baseURL    string
	clientID   string
	signature  []byte
}

// ClientOption is the type of the options for NewClient.
type ClientOption func(*Client) error

// NewClient constructs a new Client which can make requests to the Google Maps WebService APIs.
// The supplied http.Client is used for making requests to the Maps WebService APIs
func NewClient(options ...ClientOption) (*Client, error) {
	c := &Client{}
	WithHTTPClient(&http.Client{})(c)
	for _, option := range options {
		err := option(c)
		if err != nil {
			return nil, err
		}
	}
	if c.apiKey == "" && (c.clientID == "" || len(c.signature) == 0) {
		return nil, fmt.Errorf("maps.Client with no API Key or credentials")
	}

	return c, nil
}

// WithHTTPClient configures a Maps API client with a http.Client to make requests over.
func WithHTTPClient(c *http.Client) ClientOption {
	return func(client *Client) error {
		if _, ok := c.Transport.(*transport); !ok {
			t := c.Transport
			if t != nil {
				c.Transport = &transport{Base: t}
			} else {
				c.Transport = &transport{Base: http.DefaultTransport}
			}
		}
		client.httpClient = c
		return nil
	}
}

// withBaseURL is for testing only.
func withBaseURL(url string) ClientOption {
	return func(client *Client) error {
		client.baseURL = url
		return nil
	}
}

// WithAPIKey configures a Maps API client with an API Key
func WithAPIKey(apiKey string) ClientOption {
	return func(client *Client) error {
		client.apiKey = apiKey
		return nil
	}
}

// WithClientIDAndSignature configures a Maps API client for a Maps for Work application
// The signature is assumed to be URL modified Base64 encoded
func WithClientIDAndSignature(clientID, signature string) ClientOption {
	return func(client *Client) error {
		client.clientID = clientID
		decoded, err := base64.URLEncoding.DecodeString(signature)
		if err != nil {
			return err
		}
		client.signature = decoded
		return nil
	}
}

func (client *Client) httpDo(req *http.Request) (*http.Response, error) {
	return client.httpClient.Do(req)
}

const userAgent = "GoogleGeoApiClientGo/0.1"

// Transport is an http.RoundTripper that appends
// Google Cloud client's user-agent to the original
// request's user-agent header.
type transport struct {
	// Base represents the actual http.RoundTripper
	// the requests will be delegated to.
	Base http.RoundTripper
}

// RoundTrip appends a user-agent to the existing user-agent
// header and delegates the request to the base http.RoundTripper.
func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) {
	req = cloneRequest(req)
	ua := req.Header.Get("User-Agent")
	if ua == "" {
		ua = userAgent
	} else {
		ua = fmt.Sprintf("%s;%s", ua, userAgent)
	}
	req.Header.Set("User-Agent", ua)
	return t.Base.RoundTrip(req)
}

// cloneRequest returns a clone of the provided *http.Request.
// The clone is a shallow copy of the struct and its Header map.
func cloneRequest(r *http.Request) *http.Request {
	// shallow copy of the struct
	r2 := new(http.Request)
	*r2 = *r
	// deep copy of the Header
	r2.Header = make(http.Header)
	for k, s := range r.Header {
		r2.Header[k] = s
	}
	return r2
}

func (client *Client) generateAuthQuery(path string, q url.Values, acceptClientID bool) (string, error) {
	if client.apiKey != "" {
		q.Set("key", client.apiKey)
		return q.Encode(), nil
	}
	if acceptClientID {
		query, err := internal.SignURL(path, client.clientID, client.signature, q)
		if err != nil {
			return "", err
		}
		return query, nil
	}
	return "", fmt.Errorf("Must provide API key for this API. It does not accept enterprise credentials.")
}
<commit_msg>Unexporting clientOption<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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.

// More information about Google Directions API is available on
// https://developers.google.com/maps/documentation/directions/

package maps // import "google.golang.org/maps"

import (
	"encoding/base64"
	"fmt"
	"net/http"
	"net/url"

	"google.golang.org/maps/internal"
)

// Client may be used to make requests to the Google Maps WebService APIs
type Client struct {
	httpClient *http.Client
	apiKey     string
	baseURL    string
	clientID   string
	signature  []byte
}

//
type clientOption func(*Client) error

// NewClient constructs a new Client which can make requests to the Google Maps WebService APIs.
// The supplied http.Client is used for making requests to the Maps WebService APIs
func NewClient(options ...clientOption) (*Client, error) {
	c := &Client{}
	WithHTTPClient(&http.Client{})(c)
	for _, option := range options {
		err := option(c)
		if err != nil {
			return nil, err
		}
	}
	if c.apiKey == "" && (c.clientID == "" || len(c.signature) == 0) {
		return nil, fmt.Errorf("maps.Client with no API Key or Maps for Work credentials")
	}

	return c, nil
}

// WithHTTPClient configures a Maps API client with a http.Client to make requests over.
func WithHTTPClient(c *http.Client) clientOption {
	return func(client *Client) error {
		if _, ok := c.Transport.(*transport); !ok {
			t := c.Transport
			if t != nil {
				c.Transport = &transport{Base: t}
			} else {
				c.Transport = &transport{Base: http.DefaultTransport}
			}
		}
		client.httpClient = c
		return nil
	}
}

// withBaseURL is for testing only.
func withBaseURL(url string) clientOption {
	return func(client *Client) error {
		client.baseURL = url
		return nil
	}
}

// WithAPIKey configures a Maps API client with an API Key
func WithAPIKey(apiKey string) clientOption {
	return func(client *Client) error {
		client.apiKey = apiKey
		return nil
	}
}

// WithClientIDAndSignature configures a Maps API client for a Maps for Work application
// The signature is assumed to be URL modified Base64 encoded
func WithClientIDAndSignature(clientID, signature string) clientOption {
	return func(client *Client) error {
		client.clientID = clientID
		decoded, err := base64.URLEncoding.DecodeString(signature)
		if err != nil {
			return err
		}
		client.signature = decoded
		return nil
	}
}

func (client *Client) httpDo(req *http.Request) (*http.Response, error) {
	return client.httpClient.Do(req)
}

const userAgent = "GoogleGeoApiClientGo/0.1"

// Transport is an http.RoundTripper that appends
// Google Cloud client's user-agent to the original
// request's user-agent header.
type transport struct {
	// Base represents the actual http.RoundTripper
	// the requests will be delegated to.
	Base http.RoundTripper
}

// RoundTrip appends a user-agent to the existing user-agent
// header and delegates the request to the base http.RoundTripper.
func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) {
	req = cloneRequest(req)
	ua := req.Header.Get("User-Agent")
	if ua == "" {
		ua = userAgent
	} else {
		ua = fmt.Sprintf("%s;%s", ua, userAgent)
	}
	req.Header.Set("User-Agent", ua)
	return t.Base.RoundTrip(req)
}

// cloneRequest returns a clone of the provided *http.Request.
// The clone is a shallow copy of the struct and its Header map.
func cloneRequest(r *http.Request) *http.Request {
	// shallow copy of the struct
	r2 := new(http.Request)
	*r2 = *r
	// deep copy of the Header
	r2.Header = make(http.Header)
	for k, s := range r.Header {
		r2.Header[k] = s
	}
	return r2
}

func (client *Client) generateAuthQuery(path string, q url.Values, acceptClientID bool) (string, error) {
	if client.apiKey != "" {
		q.Set("key", client.apiKey)
		return q.Encode(), nil
	}
	if acceptClientID {
		query, err := internal.SignURL(path, client.clientID, client.signature, q)
		if err != nil {
			return "", err
		}
		return query, nil
	}
	return "", fmt.Errorf("Must provide API key for this API. It does not accept enterprise credentials.")
}
<|endoftext|>


<commit_before>// client.go - Katzenpost client library
// Copyright (C) 2018  David Stainton.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// Package client provides a Katzenpost client library.
package client

import (
	"path/filepath"
	"sync"

	"github.com/katzenpost/client/config"
	"github.com/katzenpost/client/session"
	"github.com/katzenpost/core/log"
	"github.com/katzenpost/core/utils"
	"gopkg.in/op/go-logging.v1"
)

// Client handles sending and receiving messages over the mix network
type Client struct {
	cfg        *config.Config
	logBackend *log.Backend
	log        *logging.Logger
	fatalErrCh chan error
	haltedCh   chan interface{}
	haltOnce   *sync.Once

	session *session.Session
}

func (c *Client) initLogging() error {
	f := c.cfg.Logging.File
	if !c.cfg.Logging.Disable && c.cfg.Logging.File != "" {
		if !filepath.IsAbs(f) {
			f = filepath.Join(c.cfg.Proxy.DataDir, f)
		}
	}

	var err error
	c.logBackend, err = log.New(f, c.cfg.Logging.Level, c.cfg.Logging.Disable)
	if err == nil {
		c.log = c.logBackend.GetLogger("katzenpost/client")
	}
	return err
}

func (c *Client) GetLogger(name string) *logging.Logger {
	return c.logBackend.GetLogger(name)
}

// Shutdown cleanly shuts down a given Client instance.
func (c *Client) Shutdown() {
	c.haltOnce.Do(func() { c.halt() })
}

// Wait waits till the Client is terminated for any reason.
func (c *Client) Wait() {
	<-c.haltedCh
}

func (c *Client) halt() {
	c.log.Noticef("Starting graceful shutdown.")
	if c.session != nil {
		c.session.Halt()
	}
	close(c.fatalErrCh)
	close(c.haltedCh)
}

func (c *Client) NewSession() (*session.Session, error) {
	var err error
	c.session, err = session.New(c.fatalErrCh, c.logBackend, c.cfg)
	return c.session, err
}

// New creates a new Client with the provided configuration.
func New(cfg *config.Config) (*Client, error) {
	c := new(Client)
	c.cfg = cfg
	c.fatalErrCh = make(chan error)
	c.haltedCh = make(chan interface{})
	c.haltOnce = new(sync.Once)

	// Do the early initialization and bring up logging.
	if err := utils.MkDataDir(c.cfg.Proxy.DataDir); err != nil {
		return nil, err
	}
	if err := c.initLogging(); err != nil {
		return nil, err
	}

	// Ensure we generate keys if the user requested it.
	if c.cfg.Debug.GenerateOnly {
		err := config.GenerateKeys(c.cfg)
		return nil, err
	}

	c.log.Noticef("😼 Katzenpost is still pre-alpha.  DO NOT DEPEND ON IT FOR STRONG SECURITY OR ANONYMITY. 😼")

	// Start the fatal error watcher.
	go func() {
		err, ok := <-c.fatalErrCh
		if !ok {
			return
		}
		c.log.Warningf("Shutting down due to error: %v", err)
		c.Shutdown()
	}()
	return c, nil
}
<commit_msg>golint<commit_after>// client.go - Katzenpost client library
// Copyright (C) 2018  David Stainton.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// Package client provides a Katzenpost client library.
package client

import (
	"path/filepath"
	"sync"

	"github.com/katzenpost/client/config"
	"github.com/katzenpost/client/session"
	"github.com/katzenpost/core/log"
	"github.com/katzenpost/core/utils"
	"gopkg.in/op/go-logging.v1"
)

// Client handles sending and receiving messages over the mix network
type Client struct {
	cfg        *config.Config
	logBackend *log.Backend
	log        *logging.Logger
	fatalErrCh chan error
	haltedCh   chan interface{}
	haltOnce   *sync.Once

	session *session.Session
}

func (c *Client) initLogging() error {
	f := c.cfg.Logging.File
	if !c.cfg.Logging.Disable && c.cfg.Logging.File != "" {
		if !filepath.IsAbs(f) {
			f = filepath.Join(c.cfg.Proxy.DataDir, f)
		}
	}

	var err error
	c.logBackend, err = log.New(f, c.cfg.Logging.Level, c.cfg.Logging.Disable)
	if err == nil {
		c.log = c.logBackend.GetLogger("katzenpost/client")
	}
	return err
}

// GetLogger returns a new logger with the given name.
func (c *Client) GetLogger(name string) *logging.Logger {
	return c.logBackend.GetLogger(name)
}

// Shutdown cleanly shuts down a given Client instance.
func (c *Client) Shutdown() {
	c.haltOnce.Do(func() { c.halt() })
}

// Wait waits till the Client is terminated for any reason.
func (c *Client) Wait() {
	<-c.haltedCh
}

func (c *Client) halt() {
	c.log.Noticef("Starting graceful shutdown.")
	if c.session != nil {
		c.session.Halt()
	}
	close(c.fatalErrCh)
	close(c.haltedCh)
}

// NewSession creates and returns a new session or an error.
func (c *Client) NewSession() (*session.Session, error) {
	var err error
	c.session, err = session.New(c.fatalErrCh, c.logBackend, c.cfg)
	return c.session, err
}

// New creates a new Client with the provided configuration.
func New(cfg *config.Config) (*Client, error) {
	c := new(Client)
	c.cfg = cfg
	c.fatalErrCh = make(chan error)
	c.haltedCh = make(chan interface{})
	c.haltOnce = new(sync.Once)

	// Do the early initialization and bring up logging.
	if err := utils.MkDataDir(c.cfg.Proxy.DataDir); err != nil {
		return nil, err
	}
	if err := c.initLogging(); err != nil {
		return nil, err
	}

	// Ensure we generate keys if the user requested it.
	if c.cfg.Debug.GenerateOnly {
		err := config.GenerateKeys(c.cfg)
		return nil, err
	}

	c.log.Noticef("😼 Katzenpost is still pre-alpha.  DO NOT DEPEND ON IT FOR STRONG SECURITY OR ANONYMITY. 😼")

	// Start the fatal error watcher.
	go func() {
		err, ok := <-c.fatalErrCh
		if !ok {
			return
		}
		c.log.Warningf("Shutting down due to error: %v", err)
		c.Shutdown()
	}()
	return c, nil
}
<|endoftext|>


<commit_before>package petrel

// Copyright (c) 2015-2016 Shawn Boyette <shawn@firepear.net>. All
// rights reserved.  Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

// This file implements the Petrel client.

import (
	"crypto/tls"
	"fmt"
	"net"
	"strconv"
	"time"
)

// Client is a Petrel client instance.
type Client struct {
	conn net.Conn
	// timeout length
	to time.Duration
	// HMAC key
	hk []byte
	// conn closed semaphore
	cc bool
}

// ClientConfig holds values to be passed to the client constructor.
type ClientConfig struct {
	// For Unix clients, Addr takes the form "/path/to/socket". For
	// TCP clients, it is either an IPv4 or IPv6 address followed by
	// the desired port number ("127.0.0.1:9090", "[::1]:9090").
	Addr string

	// Timeout is the number of milliseconds the client will wait
	// before timing out due to on a Dispatch() or Read()
	// call. Default (zero) is no timeout.
	Timeout int64

	//HMACKey is the secret key used to generate MACs for signing
	//and verifying messages. Default (nil) means MACs will not be
	//generated for messages sent, or expected for messages
	//received.
	HMACKey []byte
}

// TCPClient returns a Client which uses TCP.
func TCPClient(c *ClientConfig) (*Client, error) {
	conn, err := net.Dial("tcp", c.Addr)
	if err != nil {
		return nil, err
	}
	return newCommon(c, conn)
}

// TLSClient returns a Client which uses TLS + TCP.
func TLSClient(c *ClientConfig, t *tls.Config) (*Client, error) {
	conn, err := tls.Dial("tcp", c.Addr, t)
	if err != nil {
		return nil, err
	}
	return newCommon(c, conn)
}

// UnixClient returns a Client which uses Unix domain sockets.
func UnixClient(c *ClientConfig) (*Client, error) {
	conn, err := net.Dial("unix", c.Addr)
	if err != nil {
		return nil, err
	}
	return newCommon(c, conn)
}

func newCommon(c *ClientConfig, conn net.Conn) (*Client, error) {
	return &Client{conn, time.Duration(c.Timeout) * time.Millisecond, c.HMACKey}, nil
}

// Dispatch sends a request and returns the response.
func (c *Client) Dispatch(req []byte) ([]byte, error) {
	// TODO put check for closed conn here
	_, err := connWrite(c.conn, req, c.hk, c.to)
	if err != nil {
		return nil, err
	}
	resp, err := c.read()
	return resp, err
}

// read reads from the network.
func (c *Client) read() ([]byte, error) {
	resp, perr, _, err := connRead(c.conn, c.to, 0, c.hk)
	if err != nil {
		return nil, err
	}
	if perr != "" {
		return nil, perrs[perr]
	}
	// check for/handle remote-side error responses
	if len(resp) == 11 && resp[0] == 80 { // 11 bytes, starting with 'P'
		pp := string(resp[0:8])
		if pp == "PERRPERR" {
			code, err := strconv.Atoi(string(resp[8:11]))
			if err != nil {
				return []byte{255}, fmt.Errorf("request error: unknown code %d", code)
			}
			return []byte{255}, perrs[perrmap[code]]
		}
	}
	return resp, err
}

// Close closes the client's connection.
func (c *Client) Close() {
	c.cc = true
	c.conn.Close()
}
<commit_msg>Client conn close on petrel error is in, but not tested<commit_after>package petrel

// Copyright (c) 2015-2016 Shawn Boyette <shawn@firepear.net>. All
// rights reserved.  Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

// This file implements the Petrel client.

import (
	"crypto/tls"
	"fmt"
	"net"
	"strconv"
	"time"
)

// Client is a Petrel client instance.
type Client struct {
	conn net.Conn
	// timeout length
	to time.Duration
	// HMAC key
	hk []byte
	// conn closed semaphore
	cc bool
}

// ClientConfig holds values to be passed to the client constructor.
type ClientConfig struct {
	// For Unix clients, Addr takes the form "/path/to/socket". For
	// TCP clients, it is either an IPv4 or IPv6 address followed by
	// the desired port number ("127.0.0.1:9090", "[::1]:9090").
	Addr string

	// Timeout is the number of milliseconds the client will wait
	// before timing out due to on a Dispatch() or Read()
	// call. Default (zero) is no timeout.
	Timeout int64

	//HMACKey is the secret key used to generate MACs for signing
	//and verifying messages. Default (nil) means MACs will not be
	//generated for messages sent, or expected for messages
	//received.
	HMACKey []byte
}

// TCPClient returns a Client which uses TCP.
func TCPClient(c *ClientConfig) (*Client, error) {
	conn, err := net.Dial("tcp", c.Addr)
	if err != nil {
		return nil, err
	}
	return newCommon(c, conn)
}

// TLSClient returns a Client which uses TLS + TCP.
func TLSClient(c *ClientConfig, t *tls.Config) (*Client, error) {
	conn, err := tls.Dial("tcp", c.Addr, t)
	if err != nil {
		return nil, err
	}
	return newCommon(c, conn)
}

// UnixClient returns a Client which uses Unix domain sockets.
func UnixClient(c *ClientConfig) (*Client, error) {
	conn, err := net.Dial("unix", c.Addr)
	if err != nil {
		return nil, err
	}
	return newCommon(c, conn)
}

func newCommon(c *ClientConfig, conn net.Conn) (*Client, error) {
	return &Client{conn, time.Duration(c.Timeout) * time.Millisecond, c.HMACKey, false}, nil
}

// Dispatch sends a request and returns the response.
func (c *Client) Dispatch(req []byte) ([]byte, error) {
	// if a previous error closed the conn, refuse to do anything
	if c.cc == true {
		return nil, fmt.Errorf("the network connection is closed due to a previous error; please create a new Client.")
	}
	_, err := connWrite(c.conn, req, c.hk, c.to)
	if err != nil {
		return nil, err
	}
	resp, err := c.read()
	return resp, err
}

// read reads from the network.
func (c *Client) read() ([]byte, error) {
	resp, perr, _, err := connRead(c.conn, c.to, 0, c.hk)
	if err != nil {
		return nil, err
	}
	if perr != "" {
		return nil, perrs[perr]
	}
	// check for/handle remote-side error responses
	if len(resp) == 11 && resp[0] == 80 { // 11 bytes, starting with 'P'
		pp := string(resp[0:8])
		if pp == "PERRPERR" {
			code, err := strconv.Atoi(string(resp[8:11]))
			if code == 402 || code == 502 {
				c.Close()
			}
			if err != nil {
				return []byte{255}, fmt.Errorf("request error: unknown code %d", code)
			}
			return []byte{255}, perrs[perrmap[code]]
		}
	}
	return resp, err
}

// Close closes the client's connection.
func (c *Client) Close() {
	c.cc = true
	c.conn.Close()
}
<|endoftext|>


<commit_before>// The retryablehttp package provides a familiar HTTP client interface with
// automatic retries and exponential backoff. It is a thin wrapper over the
// standard net/http client library and exposes nearly the same public API.
// This makes retryablehttp very easy to drop into existing programs.
//
// retryablehttp performs automatic retries under certain conditions. Mainly, if
// an error is returned by the client (connection errors etc), or if a 500-range
// response is received, then a retry is invoked. Otherwise, the response is
// returned and left to the caller to interpret.
//
// The main difference from net/http is that requests which take a request body
// (POST/PUT et. al) require an io.ReadSeeker to be provided. This enables the
// request body to be "rewound" if the initial request fails so that the full
// request can be attempted again.
package retryablehttp

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"

	"github.com/hashicorp/go-cleanhttp"
)

var (
	// Default retry configuration
	defaultRetryWaitMin = 1 * time.Second
	defaultRetryWaitMax = 5 * time.Minute
	defaultRetryMax     = 32

	// defaultClient is used for performing requests without explicitly making
	// a new client. It is purposely private to avoid modifications.
	defaultClient = NewClient()

	// We need to consume response bodies to maintain http connections, but
	// limit the size we consume to respReadLimit.
	respReadLimit = int64(4096)
)

// LenReader is an interface implemented by many in-memory io.Reader's. Used
// for automatically sending the right Content-Length header when possible.
type LenReader interface {
	Len() int
}

// Request wraps the metadata needed to create HTTP requests.
type Request struct {
	// body is a seekable reader over the request body payload. This is
	// used to rewind the request data in between retries.
	body io.ReadSeeker

	// Embed an HTTP request directly. This makes a *Request act exactly
	// like an *http.Request so that all meta methods are supported.
	*http.Request
}

// NewRequest creates a new wrapped request.
func NewRequest(method, url string, body io.ReadSeeker) (*Request, error) {
	// Wrap the body in a noop ReadCloser if non-nil. This prevents the
	// reader from being closed by the HTTP client.
	var rcBody io.ReadCloser
	if body != nil {
		rcBody = ioutil.NopCloser(body)
	}

	// Make the request with the noop-closer for the body.
	httpReq, err := http.NewRequest(method, url, rcBody)
	if err != nil {
		return nil, err
	}

	// Check if we can set the Content-Length automatically.
	if lr, ok := body.(LenReader); ok {
		httpReq.ContentLength = int64(lr.Len())
	}

	return &Request{body, httpReq}, nil
}

// RequestLogHook allows a function to run before each retry. The HTTP
// request which will be made, and the retry number (0 for the initial
// request) are available to users. The internal logger is exposed to
// consumers.
type RequestLogHook func(*log.Logger, *http.Request, int)

// ResponseLogHook is like RequestLogHook, but allows running a function
// on each HTTP response. This function will be invoked at the end of
// every HTTP request executed, regardless of whether a subsequent retry
// needs to be performed or not. If the response body is read or closed
// from this method, this will affect the response returned from Do().
type ResponseLogHook func(*log.Logger, *http.Response)

// Client is used to make HTTP requests. It adds additional functionality
// like automatic retries to tolerate minor outages.
type Client struct {
	HTTPClient *http.Client // Internal HTTP client.
	Logger     *log.Logger  // Customer logger instance.

	RetryWaitMin time.Duration // Minimum time to wait
	RetryWaitMax time.Duration // Maximum time to wait
	RetryMax     int           // Maximum number of retries

	// RequestLogHook allows a user-supplied function to be called
	// before each retry.
	RequestLogHook RequestLogHook

	// ResponseLogHook allows a user-supplied function to be called
	// with the response from each HTTP request executed.
	ResponseLogHook ResponseLogHook

	// CheckRetry specifies a policy for handling retries. It is called
	// following each request with the response and error values returned by
	// the http.Client. If CheckRetry returns false, the Client stops retrying
	// and returns the response to the caller. If CheckRetry returns an error,
	// that error value is returned in lieu of the error from the request. The
	// Client will close any response body when retrying, but if the retry is
	// aborted it is up to the CheckResponse callback to properly close any
	// response body before returning.
	CheckRetry func(resp *http.Response, err error) (bool, error)
}

// NewClient creates a new Client with default settings.
func NewClient() *Client {
	return &Client{
		HTTPClient:   cleanhttp.DefaultClient(),
		Logger:       log.New(os.Stderr, "", log.LstdFlags),
		RetryWaitMin: defaultRetryWaitMin,
		RetryWaitMax: defaultRetryWaitMax,
		RetryMax:     defaultRetryMax,
	}
}

// DefaultRetryPolicy provides a default callback for Client.CheckRetry, which
// will retry on connection errors and server errors.
func DefaultRetryPolicy(resp *http.Response, err error) (bool, error) {
	if err != nil {
		return true, err
	}
	// Check the response code. We retry on 500-range responses to allow
	// the server time to recover, as 500's are typically not permanent
	// errors and may relate to outages on the server side. This will catch
	// invalid response codes as well, like 0 and 999.
	if resp.StatusCode == 0 || resp.StatusCode >= 500 {
		return true, nil
	}

	return false, nil
}

// Do wraps calling an HTTP method with retries.
func (c *Client) Do(req *Request) (*http.Response, error) {
	c.Logger.Printf("[DEBUG] %s %s", req.Method, req.URL)

	if c.CheckRetry == nil {
		c.CheckRetry = DefaultRetryPolicy
	}

	for i := 0; ; i++ {
		var code int // HTTP response code

		// Always rewind the request body when non-nil.
		if req.body != nil {
			if _, err := req.body.Seek(0, 0); err != nil {
				return nil, fmt.Errorf("failed to seek body: %v", err)
			}
		}

		if c.RequestLogHook != nil {
			c.RequestLogHook(c.Logger, req.Request, i)
		}

		// Attempt the request
		resp, err := c.HTTPClient.Do(req.Request)

		// Check if we should continue with retries.
		checkOK, checkErr := c.CheckRetry(resp, err)

		if err != nil {
			c.Logger.Printf("[ERR] %s %s request failed: %v", req.Method, req.URL, err)
		} else {
			// Call this here to maintain the behavior of logging all requests,
			// even if CheckRetry signals to stop.
			if c.ResponseLogHook != nil {
				// Call the response logger function if provided.
				c.ResponseLogHook(c.Logger, resp)
			}
		}

		// Now decide if we should continue.
		if !checkOK {
			if checkErr != nil {
				err = checkErr
			}
			return resp, err
		}

		// We're going to retry, consume any response to reuse the connection.
		if err == nil {
			c.drainBody(resp.Body)
		}

		remain := c.RetryMax - i
		if remain == 0 {
			break
		}
		wait := backoff(c.RetryWaitMin, c.RetryWaitMax, i)
		desc := fmt.Sprintf("%s %s", req.Method, req.URL)
		if code > 0 {
			desc = fmt.Sprintf("%s (status: %d)", desc, code)
		}
		c.Logger.Printf("[DEBUG] %s: retrying in %s (%d left)", desc, wait, remain)
		time.Sleep(wait)
	}

	// Return an error if we fall out of the retry loop
	return nil, fmt.Errorf("%s %s giving up after %d attempts",
		req.Method, req.URL, c.RetryMax+1)
}

// Try to read the response body so we can reuse this connection.
func (c *Client) drainBody(body io.ReadCloser) {
	defer body.Close()
	_, err := io.Copy(ioutil.Discard, io.LimitReader(body, respReadLimit))
	if err != nil {
		c.Logger.Printf("[ERR] error reading response body: %v", err)
	}
}

// Get is a shortcut for doing a GET request without making a new client.
func Get(url string) (*http.Response, error) {
	return defaultClient.Get(url)
}

// Get is a convenience helper for doing simple GET requests.
func (c *Client) Get(url string) (*http.Response, error) {
	req, err := NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}
	return c.Do(req)
}

// Head is a shortcut for doing a HEAD request without making a new client.
func Head(url string) (*http.Response, error) {
	return defaultClient.Head(url)
}

// Head is a convenience method for doing simple HEAD requests.
func (c *Client) Head(url string) (*http.Response, error) {
	req, err := NewRequest("HEAD", url, nil)
	if err != nil {
		return nil, err
	}
	return c.Do(req)
}

// Post is a shortcut for doing a POST request without making a new client.
func Post(url, bodyType string, body io.ReadSeeker) (*http.Response, error) {
	return defaultClient.Post(url, bodyType, body)
}

// Post is a convenience method for doing simple POST requests.
func (c *Client) Post(url, bodyType string, body io.ReadSeeker) (*http.Response, error) {
	req, err := NewRequest("POST", url, body)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", bodyType)
	return c.Do(req)
}

// PostForm is a shortcut to perform a POST with form data without creating
// a new client.
func PostForm(url string, data url.Values) (*http.Response, error) {
	return defaultClient.PostForm(url, data)
}

// PostForm is a convenience method for doing simple POST operations using
// pre-filled url.Values form data.
func (c *Client) PostForm(url string, data url.Values) (*http.Response, error) {
	return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
}

// backoff is used to calculate how long to sleep before retrying
// after observing failures. It takes the minimum/maximum wait time and
// iteration, and returns the duration to wait.
func backoff(min, max time.Duration, iter int) time.Duration {
	mult := math.Pow(2, float64(iter)) * float64(min)
	sleep := time.Duration(mult)
	if float64(sleep) != mult || sleep > max {
		sleep = max
	}
	return sleep
}
<commit_msg>more review changes<commit_after>// The retryablehttp package provides a familiar HTTP client interface with
// automatic retries and exponential backoff. It is a thin wrapper over the
// standard net/http client library and exposes nearly the same public API.
// This makes retryablehttp very easy to drop into existing programs.
//
// retryablehttp performs automatic retries under certain conditions. Mainly, if
// an error is returned by the client (connection errors etc), or if a 500-range
// response is received, then a retry is invoked. Otherwise, the response is
// returned and left to the caller to interpret.
//
// The main difference from net/http is that requests which take a request body
// (POST/PUT et. al) require an io.ReadSeeker to be provided. This enables the
// request body to be "rewound" if the initial request fails so that the full
// request can be attempted again.
package retryablehttp

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"

	"github.com/hashicorp/go-cleanhttp"
)

var (
	// Default retry configuration
	defaultRetryWaitMin = 1 * time.Second
	defaultRetryWaitMax = 5 * time.Minute
	defaultRetryMax     = 32

	// defaultClient is used for performing requests without explicitly making
	// a new client. It is purposely private to avoid modifications.
	defaultClient = NewClient()

	// We need to consume response bodies to maintain http connections, but
	// limit the size we consume to respReadLimit.
	respReadLimit = int64(4096)
)

// LenReader is an interface implemented by many in-memory io.Reader's. Used
// for automatically sending the right Content-Length header when possible.
type LenReader interface {
	Len() int
}

// Request wraps the metadata needed to create HTTP requests.
type Request struct {
	// body is a seekable reader over the request body payload. This is
	// used to rewind the request data in between retries.
	body io.ReadSeeker

	// Embed an HTTP request directly. This makes a *Request act exactly
	// like an *http.Request so that all meta methods are supported.
	*http.Request
}

// NewRequest creates a new wrapped request.
func NewRequest(method, url string, body io.ReadSeeker) (*Request, error) {
	// Wrap the body in a noop ReadCloser if non-nil. This prevents the
	// reader from being closed by the HTTP client.
	var rcBody io.ReadCloser
	if body != nil {
		rcBody = ioutil.NopCloser(body)
	}

	// Make the request with the noop-closer for the body.
	httpReq, err := http.NewRequest(method, url, rcBody)
	if err != nil {
		return nil, err
	}

	// Check if we can set the Content-Length automatically.
	if lr, ok := body.(LenReader); ok {
		httpReq.ContentLength = int64(lr.Len())
	}

	return &Request{body, httpReq}, nil
}

// RequestLogHook allows a function to run before each retry. The HTTP
// request which will be made, and the retry number (0 for the initial
// request) are available to users. The internal logger is exposed to
// consumers.
type RequestLogHook func(*log.Logger, *http.Request, int)

// ResponseLogHook is like RequestLogHook, but allows running a function
// on each HTTP response. This function will be invoked at the end of
// every HTTP request executed, regardless of whether a subsequent retry
// needs to be performed or not. If the response body is read or closed
// from this method, this will affect the response returned from Do().
type ResponseLogHook func(*log.Logger, *http.Response)

// CheckRetry specifies a policy for handling retries. It is called
// following each request with the response and error values returned by
// the http.Client. If CheckRetry returns false, the Client stops retrying
// and returns the response to the caller. If CheckRetry returns an error,
// that error value is returned in lieu of the error from the request. The
// Client will close any response body when retrying, but if the retry is
// aborted it is up to the CheckResponse callback to properly close any
// response body before returning.
type CheckRetry func(resp *http.Response, err error) (bool, error)

// Client is used to make HTTP requests. It adds additional functionality
// like automatic retries to tolerate minor outages.
type Client struct {
	HTTPClient *http.Client // Internal HTTP client.
	Logger     *log.Logger  // Customer logger instance.

	RetryWaitMin time.Duration // Minimum time to wait
	RetryWaitMax time.Duration // Maximum time to wait
	RetryMax     int           // Maximum number of retries

	// RequestLogHook allows a user-supplied function to be called
	// before each retry.
	RequestLogHook RequestLogHook

	// ResponseLogHook allows a user-supplied function to be called
	// with the response from each HTTP request executed.
	ResponseLogHook ResponseLogHook

	// CheckRetry specifies the policy for handling retries, and is called
	// after each request. The default policy is DefaultRetryPolicy.
	CheckRetry CheckRetry
}

// NewClient creates a new Client with default settings.
func NewClient() *Client {
	return &Client{
		HTTPClient:   cleanhttp.DefaultClient(),
		Logger:       log.New(os.Stderr, "", log.LstdFlags),
		RetryWaitMin: defaultRetryWaitMin,
		RetryWaitMax: defaultRetryWaitMax,
		RetryMax:     defaultRetryMax,
		CheckRetry:   DefaultRetryPolicy,
	}
}

// DefaultRetryPolicy provides a default callback for Client.CheckRetry, which
// will retry on connection errors and server errors.
func DefaultRetryPolicy(resp *http.Response, err error) (bool, error) {
	if err != nil {
		return true, err
	}
	// Check the response code. We retry on 500-range responses to allow
	// the server time to recover, as 500's are typically not permanent
	// errors and may relate to outages on the server side. This will catch
	// invalid response codes as well, like 0 and 999.
	if resp.StatusCode == 0 || resp.StatusCode >= 500 {
		return true, nil
	}

	return false, nil
}

// Do wraps calling an HTTP method with retries.
func (c *Client) Do(req *Request) (*http.Response, error) {
	c.Logger.Printf("[DEBUG] %s %s", req.Method, req.URL)

	for i := 0; ; i++ {
		var code int // HTTP response code

		// Always rewind the request body when non-nil.
		if req.body != nil {
			if _, err := req.body.Seek(0, 0); err != nil {
				return nil, fmt.Errorf("failed to seek body: %v", err)
			}
		}

		if c.RequestLogHook != nil {
			c.RequestLogHook(c.Logger, req.Request, i)
		}

		// Attempt the request
		resp, err := c.HTTPClient.Do(req.Request)

		// Check if we should continue with retries.
		checkOK, checkErr := c.CheckRetry(resp, err)

		if err != nil {
			c.Logger.Printf("[ERR] %s %s request failed: %v", req.Method, req.URL, err)
		} else {
			// Call this here to maintain the behavior of logging all requests,
			// even if CheckRetry signals to stop.
			if c.ResponseLogHook != nil {
				// Call the response logger function if provided.
				c.ResponseLogHook(c.Logger, resp)
			}
		}

		// Now decide if we should continue.
		if !checkOK {
			if checkErr != nil {
				err = checkErr
			}
			return resp, err
		}

		// We're going to retry, consume any response to reuse the connection.
		if err == nil {
			c.drainBody(resp.Body)
		}

		remain := c.RetryMax - i
		if remain == 0 {
			break
		}
		wait := backoff(c.RetryWaitMin, c.RetryWaitMax, i)
		desc := fmt.Sprintf("%s %s", req.Method, req.URL)
		if code > 0 {
			desc = fmt.Sprintf("%s (status: %d)", desc, code)
		}
		c.Logger.Printf("[DEBUG] %s: retrying in %s (%d left)", desc, wait, remain)
		time.Sleep(wait)
	}

	// Return an error if we fall out of the retry loop
	return nil, fmt.Errorf("%s %s giving up after %d attempts",
		req.Method, req.URL, c.RetryMax+1)
}

// Try to read the response body so we can reuse this connection.
func (c *Client) drainBody(body io.ReadCloser) {
	defer body.Close()
	_, err := io.Copy(ioutil.Discard, io.LimitReader(body, respReadLimit))
	if err != nil {
		c.Logger.Printf("[ERR] error reading response body: %v", err)
	}
}

// Get is a shortcut for doing a GET request without making a new client.
func Get(url string) (*http.Response, error) {
	return defaultClient.Get(url)
}

// Get is a convenience helper for doing simple GET requests.
func (c *Client) Get(url string) (*http.Response, error) {
	req, err := NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}
	return c.Do(req)
}

// Head is a shortcut for doing a HEAD request without making a new client.
func Head(url string) (*http.Response, error) {
	return defaultClient.Head(url)
}

// Head is a convenience method for doing simple HEAD requests.
func (c *Client) Head(url string) (*http.Response, error) {
	req, err := NewRequest("HEAD", url, nil)
	if err != nil {
		return nil, err
	}
	return c.Do(req)
}

// Post is a shortcut for doing a POST request without making a new client.
func Post(url, bodyType string, body io.ReadSeeker) (*http.Response, error) {
	return defaultClient.Post(url, bodyType, body)
}

// Post is a convenience method for doing simple POST requests.
func (c *Client) Post(url, bodyType string, body io.ReadSeeker) (*http.Response, error) {
	req, err := NewRequest("POST", url, body)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", bodyType)
	return c.Do(req)
}

// PostForm is a shortcut to perform a POST with form data without creating
// a new client.
func PostForm(url string, data url.Values) (*http.Response, error) {
	return defaultClient.PostForm(url, data)
}

// PostForm is a convenience method for doing simple POST operations using
// pre-filled url.Values form data.
func (c *Client) PostForm(url string, data url.Values) (*http.Response, error) {
	return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
}

// backoff is used to calculate how long to sleep before retrying
// after observing failures. It takes the minimum/maximum wait time and
// iteration, and returns the duration to wait.
func backoff(min, max time.Duration, iter int) time.Duration {
	mult := math.Pow(2, float64(iter)) * float64(min)
	sleep := time.Duration(mult)
	if float64(sleep) != mult || sleep > max {
		sleep = max
	}
	return sleep
}
<|endoftext|>


<commit_before>package service

import (
	"crypto/tls"
	"log"
	"net/rpc"
)

func ClientFromKeys(network, laddr, clientCrt, clientKey, caCrt string) (*rpc.Client, error) {
	cert, err := tls.LoadX509KeyPair(clientCrt, clientKey)
	if err != nil {
		return nil, err
	}
	/* XXX: Do Validation of the client cert, and ensure the extended client
	 *      usage bit is flipped, otherwise the server will choke on it */

	caPool, err := caFileToPool(caCrt)
	if err != nil {
		return nil, err
	}

	config := tls.Config{
		Certificates: []tls.Certificate{cert},
		ClientAuth:   tls.RequireAnyClientCert,
		RootCAs:      caPool,
	}

	conn, err := tls.Dial(network, laddr, &config)
	if err != nil {
		return nil, err
	}
	client := rpc.NewClient(conn)
	return client, nil
}
<commit_msg>Update<commit_after>package service

import (
	"crypto/tls"
	"net"
)

func DialFromKeys(network, laddr, clientCrt, clientKey, caCrt string) (net.Conn, error) {
	cert, err := tls.LoadX509KeyPair(clientCrt, clientKey)
	if err != nil {
		return nil, err
	}
	/* XXX: Do Validation of the client cert, and ensure the extended client
	 *      usage bit is flipped, otherwise the server will choke on it */

	caPool, err := caFileToPool(caCrt)
	if err != nil {
		return nil, err
	}

	config := tls.Config{
		Certificates: []tls.Certificate{cert},
		ClientAuth:   tls.RequireAnyClientCert,
		RootCAs:      caPool,
	}

	conn, err := tls.Dial(network, laddr, &config)
	if err != nil {
		return nil, err
	}
	return conn, nil
}
<|endoftext|>


<commit_before>package hawk

import (
	"crypto/rand"
	"encoding/hex"
	"strconv"
	"net/http"
	"strings"
	"errors"
	"regexp"
)

type Client struct {
	Credential *Credential
	Option     *Option
}

type Credential struct {
	ID  string
	Key string
	Alg Alg
}

type Option struct {
	TimeStamp   int64
	Nonce       string
	Payload     string
	ContentType string
	Hash        string
	Ext         string
	App         string
	Dlg         string
}

type Alg int

const (
	_ Alg = iota
	SHA256
	SHA512
)

func (c *Client) Header(uri string, method string) (string, error) {
	m := &Mac{
		Type: Header,
		Credential: c.Credential,
		Uri: uri,
		Method: method,
		Option: c.Option,
	}

	mac, err := m.String()
	if err != nil {
		return "", err
	}

	header := "Authorization: Hawk " +
		`id="` + c.Credential.ID + `"` +
		", " +
		`ts="` + strconv.FormatInt(c.Option.TimeStamp, 10) + `"` +
		", " +
		`nonce="` + c.Option.Nonce + `"`
	if c.Option.Hash != "" {
		header = header + ", " + `hash="` + c.Option.Hash + `"`
	}
	if c.Option.Ext != "" {
		header = header + ", " + `ext="` + c.Option.Ext + `"`
	}
	header = header + ", " + `mac="` + mac + `"`
	if c.Option.App != "" {
		header = header + ", " + `app="` + c.Option.App + `"`
		if c.Option.Dlg != "" {
			header = header + ", " + `dlg="` + c.Option.Dlg + `"`
		}
	}

	return header, nil
}

func (c *Client) Authenticate(res *http.Response) (bool, error) {
	artifacts := *c.Option

	wah := res.Header.Get("WWW-Authenticate")
	if wah != "" {
		// TODO: validate WWW-Authenticate Header
	}

	sah := res.Header.Get("Server-Authorization")
	serverAuthAttributes := parseHawkHeader(sah)

	artifacts.Ext = serverAuthAttributes["ext"]
	artifacts.Hash = serverAuthAttributes["hash"]

	m := &Mac{
		Type: Response,
		Credential: c.Credential,
		Uri: res.Request.URL.String(),
		Method: res.Request.Method,
		Option: &artifacts,
	}

	mac, err := m.String()
	if err != nil {
		return false, err
	}
	if mac != serverAuthAttributes["mac"] {
		return false, errors.New("Bad response mac")
	}

	if c.Option.Payload == "" {
		return false, nil
	}

	if serverAuthAttributes["hash"] == "" {
		return false, errors.New("Missing response hash attribute")
	}

	ph := &PayloadHash{
		ContentType: res.Header.Get("Content-Type"),
		Payload: c.Option.Payload,
		Alg: c.Credential.Alg,
	}
	if ph.String() != serverAuthAttributes["hash"] {
		return false, errors.New("Bad response payload mac")
	}

	return true, nil
}

func Nonce(n int) (string, error) {
	bytes := make([]byte, n)
	_, err := rand.Read(bytes)
	if err != nil {
		return "", err
	}
	return hex.EncodeToString(bytes), nil
}

func parseHawkHeader(headerVal string) map[string]string {
	attrs := make(map[string]string)

	hv  := strings.Split(strings.Split(headerVal, "Hawk ")[1], ", ")

	for _, v := range hv {
		r := regexp.MustCompile(`(\w+)="([^"\\]*)"\s*(?:,\s*|$)`)
		group := r.FindSubmatch([]byte(v))
		attrs[string(group[1])] = string(group[2])
	}

	return attrs
}<commit_msg>Cosme format<commit_after>package hawk

import (
	"crypto/rand"
	"encoding/hex"
	"errors"
	"net/http"
	"regexp"
	"strconv"
	"strings"
)

type Client struct {
	Credential *Credential
	Option     *Option
}

type Credential struct {
	ID  string
	Key string
	Alg Alg
}

type Option struct {
	TimeStamp   int64
	Nonce       string
	Payload     string
	ContentType string
	Hash        string
	Ext         string
	App         string
	Dlg         string
}

type Alg int

const (
	_ Alg = iota
	SHA256
	SHA512
)

func (c *Client) Header(uri string, method string) (string, error) {
	m := &Mac{
		Type:       Header,
		Credential: c.Credential,
		Uri:        uri,
		Method:     method,
		Option:     c.Option,
	}

	mac, err := m.String()
	if err != nil {
		return "", err
	}

	header := "Authorization: Hawk " +
		`id="` + c.Credential.ID + `"` +
		", " +
		`ts="` + strconv.FormatInt(c.Option.TimeStamp, 10) + `"` +
		", " +
		`nonce="` + c.Option.Nonce + `"`
	if c.Option.Hash != "" {
		header = header + ", " + `hash="` + c.Option.Hash + `"`
	}
	if c.Option.Ext != "" {
		header = header + ", " + `ext="` + c.Option.Ext + `"`
	}
	header = header + ", " + `mac="` + mac + `"`
	if c.Option.App != "" {
		header = header + ", " + `app="` + c.Option.App + `"`
		if c.Option.Dlg != "" {
			header = header + ", " + `dlg="` + c.Option.Dlg + `"`
		}
	}

	return header, nil
}

func (c *Client) Authenticate(res *http.Response) (bool, error) {
	artifacts := *c.Option

	wah := res.Header.Get("WWW-Authenticate")
	if wah != "" {
		// TODO: validate WWW-Authenticate Header
	}

	sah := res.Header.Get("Server-Authorization")
	serverAuthAttributes := parseHawkHeader(sah)

	artifacts.Ext = serverAuthAttributes["ext"]
	artifacts.Hash = serverAuthAttributes["hash"]

	m := &Mac{
		Type:       Response,
		Credential: c.Credential,
		Uri:        res.Request.URL.String(),
		Method:     res.Request.Method,
		Option:     &artifacts,
	}

	mac, err := m.String()
	if err != nil {
		return false, err
	}
	if mac != serverAuthAttributes["mac"] {
		return false, errors.New("Bad response mac")
	}

	if c.Option.Payload == "" {
		return false, nil
	}

	if serverAuthAttributes["hash"] == "" {
		return false, errors.New("Missing response hash attribute")
	}

	ph := &PayloadHash{
		ContentType: res.Header.Get("Content-Type"),
		Payload:     c.Option.Payload,
		Alg:         c.Credential.Alg,
	}
	if ph.String() != serverAuthAttributes["hash"] {
		return false, errors.New("Bad response payload mac")
	}

	return true, nil
}

func Nonce(n int) (string, error) {
	bytes := make([]byte, n)
	_, err := rand.Read(bytes)
	if err != nil {
		return "", err
	}
	return hex.EncodeToString(bytes), nil
}

func parseHawkHeader(headerVal string) map[string]string {
	attrs := make(map[string]string)

	hv := strings.Split(strings.Split(headerVal, "Hawk ")[1], ", ")

	for _, v := range hv {
		r := regexp.MustCompile(`(\w+)="([^"\\]*)"\s*(?:,\s*|$)`)
		group := r.FindSubmatch([]byte(v))
		attrs[string(group[1])] = string(group[2])
	}

	return attrs
}
<|endoftext|>


<commit_before>package turn

import (
	"context"
	"errors"
	"io"
	"net"
	"sync"
	"sync/atomic"
	"time"

	"github.com/gortc/stun"
)

// Client for TURN server.
//
// Provides transparent net.Conn interface to remote peer.
type Client struct {
	con  net.Conn
	stun STUNClient
}

type ClientOptions struct {
	Conn net.Conn
	STUN STUNClient // optional STUN client
}

func NewClient(o ClientOptions) (*Client, error) {
	if o.Conn == nil {
		return nil, errors.New("connection not provided")
	}
	if o.STUN == nil {
		var err error
		o.STUN, err = stun.NewClient(stun.ClientOptions{
			Connection: o.Conn,
		})
		if err != nil {
			return nil, err
		}
	}
	c := &Client{
		stun: o.STUN,
		con:  o.Conn,
	}
	return c, nil
}

// STUNClient abstracts STUN protocol interaction.
type STUNClient interface {
	Indicate(m *stun.Message) error
	Start(m *stun.Message, h stun.Handler) error
}

// HandleEvent implements stun.Handler.
func (c *Client) HandleEvent(e stun.Event) {
	panic("not implemented")
}

func (c *Client) sendData(buf []byte, peerAddr *PeerAddress) (int, error) {
	err := c.stun.Indicate(stun.MustBuild(stun.TransactionID,
		stun.NewType(stun.MethodSend, stun.ClassIndication),
		Data(buf), peerAddr,
	))
	if err == nil {
		return len(buf), nil
	}
	return 0, err
}

func (c *Client) sendChan(buf []byte, n ChannelNumber) (int, error) {
	if !n.Valid() {
		return 0, ErrInvalidChannelNumber
	}
	d := &ChannelData{
		Data:   buf,
		Number: n,
	}
	d.Encode()
	return c.con.Write(d.Raw)
}

func (c *Client) handleBinding(p *Permission, n ChannelNumber, f stun.Handler) error {
	return c.stun.Start(stun.MustBuild(stun.TransactionID,
		stun.NewType(stun.MethodSend, stun.ClassIndication),
	), f)
}

var ErrNotImplemented = errors.New("functionality not implemented")

// Permission. Implements net.PacketConn.
type Permission struct {
	mux          *sync.RWMutex
	binding      bool
	bindErr      error
	number       uint32
	c            *Client
	readDeadline time.Time
	peerData     chan []byte
}

// Read data from peer.
func (p *Permission) Read(b []byte) (n int, err error) {
	p.mux.Lock()
	deadline := p.readDeadline
	p.mux.Unlock()
	select {
	case <-time.After(time.Until(deadline)):
		return 0, errors.New("deadline reached")
	case d := <-p.peerData:
		if len(b) < len(d) {
			go func() {
				p.peerData <- d
			}()
			return 0, io.ErrShortBuffer
		}
		return copy(b, d), nil
	}
}

// Bound returns true if channel number is bound for current permission.
func (p *Permission) Bound() bool {
	return atomic.LoadUint32(&p.number) == 0
}

// Binding returns current channel number or 0 if not bound.
func (p *Permission) Binding() ChannelNumber {
	return ChannelNumber(atomic.LoadUint32(&p.number))
}

// ErrBindingInProgress means that previous binding transaction for selected permission
// is still in progress.
var ErrBindingInProgress = errors.New("binding in progress")

// ErrAlreadyBound means that selected permission already has bound channel number.
var ErrAlreadyBound = errors.New("channel already bound")

// Bind performs binding transaction, allocating channel binding for
// the permission.
//
// TODO: Handle ctx cancellation
// TODO: Start binding refresh cycle
func (p *Permission) Bind(ctx context.Context, n ChannelNumber) error {
	p.mux.Lock()
	defer p.mux.Unlock()

	if p.binding {
		return ErrBindingInProgress
	}
	p.binding = true
	if p.number != 0 {
		return ErrAlreadyBound
	}

	// Starting transaction.
	done := make(chan struct{})
	if err := p.c.handleBinding(p, n, func(e stun.Event) {
		p.bindErr = e.Error
		p.binding = false
		if e.Error == nil {
			atomic.StoreUint32(&p.number, uint32(n))
		}
		done <- struct{}{}
	}); err != nil {
		return err
	}

	// Waiting until transaction is done.
	select {
	case <-done:
		return p.bindErr
	case <-ctx.Done():
		go func() {
			<-done
		}()
		return ctx.Err()
	}
}

// Write sends buffer to peer.
//
// If permission is bound, the ChannelData message will be used.
func (p *Permission) Write(b []byte) (n int, err error) {
	if n := atomic.LoadUint32(&p.number); n != 0 {
		return p.c.sendChan(b, ChannelNumber(n))
	}
	return p.c.sendData(b, &PeerAddress{})
}

func (Permission) Close() error {
	return ErrNotImplemented
}

// LocalAddr is relayed address from TURN server.
func (Permission) LocalAddr() net.Addr {
	panic("implement me")
}

// RemoteAddr is peer address.
func (Permission) RemoteAddr() net.Addr {
	panic("implement me")
}

func (Permission) SetDeadline(t time.Time) error {
	return ErrNotImplemented
}

func (Permission) SetReadDeadline(t time.Time) error {
	return ErrNotImplemented
}

func (Permission) SetWriteDeadline(t time.Time) error {
	return ErrNotImplemented
}
<commit_msg>client: remove HandleEvent method<commit_after>package turn

import (
	"context"
	"errors"
	"io"
	"net"
	"sync"
	"sync/atomic"
	"time"

	"github.com/gortc/stun"
)

// Client for TURN server.
//
// Provides transparent net.Conn interface to remote peer.
type Client struct {
	con  net.Conn
	stun STUNClient
}

type ClientOptions struct {
	Conn net.Conn
	STUN STUNClient // optional STUN client
}

func NewClient(o ClientOptions) (*Client, error) {
	if o.Conn == nil {
		return nil, errors.New("connection not provided")
	}
	if o.STUN == nil {
		var err error
		o.STUN, err = stun.NewClient(stun.ClientOptions{
			Connection: o.Conn,
		})
		if err != nil {
			return nil, err
		}
	}
	c := &Client{
		stun: o.STUN,
		con:  o.Conn,
	}
	return c, nil
}

// STUNClient abstracts STUN protocol interaction.
type STUNClient interface {
	Indicate(m *stun.Message) error
	Start(m *stun.Message, h stun.Handler) error
}

func (c *Client) sendData(buf []byte, peerAddr *PeerAddress) (int, error) {
	err := c.stun.Indicate(stun.MustBuild(stun.TransactionID,
		stun.NewType(stun.MethodSend, stun.ClassIndication),
		Data(buf), peerAddr,
	))
	if err == nil {
		return len(buf), nil
	}
	return 0, err
}

func (c *Client) sendChan(buf []byte, n ChannelNumber) (int, error) {
	if !n.Valid() {
		return 0, ErrInvalidChannelNumber
	}
	d := &ChannelData{
		Data:   buf,
		Number: n,
	}
	d.Encode()
	return c.con.Write(d.Raw)
}

func (c *Client) handleBinding(p *Permission, n ChannelNumber, f stun.Handler) error {
	return c.stun.Start(stun.MustBuild(stun.TransactionID,
		stun.NewType(stun.MethodSend, stun.ClassIndication),
	), f)
}

var ErrNotImplemented = errors.New("functionality not implemented")

// Permission. Implements net.PacketConn.
type Permission struct {
	mux          *sync.RWMutex
	binding      bool
	bindErr      error
	number       uint32
	c            *Client
	readDeadline time.Time
	peerData     chan []byte
}

// Read data from peer.
func (p *Permission) Read(b []byte) (n int, err error) {
	p.mux.Lock()
	deadline := p.readDeadline
	p.mux.Unlock()
	select {
	case <-time.After(time.Until(deadline)):
		return 0, errors.New("deadline reached")
	case d := <-p.peerData:
		if len(b) < len(d) {
			go func() {
				p.peerData <- d
			}()
			return 0, io.ErrShortBuffer
		}
		return copy(b, d), nil
	}
}

// Bound returns true if channel number is bound for current permission.
func (p *Permission) Bound() bool {
	return atomic.LoadUint32(&p.number) == 0
}

// Binding returns current channel number or 0 if not bound.
func (p *Permission) Binding() ChannelNumber {
	return ChannelNumber(atomic.LoadUint32(&p.number))
}

// ErrBindingInProgress means that previous binding transaction for selected permission
// is still in progress.
var ErrBindingInProgress = errors.New("binding in progress")

// ErrAlreadyBound means that selected permission already has bound channel number.
var ErrAlreadyBound = errors.New("channel already bound")

// Bind performs binding transaction, allocating channel binding for
// the permission.
//
// TODO: Handle ctx cancellation
// TODO: Start binding refresh cycle
func (p *Permission) Bind(ctx context.Context, n ChannelNumber) error {
	p.mux.Lock()
	defer p.mux.Unlock()

	if p.binding {
		return ErrBindingInProgress
	}
	p.binding = true
	if p.number != 0 {
		return ErrAlreadyBound
	}

	// Starting transaction.
	done := make(chan struct{})
	if err := p.c.handleBinding(p, n, func(e stun.Event) {
		p.bindErr = e.Error
		p.binding = false
		if e.Error == nil {
			atomic.StoreUint32(&p.number, uint32(n))
		}
		done <- struct{}{}
	}); err != nil {
		return err
	}

	// Waiting until transaction is done.
	select {
	case <-done:
		return p.bindErr
	case <-ctx.Done():
		go func() {
			<-done
		}()
		return ctx.Err()
	}
}

// Write sends buffer to peer.
//
// If permission is bound, the ChannelData message will be used.
func (p *Permission) Write(b []byte) (n int, err error) {
	if n := atomic.LoadUint32(&p.number); n != 0 {
		return p.c.sendChan(b, ChannelNumber(n))
	}
	return p.c.sendData(b, &PeerAddress{})
}

func (Permission) Close() error {
	return ErrNotImplemented
}

// LocalAddr is relayed address from TURN server.
func (Permission) LocalAddr() net.Addr {
	panic("implement me")
}

// RemoteAddr is peer address.
func (Permission) RemoteAddr() net.Addr {
	panic("implement me")
}

func (Permission) SetDeadline(t time.Time) error {
	return ErrNotImplemented
}

func (Permission) SetReadDeadline(t time.Time) error {
	return ErrNotImplemented
}

func (Permission) SetWriteDeadline(t time.Time) error {
	return ErrNotImplemented
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 internal

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"fmt"
	"net/url"
)

// generateSignature builds the digital signature for a key and a message.
func generateSignature(key, message string) (string, error) {
	k, err := base64.URLEncoding.DecodeString(key)
	if err != nil {
		return "", err
	}

	mac := hmac.New(sha1.New, k)
	mac.Write([]byte(message))
	return base64.URLEncoding.EncodeToString(mac.Sum(nil)), nil
}

// SignURL signs a url with a clientID and signature.
// The signature is assumed to be in URL safe base64 encoding.
// See: https://developers.google.com/maps/documentation/business/webservices/auth#digital_signatures
func SignURL(path, clientID, signature string, q url.Values) (string, error) {
	q.Set("client", clientID)
	encodedQuery := q.Encode()
	message := fmt.Sprintf("%s?%s", path, encodedQuery)
	s, err := generateSignature(signature, message)
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%s&signature=%s", encodedQuery, s), nil
}
<commit_msg>Adding documentation<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 internal

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"fmt"
	"net/url"
)

// generateSignature builds the digital signature for a key and a message.
func generateSignature(key, message string) (string, error) {
	k, err := base64.URLEncoding.DecodeString(key)
	if err != nil {
		return "", err
	}

	mac := hmac.New(sha1.New, k)
	mac.Write([]byte(message))
	return base64.URLEncoding.EncodeToString(mac.Sum(nil)), nil
}

// SignURL signs a url with a clientID and signature.
// The signature is assumed to be in URL safe base64 encoding.
// The returned signature string is URLEncoded.
// See: https://developers.google.com/maps/documentation/business/webservices/auth#digital_signatures
func SignURL(path, clientID, signature string, q url.Values) (string, error) {
	q.Set("client", clientID)
	encodedQuery := q.Encode()
	message := fmt.Sprintf("%s?%s", path, encodedQuery)
	s, err := generateSignature(signature, message)
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%s&signature=%s", encodedQuery, s), nil
}
<|endoftext|>


<commit_before>package moneybird

// InvoicePayment contains info on how the invoice is paid
type InvoicePayment struct {
	PaymentDate         string `json:"payment_date"`
	Price               string `json:"price"`
	PriceBase           string `json:"price_base,omitempty"`
	FinancialAccountID  int64  `json:"financial_account_id,omitempty"`
	FinancialMutationID int64  `json:"financial_mutation_id,omitempty"`
}

// InvoicePaymentGateway encapsulates all /invoices related endpoints
type InvoicePaymentGateway struct {
	*Client
}

// InvoicePayment returns a new gateway instance
func (c *Client) InvoicePayment() *InvoicePaymentGateway {
	return &InvoicePaymentGateway{c}
}

// Create marks the invoice as paid in Moneybird
func (c *InvoicePaymentGateway) Create(invoice *Invoice, payment *InvoicePayment) error {
	res, err := c.execute("PATCH", "sales_invoices/"+invoice.ID+"/register_payment", &envelope{InvoicePayment: payment})
	if err != nil {
		return err
	}

	switch res.StatusCode {
	case 200:
		return nil
	}

	return res.error()
}
<commit_msg>update endpoint for registering payment<commit_after>package moneybird

// InvoicePayment contains info on how the invoice is paid
type InvoicePayment struct {
	PaymentDate         string `json:"payment_date"`
	Price               string `json:"price"`
	PriceBase           string `json:"price_base,omitempty"`
	FinancialAccountID  int64  `json:"financial_account_id,omitempty"`
	FinancialMutationID int64  `json:"financial_mutation_id,omitempty"`
}

// InvoicePaymentGateway encapsulates all /invoices related endpoints
type InvoicePaymentGateway struct {
	*Client
}

// InvoicePayment returns a new gateway instance
func (c *Client) InvoicePayment() *InvoicePaymentGateway {
	return &InvoicePaymentGateway{c}
}

// Create marks the invoice as paid in Moneybird
func (c *InvoicePaymentGateway) Create(invoice *Invoice, payment *InvoicePayment) error {
	res, err := c.execute("POST", "sales_invoices/"+invoice.ID+"/payments", &envelope{InvoicePayment: payment})
	if err != nil {
		return err
	}

	switch res.StatusCode {
	case 201:
		return nil
	}

	return res.error()
}
<|endoftext|>


<commit_before>// Copyright 2014 The Cayley Authors. All rights reserved.
//
// 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 graph_memstore

import (
	"fmt"
	"github.com/petar/GoLLRB/llrb"
	"graph"
	"math"
	"strings"
)

type LlrbIterator struct {
	graph.BaseIterator
	tree      *llrb.LLRB
	values    chan llrb.Item
	another   chan bool
	data      string
	isRunning bool
}

type Int64 int64

func (i Int64) Less(than llrb.Item) bool {
	return i < than.(Int64)
}

func IterateAll(tree *llrb.LLRB, c chan llrb.Item, another chan bool) {
	tree.AscendGreaterOrEqual(Int64(-1), func(i llrb.Item) bool {
		want_more := <-another
		if want_more {
			c <- i
			return true
		}
		return false
	})
}

func NewLlrbIterator(tree *llrb.LLRB, data string) *LlrbIterator {
	var it LlrbIterator
	graph.BaseIteratorInit(&it.BaseIterator)
	it.tree = tree
	it.isRunning = false
	it.values = make(chan llrb.Item)
	it.another = make(chan bool, 1)
	it.data = data
	return &it
}

func (it *LlrbIterator) Reset() {
	if it.another != nil {
		it.another <- false
		close(it.another)
	}
	it.another = nil
	if it.values != nil {
		close(it.values)
	}
	it.values = nil
	it.isRunning = false
	it.another = make(chan bool)
	it.values = make(chan llrb.Item)
}

func (it *LlrbIterator) Clone() graph.Iterator {
	var new_it = NewLlrbIterator(it.tree, it.data)
	new_it.CopyTagsFrom(it)
	return new_it
}

func (it *LlrbIterator) Close() {
	if it.another != nil {
		it.another <- false
		close(it.another)
	}
	it.another = nil
	if it.values != nil {
		close(it.values)
	}
	it.values = nil
}

func (it *LlrbIterator) Next() (graph.TSVal, bool) {
	graph.NextLogIn(it)
	// Little hack here..
	if !it.isRunning {
		go IterateAll(it.tree, it.values, it.another)
		it.isRunning = true
	}
	last := int64(0)
	if it.Last != nil {
		last = it.Last.(int64)
	}
	if it.tree.Max() == nil || last == int64(it.tree.Max().(Int64)) {
		return graph.NextLogOut(it, nil, false)
	}
	it.another <- true
	val := <-it.values
	it.Last = int64(val.(Int64))
	return graph.NextLogOut(it, it.Last, true)
}

func (it *LlrbIterator) Size() (int64, bool) {
	return int64(it.tree.Len()), true
}

func (it *LlrbIterator) Check(v graph.TSVal) bool {
	graph.CheckLogIn(it, v)
	if it.tree.Has(Int64(v.(int64))) {
		it.Last = v
		return graph.CheckLogOut(it, v, true)
	}
	return graph.CheckLogOut(it, v, false)
}

func (it *LlrbIterator) DebugString(indent int) string {
	size, _ := it.Size()
	return fmt.Sprintf("%s(%s tags:%s size:%d %s)", strings.Repeat(" ", indent), it.Type(), it.Tags(), size, it.data)
}

func (it *LlrbIterator) Type() string {
	return "llrb"
}
func (it *LlrbIterator) Sorted() bool {
	return true
}
func (it *LlrbIterator) Optimize() (graph.Iterator, bool) {
	return it, false
}

func (it *LlrbIterator) GetStats() *graph.IteratorStats {
	return &graph.IteratorStats{
		CheckCost: int64(math.Log(float64(it.tree.Len()))) + 1,
		NextCost:  1,
		Size:      int64(it.tree.Len()),
	}
}
<commit_msg>Remove unnecessary goroutine from iterator, cleaning up more easily.<commit_after>// Copyright 2014 The Cayley Authors. All rights reserved.
//
// 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 graph_memstore

import (
	"fmt"
	"github.com/petar/GoLLRB/llrb"
	"graph"
	"math"
	"strings"
)

type LlrbIterator struct {
	graph.BaseIterator
	tree      *llrb.LLRB
	data      string
	isRunning bool
	iterLast  Int64
}

type Int64 int64

func (i Int64) Less(than llrb.Item) bool {
	return i < than.(Int64)
}

func IterateOne(tree *llrb.LLRB, last Int64) Int64 {
	var next Int64
	tree.AscendGreaterOrEqual(last, func(i llrb.Item) bool {
		if i.(Int64) == last {
			return true
		} else {
			next = i.(Int64)
			return false
		}
	})
	return next
}

func NewLlrbIterator(tree *llrb.LLRB, data string) *LlrbIterator {
	var it LlrbIterator
	graph.BaseIteratorInit(&it.BaseIterator)
	it.tree = tree
	it.iterLast = Int64(-1)
	it.data = data
	return &it
}

func (it *LlrbIterator) Reset() {
	it.iterLast = Int64(-1)
}

func (it *LlrbIterator) Clone() graph.Iterator {
	var new_it = NewLlrbIterator(it.tree, it.data)
	new_it.CopyTagsFrom(it)
	return new_it
}

func (it *LlrbIterator) Close() {}

func (it *LlrbIterator) Next() (graph.TSVal, bool) {
	graph.NextLogIn(it)
	if it.tree.Max() == nil || it.Last == int64(it.tree.Max().(Int64)) {
		return graph.NextLogOut(it, nil, false)
	}
	it.iterLast = IterateOne(it.tree, it.iterLast)
	it.Last = int64(it.iterLast)
	return graph.NextLogOut(it, it.Last, true)
}

func (it *LlrbIterator) Size() (int64, bool) {
	return int64(it.tree.Len()), true
}

func (it *LlrbIterator) Check(v graph.TSVal) bool {
	graph.CheckLogIn(it, v)
	if it.tree.Has(Int64(v.(int64))) {
		it.Last = v
		return graph.CheckLogOut(it, v, true)
	}
	return graph.CheckLogOut(it, v, false)
}

func (it *LlrbIterator) DebugString(indent int) string {
	size, _ := it.Size()
	return fmt.Sprintf("%s(%s tags:%s size:%d %s)", strings.Repeat(" ", indent), it.Type(), it.Tags(), size, it.data)
}

func (it *LlrbIterator) Type() string {
	return "llrb"
}
func (it *LlrbIterator) Sorted() bool {
	return true
}
func (it *LlrbIterator) Optimize() (graph.Iterator, bool) {
	return it, false
}

func (it *LlrbIterator) GetStats() *graph.IteratorStats {
	return &graph.IteratorStats{
		CheckCost: int64(math.Log(float64(it.tree.Len()))) + 1,
		NextCost:  1,
		Size:      int64(it.tree.Len()),
	}
}
<|endoftext|>