<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, 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.Quit()
			}
			if err != nil {
				return []byte{255}, fmt.Errorf("request error: unknown code %d", code)
			}
			return []byte{255}, perrs[perrmap[code]]
		}
	}
	return resp, err
}

// Quit terminates the client's network connection and other
// operations.
func (c *Client) Quit() {
	c.cc = true
	c.conn.Close()
}
<commit_msg>golint fixup<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.Quit()
			}
			if err != nil {
				return []byte{255}, fmt.Errorf("request error: unknown code %d", code)
			}
			return []byte{255}, perrs[perrmap[code]]
		}
	}
	return resp, err
}

// Quit terminates the client's network connection and other
// operations.
func (c *Client) Quit() {
	c.cc = true
	c.conn.Close()
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"github.com/thetommytwitch/poke-go/types"
	"net/http"
)

const (
	endpoint = "http://pokeapi.co/api/v2/"
)

var dataMap = map[string]interface{}{
	"ability":                   types.Ability{},
	"berry":                     types.Berry{},
	"berry-firmness":            types.BerryFirmness{},
	"berry-flavor":              types.BerryFlavor{},
	"characteristic":            types.Characteristic{},
	"contest-effect":            types.ContestEffect{},
	"contest-type":              types.ContestType{},
	"egg-group":                 types.EggGroup{},
	"encounter-condition":       types.EncounterCondition{},
	"encounter-condition-value": types.EncounterConditionValue{},
	"encounter-method":          types.EncounterMethod{},
	"evolution-chain":           types.EvolutionChain{},
	"evolution-trigger":         types.EvolutionTrigger{},
	"gender":                    types.Gender{},
	"generation":                types.Generation{},
	"growth-rate":               types.GrowthRate{},
	"item":                      types.Item{},
	"item-attribute":            types.ItemAttribute{},
	"item-category":             types.ItemCategory{},
	"item-fling-effect":         types.ItemFlingEffect{},
	"item-pocket":               types.ItemPocket{},
	"language":                  types.Language{},
	"location":                  types.Location{},
	"location-area":             types.LocationArea{},
	"move":                      types.Move{},
	"move-ailment":              types.MoveAilment{},
	"move-battle-style":         types.MoveBattleStyle{},
	"move-category":             types.MoveCategory{},
	"move-damage-class":         types.MoveDamageClass{},
	"move-learn-method":         types.MoveLearnMethod{},
	"move-target":               types.MoveTarget{},
	"nature":                    types.Nature{},
	"pal-park-area":             types.PalParkArea{},
	"pokeathlon-stat":           types.PokeathlonStat{},
	"pokedex":                   types.Pokedex{},
	"pokemon":                   types.Pokemon{},
	"pokemon-color":             types.PokemonColor{},
	"pokemon-form":              types.PokemonForm{},
	"pokemon-habitat":           types.PokemonHabitat{},
	"pokemon-shape":             types.PokemonShape{},
	"pokemon-species":           types.PokemonSpecies{},
	"region":                    types.Region{},
	"stat":                      types.Stat{},
	"super-contest-effect":      types.SuperContestEffect{},
	"type":                      types.Type{},
	"version":                   types.Version{},
	"version-group":             types.VersionGroup{},
}

// GetPokeData ...
func GetPokeData(value string, pokemon string) (interface{}, error) {

	res, err := http.Get(endpoint + value + "/" + pokemon + "/")
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()
	decoder := json.NewDecoder(res.Body)
	for key, v := range dataMap {
		if key == value {
			decoder.Decode(&v)
			return v, nil
		}
	}
	return nil, errors.New("Err: value not found.")
}
<commit_msg>refactored and added ability<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/thetommytwitch/poke-go/types"
	"io/ioutil"
	"net/http"
)

const (
	endpoint = "http://pokeapi.co/api/v2/"
)

var dataMap = map[string]interface{}{
	"ability":                   types.Ability{},
	"berry":                     types.Berry{},
	"berry-firmness":            types.BerryFirmness{},
	"berry-flavor":              types.BerryFlavor{},
	"characteristic":            types.Characteristic{},
	"contest-effect":            types.ContestEffect{},
	"contest-type":              types.ContestType{},
	"egg-group":                 types.EggGroup{},
	"encounter-condition":       types.EncounterCondition{},
	"encounter-condition-value": types.EncounterConditionValue{},
	"encounter-method":          types.EncounterMethod{},
	"evolution-chain":           types.EvolutionChain{},
	"evolution-trigger":         types.EvolutionTrigger{},
	"gender":                    types.Gender{},
	"generation":                types.Generation{},
	"growth-rate":               types.GrowthRate{},
	"item":                      types.Item{},
	"item-attribute":            types.ItemAttribute{},
	"item-category":             types.ItemCategory{},
	"item-fling-effect":         types.ItemFlingEffect{},
	"item-pocket":               types.ItemPocket{},
	"language":                  types.Language{},
	"location":                  types.Location{},
	"location-area":             types.LocationArea{},
	"move":                      types.Move{},
	"move-ailment":              types.MoveAilment{},
	"move-battle-style":         types.MoveBattleStyle{},
	"move-category":             types.MoveCategory{},
	"move-damage-class":         types.MoveDamageClass{},
	"move-learn-method":         types.MoveLearnMethod{},
	"move-target":               types.MoveTarget{},
	"nature":                    types.Nature{},
	"pal-park-area":             types.PalParkArea{},
	"pokeathlon-stat":           types.PokeathlonStat{},
	"pokedex":                   types.Pokedex{},
	"pokemon":                   types.Pokemon{},
	"pokemon-color":             types.PokemonColor{},
	"pokemon-form":              types.PokemonForm{},
	"pokemon-habitat":           types.PokemonHabitat{},
	"pokemon-shape":             types.PokemonShape{},
	"pokemon-species":           types.PokemonSpecies{},
	"region":                    types.Region{},
	"stat":                      types.Stat{},
	"super-contest-effect":      types.SuperContestEffect{},
	"type":                      types.Type{},
	"version":                   types.Version{},
	"version-group":             types.VersionGroup{},
}

// Client ...
type Client struct{}

// GetPokeData ...
// make this concurent and add a timeout param
func (c *Client) request(value string, items []string) ([][]byte, error) {

	if items == nil {
		return nil, errors.New("Err: No parameters found, need at least one item in slice")
	}

	var response [][]byte

	for _, resource := range items {
		res, err := http.Get(endpoint + value + "/" + resource + "/")
		if err != nil {
			return nil, err
		}

		body, err := ioutil.ReadAll(res.Body)
		if err != nil {
			return nil, err
		}
		response = append(response, body)
	}

	return response, nil
}

// GetAbility ...
func (c *Client) GetAbility(items []string) ([]types.Ability, error) {
	abilities := []types.Ability{}
	responses, err := c.request("ability", items)
	if err != nil {
		return nil, err
	}
	for _, response := range responses {
		a := types.Ability{}
		err := json.Unmarshal(response, &a)
		if err != nil {
			return nil, err
		}
		abilities = append(abilities, a)
	}
	return abilities, nil
}

func main() {
	client := new(Client)
	params := []string{"1"}
	ability, err := client.GetAbility(params)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(ability)
}
<|endoftext|>


<commit_before>package kickass

import (
	"errors"
	"fmt"
	"net/http"

	"gopkg.in/xmlpath.v2"
)

// Kickass default endpoint
const DefaultEndpoint = "https://kat.cr"

// MaxElementsPerPage represents the max number of elements per page
const MaxElementsPerPage = 25

// Custom errors
var (
	ErrUnexpectedContent  = errors.New("kickass: unexpected content")
	ErrMissingUserParam   = errors.New("kickass: missing user param")
	ErrMissingSearchParam = errors.New("kickass: missing search param")
)

// Torrent represents a torrent from kickass
type Torrent struct {
	Name       string
	TorrentURL string
	MagnetURL  string
	Seed       int
	Leech      int
	Age        string
	Size       string
	FileCount  int
	Verified   bool
	User       string
}

// Client represents the kickass client
type Client struct {
	Endpoint   string
	HTTPClient *http.Client
}

// New creates a new client
func New() Client {
	return Client{
		Endpoint:   DefaultEndpoint,
		HTTPClient: http.DefaultClient,
	}
}

func (c *Client) searchBaseURL(q *Query) string {
	return fmt.Sprintf("%s/usearch/%s", c.Endpoint, q.searchField())
}

// Search searches from a query
func (c *Client) Search(q *Query) ([]*Torrent, error) {
	// The only required param is the search
	if q.Search == "" {
		return nil, ErrMissingSearchParam
	}

	return c.getPages(q, c.searchBaseURL(q))
}

func (c *Client) listByUserBaseURL(q *Query) string {
	return fmt.Sprintf("%s/user/%s/uploads", c.Endpoint, q.User)
}

// ListByUser returns the torrents for a specific user
func (c *Client) ListByUser(q *Query) ([]*Torrent, error) {
	// The only required param is the user
	if q.User == "" {
		return nil, ErrMissingUserParam
	}

	return c.getPages(q, c.listByUserBaseURL(q))
}

// getPages downloads each page and merges the results
func (c *Client) getPages(q *Query, baseURL string) ([]*Torrent, error) {
	torrents := []*Torrent{}

	// Set default number of pages to 1
	if q.Pages == 0 {
		q.Pages = 1
	}

	for i := 1; i <= q.Pages; i++ {
		URL := fmt.Sprintf("%s/%s", baseURL, q.urlParams(i))

		t, err := c.getPage(URL)
		if err != nil {
			return nil, err
		}

		torrents = append(torrents, t...)

		// If the number of results is lower than the max number of elements
		// per page that means there is no need to continue
		if len(t) < MaxElementsPerPage {
			break
		}
	}

	return torrents, nil
}

// getPage downloads a page and parses its content
func (c *Client) getPage(URL string) ([]*Torrent, error) {
	resp, err := c.HTTPClient.Get(URL)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	root, err := xmlpath.ParseHTML(resp.Body)
	if err != nil {
		return nil, err
	}
	return parseFunc(root)
}
<commit_msg>Fix New to return a pointer<commit_after>package kickass

import (
	"errors"
	"fmt"
	"net/http"

	"gopkg.in/xmlpath.v2"
)

// Kickass default endpoint
const DefaultEndpoint = "https://kat.cr"

// MaxElementsPerPage represents the max number of elements per page
const MaxElementsPerPage = 25

// Custom errors
var (
	ErrUnexpectedContent  = errors.New("kickass: unexpected content")
	ErrMissingUserParam   = errors.New("kickass: missing user param")
	ErrMissingSearchParam = errors.New("kickass: missing search param")
)

// Torrent represents a torrent from kickass
type Torrent struct {
	Name       string
	TorrentURL string
	MagnetURL  string
	Seed       int
	Leech      int
	Age        string
	Size       string
	FileCount  int
	Verified   bool
	User       string
}

// Client represents the kickass client
type Client struct {
	Endpoint   string
	HTTPClient *http.Client
}

// New creates a new client
func New() *Client {
	return &Client{
		Endpoint:   DefaultEndpoint,
		HTTPClient: http.DefaultClient,
	}
}

func (c *Client) searchBaseURL(q *Query) string {
	return fmt.Sprintf("%s/usearch/%s", c.Endpoint, q.searchField())
}

// Search searches from a query
func (c *Client) Search(q *Query) ([]*Torrent, error) {
	// The only required param is the search
	if q.Search == "" {
		return nil, ErrMissingSearchParam
	}

	return c.getPages(q, c.searchBaseURL(q))
}

func (c *Client) listByUserBaseURL(q *Query) string {
	return fmt.Sprintf("%s/user/%s/uploads", c.Endpoint, q.User)
}

// ListByUser returns the torrents for a specific user
func (c *Client) ListByUser(q *Query) ([]*Torrent, error) {
	// The only required param is the user
	if q.User == "" {
		return nil, ErrMissingUserParam
	}

	return c.getPages(q, c.listByUserBaseURL(q))
}

// getPages downloads each page and merges the results
func (c *Client) getPages(q *Query, baseURL string) ([]*Torrent, error) {
	torrents := []*Torrent{}

	// Set default number of pages to 1
	if q.Pages == 0 {
		q.Pages = 1
	}

	for i := 1; i <= q.Pages; i++ {
		URL := fmt.Sprintf("%s/%s", baseURL, q.urlParams(i))

		t, err := c.getPage(URL)
		if err != nil {
			return nil, err
		}

		torrents = append(torrents, t...)

		// If the number of results is lower than the max number of elements
		// per page that means there is no need to continue
		if len(t) < MaxElementsPerPage {
			break
		}
	}

	return torrents, nil
}

// getPage downloads a page and parses its content
func (c *Client) getPage(URL string) ([]*Torrent, error) {
	resp, err := c.HTTPClient.Get(URL)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	root, err := xmlpath.ParseHTML(resp.Body)
	if err != nil {
		return nil, err
	}
	return parseFunc(root)
}
<|endoftext|>


<commit_before>package discoverd

import (
	"errors"
	"net"
	"os"
	"sort"
	"sync"
	"time"

	"github.com/flynn/discoverd/agent"
	"github.com/flynn/rpcplus"
)

type Service struct {
	Created uint
	Name    string
	Host    string
	Port    string
	Addr    string
	Attrs   map[string]string
}

type ServiceSet struct {
	sync.Mutex
	services map[string]*Service
	filters  map[string]string
	watches  map[chan *agent.ServiceUpdate]bool
	leaders  chan *Service
	call     *rpcplus.Call
	self     *Service
	SelfAddr string
}

func copyService(service *Service) *Service {
	s := *service
	s.Attrs = make(map[string]string, len(service.Attrs))
	for k, v := range service.Attrs {
		s.Attrs[k] = v
	}
	return &s
}

func makeServiceSet(call *rpcplus.Call) *ServiceSet {
	return &ServiceSet{
		services: make(map[string]*Service),
		filters:  make(map[string]string),
		watches:  make(map[chan *agent.ServiceUpdate]bool),
		call:     call,
	}
}

func (s *ServiceSet) bind(updates chan *agent.ServiceUpdate) chan struct{} {
	// current is an event when enough service updates have been
	// received to bring us to "current" state (when subscribed)
	current := make(chan struct{})
	go func() {
		isCurrent := false
		for update := range updates {
			if update.Addr == "" && update.Name == "" && !isCurrent {
				close(current)
				isCurrent = true
				continue
			}
			s.Lock()
			if s.filters != nil && !s.matchFilters(update.Attrs) {
				s.Unlock()
				continue
			}
			if s.SelfAddr != update.Addr && update.Online {
				if _, exists := s.services[update.Addr]; !exists {
					host, port, _ := net.SplitHostPort(update.Addr)
					s.services[update.Addr] = &Service{
						Name:    update.Name,
						Addr:    update.Addr,
						Host:    host,
						Port:    port,
						Created: update.Created,
					}
				}
				s.services[update.Addr].Attrs = update.Attrs
			} else {
				if _, exists := s.services[update.Addr]; exists {
					delete(s.services, update.Addr)
				} else {
					s.Unlock()
					if s.SelfAddr == update.Addr {
						s.updateWatches(update)
					}
					continue
				}
			}
			s.Unlock()
			s.updateWatches(update)
		}
		s.closeWatches()
	}()
	return current
}

func (s *ServiceSet) updateWatches(update *agent.ServiceUpdate) {
	s.Lock()
	defer s.Unlock()
	for ch, once := range s.watches {
		ch <- update
		if once {
			delete(s.watches, ch)
		}
	}
}

func (s *ServiceSet) closeWatches() {
	s.Lock()
	defer s.Unlock()
	for ch := range s.watches {
		close(ch)
	}
}

func (s *ServiceSet) matchFilters(attrs map[string]string) bool {
	for key, value := range s.filters {
		if attrs[key] != value {
			return false
		}
	}
	return true
}

func (s *ServiceSet) Leader() *Service {
	services := s.Services()
	if len(services) > 0 {
		if s.self != nil && services[0].Created > s.self.Created {
			return s.self
		}
		return services[0]
	}
	if s.self != nil {
		return s.self
	}
	return nil
}

func (s *ServiceSet) Leaders() chan *Service {
	if s.leaders != nil {
		return s.leaders
	}
	s.leaders = make(chan *Service)
	updates := make(chan *agent.ServiceUpdate)
	s.Watch(updates, false, false)
	go func() {
		leader := s.Leader()
		s.leaders <- leader
		for update := range updates {
			if !update.Online && update.Addr == leader.Addr {
				leader = s.Leader()
				s.leaders <- leader
			}
		}
	}()
	return s.leaders
}

type serviceByAge []*Service

func (a serviceByAge) Len() int           { return len(a) }
func (a serviceByAge) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a serviceByAge) Less(i, j int) bool { return a[i].Created < a[j].Created }

func (s *ServiceSet) Services() []*Service {
	s.Lock()
	defer s.Unlock()
	list := make([]*Service, 0, len(s.services))
	for _, service := range s.services {
		list = append(list, copyService(service))
	}
	if len(list) > 0 {
		sort.Sort(serviceByAge(list))
	}
	return list
}

func (s *ServiceSet) Addrs() []string {
	list := make([]string, 0, len(s.services))
	for _, service := range s.Services() {
		list = append(list, service.Addr)
	}
	return list
}

func (s *ServiceSet) Select(attrs map[string]string) []*Service {
	s.Lock()
	defer s.Unlock()
	list := make([]*Service, 0, len(s.services))
outer:
	for _, service := range s.services {
		for key, value := range attrs {
			if service.Attrs[key] != value {
				continue outer
			}
		}
		list = append(list, service)
	}
	return list
}

func (s *ServiceSet) Filter(attrs map[string]string) {
	s.Lock()
	defer s.Unlock()
	s.filters = attrs
	for key, service := range s.services {
		if !s.matchFilters(service.Attrs) {
			delete(s.services, key)
		}
	}
}

func (s *ServiceSet) Watch(ch chan *agent.ServiceUpdate, bringCurrent bool, fireOnce bool) {
	s.Lock()
	s.watches[ch] = fireOnce
	s.Unlock()
	if bringCurrent {
		go func() {
			s.Lock()
			defer s.Unlock()
			for _, service := range s.services {
				ch <- &agent.ServiceUpdate{
					Name:    service.Name,
					Addr:    service.Addr,
					Online:  true,
					Attrs:   service.Attrs,
					Created: service.Created,
				}
			}
		}()
	}
}

func (s *ServiceSet) Unwatch(ch chan *agent.ServiceUpdate) {
	s.Lock()
	defer s.Unlock()
	delete(s.watches, ch)
}

func (s *ServiceSet) Wait() chan *agent.ServiceUpdate {
	updateCh := make(chan *agent.ServiceUpdate, 1024) // buffer because of Watch bringCurrent race bug
	s.Watch(updateCh, true, true)
	return updateCh
}

func (s *ServiceSet) Close() error {
	return s.call.CloseStream()
}

type Client struct {
	sync.Mutex
	client        *rpcplus.Client
	heartbeats    map[string]chan struct{}
	expandedAddrs map[string]string
}

func NewClient() (*Client, error) {
	addr := os.Getenv("DISCOVERD")
	if addr == "" {
		addr = "127.0.0.1:1111"
	}
	return NewClientUsingAddress(addr)
}

func NewClientUsingAddress(addr string) (*Client, error) {
	client, err := rpcplus.DialHTTP("tcp", addr)
	return &Client{
		client:        client,
		heartbeats:    make(map[string]chan struct{}),
		expandedAddrs: make(map[string]string),
	}, err
}

func (c *Client) ServiceSet(name string) (*ServiceSet, error) {
	updates := make(chan *agent.ServiceUpdate)
	call := c.client.StreamGo("Agent.Subscribe", &agent.Args{
		Name: name,
	}, updates)
	set := makeServiceSet(call)
	<-set.bind(updates)
	return set, nil
}

func (c *Client) Services(name string, timeout time.Duration) ([]*Service, error) {
	set, err := c.ServiceSet(name)
	if err != nil {
		return nil, err
	}
	defer set.Close()
	select {
	case <-set.Wait():
		return set.Services(), nil
	case <-time.After(time.Duration(timeout) * time.Second):
		return nil, errors.New("discover: wait timeout exceeded")
	}
}

func (c *Client) Register(name, addr string) error {
	return c.RegisterWithAttributes(name, addr, nil)
}

func (c *Client) RegisterWithSet(name, addr string, attributes map[string]string) (*ServiceSet, error) {
	err := c.RegisterWithAttributes(name, addr, attributes)
	if err != nil {
		return nil, err
	}
	set, err := c.ServiceSet(name)
	if err != nil {
		c.Unregister(name, addr)
		return nil, err
	}
	set.SelfAddr = c.expandedAddrs[addr]
	_, exists := set.services[set.SelfAddr]
	if !exists {
		update := <-set.Wait()
		for update.Addr != set.SelfAddr {
			update = <-set.Wait()
		}
	}
	set.Lock()
	set.self = set.services[set.SelfAddr]
	delete(set.services, set.SelfAddr)
	set.Unlock()
	return set, nil
}

func (c *Client) RegisterAndStandby(name, addr string, attributes map[string]string) (chan *Service, error) {
	set, err := c.RegisterWithSet(name, addr, attributes)
	if err != nil {
		return nil, err
	}
	standbyCh := make(chan *Service)
	go func() {
		for leader := range set.Leaders() {
			if leader.Addr == set.SelfAddr {
				set.Close()
				standbyCh <- leader
				return
			}
		}
	}()
	return standbyCh, nil
}

func (c *Client) RegisterWithAttributes(name, addr string, attributes map[string]string) error {
	args := &agent.Args{
		Name:  name,
		Addr:  addr,
		Attrs: attributes,
	}
	var ret string
	err := c.client.Call("Agent.Register", args, &ret)
	if err != nil {
		return errors.New("discover: register failed: " + err.Error())
	}
	done := make(chan struct{})
	c.Lock()
	c.heartbeats[args.Addr] = done
	c.expandedAddrs[args.Addr] = ret
	c.Unlock()
	go func() {
		ticker := time.NewTicker(agent.HeartbeatIntervalSecs * time.Second) // TODO: add jitter
		defer ticker.Stop()
		for {
			select {
			case <-ticker.C:
				// TODO: log error here
				c.client.Call("Agent.Heartbeat", &agent.Args{
					Name: name,
					Addr: args.Addr,
				}, &struct{}{})
			case <-done:
				return
			}
		}
	}()
	return nil
}

func (c *Client) Unregister(name, addr string) error {
	args := &agent.Args{
		Name: name,
		Addr: addr,
	}
	c.Lock()
	close(c.heartbeats[args.Addr])
	delete(c.heartbeats, args.Addr)
	c.Unlock()
	err := c.client.Call("Agent.Unregister", args, &struct{}{})
	if err != nil {
		return errors.New("discover: unregister failed: " + err.Error())
	}
	return nil
}
<commit_msg>put mutexes behind unexported member<commit_after>package discoverd

import (
	"errors"
	"net"
	"os"
	"sort"
	"sync"
	"time"

	"github.com/flynn/discoverd/agent"
	"github.com/flynn/rpcplus"
)

type Service struct {
	Created uint
	Name    string
	Host    string
	Port    string
	Addr    string
	Attrs   map[string]string
}

type ServiceSet struct {
	l        sync.Mutex
	services map[string]*Service
	filters  map[string]string
	watches  map[chan *agent.ServiceUpdate]bool
	leaders  chan *Service
	call     *rpcplus.Call
	self     *Service
	SelfAddr string
}

func copyService(service *Service) *Service {
	s := *service
	s.Attrs = make(map[string]string, len(service.Attrs))
	for k, v := range service.Attrs {
		s.Attrs[k] = v
	}
	return &s
}

func makeServiceSet(call *rpcplus.Call) *ServiceSet {
	return &ServiceSet{
		services: make(map[string]*Service),
		filters:  make(map[string]string),
		watches:  make(map[chan *agent.ServiceUpdate]bool),
		call:     call,
	}
}

func (s *ServiceSet) bind(updates chan *agent.ServiceUpdate) chan struct{} {
	// current is an event when enough service updates have been
	// received to bring us to "current" state (when subscribed)
	current := make(chan struct{})
	go func() {
		isCurrent := false
		for update := range updates {
			if update.Addr == "" && update.Name == "" && !isCurrent {
				close(current)
				isCurrent = true
				continue
			}
			s.l.Lock()
			if s.filters != nil && !s.matchFilters(update.Attrs) {
				s.l.Unlock()
				continue
			}
			if s.SelfAddr != update.Addr && update.Online {
				if _, exists := s.services[update.Addr]; !exists {
					host, port, _ := net.SplitHostPort(update.Addr)
					s.services[update.Addr] = &Service{
						Name:    update.Name,
						Addr:    update.Addr,
						Host:    host,
						Port:    port,
						Created: update.Created,
					}
				}
				s.services[update.Addr].Attrs = update.Attrs
			} else {
				if _, exists := s.services[update.Addr]; exists {
					delete(s.services, update.Addr)
				} else {
					s.l.Unlock()
					if s.SelfAddr == update.Addr {
						s.updateWatches(update)
					}
					continue
				}
			}
			s.l.Unlock()
			s.updateWatches(update)
		}
		s.closeWatches()
	}()
	return current
}

func (s *ServiceSet) updateWatches(update *agent.ServiceUpdate) {
	s.l.Lock()
	defer s.l.Unlock()
	for ch, once := range s.watches {
		ch <- update
		if once {
			delete(s.watches, ch)
		}
	}
}

func (s *ServiceSet) closeWatches() {
	s.l.Lock()
	defer s.l.Unlock()
	for ch := range s.watches {
		close(ch)
	}
}

func (s *ServiceSet) matchFilters(attrs map[string]string) bool {
	for key, value := range s.filters {
		if attrs[key] != value {
			return false
		}
	}
	return true
}

func (s *ServiceSet) Leader() *Service {
	services := s.Services()
	if len(services) > 0 {
		if s.self != nil && services[0].Created > s.self.Created {
			return s.self
		}
		return services[0]
	}
	if s.self != nil {
		return s.self
	}
	return nil
}

func (s *ServiceSet) Leaders() chan *Service {
	if s.leaders != nil {
		return s.leaders
	}
	s.leaders = make(chan *Service)
	updates := make(chan *agent.ServiceUpdate)
	s.Watch(updates, false, false)
	go func() {
		leader := s.Leader()
		s.leaders <- leader
		for update := range updates {
			if !update.Online && update.Addr == leader.Addr {
				leader = s.Leader()
				s.leaders <- leader
			}
		}
	}()
	return s.leaders
}

type serviceByAge []*Service

func (a serviceByAge) Len() int           { return len(a) }
func (a serviceByAge) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a serviceByAge) Less(i, j int) bool { return a[i].Created < a[j].Created }

func (s *ServiceSet) Services() []*Service {
	s.l.Lock()
	defer s.l.Unlock()
	list := make([]*Service, 0, len(s.services))
	for _, service := range s.services {
		list = append(list, copyService(service))
	}
	if len(list) > 0 {
		sort.Sort(serviceByAge(list))
	}
	return list
}

func (s *ServiceSet) Addrs() []string {
	list := make([]string, 0, len(s.services))
	for _, service := range s.Services() {
		list = append(list, service.Addr)
	}
	return list
}

func (s *ServiceSet) Select(attrs map[string]string) []*Service {
	s.l.Lock()
	defer s.l.Unlock()
	list := make([]*Service, 0, len(s.services))
outer:
	for _, service := range s.services {
		for key, value := range attrs {
			if service.Attrs[key] != value {
				continue outer
			}
		}
		list = append(list, service)
	}
	return list
}

func (s *ServiceSet) Filter(attrs map[string]string) {
	s.l.Lock()
	defer s.l.Unlock()
	s.filters = attrs
	for key, service := range s.services {
		if !s.matchFilters(service.Attrs) {
			delete(s.services, key)
		}
	}
}

func (s *ServiceSet) Watch(ch chan *agent.ServiceUpdate, bringCurrent bool, fireOnce bool) {
	s.l.Lock()
	s.watches[ch] = fireOnce
	s.l.Unlock()
	if bringCurrent {
		go func() {
			s.l.Lock()
			defer s.l.Unlock()
			for _, service := range s.services {
				ch <- &agent.ServiceUpdate{
					Name:    service.Name,
					Addr:    service.Addr,
					Online:  true,
					Attrs:   service.Attrs,
					Created: service.Created,
				}
			}
		}()
	}
}

func (s *ServiceSet) Unwatch(ch chan *agent.ServiceUpdate) {
	s.l.Lock()
	defer s.l.Unlock()
	delete(s.watches, ch)
}

func (s *ServiceSet) Wait() chan *agent.ServiceUpdate {
	updateCh := make(chan *agent.ServiceUpdate, 1024) // buffer because of Watch bringCurrent race bug
	s.Watch(updateCh, true, true)
	return updateCh
}

func (s *ServiceSet) Close() error {
	return s.call.CloseStream()
}

type Client struct {
	l             sync.Mutex
	client        *rpcplus.Client
	heartbeats    map[string]chan struct{}
	expandedAddrs map[string]string
}

func NewClient() (*Client, error) {
	addr := os.Getenv("DISCOVERD")
	if addr == "" {
		addr = "127.0.0.1:1111"
	}
	return NewClientUsingAddress(addr)
}

func NewClientUsingAddress(addr string) (*Client, error) {
	client, err := rpcplus.DialHTTP("tcp", addr)
	return &Client{
		client:        client,
		heartbeats:    make(map[string]chan struct{}),
		expandedAddrs: make(map[string]string),
	}, err
}

func (c *Client) ServiceSet(name string) (*ServiceSet, error) {
	updates := make(chan *agent.ServiceUpdate)
	call := c.client.StreamGo("Agent.Subscribe", &agent.Args{
		Name: name,
	}, updates)
	set := makeServiceSet(call)
	<-set.bind(updates)
	return set, nil
}

func (c *Client) Services(name string, timeout time.Duration) ([]*Service, error) {
	set, err := c.ServiceSet(name)
	if err != nil {
		return nil, err
	}
	defer set.Close()
	select {
	case <-set.Wait():
		return set.Services(), nil
	case <-time.After(time.Duration(timeout) * time.Second):
		return nil, errors.New("discover: wait timeout exceeded")
	}
}

func (c *Client) Register(name, addr string) error {
	return c.RegisterWithAttributes(name, addr, nil)
}

func (c *Client) RegisterWithSet(name, addr string, attributes map[string]string) (*ServiceSet, error) {
	err := c.RegisterWithAttributes(name, addr, attributes)
	if err != nil {
		return nil, err
	}
	set, err := c.ServiceSet(name)
	if err != nil {
		c.Unregister(name, addr)
		return nil, err
	}
	set.SelfAddr = c.expandedAddrs[addr]
	_, exists := set.services[set.SelfAddr]
	if !exists {
		update := <-set.Wait()
		for update.Addr != set.SelfAddr {
			update = <-set.Wait()
		}
	}
	set.l.Lock()
	set.self = set.services[set.SelfAddr]
	delete(set.services, set.SelfAddr)
	set.l.Unlock()
	return set, nil
}

func (c *Client) RegisterAndStandby(name, addr string, attributes map[string]string) (chan *Service, error) {
	set, err := c.RegisterWithSet(name, addr, attributes)
	if err != nil {
		return nil, err
	}
	standbyCh := make(chan *Service)
	go func() {
		for leader := range set.Leaders() {
			if leader.Addr == set.SelfAddr {
				set.Close()
				standbyCh <- leader
				return
			}
		}
	}()
	return standbyCh, nil
}

func (c *Client) RegisterWithAttributes(name, addr string, attributes map[string]string) error {
	args := &agent.Args{
		Name:  name,
		Addr:  addr,
		Attrs: attributes,
	}
	var ret string
	err := c.client.Call("Agent.Register", args, &ret)
	if err != nil {
		return errors.New("discover: register failed: " + err.Error())
	}
	done := make(chan struct{})
	c.l.Lock()
	c.heartbeats[args.Addr] = done
	c.expandedAddrs[args.Addr] = ret
	c.l.Unlock()
	go func() {
		ticker := time.NewTicker(agent.HeartbeatIntervalSecs * time.Second) // TODO: add jitter
		defer ticker.Stop()
		for {
			select {
			case <-ticker.C:
				// TODO: log error here
				c.client.Call("Agent.Heartbeat", &agent.Args{
					Name: name,
					Addr: args.Addr,
				}, &struct{}{})
			case <-done:
				return
			}
		}
	}()
	return nil
}

func (c *Client) Unregister(name, addr string) error {
	args := &agent.Args{
		Name: name,
		Addr: addr,
	}
	c.l.Lock()
	close(c.heartbeats[args.Addr])
	delete(c.heartbeats, args.Addr)
	c.l.Unlock()
	err := c.client.Call("Agent.Unregister", args, &struct{}{})
	if err != nil {
		return errors.New("discover: unregister failed: " + err.Error())
	}
	return nil
}
<|endoftext|>


<commit_before>package compilerapi

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strings"
)

const (
	GoogleEndpointUrl = "http://closure-compiler.appspot.com/compile"
)

type Client struct {
}

type OutputError struct {
	Charno int    `json:"charno"`
	Error  string `json:"error"`
	Lineno int    `json:"lineno"`
	File   string `json:"file"`
	Type   string `json:"type"`
	Line   string `json:"line"`
}

func (e *OutputError) AsLogline() string {
	return fmt.Sprintf("\033[36;1m[%d, %d]\033[31m error: \033[0m%s\n\t%s\n",
		e.Lineno,
		e.Charno,
		e.Error,
		e.Line,
	)
}

type OutputWarning struct {
	Charno  int    `json:"charno"`
	Warning string `json:"warning"`
	Lineno  int    `json:"lineno"`
	File    string `json:"file"`
	Type    string `json:"type"`
	Line    string `json:"line"`
}

func (w *OutputWarning) AsLogline() string {
	return fmt.Sprintf("\033[36;1m[%d, %d]\033[33m warning: \033[0m%s\n\t%s\n",
		w.Lineno,
		w.Charno,
		w.Warning,
		w.Line,
	)
}

type OutputServerError struct {
	Code  int    `json:"code"`
	Error string `json:"error"`
}

type OutputStatistics struct {
	OriginalSize   int `json:"originalSize"`
	CompressedSize int `json:"compressedSize"`
	CompileTime    int `json:"compileTime"`
}

type Output struct {
	CompiledCode string             `json:"compiledCode"`
	Errors       []OutputError      `json:"errors"`
	Warnings     []OutputWarning    `json:"warnings"`
	ServerErrors *OutputServerError `json:"serverErrors"`
	Statistics   OutputStatistics   `json:"statistics"`
}

func (client *Client) buildRequest(jsCode []byte) *http.Request {

	values := url.Values{}
	values.Set("js_code", string(jsCode[:]))
	values.Set("output_format", "json")
	values.Add("output_info", "compiled_code")
	values.Add("output_info", "statistics")
	values.Add("output_info", "warnings")
	values.Add("output_info", "errors")

	// TODO support ECMASCRIPT3, ECMASCRIPT5, ECMASCRIPT5_STRICT
	values.Set("language", "ECMASCRIPT5_STRICT")

	// TODO support WHITESPACE_ONLY, SIMPLE_OPTIMIZATIONS, ADVANCED_OPTIMIZATIONS
	values.Set("compilation_level", "SIMPLE_OPTIMIZATIONS")

	req, err := http.NewRequest(
		"POST",
		GoogleEndpointUrl,
		strings.NewReader(values.Encode()),
	)
	if err != nil {
		log.Fatalf(err.Error())
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	return req
}

func (client *Client) Compile(jsCode []byte) *Output {

	httpClient := http.Client{}

	req := client.buildRequest(jsCode)
	res, err := httpClient.Do(req)
	if err != nil {
		log.Fatalf(err.Error())
	}

	content, err := ioutil.ReadAll(res.Body)
	res.Body.Close()
	if err != nil {
		log.Fatalf(err.Error())
	}

	output := Output{}
	err = json.Unmarshal(content, &output)
	if err != nil {
		log.Fatalf(err.Error())
	}

	return &output
}
<commit_msg>Expose Language and CompilationLevel options<commit_after>package compilerapi

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strings"
)

const (
	GoogleEndpointUrl = "http://closure-compiler.appspot.com/compile"
)

// See https://developers.google.com/closure/compiler/docs/api-ref for details about the options
type Client struct {

	// Possible values: ECMASCRIPT3, ECMASCRIPT5, ECMASCRIPT5_STRICT, default to ECMASCRIPT5_STRICT
	Language string

	// Possible values:  WHITESPACE_ONLY, SIMPLE_OPTIMIZATIONS, ADVANCED_OPTIMIZATIONS, default to WHITESPACE_ONLY
	CompilationLevel string
}

type OutputError struct {
	Charno int    `json:"charno"`
	Error  string `json:"error"`
	Lineno int    `json:"lineno"`
	File   string `json:"file"`
	Type   string `json:"type"`
	Line   string `json:"line"`
}

func (e *OutputError) AsLogline() string {
	return fmt.Sprintf("\033[36;1m[%d, %d]\033[31m error: \033[0m%s\n\t%s\n",
		e.Lineno,
		e.Charno,
		e.Error,
		e.Line,
	)
}

type OutputWarning struct {
	Charno  int    `json:"charno"`
	Warning string `json:"warning"`
	Lineno  int    `json:"lineno"`
	File    string `json:"file"`
	Type    string `json:"type"`
	Line    string `json:"line"`
}

func (w *OutputWarning) AsLogline() string {
	return fmt.Sprintf("\033[36;1m[%d, %d]\033[33m warning: \033[0m%s\n\t%s\n",
		w.Lineno,
		w.Charno,
		w.Warning,
		w.Line,
	)
}

type OutputServerError struct {
	Code  int    `json:"code"`
	Error string `json:"error"`
}

type OutputStatistics struct {
	OriginalSize   int `json:"originalSize"`
	CompressedSize int `json:"compressedSize"`
	CompileTime    int `json:"compileTime"`
}

type Output struct {
	CompiledCode string             `json:"compiledCode"`
	Errors       []OutputError      `json:"errors"`
	Warnings     []OutputWarning    `json:"warnings"`
	ServerErrors *OutputServerError `json:"serverErrors"`
	Statistics   OutputStatistics   `json:"statistics"`
}

func (client *Client) buildRequest(jsCode []byte) *http.Request {

	values := url.Values{}
	values.Set("js_code", string(jsCode[:]))
	values.Set("output_format", "json")
	values.Add("output_info", "compiled_code")
	values.Add("output_info", "statistics")
	values.Add("output_info", "warnings")
	values.Add("output_info", "errors")

	if client.Language != "" {
		values.Set("language", client.Language)
	} else {
		values.Set("language", "ECMASCRIPT5_STRICT")
	}

	if client.CompilationLevel != "" {
		values.Set("compilation_level", client.CompilationLevel)
	} else {
		values.Set("compilation_level", "WHITESPACE_ONLY")
	}

	req, err := http.NewRequest(
		"POST",
		GoogleEndpointUrl,
		strings.NewReader(values.Encode()),
	)
	if err != nil {
		log.Fatalf(err.Error())
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	return req
}

func (client *Client) Compile(jsCode []byte) *Output {

	httpClient := http.Client{}

	req := client.buildRequest(jsCode)
	res, err := httpClient.Do(req)
	if err != nil {
		log.Fatalf(err.Error())
	}

	content, err := ioutil.ReadAll(res.Body)
	res.Body.Close()
	if err != nil {
		log.Fatalf(err.Error())
	}

	output := Output{}
	err = json.Unmarshal(content, &output)
	if err != nil {
		log.Fatalf(err.Error())
	}

	return &output
}
<|endoftext|>


<commit_before>package steam

import (
	"bytes"
	"compress/gzip"
	"crypto/rand"
	"encoding/binary"
	"fmt"
	"hash/crc32"
	"io/ioutil"
	"log"
	"net"
	"sync"
	"sync/atomic"
	"time"

	"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/netutil"
	. "github.com/Philipp15b/go-steam/steamid"
)

// 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
}

// 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() *netutil.PortAddr {
	server := GetRandomCM()
	c.ConnectTo(server)
	return server
}

// ConnectNorthAmerica Connects to a random North American server on the Steam network
func (c *Client) ConnectNorthAmerica() *netutil.PortAddr {
	server := GetRandomNorthAmericaCM()
	c.ConnectTo(server)
	return server
}

// ConnectEurope Connects to a random Europe server on the Steam network
func (c *Client) ConnectEurope() *netutil.PortAddr {
	server := GetRandomEuropeCM()
	c.ConnectTo(server)
	return server
}

// ConnectSingapore Connects to a random SG server on the Steam network
func (c *Client) ConnectSingapore() *netutil.PortAddr {
	server := GetRandomSingaporeCM()
	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 *netutil.PortAddr) {
	c.Disconnect()

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

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

// Connects to a specific server, and binds to a specified local IP
func (c *Client) ConnectToBind(addr *netutil.PortAddr, local *net.TCPAddr) {
	c.Disconnect()

	conn, err := dialTCP(addr.ToTCPAddr(), local)
	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)
	c.Emit(&DisconnectedEvent{})

}

// 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()))
}

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(&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)
	}
}

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

	l := make([]*netutil.PortAddr, 0)
	for i, ip := range body.GetCmAddresses() {
		l = append(l, &netutil.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
}
<commit_msg>Add Steam Directory to Connect()<commit_after>package steam

import (
	"bytes"
	"compress/gzip"
	"crypto/rand"
	"encoding/binary"
	"fmt"
	"hash/crc32"
	"io/ioutil"
	"log"
	"net"
	"sync"
	"sync/atomic"
	"time"

	"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/netutil"
	. "github.com/Philipp15b/go-steam/steamid"
)

// 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
}

// 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() *netutil.PortAddr {
	var server *netutil.PortAddr
	if steamDirectoryCache.IsInitialized() {
		server = steamDirectoryCache.GetRandomCM()
	} else {
		server = GetRandomCM()
	}
	c.ConnectTo(server)
	return server
}

// ConnectNorthAmerica Connects to a random North American server on the Steam network
func (c *Client) ConnectNorthAmerica() *netutil.PortAddr {
	server := GetRandomNorthAmericaCM()
	c.ConnectTo(server)
	return server
}

// ConnectEurope Connects to a random Europe server on the Steam network
func (c *Client) ConnectEurope() *netutil.PortAddr {
	server := GetRandomEuropeCM()
	c.ConnectTo(server)
	return server
}

// ConnectSingapore Connects to a random SG server on the Steam network
func (c *Client) ConnectSingapore() *netutil.PortAddr {
	server := GetRandomSingaporeCM()
	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 *netutil.PortAddr) {
	c.Disconnect()

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

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

// Connects to a specific server, and binds to a specified local IP
func (c *Client) ConnectToBind(addr *netutil.PortAddr, local *net.TCPAddr) {
	c.Disconnect()

	conn, err := dialTCP(addr.ToTCPAddr(), local)
	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)
	c.Emit(&DisconnectedEvent{})

}

// 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()))
}

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(&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)
	}
}

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

	l := make([]*netutil.PortAddr, 0)
	for i, ip := range body.GetCmAddresses() {
		l = append(l, &netutil.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>//
// Copyright (c) 2014 MessageBird B.V.
// All rights reserved.
//
// Author: Maurice Nonnekes <maurice@messagebird.com>

package messagebird

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"runtime"
	"strings"
)

const (
	ClientVersion = "2.1.1"
	Endpoint      = "https://rest.messagebird.com"
)

var (
	ErrResponse           = errors.New("The MessageBird API returned an error")
	ErrUnexpectedResponse = errors.New("The MessageBird API is currently unavailable")
)

type Client struct {
	AccessKey  string       // The API access key
	HTTPClient *http.Client // The HTTP client to send requests on
	DebugLog   *log.Logger  // Optional logger for debugging purposes
}

// New creates a new MessageBird client object.
func New(AccessKey string) *Client {
	return &Client{AccessKey: AccessKey, HTTPClient: &http.Client{}}
}

func (c *Client) request(v interface{}, path string, params *url.Values) error {
	uri, err := url.Parse(Endpoint + "/" + path)
	if err != nil {
		return err
	}

	var request *http.Request
	if params != nil {
		body := params.Encode()
		if request, err = http.NewRequest("POST", uri.String(), strings.NewReader(body)); err != nil {
			return err
		}

		if c.DebugLog != nil {
			if unescapedBody, err := url.QueryUnescape(body); err == nil {
				log.Printf("HTTP REQUEST: POST %s %s", uri.String(), unescapedBody)
			} else {
				log.Printf("HTTP REQUEST: POST %s %s", uri.String(), body)
			}
		}

		request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	} else {
		if request, err = http.NewRequest("GET", uri.String(), nil); err != nil {
			return err
		}

		if c.DebugLog != nil {
			log.Printf("HTTP REQUEST: GET %s", uri.String())
		}
	}

	request.Header.Add("Accept", "application/json")
	request.Header.Add("Authorization", "AccessKey "+c.AccessKey)
	request.Header.Add("User-Agent", "MessageBird/ApiClient/"+ClientVersion+" Go/"+runtime.Version())

	response, err := c.HTTPClient.Do(request)
	if err != nil {
		return err
	}

	defer response.Body.Close()

	responseBody, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return err
	}

	if c.DebugLog != nil {
		log.Printf("HTTP RESPONSE: %s", string(responseBody))
	}

	// Status code 500 is a server error and means nothing can be done at this
	// point.
	if response.StatusCode == 500 {
		return ErrUnexpectedResponse
	}

	if err = json.Unmarshal(responseBody, &v); err != nil {
		return err
	}

	// Status codes 200 and 201 are indicative of being able to convert the
	// response body to the struct that was specified.
	if response.StatusCode == 200 || response.StatusCode == 201 {
		return nil
	}

	// Anything else than a 200/201/500 should be a JSON error.
	return ErrResponse
}

// Balance returns the balance information for the account that is associated
// with the access key.
func (c *Client) Balance() (*Balance, error) {
	balance := &Balance{}
	if err := c.request(balance, "balance", nil); err != nil {
		if err == ErrResponse {
			return balance, err
		}

		return nil, err
	}

	return balance, nil
}

// HLR looks up an existing HLR object for the specified id that was previously
// created by the NewHLR function.
func (c *Client) HLR(id string) (*HLR, error) {
	hlr := &HLR{}
	if err := c.request(hlr, "hlr/"+id, nil); err != nil {
		if err == ErrResponse {
			return hlr, err
		}

		return nil, err
	}

	return hlr, nil
}

// NewHLR retrieves the information of an existing HLR.
func (c *Client) NewHLR(msisdn, reference string) (*HLR, error) {
	params := &url.Values{
		"msisdn":    {msisdn},
		"reference": {reference}}

	hlr := &HLR{}
	if err := c.request(hlr, "hlr", params); err != nil {
		if err == ErrResponse {
			return hlr, err
		}

		return nil, err
	}

	return hlr, nil
}

// Message retrieves the information of an existing Message.
func (c *Client) Message(id string) (*Message, error) {
	message := &Message{}
	if err := c.request(message, "messages/"+id, nil); err != nil {
		if err == ErrResponse {
			return message, err
		}

		return nil, err
	}

	return message, nil
}

// NewMessage creates a new message for one or more recipients.
func (c *Client) NewMessage(originator string, recipients []string, body string, msgParams *MessageParams) (*Message, error) {
	params, err := paramsForMessage(msgParams)
	if err != nil {
		return nil, err
	}

	params.Set("originator", originator)
	params.Set("body", body)
	params.Set("recipients", strings.Join(recipients, ","))

	message := &Message{}
	if err := c.request(message, "messages", params); err != nil {
		if err == ErrResponse {
			return message, err
		}

		return nil, err
	}

	return message, nil
}

// VoiceMessage retrieves the information of an existing VoiceMessage.
func (c *Client) VoiceMessage(id string) (*VoiceMessage, error) {
	message := &VoiceMessage{}
	if err := c.request(message, "voicemessages/"+id, nil); err != nil {
		if err == ErrResponse {
			return message, err
		}

		return nil, err
	}

	return message, nil
}

// NewVoiceMessage creates a new voice message for one or more recipients.
func (c *Client) NewVoiceMessage(recipients []string, body string, params *VoiceMessageParams) (*VoiceMessage, error) {
	urlParams := paramsForVoiceMessage(params)
	urlParams.Set("body", body)
	urlParams.Set("recipients", strings.Join(recipients, ","))

	message := &VoiceMessage{}
	if err := c.request(message, "voicemessages", urlParams); err != nil {
		if err == ErrResponse {
			return message, err
		}

		return nil, err
	}

	return message, nil
}

// OtpGenerate generates a new One-Time-Password for one recipient.
func (c *Client) OtpGenerate(recipient string, params *OtpParams) (*OtpMessage, error) {
	urlParams := paramsForOtp(params)
	urlParams.Set("recipient", recipient)

	message := &OtpMessage{}
	if err := c.request(message, "otp/generate", urlParams); err != nil {
		if err == ErrResponse {
			return message, err
		}

		return nil, err
	}

	return message, nil
}

// OtpVerify verifies the token that was generated with OtpGenerate.
func (c *Client) OtpVerify(recipient string, token string, params *OtpParams) (*OtpMessage, error) {
	urlParams := paramsForOtp(params)
	urlParams.Set("recipient", recipient)
	urlParams.Set("token", token)

	path := "otp/verify?" + urlParams.Encode()

	message := &OtpMessage{}
	if err := c.request(message, path, nil); err != nil {
		if err == ErrResponse {
			return message, err
		}

		return nil, err
	}

	return message, nil
}

// Lookup performs a new lookup for the specified number.
func (c *Client) Lookup(phoneNumber string, params *LookupParams) (*Lookup, error) {
	urlParams := paramsForLookup(params)
	path := "lookup/" + phoneNumber + "?" + urlParams.Encode()

	lookup := &Lookup{}
	if err := c.request(lookup, path, nil); err != nil {
		if err == ErrResponse {
			return lookup, err
		}

		return nil, err
	}

	return lookup, nil
}

// NewHLRLookup creates a new HLR lookup for the specified number.
func (c *Client) NewHLRLookup(phoneNumber string, lookupParams *LookupParams) (*HLR, error) {
	params := paramsForLookup(lookupParams)

	hlr := &HLR{}
	path := "lookup/" + phoneNumber + "/hlr"
	if err := c.request(hlr, path, params); err != nil {
		if err == ErrResponse {
			return hlr, err
		}

		return nil, err
	}

	return hlr, nil
}

// HLRLookup performs a HLR lookup for the specified number.
func (c *Client) HLRLookup(phoneNumber string, params *LookupParams) (*HLR, error) {
	urlParams := paramsForLookup(params)

	path := "lookup/" + phoneNumber + "/hlr?" + urlParams.Encode()

	hlr := &HLR{}
	if err := c.request(hlr, path, nil); err != nil {
		if err == ErrResponse {
			return hlr, err
		}

		return nil, err
	}

	return hlr, nil
}
<commit_msg>new lookup endpoints: small cosmetic fixes<commit_after>//
// Copyright (c) 2014 MessageBird B.V.
// All rights reserved.
//
// Author: Maurice Nonnekes <maurice@messagebird.com>

package messagebird

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"runtime"
	"strings"
)

const (
	ClientVersion = "2.1.1"
	Endpoint      = "https://rest.messagebird.com"
)

var (
	ErrResponse           = errors.New("The MessageBird API returned an error")
	ErrUnexpectedResponse = errors.New("The MessageBird API is currently unavailable")
)

type Client struct {
	AccessKey  string       // The API access key
	HTTPClient *http.Client // The HTTP client to send requests on
	DebugLog   *log.Logger  // Optional logger for debugging purposes
}

// New creates a new MessageBird client object.
func New(AccessKey string) *Client {
	return &Client{AccessKey: AccessKey, HTTPClient: &http.Client{}}
}

func (c *Client) request(v interface{}, path string, params *url.Values) error {
	uri, err := url.Parse(Endpoint + "/" + path)
	if err != nil {
		return err
	}

	var request *http.Request
	if params != nil {
		body := params.Encode()
		if request, err = http.NewRequest("POST", uri.String(), strings.NewReader(body)); err != nil {
			return err
		}

		if c.DebugLog != nil {
			if unescapedBody, err := url.QueryUnescape(body); err == nil {
				log.Printf("HTTP REQUEST: POST %s %s", uri.String(), unescapedBody)
			} else {
				log.Printf("HTTP REQUEST: POST %s %s", uri.String(), body)
			}
		}

		request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	} else {
		if request, err = http.NewRequest("GET", uri.String(), nil); err != nil {
			return err
		}

		if c.DebugLog != nil {
			log.Printf("HTTP REQUEST: GET %s", uri.String())
		}
	}

	request.Header.Add("Accept", "application/json")
	request.Header.Add("Authorization", "AccessKey "+c.AccessKey)
	request.Header.Add("User-Agent", "MessageBird/ApiClient/"+ClientVersion+" Go/"+runtime.Version())

	response, err := c.HTTPClient.Do(request)
	if err != nil {
		return err
	}

	defer response.Body.Close()

	responseBody, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return err
	}

	if c.DebugLog != nil {
		log.Printf("HTTP RESPONSE: %s", string(responseBody))
	}

	// Status code 500 is a server error and means nothing can be done at this
	// point.
	if response.StatusCode == 500 {
		return ErrUnexpectedResponse
	}

	if err = json.Unmarshal(responseBody, &v); err != nil {
		return err
	}

	// Status codes 200 and 201 are indicative of being able to convert the
	// response body to the struct that was specified.
	if response.StatusCode == 200 || response.StatusCode == 201 {
		return nil
	}

	// Anything else than a 200/201/500 should be a JSON error.
	return ErrResponse
}

// Balance returns the balance information for the account that is associated
// with the access key.
func (c *Client) Balance() (*Balance, error) {
	balance := &Balance{}
	if err := c.request(balance, "balance", nil); err != nil {
		if err == ErrResponse {
			return balance, err
		}

		return nil, err
	}

	return balance, nil
}

// HLR looks up an existing HLR object for the specified id that was previously
// created by the NewHLR function.
func (c *Client) HLR(id string) (*HLR, error) {
	hlr := &HLR{}
	if err := c.request(hlr, "hlr/"+id, nil); err != nil {
		if err == ErrResponse {
			return hlr, err
		}

		return nil, err
	}

	return hlr, nil
}

// NewHLR retrieves the information of an existing HLR.
func (c *Client) NewHLR(msisdn, reference string) (*HLR, error) {
	params := &url.Values{
		"msisdn":    {msisdn},
		"reference": {reference}}

	hlr := &HLR{}
	if err := c.request(hlr, "hlr", params); err != nil {
		if err == ErrResponse {
			return hlr, err
		}

		return nil, err
	}

	return hlr, nil
}

// Message retrieves the information of an existing Message.
func (c *Client) Message(id string) (*Message, error) {
	message := &Message{}
	if err := c.request(message, "messages/"+id, nil); err != nil {
		if err == ErrResponse {
			return message, err
		}

		return nil, err
	}

	return message, nil
}

// NewMessage creates a new message for one or more recipients.
func (c *Client) NewMessage(originator string, recipients []string, body string, msgParams *MessageParams) (*Message, error) {
	params, err := paramsForMessage(msgParams)
	if err != nil {
		return nil, err
	}

	params.Set("originator", originator)
	params.Set("body", body)
	params.Set("recipients", strings.Join(recipients, ","))

	message := &Message{}
	if err := c.request(message, "messages", params); err != nil {
		if err == ErrResponse {
			return message, err
		}

		return nil, err
	}

	return message, nil
}

// VoiceMessage retrieves the information of an existing VoiceMessage.
func (c *Client) VoiceMessage(id string) (*VoiceMessage, error) {
	message := &VoiceMessage{}
	if err := c.request(message, "voicemessages/"+id, nil); err != nil {
		if err == ErrResponse {
			return message, err
		}

		return nil, err
	}

	return message, nil
}

// NewVoiceMessage creates a new voice message for one or more recipients.
func (c *Client) NewVoiceMessage(recipients []string, body string, params *VoiceMessageParams) (*VoiceMessage, error) {
	urlParams := paramsForVoiceMessage(params)
	urlParams.Set("body", body)
	urlParams.Set("recipients", strings.Join(recipients, ","))

	message := &VoiceMessage{}
	if err := c.request(message, "voicemessages", urlParams); err != nil {
		if err == ErrResponse {
			return message, err
		}

		return nil, err
	}

	return message, nil
}

// OtpGenerate generates a new One-Time-Password for one recipient.
func (c *Client) OtpGenerate(recipient string, params *OtpParams) (*OtpMessage, error) {
	urlParams := paramsForOtp(params)
	urlParams.Set("recipient", recipient)

	message := &OtpMessage{}
	if err := c.request(message, "otp/generate", urlParams); err != nil {
		if err == ErrResponse {
			return message, err
		}

		return nil, err
	}

	return message, nil
}

// OtpVerify verifies the token that was generated with OtpGenerate.
func (c *Client) OtpVerify(recipient string, token string, params *OtpParams) (*OtpMessage, error) {
	urlParams := paramsForOtp(params)
	urlParams.Set("recipient", recipient)
	urlParams.Set("token", token)

	path := "otp/verify?" + urlParams.Encode()

	message := &OtpMessage{}
	if err := c.request(message, path, nil); err != nil {
		if err == ErrResponse {
			return message, err
		}

		return nil, err
	}

	return message, nil
}

// Lookup performs a new lookup for the specified number.
func (c *Client) Lookup(phoneNumber string, params *LookupParams) (*Lookup, error) {
	urlParams := paramsForLookup(params)
	path := "lookup/" + phoneNumber + "?" + urlParams.Encode()

	lookup := &Lookup{}
	if err := c.request(lookup, path, nil); err != nil {
		if err == ErrResponse {
			return lookup, err
		}

		return nil, err
	}

	return lookup, nil
}

// NewHLRLookup creates a new HLR lookup for the specified number.
func (c *Client) NewHLRLookup(phoneNumber string, params *LookupParams) (*HLR, error) {
	urlParams := paramsForLookup(params)
	path := "lookup/" + phoneNumber + "/hlr"

	hlr := &HLR{}
	if err := c.request(hlr, path, urlParams); err != nil {
		if err == ErrResponse {
			return hlr, err
		}

		return nil, err
	}

	return hlr, nil
}

// HLRLookup performs a HLR lookup for the specified number.
func (c *Client) HLRLookup(phoneNumber string, params *LookupParams) (*HLR, error) {
	urlParams := paramsForLookup(params)
	path := "lookup/" + phoneNumber + "/hlr?" + urlParams.Encode()

	hlr := &HLR{}
	if err := c.request(hlr, path, nil); err != nil {
		if err == ErrResponse {
			return hlr, err
		}

		return nil, err
	}

	return hlr, nil
}
<|endoftext|>


<commit_before>package gosseract

// #if __FreeBSD__ >= 10
// #cgo LDFLAGS: -L/usr/local/lib -llept -ltesseract
// #else
// #cgo LDFLAGS: -llept -ltesseract
// #endif
// #include <stdlib.h>
// #include "tessbridge.h"
import "C"
import (
	"fmt"
	"strings"
	"unsafe"
)

// Version returns the version of Tesseract-OCR
func Version() string {
	api := C.Create()
	defer C.Free(api)
	version := C.Version(api)
	return C.GoString(version)
}

// Client is argument builder for tesseract::TessBaseAPI.
type Client struct {
	api C.TessBaseAPI

	// Trim specifies characters to trim, which would be trimed from result string.
	// As results of OCR, text often contains unnecessary characters, such as newlines, on the head/foot of string.
	// If `Trim` is set, this client will remove specified characters from the result.
	Trim bool

	// TessdataPrefix can indicate directory path to `tessdata`.
	// It is set `/usr/local/share/tessdata/` or something like that, as default.
	// TODO: Implement and test
	TessdataPrefix *string

	// Languages are languages to be detected. If not specified, it's gonna be "eng".
	Languages []string

	// ImagePath is just path to image file to be processed OCR.
	ImagePath string

	// Variables is just a pool to evaluate "tesseract::TessBaseAPI->SetVariable" in delay.
	// TODO: Think if it should be public, or private property.
	Variables map[string]string

	// PageSegMode is a mode for page layout analysis.
	// See https://github.com/otiai10/gosseract/issues/52 for more information.
	PageSegMode *PageSegMode
}

// NewClient construct new Client. It's due to caller to Close this client.
func NewClient() *Client {
	client := &Client{
		api:       C.Create(),
		Variables: map[string]string{},
		Trim:      true,
	}
	return client
}

// Close frees allocated API. This MUST be called for ANY client constructed by "NewClient" function.
func (c *Client) Close() (err error) {
	// defer func() {
	// 	if e := recover(); e != nil {
	// 		err = fmt.Errorf("%v", e)
	// 	}
	// }()
	C.Free(c.api)
	return err
}

// SetImage sets path to image file to be processed OCR.
func (c *Client) SetImage(imagepath string) *Client {
	c.ImagePath = imagepath
	return c
}

// SetLanguage sets languages to use. English as default.
func (c *Client) SetLanguage(langs ...string) *Client {
	c.Languages = langs
	return c
}

// SetWhitelist sets whitelist chars.
// See official documentation for whitelist here https://github.com/tesseract-ocr/tesseract/wiki/ImproveQuality#dictionaries-word-lists-and-patterns
func (c *Client) SetWhitelist(whitelist string) *Client {
	return c.SetVariable("tessedit_char_whitelist", whitelist)
}

// SetVariable sets parameters, representing tesseract::TessBaseAPI->SetVariable.
// See official documentation here https://zdenop.github.io/tesseract-doc/classtesseract_1_1_tess_base_a_p_i.html#a2e09259c558c6d8e0f7e523cbaf5adf5
func (c *Client) SetVariable(key, value string) *Client {
	c.Variables[key] = value
	return c
}

// SetPageSegMode sets "Page Segmentation Mode" (PSM) to detect layout of characters.
// See official documentation for PSM here https://github.com/tesseract-ocr/tesseract/wiki/ImproveQuality#page-segmentation-method
func (c *Client) SetPageSegMode(mode PageSegMode) *Client {
	c.PageSegMode = &mode
	return c
}

// Text finally initialize tesseract::TessBaseAPI, execute OCR and extract text detected as string.
func (c *Client) Text() (string, error) {

	// Defer recover and make error
	var err error
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("%v", e)
		}
	}()

	// Initialize tesseract::TessBaseAPI
	if len(c.Languages) == 0 {
		C.Init(c.api, nil, nil)
	} else {
		langs := C.CString(strings.Join(c.Languages, "+"))
		defer C.free(unsafe.Pointer(langs))
		C.Init(c.api, nil, langs)
	}

	// Set Image by giving path
	imagepath := C.CString(c.ImagePath)
	defer C.free(unsafe.Pointer(imagepath))
	C.SetImage(c.api, imagepath)

	for key, value := range c.Variables {
		k, v := C.CString(key), C.CString(value)
		defer C.free(unsafe.Pointer(k))
		defer C.free(unsafe.Pointer(v))
		C.SetVariable(c.api, k, v)
	}

	if c.PageSegMode != nil {
		mode := C.int(*c.PageSegMode)
		C.SetPageSegMode(c.api, mode)
	}

	// Get text by execuitng
	out := C.GoString(C.UTF8Text(c.api))

	// Trim result if needed
	if c.Trim {
		out = strings.Trim(out, "\n")
	}

	return out, err
}
<commit_msg>Refactor a bit<commit_after>package gosseract

// #if __FreeBSD__ >= 10
// #cgo LDFLAGS: -L/usr/local/lib -llept -ltesseract
// #else
// #cgo LDFLAGS: -llept -ltesseract
// #endif
// #include <stdlib.h>
// #include "tessbridge.h"
import "C"
import (
	"fmt"
	"strings"
	"unsafe"
)

// Version returns the version of Tesseract-OCR
func Version() string {
	api := C.Create()
	defer C.Free(api)
	version := C.Version(api)
	return C.GoString(version)
}

// Client is argument builder for tesseract::TessBaseAPI.
type Client struct {
	api C.TessBaseAPI

	// Trim specifies characters to trim, which would be trimed from result string.
	// As results of OCR, text often contains unnecessary characters, such as newlines, on the head/foot of string.
	// If `Trim` is set, this client will remove specified characters from the result.
	Trim bool

	// TessdataPrefix can indicate directory path to `tessdata`.
	// It is set `/usr/local/share/tessdata/` or something like that, as default.
	// TODO: Implement and test
	TessdataPrefix *string

	// Languages are languages to be detected. If not specified, it's gonna be "eng".
	Languages []string

	// ImagePath is just path to image file to be processed OCR.
	ImagePath string

	// Variables is just a pool to evaluate "tesseract::TessBaseAPI->SetVariable" in delay.
	// TODO: Think if it should be public, or private property.
	Variables map[string]string

	// PageSegMode is a mode for page layout analysis.
	// See https://github.com/otiai10/gosseract/issues/52 for more information.
	PageSegMode *PageSegMode
}

// NewClient construct new Client. It's due to caller to Close this client.
func NewClient() *Client {
	client := &Client{
		api:       C.Create(),
		Variables: map[string]string{},
		Trim:      true,
	}
	return client
}

// Close frees allocated API. This MUST be called for ANY client constructed by "NewClient" function.
func (c *Client) Close() (err error) {
	// defer func() {
	// 	if e := recover(); e != nil {
	// 		err = fmt.Errorf("%v", e)
	// 	}
	// }()
	C.Free(c.api)
	return err
}

// SetImage sets path to image file to be processed OCR.
func (c *Client) SetImage(imagepath string) *Client {
	c.ImagePath = imagepath
	return c
}

// SetLanguage sets languages to use. English as default.
func (c *Client) SetLanguage(langs ...string) *Client {
	c.Languages = langs
	return c
}

// SetWhitelist sets whitelist chars.
// See official documentation for whitelist here https://github.com/tesseract-ocr/tesseract/wiki/ImproveQuality#dictionaries-word-lists-and-patterns
func (c *Client) SetWhitelist(whitelist string) *Client {
	return c.SetVariable("tessedit_char_whitelist", whitelist)
}

// SetVariable sets parameters, representing tesseract::TessBaseAPI->SetVariable.
// See official documentation here https://zdenop.github.io/tesseract-doc/classtesseract_1_1_tess_base_a_p_i.html#a2e09259c558c6d8e0f7e523cbaf5adf5
func (c *Client) SetVariable(key, value string) *Client {
	c.Variables[key] = value
	return c
}

// SetPageSegMode sets "Page Segmentation Mode" (PSM) to detect layout of characters.
// See official documentation for PSM here https://github.com/tesseract-ocr/tesseract/wiki/ImproveQuality#page-segmentation-method
func (c *Client) SetPageSegMode(mode PageSegMode) *Client {
	c.PageSegMode = &mode
	return c
}

// Initialize tesseract::TessBaseAPI
// TODO: add tessdata prefix
func (c *Client) init() {
	if len(c.Languages) == 0 {
		C.Init(c.api, nil, nil)
	} else {
		langs := C.CString(strings.Join(c.Languages, "+"))
		defer C.free(unsafe.Pointer(langs))
		C.Init(c.api, nil, langs)
	}
}

// Text finally initialize tesseract::TessBaseAPI, execute OCR and extract text detected as string.
func (c *Client) Text() (string, error) {

	// Defer recover and make error
	var err error
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("%v", e)
		}
	}()

	c.init()

	// Set Image by giving path
	imagepath := C.CString(c.ImagePath)
	defer C.free(unsafe.Pointer(imagepath))
	C.SetImage(c.api, imagepath)

	for key, value := range c.Variables {
		k, v := C.CString(key), C.CString(value)
		defer C.free(unsafe.Pointer(k))
		defer C.free(unsafe.Pointer(v))
		C.SetVariable(c.api, k, v)
	}

	if c.PageSegMode != nil {
		mode := C.int(*c.PageSegMode)
		C.SetPageSegMode(c.api, mode)
	}

	// Get text by execuitng
	out := C.GoString(C.UTF8Text(c.api))

	// Trim result if needed
	if c.Trim {
		out = strings.Trim(out, "\n")
	}

	return out, err
}
<|endoftext|>


<commit_before>package hdfs

import (
	"io"
	"io/ioutil"
	"os"
	"os/user"

	hdfs "github.com/colinmarc/hdfs/protocol/hadoop_hdfs"
	"github.com/colinmarc/hdfs/rpc"
)

// A Client represents a connection to an HDFS cluster
type Client struct {
	namenode *rpc.NamenodeConnection
	defaults *hdfs.FsServerDefaultsProto
}

// ClientOptions represents the configurable options for a client.
type ClientOptions struct {
	Addresses []string
	Namenode  *rpc.NamenodeConnection
	User      string
}

// Username returns the value of HADOOP_USER_NAME in the environment, or
// the current system user if it is not set.
func Username() (string, error) {
	username := os.Getenv("HADOOP_USER_NAME")
	if username != "" {
		return username, nil
	}
	currentUser, err := user.Current()
	if err != nil {
		return "", err
	}
	return currentUser.Username, nil
}

// NewClient returns a connected Client for the given options, or an error if
// the client could not be created.
func NewClient(options ClientOptions) (*Client, error) {
	var err error

	if options.User == "" {
		options.User, err = Username()
		if err != nil {
			return nil, err
		}
	}

	if options.Addresses == nil || len(options.Addresses) == 0 {
		options.Addresses, err = getNameNodeFromConf()
		if err != nil {
			return nil, err
		}
	}

	if options.Namenode == nil {
		options.Namenode, err = rpc.NewNamenodeConnectionWithOptions(
			rpc.NamenodeConnectionOptions{
				Addresses: options.Addresses,
				User:      options.User,
			},
		)
		if err != nil {
			return nil, err
		}
	}

	return &Client{namenode: options.Namenode}, nil
}

// New returns a connected Client, or an error if it can't connect. The user
// will be the user the code is running under. If address is an empty string
// it will try and get the namenode address from the hadoop configuration
// files.
func New(address string) (*Client, error) {
	options := ClientOptions{}

	if address != "" {
		options.Addresses = []string{address}
	}

	return NewClient(options)
}

// getNameNodeFromConf returns namenodes from the system Hadoop configuration.
func getNameNodeFromConf() ([]string, error) {
	hadoopConf := LoadHadoopConf("")

	namenodes, nnErr := hadoopConf.Namenodes()
	if nnErr != nil {
		return nil, nnErr
	}
	return namenodes, nil
}

// NewForUser returns a connected Client with the user specified, or an error if
// it can't connect.
//
// Deprecated: Use NewClient with ClientOptions instead.
func NewForUser(address string, user string) (*Client, error) {
	return NewClient(ClientOptions{
		Addresses: []string{address},
		User:      user,
	})
}

// NewForConnection returns Client with the specified, underlying rpc.NamenodeConnection.
// You can use rpc.WrapNamenodeConnection to wrap your own net.Conn.
//
// Deprecated: Use NewClient with ClientOptions instead.
func NewForConnection(namenode *rpc.NamenodeConnection) *Client {
	client, _ := NewClient(ClientOptions{Namenode: namenode})
	return client
}

// ReadFile reads the file named by filename and returns the contents.
func (c *Client) ReadFile(filename string) ([]byte, error) {
	f, err := c.Open(filename)
	if err != nil {
		return nil, err
	}

	defer f.Close()
	return ioutil.ReadAll(f)
}

// CopyToLocal copies the HDFS file specified by src to the local file at dst.
// If dst already exists, it will be overwritten.
func (c *Client) CopyToLocal(src string, dst string) error {
	remote, err := c.Open(src)
	if err != nil {
		return err
	}
	defer remote.Close()

	local, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer local.Close()

	_, err = io.Copy(local, remote)
	return err
}

// CopyToRemote copies the local file specified by src to the HDFS file at dst.
func (c *Client) CopyToRemote(src string, dst string) error {
	local, err := os.Open(src)
	if err != nil {
		return err
	}
	defer local.Close()

	remote, err := c.Create(dst)
	if err != nil {
		return err
	}
	defer remote.Close()

	_, err = io.Copy(remote, local)
	return err
}

func (c *Client) fetchDefaults() (*hdfs.FsServerDefaultsProto, error) {
	if c.defaults != nil {
		return c.defaults, nil
	}

	req := &hdfs.GetServerDefaultsRequestProto{}
	resp := &hdfs.GetServerDefaultsResponseProto{}

	err := c.namenode.Execute("getServerDefaults", req, resp)
	if err != nil {
		return nil, err
	}

	c.defaults = resp.GetServerDefaults()
	return c.defaults, nil
}

// Close terminates all underlying socket connections to remote server.
func (c *Client) Close() error {
	return c.namenode.Close()
}
<commit_msg>Only parse addresses/user out of the configuration for New, not NewClient<commit_after>package hdfs

import (
	"io"
	"io/ioutil"
	"os"
	"os/user"

	hdfs "github.com/colinmarc/hdfs/protocol/hadoop_hdfs"
	"github.com/colinmarc/hdfs/rpc"
)

// A Client represents a connection to an HDFS cluster
type Client struct {
	namenode *rpc.NamenodeConnection
	defaults *hdfs.FsServerDefaultsProto
}

// ClientOptions represents the configurable options for a client.
type ClientOptions struct {
	// Addresses specifies the namenode(s) to connect to.
	Addresses []string
	// User specifies which HDFS user the client will act as.
	User string
	// Namenode optionally specifies an existing NamenodeConnection to wrap. This
	// is useful if you needed to create the namenode net.Conn manually for
	// whatever reason.
	Namenode *rpc.NamenodeConnection
}

// Username returns the value of HADOOP_USER_NAME in the environment, or
// the current system user if it is not set.
func Username() (string, error) {
	username := os.Getenv("HADOOP_USER_NAME")
	if username != "" {
		return username, nil
	}
	currentUser, err := user.Current()
	if err != nil {
		return "", err
	}
	return currentUser.Username, nil
}

// NewClient returns a connected Client for the given options, or an error if
// the client could not be created.
func NewClient(options ClientOptions) (*Client, error) {
	var err error

	if options.Namenode == nil {
		options.Namenode, err = rpc.NewNamenodeConnectionWithOptions(
			rpc.NamenodeConnectionOptions{
				Addresses: options.Addresses,
				User:      options.User,
			},
		)
		if err != nil {
			return nil, err
		}
	}

	return &Client{namenode: options.Namenode}, nil
}

// New returns a connected Client, or an error if it can't connect. The user
// will be the user the code is running under. If address is an empty string
// it will try and get the namenode address from the hadoop configuration
// files.
func New(address string) (*Client, error) {
	options := ClientOptions{}

	if address == "" {
		options.Addresses, err = getNameNodeFromConf()
		if err != nil {
			return nil, err
		}
	} else {
		options.Addresses = []string{address}
	}

	options.User, err = Username()
	if err != nil {
		return nil, err
	}

	return NewClient(options)
}

// getNameNodeFromConf returns namenodes from the system Hadoop configuration.
func getNameNodeFromConf() ([]string, error) {
	hadoopConf := LoadHadoopConf("")

	namenodes, nnErr := hadoopConf.Namenodes()
	if nnErr != nil {
		return nil, nnErr
	}
	return namenodes, nil
}

// NewForUser returns a connected Client with the user specified, or an error if
// it can't connect.
//
// Deprecated: Use NewClient with ClientOptions instead.
func NewForUser(address string, user string) (*Client, error) {
	return NewClient(ClientOptions{
		Addresses: []string{address},
		User:      user,
	})
}

// NewForConnection returns Client with the specified, underlying rpc.NamenodeConnection.
// You can use rpc.WrapNamenodeConnection to wrap your own net.Conn.
//
// Deprecated: Use NewClient with ClientOptions instead.
func NewForConnection(namenode *rpc.NamenodeConnection) *Client {
	client, _ := NewClient(ClientOptions{Namenode: namenode})
	return client
}

// ReadFile reads the file named by filename and returns the contents.
func (c *Client) ReadFile(filename string) ([]byte, error) {
	f, err := c.Open(filename)
	if err != nil {
		return nil, err
	}

	defer f.Close()
	return ioutil.ReadAll(f)
}

// CopyToLocal copies the HDFS file specified by src to the local file at dst.
// If dst already exists, it will be overwritten.
func (c *Client) CopyToLocal(src string, dst string) error {
	remote, err := c.Open(src)
	if err != nil {
		return err
	}
	defer remote.Close()

	local, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer local.Close()

	_, err = io.Copy(local, remote)
	return err
}

// CopyToRemote copies the local file specified by src to the HDFS file at dst.
func (c *Client) CopyToRemote(src string, dst string) error {
	local, err := os.Open(src)
	if err != nil {
		return err
	}
	defer local.Close()

	remote, err := c.Create(dst)
	if err != nil {
		return err
	}
	defer remote.Close()

	_, err = io.Copy(remote, local)
	return err
}

func (c *Client) fetchDefaults() (*hdfs.FsServerDefaultsProto, error) {
	if c.defaults != nil {
		return c.defaults, nil
	}

	req := &hdfs.GetServerDefaultsRequestProto{}
	resp := &hdfs.GetServerDefaultsResponseProto{}

	err := c.namenode.Execute("getServerDefaults", req, resp)
	if err != nil {
		return nil, err
	}

	c.defaults = resp.GetServerDefaults()
	return c.defaults, nil
}

// Close terminates all underlying socket connections to remote server.
func (c *Client) Close() error {
	return c.namenode.Close()
}
<|endoftext|>