<commit_before>package rpc2

import (
	"errors"
	"io"
	"log"
	"reflect"
	"sync"
)

// Client represents an RPC Client.
// There may be multiple outstanding Calls associated
// with a single Client, and a Client may be used by
// multiple goroutines simultaneously.
type Client struct {
	mutex      sync.Mutex // protects pending, seq, request
	sending    sync.Mutex
	request    Request // temp area used in send()
	seq        uint64
	pending    map[uint64]*Call
	closing    bool
	shutdown   bool
	codec      Codec
	handlers   map[string]*handler
	disconnect chan struct{}
}

// NewClient returns a new Client to handle requests to the
// set of services at the other end of the connection.
// It adds a buffer to the write side of the connection so
// the header and payload are sent as a unit.
func NewClient(conn io.ReadWriteCloser) *Client {
	return NewClientWithCodec(newGobCodec(conn))
}

// NewClientWithCodec is like NewClient but uses the specified
// codec to encode requests and decode responses.
func NewClientWithCodec(codec Codec) *Client {
	return &Client{
		codec:      codec,
		pending:    make(map[uint64]*Call),
		handlers:   make(map[string]*handler),
		disconnect: make(chan struct{}),
	}
}

// Run the client's read loop.
// You must run this method before calling any methods on the server.
func (c *Client) Run() {
	c.readLoop()
}

// DisconnectNotify returns a channel that is closed
// when the client connection has gone away.
func (c *Client) DisconnectNotify() chan struct{} {
	return c.disconnect
}

// Handle registers the handler function for the given method. If a handler already exists for method, Handle panics.
func (c *Client) Handle(method string, handlerFunc interface{}) {
	addHandler(c.handlers, method, handlerFunc)
}

// readLoop reads messages from codec.
// It reads a reqeust or a response to the previous request.
// If the message is request, calls the handler function.
// If the message is response, sends the reply to the associated call.
func (c *Client) readLoop() {
	var err error
	var req Request
	var resp Response
	for {
		req = Request{}
		resp = Response{}
		if err = c.codec.ReadHeader(&req, &resp); err != nil {
			break
		}

		if req.Method != "" {
			// request comes to server
			if err := c.readRequest(&req); err != nil {
				log.Println("rpc2: error reading request:", err.Error())
			}
		} else {
			// response comes to client
			if err := c.readResponse(&resp); err != nil {
				log.Println("rpc2: error reading response:", err.Error())
			}
		}
	}
	// Terminate pending calls.
	c.sending.Lock()
	c.mutex.Lock()
	c.shutdown = true
	closing := c.closing
	if err == io.EOF {
		if closing {
			err = ErrShutdown
		} else {
			err = io.ErrUnexpectedEOF
		}
	}
	for _, call := range c.pending {
		call.Error = err
		call.done()
	}
	c.mutex.Unlock()
	c.sending.Unlock()
	if err != io.EOF && !closing {
		log.Println("rpc2: client protocol error:", err)
	}
	close(c.disconnect)
}

func (c *Client) readRequest(req *Request) error {
	method := c.handlers[req.Method]

	// Decode the argument value.
	var argv reflect.Value
	argIsValue := false // if true, need to indirect before calling.
	if method.argType.Kind() == reflect.Ptr {
		argv = reflect.New(method.argType.Elem())
	} else {
		argv = reflect.New(method.argType)
		argIsValue = true
	}
	// argv guaranteed to be a pointer now.
	if err := c.codec.ReadRequestBody(argv.Interface()); err != nil {
		return err
	}
	if argIsValue {
		argv = argv.Elem()
	}

	// Invoke the method, providing a new value for the reply.
	replyv := reflect.New(method.replyType.Elem())

	// Call handler function.
	go func(req Request) error {
		returnValues := method.fn.Call([]reflect.Value{reflect.ValueOf(c), argv, replyv})
		// The return value for the method is an error.
		errInter := returnValues[0].Interface()
		errmsg := ""
		if errInter != nil {
			errmsg = errInter.(error).Error()
		}
		resp := &Response{
			Seq:   req.Seq,
			Error: errmsg,
		}
		return c.codec.WriteResponse(resp, replyv.Interface())
	}(*req)

	return nil
}

func (c *Client) readResponse(resp *Response) error {
	seq := resp.Seq
	c.mutex.Lock()
	call := c.pending[seq]
	delete(c.pending, seq)
	c.mutex.Unlock()

	var err error
	switch {
	case call == nil:
		// We've got no pending call. That usually means that
		// WriteRequest partially failed, and call was already
		// removed; response is a server telling us about an
		// error reading request body. We should still attempt
		// to read error body, but there's no one to give it to.
		err = c.codec.ReadResponseBody(nil)
		if err != nil {
			err = errors.New("reading error body: " + err.Error())
		}
	case resp.Error != "":
		// We've got an error response. Give this to the request;
		// any subsequent requests will get the ReadResponseBody
		// error if there is one.
		call.Error = ServerError(resp.Error)
		err = c.codec.ReadResponseBody(nil)
		if err != nil {
			err = errors.New("reading error body: " + err.Error())
		}
		call.done()
	default:
		err = c.codec.ReadResponseBody(call.Reply)
		if err != nil {
			call.Error = errors.New("reading body " + err.Error())
		}
		call.done()
	}

	return nil
}

// Close waits for active calls to finish and closes the codec.
func (c *Client) Close() error {
	c.mutex.Lock()
	if c.shutdown || c.closing {
		c.mutex.Unlock()
		return ErrShutdown
	}
	c.closing = true
	c.mutex.Unlock()
	return c.codec.Close()
}

// Go invokes the function asynchronously.  It returns the Call structure representing
// the invocation.  The done channel will signal when the call is complete by returning
// the same Call object.  If done is nil, Go will allocate a new channel.
// If non-nil, done must be buffered or Go will deliberately crash.
func (c *Client) Go(Method string, args interface{}, reply interface{}, done chan *Call) *Call {
	call := new(Call)
	call.Method = Method
	call.Args = args
	call.Reply = reply
	if done == nil {
		done = make(chan *Call, 10) // buffered.
	} else {
		// If caller passes done != nil, it must arrange that
		// done has enough buffer for the number of simultaneous
		// RPCs that will be using that channel.  If the channel
		// is totally unbuffered, it's best not to run at all.
		if cap(done) == 0 {
			log.Panic("rpc2: done channel is unbuffered")
		}
	}
	call.Done = done
	c.send(call)
	return call
}

// Call invokes the named function, waits for it to complete, and returns its error status.
func (c *Client) Call(Method string, args interface{}, reply interface{}) error {
	call := <-c.Go(Method, args, reply, make(chan *Call, 1)).Done
	return call.Error
}

func (call *Call) done() {
	select {
	case call.Done <- call:
		// ok
	default:
		// We don't want to block here.  It is the caller's responsibility to make
		// sure the channel has enough buffer space. See comment in Go().
		log.Println("rpc2: discarding Call reply due to insufficient Done chan capacity")
	}
}

// ServerError represents an error that has been returned from
// the remote side of the RPC connection.
type ServerError string

func (e ServerError) Error() string {
	return string(e)
}

var ErrShutdown = errors.New("connection is shut down")

// Call represents an active RPC.
type Call struct {
	Method string      // The name of the service and method to call.
	Args   interface{} // The argument to the function (*struct).
	Reply  interface{} // The reply from the function (*struct).
	Error  error       // After completion, the error status.
	Done   chan *Call  // Strobes when call is complete.
}

func (c *Client) send(call *Call) {
	c.sending.Lock()
	defer c.sending.Unlock()

	// Register this call.
	c.mutex.Lock()
	if c.shutdown || c.closing {
		call.Error = ErrShutdown
		c.mutex.Unlock()
		call.done()
		return
	}
	seq := c.seq
	c.seq++
	c.pending[seq] = call
	c.mutex.Unlock()

	// Encode and send the request.
	c.request.Seq = seq
	c.request.Method = call.Method
	err := c.codec.WriteRequest(&c.request, call.Args)
	if err != nil {
		c.mutex.Lock()
		call = c.pending[seq]
		delete(c.pending, seq)
		c.mutex.Unlock()
		if call != nil {
			call.Error = err
			call.done()
		}
	}
}
<commit_msg>lowercase variable names<commit_after>package rpc2

import (
	"errors"
	"io"
	"log"
	"reflect"
	"sync"
)

// Client represents an RPC Client.
// There may be multiple outstanding Calls associated
// with a single Client, and a Client may be used by
// multiple goroutines simultaneously.
type Client struct {
	mutex      sync.Mutex // protects pending, seq, request
	sending    sync.Mutex
	request    Request // temp area used in send()
	seq        uint64
	pending    map[uint64]*Call
	closing    bool
	shutdown   bool
	codec      Codec
	handlers   map[string]*handler
	disconnect chan struct{}
}

// NewClient returns a new Client to handle requests to the
// set of services at the other end of the connection.
// It adds a buffer to the write side of the connection so
// the header and payload are sent as a unit.
func NewClient(conn io.ReadWriteCloser) *Client {
	return NewClientWithCodec(newGobCodec(conn))
}

// NewClientWithCodec is like NewClient but uses the specified
// codec to encode requests and decode responses.
func NewClientWithCodec(codec Codec) *Client {
	return &Client{
		codec:      codec,
		pending:    make(map[uint64]*Call),
		handlers:   make(map[string]*handler),
		disconnect: make(chan struct{}),
	}
}

// Run the client's read loop.
// You must run this method before calling any methods on the server.
func (c *Client) Run() {
	c.readLoop()
}

// DisconnectNotify returns a channel that is closed
// when the client connection has gone away.
func (c *Client) DisconnectNotify() chan struct{} {
	return c.disconnect
}

// Handle registers the handler function for the given method. If a handler already exists for method, Handle panics.
func (c *Client) Handle(method string, handlerFunc interface{}) {
	addHandler(c.handlers, method, handlerFunc)
}

// readLoop reads messages from codec.
// It reads a reqeust or a response to the previous request.
// If the message is request, calls the handler function.
// If the message is response, sends the reply to the associated call.
func (c *Client) readLoop() {
	var err error
	var req Request
	var resp Response
	for {
		req = Request{}
		resp = Response{}
		if err = c.codec.ReadHeader(&req, &resp); err != nil {
			break
		}

		if req.Method != "" {
			// request comes to server
			if err := c.readRequest(&req); err != nil {
				log.Println("rpc2: error reading request:", err.Error())
			}
		} else {
			// response comes to client
			if err := c.readResponse(&resp); err != nil {
				log.Println("rpc2: error reading response:", err.Error())
			}
		}
	}
	// Terminate pending calls.
	c.sending.Lock()
	c.mutex.Lock()
	c.shutdown = true
	closing := c.closing
	if err == io.EOF {
		if closing {
			err = ErrShutdown
		} else {
			err = io.ErrUnexpectedEOF
		}
	}
	for _, call := range c.pending {
		call.Error = err
		call.done()
	}
	c.mutex.Unlock()
	c.sending.Unlock()
	if err != io.EOF && !closing {
		log.Println("rpc2: client protocol error:", err)
	}
	close(c.disconnect)
}

func (c *Client) readRequest(req *Request) error {
	method := c.handlers[req.Method]

	// Decode the argument value.
	var argv reflect.Value
	argIsValue := false // if true, need to indirect before calling.
	if method.argType.Kind() == reflect.Ptr {
		argv = reflect.New(method.argType.Elem())
	} else {
		argv = reflect.New(method.argType)
		argIsValue = true
	}
	// argv guaranteed to be a pointer now.
	if err := c.codec.ReadRequestBody(argv.Interface()); err != nil {
		return err
	}
	if argIsValue {
		argv = argv.Elem()
	}

	// Invoke the method, providing a new value for the reply.
	replyv := reflect.New(method.replyType.Elem())

	// Call handler function.
	go func(req Request) error {
		returnValues := method.fn.Call([]reflect.Value{reflect.ValueOf(c), argv, replyv})
		// The return value for the method is an error.
		errInter := returnValues[0].Interface()
		errmsg := ""
		if errInter != nil {
			errmsg = errInter.(error).Error()
		}
		resp := &Response{
			Seq:   req.Seq,
			Error: errmsg,
		}
		return c.codec.WriteResponse(resp, replyv.Interface())
	}(*req)

	return nil
}

func (c *Client) readResponse(resp *Response) error {
	seq := resp.Seq
	c.mutex.Lock()
	call := c.pending[seq]
	delete(c.pending, seq)
	c.mutex.Unlock()

	var err error
	switch {
	case call == nil:
		// We've got no pending call. That usually means that
		// WriteRequest partially failed, and call was already
		// removed; response is a server telling us about an
		// error reading request body. We should still attempt
		// to read error body, but there's no one to give it to.
		err = c.codec.ReadResponseBody(nil)
		if err != nil {
			err = errors.New("reading error body: " + err.Error())
		}
	case resp.Error != "":
		// We've got an error response. Give this to the request;
		// any subsequent requests will get the ReadResponseBody
		// error if there is one.
		call.Error = ServerError(resp.Error)
		err = c.codec.ReadResponseBody(nil)
		if err != nil {
			err = errors.New("reading error body: " + err.Error())
		}
		call.done()
	default:
		err = c.codec.ReadResponseBody(call.Reply)
		if err != nil {
			call.Error = errors.New("reading body " + err.Error())
		}
		call.done()
	}

	return nil
}

// Close waits for active calls to finish and closes the codec.
func (c *Client) Close() error {
	c.mutex.Lock()
	if c.shutdown || c.closing {
		c.mutex.Unlock()
		return ErrShutdown
	}
	c.closing = true
	c.mutex.Unlock()
	return c.codec.Close()
}

// Go invokes the function asynchronously.  It returns the Call structure representing
// the invocation.  The done channel will signal when the call is complete by returning
// the same Call object.  If done is nil, Go will allocate a new channel.
// If non-nil, done must be buffered or Go will deliberately crash.
func (c *Client) Go(method string, args interface{}, reply interface{}, done chan *Call) *Call {
	call := new(Call)
	call.Method = method
	call.Args = args
	call.Reply = reply
	if done == nil {
		done = make(chan *Call, 10) // buffered.
	} else {
		// If caller passes done != nil, it must arrange that
		// done has enough buffer for the number of simultaneous
		// RPCs that will be using that channel.  If the channel
		// is totally unbuffered, it's best not to run at all.
		if cap(done) == 0 {
			log.Panic("rpc2: done channel is unbuffered")
		}
	}
	call.Done = done
	c.send(call)
	return call
}

// Call invokes the named function, waits for it to complete, and returns its error status.
func (c *Client) Call(method string, args interface{}, reply interface{}) error {
	call := <-c.Go(method, args, reply, make(chan *Call, 1)).Done
	return call.Error
}

func (call *Call) done() {
	select {
	case call.Done <- call:
		// ok
	default:
		// We don't want to block here.  It is the caller's responsibility to make
		// sure the channel has enough buffer space. See comment in Go().
		log.Println("rpc2: discarding Call reply due to insufficient Done chan capacity")
	}
}

// ServerError represents an error that has been returned from
// the remote side of the RPC connection.
type ServerError string

func (e ServerError) Error() string {
	return string(e)
}

var ErrShutdown = errors.New("connection is shut down")

// Call represents an active RPC.
type Call struct {
	Method string      // The name of the service and method to call.
	Args   interface{} // The argument to the function (*struct).
	Reply  interface{} // The reply from the function (*struct).
	Error  error       // After completion, the error status.
	Done   chan *Call  // Strobes when call is complete.
}

func (c *Client) send(call *Call) {
	c.sending.Lock()
	defer c.sending.Unlock()

	// Register this call.
	c.mutex.Lock()
	if c.shutdown || c.closing {
		call.Error = ErrShutdown
		c.mutex.Unlock()
		call.done()
		return
	}
	seq := c.seq
	c.seq++
	c.pending[seq] = call
	c.mutex.Unlock()

	// Encode and send the request.
	c.request.Seq = seq
	c.request.Method = call.Method
	err := c.codec.WriteRequest(&c.request, call.Args)
	if err != nil {
		c.mutex.Lock()
		call = c.pending[seq]
		delete(c.pending, seq)
		c.mutex.Unlock()
		if call != nil {
			call.Error = err
			call.done()
		}
	}
}
<|endoftext|>


<commit_before>package main

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

	"./vindinium"
)

// Client is responsible for interacting with the bots and executing their moves
// on the server.
type Client struct {
	Server    string
	Key       string
	Bot       vindinium.Bot
	ArenaMode bool

	state vindinium.State
}

// Play plays a game using the given client.
func (c *Client) Play() GameResult {
	var err error

	err = c.startGame()
	if err == nil {
		err = c.playGame()
	}

	return GameResult{
		Error:     err,
		LastState: c.state,
	}
}

func (c *Client) startGame() error {
	startURL := c.getStartURL()
	formValues := make(url.Values)
	formValues.Set("key", c.Key)

	resp, err := http.PostForm(startURL, formValues)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode == 200 {
		err = c.unmarshalState(resp.Body)
		return err
	}

	return c.formatResponseError(resp)

}

func (c *Client) playGame() error {
	for {
		if c.state.Game.Finished {
			return nil
		}

		dir := c.Bot.Move(c.state)

		formValues := make(url.Values)
		formValues.Set("key", c.Key)
		formValues.Set("dir", string(dir))

		resp, err := http.PostForm(c.state.PlayURL, formValues)
		if err != nil {
			return err
		}
		defer resp.Body.Close()

		if resp.StatusCode == 200 {
			err = c.unmarshalState(resp.Body)
			if err != nil {
				return err
			}
			continue
		}

		return c.formatResponseError(resp)
	}
}

func (c Client) getStartURL() string {
	if c.ArenaMode {
		return c.Server + "/api/arena"
	}
	return c.Server + "/api/training"
}

func (c *Client) unmarshalState(body io.ReadCloser) error {
	data, err := ioutil.ReadAll(body)
	if err != nil {
		return fmt.Errorf("error while reading server response: %s", err.Error())
	}

	err = json.Unmarshal(data, &c.state)
	return err
}

func (c Client) formatResponseError(resp *http.Response) error {
	if resp.StatusCode >= 500 {
		return fmt.Errorf("The server responded with status %d.", resp.StatusCode)
	}

	if resp.StatusCode >= 400 {
		reason, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return fmt.Errorf("error while reading server response: %s", err.Error())
		}

		return fmt.Errorf("Request error (status %d): %s", resp.StatusCode, string(reason))
	}

	return fmt.Errorf("received unexpected status code from server: %d", resp.StatusCode)
}
<commit_msg>Client: Actually invoke EndOfGame<commit_after>package main

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

	"./vindinium"
)

// Client is responsible for interacting with the bots and executing their moves
// on the server.
type Client struct {
	Server    string
	Key       string
	Bot       vindinium.Bot
	ArenaMode bool

	state vindinium.State
}

// Play plays a game using the given client.
func (c *Client) Play() GameResult {
	var err error

	err = c.startGame()
	if err == nil {
		err = c.playGame()
		c.Bot.EndOfGame(err, c.state)
	}

	return GameResult{
		Error:     err,
		LastState: c.state,
	}
}

func (c *Client) startGame() error {
	startURL := c.getStartURL()
	formValues := make(url.Values)
	formValues.Set("key", c.Key)

	resp, err := http.PostForm(startURL, formValues)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode == 200 {
		err = c.unmarshalState(resp.Body)
		return err
	}

	return c.formatResponseError(resp)
}

func (c *Client) playGame() error {
	for {
		if c.state.Game.Finished {
			return nil
		}

		dir := c.Bot.Move(c.state)

		formValues := make(url.Values)
		formValues.Set("key", c.Key)
		formValues.Set("dir", string(dir))

		resp, err := http.PostForm(c.state.PlayURL, formValues)
		if err != nil {
			return err
		}
		defer resp.Body.Close()

		if resp.StatusCode == 200 {
			err = c.unmarshalState(resp.Body)
			if err != nil {
				return err
			}
			continue
		}

		return c.formatResponseError(resp)
	}
}

func (c Client) getStartURL() string {
	if c.ArenaMode {
		return c.Server + "/api/arena"
	}
	return c.Server + "/api/training"
}

func (c *Client) unmarshalState(body io.ReadCloser) error {
	data, err := ioutil.ReadAll(body)
	if err != nil {
		return fmt.Errorf("error while reading server response: %s", err.Error())
	}

	err = json.Unmarshal(data, &c.state)
	return err
}

func (c Client) formatResponseError(resp *http.Response) error {
	if resp.StatusCode >= 500 {
		return fmt.Errorf("The server responded with status %d.", resp.StatusCode)
	}

	if resp.StatusCode >= 400 {
		reason, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return fmt.Errorf("error while reading server response: %s", err.Error())
		}

		return fmt.Errorf("Request error (status %d): %s", resp.StatusCode, string(reason))
	}

	return fmt.Errorf("received unexpected status code from server: %d", resp.StatusCode)
}
<|endoftext|>


<commit_before>/*
Tiny web server to handle slack outgoing webhook and push the data to resque
*/
package main

import (
	"fmt"
	"flag"
	"net/http"
	"net/url"
	"encoding/json"
	"strings"
	"github.com/fzzy/radix/redis"
	"github.com/glacjay/goini"
)

// dictionary of string
type Dict map[string]bool
func StringToDict(s string, d Dict) {
	for _, v := range strings.Split(s, ",") {
		d[v] = true
	}
	return
}

// access rule
type AccessRule struct {
	Name string
	Users Dict
	Policy bool
	Class string
	Queue string
	Allow_msg string
	Deny_msg string
}

type AccessList map[string]*AccessRule

// some default global variable
var (
	PORT string = ":8765"
	API_PATH string = "/api"
	REDIS string = "localhost:6379"
	CLASS string = "SlackOPS"
	QUEUE string = "slackops"
	TOKENS Dict = make(Dict)
	VERBOSE bool = false
	FLAGS Dict = make(Dict)
	ACCESS_LIST AccessList = make(AccessList)
	DEFAULT_ALLOW_RESPONSE_TEXT = "please wait"
	DEFAULT_DENY_RESPONSE_TEXT = "task denied"
	UNDEFINED_JOB_CAN_PASS = true
	err error
)

func check_flag(f *flag.Flag) {
	FLAGS[f.Name] = true
}

func main() {
	// config parameter
	p_port := flag.String("p", "8765", "listen port")
	p_redis := flag.String("r", "localhost:6379", "redis host")
	p_class := flag.String("c", "SlackOPS", "resque class")
	p_queue := flag.String("q", "slackops", "resque queue")
	p_tokens := flag.String("t", "", "slack tokens, split by a comma (,)")
	p_verbose := flag.Bool("v", false, "verbose")
	p_config := flag.String("C", "", "configuration file")
	flag.Parse()

	// mark parsed flags
	flag.Visit(check_flag)

	// read config file
	if FLAGS["C"] {
		config, err := ini.Load(*p_config)
		if err != nil {
			fmt.Println("error:", err)
			return
		}

		// load general config
		c_port, exist := config.GetString("general", "port")
		if exist {
			PORT = ":" + c_port
		}
		c_redis, exist := config.GetString("general", "redis")
		if exist {
			REDIS = c_redis
		}
		c_class, exist := config.GetString("general", "class")
		if exist {
			CLASS = c_class
		}
		c_queue, exist := config.GetString("general", "queue")
		if exist {
			QUEUE = c_queue
		}
		c_tokens, exist := config.GetString("general", "tokens")
		if exist {
			StringToDict(c_tokens, TOKENS)
		}
		c_verbose, exist := config.GetBool("general", "verbose")
		if exist {
			VERBOSE = c_verbose
		}

		// load access list
		for _, s := range config.GetSections() {
			if strings.HasPrefix(s, "job: ") {
				// read users and policy (mandatory)
				users_string, e_users := config.GetString(s, "users")
				policy_string, e_policy := config.GetString(s, "policy")
				if !(e_users && e_policy) {
					fmt.Printf("Skipping %s\nError: policy and users are mandatory in a job", s)
					continue
				}

				// read optional config
				allow_msg, _ := config.GetString(s, "allow_msg")
				deny_msg, _ := config.GetString(s, "deny_msg")
				class, _ := config.GetString(s, "class")
				queue, _ := config.GetString(s, "queue")

				// parse users and policy
				var policy bool
				if policy_string == "allow" {
					policy = true
				} else if policy_string == "deny" {
					policy = false
				} else {
					fmt.Printf("Skipping %s\nError: policy must be either 'allow' or 'deny'", s)
					continue
				}
				users := make(Dict)
				StringToDict(users_string, users)

				// create AccessRule
				ar := AccessRule{
					Name: strings.TrimPrefix(s, "job: "),
					Users: users,
					Policy: policy,
					Class: class,
					Queue: queue,
					Allow_msg: allow_msg,
					Deny_msg: deny_msg,
				}
				ACCESS_LIST[ar.Name] = &ar
			}
		}
	}

	// override configuration with flags
	if FLAGS["p"] {
		PORT = ":" + *p_port
	}
	if FLAGS["r"] {
		REDIS = *p_redis
	}
	if FLAGS["c"] {
		CLASS = *p_class
	}
	if FLAGS["q"] {
		QUEUE = *p_queue
	}
	if FLAGS["t"] {
		StringToDict(*p_tokens, TOKENS)
	}
	if FLAGS["v"] {
		VERBOSE = *p_verbose
	}

	if VERBOSE {
		fmt.Printf("Listening on port %s using resque at %s\n", PORT, REDIS)
		fmt.Printf("Accepting tokens:\n")
		for t, _ := range TOKENS {
			fmt.Printf("+ %s\n", t)
		}
		fmt.Printf("Jobs will be pushed to queue %s, class %s\n", QUEUE, CLASS)
	}

	// connect to redis and add queue
	rcon, err := redis.Dial("tcp", REDIS)
	if err != nil {
		fmt.Println("error:", err)
		return
	}
	rcon.Cmd("SADD", "resque:queues", QUEUE)
	// add queue optionally specified in jobs
	for k, v := range ACCESS_LIST {
		if len(v.Queue) > 0 {
			rcon.Cmd("SADD", "resque:queues", v.Queue)
			if VERBOSE {
				fmt.Printf("Job '%s' will be pushed to queue %s", k, v.Queue)
				if len(v.Class) > 0 {
					fmt.Printf(", class %s", v.Class)
				}
				fmt.Printf("\n")
			}
		}
	}
	rcon.Close()

	// start web app
	http.HandleFunc(API_PATH, apiHandler)
	err = http.ListenAndServe(PORT, nil)
	if err != nil {
		fmt.Println("error:", err)
		return
	}
}

func apiHandler(w http.ResponseWriter, r *http.Request) {
	type Response struct {
		Text string `json:"text"`
	}

	type Job struct {
		Class string `json:"class"`
		Args map[string]string `json:"args"`
	}

	// accept only POST
	if r.Method != "POST" {
		return
	}

	// read and parse request body content
	body := make([]byte, r.ContentLength)
	r.Body.Read(body)
	data, err := url.ParseQuery(string(body))
	if err != nil {
		fmt.Println("error:", err)
	}

	// check token
	if ! TOKENS[data.Get("token")] {
		return
	}

	// get request and user name
	request := strings.TrimPrefix(data.Get("text"), data.Get("trigger_word"))
	request = strings.TrimLeft(request, " ")
	request = strings.TrimRight(request, " ")
	user := data.Get("user_name")

	// filter with access list
	var pass bool = UNDEFINED_JOB_CAN_PASS
	var response_text string
	var class string
	var queue string
	if ACCESS_LIST[request] != nil {
		// if user in deny list
		if (!ACCESS_LIST[request].Policy && ACCESS_LIST[request].Users[user]) {
			pass = false
		}
		// if user not in allow list
		if (ACCESS_LIST[request].Policy && !ACCESS_LIST[request].Users[user]) {
			pass = false
		}
		// if user is denied
		if !pass {
			if len(ACCESS_LIST[request].Deny_msg) > 0 {
				response_text = ACCESS_LIST[request].Deny_msg
			} else {
				response_text = DEFAULT_DENY_RESPONSE_TEXT
			}
		}

		// if user in allow list
		if (ACCESS_LIST[request].Policy && ACCESS_LIST[request].Users[user]) {
			pass = true
		}
		// if user not in deny list
		if (!ACCESS_LIST[request].Policy && !ACCESS_LIST[request].Users[user]) {
			pass = true
		}
		// if user is allowed
		if pass {
			// choose response text
			if len(ACCESS_LIST[request].Allow_msg) > 0 {
				response_text = ACCESS_LIST[request].Allow_msg
			} else {
				response_text = DEFAULT_ALLOW_RESPONSE_TEXT
			}

			// choose class
			if len(ACCESS_LIST[request].Class) > 0 {
				class = ACCESS_LIST[request].Class
			} else {
				class = CLASS
			}

			// choose queue
			if len(ACCESS_LIST[request].Queue) > 0 {
				queue = "resque:queue:" + ACCESS_LIST[request].Queue
			} else {
				queue = "resque:queue:" + QUEUE
			}
		}
	} else {
		// undefined job
		if pass {
			response_text = DEFAULT_ALLOW_RESPONSE_TEXT
			class = CLASS
			queue = "resque:queue:" + QUEUE
		} else {
			response_text = DEFAULT_DENY_RESPONSE_TEXT
		}
	}

	if pass {
		// make connection to redis
		rcon, err := redis.Dial("tcp", REDIS)
		if err != nil {
			fmt.Println("error:", err)
		}

		// create job
		job := Job{
			Class: class,
			Args: map[string]string {
			"request": request,
			"user": user,
			"channel": data.Get("channel_name"),
			"timestamp": data.Get("timestamp"),
			},
		}
		json_job, err := json.Marshal(job)
		if err != nil {
			fmt.Println("error:", err)
		}
		// push job to resque
		rcon.Cmd("RPUSH", queue, string(json_job))
		rcon.Close()
	}

	// response to slack
	res := Response{
		Text: "@" + user + ": " + response_text,
	}
	jres, err := json.Marshal(res)
	if err != nil {
		fmt.Println("error:", err)
	}
	w.Header().Set("Content-type", "application/json")
	fmt.Fprintf(w, string(jres))
}
<commit_msg>clean up some fuzzy if structure<commit_after>/*
Tiny web server to handle slack outgoing webhook and push the data to resque
*/
package main

import (
	"fmt"
	"flag"
	"net/http"
	"net/url"
	"encoding/json"
	"strings"
	"github.com/fzzy/radix/redis"
	"github.com/glacjay/goini"
)

// dictionary of string
type Dict map[string]bool
func StringToDict(s string, d Dict) {
	for _, v := range strings.Split(s, ",") {
		d[v] = true
	}
	return
}

// access rule
type AccessRule struct {
	Name string
	Users Dict
	Policy bool
	Class string
	Queue string
	Allow_msg string
	Deny_msg string
}

type AccessList map[string]*AccessRule

// some default global variable
var (
	PORT string = ":8765"
	API_PATH string = "/api"
	REDIS string = "localhost:6379"
	CLASS string = "SlackOPS"
	QUEUE string = "slackops"
	TOKENS Dict = make(Dict)
	VERBOSE bool = false
	FLAGS Dict = make(Dict)
	ACCESS_LIST AccessList = make(AccessList)
	DEFAULT_ALLOW_RESPONSE_TEXT = "please wait"
	DEFAULT_DENY_RESPONSE_TEXT = "task denied"
	UNDEFINED_JOB_CAN_PASS = true
	err error
)

func check_flag(f *flag.Flag) {
	FLAGS[f.Name] = true
}

func main() {
	// config parameter
	p_port := flag.String("p", "8765", "listen port")
	p_redis := flag.String("r", "localhost:6379", "redis host")
	p_class := flag.String("c", "SlackOPS", "resque class")
	p_queue := flag.String("q", "slackops", "resque queue")
	p_tokens := flag.String("t", "", "slack tokens, split by a comma (,)")
	p_verbose := flag.Bool("v", false, "verbose")
	p_config := flag.String("C", "", "configuration file")
	flag.Parse()

	// mark parsed flags
	flag.Visit(check_flag)

	// read config file
	if FLAGS["C"] {
		config, err := ini.Load(*p_config)
		if err != nil {
			fmt.Println("error:", err)
			return
		}

		// load general config
		c_port, exist := config.GetString("general", "port")
		if exist {
			PORT = ":" + c_port
		}
		c_redis, exist := config.GetString("general", "redis")
		if exist {
			REDIS = c_redis
		}
		c_class, exist := config.GetString("general", "class")
		if exist {
			CLASS = c_class
		}
		c_queue, exist := config.GetString("general", "queue")
		if exist {
			QUEUE = c_queue
		}
		c_tokens, exist := config.GetString("general", "tokens")
		if exist {
			StringToDict(c_tokens, TOKENS)
		}
		c_verbose, exist := config.GetBool("general", "verbose")
		if exist {
			VERBOSE = c_verbose
		}

		// load access list
		for _, s := range config.GetSections() {
			if strings.HasPrefix(s, "job: ") {
				// read users and policy (mandatory)
				users_string, e_users := config.GetString(s, "users")
				policy_string, e_policy := config.GetString(s, "policy")
				if !(e_users && e_policy) {
					fmt.Printf("Skipping %s\nError: policy and users are mandatory in a job", s)
					continue
				}

				// read optional config
				allow_msg, _ := config.GetString(s, "allow_msg")
				deny_msg, _ := config.GetString(s, "deny_msg")
				class, _ := config.GetString(s, "class")
				queue, _ := config.GetString(s, "queue")

				// parse users and policy
				var policy bool
				if policy_string == "allow" {
					policy = true
				} else if policy_string == "deny" {
					policy = false
				} else {
					fmt.Printf("Skipping %s\nError: policy must be either 'allow' or 'deny'", s)
					continue
				}
				users := make(Dict)
				StringToDict(users_string, users)

				// create AccessRule
				ar := AccessRule{
					Name: strings.TrimPrefix(s, "job: "),
					Users: users,
					Policy: policy,
					Class: class,
					Queue: queue,
					Allow_msg: allow_msg,
					Deny_msg: deny_msg,
				}
				ACCESS_LIST[ar.Name] = &ar
			}
		}
	}

	// override configuration with flags
	if FLAGS["p"] {
		PORT = ":" + *p_port
	}
	if FLAGS["r"] {
		REDIS = *p_redis
	}
	if FLAGS["c"] {
		CLASS = *p_class
	}
	if FLAGS["q"] {
		QUEUE = *p_queue
	}
	if FLAGS["t"] {
		StringToDict(*p_tokens, TOKENS)
	}
	if FLAGS["v"] {
		VERBOSE = *p_verbose
	}

	if VERBOSE {
		fmt.Printf("Listening on port %s using resque at %s\n", PORT, REDIS)
		fmt.Printf("Accepting tokens:\n")
		for t, _ := range TOKENS {
			fmt.Printf("+ %s\n", t)
		}
		fmt.Printf("Jobs will be pushed to queue %s, class %s\n", QUEUE, CLASS)
	}

	// connect to redis and add queue
	rcon, err := redis.Dial("tcp", REDIS)
	if err != nil {
		fmt.Println("error:", err)
		return
	}
	rcon.Cmd("SADD", "resque:queues", QUEUE)
	// add queue optionally specified in jobs
	for k, v := range ACCESS_LIST {
		if len(v.Queue) > 0 {
			rcon.Cmd("SADD", "resque:queues", v.Queue)
			if VERBOSE {
				fmt.Printf("Job '%s' will be pushed to queue %s", k, v.Queue)
				if len(v.Class) > 0 {
					fmt.Printf(", class %s", v.Class)
				}
				fmt.Printf("\n")
			}
		}
	}
	rcon.Close()

	// start web app
	http.HandleFunc(API_PATH, apiHandler)
	err = http.ListenAndServe(PORT, nil)
	if err != nil {
		fmt.Println("error:", err)
		return
	}
}

func apiHandler(w http.ResponseWriter, r *http.Request) {
	type Response struct {
		Text string `json:"text"`
	}

	type Job struct {
		Class string `json:"class"`
		Args map[string]string `json:"args"`
	}

	// accept only POST
	if r.Method != "POST" {
		return
	}

	// read and parse request body content
	body := make([]byte, r.ContentLength)
	r.Body.Read(body)
	data, err := url.ParseQuery(string(body))
	if err != nil {
		fmt.Println("error:", err)
	}

	// check token
	if ! TOKENS[data.Get("token")] {
		return
	}

	// get request and user name
	request := strings.TrimPrefix(data.Get("text"), data.Get("trigger_word"))
	request = strings.TrimLeft(request, " ")
	request = strings.TrimRight(request, " ")
	user := data.Get("user_name")

	// filter with access list
	var pass bool = UNDEFINED_JOB_CAN_PASS
	var response_text string
	if UNDEFINED_JOB_CAN_PASS {
		response_text = DEFAULT_ALLOW_RESPONSE_TEXT
	} else {
		response_text = DEFAULT_DENY_RESPONSE_TEXT
	}
	var class string = CLASS
	var queue string = "resque:queue:" + QUEUE

	// if job is defined
	if ACCESS_LIST[request] != nil {
		// if user in deny list
		if (!ACCESS_LIST[request].Policy && ACCESS_LIST[request].Users[user]) {
			pass = false

		// if user not in allow list
		} else if (ACCESS_LIST[request].Policy && !ACCESS_LIST[request].Users[user]) {
			pass = false

		// if user in allow list
		} else if (ACCESS_LIST[request].Policy && ACCESS_LIST[request].Users[user]) {
			pass = true

		// if user not in deny list
		} else if (!ACCESS_LIST[request].Policy && !ACCESS_LIST[request].Users[user]) {
			pass = true
		}

		// if user is allowed
		if pass {
			// choose response text
			if len(ACCESS_LIST[request].Allow_msg) > 0 {
				response_text = ACCESS_LIST[request].Allow_msg
			} else {
				response_text = DEFAULT_ALLOW_RESPONSE_TEXT
			}

			// choose class
			if len(ACCESS_LIST[request].Class) > 0 {
				class = ACCESS_LIST[request].Class
			} else {
				class = CLASS
			}

			// choose queue
			if len(ACCESS_LIST[request].Queue) > 0 {
				queue = "resque:queue:" + ACCESS_LIST[request].Queue
			} else {
				queue = "resque:queue:" + QUEUE
			}

		// if user is denied
		} else {
			if len(ACCESS_LIST[request].Deny_msg) > 0 {
				response_text = ACCESS_LIST[request].Deny_msg
			} else {
				response_text = DEFAULT_DENY_RESPONSE_TEXT
			}
		}

	} else {
		// undefined job
		if pass {
			response_text = DEFAULT_ALLOW_RESPONSE_TEXT
			class = CLASS
			queue = "resque:queue:" + QUEUE
		} else {
			response_text = DEFAULT_DENY_RESPONSE_TEXT
		}
	}

	if pass {
		// make connection to redis
		rcon, err := redis.Dial("tcp", REDIS)
		if err != nil {
			fmt.Println("error:", err)
		}

		// create job
		job := Job{
			Class: class,
			Args: map[string]string {
			"request": request,
			"user": user,
			"channel": data.Get("channel_name"),
			"timestamp": data.Get("timestamp"),
			},
		}
		json_job, err := json.Marshal(job)
		if err != nil {
			fmt.Println("error:", err)
		}
		// push job to resque
		rcon.Cmd("RPUSH", queue, string(json_job))
		rcon.Close()
	}

	// response to slack
	res := Response{
		Text: "@" + user + ": " + response_text,
	}
	json_res, err := json.Marshal(res)
	if err != nil {
		fmt.Println("error:", err)
	}
	w.Header().Set("Content-type", "application/json")
	fmt.Fprintf(w, string(json_res))
}
<|endoftext|>


<commit_before>package jsonrpc

import (
	"sync"

	"github.com/neptulon/cmap"
	"github.com/neptulon/neptulon/client"
)

// Client is a Neptulon JSON-RPC client.
type Client struct {
	Middleware
	sender Sender
	client *client.Client // Inner Neptulon client.
}

// NewClient creates a new Client object.
// msgWG = (optional) sets the given *sync.WaitGroup reference to be used for counting active gorotuines that are used for handling incoming/outgoing messages.
// disconnHandler = (optional) registers a function to handle client disconnection events.
func NewClient(msgWG *sync.WaitGroup, disconnHandler func(client *client.Client)) *Client {
	return UseClient(client.NewClient(msgWG, disconnHandler))
}

// UseClient wraps an established Neptulon Client into a JSON-RPC Client.
func UseClient(client *client.Client) *Client {
	c := Client{client: client}
	c.client.MiddlewareIn(c.Middleware.neptulonMiddleware)
	c.sender = NewSender(func(connID string, msg []byte) error { return c.client.Send(msg) }) // todo: this should be the last to be registered but how?
	return &c
}

// ConnID is a randomly generated unique client connection ID.
func (c *Client) ConnID() string {
	return c.client.ConnID()
}

// Session is a thread-safe data store for storing arbitrary data for this connection session.
func (c *Client) Session() *cmap.CMap {
	return c.client.Session()
}

// SetDeadline set the read/write deadlines for the connection, in seconds.
func (c *Client) SetDeadline(seconds int) {
	c.client.SetDeadline(seconds)
}

// UseTLS enables Transport Layer Security for the connection.
// ca = Optional CA certificate to be used for verifying the server certificate. Useful for using self-signed server certificates.
// clientCert, clientCertKey = Optional certificate/privat key pair for TLS client certificate authentication.
// All certificates/private keys are in PEM encoded X.509 format.
func (c *Client) UseTLS(ca, clientCert, clientCertKey []byte) {
	c.client.UseTLS(ca, clientCert, clientCertKey)
}

// Connect connectes to the server at given network address and starts receiving messages.
func (c *Client) Connect(addr string, debug bool) error {
	return c.client.Connect(addr, debug)
}

// SendRequest sends a JSON-RPC request throught the connection denoted by the connection ID with an auto generated request ID.
// resHandler is called when a response is returned.
func (c *Client) SendRequest(method string, params interface{}, resHandler func(ctx *ResCtx)) (reqID string, err error) {
	return c.sender.SendRequest("", method, params, resHandler)
}

// SendRequestArr sends a JSON-RPC request throught the connection denoted by the connection ID, with array params and auto generated request ID.
// resHandler is called when a response is returned.
func (c *Client) SendRequestArr(method string, resHandler func(ctx *ResCtx), params ...interface{}) (reqID string, err error) {
	return c.sender.SendRequestArr("", method, resHandler, params)
}

// SendNotification sends a JSON-RPC notification throught the connection denoted by the connection ID with structured params object.
func (c *Client) SendNotification(method string, params interface{}) error {
	return c.sender.SendNotification("", method, params)
}

// SendNotificationArr sends a JSON-RPC notification message throught the connection denoted by the connection ID with array params.
func (c *Client) SendNotificationArr(method string, params ...interface{}) error {
	return c.sender.SendNotificationArr("", method, params)
}

// SendResponse sends a JSON-RPC response throught the connection denoted by the connection ID.
func (c *Client) SendResponse(id string, result interface{}, err *ResError) error {
	return c.sender.SendResponse("", id, result, err)
}

// Close closes a client connection.
func (c *Client) Close() error {
	return c.client.Close()
}
<commit_msg>lazy initialize Sender in Client to register handler middleware the last<commit_after>package jsonrpc

import (
	"sync"

	"github.com/neptulon/cmap"
	"github.com/neptulon/neptulon/client"
)

// Client is a Neptulon JSON-RPC client.
type Client struct {
	Middleware
	sender Sender
	client *client.Client // Inner Neptulon client.
}

// NewClient creates a new Client object.
// msgWG = (optional) sets the given *sync.WaitGroup reference to be used for counting active gorotuines that are used for handling incoming/outgoing messages.
// disconnHandler = (optional) registers a function to handle client disconnection events.
func NewClient(msgWG *sync.WaitGroup, disconnHandler func(client *client.Client)) *Client {
	return UseClient(client.NewClient(msgWG, disconnHandler))
}

// UseClient wraps an established Neptulon Client into a JSON-RPC Client.
func UseClient(client *client.Client) *Client {
	c := Client{client: client}
	c.client.MiddlewareIn(c.Middleware.neptulonMiddleware)
	return &c
}

// ConnID is a randomly generated unique client connection ID.
func (c *Client) ConnID() string {
	return c.client.ConnID()
}

// Session is a thread-safe data store for storing arbitrary data for this connection session.
func (c *Client) Session() *cmap.CMap {
	return c.client.Session()
}

// SetDeadline set the read/write deadlines for the connection, in seconds.
func (c *Client) SetDeadline(seconds int) {
	c.client.SetDeadline(seconds)
}

// UseTLS enables Transport Layer Security for the connection.
// ca = Optional CA certificate to be used for verifying the server certificate. Useful for using self-signed server certificates.
// clientCert, clientCertKey = Optional certificate/privat key pair for TLS client certificate authentication.
// All certificates/private keys are in PEM encoded X.509 format.
func (c *Client) UseTLS(ca, clientCert, clientCertKey []byte) {
	c.client.UseTLS(ca, clientCert, clientCertKey)
}

// Connect connectes to the server at given network address and starts receiving messages.
func (c *Client) Connect(addr string, debug bool) error {
	return c.client.Connect(addr, debug)
}

// SendRequest sends a JSON-RPC request throught the connection denoted by the connection ID with an auto generated request ID.
// resHandler is called when a response is returned.
func (c *Client) SendRequest(method string, params interface{}, resHandler func(ctx *ResCtx)) (reqID string, err error) {
	c.lazyRegisterSender()
	return c.sender.SendRequest("", method, params, resHandler)
}

// SendRequestArr sends a JSON-RPC request throught the connection denoted by the connection ID, with array params and auto generated request ID.
// resHandler is called when a response is returned.
func (c *Client) SendRequestArr(method string, resHandler func(ctx *ResCtx), params ...interface{}) (reqID string, err error) {
	c.lazyRegisterSender()
	return c.sender.SendRequestArr("", method, resHandler, params)
}

// SendNotification sends a JSON-RPC notification throught the connection denoted by the connection ID with structured params object.
func (c *Client) SendNotification(method string, params interface{}) error {
	c.lazyRegisterSender()
	return c.sender.SendNotification("", method, params)
}

// SendNotificationArr sends a JSON-RPC notification message throught the connection denoted by the connection ID with array params.
func (c *Client) SendNotificationArr(method string, params ...interface{}) error {
	c.lazyRegisterSender()
	return c.sender.SendNotificationArr("", method, params)
}

// SendResponse sends a JSON-RPC response throught the connection denoted by the connection ID.
func (c *Client) SendResponse(id string, result interface{}, err *ResError) error {
	c.lazyRegisterSender()
	return c.sender.SendResponse("", id, result, err)
}

// Close closes a client connection.
func (c *Client) Close() error {
	return c.client.Close()
}

// Sender middleware should be registered the last so all the middleware will intercept the incoming messages
// before they are delivered to the final user handler.
func (c *Client) lazyRegisterSender() {
	if c.sender.send == nil {
		c.sender = NewSender(func(connID string, msg []byte) error { return c.client.Send(msg) })
	}
}
<|endoftext|>


<commit_before>package conntrack

import (
	"encoding/binary"
	"fmt"
	"net"
	"strconv"
	"syscall"
	"os"
	"unsafe"
)

type nfgenmsg struct {
	Family  uint8  /* AF_xxx */
	Version uint8  /* nfnetlink version */
	ResID   uint16 /* resource id */
}

const (
	sizeofGenmsg = uint32(unsafe.Sizeof(nfgenmsg{})) // TODO
)

type ConntrackListReq struct {
	Header syscall.NlMsghdr
	Body   nfgenmsg
}

func (c *ConntrackListReq) toWireFormat() []byte {
	// adapted from syscall/NetlinkRouteRequest.toWireFormat
	b := make([]byte, c.Header.Len)
	*(*uint32)(unsafe.Pointer(&b[0:4][0])) = c.Header.Len
	*(*uint16)(unsafe.Pointer(&b[4:6][0])) = c.Header.Type
	*(*uint16)(unsafe.Pointer(&b[6:8][0])) = c.Header.Flags
	*(*uint32)(unsafe.Pointer(&b[8:12][0])) = c.Header.Seq
	*(*uint32)(unsafe.Pointer(&b[12:16][0])) = c.Header.Pid
	b[16] = byte(c.Body.Family)
	b[17] = byte(c.Body.Version)
	*(*uint16)(unsafe.Pointer(&b[18:20][0])) = c.Body.ResID
	return b
}

func connectNetfilter(groups uint32) (int, *syscall.SockaddrNetlink, error) {
	s, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, syscall.NETLINK_NETFILTER)
	if err != nil {
		return 0, nil, err
	}
	lsa := &syscall.SockaddrNetlink{
		Family: syscall.AF_NETLINK,
		Groups: groups,
	}
	if err := syscall.Bind(s, lsa); err != nil {
		return 0, nil, err
	}

	return s, lsa, nil
}

// Established lists all established TCP connections.
func Established() ([]ConnTCP, error) {
	s, lsa, err := connectNetfilter(0)
	if err != nil {
		return nil, err
	}
	defer syscall.Close(s)

	var conns []ConnTCP
	msg := ConntrackListReq{
		Header: syscall.NlMsghdr{
			Len:   syscall.NLMSG_HDRLEN + sizeofGenmsg,
			Type:  (NFNL_SUBSYS_CTNETLINK << 8) | uint16(IpctnlMsgCtGet),
			Flags: syscall.NLM_F_REQUEST | syscall.NLM_F_DUMP,
			Pid:   0,
			Seq:   0,
		},
		Body: nfgenmsg{
			Family:  syscall.AF_INET,
			Version: NFNETLINK_V0,
			ResID:   0,
		},
	}
	wb := msg.toWireFormat()
	// fmt.Printf("msg bytes: %q\n", wb)
	if err := syscall.Sendto(s, wb, 0, lsa); err != nil {
		return nil, err
	}

	local := localIPs()
	readMsgs(s, func(c Conn) {
		if c.MsgType != NfctMsgUpdate {
			fmt.Printf("msg isn't an update: %d\n", c.MsgType)
			return
		}
		if c.TCPState != "ESTABLISHED" {
			// fmt.Printf("state isn't ESTABLISHED: %s\n", c.TCPState)
			return
		}
		if tc := c.ConnTCP(local); tc != nil {
			conns = append(conns, *tc)
		}
	})
	return conns, nil
}

// Follow gives a channel with all changes.
func Follow() (<-chan Conn, func(), error) {
	s, _, err := connectNetfilter(
		NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_UPDATE |
		NF_NETLINK_CONNTRACK_DESTROY)
	stop := func() {
		syscall.Close(s)
	}
	if err != nil {
		return nil, stop, err
	}

	res := make(chan Conn, 1)
	go func() {
		defer syscall.Close(s)
		if err := readMsgs(s, func(c Conn) {
			// if conn.TCPState != 3 {
			// // 3 is TCP established.
			// continue
			// }
			res <- c
		}); err != nil {
			panic(err)
		}
	}()
	return res, stop, nil
}

func readMsgs(s int, cb func(Conn)) error {
	for {
		rb := make([]byte, 2*syscall.Getpagesize()) // TODO: re-use
		nr, _, err := syscall.Recvfrom(s, rb, 0)
		if err != nil {
			return err
		}

		msgs, err := syscall.ParseNetlinkMessage(rb[:nr])
		if err != nil {
			return err
		}
		for _, msg := range msgs {
			if err := nfnlIsError(msg.Header); err != nil {
				return fmt.Errorf("msg is some error: %s\n", err)
			}
			if nflnSubsysID(msg.Header.Type) != NFNL_SUBSYS_CTNETLINK {
				return fmt.Errorf(
					"unexpected subsys_id: %d\n",
					nflnSubsysID(msg.Header.Type),
				)
			}
			conn, err := parsePayload(msg.Data[sizeofGenmsg:])
			if err != nil {
				return err
			}

			// Taken from conntrack/parse.c:__parse_message_type
			switch CntlMsgTypes(nflnMsgType(msg.Header.Type)) {
			case IpctnlMsgCtNew:
				conn.MsgType = NfctMsgUpdate
				if msg.Header.Flags&(syscall.NLM_F_CREATE|syscall.NLM_F_EXCL) > 0 {
					conn.MsgType = NfctMsgNew
				}
			case IpctnlMsgCtDelete:
				conn.MsgType = NfctMsgDestroy
			}

			cb(*conn)
		}
	}
}

type Conn struct {
	MsgType  NfConntrackMsg
	Proto    int
	Src      net.IP
	SrcPort  uint16
	Dst      net.IP
	DstPort  uint16
	TCPState string

	// ICMP stuff.
	IcmpId   uint16
	IcmpType uint8
	IcmpCode uint8

	// ct.mark, used to set permission type of the flow.
	CtMark uint32

	// For multitenancy.
	Zone uint16

	// Flow stats.
	ReplyPktLen uint64
	ReplyPktCount uint64
	OrigPktLen uint64
	OrigPktCount uint64
}

// ConnTCP decides which way this connection is going and makes a ConnTCP.
func (c Conn) ConnTCP(local map[string]struct{}) *ConnTCP {
	// conntrack gives us all connections, even things passing through, but it
	// doesn't tell us what the local IP is. So we use `local` as a guide
	// what's local.
	src := c.Src.String()
	dst := c.Dst.String()
	_, srcLocal := local[src]
	_, dstLocal := local[dst]
	// If both are local we must just order things predictably.
	if srcLocal && dstLocal {
		srcLocal = c.SrcPort < c.DstPort
	}
	if srcLocal {
		return &ConnTCP{
			Local:      src,
			LocalPort:  strconv.Itoa(int(c.SrcPort)),
			Remote:     dst,
			RemotePort: strconv.Itoa(int(c.DstPort)),
		}
	}
	if dstLocal {
		return &ConnTCP{
			Local:      dst,
			LocalPort:  strconv.Itoa(int(c.DstPort)),
			Remote:     src,
			RemotePort: strconv.Itoa(int(c.SrcPort)),
		}
	}
	// Neither is local. conntrack also reports NAT connections.
	return nil
}

func parsePayload(b []byte) (*Conn, error) {
	// Most of this comes from libnetfilter_conntrack/src/conntrack/parse_mnl.c
	conn := &Conn{}
	attrs, err := parseAttrs(b)
	if err != nil {
		return conn, err
	}
	for _, attr := range attrs {
		switch CtattrType(attr.Typ) {
		case CtaTupleOrig:
		case CtaTupleReply:
			// fmt.Printf("It's a reply\n")
			// We take the reply, nor the orig.... Sure?
			parseTuple(attr.Msg, conn)
		case CtaCountersOrig:
			conn.OrigPktLen, conn.OrigPktCount, _ = parseCounters(attr.Msg)
		case CtaCountersReply:
			conn.ReplyPktLen, conn.ReplyPktCount, _ = parseCounters(attr.Msg)
		case CtaStatus:
			// These are ip_conntrack_status
			// status := binary.BigEndian.Uint32(attr.Msg)
			// fmt.Printf("It's status %d\n", status)
		case CtaProtoinfo:
			parseProtoinfo(attr.Msg, conn)
		case CtaMark:
			conn.CtMark = binary.BigEndian.Uint32(attr.Msg)
		case CtaZone:
			conn.Zone = binary.BigEndian.Uint16(attr.Msg)
		}
	}
	return conn, nil
}

func parseTuple(b []byte, conn *Conn) error {
	attrs, err := parseAttrs(b)
	if err != nil {
		return fmt.Errorf("invalid tuple attr: %s", err)
	}
	for _, attr := range attrs {
		// fmt.Printf("pl: %d, type: %d, multi: %t, bigend: %t\n", len(attr.Msg), attr.Typ, attr.IsNested, attr.IsNetByteorder)
		switch CtattrTuple(attr.Typ) {
		case CtaTupleUnspec:
			// fmt.Printf("It's a tuple unspec\n")
		case CtaTupleIp:
			// fmt.Printf("It's a tuple IP\n")
			if err := parseIP(attr.Msg, conn); err != nil {
				return err
			}
		case CtaTupleProto:
			// fmt.Printf("It's a tuple proto\n")
			parseProto(attr.Msg, conn)
		}
	}
	return nil
}

func parseCounters(b []byte) (uint64, uint64, error) {
	attrs, err := parseAttrs(b)
	if err != nil {
		return 0, 0, fmt.Errorf("invalid tuple attr: %s", err)
	}
	packets := uint64(0)
	bytes := uint64(0)
	for _, attr := range attrs {
		switch CtattrCounters(attr.Typ) {
		case CtaCountersPackets:
			packets = binary.BigEndian.Uint64(attr.Msg)
		case CtaCountersBytes:
			bytes = binary.BigEndian.Uint64(attr.Msg)
		}
	}
	return packets, bytes, nil
}

func parseIP(b []byte, conn *Conn) error {
	attrs, err := parseAttrs(b)
	if err != nil {
		return fmt.Errorf("invalid tuple attr: %s", err)
	}
	for _, attr := range attrs {
		switch CtattrIp(attr.Typ) {
		case CtaIpV4Src:
			conn.Src = net.IP(attr.Msg) // TODO: copy so we can reuse the buffer?
		case CtaIpV4Dst:
			conn.Dst = net.IP(attr.Msg) // TODO: copy so we can reuse the buffer?
		case CtaIpV6Src:
			// TODO
		case CtaIpV6Dst:
			// TODO
		}
	}
	return nil
}

func parseProto(b []byte, conn *Conn) error {
	attrs, err := parseAttrs(b)
	if err != nil {
		return fmt.Errorf("invalid tuple attr: %s", err)
	}
	for _, attr := range attrs {
		switch CtattrL4proto(attr.Typ) {
		// Protocol number.
		case CtaProtoNum:
			conn.Proto = int(uint8(attr.Msg[0]))

		// TCP stuff.
		case CtaProtoSrcPort:
			conn.SrcPort = binary.BigEndian.Uint16(attr.Msg)
		case CtaProtoDstPort:
			conn.DstPort = binary.BigEndian.Uint16(attr.Msg)

		// ICMP stuff.
		case CtaProtoIcmpId:
			conn.IcmpId = binary.BigEndian.Uint16(attr.Msg)
		case CtaProtoIcmpType:
			conn.IcmpType = uint8(attr.Msg[0])
		case CtaProtoIcmpCode:
			conn.IcmpCode = uint8(attr.Msg[0])
		}
	}
	return nil
}

func parseProtoinfo(b []byte, conn *Conn) error {
	attrs, err := parseAttrs(b)
	if err != nil {
		return fmt.Errorf("invalid tuple attr: %s", err)
	}
	for _, attr := range attrs {
		switch CtattrProtoinfo(attr.Typ) {
		case CtaProtoinfoTcp:
			if err := parseProtoinfoTCP(attr.Msg, conn); err != nil {
				return err
			}
		default:
			// we're not interested in other protocols
		}
	}
	return nil
}

func parseProtoinfoTCP(b []byte, conn *Conn) error {
	attrs, err := parseAttrs(b)
	if err != nil {
		return fmt.Errorf("invalid tuple attr: %s", err)
	}
	for _, attr := range attrs {
		switch CtattrProtoinfoTcp(attr.Typ) {
		case CtaProtoinfoTcpState:
			conn.TCPState = tcpState[uint8(attr.Msg[0])]
		default:
			// not interested
		}
	}
	return nil
}
<commit_msg>parse conntrack id<commit_after>package conntrack

import (
	"encoding/binary"
	"fmt"
	"net"
	"strconv"
	"syscall"
	"unsafe"
)

type nfgenmsg struct {
	Family  uint8  /* AF_xxx */
	Version uint8  /* nfnetlink version */
	ResID   uint16 /* resource id */
}

const (
	sizeofGenmsg = uint32(unsafe.Sizeof(nfgenmsg{})) // TODO
)

type ConntrackListReq struct {
	Header syscall.NlMsghdr
	Body   nfgenmsg
}

func (c *ConntrackListReq) toWireFormat() []byte {
	// adapted from syscall/NetlinkRouteRequest.toWireFormat
	b := make([]byte, c.Header.Len)
	*(*uint32)(unsafe.Pointer(&b[0:4][0])) = c.Header.Len
	*(*uint16)(unsafe.Pointer(&b[4:6][0])) = c.Header.Type
	*(*uint16)(unsafe.Pointer(&b[6:8][0])) = c.Header.Flags
	*(*uint32)(unsafe.Pointer(&b[8:12][0])) = c.Header.Seq
	*(*uint32)(unsafe.Pointer(&b[12:16][0])) = c.Header.Pid
	b[16] = byte(c.Body.Family)
	b[17] = byte(c.Body.Version)
	*(*uint16)(unsafe.Pointer(&b[18:20][0])) = c.Body.ResID
	return b
}

func connectNetfilter(groups uint32) (int, *syscall.SockaddrNetlink, error) {
	s, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, syscall.NETLINK_NETFILTER)
	if err != nil {
		return 0, nil, err
	}
	lsa := &syscall.SockaddrNetlink{
		Family: syscall.AF_NETLINK,
		Groups: groups,
	}
	if err := syscall.Bind(s, lsa); err != nil {
		return 0, nil, err
	}

	return s, lsa, nil
}

// Established lists all established TCP connections.
func Established() ([]ConnTCP, error) {
	s, lsa, err := connectNetfilter(0)
	if err != nil {
		return nil, err
	}
	defer syscall.Close(s)

	var conns []ConnTCP
	msg := ConntrackListReq{
		Header: syscall.NlMsghdr{
			Len:   syscall.NLMSG_HDRLEN + sizeofGenmsg,
			Type:  (NFNL_SUBSYS_CTNETLINK << 8) | uint16(IpctnlMsgCtGet),
			Flags: syscall.NLM_F_REQUEST | syscall.NLM_F_DUMP,
			Pid:   0,
			Seq:   0,
		},
		Body: nfgenmsg{
			Family:  syscall.AF_INET,
			Version: NFNETLINK_V0,
			ResID:   0,
		},
	}
	wb := msg.toWireFormat()
	// fmt.Printf("msg bytes: %q\n", wb)
	if err := syscall.Sendto(s, wb, 0, lsa); err != nil {
		return nil, err
	}

	local := localIPs()
	readMsgs(s, func(c Conn) {
		if c.MsgType != NfctMsgUpdate {
			fmt.Printf("msg isn't an update: %d\n", c.MsgType)
			return
		}
		if c.TCPState != "ESTABLISHED" {
			// fmt.Printf("state isn't ESTABLISHED: %s\n", c.TCPState)
			return
		}
		if tc := c.ConnTCP(local); tc != nil {
			conns = append(conns, *tc)
		}
	})
	return conns, nil
}

// Follow gives a channel with all changes.
func Follow() (<-chan Conn, func(), error) {
	s, _, err := connectNetfilter(
		NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_UPDATE |
		NF_NETLINK_CONNTRACK_DESTROY)
	stop := func() {
		syscall.Close(s)
	}
	if err != nil {
		return nil, stop, err
	}

	res := make(chan Conn, 1)
	go func() {
		defer syscall.Close(s)
		if err := readMsgs(s, func(c Conn) {
			// if conn.TCPState != 3 {
			// // 3 is TCP established.
			// continue
			// }
			res <- c
		}); err != nil {
			panic(err)
		}
	}()
	return res, stop, nil
}

func readMsgs(s int, cb func(Conn)) error {
	for {
		rb := make([]byte, 2*syscall.Getpagesize()) // TODO: re-use
		nr, _, err := syscall.Recvfrom(s, rb, 0)
		if err != nil {
			return err
		}

		msgs, err := syscall.ParseNetlinkMessage(rb[:nr])
		if err != nil {
			return err
		}
		for _, msg := range msgs {
			if err := nfnlIsError(msg.Header); err != nil {
				return fmt.Errorf("msg is some error: %s\n", err)
			}
			if nflnSubsysID(msg.Header.Type) != NFNL_SUBSYS_CTNETLINK {
				return fmt.Errorf(
					"unexpected subsys_id: %d\n",
					nflnSubsysID(msg.Header.Type),
				)
			}
			conn, err := parsePayload(msg.Data[sizeofGenmsg:])
			if err != nil {
				return err
			}

			// Taken from conntrack/parse.c:__parse_message_type
			switch CntlMsgTypes(nflnMsgType(msg.Header.Type)) {
			case IpctnlMsgCtNew:
				conn.MsgType = NfctMsgUpdate
				if msg.Header.Flags&(syscall.NLM_F_CREATE|syscall.NLM_F_EXCL) > 0 {
					conn.MsgType = NfctMsgNew
				}
			case IpctnlMsgCtDelete:
				conn.MsgType = NfctMsgDestroy
			}

			cb(*conn)
		}
	}
}

type Conn struct {
	MsgType  NfConntrackMsg
	Proto    int
	Src      net.IP
	SrcPort  uint16
	Dst      net.IP
	DstPort  uint16
	TCPState string

	// ICMP stuff.
	IcmpId   uint16
	IcmpType uint8
	IcmpCode uint8

	// ct.mark, used to set permission type of the flow.
	CtMark uint32

	// ct.id, used to identify connections.
	CtId uint32

	// For multitenancy.
	Zone uint16

	// Flow stats.
	ReplyPktLen uint64
	ReplyPktCount uint64
	OrigPktLen uint64
	OrigPktCount uint64
}

// ConnTCP decides which way this connection is going and makes a ConnTCP.
func (c Conn) ConnTCP(local map[string]struct{}) *ConnTCP {
	// conntrack gives us all connections, even things passing through, but it
	// doesn't tell us what the local IP is. So we use `local` as a guide
	// what's local.
	src := c.Src.String()
	dst := c.Dst.String()
	_, srcLocal := local[src]
	_, dstLocal := local[dst]
	// If both are local we must just order things predictably.
	if srcLocal && dstLocal {
		srcLocal = c.SrcPort < c.DstPort
	}
	if srcLocal {
		return &ConnTCP{
			Local:      src,
			LocalPort:  strconv.Itoa(int(c.SrcPort)),
			Remote:     dst,
			RemotePort: strconv.Itoa(int(c.DstPort)),
		}
	}
	if dstLocal {
		return &ConnTCP{
			Local:      dst,
			LocalPort:  strconv.Itoa(int(c.DstPort)),
			Remote:     src,
			RemotePort: strconv.Itoa(int(c.SrcPort)),
		}
	}
	// Neither is local. conntrack also reports NAT connections.
	return nil
}

func parsePayload(b []byte) (*Conn, error) {
	// Most of this comes from libnetfilter_conntrack/src/conntrack/parse_mnl.c
	conn := &Conn{}
	attrs, err := parseAttrs(b)
	if err != nil {
		return conn, err
	}
	for _, attr := range attrs {
		switch CtattrType(attr.Typ) {
		case CtaTupleOrig:
		case CtaTupleReply:
			// fmt.Printf("It's a reply\n")
			// We take the reply, nor the orig.... Sure?
			parseTuple(attr.Msg, conn)
		case CtaCountersOrig:
			conn.OrigPktLen, conn.OrigPktCount, _ = parseCounters(attr.Msg)
		case CtaCountersReply:
			conn.ReplyPktLen, conn.ReplyPktCount, _ = parseCounters(attr.Msg)
		case CtaStatus:
			// These are ip_conntrack_status
			// status := binary.BigEndian.Uint32(attr.Msg)
			// fmt.Printf("It's status %d\n", status)
		case CtaProtoinfo:
			parseProtoinfo(attr.Msg, conn)
		case CtaMark:
			conn.CtMark = binary.BigEndian.Uint32(attr.Msg)
		case CtaZone:
			conn.Zone = binary.BigEndian.Uint16(attr.Msg)
		case CtaId:
			conn.CtId = binary.BigEndian.Uint32(attr.Msg)
		}
	}
	return conn, nil
}

func parseTuple(b []byte, conn *Conn) error {
	attrs, err := parseAttrs(b)
	if err != nil {
		return fmt.Errorf("invalid tuple attr: %s", err)
	}
	for _, attr := range attrs {
		// fmt.Printf("pl: %d, type: %d, multi: %t, bigend: %t\n", len(attr.Msg), attr.Typ, attr.IsNested, attr.IsNetByteorder)
		switch CtattrTuple(attr.Typ) {
		case CtaTupleUnspec:
			// fmt.Printf("It's a tuple unspec\n")
		case CtaTupleIp:
			// fmt.Printf("It's a tuple IP\n")
			if err := parseIP(attr.Msg, conn); err != nil {
				return err
			}
		case CtaTupleProto:
			// fmt.Printf("It's a tuple proto\n")
			parseProto(attr.Msg, conn)
		}
	}
	return nil
}

func parseCounters(b []byte) (uint64, uint64, error) {
	attrs, err := parseAttrs(b)
	if err != nil {
		return 0, 0, fmt.Errorf("invalid tuple attr: %s", err)
	}
	packets := uint64(0)
	bytes := uint64(0)
	for _, attr := range attrs {
		switch CtattrCounters(attr.Typ) {
		case CtaCountersPackets:
			packets = binary.BigEndian.Uint64(attr.Msg)
		case CtaCountersBytes:
			bytes = binary.BigEndian.Uint64(attr.Msg)
		}
	}
	return packets, bytes, nil
}

func parseIP(b []byte, conn *Conn) error {
	attrs, err := parseAttrs(b)
	if err != nil {
		return fmt.Errorf("invalid tuple attr: %s", err)
	}
	for _, attr := range attrs {
		switch CtattrIp(attr.Typ) {
		case CtaIpV4Src:
			conn.Src = net.IP(attr.Msg) // TODO: copy so we can reuse the buffer?
		case CtaIpV4Dst:
			conn.Dst = net.IP(attr.Msg) // TODO: copy so we can reuse the buffer?
		case CtaIpV6Src:
			// TODO
		case CtaIpV6Dst:
			// TODO
		}
	}
	return nil
}

func parseProto(b []byte, conn *Conn) error {
	attrs, err := parseAttrs(b)
	if err != nil {
		return fmt.Errorf("invalid tuple attr: %s", err)
	}
	for _, attr := range attrs {
		switch CtattrL4proto(attr.Typ) {
		// Protocol number.
		case CtaProtoNum:
			conn.Proto = int(uint8(attr.Msg[0]))

		// TCP stuff.
		case CtaProtoSrcPort:
			conn.SrcPort = binary.BigEndian.Uint16(attr.Msg)
		case CtaProtoDstPort:
			conn.DstPort = binary.BigEndian.Uint16(attr.Msg)

		// ICMP stuff.
		case CtaProtoIcmpId:
			conn.IcmpId = binary.BigEndian.Uint16(attr.Msg)
		case CtaProtoIcmpType:
			conn.IcmpType = uint8(attr.Msg[0])
		case CtaProtoIcmpCode:
			conn.IcmpCode = uint8(attr.Msg[0])
		}
	}
	return nil
}

func parseProtoinfo(b []byte, conn *Conn) error {
	attrs, err := parseAttrs(b)
	if err != nil {
		return fmt.Errorf("invalid tuple attr: %s", err)
	}
	for _, attr := range attrs {
		switch CtattrProtoinfo(attr.Typ) {
		case CtaProtoinfoTcp:
			if err := parseProtoinfoTCP(attr.Msg, conn); err != nil {
				return err
			}
		default:
			// we're not interested in other protocols
		}
	}
	return nil
}

func parseProtoinfoTCP(b []byte, conn *Conn) error {
	attrs, err := parseAttrs(b)
	if err != nil {
		return fmt.Errorf("invalid tuple attr: %s", err)
	}
	for _, attr := range attrs {
		switch CtattrProtoinfoTcp(attr.Typ) {
		case CtaProtoinfoTcpState:
			conn.TCPState = tcpState[uint8(attr.Msg[0])]
		default:
			// not interested
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// Package bitfinex is the official client to access to bitfinex.com API
package bitfinex

import (
    "crypto/hmac"
    "crypto/sha512"
    "encoding/base64"
    "encoding/hex"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
    "net/url"
    "time"
)

const (
    DefaultBaseURL      = "https://api.bitfinex.com/v1/"
    DefaultWebSocketURL = "wss://api2.bitfinex.com:3000/ws"
)

var nonce int64

type Param struct {
    Key string
    Val string
}

type Client struct {
    // Base URL for API requests.
    BaseURL                *url.URL
    WebSocketURL           string
    WebSocketTLSSkipVerify bool

    // Auth data
    ApiKey    string
    ApiSecret string

    // Services
    Pairs         *PairsService
    Stats         *StatsService
    Ticker        *TickerService
    Account       *AccountService
    Balances      *BalancesService
    Offers        *OffersService
    Credits       *CreditsService
    Deposit       *DepositService
    Lendbook      *LendbookService
    MarginInfo    *MarginInfoService
    MarginFunding *MarginFundingService
    OrderBook     *OrderBookServive
    Orders        *OrderService
    Trades        *TradesService
    Positions     *PositionsService
    History       *HistoryService
    WebSocket     *WebSocketService
    Wallet        *WalletService
}

// NewClient creates new Bitfinex.com API http client
func NewClient() *Client {
    baseURL, _ := url.Parse(DefaultBaseURL)

    c := &Client{BaseURL: baseURL, WebSocketURL: DefaultWebSocketURL}
    c.Pairs = &PairsService{client: c}
    c.Stats = &StatsService{client: c}
    c.Account = &AccountService{client: c}
    c.Ticker = &TickerService{client: c}
    c.Balances = &BalancesService{client: c}
    c.Offers = &OffersService{client: c}
    c.Credits = &CreditsService{client: c}
    c.Deposit = &DepositService{client: c}
    c.Lendbook = &LendbookService{client: c}
    c.MarginInfo = &MarginInfoService{client: c}
    c.MarginFunding = &MarginFundingService{client: c}
    c.OrderBook = &OrderBookServive{client: c}
    c.Orders = &OrderService{client: c}
    c.History = &HistoryService{client: c}
    c.Trades = &TradesService{client: c}
    c.Positions = &PositionsService{client: c}
    c.Wallet = &WalletService{client: c}
    c.WebSocket = NewWebSocketService(c)
    c.WebSocketTLSSkipVerify = false

    return c
}

// NewRequest create new API request. Relative url can be provided in refUrl.
func (c *Client) newRequest(method string, refUrl string, params url.Values) (*http.Request, error) {
    rel, err := url.Parse(refUrl)
    if err != nil {
        return nil, err
    }
    if params != nil {
        rel.RawQuery = params.Encode()
    }
    var req *http.Request
    u := c.BaseURL.ResolveReference(rel)
    req, err = http.NewRequest(method, u.String(), nil)

    if err != nil {
        return nil, err
    }

    return req, nil
}

// getNonce - getting unique nonce
func getNonce() int64 {
    if nonce == 0 {
        nonce = time.Now().UnixNano()
    }
    nonce++
    return nonce
}

// NewAuthenticatedRequest creates new http request for authenticated routes
func (c *Client) newAuthenticatedRequest(m string, refUrl string, data map[string]interface{}) (*http.Request, error) {
    req, err := c.newRequest(m, refUrl, nil)
    if err != nil {
        return nil, err
    }

    payload := map[string]interface{}{
        "request": "/v1/" + refUrl,
        "nonce":   fmt.Sprintf("%v", getNonce()),
    }

    if len(data) > 0 {
        for k, v := range data {
            payload[k] = v
        }
    }

    payload_json, _ := json.Marshal(payload)
    payload_enc := base64.StdEncoding.EncodeToString(payload_json)

    req.Header.Add("Content-Type", "application/json")
    req.Header.Add("Accept", "application/json")
    req.Header.Add("X-BFX-APIKEY", c.ApiKey)
    req.Header.Add("X-BFX-PAYLOAD", payload_enc)
    req.Header.Add("X-BFX-SIGNATURE", c.signPayload(payload_enc))

    return req, nil
}

func (c *Client) signPayload(payload string) string {
    sig := hmac.New(sha512.New384, []byte(c.ApiSecret))
    sig.Write([]byte(payload))
    return hex.EncodeToString(sig.Sum(nil))
}

// Auth sets api key and secret for usage is requests that
// requires authentication
func (c *Client) Auth(key string, secret string) *Client {
    c.ApiKey = key
    c.ApiSecret = secret

    return c
}

var httpDo = func(req *http.Request) (*http.Response, error) {
    return http.DefaultClient.Do(req)
}

// Do executes API request created by NewRequest method or custom *http.Request.
func (c *Client) do(req *http.Request, v interface{}) (*Response, error) {
    resp, err := httpDo(req)

    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    response := newResponse(resp)

    err = checkResponse(response)
    if err != nil {
        // Return response in case caller need to debug it.
        return response, err
    }

    if v != nil {
        err = json.Unmarshal(response.Body, v)

        if err != nil {
            return response, err
        }
    }

    return response, nil
}

// Response is wrapper for standard http.Response and provides
// more methods.
type Response struct {
    Response *http.Response
    Body     []byte
}

// newResponse creates new wrapper.
func newResponse(r *http.Response) *Response {
    body, err := ioutil.ReadAll(r.Body)
    if err != nil {
        body = []byte(`Error reading body:` + err.Error())
    }

    return &Response{r, body}
}

// String converts response body to string.
// An empty string will be returned if error.
func (r *Response) String() string {
    return string(r.Body)
}

// In case if API will wrong response code
// ErrorResponse will be returned to caller
type ErrorResponse struct {
    Response *Response
    Message  string `json:"message"`
}

func (r *ErrorResponse) Error() string {
    return fmt.Sprintf("%v %v: %d %v",
        r.Response.Response.Request.Method,
        r.Response.Response.Request.URL,
        r.Response.Response.StatusCode,
        r.Message,
    )
}

// checkResponse checks response status code and response
// for errors.
func checkResponse(r *Response) error {
    if c := r.Response.StatusCode; 200 <= c && c <= 299 {
        return nil
    }

    // Try to decode error message
    errorResponse := &ErrorResponse{Response: r}
    err := json.Unmarshal(r.Body, errorResponse)
    if err != nil {
        errorResponse.Message = "Error decoding response error message. " +
            "Please see response body for more information."
    }

    return errorResponse
}
<commit_msg>update API endpoints<commit_after>// Package bitfinex is the official client to access to bitfinex.com API
package bitfinex

import (
    "crypto/hmac"
    "crypto/sha512"
    "encoding/base64"
    "encoding/hex"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
    "net/url"
    "time"
)

const (
    DefaultBaseURL        = "wss://api.bitfinex.com/v1"
    DefaultBaseV2URL      = "wss://api.bitfinex.com/v2"
    DefaultWebSocketURL   = "wss://api.bitfinex.com/ws/"
    DefaultWebSocketV2URL = "wss://api.bitfinex.com/ws/2"
)

var nonce int64

type Param struct {
    Key string
    Val string
}

type Client struct {
    // Base URL for API requests.
    BaseURL                *url.URL
    WebSocketURL           string
    WebSocketTLSSkipVerify bool

    // Auth data
    ApiKey    string
    ApiSecret string

    // Services
    Pairs         *PairsService
    Stats         *StatsService
    Ticker        *TickerService
    Account       *AccountService
    Balances      *BalancesService
    Offers        *OffersService
    Credits       *CreditsService
    Deposit       *DepositService
    Lendbook      *LendbookService
    MarginInfo    *MarginInfoService
    MarginFunding *MarginFundingService
    OrderBook     *OrderBookServive
    Orders        *OrderService
    Trades        *TradesService
    Positions     *PositionsService
    History       *HistoryService
    WebSocket     *WebSocketService
    Wallet        *WalletService
}

// NewClient creates new Bitfinex.com API http client
func NewClient() *Client {
    baseURL, _ := url.Parse(DefaultBaseURL)

    c := &Client{BaseURL: baseURL, WebSocketURL: DefaultWebSocketURL}
    c.Pairs = &PairsService{client: c}
    c.Stats = &StatsService{client: c}
    c.Account = &AccountService{client: c}
    c.Ticker = &TickerService{client: c}
    c.Balances = &BalancesService{client: c}
    c.Offers = &OffersService{client: c}
    c.Credits = &CreditsService{client: c}
    c.Deposit = &DepositService{client: c}
    c.Lendbook = &LendbookService{client: c}
    c.MarginInfo = &MarginInfoService{client: c}
    c.MarginFunding = &MarginFundingService{client: c}
    c.OrderBook = &OrderBookServive{client: c}
    c.Orders = &OrderService{client: c}
    c.History = &HistoryService{client: c}
    c.Trades = &TradesService{client: c}
    c.Positions = &PositionsService{client: c}
    c.Wallet = &WalletService{client: c}
    c.WebSocket = NewWebSocketService(c)
    c.WebSocketTLSSkipVerify = false

    return c
}

// NewRequest create new API request. Relative url can be provided in refUrl.
func (c *Client) newRequest(method string, refUrl string, params url.Values) (*http.Request, error) {
    rel, err := url.Parse(refUrl)
    if err != nil {
        return nil, err
    }
    if params != nil {
        rel.RawQuery = params.Encode()
    }
    var req *http.Request
    u := c.BaseURL.ResolveReference(rel)
    req, err = http.NewRequest(method, u.String(), nil)

    if err != nil {
        return nil, err
    }

    return req, nil
}

// getNonce - getting unique nonce
func getNonce() int64 {
    if nonce == 0 {
        nonce = time.Now().UnixNano()
    }
    nonce++
    return nonce
}

// NewAuthenticatedRequest creates new http request for authenticated routes
func (c *Client) newAuthenticatedRequest(m string, refUrl string, data map[string]interface{}) (*http.Request, error) {
    req, err := c.newRequest(m, refUrl, nil)
    if err != nil {
        return nil, err
    }

    payload := map[string]interface{}{
        "request": "/v1/" + refUrl,
        "nonce":   fmt.Sprintf("%v", getNonce()),
    }

    if len(data) > 0 {
        for k, v := range data {
            payload[k] = v
        }
    }

    payload_json, _ := json.Marshal(payload)
    payload_enc := base64.StdEncoding.EncodeToString(payload_json)

    req.Header.Add("Content-Type", "application/json")
    req.Header.Add("Accept", "application/json")
    req.Header.Add("X-BFX-APIKEY", c.ApiKey)
    req.Header.Add("X-BFX-PAYLOAD", payload_enc)
    req.Header.Add("X-BFX-SIGNATURE", c.signPayload(payload_enc))

    return req, nil
}

func (c *Client) signPayload(payload string) string {
    sig := hmac.New(sha512.New384, []byte(c.ApiSecret))
    sig.Write([]byte(payload))
    return hex.EncodeToString(sig.Sum(nil))
}

// Auth sets api key and secret for usage is requests that
// requires authentication
func (c *Client) Auth(key string, secret string) *Client {
    c.ApiKey = key
    c.ApiSecret = secret

    return c
}

var httpDo = func(req *http.Request) (*http.Response, error) {
    return http.DefaultClient.Do(req)
}

// Do executes API request created by NewRequest method or custom *http.Request.
func (c *Client) do(req *http.Request, v interface{}) (*Response, error) {
    resp, err := httpDo(req)

    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    response := newResponse(resp)

    err = checkResponse(response)
    if err != nil {
        // Return response in case caller need to debug it.
        return response, err
    }

    if v != nil {
        err = json.Unmarshal(response.Body, v)

        if err != nil {
            return response, err
        }
    }

    return response, nil
}

// Response is wrapper for standard http.Response and provides
// more methods.
type Response struct {
    Response *http.Response
    Body     []byte
}

// newResponse creates new wrapper.
func newResponse(r *http.Response) *Response {
    body, err := ioutil.ReadAll(r.Body)
    if err != nil {
        body = []byte(`Error reading body:` + err.Error())
    }

    return &Response{r, body}
}

// String converts response body to string.
// An empty string will be returned if error.
func (r *Response) String() string {
    return string(r.Body)
}

// In case if API will wrong response code
// ErrorResponse will be returned to caller
type ErrorResponse struct {
    Response *Response
    Message  string `json:"message"`
}

func (r *ErrorResponse) Error() string {
    return fmt.Sprintf("%v %v: %d %v",
        r.Response.Response.Request.Method,
        r.Response.Response.Request.URL,
        r.Response.Response.StatusCode,
        r.Message,
    )
}

// checkResponse checks response status code and response
// for errors.
func checkResponse(r *Response) error {
    if c := r.Response.StatusCode; 200 <= c && c <= 299 {
        return nil
    }

    // Try to decode error message
    errorResponse := &ErrorResponse{Response: r}
    err := json.Unmarshal(r.Body, errorResponse)
    if err != nil {
        errorResponse.Message = "Error decoding response error message. " +
            "Please see response body for more information."
    }

    return errorResponse
}
<|endoftext|>


<commit_before>package typhon

import (
	"net/http"
	"time"

	"github.com/monzo/terrors"
)

var (
	// Client is used to send all requests by default. It can be overridden globally but MUST only be done before use
	// takes place; access is not synchronised.
	Client Service = BareClient
	// RoundTripper is used by default in Typhon clients
	RoundTripper http.RoundTripper = &http.Transport{
		Proxy:               http.ProxyFromEnvironment,
		DisableKeepAlives:   false,
		DisableCompression:  false,
		IdleConnTimeout:     10 * time.Minute,
		MaxIdleConnsPerHost: 10}
)

// A ResponseFuture is a container for a Response which will materialise at some point.
type ResponseFuture struct {
	done <-chan struct{} // guards access to r
	r    Response
}

// WaitC returns a channel which can be waited upon until the response is available
func (f *ResponseFuture) WaitC() <-chan struct{} {
	return f.done
}

// Response provides access to the response object, blocking until it is available
func (f *ResponseFuture) Response() Response {
	<-f.WaitC()
	return f.r
}

// HttpService returns a Service which sends requests via the given net/http RoundTripper.
// Only use this if you need to do something custom at the transport level.
func HttpService(rt http.RoundTripper) Service {
	return func(req Request) Response {
		ctx := req.unwrappedContext()
		httpRsp, err := rt.RoundTrip(req.Request.WithContext(ctx))
		// When the calling context is cancelled, close the response body
		// This protects callers that forget to call Close(), or those which proxy responses upstream
		//
		// If the calling context is infinite (ie. returns nil for Done()), it can never signal cancellation
		// so we bypass this as a performance optimisation
		if httpRsp != nil && httpRsp.Body != nil && ctx.Done() != nil {
			body := newDoneReader(httpRsp.Body, httpRsp.ContentLength)
			httpRsp.Body = body
			go func() {
				select {
				case <-body.closed:
				case <-ctx.Done():
					body.Close()
				}
			}()
		}
		return Response{
			Request:  &req,
			Response: httpRsp,
			Error:    terrors.Wrap(err, nil)}
	}
}

// BareClient is the most basic way to send a request, using the default http RoundTripper
func BareClient(req Request) Response {
	return HttpService(RoundTripper)(req)
}

// SendVia round-trips the request via the passed Service. It does not block, instead returning a ResponseFuture
// representing the asynchronous operation to produce the response.
func SendVia(req Request, svc Service) *ResponseFuture {
	done := make(chan struct{}, 0)
	f := &ResponseFuture{
		done: done}
	go func() {
		defer close(done) // makes the response available to waiters
		f.r = svc(req)
	}()
	return f
}

// Send round-trips the request via the default Client. It does not block, instead returning a ResponseFuture
// representing the asynchronous operation to produce the response. It is equivalent to:
//
//  SendVia(req, Client)
func Send(req Request) *ResponseFuture {
	return SendVia(req, Client)
}
<commit_msg>Add the ability to have H2c with Prior Knowledge<commit_after>package typhon

import (
	"context"
	"crypto/tls"
	"net"
	"net/http"
	"time"

	"github.com/monzo/terrors"
	"golang.org/x/net/http2"
)

var (
	// Client is used to send all requests by default. It can be overridden globally but MUST only be done before use
	// takes place; access is not synchronised.
	Client Service = BareClient
	// RoundTripper chooses HTTP1, or H2C based on a context flag (see WithH2C)
	RoundTripper http.RoundTripper = dynamicRoundTripper{}

	// HTTPRoundTripper is a HTTP1 and TLS HTTP2 client
	HTTPRoundTripper http.RoundTripper = &http.Transport{
		Proxy:               http.ProxyFromEnvironment,
		DisableKeepAlives:   false,
		DisableCompression:  false,
		IdleConnTimeout:     10 * time.Minute,
		MaxIdleConnsPerHost: 10,
	}

	// H2cRoundTripper is a prior-knowledge H2c client. It does not support ProxyFromEnvironment.
	H2cRoundTripper http.RoundTripper = &http2.Transport{
		AllowHTTP: true,
		// This monstrosity is needed to get the http2 Transport to dial over cleartext.
		// See https://github.com/thrawn01/h2c-golang-example
		DialTLS: func(network, addr string, _ *tls.Config) (net.Conn, error) {
			return net.Dial(network, addr)
		},
	}
)

// A ResponseFuture is a container for a Response which will materialise at some point.
type ResponseFuture struct {
	done <-chan struct{} // guards access to r
	r    Response
}

// WaitC returns a channel which can be waited upon until the response is available
func (f *ResponseFuture) WaitC() <-chan struct{} {
	return f.done
}

// Response provides access to the response object, blocking until it is available
func (f *ResponseFuture) Response() Response {
	<-f.WaitC()
	return f.r
}

// HttpService returns a Service which sends requests via the given net/http RoundTripper.
// Only use this if you need to do something custom at the transport level.
func HttpService(rt http.RoundTripper) Service {
	return func(req Request) Response {
		ctx := req.unwrappedContext()
		httpRsp, err := rt.RoundTrip(req.Request.WithContext(ctx))
		// When the calling context is cancelled, close the response body
		// This protects callers that forget to call Close(), or those which proxy responses upstream
		//
		// If the calling context is infinite (ie. returns nil for Done()), it can never signal cancellation
		// so we bypass this as a performance optimisation
		if httpRsp != nil && httpRsp.Body != nil && ctx.Done() != nil {
			body := newDoneReader(httpRsp.Body, httpRsp.ContentLength)
			httpRsp.Body = body
			go func() {
				select {
				case <-body.closed:
				case <-ctx.Done():
					body.Close()
				}
			}()
		}
		return Response{
			Request:  &req,
			Response: httpRsp,
			Error:    terrors.Wrap(err, nil)}
	}
}

// BareClient is the most basic way to send a request, using the default http RoundTripper
func BareClient(req Request) Response {
	return HttpService(RoundTripper)(req)
}

// SendVia round-trips the request via the passed Service. It does not block, instead returning a ResponseFuture
// representing the asynchronous operation to produce the response.
func SendVia(req Request, svc Service) *ResponseFuture {
	done := make(chan struct{}, 0)
	f := &ResponseFuture{
		done: done}
	go func() {
		defer close(done) // makes the response available to waiters
		f.r = svc(req)
	}()
	return f
}

// Send round-trips the request via the default Client. It does not block, instead returning a ResponseFuture
// representing the asynchronous operation to produce the response. It is equivalent to:
//
//  SendVia(req, Client)
func Send(req Request) *ResponseFuture {
	return SendVia(req, Client)
}

type withH2C struct{}

// WithH2C instructs the dynamicRoundTripper to use prior-knowledge cleartext HTTP2 instead of HTTP1.1
func WithH2C(ctx context.Context) context.Context {
	return context.WithValue(ctx, withH2C{}, true)
}

func isH2C(ctx context.Context) bool {
	b, _ := ctx.Value(withH2C{}).(bool)
	return b
}

type dynamicRoundTripper struct{}

func (d dynamicRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
	if r.URL.Scheme == "http" && isH2C(r.Context()) {
		return H2cRoundTripper.RoundTrip(r)
	}
	return HTTPRoundTripper.RoundTrip(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"
	"os"
	"runtime"
	"strings"
)

const (
	ClientVersion = "1.1.0"
	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
}
<commit_msg>Remove bogus 'os' import<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 = "1.1.0"
	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
}
<|endoftext|>