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

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

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

var (
	// Default retry configuration
	defaultRetryWaitMin = 1 * time.Second
	defaultRetryWaitMax = 30 * time.Second
	defaultRetryMax     = 4

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

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

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

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

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

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

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

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

	return &Request{body, httpReq}, nil
}

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

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

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

// Backoff specifies a policy for how long to wait between retries.
// It is called after a failing request to determine the amount of time
// that should pass before trying again.
type Backoff func(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration

// ErrorHandler is called if retries are expired, containing the last status
// from the http library. The default will close the body and return an error
// indicating how many tries were attempted. If overriding this, be sure to
// close the body if needed.
type ErrorHandler func(resp *http.Response, err error, numTries int) (*http.Response, error)

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

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

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

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

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

	// Backoff specifies the policy for how long to wait between retries
	Backoff Backoff

	// ErrorHandler specifies the custom error handler to use, if any
	ErrorHandler ErrorHandler
}

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

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

	return false, nil
}

// DefaultBackoff provides a default callback for Client.Backoff which
// will perform exponential backoff based on the attempt number and limited
// by the provided minimum and maximum durations.
func DefaultBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration {
	mult := math.Pow(2, float64(attemptNum)) * float64(min)
	sleep := time.Duration(mult)
	if float64(sleep) != mult || sleep > max {
		sleep = max
	}
	return sleep
}

// LinearJitterBackoff provides a callback for Client.Backoff which will
// perform linear backoff based on the attempt number and limited by the
// provided minimum and maximum, which are applied *prior to linear
// adjustment*. Jitter will be applied to prevent a thundering herd.
func LinearJitterBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration {
	if max <= min {
		// Unclear what to do here, so return min
		return min
	}
	// Seed rand; doing this every time is fine
	rand := rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
	// Pick a random number that lies somewhere between the min and max and
	// multiply by the attemptNum. attemptNum starts at zero so we always
	// increment here.
	return time.Duration((int64(rand.Int63()) % int64(max-min)) * int64((attemptNum + 1)))
}

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

	var resp *http.Response
	var err error

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

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

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

		// Attempt the request
		resp, err = c.HTTPClient.Do(req.Request)
		if resp != nil {
			code = resp.StatusCode
		}

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

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

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

		// We do this before drainBody beause there's no need for the I/O if
		// we're breaking out
		remain := c.RetryMax - i
		if remain == 0 {
			break
		}

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

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

	if c.ErrorHandler != nil {
		return c.ErrorHandler(resp, err, c.RetryMax+1)
	}

	// By default, we close the response body and return an error without
	// returning the response
	if resp != nil {
		resp.Body.Close()
	}
	return nil, fmt.Errorf("%s %s giving up after %d attempts",
		req.Method, req.URL, c.RetryMax+1)
}

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

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

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

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

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

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

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

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

// PostForm is a convenience method for doing simple POST operations using
// pre-filled url.Values form data.
func (c *Client) PostForm(url string, data url.Values) (*http.Response, error) {
	return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
}
<commit_msg>Update LinearJitterBackoff comment<commit_after>// The retryablehttp package provides a familiar HTTP client interface with
// automatic retries and exponential backoff. It is a thin wrapper over the
// standard net/http client library and exposes nearly the same public API.
// This makes retryablehttp very easy to drop into existing programs.
//
// retryablehttp performs automatic retries under certain conditions. Mainly, if
// an error is returned by the client (connection errors etc), or if a 500-range
// response is received, then a retry is invoked. Otherwise, the response is
// returned and left to the caller to interpret.
//
// The main difference from net/http is that requests which take a request body
// (POST/PUT et. al) require an io.ReadSeeker to be provided. This enables the
// request body to be "rewound" if the initial request fails so that the full
// request can be attempted again.
package retryablehttp

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

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

var (
	// Default retry configuration
	defaultRetryWaitMin = 1 * time.Second
	defaultRetryWaitMax = 30 * time.Second
	defaultRetryMax     = 4

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

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

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

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

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

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

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

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

	return &Request{body, httpReq}, nil
}

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

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

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

// Backoff specifies a policy for how long to wait between retries.
// It is called after a failing request to determine the amount of time
// that should pass before trying again.
type Backoff func(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration

// ErrorHandler is called if retries are expired, containing the last status
// from the http library. The default will close the body and return an error
// indicating how many tries were attempted. If overriding this, be sure to
// close the body if needed.
type ErrorHandler func(resp *http.Response, err error, numTries int) (*http.Response, error)

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

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

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

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

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

	// Backoff specifies the policy for how long to wait between retries
	Backoff Backoff

	// ErrorHandler specifies the custom error handler to use, if any
	ErrorHandler ErrorHandler
}

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

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

	return false, nil
}

// DefaultBackoff provides a default callback for Client.Backoff which
// will perform exponential backoff based on the attempt number and limited
// by the provided minimum and maximum durations.
func DefaultBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration {
	mult := math.Pow(2, float64(attemptNum)) * float64(min)
	sleep := time.Duration(mult)
	if float64(sleep) != mult || sleep > max {
		sleep = max
	}
	return sleep
}

// LinearJitterBackoff provides a callback for Client.Backoff which will
// perform linear backoff based on the attempt number and with jitter to
// prevent a thundering herd. The final backoff time is the attempt number
// multiplied by the random chosen value between min/max.
func LinearJitterBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration {
	if max <= min {
		// Unclear what to do here, so return min
		return min
	}
	// Seed rand; doing this every time is fine
	rand := rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
	// Pick a random number that lies somewhere between the min and max and
	// multiply by the attemptNum. attemptNum starts at zero so we always
	// increment here.
	return time.Duration((int64(rand.Int63()) % int64(max-min)) * int64((attemptNum + 1)))
}

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

	var resp *http.Response
	var err error

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

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

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

		// Attempt the request
		resp, err = c.HTTPClient.Do(req.Request)
		if resp != nil {
			code = resp.StatusCode
		}

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

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

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

		// We do this before drainBody beause there's no need for the I/O if
		// we're breaking out
		remain := c.RetryMax - i
		if remain == 0 {
			break
		}

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

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

	if c.ErrorHandler != nil {
		return c.ErrorHandler(resp, err, c.RetryMax+1)
	}

	// By default, we close the response body and return an error without
	// returning the response
	if resp != nil {
		resp.Body.Close()
	}
	return nil, fmt.Errorf("%s %s giving up after %d attempts",
		req.Method, req.URL, c.RetryMax+1)
}

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

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

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

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

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

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

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

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

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


<commit_before>package socketio_client

import (
	"net/url"
	"path"
	"reflect"
	"strings"
	"sync"
)

type Options struct {
	Transport string            //protocol name string,websocket polling...
	Query     map[string]string //url的附加的参数
	Header 	  map[string][]string
}

type Client struct {
	opts *Options

	conn *clientConn

	eventsLock sync.RWMutex
	events     map[string]*caller
	acks       map[int]*caller
	id         int
	namespace  string
}

func NewClient(uri string, opts *Options) (client *Client, err error) {

	url, err := url.Parse(uri)
	if err != nil {
		return
	}
	url.Path = path.Join("/socket.io", url.Path)
	url.Path = url.EscapedPath()
	if strings.HasSuffix(url.Path, "socket.io") {
		url.Path += "/"
	}
	q := url.Query()
	for k, v := range opts.Query {
		q.Set(k, v)
	}
	url.RawQuery = q.Encode()

	socket, err := newClientConn(opts, url)
	if err != nil {
		return
	}

	client = &Client{
		opts: opts,
		conn: socket,

		events: make(map[string]*caller),
		acks:   make(map[int]*caller),
	}

	go client.readLoop()

	return
}

func (client *Client) On(message string, f interface{}) (err error) {
	c, err := newCaller(f)
	if err != nil {
		return
	}
	client.eventsLock.Lock()
	client.events[message] = c
	client.eventsLock.Unlock()
	return
}

func (client *Client) Emit(message string, args ...interface{}) (err error) {
	var c *caller
	if l := len(args); l > 0 {
		fv := reflect.ValueOf(args[l-1])
		if fv.Kind() == reflect.Func {
			var err error
			c, err = newCaller(args[l-1])
			if err != nil {
				return err
			}
			args = args[:l-1]
		}
	}
	args = append([]interface{}{message}, args...)
	if c != nil {
		id, err := client.sendId(args)
		if err != nil {
			return err
		}
		client.acks[id] = c
		return nil
	}
	return client.send(args)
}

func (client *Client) sendConnect() error {
	packet := packet{
		Type: _CONNECT,
		Id:   -1,
		NSP:  client.namespace,
	}
	encoder := newEncoder(client.conn)
	return encoder.Encode(packet)
}

func (client *Client) sendId(args []interface{}) (int, error) {
	packet := packet{
		Type: _EVENT,
		Id:   client.id,
		NSP:  client.namespace,
		Data: args,
	}
	client.id++
	if client.id < 0 {
		client.id = 0
	}
	encoder := newEncoder(client.conn)
	err := encoder.Encode(packet)
	if err != nil {
		return -1, nil
	}
	return packet.Id, nil
}

func (client *Client) send(args []interface{}) error {
	packet := packet{
		Type: _EVENT,
		Id:   -1,
		NSP:  client.namespace,
		Data: args,
	}
	encoder := newEncoder(client.conn)
	return encoder.Encode(packet)
}

func (client *Client) onPacket(decoder *decoder, packet *packet) ([]interface{}, error) {
	var message string
	switch packet.Type {
	case _CONNECT:
		message = "connection"
	case _DISCONNECT:
		message = "disconnection"
	case _ERROR:
		message = "error"
	case _ACK:
	case _BINARY_ACK:
		return nil, client.onAck(packet.Id, decoder, packet)
	default:
		message = decoder.Message()
	}
	client.eventsLock.RLock()
	c, ok := client.events[message]
	client.eventsLock.RUnlock()
	if !ok {
		// If the message is not recognized by the server, the decoder.currentCloser
		// needs to be closed otherwise the server will be stuck until the e
		decoder.Close()
		return nil, nil
	}
	args := c.GetArgs()
	olen := len(args)
	if olen > 0 {
		packet.Data = &args
		if err := decoder.DecodeData(packet); err != nil {
			return nil, err
		}
	}
	for i := len(args); i < olen; i++ {
		args = append(args, nil)
	}

	retV := c.Call(args)
	if len(retV) == 0 {
		return nil, nil
	}

	var err error
	if last, ok := retV[len(retV)-1].Interface().(error); ok {
		err = last
		retV = retV[0 : len(retV)-1]
	}
	ret := make([]interface{}, len(retV))
	for i, v := range retV {
		ret[i] = v.Interface()
	}
	return ret, err
}

func (client *Client) onAck(id int, decoder *decoder, packet *packet) error {
	c, ok := client.acks[id]
	if !ok {
		return nil
	}
	delete(client.acks, id)

	args := c.GetArgs()
	packet.Data = &args
	if err := decoder.DecodeData(packet); err != nil {
		return err
	}
	c.Call(args)
	return nil
}

func (client *Client) readLoop() error {
	defer func() {
		p := packet{
			Type: _DISCONNECT,
			Id:   -1,
		}
		client.onPacket(nil, &p)
	}()

	for {
		decoder := newDecoder(client.conn)
		var p packet
		if err := decoder.Decode(&p); err != nil {
			return err
		}
		ret, err := client.onPacket(decoder, &p)
		if err != nil {
			return err
		}
		switch p.Type {
		case _CONNECT:
			client.namespace = p.NSP
			// !!!下面这个不能有，否则会有死循环
			//client.sendConnect()
		case _BINARY_EVENT:
			fallthrough
		case _EVENT:
			if p.Id >= 0 {
				p := packet{
					Type: _ACK,
					Id:   p.Id,
					NSP:  client.namespace,
					Data: ret,
				}
				encoder := newEncoder(client.conn)
				if err := encoder.Encode(p); err != nil {
					return err
				}
			}
		case _DISCONNECT:
			return nil
		}
	}
}
<commit_msg>bug fix.<commit_after>package socketio_client

import (
	"net/url"
	"path"
	"reflect"
	"strings"
	"sync"
)

type Options struct {
	Transport string            //protocol name string,websocket polling...
	Query     map[string]string //url的附加的参数
	Header 	  map[string][]string
}

type Client struct {
	opts *Options

	conn *clientConn

	eventsLock sync.RWMutex
	events     map[string]*caller
	acks       map[int]*caller
	id         int
	namespace  string
}

func NewClient(uri string, opts *Options) (client *Client, err error) {

	url, err := url.Parse(uri)
	if err != nil {
		return
	}
	url.Path = path.Join("/socket.io", url.Path)
	url.Path = url.EscapedPath()
	if strings.HasSuffix(url.Path, "socket.io") {
		url.Path += "/"
	}
	q := url.Query()
	for k, v := range opts.Query {
		q.Set(k, v)
	}
	url.RawQuery = q.Encode()

	socket, err := newClientConn(opts, url)
	if err != nil {
		return
	}

	client = &Client{
		opts: opts,
		conn: socket,

		events: make(map[string]*caller),
		acks:   make(map[int]*caller),
	}

	go client.readLoop()

	return
}

func (client *Client) On(message string, f interface{}) (err error) {
	c, err := newCaller(f)
	if err != nil {
		return
	}
	client.eventsLock.Lock()
	client.events[message] = c
	client.eventsLock.Unlock()
	return
}

func (client *Client) Emit(message string, args ...interface{}) (err error) {
	var c *caller
	if l := len(args); l > 0 {
		fv := reflect.ValueOf(args[l-1])
		if fv.Kind() == reflect.Func {
			var err error
			c, err = newCaller(args[l-1])
			if err != nil {
				return err
			}
			args = args[:l-1]
		}
	}
	args = append([]interface{}{message}, args...)
	if c != nil {
		id, err := client.sendId(args)
		if err != nil {
			return err
		}
		client.acks[id] = c
		return nil
	}
	return client.send(args)
}

func (client *Client) sendConnect() error {
	packet := packet{
		Type: _CONNECT,
		Id:   -1,
		NSP:  client.namespace,
	}
	encoder := newEncoder(client.conn)
	return encoder.Encode(packet)
}

func (client *Client) sendId(args []interface{}) (int, error) {
	packet := packet{
		Type: _EVENT,
		Id:   client.id,
		NSP:  client.namespace,
		Data: args,
	}
	client.id++
	if client.id < 0 {
		client.id = 0
	}
	encoder := newEncoder(client.conn)
	err := encoder.Encode(packet)
	if err != nil {
		return -1, nil
	}
	return packet.Id, nil
}

func (client *Client) send(args []interface{}) error {
	packet := packet{
		Type: _EVENT,
		Id:   -1,
		NSP:  client.namespace,
		Data: args,
	}
	encoder := newEncoder(client.conn)
	return encoder.Encode(packet)
}

func (client *Client) onPacket(decoder *decoder, packet *packet) ([]interface{}, error) {
	var message string
	switch packet.Type {
	case _CONNECT:
		message = "connection"
	case _DISCONNECT:
		message = "disconnection"
	case _ERROR:
		message = "error"
	case _ACK:
		fallthrough
	case _BINARY_ACK:
		return nil, client.onAck(packet.Id, decoder, packet)
	default:
		message = decoder.Message()
	}
	client.eventsLock.RLock()
	c, ok := client.events[message]
	client.eventsLock.RUnlock()
	if !ok {
		// If the message is not recognized by the server, the decoder.currentCloser
		// needs to be closed otherwise the server will be stuck until the e
		decoder.Close()
		return nil, nil
	}
	args := c.GetArgs()
	olen := len(args)
	if olen > 0 {
		packet.Data = &args
		if err := decoder.DecodeData(packet); err != nil {
			return nil, err
		}
	}
	for i := len(args); i < olen; i++ {
		args = append(args, nil)
	}

	retV := c.Call(args)
	if len(retV) == 0 {
		return nil, nil
	}

	var err error
	if last, ok := retV[len(retV)-1].Interface().(error); ok {
		err = last
		retV = retV[0 : len(retV)-1]
	}
	ret := make([]interface{}, len(retV))
	for i, v := range retV {
		ret[i] = v.Interface()
	}
	return ret, err
}

func (client *Client) onAck(id int, decoder *decoder, packet *packet) error {
	c, ok := client.acks[id]
	if !ok {
		return nil
	}
	delete(client.acks, id)

	args := c.GetArgs()
	packet.Data = &args
	if err := decoder.DecodeData(packet); err != nil {
		return err
	}
	c.Call(args)
	return nil
}

func (client *Client) readLoop() error {
	defer func() {
		p := packet{
			Type: _DISCONNECT,
			Id:   -1,
		}
		client.onPacket(nil, &p)
	}()

	for {
		decoder := newDecoder(client.conn)
		var p packet
		if err := decoder.Decode(&p); err != nil {
			return err
		}
		ret, err := client.onPacket(decoder, &p)
		if err != nil {
			return err
		}
		switch p.Type {
		case _CONNECT:
			client.namespace = p.NSP
			// !!!下面这个不能有，否则会有死循环
			//client.sendConnect()
		case _BINARY_EVENT:
			fallthrough
		case _EVENT:
			if p.Id >= 0 {
				p := packet{
					Type: _ACK,
					Id:   p.Id,
					NSP:  client.namespace,
					Data: ret,
				}
				encoder := newEncoder(client.conn)
				if err := encoder.Encode(p); err != nil {
					return err
				}
			}
		case _DISCONNECT:
			return nil
		}
	}
}
<|endoftext|>


<commit_before>package kafka

import (
	"encoding/binary"
	"math"
	"net"
)

type ApiKey int16
type ApiVersion int16

type API struct {
	key     ApiKey
	version ApiVersion
}

var (
	REQUEST_PRODUCE        = API{0, 0}
	REQUEST_FETCH          = API{1, 0}
	REQUEST_OFFSET         = API{2, 0}
	REQUEST_METADATA       = API{3, 0}
	REQUEST_LEADER_AND_ISR = API{4, 0}
	REQUEST_STOP_REPLICA   = API{5, 0}
	REQUEST_OFFSET_COMMIT  = API{6, 0}
	REQUEST_OFFSET_FETCH   = API{7, 0}
)

type Client struct {
	addr, id       string
	correlation_id int32
	conn           net.Conn
}

func NewClient(addr string) (client *Client, err error) {
	conn, err := net.Dial("tcp", addr)
	if err != nil {
		return nil, err
	}
	client = &Client{addr, "", 0, conn}
	return client, err
}

func (client *Client) write(buf []byte) (err error) {
	size := make([]byte, 4)
	binary.BigEndian.PutUint32(size, uint32(len(buf)))
	_, err = client.conn.Write(size)
	if err != nil {
		return err
	}
	_, err = client.conn.Write(buf)
	if err != nil {
		return err
	}
	return nil
}

func (client *Client) read() (buf []byte, err error) {
	size := make([]byte, 4)
	n, err := client.conn.Read(size)
	if err != nil {
		return nil, err
	}
	if n != 4 {
		return nil, nil
	}
	s := binary.BigEndian.Uint32(size)
	buf = make([]byte, s)
	n, err = client.conn.Read(buf)
	if err != nil {
		return nil, err
	}
	if uint32(n) != s {
		return nil, nil
	}
	return buf, nil
}

func encodeString(in string) (buf []byte) {
	size := len(in)
	if size > math.MaxInt16 {
		panic("string too long to encode") /* Or just return nil? */
	}
	buf = make([]byte, 2+size)
	binary.BigEndian.PutUint16(buf, uint16(size))
	if size > 0 {
		copy(buf[2:], in)
	}
	return buf
}

func encodeBytes(in []byte) (buf []byte) {
	size := len(in)
	if size > math.MaxInt32 {
		panic("bytes too long to encode") /* Or just return nil? */
	}
	buf = make([]byte, 4+size)
	binary.BigEndian.PutUint32(buf, uint32(size))
	if size > 0 {
		copy(buf[4:], in)
	}
	return buf
}

func (client *Client) sendRequest(api *API, body []byte) (err error) {
	id := encodeString(client.id)
	buf := make([]byte, 4+len(id)+len(body))
	binary.BigEndian.PutUint16(buf[0:2], uint16(api.key))
	binary.BigEndian.PutUint16(buf[2:4], uint16(api.version))
	binary.BigEndian.PutUint32(buf[4:8], uint32(client.correlation_id))
	copy(buf[8:], id)
	copy(buf[8+len(id):], body)
	return client.write(buf)
}
<commit_msg>Actually bump the correlation ID<commit_after>package kafka

import (
	"encoding/binary"
	"math"
	"net"
)

type ApiKey int16
type ApiVersion int16

type API struct {
	key     ApiKey
	version ApiVersion
}

var (
	REQUEST_PRODUCE        = API{0, 0}
	REQUEST_FETCH          = API{1, 0}
	REQUEST_OFFSET         = API{2, 0}
	REQUEST_METADATA       = API{3, 0}
	REQUEST_LEADER_AND_ISR = API{4, 0}
	REQUEST_STOP_REPLICA   = API{5, 0}
	REQUEST_OFFSET_COMMIT  = API{6, 0}
	REQUEST_OFFSET_FETCH   = API{7, 0}
)

type Client struct {
	addr, id       string
	correlation_id int32
	conn           net.Conn
}

func NewClient(addr string) (client *Client, err error) {
	conn, err := net.Dial("tcp", addr)
	if err != nil {
		return nil, err
	}
	client = &Client{addr, "", 0, conn}
	return client, err
}

func (client *Client) write(buf []byte) (err error) {
	size := make([]byte, 4)
	binary.BigEndian.PutUint32(size, uint32(len(buf)))
	_, err = client.conn.Write(size)
	if err != nil {
		return err
	}
	_, err = client.conn.Write(buf)
	if err != nil {
		return err
	}
	return nil
}

func (client *Client) read() (buf []byte, err error) {
	size := make([]byte, 4)
	n, err := client.conn.Read(size)
	if err != nil {
		return nil, err
	}
	if n != 4 {
		return nil, nil
	}
	s := binary.BigEndian.Uint32(size)
	buf = make([]byte, s)
	n, err = client.conn.Read(buf)
	if err != nil {
		return nil, err
	}
	if uint32(n) != s {
		return nil, nil
	}
	return buf, nil
}

func encodeString(in string) (buf []byte) {
	size := len(in)
	if size > math.MaxInt16 {
		panic("string too long to encode") /* Or just return nil? */
	}
	buf = make([]byte, 2+size)
	binary.BigEndian.PutUint16(buf, uint16(size))
	if size > 0 {
		copy(buf[2:], in)
	}
	return buf
}

func encodeBytes(in []byte) (buf []byte) {
	size := len(in)
	if size > math.MaxInt32 {
		panic("bytes too long to encode") /* Or just return nil? */
	}
	buf = make([]byte, 4+size)
	binary.BigEndian.PutUint32(buf, uint32(size))
	if size > 0 {
		copy(buf[4:], in)
	}
	return buf
}

func (client *Client) sendRequest(api *API, body []byte) (err error) {
	id := encodeString(client.id)
	buf := make([]byte, 4+len(id)+len(body))
	binary.BigEndian.PutUint16(buf[0:2], uint16(api.key))
	binary.BigEndian.PutUint16(buf[2:4], uint16(api.version))
	binary.BigEndian.PutUint32(buf[4:8], uint32(client.correlation_id))
	client.correlation_id++
	copy(buf[8:], id)
	copy(buf[8+len(id):], body)
	return client.write(buf)
}
<|endoftext|>


<commit_before>package client

import (
	"net/http"
	"net/url"
)

type ClientCfg struct {
	userAgent string
	useNUM    int
	maxTTL    int
	proxyURL  string
}

type client struct {
	cfg *ClientCfg
	c   *http.Client
	ip  string
}

type Client struct {
	p    int
	list []*client
}

func newClient(cfg ClientCfg) (*client, error) {
	if cfg.proxyURL == "" {
		return &client{
			&cfg,
			new(http.Client),
			"",
		}, nil
	}
	proxyURL, err := url.Parse(cfg.proxyURL)
	if err != nil {
		return nil, err
	}
	return &client{
		&cfg,
		&http.Client{
			Transport: &http.Transport{
				Proxy: http.ProxyURL(proxyURL),
			},
		},
		"",
	}, nil
}

func NewClient(cfgs ...ClientCfg) *Client {
}
<commit_msg>http clientの生成<commit_after>package client

import (
	"net/http"
	"net/url"
)

type ClientCfg struct {
	userAgent string
	useNUM    int
	maxTTL    int
	proxyURL  string
}

type client struct {
	cfg *ClientCfg
	c   *http.Client
	ip  string
}

type Client struct {
	p    int
	list []*client
}

func newClient(cfg ClientCfg) (*client, error) {
	if cfg.proxyURL == "" {
		return &client{
			&cfg,
			new(http.Client),
			"",
		}, nil
	}
	proxyURL, err := url.Parse(cfg.proxyURL)
	if err != nil {
		return nil, err
	}
	return &client{
		&cfg,
		&http.Client{
			Transport: &http.Transport{
				Proxy: http.ProxyURL(proxyURL),
			},
		},
		"",
	}, nil
}

func NewClient(cfgs ...ClientCfg) *Client {
	ret := Client{
		0,
		make([]*client, 0, len(cfgs)),
	}
	for _, cfg := range cfgs {
		c, err := newClient(cfg)
		if err != nil {
			continue
		}
		ret.list = append(ret.list, c)
	}
	return &ret
}

func (c *Client) Len() int {
	return len(c.list)
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"

	"github.com/phil-mansfield/shellfish/cmd/catalog"
	"github.com/phil-mansfield/shellfish/cmd/env"
	"github.com/phil-mansfield/shellfish/cmd/halo"
	"github.com/phil-mansfield/shellfish/cmd/memo"
	"github.com/phil-mansfield/shellfish/io"
	"github.com/phil-mansfield/shellfish/parse"
)

const finderCells = 150

// IDConfig contains the configuration fileds for the 'id' mode of the shellfish
// tool.
type IDConfig struct {
	idType                     string
	ids                        []int64
	idStart, idEnd, snap, mult int64

	exclusionStrategy   string
	exclusionRadiusMult float64
}

var _ Mode = &IDConfig{}

// ExampleConfig creates an example id.config file.
func (config *IDConfig) ExampleConfig() string {
	return `[id.config]
#####################
## Required Fields ##
#####################

# Index of the snapshot to be analyzed.
Snap = 100

IDs = 10, 11, 12, 13, 14

#####################
## Optional Fields ##
#####################

# IDType indicates what the input IDs correspond to. It can be set to the
# following modes:
# halo-id - The numeric IDs given in the halo catalog.
# m200m   - The rank of the halos when sorted by M200m.
#
# Defaults to m200m if not set.
# IDType = m200m

# An alternative way of specifying IDs is to select start and end (inclusive)
# ID values. If the IDs variable is not set, both of these values must be set.
#
# IDStart = 10
# IDEnd = 15

# ExclusionStrategy determines how to exclude IDs from the given set. This is
# useful because splashback shells are not particularly meaningful for
# subhalos. It can be set to the following modes:
# none    - No halos are removed
# subhalo - Halos flagged as subhalos in the catalog are removed
# overlap - Halos which have an R200m shell that overlaps with a larger halo's
#           R200m shell are removed
#
# ExclusionStrategy defaults to overlap if not set.
#
# ExclusionStrategy = overlap

# ExclusionRadiusMult is a multiplier of R200m applied for the sake of
# determining exclusions.
#
# ExclusionRadiusMult defaults to 1 if not set.
#
# ExclustionRadiusMult = 1

# Mult is the number of times a given ID should be repeated. This is most useful
# if you want to estimate the scatter in shell measurements for halos with a
# given set of shell parameters.
#
# Mult defaults to 1 if not set.
#
# Mult = 1`
}

// ReadConfig reads in an id.config file into config.
func (config *IDConfig) ReadConfig(fname string) error {

	vars := parse.NewConfigVars("id.config")
	vars.String(&config.idType, "IDType", "m200m")
	vars.Ints(&config.ids, "IDs", []int64{})
	vars.Int(&config.idStart, "IDStart", -1)
	vars.Int(&config.idEnd, "IDEnd", -1)
	vars.Int(&config.mult, "Mult", 1)
	vars.Int(&config.snap, "Snap", -1)
	vars.String(&config.exclusionStrategy, "ExclusionStrategy", "subhalo")
	vars.Float(&config.exclusionRadiusMult, "ExclusionRadiusMult", 1)

	if fname == "" {
		return nil
	}
	if err := parse.ReadConfig(fname, vars); err != nil {
		return err
	}
	return config.validate()
}

// validate checks whether all the fields of config are valid.
func (config *IDConfig) validate() error {
	switch config.idType {
	case "halo-id", "m200m":
	default:
		return fmt.Errorf("The 'IDType' variable is set to '%s', which I "+
			"don't recognize.", config.idType)
	}

	switch config.exclusionStrategy {
	case "none", "subhalo":
	case "overlap":
		if config.exclusionRadiusMult <= 0 {
			return fmt.Errorf("The 'ExclusionRadiusMult' varaible is set to "+
				"%g, but it needs to be positive.", config.exclusionRadiusMult)
		}
	default:
		return fmt.Errorf("The 'ExclusionStrategy' variable is set to '%s', "+
			"which I don't recognize.", config.exclusionStrategy)
	}

	// TODO: Check the ranges of the IDs as well as IDStart and IDEnd
	if len(config.ids) == 0 {
		switch {
		case config.idStart == -1 && config.idEnd == -1:
			return fmt.Errorf("'IDs' variable not set.")
		case config.idStart == -1:
			return fmt.Errorf("'IDStart variable not set.")
		case config.idEnd == -1:
			return fmt.Errorf("'IDEnd' variable not set.")
		case config.idEnd < config.idStart:
			return fmt.Errorf("'IDEnd' variable set to %d, but 'IDStart' "+
				"variable set to %d.", config.idEnd, config.idStart)
		}
	}

	switch {
	case config.snap == -1:
		return fmt.Errorf("'Snap' variable not set.")
	case config.snap < 0:
		return fmt.Errorf("'Snap' variable set to %d.", config.snap)
	}

	if config.mult <= 0 {
		return fmt.Errorf("'Mult' variable set to %d", config.mult)
	}

	return nil
}

// Run executes the ID mode of shellfish tool.
func (config *IDConfig) Run(
	flags []string, gConfig *GlobalConfig, e *env.Environment, stdin []string,
) ([]string, error) {

	if config.snap < gConfig.SnapMin || config.snap > gConfig.SnapMax {
		return nil, fmt.Errorf("'Snap' = %d, but 'SnapMin' = %d and "+
			"'SnapMax = %d'", config.snap, gConfig.SnapMin, gConfig.SnapMax)
	}

	// Get IDs and snapshots

	rawIds := getIDs(config.idStart, config.idEnd, config.ids)

	vars := &halo.VarColumns{
		ID:    int(gConfig.HaloIDColumn),
		X:     int(gConfig.HaloPositionColumns[0]),
		Y:     int(gConfig.HaloPositionColumns[1]),
		Z:     int(gConfig.HaloPositionColumns[2]),
		M200m: int(gConfig.HaloM200mColumn),
	}

	var (
		ids, snaps []int
		buf        io.VectorBuffer
	)

	switch config.idType {
	case "halo-id":
		snaps = make([]int, len(rawIds))
		for i := range snaps {
			snaps[i] = int(config.snap)
		}
		ids = rawIds

		var err error
		buf, err = getVectorBuffer(
			e.ParticleCatalog(snaps[0], 0),
			gConfig.SnapshotType, gConfig.Endianness,
		)
		if err != nil {
			return nil, err
		}
	case "m200m":
		snaps = make([]int, len(rawIds))
		for i := range snaps {
			snaps[i] = int(config.snap)
		}

		var err error
		buf, err = getVectorBuffer(
			e.ParticleCatalog(snaps[0], 0),
			gConfig.SnapshotType, gConfig.Endianness,
		)
		if err != nil {
			return nil, err
		}

		ids, err = convertSortedIDs(rawIds, int(config.snap), vars, buf, e)
		if err != nil {
			return nil, err
		}
	default:
		panic("Impossible")
	}

	// Tag subhalos, if neccessary.
	exclude := make([]bool, len(ids))
	switch config.exclusionStrategy {
	case "none":
	case "subhalo":
		panic("subhalo is not implemented")
	case "overlap":
		var err error
		exclude, err = findOverlapSubs(ids, snaps, vars, buf, e, config)
		if err != nil {
			return nil, err
		}
	}

	// Generate lines
	intCols := [][]int{ids, snaps}
	floatCols := [][]float64{}
	colOrder := []int{0, 1}
	lines := catalog.FormatCols(intCols, floatCols, colOrder)

	// Filter
	fLines := []string{}
	for i := range lines {
		if !exclude[i] {
			fLines = append(fLines, lines[i])
		}
	}

	// Multiply
	mLines := []string{}
	for i := range fLines {
		for j := 0; j < int(config.mult); j++ {
			mLines = append(mLines, fLines[i])
		}
	}

	cString := catalog.CommentString(
		[]string{"ID", "Snapshot"}, []string{}, []int{0, 1},
	)
	mLines = append([]string{cString}, mLines...)

	return mLines, nil
}

func getIDs(idStart, idEnd int64, ids []int64) []int {
	if idStart != -1 {
		out := make([]int, idEnd-idStart)
		for i := range out {
			out[i] = int(idStart) + i
		}
		return out
	} else {
		out := make([]int, len(ids))
		for i := range out {
			out[i] = int(ids[i])
		}
		return out
	}
}

func convertSortedIDs(
	rawIDs []int, snap int, vars *halo.VarColumns,
	buf io.VectorBuffer, e *env.Environment,
) ([]int, error) {
	maxID := 0
	for _, id := range rawIDs {
		if id > maxID {
			maxID = id
		}
	}

	rids, err := memo.ReadSortedRockstarIDs(snap, maxID, vars, buf, e)
	if err != nil {
		return nil, err
	}

	ids := make([]int, len(rawIDs))
	for i := range ids {
		ids[i] = rids[rawIDs[i]]
	}
	return ids, nil
}

func findOverlapSubs(
	rawIDs, snaps []int, vars *halo.VarColumns,
	buf io.VectorBuffer, e *env.Environment, config *IDConfig,
) ([]bool, error) {
	isSub := make([]bool, len(rawIDs))

	// Group by snapshot.
	snapGroups := make(map[int][]int)
	groupIdxs := make(map[int][]int)
	for i, id := range rawIDs {
		snap := snaps[i]
		snapGroups[snap] = append(snapGroups[snap], id)
		groupIdxs[snap] = append(groupIdxs[snap], i)
	}

	// Load each snapshot.
	hds, _, err := memo.ReadHeaders(snaps[0], buf, e)
	if err != nil {
		return nil, err
	}
	hd := hds[0]

	for snap, group := range snapGroups {
		rids, err := memo.ReadSortedRockstarIDs(snap, -1, vars, buf, e)
		if err != nil {
			return nil, err
		}
		_, xs, ys, zs, _, rs, err := memo.ReadRockstar(snap, rids, vars, buf, e)

		g := halo.NewGrid(finderCells, hd.TotalWidth, len(xs))
		g.Insert(xs, ys, zs)
		sf := halo.NewSubhaloFinder(g)
		sf.FindSubhalos(xs, ys, zs, rs, config.exclusionRadiusMult)

		for i, id := range group {
			origIdx := groupIdxs[snap][i]
			// TODO: Holy linear search, batman! Fix this.
			for j, checkID := range rids {
				if checkID == id {
					isSub[origIdx] = sf.HostCount(j) > 0
					break
				} else if j == len(rids)-1 {
					return nil, fmt.Errorf("ID %d not in halo list.", id)
				}
			}
		}
	}
	return isSub, nil
}
<commit_msg>More descriptive erros when id.config not passed.<commit_after>package cmd

import (
	"fmt"

	"github.com/phil-mansfield/shellfish/cmd/catalog"
	"github.com/phil-mansfield/shellfish/cmd/env"
	"github.com/phil-mansfield/shellfish/cmd/halo"
	"github.com/phil-mansfield/shellfish/cmd/memo"
	"github.com/phil-mansfield/shellfish/io"
	"github.com/phil-mansfield/shellfish/parse"
)

const finderCells = 150

// IDConfig contains the configuration fileds for the 'id' mode of the shellfish
// tool.
type IDConfig struct {
	idType                     string
	ids                        []int64
	idStart, idEnd, snap, mult int64

	exclusionStrategy   string
	exclusionRadiusMult float64
}

var _ Mode = &IDConfig{}

// ExampleConfig creates an example id.config file.
func (config *IDConfig) ExampleConfig() string {
	return `[id.config]
#####################
## Required Fields ##
#####################

# Index of the snapshot to be analyzed.
Snap = 100

IDs = 10, 11, 12, 13, 14

#####################
## Optional Fields ##
#####################

# IDType indicates what the input IDs correspond to. It can be set to the
# following modes:
# halo-id - The numeric IDs given in the halo catalog.
# m200m   - The rank of the halos when sorted by M200m.
#
# Defaults to m200m if not set.
# IDType = m200m

# An alternative way of specifying IDs is to select start and end (inclusive)
# ID values. If the IDs variable is not set, both of these values must be set.
#
# IDStart = 10
# IDEnd = 15

# ExclusionStrategy determines how to exclude IDs from the given set. This is
# useful because splashback shells are not particularly meaningful for
# subhalos. It can be set to the following modes:
# none    - No halos are removed
# subhalo - Halos flagged as subhalos in the catalog are removed
# overlap - Halos which have an R200m shell that overlaps with a larger halo's
#           R200m shell are removed
#
# ExclusionStrategy defaults to overlap if not set.
#
# ExclusionStrategy = overlap

# ExclusionRadiusMult is a multiplier of R200m applied for the sake of
# determining exclusions.
#
# ExclusionRadiusMult defaults to 1 if not set.
#
# ExclustionRadiusMult = 1

# Mult is the number of times a given ID should be repeated. This is most useful
# if you want to estimate the scatter in shell measurements for halos with a
# given set of shell parameters.
#
# Mult defaults to 1 if not set.
#
# Mult = 1`
}

// ReadConfig reads in an id.config file into config.
func (config *IDConfig) ReadConfig(fname string) error {

	vars := parse.NewConfigVars("id.config")
	vars.String(&config.idType, "IDType", "m200m")
	vars.Ints(&config.ids, "IDs", []int64{})
	vars.Int(&config.idStart, "IDStart", -1)
	vars.Int(&config.idEnd, "IDEnd", -1)
	vars.Int(&config.mult, "Mult", 1)
	vars.Int(&config.snap, "Snap", -1)
	vars.String(&config.exclusionStrategy, "ExclusionStrategy", "subhalo")
	vars.Float(&config.exclusionRadiusMult, "ExclusionRadiusMult", 1)

	if fname == "" {
		return nil
	}
	if err := parse.ReadConfig(fname, vars); err != nil {
		return err
	}
	return config.validate()
}

// validate checks whether all the fields of config are valid.
func (config *IDConfig) validate() error {
	switch config.idType {
	case "halo-id", "m200m":
	default:
		return fmt.Errorf("The 'IDType' variable is set to '%s', which I "+
			"don't recognize.", config.idType)
	}

	switch config.exclusionStrategy {
	case "none", "subhalo":
	case "overlap":
		if config.exclusionRadiusMult <= 0 {
			return fmt.Errorf("The 'ExclusionRadiusMult' varaible is set to "+
				"%g, but it needs to be positive.", config.exclusionRadiusMult)
		}
	default:
		return fmt.Errorf("The 'ExclusionStrategy' variable is set to '%s', "+
			"which I don't recognize.", config.exclusionStrategy)
	}

	// TODO: Check the ranges of the IDs as well as IDStart and IDEnd
	if len(config.ids) == 0 {
		switch {
		case config.idStart == -1 && config.idEnd == -1:
			return fmt.Errorf("'IDs' variable not set.")
		case config.idStart == -1:
			return fmt.Errorf("'IDStart variable not set.")
		case config.idEnd == -1:
			return fmt.Errorf("'IDEnd' variable not set.")
		case config.idEnd < config.idStart:
			return fmt.Errorf("'IDEnd' variable set to %d, but 'IDStart' "+
				"variable set to %d.", config.idEnd, config.idStart)
		}
	}

	switch {
	case config.snap == -1:
		return fmt.Errorf("'Snap' variable not set.")
	case config.snap < 0:
		return fmt.Errorf("'Snap' variable set to %d.", config.snap)
	}

	if config.mult <= 0 {
		return fmt.Errorf("'Mult' variable set to %d", config.mult)
	}

	return nil
}

// Run executes the ID mode of shellfish tool.
func (config *IDConfig) Run(
	flags []string, gConfig *GlobalConfig, e *env.Environment, stdin []string,
) ([]string, error) {

	if config.snap == -1 {
		return nil, fmt.Errorf("Either no id.config file was provided or "+
			"the 'Snap' variable wasn't set.")
	}

	if config.snap < gConfig.SnapMin || config.snap > gConfig.SnapMax {
		return nil, fmt.Errorf("'Snap' = %d, but 'SnapMin' = %d and "+
			"'SnapMax = %d'", config.snap, gConfig.SnapMin, gConfig.SnapMax)
	}

	// Get IDs and snapshots

	rawIds := getIDs(config.idStart, config.idEnd, config.ids)

	vars := &halo.VarColumns{
		ID:    int(gConfig.HaloIDColumn),
		X:     int(gConfig.HaloPositionColumns[0]),
		Y:     int(gConfig.HaloPositionColumns[1]),
		Z:     int(gConfig.HaloPositionColumns[2]),
		M200m: int(gConfig.HaloM200mColumn),
	}

	var (
		ids, snaps []int
		buf        io.VectorBuffer
	)

	switch config.idType {
	case "halo-id":
		snaps = make([]int, len(rawIds))
		for i := range snaps {
			snaps[i] = int(config.snap)
		}
		ids = rawIds

		var err error
		buf, err = getVectorBuffer(
			e.ParticleCatalog(snaps[0], 0),
			gConfig.SnapshotType, gConfig.Endianness,
		)
		if err != nil {
			return nil, err
		}
	case "m200m":
		snaps = make([]int, len(rawIds))
		for i := range snaps {
			snaps[i] = int(config.snap)
		}

		var err error
		buf, err = getVectorBuffer(
			e.ParticleCatalog(snaps[0], 0),
			gConfig.SnapshotType, gConfig.Endianness,
		)
		if err != nil {
			return nil, err
		}

		ids, err = convertSortedIDs(rawIds, int(config.snap), vars, buf, e)
		if err != nil {
			return nil, err
		}
	default:
		panic("Impossible")
	}

	// Tag subhalos, if neccessary.
	exclude := make([]bool, len(ids))
	switch config.exclusionStrategy {
	case "none":
	case "subhalo":
		panic("subhalo is not implemented")
	case "overlap":
		var err error
		exclude, err = findOverlapSubs(ids, snaps, vars, buf, e, config)
		if err != nil {
			return nil, err
		}
	}

	// Generate lines
	intCols := [][]int{ids, snaps}
	floatCols := [][]float64{}
	colOrder := []int{0, 1}
	lines := catalog.FormatCols(intCols, floatCols, colOrder)

	// Filter
	fLines := []string{}
	for i := range lines {
		if !exclude[i] {
			fLines = append(fLines, lines[i])
		}
	}

	// Multiply
	mLines := []string{}
	for i := range fLines {
		for j := 0; j < int(config.mult); j++ {
			mLines = append(mLines, fLines[i])
		}
	}

	cString := catalog.CommentString(
		[]string{"ID", "Snapshot"}, []string{}, []int{0, 1},
	)
	mLines = append([]string{cString}, mLines...)

	return mLines, nil
}

func getIDs(idStart, idEnd int64, ids []int64) []int {
	if idStart != -1 {
		out := make([]int, idEnd-idStart)
		for i := range out {
			out[i] = int(idStart) + i
		}
		return out
	} else {
		out := make([]int, len(ids))
		for i := range out {
			out[i] = int(ids[i])
		}
		return out
	}
}

func convertSortedIDs(
	rawIDs []int, snap int, vars *halo.VarColumns,
	buf io.VectorBuffer, e *env.Environment,
) ([]int, error) {
	maxID := 0
	for _, id := range rawIDs {
		if id > maxID {
			maxID = id
		}
	}

	rids, err := memo.ReadSortedRockstarIDs(snap, maxID, vars, buf, e)
	if err != nil {
		return nil, err
	}

	ids := make([]int, len(rawIDs))
	for i := range ids {
		ids[i] = rids[rawIDs[i]]
	}
	return ids, nil
}

func findOverlapSubs(
	rawIDs, snaps []int, vars *halo.VarColumns,
	buf io.VectorBuffer, e *env.Environment, config *IDConfig,
) ([]bool, error) {
	isSub := make([]bool, len(rawIDs))

	// Group by snapshot.
	snapGroups := make(map[int][]int)
	groupIdxs := make(map[int][]int)
	for i, id := range rawIDs {
		snap := snaps[i]
		snapGroups[snap] = append(snapGroups[snap], id)
		groupIdxs[snap] = append(groupIdxs[snap], i)
	}

	// Load each snapshot.
	hds, _, err := memo.ReadHeaders(snaps[0], buf, e)
	if err != nil {
		return nil, err
	}
	hd := hds[0]

	for snap, group := range snapGroups {
		rids, err := memo.ReadSortedRockstarIDs(snap, -1, vars, buf, e)
		if err != nil {
			return nil, err
		}
		_, xs, ys, zs, _, rs, err := memo.ReadRockstar(snap, rids, vars, buf, e)

		g := halo.NewGrid(finderCells, hd.TotalWidth, len(xs))
		g.Insert(xs, ys, zs)
		sf := halo.NewSubhaloFinder(g)
		sf.FindSubhalos(xs, ys, zs, rs, config.exclusionRadiusMult)

		for i, id := range group {
			origIdx := groupIdxs[snap][i]
			// TODO: Holy linear search, batman! Fix this.
			for j, checkID := range rids {
				if checkID == id {
					isSub[origIdx] = sf.HostCount(j) > 0
					break
				} else if j == len(rids)-1 {
					return nil, fmt.Errorf("ID %d not in halo list.", id)
				}
			}
		}
	}
	return isSub, nil
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013 Conformal Systems LLC <info@conformal.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package main

import (
	"encoding/json"
	"fmt"
	"github.com/conformal/btcjson"
	"sync"
	"time"
)

// Errors
var (
	// Standard JSON-RPC 2.0 errors
	InvalidRequest = btcjson.Error{
		Code:    -32600,
		Message: "Invalid request",
	}
	MethodNotFound = btcjson.Error{
		Code:    -32601,
		Message: "Method not found",
	}
	InvalidParams = btcjson.Error{
		Code:    -32602,
		Message: "Invalid paramaters",
	}
	InternalError = btcjson.Error{
		Code:    -32603,
		Message: "Internal error",
	}
	ParseError = btcjson.Error{
		Code:    -32700,
		Message: "Parse error",
	}

	// General application defined errors
	MiscError = btcjson.Error{
		Code:    -1,
		Message: "Miscellaneous error",
	}
	ForbiddenBySafeMode = btcjson.Error{
		Code:    -2,
		Message: "Server is in safe mode, and command is not allowed in safe mode",
	}
	TypeError = btcjson.Error{
		Code:    -3,
		Message: "Unexpected type was passed as parameter",
	}
	InvalidAddressOrKey = btcjson.Error{
		Code:    -5,
		Message: "Invalid address or key",
	}
	OutOfMemory = btcjson.Error{
		Code:    -7,
		Message: "Ran out of memory during operation",
	}
	InvalidParameter = btcjson.Error{
		Code:    -8,
		Message: "Invalid, missing or duplicate parameter",
	}
	DatabaseError = btcjson.Error{
		Code:    -20,
		Message: "Database error",
	}
	DeserializationError = btcjson.Error{
		Code:    -22,
		Message: "Error parsing or validating structure in raw format",
	}

	// Wallet errors
	WalletError = btcjson.Error{
		Code:    -4,
		Message: "Unspecified problem with wallet",
	}
	WalletInsufficientFunds = btcjson.Error{
		Code:    -6,
		Message: "Not enough funds in wallet or account",
	}
	WalletInvalidAccountName = btcjson.Error{
		Code:    -11,
		Message: "Invalid account name",
	}
	WalletKeypoolRanOut = btcjson.Error{
		Code:    -12,
		Message: "Keypool ran out, call keypoolrefill first",
	}
	WalletUnlockNeeded = btcjson.Error{
		Code:    -13,
		Message: "Enter the wallet passphrase with walletpassphrase first",
	}
	WalletPassphraseIncorrect = btcjson.Error{
		Code:    -14,
		Message: "The wallet passphrase entered was incorrect",
	}
	WalletWrongEncState = btcjson.Error{
		Code:    -15,
		Message: "Command given in wrong wallet encryption state",
	}
	WalletEncryptionFailed = btcjson.Error{
		Code:    -16,
		Message: "Failed to encrypt the wallet",
	}
	WalletAlreadyUnlocked = btcjson.Error{
		Code:    -17,
		Message: "Wallet is already unlocked",
	}
)

var (
	// seq holds the btcwallet sequence number for frontend messages
	// which must be sent to and received from btcd.  A Mutex protects
	// against concurrent access.
	seq = struct {
		sync.Mutex
		n uint64
	}{}

	// replyRouter maps uint64 ids to reply channels, so btcd replies can
	// be routed to the correct frontend.
	replyRouter = struct {
		sync.Mutex
		m map[uint64]chan []byte
	}{
		m: make(map[uint64]chan []byte),
	}
)

// ProcessFrontendMsg checks the message sent from a frontend.  If the
// message method is one that must be handled by btcwallet, the request
// is processed here.  Otherwise, the message is sent to btcd.
func ProcessFrontendMsg(reply chan []byte, msg []byte) {
	cmd, err := btcjson.JSONGetMethod(msg)
	if err != nil {
		log.Error("Unable to parse JSON method from message.")
		return
	}

	switch cmd {
	case "getaddressesbyaccount":
		GetAddressesByAccount(reply, msg)
	case "getnewaddress":
		GetNewAddress(reply, msg)
	case "walletlock":
		WalletLock(reply, msg)
	case "walletpassphrase":
		WalletPassphrase(reply, msg)
	default:
		// btcwallet does not understand method.  Pass to btcd.
		log.Info("Unknown btcwallet method", cmd)

		seq.Lock()
		n := seq.n
		seq.n++
		seq.Unlock()

		var m map[string]interface{}
		json.Unmarshal(msg, &m)
		m["id"] = fmt.Sprintf("btcwallet(%v)-%v", n, m["id"])
		newMsg, err := json.Marshal(m)
		if err != nil {
			log.Info("Error marshalling json: " + err.Error())
		}
		replyRouter.Lock()
		replyRouter.m[n] = reply
		replyRouter.Unlock()
		btcdMsgs <- newMsg
	}
}

// ReplyError creates and marshalls a btcjson.Reply with the error e,
// sending the reply to a reply channel.
func ReplyError(reply chan []byte, id interface{}, e *btcjson.Error) {
	r := btcjson.Reply{
		Error: e,
		Id:    &id,
	}
	if mr, err := json.Marshal(r); err != nil {
		reply <- mr
	}
}

// ReplySuccess creates and marshalls a btcjson.Reply with the result r,
// sending the reply to a reply channel.
func ReplySuccess(reply chan []byte, id interface{}, result interface{}) {
	r := btcjson.Reply{
		Result: result,
		Id: &id,
	}
	if mr, err := json.Marshal(r); err != nil {
		reply <- mr
	}
}

// GetAddressesByAccount Gets all addresses for an account.
func GetAddressesByAccount(reply chan []byte, msg []byte) {
	var v map[string]interface{}
	json.Unmarshal(msg, &v)
	params := v["params"].([]interface{})
	id := v["id"]
	r := btcjson.Reply{
		Id: &id,
	}
	if w := wallets[params[0].(string)]; w != nil {
		r.Result = w.GetActiveAddresses()
	} else {
		r.Result = []interface{}{}
	}
	mr, err := json.Marshal(r)
	if err != nil {
		log.Info("Error marshalling reply: %v", err)
		return
	}
	reply <- mr
}

// GetNewAddress gets or generates a new address for an account.
//
// TODO(jrick): support non-default account wallets.
func GetNewAddress(reply chan []byte, msg []byte) {
	var v map[string]interface{}
	json.Unmarshal(msg, &v)
	params := v["params"].([]interface{})
	if len(params) == 0 || params[0].(string) == "" {
		if w := wallets[""]; w != nil {
			addr := w.NextUnusedAddress()
			id := v["id"]
			r := btcjson.Reply{
				Result: addr,
				Id:     &id,
			}
			mr, err := json.Marshal(r)
			if err != nil {
				log.Info("Error marshalling reply: %v", err)
				return
			}
			reply <- mr
		}
	}
}

// WalletLock locks the wallet.
//
// TODO(jrick): figure out how multiple wallets/accounts will work
// with this.
func WalletLock(reply chan []byte, msg []byte) {
	var v map[string]interface{}
	json.Unmarshal(msg, &v)
	if w := wallets[""]; w != nil {
		if err := w.Lock(); err != nil {
			ReplyError(reply, v["id"], &WalletWrongEncState)
		} else {
			ReplySuccess(reply, v["id"], nil)
		}
	}
}

// WalletPassphrase stores the decryption key for the default account,
// unlocking the wallet.
//
// TODO(jrick): figure out how multiple wallets/accounts will work
// with this.
func WalletPassphrase(reply chan []byte, msg []byte) {
	var v map[string]interface{}
	json.Unmarshal(msg, &v)
	params := v["params"].([]interface{})
	if len(params) != 2 {
		ReplyError(reply, v["id"], &InvalidParams)
		return
	}
	passphrase, ok1 := params[0].(string)
	timeout, ok2 := params[1].(float64)
	if !ok1 || !ok2 {
		ReplyError(reply, v["id"], &InvalidParams)
		return
	}

	if w := wallets[""]; w != nil {
		if err := w.Unlock([]byte(passphrase)); err != nil {
			ReplyError(reply, v["id"], &WalletPassphraseIncorrect)
			return
		}
		go func() {
			time.Sleep(time.Second * time.Duration(int64(timeout)))
			w.Lock()
		}()
	}
}
<commit_msg>Send JSON reply after successfully decrypting wallet<commit_after>/*
 * Copyright (c) 2013 Conformal Systems LLC <info@conformal.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package main

import (
	"encoding/json"
	"fmt"
	"github.com/conformal/btcjson"
	"sync"
	"time"
)

// Errors
var (
	// Standard JSON-RPC 2.0 errors
	InvalidRequest = btcjson.Error{
		Code:    -32600,
		Message: "Invalid request",
	}
	MethodNotFound = btcjson.Error{
		Code:    -32601,
		Message: "Method not found",
	}
	InvalidParams = btcjson.Error{
		Code:    -32602,
		Message: "Invalid paramaters",
	}
	InternalError = btcjson.Error{
		Code:    -32603,
		Message: "Internal error",
	}
	ParseError = btcjson.Error{
		Code:    -32700,
		Message: "Parse error",
	}

	// General application defined errors
	MiscError = btcjson.Error{
		Code:    -1,
		Message: "Miscellaneous error",
	}
	ForbiddenBySafeMode = btcjson.Error{
		Code:    -2,
		Message: "Server is in safe mode, and command is not allowed in safe mode",
	}
	TypeError = btcjson.Error{
		Code:    -3,
		Message: "Unexpected type was passed as parameter",
	}
	InvalidAddressOrKey = btcjson.Error{
		Code:    -5,
		Message: "Invalid address or key",
	}
	OutOfMemory = btcjson.Error{
		Code:    -7,
		Message: "Ran out of memory during operation",
	}
	InvalidParameter = btcjson.Error{
		Code:    -8,
		Message: "Invalid, missing or duplicate parameter",
	}
	DatabaseError = btcjson.Error{
		Code:    -20,
		Message: "Database error",
	}
	DeserializationError = btcjson.Error{
		Code:    -22,
		Message: "Error parsing or validating structure in raw format",
	}

	// Wallet errors
	WalletError = btcjson.Error{
		Code:    -4,
		Message: "Unspecified problem with wallet",
	}
	WalletInsufficientFunds = btcjson.Error{
		Code:    -6,
		Message: "Not enough funds in wallet or account",
	}
	WalletInvalidAccountName = btcjson.Error{
		Code:    -11,
		Message: "Invalid account name",
	}
	WalletKeypoolRanOut = btcjson.Error{
		Code:    -12,
		Message: "Keypool ran out, call keypoolrefill first",
	}
	WalletUnlockNeeded = btcjson.Error{
		Code:    -13,
		Message: "Enter the wallet passphrase with walletpassphrase first",
	}
	WalletPassphraseIncorrect = btcjson.Error{
		Code:    -14,
		Message: "The wallet passphrase entered was incorrect",
	}
	WalletWrongEncState = btcjson.Error{
		Code:    -15,
		Message: "Command given in wrong wallet encryption state",
	}
	WalletEncryptionFailed = btcjson.Error{
		Code:    -16,
		Message: "Failed to encrypt the wallet",
	}
	WalletAlreadyUnlocked = btcjson.Error{
		Code:    -17,
		Message: "Wallet is already unlocked",
	}
)

var (
	// seq holds the btcwallet sequence number for frontend messages
	// which must be sent to and received from btcd.  A Mutex protects
	// against concurrent access.
	seq = struct {
		sync.Mutex
		n uint64
	}{}

	// replyRouter maps uint64 ids to reply channels, so btcd replies can
	// be routed to the correct frontend.
	replyRouter = struct {
		sync.Mutex
		m map[uint64]chan []byte
	}{
		m: make(map[uint64]chan []byte),
	}
)

// ProcessFrontendMsg checks the message sent from a frontend.  If the
// message method is one that must be handled by btcwallet, the request
// is processed here.  Otherwise, the message is sent to btcd.
func ProcessFrontendMsg(reply chan []byte, msg []byte) {
	cmd, err := btcjson.JSONGetMethod(msg)
	if err != nil {
		log.Error("Unable to parse JSON method from message.")
		return
	}

	switch cmd {
	case "getaddressesbyaccount":
		GetAddressesByAccount(reply, msg)
	case "getnewaddress":
		GetNewAddress(reply, msg)
	case "walletlock":
		WalletLock(reply, msg)
	case "walletpassphrase":
		WalletPassphrase(reply, msg)
	default:
		// btcwallet does not understand method.  Pass to btcd.
		log.Info("Unknown btcwallet method", cmd)

		seq.Lock()
		n := seq.n
		seq.n++
		seq.Unlock()

		var m map[string]interface{}
		json.Unmarshal(msg, &m)
		m["id"] = fmt.Sprintf("btcwallet(%v)-%v", n, m["id"])
		newMsg, err := json.Marshal(m)
		if err != nil {
			log.Info("Error marshalling json: " + err.Error())
		}
		replyRouter.Lock()
		replyRouter.m[n] = reply
		replyRouter.Unlock()
		btcdMsgs <- newMsg
	}
}

// ReplyError creates and marshalls a btcjson.Reply with the error e,
// sending the reply to a reply channel.
func ReplyError(reply chan []byte, id interface{}, e *btcjson.Error) {
	r := btcjson.Reply{
		Error: e,
		Id:    &id,
	}
	if mr, err := json.Marshal(r); err != nil {
		reply <- mr
	}
}

// ReplySuccess creates and marshalls a btcjson.Reply with the result r,
// sending the reply to a reply channel.
func ReplySuccess(reply chan []byte, id interface{}, result interface{}) {
	r := btcjson.Reply{
		Result: result,
		Id: &id,
	}
	if mr, err := json.Marshal(r); err != nil {
		reply <- mr
	}
}

// GetAddressesByAccount Gets all addresses for an account.
func GetAddressesByAccount(reply chan []byte, msg []byte) {
	var v map[string]interface{}
	json.Unmarshal(msg, &v)
	params := v["params"].([]interface{})
	id := v["id"]
	r := btcjson.Reply{
		Id: &id,
	}
	if w := wallets[params[0].(string)]; w != nil {
		r.Result = w.GetActiveAddresses()
	} else {
		r.Result = []interface{}{}
	}
	mr, err := json.Marshal(r)
	if err != nil {
		log.Info("Error marshalling reply: %v", err)
		return
	}
	reply <- mr
}

// GetNewAddress gets or generates a new address for an account.
//
// TODO(jrick): support non-default account wallets.
func GetNewAddress(reply chan []byte, msg []byte) {
	var v map[string]interface{}
	json.Unmarshal(msg, &v)
	params := v["params"].([]interface{})
	if len(params) == 0 || params[0].(string) == "" {
		if w := wallets[""]; w != nil {
			addr := w.NextUnusedAddress()
			id := v["id"]
			r := btcjson.Reply{
				Result: addr,
				Id:     &id,
			}
			mr, err := json.Marshal(r)
			if err != nil {
				log.Info("Error marshalling reply: %v", err)
				return
			}
			reply <- mr
		}
	}
}

// WalletLock locks the wallet.
//
// TODO(jrick): figure out how multiple wallets/accounts will work
// with this.
func WalletLock(reply chan []byte, msg []byte) {
	var v map[string]interface{}
	json.Unmarshal(msg, &v)
	if w := wallets[""]; w != nil {
		if err := w.Lock(); err != nil {
			ReplyError(reply, v["id"], &WalletWrongEncState)
		} else {
			ReplySuccess(reply, v["id"], nil)
		}
	}
}

// WalletPassphrase stores the decryption key for the default account,
// unlocking the wallet.
//
// TODO(jrick): figure out how multiple wallets/accounts will work
// with this.
func WalletPassphrase(reply chan []byte, msg []byte) {
	var v map[string]interface{}
	json.Unmarshal(msg, &v)
	params := v["params"].([]interface{})
	if len(params) != 2 {
		ReplyError(reply, v["id"], &InvalidParams)
		return
	}
	passphrase, ok1 := params[0].(string)
	timeout, ok2 := params[1].(float64)
	if !ok1 || !ok2 {
		ReplyError(reply, v["id"], &InvalidParams)
		return
	}

	if w := wallets[""]; w != nil {
		if err := w.Unlock([]byte(passphrase)); err != nil {
			ReplyError(reply, v["id"], &WalletPassphraseIncorrect)
			return
		}
		ReplySuccess(reply, v["id"], nil)
		go func() {
			time.Sleep(time.Second * time.Duration(int64(timeout)))
			w.Lock()
		}()
	}
}
<|endoftext|>


<commit_before>package bloomskyStructure

import (
	"encoding/json"
	"fmt"
	"time"

	config "github.com/patrickalin/GoBloomsky/config"

	mylog "github.com/patrickalin/GoMyLog"
	rest "github.com/patrickalin/GoRest"
)

// generate by http://mervine.net/json2struct

// BloomskyStructure represent the structure of the JSON return by the API
type BloomskyStructure struct {
	UTC              float64                `json:"UTC"`
	CityName         string                 `json:"CityName"`
	Storm            BloomskyStormStructure `json:"Storm"`
	Searchable       bool                   `json:"Searchable"`
	DeviceName       string                 `json:"DeviceName"`
	RegisterTime     float64                `json:"RegisterTime"`
	DST              float64                `json:"DST"`
	BoundedPoint     string                 `json:"BoundedPoint"`
	LON              float64                `json:"LON"`
	Point            interface{}            `json:"Point"`
	VideoList        []string               `json:"VideoList"`
	VideoList_C      []string               `json:"VideoList_C"`
	DeviceID         string                 `json:"DeviceID"`
	NumOfFollowers   float64                `json:"NumOfFollowers"`
	LAT              float64                `json:"LAT"`
	ALT              float64                `json:"ALT"`
	Data             BloomskyDataStructure  `json:"Data"`
	FullAddress      string                 `json:"FullAddress"`
	StreetName       string                 `json:"StreetName"`
	PreviewImageList []string               `json:"PreviewImageList"`
}

// BloomskyStructure represent the structure STORM of the JSON return by the API
type BloomskyStormStructure struct {
	UVIndex            string  `json:"UVIndex"`
	WindDirection      string  `json:"WindDirection"`
	RainDaily          float64 `json:"RainDaily"`
	WindGust           float64 `json:"WindGust"`
	SustainedWindSpeed float64 `json:"SustainedWindSpeed"`
	RainRate           float64 `json:"RainRate"`
	Rain               float64 `json:"24hRain"`
}

// BloomskyStructure represent the structure SKY of the JSON return by the API
type BloomskyDataStructure struct {
	Luminance   float64 `json:"Luminance"`
	Temperature float64 `json:"Temperature"`
	ImageURL    string  `json:"ImageURL"`
	TS          float64 `json:"TS"`
	Rain        bool    `json:"Rain"`
	Humidity    float64 `json:"Humidity"`
	Pressure    float64 `json:"Pressure"`
	DeviceType  string  `json:"DeviceType"`
	Voltage     float64 `json:"Voltage"`
	Night       bool    `json:"Night"`
	UVIndex     float64 `json:"UVIndex"`
	ImageTS     float64 `json:"ImageTS"`
}

// bloomskyStructure is the Interface bloomskyStructure
type bloomskyStructure interface {
	GetDeviceID() string
	GetSoftwareVersion() string
	GetAmbientTemperatureC() float64
	GetTargetTemperatureC() float64
	GetAmbientTemperatureF() float64
	GetTargetTemperatureF() float64
	GetHumidity() float64
	GetAway() string
	ShowPrettyAll() int
}

type bloomskyError struct {
	message error
	advice  string
}

func (e *bloomskyError) Error() string {
	return fmt.Sprintf("\n \t bloomskyError :> %s \n\t Advice :> %s", e.message, e.advice)
}

// ShowPrettyAll prints to the console the JSON
func (bloomskyInfo BloomskyStructure) ShowPrettyAll() int {
	out, err := json.Marshal(bloomskyInfo)
	if err != nil {
		fmt.Println("Error with parsing Json")
		mylog.Error.Fatal(err)
	}
	mylog.Trace.Printf("Decode:> \n %s \n\n", out)
	return 0
}

//GetTimeStamp returns the timestamp give by Bloomsky
func (bloomskyInfo BloomskyStructure) GetTimeStamp() time.Time {
	tm := time.Unix(int64(bloomskyInfo.Data.TS), 0)
	return tm
}

//GetCity returns the city name
func (bloomskyInfo BloomskyStructure) GetCity() string {
	return bloomskyInfo.CityName
}

//GetDevideId returns the Device Id
func (bloomskyInfo BloomskyStructure) GetDeviceID() string {
	return bloomskyInfo.DeviceID
}

//GetNumOfFollowers returns the number of followers
func (bloomskyInfo BloomskyStructure) GetNumOfFollowers() int {
	return int(bloomskyInfo.NumOfFollowers)
}

//GetIndexUV returns the UV index from 1 to 11
func (bloomskyInfo BloomskyStructure) GetIndexUV() string {
	return bloomskyInfo.Storm.UVIndex
}

//IsNight returns true if it's the night
func (bloomskyInfo BloomskyStructure) IsNight() bool {
	return bloomskyInfo.Data.Night
}

//GetTemperatureFahrenheit returns temperature in Fahrenheit
func (bloomskyInfo BloomskyStructure) GetTemperatureFahrenheit() float64 {
	return bloomskyInfo.Data.Temperature
}

func (bloomskyInfo BloomskyStructure) GetTemperatureCelcius() float64 {
	return ((bloomskyInfo.Data.Temperature - 32.00) * 5.00 / 9.00)
}

func (bloomskyInfo BloomskyStructure) GetHumidity() float64 {
	return bloomskyInfo.Data.Humidity
}

func (bloomskyInfo BloomskyStructure) GetPressureHPa() float64 {
	return (bloomskyInfo.Data.Pressure * 33.8638815)
}

func (bloomskyInfo BloomskyStructure) GetPressureInHg() float64 {
	return bloomskyInfo.Data.Pressure
}

func (bloomskyInfo BloomskyStructure) GetWindDirection() string {
	return bloomskyInfo.Storm.WindDirection
}

func (bloomskyInfo BloomskyStructure) GetWindGustMph() float64 {
	return bloomskyInfo.Storm.WindGust
}

func (bloomskyInfo BloomskyStructure) GetWindGustMs() float64 {
	return (bloomskyInfo.Storm.WindGust * 1.61)
}

func (bloomskyInfo BloomskyStructure) GetSustainedWindSpeedMph() float64 {
	return bloomskyInfo.Storm.SustainedWindSpeed
}

func (bloomskyInfo BloomskyStructure) GetSustainedWindSpeedMs() float64 {
	return (bloomskyInfo.Storm.SustainedWindSpeed * 1.61)
}

func (bloomskyInfo BloomskyStructure) IsRain() bool {
	return bloomskyInfo.Data.Rain
}

func (bloomskyInfo BloomskyStructure) GetRainDailyIn() float64 {
	return bloomskyInfo.Storm.RainDaily
}

func (bloomskyInfo BloomskyStructure) GetRainIn() float64 {
	return bloomskyInfo.Storm.Rain
}

func (bloomskyInfo BloomskyStructure) GetRainRateIn() float64 {
	return bloomskyInfo.Storm.RainRate
}

func (bloomskyInfo BloomskyStructure) GetRainDailyMm() float64 {
	return bloomskyInfo.Storm.RainDaily
}

func (bloomskyInfo BloomskyStructure) GetRainMm() float64 {
	return bloomskyInfo.Storm.Rain
}

func (bloomskyInfo BloomskyStructure) GetRainRateMm() float64 {
	return bloomskyInfo.Storm.RainRate
}

/*
	DeviceType  string  `json:"DeviceType"`
	Voltage     float64 `json:"Voltage"`
*/

// MakeNew calls bloomsky and get structurebloomsky
func MakeNew(oneConfig config.ConfigStructure) BloomskyStructure {

	var retry = 0
	var err error
	var duration = time.Minute * 5

	// get body from Rest API
	mylog.Trace.Printf("Get from Rest bloomsky API")
	myRest := rest.MakeNew()

	b := []string{oneConfig.BloomskyAccessToken}

	var m map[string][]string
	m = make(map[string][]string)
	m["Authorization"] = b

	for retry < 5 {
		err = myRest.GetWithHeaders(oneConfig.BloomskyURL, m)
		if err != nil {
			mylog.Error.Println(&bloomskyError{err, "Problem with call rest, check the URL and the secret ID in the config file"})
			retry++
			time.Sleep(duration)
		} else {
			retry = 5
		}
	}

	if err != nil {
		mylog.Error.Fatal(&bloomskyError{err, "Problem with call rest, check the URL and the secret ID in the config file"})
	}

	var bloomskyInfo []BloomskyStructure

	body := myRest.GetBody()

	mylog.Trace.Printf("Unmarshal the response")
	err = json.Unmarshal(body, &bloomskyInfo)

	if err != nil {
		mylog.Error.Fatal(&bloomskyError{err, "Problem with json to struct, problem in the struct ?"})
	}

	bloomskyInfo[0].ShowPrettyAll()

	return bloomskyInfo[0]
}
<commit_msg>Add some comments<commit_after>package bloomskyStructure

import (
	"encoding/json"
	"fmt"
	"time"

	config "github.com/patrickalin/GoBloomsky/config"

	mylog "github.com/patrickalin/GoMyLog"
	rest "github.com/patrickalin/GoRest"
)

// generate by http://mervine.net/json2struct

// BloomskyStructure represent the structure of the JSON return by the API
type BloomskyStructure struct {
	UTC              float64                `json:"UTC"`
	CityName         string                 `json:"CityName"`
	Storm            BloomskyStormStructure `json:"Storm"`
	Searchable       bool                   `json:"Searchable"`
	DeviceName       string                 `json:"DeviceName"`
	RegisterTime     float64                `json:"RegisterTime"`
	DST              float64                `json:"DST"`
	BoundedPoint     string                 `json:"BoundedPoint"`
	LON              float64                `json:"LON"`
	Point            interface{}            `json:"Point"`
	VideoList        []string               `json:"VideoList"`
	VideoListC       []string               `json:"VideoList_C"`
	DeviceID         string                 `json:"DeviceID"`
	NumOfFollowers   float64                `json:"NumOfFollowers"`
	LAT              float64                `json:"LAT"`
	ALT              float64                `json:"ALT"`
	Data             BloomskyDataStructure  `json:"Data"`
	FullAddress      string                 `json:"FullAddress"`
	StreetName       string                 `json:"StreetName"`
	PreviewImageList []string               `json:"PreviewImageList"`
}

// BloomskyStormStructure represent the structure STORM of the JSON return by the API
type BloomskyStormStructure struct {
	UVIndex            string  `json:"UVIndex"`
	WindDirection      string  `json:"WindDirection"`
	RainDaily          float64 `json:"RainDaily"`
	WindGust           float64 `json:"WindGust"`
	SustainedWindSpeed float64 `json:"SustainedWindSpeed"`
	RainRate           float64 `json:"RainRate"`
	Rain               float64 `json:"24hRain"`
}

// BloomskyDataStructure represent the structure SKY of the JSON return by the API
type BloomskyDataStructure struct {
	Luminance   float64 `json:"Luminance"`
	Temperature float64 `json:"Temperature"`
	ImageURL    string  `json:"ImageURL"`
	TS          float64 `json:"TS"`
	Rain        bool    `json:"Rain"`
	Humidity    float64 `json:"Humidity"`
	Pressure    float64 `json:"Pressure"`
	DeviceType  string  `json:"DeviceType"`
	Voltage     float64 `json:"Voltage"`
	Night       bool    `json:"Night"`
	UVIndex     float64 `json:"UVIndex"`
	ImageTS     float64 `json:"ImageTS"`
}

// bloomskyStructure is the interface bloomskyStructure
type bloomskyStructure interface {
	GetDeviceID() string
	GetSoftwareVersion() string
	GetAmbientTemperatureC() float64
	GetTargetTemperatureC() float64
	GetAmbientTemperatureF() float64
	GetTargetTemperatureF() float64
	GetHumidity() float64
	GetAway() string
	ShowPrettyAll() int
}

type bloomskyError struct {
	message error
	advice  string
}

func (e *bloomskyError) Error() string {
	return fmt.Sprintf("\n \t bloomskyError :> %s \n\t Advice :> %s", e.message, e.advice)
}

// ShowPrettyAll prints to the console the JSON
func (bloomskyInfo BloomskyStructure) ShowPrettyAll() int {
	out, err := json.Marshal(bloomskyInfo)
	if err != nil {
		fmt.Println("Error with parsing Json")
		mylog.Error.Fatal(err)
	}
	mylog.Trace.Printf("Decode:> \n %s \n\n", out)
	return 0
}

//GetTimeStamp returns the timestamp give by Bloomsky
func (bloomskyInfo BloomskyStructure) GetTimeStamp() time.Time {
	tm := time.Unix(int64(bloomskyInfo.Data.TS), 0)
	return tm
}

//GetCity returns the city name
func (bloomskyInfo BloomskyStructure) GetCity() string {
	return bloomskyInfo.CityName
}

//GetDevideId returns the Device Id
func (bloomskyInfo BloomskyStructure) GetDeviceID() string {
	return bloomskyInfo.DeviceID
}

//GetNumOfFollowers returns the number of followers
func (bloomskyInfo BloomskyStructure) GetNumOfFollowers() int {
	return int(bloomskyInfo.NumOfFollowers)
}

//GetIndexUV returns the UV index from 1 to 11
func (bloomskyInfo BloomskyStructure) GetIndexUV() string {
	return bloomskyInfo.Storm.UVIndex
}

//IsNight returns true if it's the night
func (bloomskyInfo BloomskyStructure) IsNight() bool {
	return bloomskyInfo.Data.Night
}

//GetTemperatureFahrenheit returns temperature in Fahrenheit
func (bloomskyInfo BloomskyStructure) GetTemperatureFahrenheit() float64 {
	return bloomskyInfo.Data.Temperature
}

//GetTemperatureCelcius returns temperature in Celcius
func (bloomskyInfo BloomskyStructure) GetTemperatureCelcius() float64 {
	return ((bloomskyInfo.Data.Temperature - 32.00) * 5.00 / 9.00)
}

//GetHumidity returns hulidity %
func (bloomskyInfo BloomskyStructure) GetHumidity() float64 {
	return bloomskyInfo.Data.Humidity
}

//GetPressureHPa returns pressure in HPa
func (bloomskyInfo BloomskyStructure) GetPressureHPa() float64 {
	return (bloomskyInfo.Data.Pressure * 33.8638815)
}

//GetPressureInHg returns pressure in InHg
func (bloomskyInfo BloomskyStructure) GetPressureInHg() float64 {
	return bloomskyInfo.Data.Pressure
}

//GetWindDirection returns wind direction (N,S,W,E, ...)
func (bloomskyInfo BloomskyStructure) GetWindDirection() string {
	return bloomskyInfo.Storm.WindDirection
}

//GetWindGustMph returns Wind in Mph
func (bloomskyInfo BloomskyStructure) GetWindGustMph() float64 {
	return bloomskyInfo.Storm.WindGust
}

//GetWindGustMs returns Wind in Ms
func (bloomskyInfo BloomskyStructure) GetWindGustMs() float64 {
	return (bloomskyInfo.Storm.WindGust * 1.61)
}

//GetSustainedWindSpeedMph returns Sustained Wind Speed in Mph
func (bloomskyInfo BloomskyStructure) GetSustainedWindSpeedMph() float64 {
	return bloomskyInfo.Storm.SustainedWindSpeed
}

//GetSustainedWindSpeedMs returns Sustained Wind Speed in Ms
func (bloomskyInfo BloomskyStructure) GetSustainedWindSpeedMs() float64 {
	return (bloomskyInfo.Storm.SustainedWindSpeed * 1.61)
}

//IsRain returns true if it's rain
func (bloomskyInfo BloomskyStructure) IsRain() bool {
	return bloomskyInfo.Data.Rain
}

//GetRainDailyIn returns rain daily in In
func (bloomskyInfo BloomskyStructure) GetRainDailyIn() float64 {
	return bloomskyInfo.Storm.RainDaily
}

//GetRainIn returns total rain in In
func (bloomskyInfo BloomskyStructure) GetRainIn() float64 {
	return bloomskyInfo.Storm.Rain
}

//GetRainRateIn returns rain in In
func (bloomskyInfo BloomskyStructure) GetRainRateIn() float64 {
	return bloomskyInfo.Storm.RainRate
}

//GetRainDailyMm returns rain daily in mm
func (bloomskyInfo BloomskyStructure) GetRainDailyMm() float64 {
	return bloomskyInfo.Storm.RainDaily
}

//GetRainMm returns total rain in mm
func (bloomskyInfo BloomskyStructure) GetRainMm() float64 {
	return bloomskyInfo.Storm.Rain
}

//GetRainRateMm returns rain in mm
func (bloomskyInfo BloomskyStructure) GetRainRateMm() float64 {
	return bloomskyInfo.Storm.RainRate
}

/*
	DeviceType  string  `json:"DeviceType"`
	Voltage     float64 `json:"Voltage"`
*/

// MakeNew calls bloomsky and get structurebloomsky
func MakeNew(oneConfig config.ConfigStructure) BloomskyStructure {

	var retry = 0
	var err error
	var duration = time.Minute * 5

	// get body from Rest API
	mylog.Trace.Printf("Get from Rest bloomsky API")
	myRest := rest.MakeNew()

	b := []string{oneConfig.BloomskyAccessToken}

	var m map[string][]string
	m = make(map[string][]string)
	m["Authorization"] = b

	for retry < 5 {
		err = myRest.GetWithHeaders(oneConfig.BloomskyURL, m)
		if err != nil {
			mylog.Error.Println(&bloomskyError{err, "Problem with call rest, check the URL and the secret ID in the config file"})
			retry++
			time.Sleep(duration)
		} else {
			retry = 5
		}
	}

	if err != nil {
		mylog.Error.Fatal(&bloomskyError{err, "Problem with call rest, check the URL and the secret ID in the config file"})
	}

	var bloomskyInfo []BloomskyStructure

	body := myRest.GetBody()

	mylog.Trace.Printf("Unmarshal the response")
	err = json.Unmarshal(body, &bloomskyInfo)

	if err != nil {
		mylog.Error.Fatal(&bloomskyError{err, "Problem with json to struct, problem in the struct ?"})
	}

	bloomskyInfo[0].ShowPrettyAll()

	return bloomskyInfo[0]
}
<|endoftext|>