<commit_before>package GoMM

import (
	"errors"
	"github.com/hashicorp/memberlist"
	"log"
	"sort"
	"strconv"
	"sync"
	"time"
)

type Client struct {
	memberTracker *memberlist.Memberlist // Underlying tracker to

	pendingMembersLock sync.Mutex
	pendingMembers     map[string]Node // Members that are online, but not active
	ActiveMembersLock  sync.Mutex
	ActiveMembers      map[string]Node // Members that are online and active, mapped by the memberlist.Node.Name
	Name               string          // Unique name of the Client
	node               Node            // Used for TCP communications

	messenger Messenger
	listener  Listener

	barrierChannel chan string // The channel that handles barrier message, will be the name of the node that sent the barrier
	// Channel for recieve broadcast messages
	BroadcastChannel chan Message
}

func (c Client) NumMembers() int {
	return c.memberTracker.NumMembers()
}

func (c *Client) NumActiveMembers() int {
	c.ActiveMembersLock.Lock()
	num := len(c.ActiveMembers)
	c.ActiveMembersLock.Unlock()
	return num
}

// Cause a node to join another memberlist group. This function removes this
// node from the active list. Further more, this should only be called
// when a node is alone in it's undelying memberlist. Therefore, a group
// of nodes cannot merge with another group, but the sub group must all join
// individually. Should this be blocking until the node is made active?
func (c *Client) Join(address string) {
	c.memberTracker.Join([]string{address})
	c.updateActiveMemberList([]Node{})
	return
}

func (c *Client) JoinAddr() string {
	return c.node.GetMemberlistStringAddr()
}

func (c Client) HandleMessage(msg Message) {
	if msg.Type == activateMsg || msg.Type == broadcastMsg || msg.Type == barrierMsg {
		c.continueMessageBroadcast(msg) // continues to broadcast the message
	}

	switch msg.Type {
	case activateMsg:
		c.handleActivateMessage(msg)
		break
	case barrierMsg:
		c.barrierChannel <- msg.StringData[0] // Pass on the name, will be handled on the calling thread
		break
	case broadcastMsg:
		c.BroadcastChannel <- msg
		break
	default:
		log.Println("[ERROR] Unknown message type")
	}
}

func (c *Client) handleActivateMessage(msg Message) {

	activeNodes, err := decodeActivateMsg(msg)
	if err != nil {
		log.Println("[ERROR] Received malformed activate message")
		return
	}
	c.updateActiveMemberList(activeNodes)
	log.Println("[DEBUG]", c.Name, "IsActive", c.IsActive(), "total active nodes: "+strconv.Itoa(len(activeNodes)))
}

// Handles messages that must be broadcast
func (c *Client) continueMessageBroadcast(msg Message) {
	// Send to children nodes if they exit
	id := c.GetId()
	c.ActiveMembersLock.Lock()
	totalNodes := len(c.ActiveMembers)
	c.ActiveMembersLock.Unlock()
	left, right := c.getChildren(id, totalNodes)

	// Send the message to the children nodes if they exist
	if left != -1 {
		c.sendMsg(msg, left)
	}

	if right != -1 {
		c.sendMsg(msg, right)
	}

}

// Returns the children in a binary tree for the nodes. Returns
// -1 for invalid nodes
func (c *Client) getChildren(id, totalNodes int) (int, int) {
	if totalNodes == 0 {
		// invalid tree
		return -1, -1
	}

	left := 2*id + 1
	right := 2 * (id + 1)

	if left >= totalNodes {
		left = -1
	}

	if right >= totalNodes {
		right = -1
	}
	return left, right
}

func (c *Client) Start() error {
	// Start event processing
	c.listener = NewListener(c)
	go c.listener.Listen(c.messenger)

	var config *memberlist.Config = memberlist.DefaultLocalConfig()
	config.BindPort = c.node.MemberlistPort
	config.BindAddr = c.node.Addr.String()
	config.Name = c.Name
	config.AdvertisePort = c.node.MemberlistPort
	config.Events = c

	list, err := memberlist.Create(config)
	if err != nil {
		log.Println("[ERROR] Failed to create member list for", c.Name, "Error: ", err.Error())
	}
	log.Println("[DEBUG] Started memberlist for", c.Name)
	c.memberTracker = list

	log.Println("[DEBUG] Started Client", c.Name)

	return nil
}

func (c *Client) Close() {
	c.memberTracker.Leave(time.Millisecond * 1)
	log.Println("[DEBUG]", c.Name, "left memberTracker")
	c.listener.Stop()
	// Not totally sure how closing channels works TODO
	close(c.barrierChannel)
	log.Println("[DEBUG]", c.Name, "shut down")
}

// Wait until the Client is active
func (c *Client) WaitActive() {
	for {
		if c.IsActive() == true {
			break
		}
		time.Sleep(time.Millisecond * 10)
	}
}

// Allows members currently waiting to become active to become active,
// this method blocks and requires that all current active members
// have also called this method.
func (c *Client) UpdateActiveMembers() int {
	// Need to ensure all active members have decided to do this
	c.Barrier()
	c.activatePendingMembers()
	// Need to send go ahead message to new members to be made active
	return c.NumActiveMembers()
}

func (c *Client) updateActiveMemberList(members []Node) {

	c.ActiveMembersLock.Lock()

	// Delete everything in the map, can't just make a new one, otherwise
	// the references can be broken across threads
	for k := range c.ActiveMembers {
		delete(c.ActiveMembers, k)
	}

	for i := range members {
		c.ActiveMembers[members[i].Name] = members[i]
	}

	log.Println("[DEBUG] Updateing active member list with:", c.ActiveMembers)

	c.ActiveMembersLock.Unlock()

}

// Determine if the given Client is in the active pool
func (c *Client) IsActive() bool {
	c.ActiveMembersLock.Lock()
	_, ok := c.ActiveMembers[c.Name]
	c.ActiveMembersLock.Unlock()
	return ok
}

// Barrier that blacks for all active nodes
func (c *Client) Barrier() {
	if !c.IsActive() {
		panic("Client is not active!")
	}
	if c.NumActiveMembers() == 1 {
		// This is the only member so can return instantly
		return
	}

	// Need to broadcast the barrier message
	msg := createBarrierMsg(c.Name)

	log.Println("[DEBUG] Broadcasting barrier from", c.Name)
	c.broadCastMsg(msg)

	// Wait for each node to respond
	//Get messages from channel

	responded := make(map[string]bool)
PollingLoop:
	for {
		select {
		case name := <-c.barrierChannel:
			responded[name] = true
			log.Println("[DEBUG]", c.Name, "Received barrier from", name, len(responded), "of", c.NumActiveMembers()-1)
		default:
			if len(responded) == c.NumActiveMembers()-1 {
				log.Println("[DEBUG] Barrier completed by", c.Name)
				break PollingLoop // everyone is at the barrier
			}
		}
	}
}

// Send message to all nodes
// TODO implement a tree rather than naive send to all
func (c *Client) Broadcast(stringData []string, floatData []float64) {
	msg := CreateBroadcastMsg(stringData, floatData)
	msg.Origin = c.GetId()
	c.broadCastMsg(msg)
}

// Sends a message to a node with the supplied id
func (c *Client) sendMsg(msg Message, targetId int) error {
	target, err := c.ResolveId(targetId)
	if err != nil {
		return err
	}
	msg.Target = target
	err = c.messenger.Send(msg)

	return err
}

// Resolve the id to a client address. The id is currently based on
// the sorted string order of the nodes address.
func (c *Client) ResolveId(id int) (string, error) {
	// Check valid id
	if id < 0 || id > len(c.ActiveMembers)-1 {
		return "", errors.New("Id out of bounds")
	}
	memberAddresses := c.getSortedMemberAddresses()

	return memberAddresses[id], nil
}

func (c *Client) GetAddrId(addr string) (int, error) {
	memberAddresses := c.getSortedMemberAddresses()

	for i := 0; i < len(memberAddresses); i++ {
		if addr == memberAddresses[i] {
			return i, nil
		}
	}

	return -1, errors.New("Address not found")
}

func (c *Client) GetId() int {
	id, _ := c.GetAddrId(c.node.GetStringAddr())
	return id
}

func (c *Client) getSortedMemberAddresses() []string {
	c.ActiveMembersLock.Lock()
	defer c.ActiveMembersLock.Unlock()

	// Generate a list of addresses
	memberAddresses := make([]string, len(c.ActiveMembers))
	i := 0
	for _, v := range c.ActiveMembers {
		memberAddresses[i] = v.GetStringAddr()
		i++
	}

	sort.Strings(memberAddresses)

	return memberAddresses
}

func (c *Client) broadCastMsg(msg Message) {
	// Send it to the root node for propogation
	c.sendMsg(msg, 0)
}

func (c Client) NotifyJoin(n *memberlist.Node) {
	new_node := Node{
		Name: n.Name,
		Addr: n.Addr,
		Port: int(n.Port) + 100, // Add 100 for the port offset
	}

	if n.Name == c.Name {
		// The initial self notification
		c.ActiveMembersLock.Lock()
		c.ActiveMembers[c.Name] = new_node
		c.ActiveMembersLock.Unlock()
		return
	}

	c.pendingMembersLock.Lock()
	c.pendingMembers[n.Name] = new_node
	c.pendingMembersLock.Unlock()
}

func (c Client) NotifyLeave(n *memberlist.Node) {
	log.Println("[DEBUG]", n.Name, "left")
	c.ActiveMembersLock.Lock()
	// Delete the node from active members
	delete(c.ActiveMembers, n.Name)
	c.ActiveMembersLock.Unlock()

	// Delete the node from pending members
	c.pendingMembersLock.Lock()
	delete(c.pendingMembers, n.Name)
	c.pendingMembersLock.Unlock()
}

func (c Client) NotifyUpdate(n *memberlist.Node) {

}
<commit_msg>Added shutdown call to client close to properly shutdown memberlist. Also removed closing of recv channel so the client can be started again.<commit_after>package GoMM

import (
	"errors"
	"github.com/hashicorp/memberlist"
	"log"
	"sort"
	"strconv"
	"sync"
	"time"
)

type Client struct {
	memberTracker *memberlist.Memberlist // Underlying tracker to

	pendingMembersLock sync.Mutex
	pendingMembers     map[string]Node // Members that are online, but not active
	ActiveMembersLock  sync.Mutex
	ActiveMembers      map[string]Node // Members that are online and active, mapped by the memberlist.Node.Name
	Name               string          // Unique name of the Client
	node               Node            // Used for TCP communications

	messenger Messenger
	listener  Listener

	barrierChannel chan string // The channel that handles barrier message, will be the name of the node that sent the barrier
	// Channel for recieve broadcast messages
	BroadcastChannel chan Message
}

func (c Client) NumMembers() int {
	return c.memberTracker.NumMembers()
}

func (c *Client) NumActiveMembers() int {
	c.ActiveMembersLock.Lock()
	num := len(c.ActiveMembers)
	c.ActiveMembersLock.Unlock()
	return num
}

// Cause a node to join another memberlist group. This function removes this
// node from the active list. Further more, this should only be called
// when a node is alone in it's undelying memberlist. Therefore, a group
// of nodes cannot merge with another group, but the sub group must all join
// individually. Should this be blocking until the node is made active?
func (c *Client) Join(address string) {
	c.memberTracker.Join([]string{address})
	c.updateActiveMemberList([]Node{})
	return
}

func (c *Client) JoinAddr() string {
	return c.node.GetMemberlistStringAddr()
}

func (c Client) HandleMessage(msg Message) {
	if msg.Type == activateMsg || msg.Type == broadcastMsg || msg.Type == barrierMsg {
		c.continueMessageBroadcast(msg) // continues to broadcast the message
	}

	switch msg.Type {
	case activateMsg:
		c.handleActivateMessage(msg)
		break
	case barrierMsg:
		c.barrierChannel <- msg.StringData[0] // Pass on the name, will be handled on the calling thread
		break
	case broadcastMsg:
		c.BroadcastChannel <- msg
		break
	default:
		log.Println("[ERROR] Unknown message type")
	}
}

func (c *Client) handleActivateMessage(msg Message) {

	activeNodes, err := decodeActivateMsg(msg)
	if err != nil {
		log.Println("[ERROR] Received malformed activate message")
		return
	}
	c.updateActiveMemberList(activeNodes)
	log.Println("[DEBUG]", c.Name, "IsActive", c.IsActive(), "total active nodes: "+strconv.Itoa(len(activeNodes)))
}

// Handles messages that must be broadcast
func (c *Client) continueMessageBroadcast(msg Message) {
	// Send to children nodes if they exit
	id := c.GetId()
	c.ActiveMembersLock.Lock()
	totalNodes := len(c.ActiveMembers)
	c.ActiveMembersLock.Unlock()
	left, right := c.getChildren(id, totalNodes)

	// Send the message to the children nodes if they exist
	if left != -1 {
		c.sendMsg(msg, left)
	}

	if right != -1 {
		c.sendMsg(msg, right)
	}

}

// Returns the children in a binary tree for the nodes. Returns
// -1 for invalid nodes
func (c *Client) getChildren(id, totalNodes int) (int, int) {
	if totalNodes == 0 {
		// invalid tree
		return -1, -1
	}

	left := 2*id + 1
	right := 2 * (id + 1)

	if left >= totalNodes {
		left = -1
	}

	if right >= totalNodes {
		right = -1
	}
	return left, right
}

func (c *Client) Start() error {
	// Start event processing
	c.listener = NewListener(c)
	go c.listener.Listen(c.messenger)

	var config *memberlist.Config = memberlist.DefaultLocalConfig()
	config.BindPort = c.node.MemberlistPort
	config.BindAddr = c.node.Addr.String()
	config.Name = c.Name
	config.AdvertisePort = c.node.MemberlistPort
	config.Events = c

	list, err := memberlist.Create(config)
	if err != nil {
		log.Println("[ERROR] Failed to create member list for", c.Name, "Error: ", err.Error())
	}
	log.Println("[DEBUG] Started memberlist for", c.Name)
	c.memberTracker = list

	log.Println("[DEBUG] Started Client", c.Name)

	return nil
}

func (c *Client) Close() {
	c.memberTracker.Leave(time.Millisecond * 500)
	c.memberTracker.Shutdown()
	log.Println("[DEBUG]", c.Name, "left memberTracker")
	c.listener.Stop()
	// Not totally sure how closing channels works TODO
	// close(c.barrierChannel)
	log.Println("[DEBUG]", c.Name, "shut down")
}

// Wait until the Client is active
func (c *Client) WaitActive() {
	for {
		if c.IsActive() == true {
			break
		}
		time.Sleep(time.Millisecond * 10)
	}
}

// Allows members currently waiting to become active to become active,
// this method blocks and requires that all current active members
// have also called this method.
func (c *Client) UpdateActiveMembers() int {
	// Need to ensure all active members have decided to do this
	c.Barrier()
	c.activatePendingMembers()
	// Need to send go ahead message to new members to be made active
	return c.NumActiveMembers()
}

func (c *Client) updateActiveMemberList(members []Node) {

	c.ActiveMembersLock.Lock()

	// Delete everything in the map, can't just make a new one, otherwise
	// the references can be broken across threads
	for k := range c.ActiveMembers {
		delete(c.ActiveMembers, k)
	}

	for i := range members {
		c.ActiveMembers[members[i].Name] = members[i]
	}

	log.Println("[DEBUG] Updateing active member list with:", c.ActiveMembers)

	c.ActiveMembersLock.Unlock()

}

// Determine if the given Client is in the active pool
func (c *Client) IsActive() bool {
	c.ActiveMembersLock.Lock()
	_, ok := c.ActiveMembers[c.Name]
	c.ActiveMembersLock.Unlock()
	return ok
}

// Barrier that blacks for all active nodes
func (c *Client) Barrier() {
	if !c.IsActive() {
		panic("Client is not active!")
	}
	if c.NumActiveMembers() == 1 {
		// This is the only member so can return instantly
		return
	}

	// Need to broadcast the barrier message
	msg := createBarrierMsg(c.Name)

	log.Println("[DEBUG] Broadcasting barrier from", c.Name)
	c.broadCastMsg(msg)

	// Wait for each node to respond
	//Get messages from channel

	responded := make(map[string]bool)
PollingLoop:
	for {
		select {
		case name := <-c.barrierChannel:
			responded[name] = true
			log.Println("[DEBUG]", c.Name, "Received barrier from", name, len(responded), "of", c.NumActiveMembers()-1)
		default:
			if len(responded) == c.NumActiveMembers()-1 {
				log.Println("[DEBUG] Barrier completed by", c.Name)
				break PollingLoop // everyone is at the barrier
			}
		}
	}
}

// Send message to all nodes
// TODO implement a tree rather than naive send to all
func (c *Client) Broadcast(stringData []string, floatData []float64) {
	msg := CreateBroadcastMsg(stringData, floatData)
	msg.Origin = c.GetId()
	c.broadCastMsg(msg)
}

// Sends a message to a node with the supplied id
func (c *Client) sendMsg(msg Message, targetId int) error {
	target, err := c.ResolveId(targetId)
	if err != nil {
		return err
	}
	msg.Target = target
	err = c.messenger.Send(msg)

	return err
}

// Resolve the id to a client address. The id is currently based on
// the sorted string order of the nodes address.
func (c *Client) ResolveId(id int) (string, error) {
	// Check valid id
	if id < 0 || id > len(c.ActiveMembers)-1 {
		return "", errors.New("Id out of bounds")
	}
	memberAddresses := c.getSortedMemberAddresses()

	return memberAddresses[id], nil
}

func (c *Client) GetAddrId(addr string) (int, error) {
	memberAddresses := c.getSortedMemberAddresses()

	for i := 0; i < len(memberAddresses); i++ {
		if addr == memberAddresses[i] {
			return i, nil
		}
	}

	return -1, errors.New("Address not found")
}

func (c *Client) GetId() int {
	id, _ := c.GetAddrId(c.node.GetStringAddr())
	return id
}

func (c *Client) getSortedMemberAddresses() []string {
	c.ActiveMembersLock.Lock()
	defer c.ActiveMembersLock.Unlock()

	// Generate a list of addresses
	memberAddresses := make([]string, len(c.ActiveMembers))
	i := 0
	for _, v := range c.ActiveMembers {
		memberAddresses[i] = v.GetStringAddr()
		i++
	}

	sort.Strings(memberAddresses)

	return memberAddresses
}

func (c *Client) broadCastMsg(msg Message) {
	// Send it to the root node for propogation
	c.sendMsg(msg, 0)
}

func (c Client) NotifyJoin(n *memberlist.Node) {
	new_node := Node{
		Name: n.Name,
		Addr: n.Addr,
		Port: int(n.Port) + 100, // Add 100 for the port offset
	}

	if n.Name == c.Name {
		// The initial self notification
		c.ActiveMembersLock.Lock()
		c.ActiveMembers[c.Name] = new_node
		c.ActiveMembersLock.Unlock()
		return
	}

	c.pendingMembersLock.Lock()
	c.pendingMembers[n.Name] = new_node
	c.pendingMembersLock.Unlock()
}

func (c Client) NotifyLeave(n *memberlist.Node) {
	log.Println("[DEBUG]", c.Name, "sees", n.Name, "left")
	c.ActiveMembersLock.Lock()
	// Delete the node from active members
	delete(c.ActiveMembers, n.Name)
	c.ActiveMembersLock.Unlock()

	// Delete the node from pending members
	c.pendingMembersLock.Lock()
	delete(c.pendingMembers, n.Name)
	c.pendingMembersLock.Unlock()
}

func (c Client) NotifyUpdate(n *memberlist.Node) {

}
<|endoftext|>


<commit_before>package soap

import (
	"encoding/xml"
	"net/http"
	"time"

	soap_http "github.com/Bridgevine/soap/http"
)

// Request represents a SOAP request.
type Request struct {
	Env         Envelope
	HTTPHeaders http.Header
	CreatedAt   time.Time
	SentAt      time.Time
}

// NewRequest TODO.
func NewRequest(action string, env Envelope) *Request {
	return &Request{
		Env:         env,
		HTTPHeaders: getHTTPHeaders(env.version(), action),
		CreatedAt:   time.Now(),
	}
}

// Response represents a SOAP response.
type Response struct {
	Env        Envelope
	Request    *Request
	ReceivedAt time.Time
	URL        string // URL that sent the response.
	Endpoint   string // An endpoint called by request.
	Method     string // A HTTP method used to contact the endpoint.
	StatusCode int    // HTTP status code received
}

// Client represents a SOAP client that will be used
// to send requests and process responses.
type Client struct {
	// The URL of the endpoint to which the requests will be sent.
	url string

	// If different than nil, the HTTP Client adapter that
	// will be used to send the request.
	httpClient soap_http.ClientAdapter
}

// Do sends a SOAP request.
func (c *Client) Do(req *Request) (*Response, error) {
	payload, err := xml.Marshal(req.Env)
	if err != nil {
		return nil, err
	}

	httpReq := soap_http.NewRequest("POST", c.url, payload)
	httpReq.Header = req.HTTPHeaders
	httpClient := c.httpClient
	if httpClient == nil {
		httpClient = soap_http.NewClientAdapter()
	}

	req.SentAt = time.Now()

	httpRes, err := httpClient.Do(httpReq)
	if err != nil {
		return nil, err
	}

	env, err := decodeEnvelope(req.Env.version(), httpRes.Body)
	if err != nil {
		return nil, err
	}

	resp := Response{
		Env:        env,
		Request:    req,
		ReceivedAt: time.Now(),
		URL:        c.url,
		Method:     httpReq.Method,
		StatusCode: httpRes.StatusCode,
	}

	return &resp, nil
}

// Option represents a configuration function for a SOAP client.
// An option will configure or set up internal details of a SOAP client.
type Option func(*Client)

// SetHTTPClient returns a configuration function to configure
// the HTTP Client that will be used to send the requests.
func SetHTTPClient(httpClient soap_http.ClientAdapter) Option {
	return func(c *Client) {
		c.httpClient = httpClient
	}
}

// NewClient creates a new SOAP client and set its initial state.
// The url parameter represents the SOAP Service URL.
func NewClient(url string, opts ...Option) (*Client, error) {
	c := &Client{
		url: url,
	}

	for _, opt := range opts {
		opt(c)
	}

	return c, nil
}
<commit_msg>expaned response to included payload, when an error happens the response body can be placed into payload for logging<commit_after>package soap

import (
	"encoding/xml"
	"net/http"
	"time"

	soap_http "github.com/Bridgevine/soap/http"
)

// Request represents a SOAP request.
type Request struct {
	Env         Envelope
	HTTPHeaders http.Header
	CreatedAt   time.Time
	SentAt      time.Time
}

// NewRequest TODO.
func NewRequest(action string, env Envelope) *Request {
	return &Request{
		Env:         env,
		HTTPHeaders: getHTTPHeaders(env.version(), action),
		CreatedAt:   time.Now(),
	}
}

// Response represents a SOAP response.
type Response struct {
	Env        Envelope
	Request    *Request
	ReceivedAt time.Time
	URL        string // URL that sent the response.
	Endpoint   string // An endpoint called by request.
	Method     string // A HTTP method used to contact the endpoint.
	StatusCode int    // HTTP status code received
	Payload    string // used when there is an error to strinify the body response
}

// Client represents a SOAP client that will be used
// to send requests and process responses.
type Client struct {
	// The URL of the endpoint to which the requests will be sent.
	url string

	// If different than nil, the HTTP Client adapter that
	// will be used to send the request.
	httpClient soap_http.ClientAdapter
}

// Do sends a SOAP request.
func (c *Client) Do(req *Request) (*Response, error) {
	payload, err := xml.Marshal(req.Env)
	if err != nil {
		return nil, err
	}

	httpReq := soap_http.NewRequest("POST", c.url, payload)
	httpReq.Header = req.HTTPHeaders
	httpClient := c.httpClient
	if httpClient == nil {
		httpClient = soap_http.NewClientAdapter()
	}

	req.SentAt = time.Now()

	httpRes, err := httpClient.Do(httpReq)
	if err != nil {
		return nil, err
	}

	env, err := decodeEnvelope(req.Env.version(), httpRes.Body)
	if err != nil {
		return &Response{
			Request:    req,
			ReceivedAt: time.Now(),
			URL:        c.url,
			Method:     httpReq.Method,
			StatusCode: httpRes.StatusCode,
			Payload:    string(httpRes.Body),
		}, err
	}

	resp := Response{
		Env:        env,
		Request:    req,
		ReceivedAt: time.Now(),
		URL:        c.url,
		Method:     httpReq.Method,
		StatusCode: httpRes.StatusCode,
	}

	return &resp, nil
}

// Option represents a configuration function for a SOAP client.
// An option will configure or set up internal details of a SOAP client.
type Option func(*Client)

// SetHTTPClient returns a configuration function to configure
// the HTTP Client that will be used to send the requests.
func SetHTTPClient(httpClient soap_http.ClientAdapter) Option {
	return func(c *Client) {
		c.httpClient = httpClient
	}
}

// NewClient creates a new SOAP client and set its initial state.
// The url parameter represents the SOAP Service URL.
func NewClient(url string, opts ...Option) (*Client, error) {
	c := &Client{
		url: url,
	}

	for _, opt := range opts {
		opt(c)
	}

	return c, nil
}
<|endoftext|>


<commit_before>package grestclient

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	rt "reflect"
)

type Client interface {

	//Headers returns the default headers that will
	//be set with every request made with the client.
	Headers() http.Header

	//SetHeaders sets the default headers that will be sent with
	//every request made with this client.
	SetHeaders(http.Header)

	//Query returns the default query to use for all requests
	Query() url.Values

	//SetQuery sets the default query to use for all requests
	SetQuery(url.Values)

	//SetBaseUrl sets the base url to use for all requests
	//If you want to use a different base url then you must
	//create a new client with the new base url. Call
	//CloneWithNewBaseUrl( url ) to get a clone of this
	//client's settings but with a new base url.
	//An error should be returned if the url is "unsupported",
	//whatever that may mean.
	//If you wish to use a username/password combination, set
	//the userinfo on the url. It will be used during requests.
	//To use one client with credentials and another without,
	//use CloneWithNewBaseUrl  and then change the base url.
	//Any query parameters added here should be ignored.
	//Clients should use the SetQuery method to set default
	//query parameters
	SetBaseUrl(*url.URL) error

	//BaseUrl returns the base url being used
	BaseUrl() *url.URL

	//Clones the client with everything the old client had
	//Ideally, clones should be independent of the original and can be changed
	//without affecting the original and vice versa.
	//This implementation, however, shared the http.Client among clones.
	//All other 'things' like headers, base url, query, marshalers are separate and
	//can be adjusted without affecting the original/clones.
	Clone() Client

	//GetHttpClient returns the current http.Client being used
	//If none has been set, this should return http.DefaultClient
	GetHttpClient() *http.Client

	//SetHttpClient sets the http.Client to use during requests
	//Use this to customize your http.Client as you wish. If you
	//don't set one, the default http.Client will be used.
	SetHttpClient(*http.Client)

	//SetMarshaler sets the marshal function to be used
	//to marshal the request bodies for requests
	//Doesn't have to mirror the Unmarshaler. Send plain text, get back json
	//Default is a json marshaler
	SetMarshaler(MarshalerFunc)
	//SetUnmarshaler sets the unmarshal function to be used
	//to unmarshal the response body for responses
	//Doesn't have to mirror the Marshaler. Send XML, get back json
	//Default is a json unmarshaler
	SetUnmarshaler(UnmarshalerFunc)

	//AddRequestMutator adds a mutator that the request will be
	//passed through before executing the request. All RequestMutators
	//are called AFTER the Marshaler is used.
	//RequestMutators should be called in the order they were added
	AddRequestMutators(...RequestMutator) Client

	//AddResponseMutator adds a mutator that the response will be
	//passed through after the server responds. All ResponseMutators
	//are called BEFORE the Unmarshaler is used.
	//ResponseMutators should be called in the order they were added
	AddResponseMutators(...ResponseMutator) Client

	//RemoveRequestMutator removes a request mutator
	SetRequestMutators(...RequestMutator) Client
	//RemoveResponseMutator removes a response mutator
	SetResponseMutators(...ResponseMutator) Client

	//Returns the RequestMutators
	RequestMutators() []RequestMutator
	//Returns the ResponseMutators
	ResponseMutators() []ResponseMutator

	//Get performs a get request with the base url plus the path appended to it. You can send query values and
	//supply a successResult that will be populated if the http response has a return code of 300.
	//errorResult is populated if the error code is 400 or more
	//Returns the raw http.Response and error similar to Do method of http.Client
	//The returned http.Response might be non-nil even though an error was also returned
	//depending on where the operation failed.
	Get(path string, query url.Values, successResult interface{}, errorResult interface{}) (*http.Response, error)

	//Post performs a post request with the base url plus the path appended to it. You can send query values and
	//supply a successResult that will be populated if the http response has a return code of 300.
	//errorResult is populated if the error code is 400 or more
	//With post you can also provide a post body.
	//Returns the raw http.Response and error similar to Do method of http.Client
	//The returned http.Response might be non-nil even though an error was also returned
	//depending on where the operation failed.
	Post(path string, query url.Values, postBody interface{}, successResult interface{}, errorResult interface{}) (*http.Response, error)

	//Put performs a put request with the base url plus the path appended to it. You can send query values and
	//supply a successResult that will be populated if the http response has a return code of 300.
	//errorResult is populated if the error code is 400 or more
	//With put you can also provide a put body.
	//Returns the raw http.Response and error similar to Do method of http.Client
	//The returned http.Response might be non-nil even though an error was also returned
	//depending on where the operation failed.
	Put(path string, query url.Values, putBody interface{}, successResult interface{}, errorResult interface{}) (*http.Response, error)

	//Patch performs a patch request with the base url plus the path appended to it. You can send query values and
	//supply a successResult that will be populated if the http response has a return code of 300.
	//errorResult is populated if the error code is 400 or more
	//With patch you can also provide a patch body.
	//Returns the raw http.Response and error similar to Do method of http.Client
	//The returned http.Response might be non-nil even though an error was also returned
	//depending on where the operation failed.
	Patch(path string, query url.Values, patchBody interface{}, successResult interface{}, errorResult interface{}) (*http.Response, error)

	//Head performs a head request with the base url plus the path appended to it.
	//supply a successResult that will be populated if the http response has a return code of 300.
	//errorResult is populated if the error code is 400 or more
	//Returns the raw http.Response and error similar to Do method of http.Client
	//The returned http.Response might be non-nil even though an error was also returned
	//depending on where the operation failed.
	Head(path string, successResult interface{}, errorResultg interface{}) (*http.Response, error)

	//Option performs an option request with the base url plus the path appended to it.
	//supply a successResult that will be populated if the http response has a return code of 300.
	//errorResult is populated if the error code is 400 or more
	//Returns the raw http.Response and error similar to Do method of http.Client
	//The returned http.Response might be non-nil even though an error was also returned
	//depending on where the operation failed.
	Options(path string, successResult interface{}, errorResult interface{}) (*http.Response, error)

	//Delete performs an delete request with the base url plus the path appended to it.
	//supply a successResult that will be populated if the http response has a return code of 300.
	//errorResult is populated if the error code is 400 or more
	//Returns the raw http.Response and error similar to Do method of http.Client
	//The returned http.Response might be non-nil even though an error was also returned
	//depending on where the operation failed.
	Delete(path string, query url.Values, successResult interface{}, errorResult interface{}) (*http.Response, error)
}

//MarshalerFunc takes something and converts it into a
//io.ReadCloser that can be used for the request body
type MarshalerFunc func(v interface{}) (io.ReadCloser, error)

//UnmarshalerFunc takes the response body and converts it into
//something you can use.
type UnmarshalerFunc func(body io.ReadCloser, v interface{}) error

//ByteSliceToReadCloser takes a byte slice and converts it to an
//io.ReadCloser that can be used as a request/resonse body
func ByteSliceToReadCloser(b []byte) (io.ReadCloser, error) {
	if b == nil {
		return nil, errors.New("ReadCloserFromByteSlice received a nil byte slice.")
	}

	buf := bytes.NewBuffer(b)
	return ioutil.NopCloser(buf), nil
}

//StringToReadCloser takes a string and converts it to an
//io.ReadCloser that can be used as a request/resonse body
func StringToReadCloser(s string) (io.ReadCloser, error) {
	buf := bytes.NewBufferString(s)
	return ioutil.NopCloser(buf), nil
}

//JsonMarshalerFunc can be used to marshal request bodies
//into json
func JsonMarshalerFunc(v interface{}) (io.ReadCloser, error) {
	b, err := json.Marshal(v)
	if err != nil {
		return nil, err
	}
	body, err := ByteSliceToReadCloser(b)
	if err != nil {
		return nil, err
	}
	return body, nil
}

//JsonUnmarshalerFunc can be used to unmarshal response bodies
//from json
func JsonUnmarshalerFunc(body io.ReadCloser, v interface{}) error {
	defer body.Close()
	b, err := ioutil.ReadAll(body)
	if err != nil {
		return err
	}

	err = json.Unmarshal(b, v)

	if err != nil {
		return err
	}

	return nil
}

//StringMarshalerFunc can be used to marshal strings into a request.
func StringMarshalerFunc(v interface{}) (io.ReadCloser, error) {
	switch t := v.(type) {
	case fmt.Stringer:
		return StringToReadCloser(t.String())
	case string:
		return StringToReadCloser(t)
	}

	return nil, errors.New("Did not know how to use the body as text.")
}

//StringUnmarshalerFunc can be used to unmarshal strings from a response.
func StringUnmarshalerFunc(body io.ReadCloser, v interface{}) error {
	defer body.Close()

	if v == nil {
		return nil
	}

	b, err := ioutil.ReadAll(body)

	if err != nil {
		return err
	}

	switch v.(type) {
	case *string:
		elem := rt.ValueOf(v).Elem()

		if elem.CanSet() {
			elem.SetString(string(b))
			return nil
		}
	case string:
		return errors.New("You must pass the string by reference: ")
	}

	return errors.New("Did not know how to unmarshal the text coming back.")
}

//JsonContentTypeMutator sets the Content-Type of the request to be
//application/json
func JsonContentTypeMutator(r *http.Request) error {
	r.Header.Add("Content-Type", "application/json")
	return nil
}

//RequestMutators are called before the request is made but after the marshaler function has been
//called.
type RequestMutator func(*http.Request) error
type ResponseMutator func(*http.Response) error

//SetupClientForJson is a convenience method that sets the
//marshaler and unmarshaler funcs on the client to be the
//Json funcs in this package. It also sets a request mutator
//to set the Content-Type to json.
func SetupForJson(c Client) {
	c.SetMarshaler(JsonMarshalerFunc)
	c.SetUnmarshaler(JsonUnmarshalerFunc)
	c.AddRequestMutators(JsonContentTypeMutator)
}
<commit_msg>Added documentation<commit_after>package grestclient

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	rt "reflect"
)

type Client interface {

	//Headers returns the default headers that will
	//be set with every request made with the client.
	Headers() http.Header

	//SetHeaders sets the default headers that will be sent with
	//every request made with this client.
	SetHeaders(http.Header)

	//Query returns the default query to use for all requests
	Query() url.Values

	//SetQuery sets the default query to use for all requests
	SetQuery(url.Values)

	//SetBaseUrl sets the base url to use for all requests
	//If you want to use a different base url then you must
	//create a new client with the new base url. Call
	//CloneWithNewBaseUrl( url ) to get a clone of this
	//client's settings but with a new base url.
	//An error should be returned if the url is "unsupported",
	//whatever that may mean.
	//If you wish to use a username/password combination, set
	//the userinfo on the url. It will be used during requests.
	//To use one client with credentials and another without,
	//use CloneWithNewBaseUrl  and then change the base url.
	//Any query parameters added here should be ignored.
	//Clients should use the SetQuery method to set default
	//query parameters
	SetBaseUrl(*url.URL) error

	//BaseUrl returns the base url being used. This implementation
	//allows you to change the base url here directly but other
	//implementations might give you a clone so changing it won't affect
	//the client. In those cases, use SetBaseUrl to change the url.
	BaseUrl() *url.URL

	//Clones the client with everything the old client had
	//Ideally, clones should be independent of the original and can be changed
	//without affecting the original and vice versa.
	//This implementation, however, shared the http.Client among clones.
	//All other 'things' like headers, base url, query, marshalers are separate and
	//can be adjusted without affecting the original/clones.
	Clone() Client

	//GetHttpClient returns the current http.Client being used
	//If none has been set, this should return http.DefaultClient
	GetHttpClient() *http.Client

	//SetHttpClient sets the http.Client to use during requests
	//Use this to customize your http.Client as you wish. If you
	//don't set one, the default http.Client will be used.
	SetHttpClient(*http.Client)

	//SetMarshaler sets the marshal function to be used
	//to marshal the request bodies for requests
	//Doesn't have to mirror the Unmarshaler. Send plain text, get back json
	//Default is a json marshaler
	SetMarshaler(MarshalerFunc)
	//SetUnmarshaler sets the unmarshal function to be used
	//to unmarshal the response body for responses
	//Doesn't have to mirror the Marshaler. Send XML, get back json
	//Default is a json unmarshaler
	SetUnmarshaler(UnmarshalerFunc)

	//AddRequestMutator adds a mutator that the request will be
	//passed through before executing the request. All RequestMutators
	//are called AFTER the Marshaler is used.
	//RequestMutators should be called in the order they were added
	AddRequestMutators(...RequestMutator) Client

	//AddResponseMutator adds a mutator that the response will be
	//passed through after the server responds. All ResponseMutators
	//are called BEFORE the Unmarshaler is used.
	//ResponseMutators should be called in the order they were added
	AddResponseMutators(...ResponseMutator) Client

	//RemoveRequestMutator removes a request mutator
	SetRequestMutators(...RequestMutator) Client
	//RemoveResponseMutator removes a response mutator
	SetResponseMutators(...ResponseMutator) Client

	//Returns the RequestMutators
	RequestMutators() []RequestMutator
	//Returns the ResponseMutators
	ResponseMutators() []ResponseMutator

	//Get performs a get request with the base url plus the path appended to it. You can send query values and
	//supply a successResult that will be populated if the http response has a return code of 300.
	//errorResult is populated if the error code is 400 or more
	//Returns the raw http.Response and error similar to Do method of http.Client
	//The returned http.Response might be non-nil even though an error was also returned
	//depending on where the operation failed.
	Get(path string, query url.Values, successResult interface{}, errorResult interface{}) (*http.Response, error)

	//Post performs a post request with the base url plus the path appended to it. You can send query values and
	//supply a successResult that will be populated if the http response has a return code of 300.
	//errorResult is populated if the error code is 400 or more
	//With post you can also provide a post body.
	//Returns the raw http.Response and error similar to Do method of http.Client
	//The returned http.Response might be non-nil even though an error was also returned
	//depending on where the operation failed.
	Post(path string, query url.Values, postBody interface{}, successResult interface{}, errorResult interface{}) (*http.Response, error)

	//Put performs a put request with the base url plus the path appended to it. You can send query values and
	//supply a successResult that will be populated if the http response has a return code of 300.
	//errorResult is populated if the error code is 400 or more
	//With put you can also provide a put body.
	//Returns the raw http.Response and error similar to Do method of http.Client
	//The returned http.Response might be non-nil even though an error was also returned
	//depending on where the operation failed.
	Put(path string, query url.Values, putBody interface{}, successResult interface{}, errorResult interface{}) (*http.Response, error)

	//Patch performs a patch request with the base url plus the path appended to it. You can send query values and
	//supply a successResult that will be populated if the http response has a return code of 300.
	//errorResult is populated if the error code is 400 or more
	//With patch you can also provide a patch body.
	//Returns the raw http.Response and error similar to Do method of http.Client
	//The returned http.Response might be non-nil even though an error was also returned
	//depending on where the operation failed.
	Patch(path string, query url.Values, patchBody interface{}, successResult interface{}, errorResult interface{}) (*http.Response, error)

	//Head performs a head request with the base url plus the path appended to it.
	//supply a successResult that will be populated if the http response has a return code of 300.
	//errorResult is populated if the error code is 400 or more
	//Returns the raw http.Response and error similar to Do method of http.Client
	//The returned http.Response might be non-nil even though an error was also returned
	//depending on where the operation failed.
	Head(path string, successResult interface{}, errorResultg interface{}) (*http.Response, error)

	//Option performs an option request with the base url plus the path appended to it.
	//supply a successResult that will be populated if the http response has a return code of 300.
	//errorResult is populated if the error code is 400 or more
	//Returns the raw http.Response and error similar to Do method of http.Client
	//The returned http.Response might be non-nil even though an error was also returned
	//depending on where the operation failed.
	Options(path string, successResult interface{}, errorResult interface{}) (*http.Response, error)

	//Delete performs an delete request with the base url plus the path appended to it.
	//supply a successResult that will be populated if the http response has a return code of 300.
	//errorResult is populated if the error code is 400 or more
	//Returns the raw http.Response and error similar to Do method of http.Client
	//The returned http.Response might be non-nil even though an error was also returned
	//depending on where the operation failed.
	Delete(path string, query url.Values, successResult interface{}, errorResult interface{}) (*http.Response, error)
}

//MarshalerFunc takes something and converts it into a
//io.ReadCloser that can be used for the request body
type MarshalerFunc func(v interface{}) (io.ReadCloser, error)

//UnmarshalerFunc takes the response body and converts it into
//something you can use.
type UnmarshalerFunc func(body io.ReadCloser, v interface{}) error

//ByteSliceToReadCloser takes a byte slice and converts it to an
//io.ReadCloser that can be used as a request/resonse body
func ByteSliceToReadCloser(b []byte) (io.ReadCloser, error) {
	if b == nil {
		return nil, errors.New("ReadCloserFromByteSlice received a nil byte slice.")
	}

	buf := bytes.NewBuffer(b)
	return ioutil.NopCloser(buf), nil
}

//StringToReadCloser takes a string and converts it to an
//io.ReadCloser that can be used as a request/resonse body
func StringToReadCloser(s string) (io.ReadCloser, error) {
	buf := bytes.NewBufferString(s)
	return ioutil.NopCloser(buf), nil
}

//JsonMarshalerFunc can be used to marshal request bodies
//into json
func JsonMarshalerFunc(v interface{}) (io.ReadCloser, error) {
	b, err := json.Marshal(v)
	if err != nil {
		return nil, err
	}
	body, err := ByteSliceToReadCloser(b)
	if err != nil {
		return nil, err
	}
	return body, nil
}

//JsonUnmarshalerFunc can be used to unmarshal response bodies
//from json
func JsonUnmarshalerFunc(body io.ReadCloser, v interface{}) error {
	defer body.Close()
	b, err := ioutil.ReadAll(body)
	if err != nil {
		return err
	}

	err = json.Unmarshal(b, v)

	if err != nil {
		return err
	}

	return nil
}

//StringMarshalerFunc can be used to marshal strings into a request.
func StringMarshalerFunc(v interface{}) (io.ReadCloser, error) {
	switch t := v.(type) {
	case fmt.Stringer:
		return StringToReadCloser(t.String())
	case string:
		return StringToReadCloser(t)
	}

	return nil, errors.New("Did not know how to use the body as text.")
}

//StringUnmarshalerFunc can be used to unmarshal strings from a response.
func StringUnmarshalerFunc(body io.ReadCloser, v interface{}) error {
	defer body.Close()

	if v == nil {
		return nil
	}

	b, err := ioutil.ReadAll(body)

	if err != nil {
		return err
	}

	switch v.(type) {
	case *string:
		elem := rt.ValueOf(v).Elem()

		if elem.CanSet() {
			elem.SetString(string(b))
			return nil
		}
	case string:
		return errors.New("You must pass the string by reference: ")
	}

	return errors.New("Did not know how to unmarshal the text coming back.")
}

//JsonContentTypeMutator sets the Content-Type of the request to be
//application/json
func JsonContentTypeMutator(r *http.Request) error {
	r.Header.Add("Content-Type", "application/json")
	return nil
}

//RequestMutators are called before the request is made but after the marshaler function has been
//called.
type RequestMutator func(*http.Request) error
type ResponseMutator func(*http.Response) error

//SetupClientForJson is a convenience method that sets the
//marshaler and unmarshaler funcs on the client to be the
//Json funcs in this package. It also sets a request mutator
//to set the Content-Type to json.
func SetupForJson(c Client) {
	c.SetMarshaler(JsonMarshalerFunc)
	c.SetUnmarshaler(JsonUnmarshalerFunc)
	c.AddRequestMutators(JsonContentTypeMutator)
}
<|endoftext|>


<commit_before>package xbl

import (
	"bytes"
	"encoding/json"
	"net/http"
	"strconv"
	"time"
)

const userAgent = "SmartGlass/com.microsoft.smartglass (1610.1205.1554; OS Version 10.1.1 (Build 14B100))"

// Client encapsulates the entire Xbox Live API and a set
// of credentials to access the API.
//
// A Client is safe for concurrent access.
type Client struct {
	client      http.Client
	credentials *credentials
}

// Expiry returns the time at which this client's credentials will
// no longer be valid and the client should be replaced with a new
// one obtained from Login.
func (c *Client) Expiry() time.Time {
	return c.credentials.expiresAt
}

// UserID returns the XID user ID of the user who is authenticated with
// the API. All requests to the API are performed as this user.
func (c *Client) UserID() string {
	return c.credentials.xid
}

// Gamertag returns the gamertag of the user who is authenticated with the API.
func (c *Client) Gamertag() string {
	return c.credentials.gamertag
}

type apiVersion int

const (
	vXbox360 apiVersion = 1
	vXboxOne apiVersion = 2
	vBoth    apiVersion = 3
)

func (c *Client) post(url string, v apiVersion, body interface{}, respBody interface{}) error {
	b, err := json.Marshal(body)
	if err != nil {
		return err
	}

	req, err := http.NewRequest("POST", url, bytes.NewReader(b))
	if err != nil {
		return err
	}
	req.Header.Set("User-Agent", userAgent)
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Authorization", c.credentials.authHeader())
	req.Header.Set("x-xbl-contract-version", strconv.Itoa(int(v)))

	resp, err := c.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	return json.NewDecoder(resp.Body).Decode(respBody)
}

func (c *Client) get(u string, v apiVersion, respBody interface{}) error {
	req, err := http.NewRequest("GET", u, nil)
	if err != nil {
		return err
	}
	req.Header.Set("User-Agent", userAgent)
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Authorization", c.credentials.authHeader())
	req.Header.Set("x-xbl-contract-version", strconv.Itoa(int(v)))

	resp, err := c.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	return json.NewDecoder(resp.Body).Decode(respBody)
}

type reqOptions struct {
	updatedSince time.Time
}

// Option defines an option for a client method.
type Option func(*reqOptions)

// UpdatedSince will filter results to only results updated since the
// provided time.
func UpdatedSince(t time.Time) func(*reqOptions) {
	return func(ro *reqOptions) { ro.updatedSince = t }
}
<commit_msg>UpdatedSince returns Option<commit_after>package xbl

import (
	"bytes"
	"encoding/json"
	"net/http"
	"strconv"
	"time"
)

const userAgent = "SmartGlass/com.microsoft.smartglass (1610.1205.1554; OS Version 10.1.1 (Build 14B100))"

// Client encapsulates the entire Xbox Live API and a set
// of credentials to access the API.
//
// A Client is safe for concurrent access.
type Client struct {
	client      http.Client
	credentials *credentials
}

// Expiry returns the time at which this client's credentials will
// no longer be valid and the client should be replaced with a new
// one obtained from Login.
func (c *Client) Expiry() time.Time {
	return c.credentials.expiresAt
}

// UserID returns the XID user ID of the user who is authenticated with
// the API. All requests to the API are performed as this user.
func (c *Client) UserID() string {
	return c.credentials.xid
}

// Gamertag returns the gamertag of the user who is authenticated with the API.
func (c *Client) Gamertag() string {
	return c.credentials.gamertag
}

type apiVersion int

const (
	vXbox360 apiVersion = 1
	vXboxOne apiVersion = 2
	vBoth    apiVersion = 3
)

func (c *Client) post(url string, v apiVersion, body interface{}, respBody interface{}) error {
	b, err := json.Marshal(body)
	if err != nil {
		return err
	}

	req, err := http.NewRequest("POST", url, bytes.NewReader(b))
	if err != nil {
		return err
	}
	req.Header.Set("User-Agent", userAgent)
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Authorization", c.credentials.authHeader())
	req.Header.Set("x-xbl-contract-version", strconv.Itoa(int(v)))

	resp, err := c.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	return json.NewDecoder(resp.Body).Decode(respBody)
}

func (c *Client) get(u string, v apiVersion, respBody interface{}) error {
	req, err := http.NewRequest("GET", u, nil)
	if err != nil {
		return err
	}
	req.Header.Set("User-Agent", userAgent)
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Authorization", c.credentials.authHeader())
	req.Header.Set("x-xbl-contract-version", strconv.Itoa(int(v)))

	resp, err := c.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	return json.NewDecoder(resp.Body).Decode(respBody)
}

type reqOptions struct {
	updatedSince time.Time
}

// Option defines an option for a client method.
type Option func(*reqOptions)

// UpdatedSince will filter results to only results updated since the
// provided time.
func UpdatedSince(t time.Time) Option {
	return func(ro *reqOptions) { ro.updatedSince = t }
}
<|endoftext|>


<commit_before>package getter

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	urlhelper "github.com/hashicorp/terraform/helper/url"
)

// Client is a client for downloading things.
//
// Top-level functions such as Get are shortcuts for interacting with a client.
// Using a client directly allows more fine-grained control over how downloading
// is done, as well as customizing the protocols supported.
type Client struct {
	// Src is the source URL to get.
	//
	// Dst is the path to save the downloaded thing as. If Dir is set to
	// true, then this should be a directory. If the directory doesn't exist,
	// it will be created for you.
	Src string
	Dst string

	// Dir, if true, tells the Client it is downloading a directory (versus
	// a single file). This distinction is necessary since filenames and
	// directory names follow the same format so disambiguating is impossible
	// without knowing ahead of time.
	Dir bool

	// Getters is the map of protocols supported by this client. Use
	// the global Getters variable for the built-in defaults.
	Getters map[string]Getter
}

// Get downloads the configured source to the destination.
func (c *Client) Get() error {
	force, src := getForcedGetter(c.Src)

	// If there is a subdir component, then we download the root separately
	// and then copy over the proper subdir.
	var realDst string
	dst := c.Dst
	src, subDir := SourceDirSubdir(src)
	if subDir != "" {
		tmpDir, err := ioutil.TempDir("", "tf")
		if err != nil {
			return err
		}
		if err := os.RemoveAll(tmpDir); err != nil {
			return err
		}
		defer os.RemoveAll(tmpDir)

		realDst = dst
		dst = tmpDir
	}

	u, err := urlhelper.Parse(src)
	if err != nil {
		return err
	}
	if force == "" {
		force = u.Scheme
	}

	g, ok := c.Getters[force]
	if !ok {
		return fmt.Errorf(
			"download not supported for scheme '%s'", force)
	}

	// If we're not downloading a directory, then just download the file
	// and return.
	if !c.Dir {
		return g.GetFile(dst, u)
	}

	// We're downloading a directory, which might require a bit more work
	// if we're specifying a subdir.
	err = g.Get(dst, u)
	if err != nil {
		err = fmt.Errorf("error downloading '%s': %s", src, err)
		return err
	}

	// If we have a subdir, copy that over
	if subDir != "" {
		if err := os.RemoveAll(realDst); err != nil {
			return err
		}
		if err := os.MkdirAll(realDst, 0755); err != nil {
			return err
		}

		return copyDir(realDst, filepath.Join(dst, subDir), false)
	}

	return nil
}
<commit_msg>add basic checksum<commit_after>package getter

import (
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"

	urlhelper "github.com/hashicorp/terraform/helper/url"
)

// Client is a client for downloading things.
//
// Top-level functions such as Get are shortcuts for interacting with a client.
// Using a client directly allows more fine-grained control over how downloading
// is done, as well as customizing the protocols supported.
type Client struct {
	// Src is the source URL to get.
	//
	// Dst is the path to save the downloaded thing as. If Dir is set to
	// true, then this should be a directory. If the directory doesn't exist,
	// it will be created for you.
	Src string
	Dst string

	// Dir, if true, tells the Client it is downloading a directory (versus
	// a single file). This distinction is necessary since filenames and
	// directory names follow the same format so disambiguating is impossible
	// without knowing ahead of time.
	Dir bool

	// Getters is the map of protocols supported by this client. Use
	// the global Getters variable for the built-in defaults.
	Getters map[string]Getter
}

// Get downloads the configured source to the destination.
func (c *Client) Get() error {
	force, src := getForcedGetter(c.Src)

	// If there is a subdir component, then we download the root separately
	// and then copy over the proper subdir.
	var realDst string
	dst := c.Dst
	src, subDir := SourceDirSubdir(src)
	if subDir != "" {
		tmpDir, err := ioutil.TempDir("", "tf")
		if err != nil {
			return err
		}
		if err := os.RemoveAll(tmpDir); err != nil {
			return err
		}
		defer os.RemoveAll(tmpDir)

		realDst = dst
		dst = tmpDir
	}

	u, err := urlhelper.Parse(src)
	if err != nil {
		return err
	}
	if force == "" {
		force = u.Scheme
	}

	g, ok := c.Getters[force]
	if !ok {
		return fmt.Errorf(
			"download not supported for scheme '%s'", force)
	}

	var sum string
	sumRaw := u.Query()["checksum"]
	if len(sumRaw) == 1 {
		sum = sumRaw[0]
	}

	// If we're not downloading a directory, then just download the file
	// and return.
	if !c.Dir {
		if sum == "" {
			return g.GetFile(dst, u)
		}

		err := g.GetFile(dst, u)
		if err != nil {
			return err
		}

		return checksum(dst, sum)
	}

	// We're downloading a directory, which might require a bit more work
	// if we're specifying a subdir.
	err = g.Get(dst, u)
	if err != nil {
		err = fmt.Errorf("error downloading '%s': %s", src, err)
		return err
	}

	// If we have a subdir, copy that over
	if subDir != "" {
		if err := os.RemoveAll(realDst); err != nil {
			return err
		}
		if err := os.MkdirAll(realDst, 0755); err != nil {
			return err
		}

		return copyDir(realDst, filepath.Join(dst, subDir), false)
	}

	return checksum(realDst, sum)
}

// checksum is a simple method to compute the SHA256 checksum of a source (file
// or dir) and compare it to a given sum.
func checksum(source, sum string) error {
	if sum == "" {
		return nil
	}
	// compute and check checksum
	log.Printf("[DEBUG] Running checksum on (%s)", source)
	hasher := sha256.New()
	file, err := os.Open(source)
	if err != nil {
		return fmt.Errorf("Failed to open file for checksum: %s", err)
	}

	defer file.Close()
	io.Copy(hasher, file)

	computed := hex.EncodeToString(hasher.Sum(nil))
	if sum != computed {
		return fmt.Errorf(
			"Checksums did not match.\nExpected (%s), got (%s)",
			sum,
			computed)
	}

	return nil
}
<|endoftext|>


<commit_before>package wireless

import (
	"errors"
	"strconv"
	"time"
)

// Client represents a wireless client
type Client struct {
	conn *Conn
}

// NewClient will create a new client by connecting to the
// given interface in WPA
func NewClient(iface string) (c *Client, err error) {
	c.conn, err = Dial(iface)
	if err != nil {
		return
	}

	return
}

// NewClientFromConn returns a new client from an already established connection
func NewClientFromConn(conn *Conn) (c *Client) {
	c.conn = conn
	return
}

// Close will close the client connection
func (cl *Client) Close() {
	cl.conn.Close()
}

// Conn will return the underlying connection
func (cl *Client) Conn() *Conn {
	return cl.conn
}

// Subscribe will subscribe to certain events that happen in WPA
func (cl *Client) Subscribe(topics ...string) *Subscription {
	return cl.conn.Subscribe(topics...)
}

// Status will return the current state of the WPA
func (cl *Client) Status() (State, error) {
	data, err := cl.conn.SendCommand(CmdStatus)
	if err != nil {
		return State{}, err
	}
	s := NewState(data)
	return s, nil
}

// Scan will scan for networks and return the APs it finds
func (cl *Client) Scan() (nets []AP, err error) {
	err = cl.conn.SendCommandBool(CmdScan)
	if err != nil {
		return
	}

	results := cl.conn.Subscribe(EventScanResults)
	failed := cl.conn.Subscribe(EventScanFailed)

	for {
		select {
		case <-failed.Next():
			err = ErrScanFailed
			return
		case <-results.Next():
			break
		case <-time.NewTimer(time.Second * 2).C:
			break
		}
	}

	scanned, err := cl.conn.SendCommand(CmdScanResults)
	if err != nil {
		return
	}

	return parseAP([]byte(scanned))
}

// Networks lists the known networks
func (cl *Client) Networks() (nets []Network, err error) {
	data, err := cl.conn.SendCommand(CmdListNetworks)
	if err != nil {
		return nil, err
	}

	return parseNetwork([]byte(data))
}

// Connect to a new or existing network
func (cl *Client) Connect(net Network) (Network, error) {
	net, err := cl.AddOrUpdateNetwork(net)
	if err != nil {
		return net, err
	}

	sub := cl.conn.Subscribe(EventNetworkNotFound, EventAuthReject, EventConnected, EventDisconnected, EventAssocReject)
	if err := cl.EnableNetwork(net.ID); err != nil {
		return net, err
	}

	ev := <-sub.Next()

	switch ev.Name {
	case EventConnected:
		return net, cl.SaveConfig()
	case EventNetworkNotFound:
		return net, ErrSSIDNotFound
	case EventAuthReject:
		return net, ErrAuthFailed
	case EventDisconnected:
		return net, ErrDisconnected
	case EventAssocReject:
		return net, ErrAssocRejected
	}

	return net, errors.New("failed to catch event " + ev.Name)
}

// AddOrUpdateNetwork will add or, if the network has IDStr set, update it
func (cl *Client) AddOrUpdateNetwork(net Network) (Network, error) {
	if net.IDStr != "" {
		nets, err := cl.Networks()
		if err != nil {
			return net, err
		}

		for _, n := range nets {
			if n.IDStr == net.IDStr {
				return cl.UpdateNetwork(net)
			}
		}
	}

	return cl.AddNetwork(net)
}

// UpdateNetwork will update the given network, an error will be thrown
// if the network doesn't have IDStr specified
func (cl *Client) UpdateNetwork(net Network) (Network, error) {
	if net.IDStr == "" {
		return net, ErrNoIdentifier
	}

	for _, cmd := range net.SetCmds() {
		if err := cl.conn.SendCommandBool(cmd...); err != nil {
			return net, err
		}
	}

	return net, nil
}

// AddNetwork will add a new network
func (cl *Client) AddNetwork(net Network) (Network, error) {
	i, err := cl.conn.SendCommandInt(CmdAddNetwork)
	if err != nil {
		return net, err
	}

	net.ID = i

	if net.IDStr == "" {
		net.IDStr = net.SSID
	}

	for _, cmd := range net.SetCmds() {
		if err := cl.conn.SendCommandBool(cmd...); err != nil {
			return net, err
		}
	}

	return net, nil
}

// RemoveNetwork will RemoveNetwork
func (cl *Client) RemoveNetwork(id int) error {
	return cl.conn.SendCommandBool(CmdRemoveNetwork, strconv.Itoa(id))
}

// EnableNetwork will EnableNetwork
func (cl *Client) EnableNetwork(id int) error {
	return cl.conn.SendCommandBool(CmdEnableNetwork + " " + strconv.Itoa(id))
}

// DisableNetwork will DisableNetwork
func (cl *Client) DisableNetwork(id int) error {
	return cl.conn.SendCommandBool(CmdDisableNetwork + " " + strconv.Itoa(id))
}

// SaveConfig will SaveConfig
func (cl *Client) SaveConfig() error {
	return cl.conn.SendCommandBool(CmdSaveConfig)
}

// LoadConfig will LoadConfig
func (cl *Client) LoadConfig() error {
	return cl.conn.SendCommandBool(CmdReconfigure)
}
<commit_msg>fix bug with scanning<commit_after>package wireless

import (
	"errors"
	"strconv"
	"time"
)

// Client represents a wireless client
type Client struct {
	conn *Conn
}

// NewClient will create a new client by connecting to the
// given interface in WPA
func NewClient(iface string) (c *Client, err error) {
	c = new(Client)
	c.conn, err = Dial(iface)
	if err != nil {
		return
	}

	return
}

// NewClientFromConn returns a new client from an already established connection
func NewClientFromConn(conn *Conn) (c *Client) {
	c.conn = conn
	return
}

// Close will close the client connection
func (cl *Client) Close() {
	cl.conn.Close()
}

// Conn will return the underlying connection
func (cl *Client) Conn() *Conn {
	return cl.conn
}

// Subscribe will subscribe to certain events that happen in WPA
func (cl *Client) Subscribe(topics ...string) *Subscription {
	return cl.conn.Subscribe(topics...)
}

// Status will return the current state of the WPA
func (cl *Client) Status() (State, error) {
	data, err := cl.conn.SendCommand(CmdStatus)
	if err != nil {
		return State{}, err
	}
	s := NewState(data)
	return s, nil
}

// Scan will scan for networks and return the APs it finds
func (cl *Client) Scan() (nets []AP, err error) {
	err = cl.conn.SendCommandBool(CmdScan)
	if err != nil {
		return
	}

	results := cl.conn.Subscribe(EventScanResults)
	failed := cl.conn.Subscribe(EventScanFailed)

	func() {
		for {
			select {
			case <-failed.Next():
				err = ErrScanFailed
				return
			case <-results.Next():
				return
			case <-time.NewTimer(time.Second * 2).C:
				return
			}
		}
	}()

	scanned, err := cl.conn.SendCommand(CmdScanResults)
	if err != nil {
		return
	}

	return parseAP([]byte(scanned))
}

// Networks lists the known networks
func (cl *Client) Networks() (nets []Network, err error) {
	data, err := cl.conn.SendCommand(CmdListNetworks)
	if err != nil {
		return nil, err
	}

	return parseNetwork([]byte(data))
}

// Connect to a new or existing network
func (cl *Client) Connect(net Network) (Network, error) {
	net, err := cl.AddOrUpdateNetwork(net)
	if err != nil {
		return net, err
	}

	sub := cl.conn.Subscribe(EventNetworkNotFound, EventAuthReject, EventConnected, EventDisconnected, EventAssocReject)
	if err := cl.EnableNetwork(net.ID); err != nil {
		return net, err
	}

	ev := <-sub.Next()

	switch ev.Name {
	case EventConnected:
		return net, cl.SaveConfig()
	case EventNetworkNotFound:
		return net, ErrSSIDNotFound
	case EventAuthReject:
		return net, ErrAuthFailed
	case EventDisconnected:
		return net, ErrDisconnected
	case EventAssocReject:
		return net, ErrAssocRejected
	}

	return net, errors.New("failed to catch event " + ev.Name)
}

// AddOrUpdateNetwork will add or, if the network has IDStr set, update it
func (cl *Client) AddOrUpdateNetwork(net Network) (Network, error) {
	if net.IDStr != "" {
		nets, err := cl.Networks()
		if err != nil {
			return net, err
		}

		for _, n := range nets {
			if n.IDStr == net.IDStr {
				return cl.UpdateNetwork(net)
			}
		}
	}

	return cl.AddNetwork(net)
}

// UpdateNetwork will update the given network, an error will be thrown
// if the network doesn't have IDStr specified
func (cl *Client) UpdateNetwork(net Network) (Network, error) {
	if net.IDStr == "" {
		return net, ErrNoIdentifier
	}

	for _, cmd := range net.SetCmds() {
		if err := cl.conn.SendCommandBool(cmd...); err != nil {
			return net, err
		}
	}

	return net, nil
}

// AddNetwork will add a new network
func (cl *Client) AddNetwork(net Network) (Network, error) {
	i, err := cl.conn.SendCommandInt(CmdAddNetwork)
	if err != nil {
		return net, err
	}

	net.ID = i

	if net.IDStr == "" {
		net.IDStr = net.SSID
	}

	for _, cmd := range net.SetCmds() {
		if err := cl.conn.SendCommandBool(cmd...); err != nil {
			return net, err
		}
	}

	return net, nil
}

// RemoveNetwork will RemoveNetwork
func (cl *Client) RemoveNetwork(id int) error {
	return cl.conn.SendCommandBool(CmdRemoveNetwork, strconv.Itoa(id))
}

// EnableNetwork will EnableNetwork
func (cl *Client) EnableNetwork(id int) error {
	return cl.conn.SendCommandBool(CmdEnableNetwork + " " + strconv.Itoa(id))
}

// DisableNetwork will DisableNetwork
func (cl *Client) DisableNetwork(id int) error {
	return cl.conn.SendCommandBool(CmdDisableNetwork + " " + strconv.Itoa(id))
}

// SaveConfig will SaveConfig
func (cl *Client) SaveConfig() error {
	return cl.conn.SendCommandBool(CmdSaveConfig)
}

// LoadConfig will LoadConfig
func (cl *Client) LoadConfig() error {
	return cl.conn.SendCommandBool(CmdReconfigure)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"log"
	"net/http"
	"strings"
)

func loop() {
	client := &http.Client{}
	fqdn := "localhost"

	resp, err := client.Post("http://localhost:1234/poll", "", strings.NewReader(fqdn))
	if err != nil {
		log.Fatal(err)
		return
	}
	defer resp.Body.Close()
	request, _ := http.ReadRequest(bufio.NewReader(resp.Body))
	id := request.Header.Get("id") // Needed so they can be linked.
	log.Printf("Got request for %s", request.URL.String())
	request.RequestURI = ""

	scrapeResp, err := client.Do(request)
	if err != nil {
		log.Fatal(err)
		return
	}
	log.Printf("Scraped %s", request.URL.String())
	scrapeResp.Header.Set("id", id)
	buf := &bytes.Buffer{}
	scrapeResp.Write(buf)
	log.Println(buf.Len())

	_, err = client.Post("http://localhost:1234/push", "", buf)
	if err != nil {
		log.Fatal(err)
		return
	}
	log.Printf("Pushed scrape result for %s", request.URL.String())
}

func main() {
	for {
		loop()
	}
}
<commit_msg>Make client more resilient and performant<commit_after>package main

import (
	"bufio"
	"bytes"
	"log"
	"net/http"
	"strings"
	"time"
)

func doScrape(request *http.Request) {
	client := &http.Client{}
	id := request.Header.Get("id") // Needed so they can be linked.

	scrapeResp, err := client.Do(request)
	if err != nil {
		log.Printf("Failed to scrape %s: %s", request.URL.String(), err)
		return
	}
	log.Printf("Scraped %s", request.URL.String())
	scrapeResp.Header.Set("id", id)
	buf := &bytes.Buffer{}
	scrapeResp.Write(buf)
	log.Println(buf.Len())

	_, err = client.Post("http://localhost:1234/push", "", buf)
	if err != nil {
		log.Printf("Failed to push scrape result for %s: %s", request.URL.String(), err)
		return
	}
	log.Printf("Pushed scrape result for %s", request.URL.String())
}

func loop() {
	client := &http.Client{}
	fqdn := "localhost"

	resp, err := client.Post("http://localhost:1234/poll", "", strings.NewReader(fqdn))
	if err != nil {
		log.Printf("Error polling: %s", err)
		time.Sleep(time.Second) // Don't pound the server.
		return
	}
	defer resp.Body.Close()
	request, _ := http.ReadRequest(bufio.NewReader(resp.Body))
	log.Printf("Got request for %s", request.URL.String())
	request.RequestURI = ""

	go doScrape(request)
}

func main() {
	for {
		loop()
	}
}
<|endoftext|>


<commit_before>package mongo

import (
	"errors"
	"github.com/gobly/core"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"runtime"
)

type Client struct {
	db         *mgo.Database
	c          *mgo.Collection
	session    *mgo.Session
	collection string
}

func (m *Client) Connect(url string, db string, collection string) error {
	session, err := mgo.Dial(url)

	if err != nil {
		return err
	}

	session.SetMode(mgo.Monotonic, true)

	m.db = session.DB(db)
	m.c = m.db.C(collection)
	m.session = session
	m.collection = collection

	runtime.SetFinalizer(m, func(m *Client) { m.session.Close() })
	return nil
}

func (m *Client) Insert(v interface{}) error {
	objectId := NewObjectId(v)
	oid, ok := objectId.Value()
	if !ok {
		objectId.SetValue(oid)
	}
	err := m.c.Insert(v)
	return err
}

func (m *Client) Update(v interface{}) error {
	objectId := NewObjectId(v)
	oid, ok := objectId.Value()
	if !ok {
		return errors.New("Missing objectID")
	}

	return m.c.UpdateId(oid, v)
}

func (m *Client) UpdateRawFiltered(f bson.M, q bson.M) error {
	return m.c.Update(f, q)
}

func (m *Client) UpdateRaw(q bson.M) error {
	return m.c.Update(nil, q)
}

func (m *Client) ReadByValueFiltered(f interface{}, v interface{}) error {
	return m.c.Find(v).Select(f).One(v)
}

func (m *Client) ReadByValue(v interface{}) error {
	return m.c.Find(v).One(v)
}

func (m *Client) ReadRaw(q map[string]string, v interface{}) error {
	return m.c.Find(q).One(v)
}

func (m *Client) ReadByID(objectId string, v interface{}) error {
	if !bson.IsObjectIdHex(objectId) {
		return errors.New("Invalid ObjectID format")
	}

	return m.c.FindId(bson.ObjectIdHex(objectId)).One(v)
}

func (m *Client) ReadBySlug(slug string, v interface{}) error {
	if bson.IsObjectIdHex(slug) {
		return m.c.FindId(bson.ObjectIdHex(slug)).One(v)
	}

	s := core.NewSlug(v)
	s.SetValue(slug)
	return m.c.Find(v).One(v)
}

func (m *Client) FindAll(v interface{}) error {
	return m.c.Find(nil).All(v)
}

func (m *Client) FindByValue(q interface{}, v interface{}) error {
	return m.c.Find(q).All(v)
}

func (m *Client) FindByValueSorted(q interface{}, v interface{}, fields ...string) error {
	return m.c.Find(q).Sort(fields...).All(v)
}

func (m *Client) FindById(objectId string, v interface{}) error {
	if !bson.IsObjectIdHex(objectId) {
		return errors.New("Invalid ObjectID format")
	}

	return m.c.FindId(bson.ObjectIdHex(objectId)).All(v)
}

func (m *Client) FindGroup(q interface{}, groupPipe bson.M, sortPipe bson.M, v interface{}) error {
	pipe := []bson.M{
		{"$match": q},
		{"$group": groupPipe},
	}

	if len(sortPipe) > 0 {
		return m.c.Pipe(append(pipe, bson.M{"$sort": sortPipe})).All(v)
	}

	return m.c.Pipe(pipe).All(v)
}

func (m *Client) FindRedact(q interface{}, redactPipe bson.M, sortPipe bson.M, v interface{}) error {
	pipe := []bson.M{
		{"$match": q},
		{"$redact": bson.M{
			"$cond": []interface{}{redactPipe, "$$KEEP", "$$PRUNE"},
		}},
	}

	if len(sortPipe) > 0 {
		return m.c.Pipe(append(pipe, bson.M{"$sort": sortPipe})).All(v)
	}

	return m.c.Pipe(pipe).All(v)
}

func (m *Client) DeleteById(objectId string) error {
	if !bson.IsObjectIdHex(objectId) {
		return errors.New("Invalid ObjectID format")
	}

	return m.c.RemoveId(bson.ObjectIdHex(objectId))
}

func (m *Client) DeleteBySlug(slug string, v interface{}) error {
	if bson.IsObjectIdHex(slug) {
		return m.c.RemoveId(bson.ObjectIdHex(slug))
	}

	s := core.NewSlug(v)
	s.SetValue(slug)
	return m.c.Remove(v)
}

func (m *Client) CreateCollection() error {
	cols, err := m.db.CollectionNames()
	if err != nil {
		return err
	}

	for _, collection := range cols {
		if collection == m.collection {
			return errors.New("Collection already exists")
		}
	}

	return m.c.Create(&mgo.CollectionInfo{})
}

func (m *Client) DropCollection() error {
	return m.c.DropCollection()
}
<commit_msg>Use consistent raw queries<commit_after>package mongo

import (
	"errors"
	"github.com/gobly/core"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"runtime"
)

type Client struct {
	db         *mgo.Database
	c          *mgo.Collection
	session    *mgo.Session
	collection string
}

func (m *Client) Connect(url string, db string, collection string) error {
	session, err := mgo.Dial(url)

	if err != nil {
		return err
	}

	session.SetMode(mgo.Monotonic, true)

	m.db = session.DB(db)
	m.c = m.db.C(collection)
	m.session = session
	m.collection = collection

	runtime.SetFinalizer(m, func(m *Client) { m.session.Close() })
	return nil
}

func (m *Client) Insert(v interface{}) error {
	objectId := NewObjectId(v)
	oid, ok := objectId.Value()
	if !ok {
		objectId.SetValue(oid)
	}
	err := m.c.Insert(v)
	return err
}

func (m *Client) Update(v interface{}) error {
	objectId := NewObjectId(v)
	oid, ok := objectId.Value()
	if !ok {
		return errors.New("Missing objectID")
	}

	return m.c.UpdateId(oid, v)
}

func (m *Client) UpdateRawFiltered(f bson.M, q bson.M) error {
	return m.c.Update(f, q)
}

func (m *Client) UpdateRaw(q bson.M) error {
	return m.c.Update(nil, q)
}

func (m *Client) ReadByValueFiltered(f interface{}, v interface{}) error {
	return m.c.Find(v).Select(f).One(v)
}

func (m *Client) ReadByValue(v interface{}) error {
	return m.c.Find(v).One(v)
}

func (m *Client) ReadRaw(q bson.M, v interface{}) error {
	return m.c.Find(q).One(v)
}

func (m *Client) ReadByID(objectId string, v interface{}) error {
	if !bson.IsObjectIdHex(objectId) {
		return errors.New("Invalid ObjectID format")
	}

	return m.c.FindId(bson.ObjectIdHex(objectId)).One(v)
}

func (m *Client) ReadBySlug(slug string, v interface{}) error {
	if bson.IsObjectIdHex(slug) {
		return m.c.FindId(bson.ObjectIdHex(slug)).One(v)
	}

	s := core.NewSlug(v)
	s.SetValue(slug)
	return m.c.Find(v).One(v)
}

func (m *Client) FindAll(v interface{}) error {
	return m.c.Find(nil).All(v)
}

func (m *Client) FindByValue(q interface{}, v interface{}) error {
	return m.c.Find(q).All(v)
}

func (m *Client) FindByValueSorted(q interface{}, v interface{}, fields ...string) error {
	return m.c.Find(q).Sort(fields...).All(v)
}

func (m *Client) FindById(objectId string, v interface{}) error {
	if !bson.IsObjectIdHex(objectId) {
		return errors.New("Invalid ObjectID format")
	}

	return m.c.FindId(bson.ObjectIdHex(objectId)).All(v)
}

func (m *Client) FindGroup(q interface{}, groupPipe bson.M, sortPipe bson.M, v interface{}) error {
	pipe := []bson.M{
		{"$match": q},
		{"$group": groupPipe},
	}

	if len(sortPipe) > 0 {
		return m.c.Pipe(append(pipe, bson.M{"$sort": sortPipe})).All(v)
	}

	return m.c.Pipe(pipe).All(v)
}

func (m *Client) FindRedact(q interface{}, redactPipe bson.M, sortPipe bson.M, v interface{}) error {
	pipe := []bson.M{
		{"$match": q},
		{"$redact": bson.M{
			"$cond": []interface{}{redactPipe, "$$KEEP", "$$PRUNE"},
		}},
	}

	if len(sortPipe) > 0 {
		return m.c.Pipe(append(pipe, bson.M{"$sort": sortPipe})).All(v)
	}

	return m.c.Pipe(pipe).All(v)
}

func (m *Client) DeleteById(objectId string) error {
	if !bson.IsObjectIdHex(objectId) {
		return errors.New("Invalid ObjectID format")
	}

	return m.c.RemoveId(bson.ObjectIdHex(objectId))
}

func (m *Client) DeleteBySlug(slug string, v interface{}) error {
	if bson.IsObjectIdHex(slug) {
		return m.c.RemoveId(bson.ObjectIdHex(slug))
	}

	s := core.NewSlug(v)
	s.SetValue(slug)
	return m.c.Remove(v)
}

func (m *Client) CreateCollection() error {
	cols, err := m.db.CollectionNames()
	if err != nil {
		return err
	}

	for _, collection := range cols {
		if collection == m.collection {
			return errors.New("Collection already exists")
		}
	}

	return m.c.Create(&mgo.CollectionInfo{})
}

func (m *Client) DropCollection() error {
	return m.c.DropCollection()
}
<|endoftext|>


<commit_before>// Copyright 2016 Afshin Darian. All rights reserved.
// Use of this source code is governed by The MIT License
// that can be found in the LICENSE file.

package sleuth

import (
	"net/http"
	"sync"
	"time"

	"github.com/pborman/uuid"
	"github.com/ursiform/logger"
	"github.com/zeromq/gyre"
)

type listener struct {
	*sync.Mutex
	handles map[string]chan *http.Response
}

type notifier struct {
	*sync.Mutex
	notify chan struct{}
}

// Client is the peer on the sleuth network that makes requests and, if a
// handler has been provided, responds to peer requests.
type Client struct {
	// Timeout is the duration to wait before an outstanding request times out.
	// By default, it is set to 500ms.
	Timeout time.Duration

	additions *notifier
	handler   http.Handler
	listener  *listener
	log       *logger.Logger
	node      *gyre.Gyre

	directory map[string]string   // map[node-name]service-type
	services  map[string]*workers // map[service-type]service-workers
}

func (c *Client) add(gid, name, node, service, version string) error {
	if gid != group {
		c.log.Debug("sleuth: no group header for %s, client-only", name)
		return nil
	}
	// Node and service are required. Version is optional.
	if len(node) == 0 || len(service) == 0 {
		return newError(errAdd, "failed to add %s node?=%t, type?=%t",
			name, len(node) > 0, len(service) > 0)
	}
	// Associate the node name with its service in the directory.
	c.directory[name] = service
	// Create a service workers collection if necessary.
	if c.services[service] == nil {
		c.services[service] = newWorkers()
	}
	// Add peer to the service workers.
	p := &peer{name: name, node: node, service: service, version: version}
	c.services[service].add(p)
	// If necessary, notify the additions channel that a peer has been added.
	if c.additions.notify != nil {
		c.additions.notify <- struct{}{}
	}
	c.log.Info("sleuth: add %s/%s %s to %s", service, version, name, group)
	return nil
}

// Returns true if it had to block and false if it returns immediately.
func (c *Client) block(services ...string) bool {
	// Block until the required services are available in the pool.
	c.additions.Lock()
	defer c.additions.Unlock()
	// Even though the client may have just checked to see if services exist,
	// the check is performed here in case there was a delay waiting for the
	// additions mutex to become available.
	if c.has(services...) {
		return false
	}
	c.log.Blocked("sleuth: waiting for client to find services %s", services)
	c.additions.notify = make(chan struct{})
	for range c.additions.notify {
		if c.has(services...) {
			break
		}
	}
	c.additions.notify = nil
	return true
}

// Close leaves the sleuth network and stops the Gyre node.
func (c *Client) Close() error {
	c.log.Info("%s leaving %s...", c.node.Name(), group)
	if err := c.node.Leave(group); err != nil {
		return newError(errLeave, err.Error())
	}
	if err := c.node.Stop(); err != nil {
		c.log.Warn("sleuth: %s %s [%d]",
			c.node.Name(), err.Error(), warnClose)
	}
	return nil
}

func (c *Client) dispatch(payload []byte) error {
	// Returned responses (RECV command) and outstanding requests (REPL command)
	// have these headers, respectively: SLEUTH-V0RECV and SLEUTH-V0REPL
	groupLength := len(group)
	dispatchLength := 4
	headerLength := groupLength + dispatchLength
	// If the message header does not match the group, bail.
	if len(payload) < headerLength || string(payload[0:groupLength]) != group {
		return newError(errDispatchHeader, "bad header")
	}
	action := string(payload[groupLength : groupLength+dispatchLength])
	switch action {
	case recv:
		return c.receive(payload[headerLength:])
	case repl:
		return c.reply(payload[headerLength:])
	default:
		return newError(errDispatchAction, "bad action: %s", action)
	}
}

// Do sends an HTTP request to a service and returns and HTTP response. The URL
// for requests needs to use the following format:
// 	sleuth://service-name/requested-path
// For example, a request to the path /bar?baz=qux of a service called
// foo-service would have the URL:
// 	sleuth://foo-service/bar?baz=qux
func (c *Client) Do(req *http.Request) (*http.Response, error) {
	handle := uuid.New()
	to := req.URL.Host
	if req.URL.Scheme != scheme {
		err := newError(errScheme,
			"URL scheme must be \"%s\" in %s", scheme, req.URL.String())
		return nil, err
	}
	services, ok := c.services[to]
	if !ok {
		return nil, newError(errUnknownService, "%s is an unknown service", to)
	}
	p := services.next()
	receiver := c.node.UUID()
	payload, err := marshalRequest(receiver, handle, req)
	if err != nil {
		return nil, err.(*Error).escalate(errRequest)
	}
	c.log.Debug("sleuth: %s %s://%s@%s%s",
		req.Method, scheme, to, p.name, req.URL.String())
	if err = c.node.Whisper(p.node, payload); err != nil {
		return nil, newError(errReqWhisper, err.Error())
	}
	listener := make(chan *http.Response, 1)
	c.listen(handle, listener)
	response := <-listener
	if response != nil {
		return response, nil
	}
	return nil, newError(errTimeout,
		"%s {%s}%s timed out", req.Method, to, req.URL.String())
}

func (c *Client) has(services ...string) bool {
	// Check to see if required services are already registered.
	verified := make(map[string]bool)
	available := 0
	for _, service := range services {
		verified[service] = false
	}
	total := len(verified)
	for service := range verified {
		if workers, ok := c.services[service]; ok && workers.available() {
			verified[service] = true
			available += 1
		}
	}
	return available == total
}

func (c *Client) listen(handle string, listener chan *http.Response) {
	c.listener.Lock()
	defer c.listener.Unlock()
	c.listener.handles[handle] = listener
	go c.timeout(handle)
}

func (c *Client) receive(payload []byte) error {
	handle, res, err := unmarshalResponse(payload)
	if err != nil {
		return err.(*Error).escalate(errRECV)
	}
	c.listener.Lock()
	defer c.listener.Unlock()
	if listener, ok := c.listener.handles[handle]; ok {
		listener <- res
		delete(c.listener.handles, handle)
	} else {
		return newError(errRECV, "unknown handle %s", handle)
	}
	return nil
}

func (c *Client) remove(name string) {
	if service, ok := c.directory[name]; ok {
		remaining, _ := c.services[service].remove(name)
		if remaining == 0 {
			delete(c.services, service)
		}
		delete(c.directory, name)
		c.log.Info("sleuth: remove %s (%s) from %s", service, name, group)
	}
}

func (c *Client) reply(payload []byte) error {
	dest, req, err := unmarshalRequest(payload)
	if err != nil {
		return err.(*Error).escalate(errREPL)
	}
	c.handler.ServeHTTP(newWriter(c.node, dest), req)
	return nil
}

func (c *Client) timeout(handle string) {
	<-time.After(c.Timeout)
	c.listener.Lock()
	defer c.listener.Unlock()
	if listener, ok := c.listener.handles[handle]; ok {
		listener <- nil
		delete(c.listener.handles, handle)
	}
}

// WaitFor blocks until the required services are available in the pool.
func (c *Client) WaitFor(services ...string) {
	if !c.has(services...) {
		c.block(services...)
	}
}

func newClient(node *gyre.Gyre, out *logger.Logger) *Client {
	return &Client{
		additions: &notifier{Mutex: new(sync.Mutex)},
		directory: make(map[string]string),
		listener: &listener{
			new(sync.Mutex),
			make(map[string]chan *http.Response)},
		log:      out,
		node:     node,
		Timeout:  time.Millisecond * 500,
		services: make(map[string]*workers)}
}
<commit_msg>trivial language tweak<commit_after>// Copyright 2016 Afshin Darian. All rights reserved.
// Use of this source code is governed by The MIT License
// that can be found in the LICENSE file.

package sleuth

import (
	"net/http"
	"sync"
	"time"

	"github.com/pborman/uuid"
	"github.com/ursiform/logger"
	"github.com/zeromq/gyre"
)

type listener struct {
	*sync.Mutex
	handles map[string]chan *http.Response
}

type notifier struct {
	*sync.Mutex
	notify chan struct{}
}

// Client is the peer on the sleuth network that makes requests and, if a
// handler has been provided, responds to peer requests.
type Client struct {
	// Timeout is the duration to wait before an outstanding request times out.
	// By default, it is set to 500ms.
	Timeout time.Duration

	additions *notifier
	handler   http.Handler
	listener  *listener
	log       *logger.Logger
	node      *gyre.Gyre

	directory map[string]string   // map[node-name]service-type
	services  map[string]*workers // map[service-type]service-workers
}

func (c *Client) add(gid, name, node, service, version string) error {
	if gid != group {
		c.log.Debug("sleuth: no group header for %s, client-only", name)
		return nil
	}
	// Node and service are required. Version is optional.
	if len(node) == 0 || len(service) == 0 {
		return newError(errAdd, "failed to add %s node?=%t, type?=%t",
			name, len(node) > 0, len(service) > 0)
	}
	// Associate the node name with its service in the directory.
	c.directory[name] = service
	// Create a service workers collection if necessary.
	if c.services[service] == nil {
		c.services[service] = newWorkers()
	}
	// Add peer to the service workers.
	p := &peer{name: name, node: node, service: service, version: version}
	c.services[service].add(p)
	// If necessary, notify the additions channel that a peer has been added.
	if c.additions.notify != nil {
		c.additions.notify <- struct{}{}
	}
	c.log.Info("sleuth: add %s/%s %s to %s", service, version, name, group)
	return nil
}

// Returns true if it had to block and false if it returns immediately.
func (c *Client) block(services ...string) bool {
	// Block until the required services are available to the client.
	c.additions.Lock()
	defer c.additions.Unlock()
	// Even though the client may have just checked to see if services exist,
	// the check is performed here in case there was a delay waiting for the
	// additions mutex to become available.
	if c.has(services...) {
		return false
	}
	c.log.Blocked("sleuth: waiting for client to find services %s", services)
	c.additions.notify = make(chan struct{})
	for range c.additions.notify {
		if c.has(services...) {
			break
		}
	}
	c.additions.notify = nil
	return true
}

// Close leaves the sleuth network and stops the Gyre node.
func (c *Client) Close() error {
	c.log.Info("%s leaving %s...", c.node.Name(), group)
	if err := c.node.Leave(group); err != nil {
		return newError(errLeave, err.Error())
	}
	if err := c.node.Stop(); err != nil {
		c.log.Warn("sleuth: %s %s [%d]",
			c.node.Name(), err.Error(), warnClose)
	}
	return nil
}

func (c *Client) dispatch(payload []byte) error {
	// Returned responses (RECV command) and outstanding requests (REPL command)
	// have these headers, respectively: SLEUTH-V0RECV and SLEUTH-V0REPL
	groupLength := len(group)
	dispatchLength := 4
	headerLength := groupLength + dispatchLength
	// If the message header does not match the group, bail.
	if len(payload) < headerLength || string(payload[0:groupLength]) != group {
		return newError(errDispatchHeader, "bad header")
	}
	action := string(payload[groupLength : groupLength+dispatchLength])
	switch action {
	case recv:
		return c.receive(payload[headerLength:])
	case repl:
		return c.reply(payload[headerLength:])
	default:
		return newError(errDispatchAction, "bad action: %s", action)
	}
}

// Do sends an HTTP request to a service and returns and HTTP response. The URL
// for requests needs to use the following format:
// 	sleuth://service-name/requested-path
// For example, a request to the path /bar?baz=qux of a service called
// foo-service would have the URL:
// 	sleuth://foo-service/bar?baz=qux
func (c *Client) Do(req *http.Request) (*http.Response, error) {
	handle := uuid.New()
	to := req.URL.Host
	if req.URL.Scheme != scheme {
		err := newError(errScheme,
			"URL scheme must be \"%s\" in %s", scheme, req.URL.String())
		return nil, err
	}
	services, ok := c.services[to]
	if !ok {
		return nil, newError(errUnknownService, "%s is an unknown service", to)
	}
	p := services.next()
	receiver := c.node.UUID()
	payload, err := marshalRequest(receiver, handle, req)
	if err != nil {
		return nil, err.(*Error).escalate(errRequest)
	}
	c.log.Debug("sleuth: %s %s://%s@%s%s",
		req.Method, scheme, to, p.name, req.URL.String())
	if err = c.node.Whisper(p.node, payload); err != nil {
		return nil, newError(errReqWhisper, err.Error())
	}
	listener := make(chan *http.Response, 1)
	c.listen(handle, listener)
	response := <-listener
	if response != nil {
		return response, nil
	}
	return nil, newError(errTimeout,
		"%s {%s}%s timed out", req.Method, to, req.URL.String())
}

func (c *Client) has(services ...string) bool {
	// Check to see if required services are already registered.
	verified := make(map[string]bool)
	available := 0
	for _, service := range services {
		verified[service] = false
	}
	total := len(verified)
	for service := range verified {
		if workers, ok := c.services[service]; ok && workers.available() {
			verified[service] = true
			available += 1
		}
	}
	return available == total
}

func (c *Client) listen(handle string, listener chan *http.Response) {
	c.listener.Lock()
	defer c.listener.Unlock()
	c.listener.handles[handle] = listener
	go c.timeout(handle)
}

func (c *Client) receive(payload []byte) error {
	handle, res, err := unmarshalResponse(payload)
	if err != nil {
		return err.(*Error).escalate(errRECV)
	}
	c.listener.Lock()
	defer c.listener.Unlock()
	if listener, ok := c.listener.handles[handle]; ok {
		listener <- res
		delete(c.listener.handles, handle)
	} else {
		return newError(errRECV, "unknown handle %s", handle)
	}
	return nil
}

func (c *Client) remove(name string) {
	if service, ok := c.directory[name]; ok {
		remaining, _ := c.services[service].remove(name)
		if remaining == 0 {
			delete(c.services, service)
		}
		delete(c.directory, name)
		c.log.Info("sleuth: remove %s (%s) from %s", service, name, group)
	}
}

func (c *Client) reply(payload []byte) error {
	dest, req, err := unmarshalRequest(payload)
	if err != nil {
		return err.(*Error).escalate(errREPL)
	}
	c.handler.ServeHTTP(newWriter(c.node, dest), req)
	return nil
}

func (c *Client) timeout(handle string) {
	<-time.After(c.Timeout)
	c.listener.Lock()
	defer c.listener.Unlock()
	if listener, ok := c.listener.handles[handle]; ok {
		listener <- nil
		delete(c.listener.handles, handle)
	}
}

// WaitFor blocks until the required services are available to the client.
func (c *Client) WaitFor(services ...string) {
	if !c.has(services...) {
		c.block(services...)
	}
}

func newClient(node *gyre.Gyre, out *logger.Logger) *Client {
	return &Client{
		additions: &notifier{Mutex: new(sync.Mutex)},
		directory: make(map[string]string),
		listener: &listener{
			new(sync.Mutex),
			make(map[string]chan *http.Response)},
		log:      out,
		node:     node,
		Timeout:  time.Millisecond * 500,
		services: make(map[string]*workers)}
}
<|endoftext|>