<commit_before>package apns2

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"

	"golang.org/x/net/http2"
)

// Apple Development and Production URLs
const (
	Development = "https://api.development.push.apple.com"
	Production  = "https://api.push.apple.com"
)

type ApnsResponse struct {
	ApnsID string `json:"apns-id,omitempty"`
	Reason string `json:"reason,omitempty"`
}

type ErrorResponse struct {
	Reason    string `json:"reason,omitempty"`
	Timestamp int64  `json:"timestamp,omitempty"`
}

// Client struct with HTTPClient and Certificate as parameters
type Client struct {
	HTTPClient  *http.Client
	Certificate tls.Certificate
	Host        string
}

// NewClient constructor tls.Certificate parameter
func NewClient(certificate tls.Certificate, host string) (*Client, error) {
	config := &tls.Config{
		Certificates: []tls.Certificate{certificate},
	}

	config.BuildNameToCertificate()

	transport := &http.Transport{TLSClientConfig: config}

	if err := http2.ConfigureTransport(transport); err != nil {
		return nil, err
	}

	client := &Client{
		HTTPClient:  &http.Client{Transport: transport},
		Certificate: certificate,
		Host:        host,
	}

	return client, nil
}

// SendPush a push notification with payload []byte and device token
func (c *Client) SendPush(payload []byte, deviceToken string, headers *Headers) (*ApnsResponse, error) {

	url := fmt.Sprintf("%v/3/device/%v", c.Host, deviceToken)

	req, err := http.NewRequest("POST", url, bytes.NewReader(payload))
	if err != nil {
		log.Fatal(err)
	}

	// Send JSON Headers
	headers.Set(req.Header)

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

	if err != nil {
		return nil, err
	}

	apnsResponse := ApnsResponse{}

	if resp.StatusCode == http.StatusOK {
		apnsResponse.ApnsID = resp.Header.Get("apns-id")
	}

	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		return nil, err
	}

	var errorResponse ErrorResponse
	json.Unmarshal(body, &errorResponse)

	if errorResponse.Reason != "" {
		apnsResponse.Reason = errorReason[errorResponse.Reason]
	}

	return &apnsResponse, nil
}
<commit_msg>refactor<commit_after>package apns2

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"

	"golang.org/x/net/http2"
)

// Apple Development and Production URLs
const (
	Development = "https://api.development.push.apple.com"
	Production  = "https://api.push.apple.com"
)

// ApnsResponse contains apns-id, reason
type ApnsResponse struct {
	ApnsID string `json:"apns-id,omitempty"`
	Reason string `json:"reason,omitempty"`
}

// ErrorResponse contains reason, timestamp
type ErrorResponse struct {
	Reason    string `json:"reason,omitempty"`
	Timestamp int64  `json:"timestamp,omitempty"`
}

// Client struct with HTTPClient and Certificate as parameters
type Client struct {
	HTTPClient  *http.Client
	Certificate tls.Certificate
	Host        string
}

// NewClient constructor tls.Certificate parameter
func NewClient(certificate tls.Certificate, host string) (*Client, error) {
	config := &tls.Config{
		Certificates: []tls.Certificate{certificate},
	}

	config.BuildNameToCertificate()

	transport := &http.Transport{TLSClientConfig: config}

	if err := http2.ConfigureTransport(transport); err != nil {
		return nil, err
	}

	client := &Client{
		HTTPClient:  &http.Client{Transport: transport},
		Certificate: certificate,
		Host:        host,
	}

	return client, nil
}

// SendPush a push notification with payload ([]byte), device token, *Headers
// returns ApnsResponse
func (c *Client) SendPush(payload []byte, deviceToken string, headers *Headers) (*ApnsResponse, error) {

	url := fmt.Sprintf("%v/3/device/%v", c.Host, deviceToken)

	req, err := http.NewRequest("POST", url, bytes.NewReader(payload))
	if err != nil {
		log.Fatal(err)
	}

	// Send headers
	headers.Set(req.Header)

	resp, err := c.HTTPClient.Do(req)

	if err != nil {
		return nil, err
	}

	apnsResponse := ApnsResponse{}

	if resp.StatusCode == http.StatusOK {
		apnsResponse.ApnsID = resp.Header.Get("apns-id")
	}

	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		return nil, err
	}

	var errorResponse ErrorResponse
	json.Unmarshal(body, &errorResponse)

	if errorResponse.Reason != "" {
		apnsResponse.Reason = errorReason[errorResponse.Reason]
	}

	return &apnsResponse, nil
}
<|endoftext|>


<commit_before>package sirius

import (
	"fmt"
	"golang.org/x/net/context"
	"reflect"
	"strings"
	"time"
)

type Client struct {
	user   *User
	conn   Connection
	loader ExtensionLoader
}

func NewClient(user *User, loader ExtensionLoader) *Client {
	conn := NewRTMConnection(user.Token)

	return &Client{
		conn:   conn,
		user:   user,
		loader: loader,
	}
}

func (c *Client) Start(ctx context.Context) {
	go c.conn.Listen()

	for {
		select {
		case msg := <-c.conn.Messages():
			c.handleMessage(&msg)
		}
	}
}

func (c *Client) handleMessage(msg *Message) {
	if !c.isSender(msg) {
		return
	}

	if msg.escaped() {
		msg.Text = trimEscape(msg.Text)
		c.conn.Update(msg)
	}

	act := c.runExtensions(msg)
	c.applyActions(act, msg)
}

func (c *Client) runExtensions(msg *Message) []MessageAction {
	cfgs := c.user.Configurations
	act := make(chan MessageAction, len(cfgs))

	for _, cfg := range cfgs {
		err, ext := c.loader.Load(cfg.EID)

		if err != nil {
			panic(err)
		}

		execute(ext, msg, act)
	}

	var actions []MessageAction

ActionReceive:
	for range cfgs {
		select {
		case a := <-act:
			actions = append(actions, a)

		// Allow extensions max 2s to execute and provide an actionable result
		case <-time.After(time.Second * 2):
			break ActionReceive
		}
	}

	return actions
}

func (c *Client) applyActions(act []MessageAction, msg *Message) {
	oldText := msg.Text

	for _, a := range act {
		err := a.Perform(msg)

		if err != nil {
			panic(err)
		}
	}

	if msg.Text != oldText {
		c.conn.Update(msg)
	}
}

func (c *Client) isSender(msg *Message) bool {
	err, id := c.conn.ID()

	if err != nil {
		panic(err)
	}

	return msg.UserID.Equals(&id)
}

func (m *Message) escaped() bool {
	return strings.HasPrefix(m.Text, `\`)
}

func trimEscape(text string) string {
	return strings.TrimPrefix(text, `\`)
}

/*
Executes ext(msg) and passes the results onto act
*/
func execute(ext Extension, msg *Message, act chan<- MessageAction) {
	go func() {
		err, a := ext.Run(*msg, ExtensionConfig{})

		if err != nil {
			fmt.Printf("[%s]: %v\n", reflect.TypeOf(ext), err)
			return
		}

		act <- a
	}()
}
<commit_msg>Fix bug where extensions would be executed on escaped messages<commit_after>package sirius

import (
	"fmt"
	"golang.org/x/net/context"
	"reflect"
	"strings"
	"time"
)

type Client struct {
	user   *User
	conn   Connection
	loader ExtensionLoader
}

func NewClient(user *User, loader ExtensionLoader) *Client {
	conn := NewRTMConnection(user.Token)

	return &Client{
		conn:   conn,
		user:   user,
		loader: loader,
	}
}

func (c *Client) Start(ctx context.Context) {
	go c.conn.Listen()

	for {
		select {
		case msg := <-c.conn.Messages():
			c.handleMessage(&msg)
		}
	}
}

func (c *Client) handleMessage(msg *Message) {
	if !c.isSender(msg) {
		return
	}

	if msg.escaped() {
		msg.Text = trimEscape(msg.Text)
		c.conn.Update(msg)
		return
	}

	act := c.runExtensions(msg)
	c.applyActions(act, msg)
}

func (c *Client) runExtensions(msg *Message) []MessageAction {
	cfgs := c.user.Configurations
	act := make(chan MessageAction, len(cfgs))

	for _, cfg := range cfgs {
		err, ext := c.loader.Load(cfg.EID)

		if err != nil {
			panic(err)
		}

		execute(ext, msg, act)
	}

	var actions []MessageAction

ActionReceive:
	for range cfgs {
		select {
		case a := <-act:
			actions = append(actions, a)

		// Allow extensions max 2s to execute and provide an actionable result
		case <-time.After(time.Second * 2):
			break ActionReceive
		}
	}

	return actions
}

func (c *Client) applyActions(act []MessageAction, msg *Message) {
	oldText := msg.Text

	for _, a := range act {
		err := a.Perform(msg)

		if err != nil {
			panic(err)
		}
	}

	if msg.Text != oldText {
		c.conn.Update(msg)
	}
}

func (c *Client) isSender(msg *Message) bool {
	err, id := c.conn.ID()

	if err != nil {
		panic(err)
	}

	return msg.UserID.Equals(&id)
}

func (m *Message) escaped() bool {
	return strings.HasPrefix(m.Text, `\`)
}

func trimEscape(text string) string {
	return strings.TrimPrefix(text, `\`)
}

/*
Executes ext(msg) and passes the results onto act
*/
func execute(ext Extension, msg *Message, act chan<- MessageAction) {
	go func() {
		err, a := ext.Run(*msg, ExtensionConfig{})

		if err != nil {
			fmt.Printf("[%s]: %v\n", reflect.TypeOf(ext), err)
			return
		}

		act <- a
	}()
}
<|endoftext|>


<commit_before>package apns

import (
	"crypto/tls"
	"errors"
	"fmt"
	"net"
	"strings"
	"time"
)

// Client contains the fields necessary to communicate
// with Apple, such as the gateway to use and your
// certificate contents.
//
// You'll need to provide your own CertificateFile
// and KeyFile to send notifications. Ideally, you'll
// just set the CertificateFile and KeyFile fields to
// a location on drive where the certs can be loaded,
// but if you prefer you can use the CertificateBase64
// and KeyBase64 fields to store the actual contents.
type Client struct {
	Gateway           string
	CertificateFile   string
	CertificateBase64 string
	KeyFile           string
	KeyBase64         string
	certificate       tls.Certificate
	apnsConnection    *tls.Conn
}

// BareClient can be used to set the contents of your
// certificate and key blocks manually.
func BareClient(gateway, certificateBase64, keyBase64 string) (c *Client) {
	c = new(Client)
	c.Gateway = gateway
	c.CertificateBase64 = certificateBase64
	c.KeyBase64 = keyBase64
	return
}

// NewClient assumes you'll be passing in paths that
// point to your certificate and key.
func NewClient(gateway, certificateFile, keyFile string) (c *Client) {
	c = new(Client)
	c.Gateway = gateway
	c.CertificateFile = certificateFile
	c.KeyFile = keyFile
	return
}

// Send connects to the APN service and sends your push notification.
// Remember that if the submission is successful, Apple won't reply.
func (client *Client) Send(pn *PushNotification) (resp *PushNotificationResponse) {
	resp = new(PushNotificationResponse)

	payload, err := pn.ToBytes()
	if err != nil {
		resp.Success = false
		resp.Error = err
		return
	}

	err = client.ConnectAndWrite(resp, payload)
	if err != nil {
		resp.Success = false
		resp.Error = err
		return
	}

	resp.Success = true
	resp.Error = nil

	return
}

// ConnectAndWrite establishes the connection to Apple and handles the
// transmission of your push notification, as well as waiting for a reply.
//
// In lieu of a timeout (which would be available in Go 1.1)
// we use a timeout channel pattern instead. We start two goroutines,
// one of which just sleeps for TimeoutSeconds seconds, while the other
// waits for a response from the Apple servers.
//
// Whichever channel puts data on first is the "winner". As such, it's
// possible to get a false positive if Apple takes a long time to respond.
// It's probably not a deal-breaker, but something to be aware of.
func (client *Client) ConnectAndWrite(resp *PushNotificationResponse, payload []byte) error {
	var bytesWritten int
	var err error

	if client.apnsConnection == nil {
		// We want to re-establish the connection to APNS after a number of messages sent
		err = client.openConnection()
		if err != nil {
			return err
		}
	}

	bytesWritten, err = client.apnsConnection.Write(payload)
	if err != nil {
		if err.Error() != "use of closed network connection" && err.Error() != "EOF" && err.Error() != fmt.Sprintf("write tcp %s: broken pipe", client.Gateway) {
			return err
		}

		// If the connection is closed, reconnect
		err = client.openConnection()
		if err != nil {
			return err
		}

		bytesWritten, err = client.apnsConnection.Write(payload)
		if err != nil {
			return err
		}
		if bytesWritten == 0 {
			client.apnsConnection.Close()
			return fmt.Errorf("Could not open connection to %s.  Please try again.", client.Gateway)
		}
	}

	// Create one channel that will serve to handle
	// timeouts when the notification succeeds.
	timeoutChannel := make(chan bool, 1)
	go func() {
		time.Sleep(time.Second * TimeoutSeconds)
		timeoutChannel <- true
	}()

	// This channel will contain the binary response
	// from Apple in the event of a failure.
	responseChannel := make(chan []byte, 1)
	go func() {
		buffer := make([]byte, 6, 6)
		client.apnsConnection.Read(buffer)
		responseChannel <- buffer
	}()

	// First one back wins!
	// The data structure for an APN response is as follows:
	//
	// command    -> 1 byte
	// status     -> 1 byte
	// identifier -> 4 bytes
	//
	// The first byte will always be set to 8.
	select {
	case r := <-responseChannel:
		resp.Success = false
		resp.AppleResponse = ApplePushResponses[r[1]]
		err = errors.New(resp.AppleResponse)
	case <-timeoutChannel:
		resp.Success = true
	}

	return err
}

// Opens a connection to the Apple APNS server
// The connection is created and persisted to the client's apnsConnection property
//	to save on the overhead of the crypto libraries.
func (client *Client) openConnection() error {
	if client.apnsConnection != nil {
		client.apnsConnection.Close()
	}

	err := client.getCertificate()
	if err != nil {
		return err
	}

	conf := &tls.Config{
		Certificates: []tls.Certificate{client.certificate},
	}

	conn, err := net.Dial("tcp", client.Gateway)
	if err != nil {
		return err
	}

	tlsConn := tls.Client(conn, conf)
	err = tlsConn.Handshake()
	if err != nil {
		return err
	}

	client.apnsConnection = tlsConn
	return nil
}

// Returns a certificate to use to send the notification.
// The certificate is only created once to save on
// the overhead of the crypto libraries.
func (client *Client) getCertificate() error {
	var err error

	if client.certificate.PrivateKey == nil {
		if len(client.CertificateBase64) == 0 && len(client.KeyBase64) == 0 {
			// The user did not specify raw block contents, so check the filesystem.
			client.certificate, err = tls.LoadX509KeyPair(client.CertificateFile, client.KeyFile)
		} else {
			// The user provided the raw block contents, so use that.
			client.certificate, err = tls.X509KeyPair([]byte(client.CertificateBase64), []byte(client.KeyBase64))
		}
	}

	return err
}
<commit_msg>removed unused library strings<commit_after>package apns

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

// Client contains the fields necessary to communicate
// with Apple, such as the gateway to use and your
// certificate contents.
//
// You'll need to provide your own CertificateFile
// and KeyFile to send notifications. Ideally, you'll
// just set the CertificateFile and KeyFile fields to
// a location on drive where the certs can be loaded,
// but if you prefer you can use the CertificateBase64
// and KeyBase64 fields to store the actual contents.
type Client struct {
	Gateway           string
	CertificateFile   string
	CertificateBase64 string
	KeyFile           string
	KeyBase64         string
	certificate       tls.Certificate
	apnsConnection    *tls.Conn
}

// BareClient can be used to set the contents of your
// certificate and key blocks manually.
func BareClient(gateway, certificateBase64, keyBase64 string) (c *Client) {
	c = new(Client)
	c.Gateway = gateway
	c.CertificateBase64 = certificateBase64
	c.KeyBase64 = keyBase64
	return
}

// NewClient assumes you'll be passing in paths that
// point to your certificate and key.
func NewClient(gateway, certificateFile, keyFile string) (c *Client) {
	c = new(Client)
	c.Gateway = gateway
	c.CertificateFile = certificateFile
	c.KeyFile = keyFile
	return
}

// Send connects to the APN service and sends your push notification.
// Remember that if the submission is successful, Apple won't reply.
func (client *Client) Send(pn *PushNotification) (resp *PushNotificationResponse) {
	resp = new(PushNotificationResponse)

	payload, err := pn.ToBytes()
	if err != nil {
		resp.Success = false
		resp.Error = err
		return
	}

	err = client.ConnectAndWrite(resp, payload)
	if err != nil {
		resp.Success = false
		resp.Error = err
		return
	}

	resp.Success = true
	resp.Error = nil

	return
}

// ConnectAndWrite establishes the connection to Apple and handles the
// transmission of your push notification, as well as waiting for a reply.
//
// In lieu of a timeout (which would be available in Go 1.1)
// we use a timeout channel pattern instead. We start two goroutines,
// one of which just sleeps for TimeoutSeconds seconds, while the other
// waits for a response from the Apple servers.
//
// Whichever channel puts data on first is the "winner". As such, it's
// possible to get a false positive if Apple takes a long time to respond.
// It's probably not a deal-breaker, but something to be aware of.
func (client *Client) ConnectAndWrite(resp *PushNotificationResponse, payload []byte) error {
	var bytesWritten int
	var err error

	if client.apnsConnection == nil {
		// We want to re-establish the connection to APNS after a number of messages sent
		err = client.openConnection()
		if err != nil {
			return err
		}
	}

	bytesWritten, err = client.apnsConnection.Write(payload)
	if err != nil {
		if err.Error() != "use of closed network connection" && err.Error() != "EOF" && err.Error() != fmt.Sprintf("write tcp %s: broken pipe", client.Gateway) {
			return err
		}

		// If the connection is closed, reconnect
		err = client.openConnection()
		if err != nil {
			return err
		}

		bytesWritten, err = client.apnsConnection.Write(payload)
		if err != nil {
			return err
		}
		if bytesWritten == 0 {
			client.apnsConnection.Close()
			return fmt.Errorf("Could not open connection to %s.  Please try again.", client.Gateway)
		}
	}

	// Create one channel that will serve to handle
	// timeouts when the notification succeeds.
	timeoutChannel := make(chan bool, 1)
	go func() {
		time.Sleep(time.Second * TimeoutSeconds)
		timeoutChannel <- true
	}()

	// This channel will contain the binary response
	// from Apple in the event of a failure.
	responseChannel := make(chan []byte, 1)
	go func() {
		buffer := make([]byte, 6, 6)
		client.apnsConnection.Read(buffer)
		responseChannel <- buffer
	}()

	// First one back wins!
	// The data structure for an APN response is as follows:
	//
	// command    -> 1 byte
	// status     -> 1 byte
	// identifier -> 4 bytes
	//
	// The first byte will always be set to 8.
	select {
	case r := <-responseChannel:
		resp.Success = false
		resp.AppleResponse = ApplePushResponses[r[1]]
		err = errors.New(resp.AppleResponse)
	case <-timeoutChannel:
		resp.Success = true
	}

	return err
}

// Opens a connection to the Apple APNS server
// The connection is created and persisted to the client's apnsConnection property
//	to save on the overhead of the crypto libraries.
func (client *Client) openConnection() error {
	if client.apnsConnection != nil {
		client.apnsConnection.Close()
	}

	err := client.getCertificate()
	if err != nil {
		return err
	}

	conf := &tls.Config{
		Certificates: []tls.Certificate{client.certificate},
	}

	conn, err := net.Dial("tcp", client.Gateway)
	if err != nil {
		return err
	}

	tlsConn := tls.Client(conn, conf)
	err = tlsConn.Handshake()
	if err != nil {
		return err
	}

	client.apnsConnection = tlsConn
	return nil
}

// Returns a certificate to use to send the notification.
// The certificate is only created once to save on
// the overhead of the crypto libraries.
func (client *Client) getCertificate() error {
	var err error

	if client.certificate.PrivateKey == nil {
		if len(client.CertificateBase64) == 0 && len(client.KeyBase64) == 0 {
			// The user did not specify raw block contents, so check the filesystem.
			client.certificate, err = tls.LoadX509KeyPair(client.CertificateFile, client.KeyFile)
		} else {
			// The user provided the raw block contents, so use that.
			client.certificate, err = tls.X509KeyPair([]byte(client.CertificateBase64), []byte(client.KeyBase64))
		}
	}

	return err
}
<|endoftext|>


<commit_before>package goio

import (
	"sync"
	"time"
)

type Client struct {
	Event
	Id            string
	UserId        string
	Messages      []*Message
	LastHandshake int64
	LifeCycle     int64
	lock          sync.RWMutex
}

func (self *Client) CleanMessages() {
	self.lock.Lock()
	defer self.lock.Unlock()

	self.Messages = make([]*Message, 0)
}

func (self *Client) Receive(message *Message) {
	self.lock.Lock()
	defer self.lock.Unlock()

	self.Messages = append(self.Messages, message)
}

func (self *Client) Destroy() {
	self.Emit("destroy", nil)
}

func (self *Client) Handshake() {
	self.lock.Lock()
	defer self.lock.Unlock()

	self.LastHandshake = time.Now().Unix()
}

func (self *Client) LifeRemain() int64 {
	remain := self.LifeCycle - (time.Now().Unix() - self.LastHandshake)
	if 0 >= remain {
		return 0
	}

	return remain
}

func (self *Client) IsLive() bool {
	return 0 < self.LifeRemain()
}
<commit_msg>limit the max count of messages in client<commit_after>package goio

import (
	"sync"
	"time"
)

type Client struct {
	Event
	Id            string
	UserId        string
	Messages      []*Message
	LastHandshake int64
	LifeCycle     int64
	lock          sync.RWMutex
}

func (self *Client) CleanMessages() {
	self.lock.Lock()
	defer self.lock.Unlock()

	//make a default cap value, it's enough for most use
	self.Messages = make([]*Message, 0, 20)
}

func (self *Client) Receive(message *Message) {
	self.lock.Lock()
	defer self.lock.Unlock()

	// if there are too many messages, we limit the max to 80
	if len(self.Messages) >= 80 {
		self.Messages = self.Messages[1:]
	}

	self.Messages = append(self.Messages, message)
}

func (self *Client) Destroy() {
	self.Emit("destroy", nil)
}

func (self *Client) Handshake() {
	self.lock.Lock()
	defer self.lock.Unlock()

	self.LastHandshake = time.Now().Unix()
}

func (self *Client) LifeRemain() int64 {
	remain := self.LifeCycle - (time.Now().Unix() - self.LastHandshake)
	if 0 >= remain {
		return 0
	}

	return remain
}

func (self *Client) IsLive() bool {
	return 0 < self.LifeRemain()
}
<|endoftext|>


<commit_before>package phosphor

import (
	"errors"
	"fmt"
	"sync"
	"time"

	log "github.com/cihub/seelog"
	"github.com/davegardnerisme/deephash"
	"github.com/golang/protobuf/proto"
	phos "github.com/mondough/phosphor/proto"
)

const (
	configForceReloadTime  = 1 * time.Minute
	defaultTraceBufferSize = 250 // 64KB max per trace, default max 16MB mem usage
)

var (
	// ErrTimeout represents a timeout error while attempting to send
	ErrTimeout = errors.New("timeout queueing annotation")
)

var (
	tracer *traceClient
)

// Phosphor is a client which sends annotations to the phosphor server. This
// should be initialised with New() rather than directly
type Phosphor struct {
	// ensure the client is initialized
	initOnce sync.Once

	// traceChan internally buffers traces and passes these from
	// producers to the writers
	traceChan chan []byte

	// configMtx guards access to the config provider, and hash of the currently
	// loaded configuration
	configMtx sync.RWMutex

	// configProvider which returns configuration for our client
	configProvider ConfigProvider

	// configLastHash is the hash of the most recently used configuration
	// we use this to determine if we need to reinitialise on notification of a
	// config change
	configLastHash []byte

	// exitChan is closed when the client is shutting down
	// TODO refactor to use tomb
	exitChan chan struct{}

	// the transport is used to send traces onto phosphor or phosphorD
	tr    transport
	trMtx sync.Mutex
}

// New initialises and returns a Phosphor client
// This takes a config provider which can be a simple static config,
// or a more dynamic configuration loader which watches a remote config source
func New(configProvider ConfigProvider) (*Phosphor, error) {
	configProvider.Config().assertInitialized()

	// // TODO validate config
	// c := configProvider.Config()

	p := &Phosphor{
		configProvider: configProvider,
		// initialise traceChan with default length, we'll replace this
		// asynchronously with one of the correct length on first use
		traceChan: make(chan []byte, defaultTraceBufferSize),
		exitChan:  make(chan struct{}),
	}

	return p, nil
}

// Send an annotation to Phosphor
func (p *Phosphor) Send(a *phos.Annotation) error {
	// Initialise the tracer on first use
	p.initOnce.Do(p.init)

	// Marshal to bytes to be sent on the wire
	//
	// We're marshaling this here so that the marshalling can be executed
	// concurrently by any number of clients before pushing this to a single
	// worker goroutine for transport
	//
	// TODO future versions of this may use a more feature rich wire format
	b, err := proto.Marshal(a)
	if err != nil {
		return err
	}

	c := p.configProvider.Config()

	select {
	case p.traceChan <- b:
	case <-time.After(c.SendTimeout):
		log.Tracef("Timeout after %v attempting to queue trace annotation: %+v", c.SendTimeout, a)
		return ErrTimeout
	}

	return nil
}

// init should be called precisely once to initialise the client
func (p *Phosphor) init() {
	// fire config monitor which will immediately reinitialise the config
	// TODO control this with a tomb
	go p.monitorConfig()
}

// monitorConfig observes the config provider and triggers a reload of the
// configuration both when notified of a change and when a minimum time period
// has elapsed
func (p *Phosphor) monitorConfig() {
	configChange := p.configProvider.Notify()
	configTimer := time.NewTicker(configForceReloadTime)
	immediate := make(chan struct{}, 1)
	immediate <- struct{}{}

	for {
		select {
		case <-p.exitChan:
			configTimer.Stop()
			return
		case <-immediate:
		case <-configChange:
		case <-configTimer.C:
		}
		if err := p.reloadConfig(); err != nil {
			log.Warnf("[Phosphor] Failed to reload configuration: %v", err)
		}
	}
}

func (p *Phosphor) compareConfigHash(h []byte) bool {
	p.configMtx.RLock()
	defer p.configMtx.RUnlock()
	return p.configLastHash == h
}

func (p *Phosphor) updateConfigHash(h []byte) {
	p.configMtx.Lock()
	p.configLastHash = h
	p.configMtx.Unlock()
	return
}

// reloadConfig and reinitialise phosphor client if necessary
//
// Get Config
// Test hash of config to determine if changed
// If so, update config & reinit
func (p *Phosphor) reloadConfig() error {
	c := p.configProvider.Config()
	h := deephash.Hash(c)

	// Skip reloading if the config is the same
	if p.compareConfigHash(newHash) {
		return nil
	}

	// keep reference to the old channel so we can drain this in parallel with
	// new traces the transport receives
	oldChan := p.traceChan

	// init new channel for traces, ensure this *isn't* zero
	bufLen := c.BufferSize
	if bufLen == 0 {
		bufLen = defaultTraceBufferSize
	}
	newChan := make(chan []byte, bufLen)

	// Get a new transport and keep a reference to the old one
	p.trMtx.Lock()
	defer p.trMtx.Unlock()
	oldTr := p.tr
	endpoint := fmt.Sprintf("%s:%v", c.Host, c.Port)
	newTr := newUDPTransport(endpoint)

	// start new transport by passing both channels to this
	// therefore it starts consuming from the new one (with nothing)
	// and also the old one (still current) in parallel to the previous transport
	// If this somehow fails, abort until next attempt
	if err := newTr.Consume(oldChan, newChan); err != nil {
		newTr.Stop()
		return err
	}

	// swap the client reference of the trace channel from old to new, so
	// new clients start using the new resized channel
	// TODO atomically swap this
	p.traceChan = newChan

	// gracefully shut down old transport, so just the new one is running
	if err := oldTr.Stop(); err != nil {
		return err
	}

	// set the config hash & swap the transport as we're finished
	p.updateConfigHash(h)
	p.tr = newTr

	return nil
}
<commit_msg>Fix hash comparison<commit_after>package phosphor

import (
	"errors"
	"fmt"
	"sync"
	"time"

	log "github.com/cihub/seelog"
	"github.com/davegardnerisme/deephash"
	"github.com/golang/protobuf/proto"
	phos "github.com/mondough/phosphor/proto"
)

const (
	configForceReloadTime  = 1 * time.Minute
	defaultTraceBufferSize = 250 // 64KB max per trace, default max 16MB mem usage
)

var (
	// ErrTimeout represents a timeout error while attempting to send
	ErrTimeout = errors.New("timeout queueing annotation")
)

var (
	tracer *traceClient
)

// Phosphor is a client which sends annotations to the phosphor server. This
// should be initialised with New() rather than directly
type Phosphor struct {
	// ensure the client is initialized
	initOnce sync.Once

	// traceChan internally buffers traces and passes these from
	// producers to the writers
	traceChan chan []byte

	// configMtx guards access to the config provider, and hash of the currently
	// loaded configuration
	configMtx sync.RWMutex

	// configProvider which returns configuration for our client
	configProvider ConfigProvider

	// configLastHash is the hash of the most recently used configuration
	// we use this to determine if we need to reinitialise on notification of a
	// config change
	configLastHash string

	// exitChan is closed when the client is shutting down
	// TODO refactor to use tomb
	exitChan chan struct{}

	// the transport is used to send traces onto phosphor or phosphorD
	tr    transport
	trMtx sync.Mutex
}

// New initialises and returns a Phosphor client
// This takes a config provider which can be a simple static config,
// or a more dynamic configuration loader which watches a remote config source
func New(configProvider ConfigProvider) (*Phosphor, error) {
	configProvider.Config().assertInitialized()

	// // TODO validate config
	// c := configProvider.Config()

	p := &Phosphor{
		configProvider: configProvider,
		// initialise traceChan with default length, we'll replace this
		// asynchronously with one of the correct length on first use
		traceChan: make(chan []byte, defaultTraceBufferSize),
		exitChan:  make(chan struct{}),
	}

	return p, nil
}

// Send an annotation to Phosphor
func (p *Phosphor) Send(a *phos.Annotation) error {
	// Initialise the tracer on first use
	p.initOnce.Do(p.init)

	// Marshal to bytes to be sent on the wire
	//
	// We're marshaling this here so that the marshalling can be executed
	// concurrently by any number of clients before pushing this to a single
	// worker goroutine for transport
	//
	// TODO future versions of this may use a more feature rich wire format
	b, err := proto.Marshal(a)
	if err != nil {
		return err
	}

	c := p.configProvider.Config()

	select {
	case p.traceChan <- b:
	case <-time.After(c.SendTimeout):
		log.Tracef("Timeout after %v attempting to queue trace annotation: %+v", c.SendTimeout, a)
		return ErrTimeout
	}

	return nil
}

// init should be called precisely once to initialise the client
func (p *Phosphor) init() {
	// fire config monitor which will immediately reinitialise the config
	// TODO control this with a tomb
	go p.monitorConfig()
}

// monitorConfig observes the config provider and triggers a reload of the
// configuration both when notified of a change and when a minimum time period
// has elapsed
func (p *Phosphor) monitorConfig() {
	configChange := p.configProvider.Notify()
	configTimer := time.NewTicker(configForceReloadTime)
	immediate := make(chan struct{}, 1)
	immediate <- struct{}{}

	for {
		select {
		case <-p.exitChan:
			configTimer.Stop()
			return
		case <-immediate:
		case <-configChange:
		case <-configTimer.C:
		}
		if err := p.reloadConfig(); err != nil {
			log.Warnf("[Phosphor] Failed to reload configuration: %v", err)
		}
	}
}

func (p *Phosphor) compareConfigHash(h string) bool {
	p.configMtx.RLock()
	defer p.configMtx.RUnlock()
	return p.configLastHash == h
}

func (p *Phosphor) updateConfigHash(h string) {
	p.configMtx.Lock()
	p.configLastHash = h
	p.configMtx.Unlock()
	return
}

// reloadConfig and reinitialise phosphor client if necessary
//
// Get Config
// Test hash of config to determine if changed
// If so, update config & reinit
func (p *Phosphor) reloadConfig() error {
	c := p.configProvider.Config()

	// Skip reloading if the config is the same
	h := fmt.Sprintf("%x", deephash.Hash(c))
	if p.compareConfigHash(h) {
		return nil
	}

	// keep reference to the old channel so we can drain this in parallel with
	// new traces the transport receives
	oldChan := p.traceChan

	// init new channel for traces, ensure this *isn't* zero
	bufLen := c.BufferSize
	if bufLen == 0 {
		bufLen = defaultTraceBufferSize
	}
	newChan := make(chan []byte, bufLen)

	// Get a new transport and keep a reference to the old one
	p.trMtx.Lock()
	defer p.trMtx.Unlock()
	oldTr := p.tr
	endpoint := fmt.Sprintf("%s:%v", c.Host, c.Port)
	newTr := newUDPTransport(endpoint)

	// start new transport by passing both channels to this
	// therefore it starts consuming from the new one (with nothing)
	// and also the old one (still current) in parallel to the previous transport
	// If this somehow fails, abort until next attempt
	if err := newTr.Consume(oldChan, newChan); err != nil {
		newTr.Stop()
		return err
	}

	// swap the client reference of the trace channel from old to new, so
	// new clients start using the new resized channel
	// TODO atomically swap this
	p.traceChan = newChan

	// gracefully shut down old transport, so just the new one is running
	if err := oldTr.Stop(); err != nil {
		return err
	}

	// set the config hash & swap the transport as we're finished
	p.updateConfigHash(h)
	p.tr = newTr

	return nil
}
<|endoftext|>


<commit_before>package bitbucket

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"

	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/bitbucket"
	"golang.org/x/oauth2/clientcredentials"
)

const DEFAULT_PAGE_LENGTH = 10
const DEFAULT_MAX_DEPTH = 1
const DEFAULT_BITBUCKET_API_BASE_URL = "https://api.bitbucket.org/2.0"

func apiBaseUrl() (*url.URL, error) {
	ev := os.Getenv("BITBUCKET_API_BASE_URL")
	if ev == "" {
		ev = DEFAULT_BITBUCKET_API_BASE_URL
	}

	return url.Parse(ev)
}

type Client struct {
	Auth         *auth
	Users        users
	User         user
	Teams        teams
	Repositories *Repositories
	Workspaces   *Workspace
	Pagelen      uint64
	MaxDepth     uint64
	apiBaseURL   *url.URL

	HttpClient *http.Client
}

type auth struct {
	appID, secret  string
	user, password string
	token          oauth2.Token
	bearerToken    string
}

type Response struct {
	Size     int           `json:"size"`
	Page     int           `json:"page"`
	Pagelen  int           `json:"pagelen"`
	Next     string        `json:"next"`
	Previous string        `json:"previous"`
	Values   []interface{} `json:"values"`
}

// Uses the Client Credentials Grant oauth2 flow to authenticate to Bitbucket
func NewOAuthClientCredentials(i, s string) *Client {
	a := &auth{appID: i, secret: s}
	ctx := context.Background()
	conf := &clientcredentials.Config{
		ClientID:     i,
		ClientSecret: s,
		TokenURL:     bitbucket.Endpoint.TokenURL,
	}

	tok, err := conf.Token(ctx)
	if err != nil {
		log.Fatal(err)
	}
	a.token = *tok
	return injectClient(a)

}

func NewOAuth(i, s string) *Client {
	a := &auth{appID: i, secret: s}
	ctx := context.Background()
	conf := &oauth2.Config{
		ClientID:     i,
		ClientSecret: s,
		Endpoint:     bitbucket.Endpoint,
	}

	// Redirect user to consent page to ask for permission
	// for the scopes specified above.
	url := conf.AuthCodeURL("state", oauth2.AccessTypeOffline)
	fmt.Printf("Visit the URL for the auth dialog:\n%v", url)

	// Use the authorization code that is pushed to the redirect
	// URL. Exchange will do the handshake to retrieve the
	// initial access token. The HTTP Client returned by
	// conf.Client will refresh the token as necessary.
	var code string
	fmt.Printf("Enter the code in the return URL: ")
	if _, err := fmt.Scan(&code); err != nil {
		log.Fatal(err)
	}
	tok, err := conf.Exchange(ctx, code)
	if err != nil {
		log.Fatal(err)
	}
	a.token = *tok
	return injectClient(a)
}

// NewOAuthWithCode finishes the OAuth handshake with a given code
// and returns a *Client
func NewOAuthWithCode(i, s, c string) (*Client, string) {
	a := &auth{appID: i, secret: s}
	ctx := context.Background()
	conf := &oauth2.Config{
		ClientID:     i,
		ClientSecret: s,
		Endpoint:     bitbucket.Endpoint,
	}

	tok, err := conf.Exchange(ctx, c)
	if err != nil {
		log.Fatal(err)
	}
	a.token = *tok
	return injectClient(a), tok.AccessToken
}

func NewOAuthbearerToken(t string) *Client {
	a := &auth{bearerToken: t}
	return injectClient(a)
}

func NewBasicAuth(u, p string) *Client {
	a := &auth{user: u, password: p}
	return injectClient(a)
}

func injectClient(a *auth) *Client {
	bitbucketUrl, err := apiBaseUrl()
	if err != nil {
		log.Fatalf("invalid bitbucket url")
	}
	c := &Client{Auth: a, Pagelen: DEFAULT_PAGE_LENGTH, MaxDepth: DEFAULT_MAX_DEPTH, apiBaseURL: bitbucketUrl}
	c.Repositories = &Repositories{
		c:                  c,
		PullRequests:       &PullRequests{c: c},
		Pipelines:          &Pipelines{c: c},
		Repository:         &Repository{c: c},
		Issues:             &Issues{c: c},
		Commits:            &Commits{c: c},
		Diff:               &Diff{c: c},
		BranchRestrictions: &BranchRestrictions{c: c},
		Webhooks:           &Webhooks{c: c},
		Downloads:          &Downloads{c: c},
		DeployKeys:         &DeployKeys{c: c},
	}
	c.Users = &Users{c: c}
	c.User = &User{c: c}
	c.Teams = &Teams{c: c}
	c.Workspaces = &Workspace{c: c, Repositories: c.Repositories, Permissions: &Permission{c: c}}
	c.HttpClient = new(http.Client)
	return c
}

func (c *Client) GetApiBaseURL() string {
	return fmt.Sprintf("%s%s", c.GetApiHostnameURL(), c.apiBaseURL.Path)
}

func (c *Client) GetApiHostnameURL() string {
	return fmt.Sprintf("%s://%s", c.apiBaseURL.Scheme, c.apiBaseURL.Host)
}

func (c *Client) SetApiBaseURL(urlStr url.URL) {
	c.apiBaseURL = &urlStr
}

func (c *Client) executeRaw(method string, urlStr string, text string) (io.ReadCloser, error) {
	body := strings.NewReader(text)

	req, err := http.NewRequest(method, urlStr, body)
	if err != nil {
		return nil, err
	}
	if text != "" {
		req.Header.Set("Content-Type", "application/json")
	}

	c.authenticateRequest(req)
	return c.doRawRequest(req, false)
}

func (c *Client) execute(method string, urlStr string, text string) (interface{}, error) {
	// Use pagination if changed from default value
	const DEC_RADIX = 10
	if strings.Contains(urlStr, "/repositories/") {
		if c.Pagelen != DEFAULT_PAGE_LENGTH {
			urlObj, err := url.Parse(urlStr)
			if err != nil {
				return nil, err
			}
			q := urlObj.Query()
			q.Set("pagelen", strconv.FormatUint(c.Pagelen, DEC_RADIX))
			urlObj.RawQuery = q.Encode()
			urlStr = urlObj.String()
		}

		if c.MaxDepth != DEFAULT_MAX_DEPTH {
			urlObj, err := url.Parse(urlStr)
			if err != nil {
				return nil, err
			}
			q := urlObj.Query()
			q.Set("max_depth", strconv.FormatUint(c.MaxDepth, DEC_RADIX))
			urlObj.RawQuery = q.Encode()
			urlStr = urlObj.String()
		}
	}

	body := strings.NewReader(text)

	req, err := http.NewRequest(method, urlStr, body)
	if err != nil {
		return nil, err
	}
	if text != "" {
		req.Header.Set("Content-Type", "application/json")
	}

	c.authenticateRequest(req)
	result, err := c.doRequest(req, false)
	if err != nil {
		return nil, err
	}

	return result, nil
}

func (c *Client) executeFileUpload(method string, urlStr string, filePath string, fileName string, fieldname string, params map[string]string) (interface{}, error) {
	fileReader, err := os.Open(filePath)
	if err != nil {
		return nil, err
	}
	defer fileReader.Close()

	// Prepare a form that you will submit to that URL.
	var b bytes.Buffer
	w := multipart.NewWriter(&b)

	var fw io.Writer
	if fw, err = w.CreateFormFile(fieldname, fileName); err != nil {
		return nil, err
	}

	if _, err = io.Copy(fw, fileReader); err != nil {
		return nil, err
	}

	for key, value := range params {
		err = w.WriteField(key, value)
		if err != nil {
			return nil, err
		}
	}

	// Don't forget to close the multipart writer.
	// If you don't close it, your request will be missing the terminating boundary.
	w.Close()

	// Now that you have a form, you can submit it to your handler.
	req, err := http.NewRequest(method, urlStr, &b)
	if err != nil {
		return nil, err
	}
	// Don't forget to set the content type, this will contain the boundary.
	req.Header.Set("Content-Type", w.FormDataContentType())

	c.authenticateRequest(req)
	return c.doRequest(req, true)

}

func (c *Client) authenticateRequest(req *http.Request) {
	if c.Auth.bearerToken != "" {
		req.Header.Set("Authorization", "Bearer "+c.Auth.bearerToken)
	}

	if c.Auth.user != "" && c.Auth.password != "" {
		req.SetBasicAuth(c.Auth.user, c.Auth.password)
	} else if c.Auth.token.Valid() {
		c.Auth.token.SetAuthHeader(req)
	}
}

func (c *Client) doRequest(req *http.Request, emptyResponse bool) (interface{}, error) {
	resBody, err := c.doRawRequest(req, emptyResponse)
	if err != nil {
		return nil, err
	}
	if emptyResponse || resBody == nil {
		return nil, nil
	}

	defer resBody.Close()

	responseBytes, err := ioutil.ReadAll(resBody)
	if err != nil {
		return resBody, err
	}

	responsePaginated := &Response{}
	err = json.Unmarshal(responseBytes, responsePaginated)
	if err == nil && len(responsePaginated.Values) > 0 {
		var values []interface{}
		for {
			values = append(values, responsePaginated.Values...)
			if responsePaginated.Size/responsePaginated.Pagelen <= responsePaginated.Page {
				break
			}
			newReq, err := http.NewRequest(req.Method, responsePaginated.Next, nil)
			if err != nil {
				return resBody, err
			}
			c.authenticateRequest(newReq)
			resp, err := c.doRawRequest(newReq, false)
			if err != nil {
				return resBody, err
			}
			json.NewDecoder(resp).Decode(responsePaginated)
		}
		responsePaginated.Values = values
		responseBytes, err = json.Marshal(responsePaginated)
		if err != nil {
			return resBody, err
		}
	}

	var result interface{}
	if err := json.Unmarshal(responseBytes, &result); err != nil {
		log.Println("Could not unmarshal JSON payload, returning raw response")
		return resBody, err
	}
	return result, nil
}

func (c *Client) doRawRequest(req *http.Request, emptyResponse bool) (io.ReadCloser, error) {
	resp, err := c.HttpClient.Do(req)
	if err != nil {
		return nil, err
	}

	if unexpectedHttpStatusCode(resp.StatusCode) {
		defer resp.Body.Close()

		out := &UnexpectedResponseStatusError{Status: resp.Status}

		body, err := io.ReadAll(resp.Body)
		if err != nil {
			out.Body = []byte(fmt.Sprintf("could not read the response body: %v", err))
		} else {
			out.Body = body
		}

		return nil, out
	}

	if emptyResponse || resp.StatusCode == http.StatusNoContent {
		resp.Body.Close()
		return nil, nil
	}

	if resp.Body == nil {
		return nil, fmt.Errorf("response body is nil")
	}

	return resp.Body, nil
}

func unexpectedHttpStatusCode(statusCode int) bool {
	switch statusCode {
	case http.StatusOK,
		http.StatusCreated,
		http.StatusNoContent,
		http.StatusAccepted:
		return false
	default:
		return true
	}
}

func (c *Client) requestUrl(template string, args ...interface{}) string {

	if len(args) == 1 && args[0] == "" {
		return c.GetApiBaseURL() + template
	}
	return c.GetApiBaseURL() + fmt.Sprintf(template, args...)
}
<commit_msg>fix(client): Avoid division by zero in pagination (#188)<commit_after>package bitbucket

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"

	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/bitbucket"
	"golang.org/x/oauth2/clientcredentials"
)

const DEFAULT_PAGE_LENGTH = 10
const DEFAULT_MAX_DEPTH = 1
const DEFAULT_BITBUCKET_API_BASE_URL = "https://api.bitbucket.org/2.0"

func apiBaseUrl() (*url.URL, error) {
	ev := os.Getenv("BITBUCKET_API_BASE_URL")
	if ev == "" {
		ev = DEFAULT_BITBUCKET_API_BASE_URL
	}

	return url.Parse(ev)
}

type Client struct {
	Auth         *auth
	Users        users
	User         user
	Teams        teams
	Repositories *Repositories
	Workspaces   *Workspace
	Pagelen      uint64
	MaxDepth     uint64
	apiBaseURL   *url.URL

	HttpClient *http.Client
}

type auth struct {
	appID, secret  string
	user, password string
	token          oauth2.Token
	bearerToken    string
}

type Response struct {
	Size     int           `json:"size"`
	Page     int           `json:"page"`
	Pagelen  int           `json:"pagelen"`
	Next     string        `json:"next"`
	Previous string        `json:"previous"`
	Values   []interface{} `json:"values"`
}

// Uses the Client Credentials Grant oauth2 flow to authenticate to Bitbucket
func NewOAuthClientCredentials(i, s string) *Client {
	a := &auth{appID: i, secret: s}
	ctx := context.Background()
	conf := &clientcredentials.Config{
		ClientID:     i,
		ClientSecret: s,
		TokenURL:     bitbucket.Endpoint.TokenURL,
	}

	tok, err := conf.Token(ctx)
	if err != nil {
		log.Fatal(err)
	}
	a.token = *tok
	return injectClient(a)

}

func NewOAuth(i, s string) *Client {
	a := &auth{appID: i, secret: s}
	ctx := context.Background()
	conf := &oauth2.Config{
		ClientID:     i,
		ClientSecret: s,
		Endpoint:     bitbucket.Endpoint,
	}

	// Redirect user to consent page to ask for permission
	// for the scopes specified above.
	url := conf.AuthCodeURL("state", oauth2.AccessTypeOffline)
	fmt.Printf("Visit the URL for the auth dialog:\n%v", url)

	// Use the authorization code that is pushed to the redirect
	// URL. Exchange will do the handshake to retrieve the
	// initial access token. The HTTP Client returned by
	// conf.Client will refresh the token as necessary.
	var code string
	fmt.Printf("Enter the code in the return URL: ")
	if _, err := fmt.Scan(&code); err != nil {
		log.Fatal(err)
	}
	tok, err := conf.Exchange(ctx, code)
	if err != nil {
		log.Fatal(err)
	}
	a.token = *tok
	return injectClient(a)
}

// NewOAuthWithCode finishes the OAuth handshake with a given code
// and returns a *Client
func NewOAuthWithCode(i, s, c string) (*Client, string) {
	a := &auth{appID: i, secret: s}
	ctx := context.Background()
	conf := &oauth2.Config{
		ClientID:     i,
		ClientSecret: s,
		Endpoint:     bitbucket.Endpoint,
	}

	tok, err := conf.Exchange(ctx, c)
	if err != nil {
		log.Fatal(err)
	}
	a.token = *tok
	return injectClient(a), tok.AccessToken
}

func NewOAuthbearerToken(t string) *Client {
	a := &auth{bearerToken: t}
	return injectClient(a)
}

func NewBasicAuth(u, p string) *Client {
	a := &auth{user: u, password: p}
	return injectClient(a)
}

func injectClient(a *auth) *Client {
	bitbucketUrl, err := apiBaseUrl()
	if err != nil {
		log.Fatalf("invalid bitbucket url")
	}
	c := &Client{Auth: a, Pagelen: DEFAULT_PAGE_LENGTH, MaxDepth: DEFAULT_MAX_DEPTH, apiBaseURL: bitbucketUrl}
	c.Repositories = &Repositories{
		c:                  c,
		PullRequests:       &PullRequests{c: c},
		Pipelines:          &Pipelines{c: c},
		Repository:         &Repository{c: c},
		Issues:             &Issues{c: c},
		Commits:            &Commits{c: c},
		Diff:               &Diff{c: c},
		BranchRestrictions: &BranchRestrictions{c: c},
		Webhooks:           &Webhooks{c: c},
		Downloads:          &Downloads{c: c},
		DeployKeys:         &DeployKeys{c: c},
	}
	c.Users = &Users{c: c}
	c.User = &User{c: c}
	c.Teams = &Teams{c: c}
	c.Workspaces = &Workspace{c: c, Repositories: c.Repositories, Permissions: &Permission{c: c}}
	c.HttpClient = new(http.Client)
	return c
}

func (c *Client) GetApiBaseURL() string {
	return fmt.Sprintf("%s%s", c.GetApiHostnameURL(), c.apiBaseURL.Path)
}

func (c *Client) GetApiHostnameURL() string {
	return fmt.Sprintf("%s://%s", c.apiBaseURL.Scheme, c.apiBaseURL.Host)
}

func (c *Client) SetApiBaseURL(urlStr url.URL) {
	c.apiBaseURL = &urlStr
}

func (c *Client) executeRaw(method string, urlStr string, text string) (io.ReadCloser, error) {
	body := strings.NewReader(text)

	req, err := http.NewRequest(method, urlStr, body)
	if err != nil {
		return nil, err
	}
	if text != "" {
		req.Header.Set("Content-Type", "application/json")
	}

	c.authenticateRequest(req)
	return c.doRawRequest(req, false)
}

func (c *Client) execute(method string, urlStr string, text string) (interface{}, error) {
	// Use pagination if changed from default value
	const DEC_RADIX = 10
	if strings.Contains(urlStr, "/repositories/") {
		if c.Pagelen != DEFAULT_PAGE_LENGTH {
			urlObj, err := url.Parse(urlStr)
			if err != nil {
				return nil, err
			}
			q := urlObj.Query()
			q.Set("pagelen", strconv.FormatUint(c.Pagelen, DEC_RADIX))
			urlObj.RawQuery = q.Encode()
			urlStr = urlObj.String()
		}

		if c.MaxDepth != DEFAULT_MAX_DEPTH {
			urlObj, err := url.Parse(urlStr)
			if err != nil {
				return nil, err
			}
			q := urlObj.Query()
			q.Set("max_depth", strconv.FormatUint(c.MaxDepth, DEC_RADIX))
			urlObj.RawQuery = q.Encode()
			urlStr = urlObj.String()
		}
	}

	body := strings.NewReader(text)

	req, err := http.NewRequest(method, urlStr, body)
	if err != nil {
		return nil, err
	}
	if text != "" {
		req.Header.Set("Content-Type", "application/json")
	}

	c.authenticateRequest(req)
	result, err := c.doRequest(req, false)
	if err != nil {
		return nil, err
	}

	return result, nil
}

func (c *Client) executeFileUpload(method string, urlStr string, filePath string, fileName string, fieldname string, params map[string]string) (interface{}, error) {
	fileReader, err := os.Open(filePath)
	if err != nil {
		return nil, err
	}
	defer fileReader.Close()

	// Prepare a form that you will submit to that URL.
	var b bytes.Buffer
	w := multipart.NewWriter(&b)

	var fw io.Writer
	if fw, err = w.CreateFormFile(fieldname, fileName); err != nil {
		return nil, err
	}

	if _, err = io.Copy(fw, fileReader); err != nil {
		return nil, err
	}

	for key, value := range params {
		err = w.WriteField(key, value)
		if err != nil {
			return nil, err
		}
	}

	// Don't forget to close the multipart writer.
	// If you don't close it, your request will be missing the terminating boundary.
	w.Close()

	// Now that you have a form, you can submit it to your handler.
	req, err := http.NewRequest(method, urlStr, &b)
	if err != nil {
		return nil, err
	}
	// Don't forget to set the content type, this will contain the boundary.
	req.Header.Set("Content-Type", w.FormDataContentType())

	c.authenticateRequest(req)
	return c.doRequest(req, true)

}

func (c *Client) authenticateRequest(req *http.Request) {
	if c.Auth.bearerToken != "" {
		req.Header.Set("Authorization", "Bearer "+c.Auth.bearerToken)
	}

	if c.Auth.user != "" && c.Auth.password != "" {
		req.SetBasicAuth(c.Auth.user, c.Auth.password)
	} else if c.Auth.token.Valid() {
		c.Auth.token.SetAuthHeader(req)
	}
}

func (c *Client) doRequest(req *http.Request, emptyResponse bool) (interface{}, error) {
	resBody, err := c.doRawRequest(req, emptyResponse)
	if err != nil {
		return nil, err
	}
	if emptyResponse || resBody == nil {
		return nil, nil
	}

	defer resBody.Close()

	responseBytes, err := ioutil.ReadAll(resBody)
	if err != nil {
		return resBody, err
	}

	responsePaginated := &Response{}
	err = json.Unmarshal(responseBytes, responsePaginated)
	if err == nil && len(responsePaginated.Values) > 0 {
		var values []interface{}
		for {
			values = append(values, responsePaginated.Values...)
			if responsePaginated.Pagelen == 0 || responsePaginated.Size/responsePaginated.Pagelen <= responsePaginated.Page {
				break
			}
			newReq, err := http.NewRequest(req.Method, responsePaginated.Next, nil)
			if err != nil {
				return resBody, err
			}
			c.authenticateRequest(newReq)
			resp, err := c.doRawRequest(newReq, false)
			if err != nil {
				return resBody, err
			}
			json.NewDecoder(resp).Decode(responsePaginated)
		}
		responsePaginated.Values = values
		responseBytes, err = json.Marshal(responsePaginated)
		if err != nil {
			return resBody, err
		}
	}

	var result interface{}
	if err := json.Unmarshal(responseBytes, &result); err != nil {
		log.Println("Could not unmarshal JSON payload, returning raw response")
		return resBody, err
	}
	return result, nil
}

func (c *Client) doRawRequest(req *http.Request, emptyResponse bool) (io.ReadCloser, error) {
	resp, err := c.HttpClient.Do(req)
	if err != nil {
		return nil, err
	}

	if unexpectedHttpStatusCode(resp.StatusCode) {
		defer resp.Body.Close()

		out := &UnexpectedResponseStatusError{Status: resp.Status}

		body, err := io.ReadAll(resp.Body)
		if err != nil {
			out.Body = []byte(fmt.Sprintf("could not read the response body: %v", err))
		} else {
			out.Body = body
		}

		return nil, out
	}

	if emptyResponse || resp.StatusCode == http.StatusNoContent {
		resp.Body.Close()
		return nil, nil
	}

	if resp.Body == nil {
		return nil, fmt.Errorf("response body is nil")
	}

	return resp.Body, nil
}

func unexpectedHttpStatusCode(statusCode int) bool {
	switch statusCode {
	case http.StatusOK,
		http.StatusCreated,
		http.StatusNoContent,
		http.StatusAccepted:
		return false
	default:
		return true
	}
}

func (c *Client) requestUrl(template string, args ...interface{}) string {

	if len(args) == 1 && args[0] == "" {
		return c.GetApiBaseURL() + template
	}
	return c.GetApiBaseURL() + fmt.Sprintf(template, args...)
}
<|endoftext|>


<commit_before>package dns

// A concurrent client implementation.

import (
	"io"
	"net"
	"time"
)

type reply struct {
	client         *Client
	addr           string
	req            *Msg
	conn           net.Conn
	tsigRequestMAC string
	tsigTimersOnly bool
	tsigStatus     error
	rtt            time.Duration
	t              time.Time
}

// A Client defines parameter for a DNS client. A nil
// Client is usable for sending queries.
type Client struct {
	Net          string            // if "tcp" a TCP query will be initiated, otherwise an UDP one (default is "" for UDP)
	Attempts     int               // number of attempts, if not set defaults to 1
	Retry        bool              // retry with TCP
	ReadTimeout  time.Duration     // the net.Conn.SetReadTimeout value for new connections (ns), defauls to 2 * 1e9
	WriteTimeout time.Duration     // the net.Conn.SetWriteTimeout value for new connections (ns), defauls to 2 * 1e9
	TsigSecret   map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be fully qualified
}

func (w *reply) RemoteAddr() net.Addr {
	if w.conn != nil {
		return w.conn.RemoteAddr()
	}
	return nil
}

// Do performs an asynchronous query. The msg *Msg is the question to ask, the
// string addr is the address of the nameserver, the parameter data is used
// in the callback function. The call backback function is called with the
// original query, the answer returned from the nameserver an optional error and
// data.
func (c *Client) Do(msg *Msg, addr string, data interface{}, callback func(*Msg, *Msg, error, interface{})) {
	go func() {
		r, err := c.Exchange(msg, addr)
		callback(msg, r, err, data)
	}()
}

// DoRtt is equivalent to Do, except that is calls ExchangeRtt.
func (c *Client) DoRtt(msg *Msg, addr string, data interface{}, callback func(*Msg, *Msg, time.Duration, error, interface{})) {
	go func() {
		r, rtt, err := c.ExchangeRtt(msg, addr)
		callback(msg, r, rtt, err, data)
	}()
}

func (c *Client) exchangeBuffer(inbuf []byte, a string, outbuf []byte) (n int, w *reply, err error) {
	w = new(reply)
	w.client = c
	w.addr = a
	// attempts useful for dialing?
	if err = w.dial(); err != nil {
		return 0, w, err
	}
	defer w.conn.Close()
	w.t = time.Now()
	if n, err = w.write(inbuf); err != nil {
		return 0, w, err
	}
	if n, err = w.read(outbuf); err != nil {
		return n, w, err
	}
	w.rtt = time.Since(w.t)
	return n, w, nil
}

// Exchange performs an synchronous query. It sends the message m to the address
// contained in a and waits for an reply. Basic use pattern with a *Client:
//
//	c := new(dns.Client)
//	in, err := c.Exchange(message, "127.0.0.1:53")
//
// See Client.ExchangeRtt(...) to get the round trip time.
func (c *Client) Exchange(m *Msg, a string) (r *Msg, err error) {
	r, _, err = c.ExchangeRtt(m, a)
	return
}

// ExchangeRtt performs an synchronous query. It sends the message m to the address
// contained in a and waits for an reply. Basic use pattern with a *Client:
//
//	c := new(dns.Client)
//	in, rtt, err := c.ExchangeRtt(message, "127.0.0.1:53")
//
func (c *Client) ExchangeRtt(m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
	w := new(reply)
	w.client = c
	w.addr = a
	if err = w.dial(); err != nil {
		return nil, 0, err
	}
	if err = w.send(m); err != nil {
		return nil, 0, err
	}
	r, err = w.receive()
	return r, w.rtt, err
}

// dial connects to the address addr for the network set in c.Net
func (w *reply) dial() (err error) {
	var conn net.Conn
	if w.client.Net == "" {
		conn, err = net.Dial("udp", w.addr)
	} else {
		conn, err = net.Dial(w.client.Net, w.addr)
	}
	if err != nil {
		return
	}
	w.conn = conn
	return nil
}

func (w *reply) receive() (*Msg, error) {
	var p []byte
	m := new(Msg)
	switch w.client.Net {
	case "tcp", "tcp4", "tcp6":
		p = make([]byte, MaxMsgSize)
	case "", "udp", "udp4", "udp6":
		// OPT! TODO(mg)
		p = make([]byte, DefaultMsgSize)
	}
	n, err := w.read(p)
	if err != nil && n == 0 {
		return nil, err
	}
	p = p[:n]
	if err := m.Unpack(p); err != nil {
		return nil, err
	}
	w.rtt = time.Since(w.t)
	m.Size = n
	if t := m.IsTsig(); t != nil {
		secret := t.Hdr.Name
		if _, ok := w.client.TsigSecret[secret]; !ok {
			w.tsigStatus = ErrSecret
			return m, nil
		}
		// Need to work on the original message p, as that was used to calculate the tsig.
		w.tsigStatus = TsigVerify(p, w.client.TsigSecret[secret], w.tsigRequestMAC, w.tsigTimersOnly)
	}
	return m, nil
}

func (w *reply) read(p []byte) (n int, err error) {
	if w.conn == nil {
		return 0, ErrConnEmpty
	}
	if len(p) < 2 {
		return 0, io.ErrShortBuffer
	}
	attempts := w.client.Attempts
	if attempts == 0 {
		attempts = 1
	}
	switch w.client.Net {
	case "tcp", "tcp4", "tcp6":
		setTimeouts(w)
		for a := 0; a < attempts; a++ {
			n, err = w.conn.(*net.TCPConn).Read(p[0:2])
			if err != nil || n != 2 {
				if e, ok := err.(net.Error); ok && e.Timeout() {
					continue
				}
				return n, err
			}
			l, _ := unpackUint16(p[0:2], 0)
			if l == 0 {
				return 0, ErrShortRead
			}
			if int(l) > len(p) {
				return int(l), io.ErrShortBuffer
			}
			n, err = w.conn.(*net.TCPConn).Read(p[:l])
			if err != nil {
				if e, ok := err.(net.Error); ok && e.Timeout() {
					continue
				}
				return n, err
			}
			i := n
			for i < int(l) {
				j, err := w.conn.(*net.TCPConn).Read(p[i:int(l)])
				if err != nil {
					if e, ok := err.(net.Error); ok && e.Timeout() {
						// We are half way in our read...
						continue
					}
					return i, err
				}
				i += j
			}
			n = i
		}
	case "", "udp", "udp4", "udp6":
		for a := 0; a < attempts; a++ {
			setTimeouts(w)
			n, _, err = w.conn.(*net.UDPConn).ReadFromUDP(p)
			if err == nil {
				return n, err
			}
			if err != nil {
				if e, ok := err.(net.Error); ok && e.Timeout() {
					continue
				}
				return n, err
			}
		}
	}
	return
}

// send sends a dns msg to the address specified in w.
// If the message m contains a TSIG record the transaction
// signature is calculated.
func (w *reply) send(m *Msg) (err error) {
	var out []byte
	if t := m.IsTsig(); t != nil {
		mac := ""
		name := t.Hdr.Name
		if _, ok := w.client.TsigSecret[name]; !ok {
			return ErrSecret
		}
		out, mac, err = TsigGenerate(m, w.client.TsigSecret[name], w.tsigRequestMAC, w.tsigTimersOnly)
		w.tsigRequestMAC = mac
	} else {
		out, err = m.Pack()
	}
	if err != nil {
		return err
	}
	w.t = time.Now()
	if _, err = w.write(out); err != nil {
		return err
	}
	return nil
}

func (w *reply) write(p []byte) (n int, err error) {
	attempts := w.client.Attempts
	if attempts == 0 {
		attempts = 1
	}
	switch w.client.Net {
	case "tcp", "tcp4", "tcp6":
		if len(p) < 2 {
			return 0, io.ErrShortBuffer
		}
		for a := 0; a < attempts; a++ {
			setTimeouts(w)
			a, b := packUint16(uint16(len(p)))
			n, err = w.conn.Write([]byte{a, b})
			if err != nil {
				if e, ok := err.(net.Error); ok && e.Timeout() {
					continue
				}
				return n, err
			}
			if n != 2 {
				return n, io.ErrShortWrite
			}
			n, err = w.conn.Write(p)
			if err != nil {
				if e, ok := err.(net.Error); ok && e.Timeout() {
					continue
				}
				return n, err
			}
			i := n
			if i < len(p) {
				j, err := w.conn.Write(p[i:len(p)])
				if err != nil {
					if e, ok := err.(net.Error); ok && e.Timeout() {
						// We are half way in our write...
						continue
					}
					return i, err
				}
				i += j
			}
			n = i
		}
	case "", "udp", "udp4", "udp6":
		for a := 0; a < attempts; a++ {
			setTimeouts(w)
			n, err = w.conn.(*net.UDPConn).Write(p)
			if err == nil {
				return
			}
			if err != nil {
				if e, ok := err.(net.Error); ok && e.Timeout() {
					continue
				}
				return n, err
			}
		}
	}
	return
}

func setTimeouts(w *reply) {
	if w.client.ReadTimeout == 0 {
		w.conn.SetReadDeadline(time.Now().Add(2 * 1e9))
	} else {
		w.conn.SetReadDeadline(time.Now().Add(w.client.ReadTimeout))
	}

	if w.client.WriteTimeout == 0 {
		w.conn.SetWriteDeadline(time.Now().Add(2 * 1e9))
	} else {
		w.conn.SetWriteDeadline(time.Now().Add(w.client.WriteTimeout))
	}
}
<commit_msg>Tsig for queries is working again<commit_after>package dns

// A concurrent client implementation.

import (
	"io"
	"net"
	"time"
)

type reply struct {
	client         *Client
	addr           string
	req            *Msg
	conn           net.Conn
	tsigRequestMAC string
	tsigTimersOnly bool
	tsigStatus     error
	rtt            time.Duration
	t              time.Time
}

// A Client defines parameter for a DNS client. A nil
// Client is usable for sending queries.
type Client struct {
	Net          string            // if "tcp" a TCP query will be initiated, otherwise an UDP one (default is "" for UDP)
	Attempts     int               // number of attempts, if not set defaults to 1
	Retry        bool              // retry with TCP
	ReadTimeout  time.Duration     // the net.Conn.SetReadTimeout value for new connections (ns), defauls to 2 * 1e9
	WriteTimeout time.Duration     // the net.Conn.SetWriteTimeout value for new connections (ns), defauls to 2 * 1e9
	TsigSecret   map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be fully qualified
}

func (w *reply) RemoteAddr() net.Addr {
	if w.conn != nil {
		return w.conn.RemoteAddr()
	}
	return nil
}

// Do performs an asynchronous query. The msg *Msg is the question to ask, the
// string addr is the address of the nameserver, the parameter data is used
// in the callback function. The call backback function is called with the
// original query, the answer returned from the nameserver an optional error and
// data.
func (c *Client) Do(msg *Msg, addr string, data interface{}, callback func(*Msg, *Msg, error, interface{})) {
	go func() {
		r, err := c.Exchange(msg, addr)
		callback(msg, r, err, data)
	}()
}

// DoRtt is equivalent to Do, except that is calls ExchangeRtt.
func (c *Client) DoRtt(msg *Msg, addr string, data interface{}, callback func(*Msg, *Msg, time.Duration, error, interface{})) {
	go func() {
		r, rtt, err := c.ExchangeRtt(msg, addr)
		callback(msg, r, rtt, err, data)
	}()
}

func (c *Client) exchangeBuffer(inbuf []byte, a string, outbuf []byte) (n int, w *reply, err error) {
	w = new(reply)
	w.client = c
	w.addr = a
	// attempts useful for dialing?
	if err = w.dial(); err != nil {
		return 0, w, err
	}
	defer w.conn.Close()
	w.t = time.Now()
	if n, err = w.write(inbuf); err != nil {
		return 0, w, err
	}
	if n, err = w.read(outbuf); err != nil {
		return n, w, err
	}
	w.rtt = time.Since(w.t)
	return n, w, nil
}

// Exchange performs an synchronous query. It sends the message m to the address
// contained in a and waits for an reply. Basic use pattern with a *Client:
//
//	c := new(dns.Client)
//	in, err := c.Exchange(message, "127.0.0.1:53")
//
// See Client.ExchangeRtt(...) to get the round trip time.
func (c *Client) Exchange(m *Msg, a string) (r *Msg, err error) {
	r, _, err = c.ExchangeRtt(m, a)
	return
}

// ExchangeRtt performs an synchronous query. It sends the message m to the address
// contained in a and waits for an reply. Basic use pattern with a *Client:
//
//	c := new(dns.Client)
//	in, rtt, err := c.ExchangeRtt(message, "127.0.0.1:53")
//
func (c *Client) ExchangeRtt(m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
	w := new(reply)
	w.client = c
	w.addr = a
	if err = w.dial(); err != nil {
		return nil, 0, err
	}
	if err = w.send(m); err != nil {
		return nil, 0, err
	}
	r, err = w.receive()
	return r, w.rtt, err
}

// dial connects to the address addr for the network set in c.Net
func (w *reply) dial() (err error) {
	var conn net.Conn
	if w.client.Net == "" {
		conn, err = net.Dial("udp", w.addr)
	} else {
		conn, err = net.Dial(w.client.Net, w.addr)
	}
	if err != nil {
		return
	}
	w.conn = conn
	return nil
}

func (w *reply) receive() (*Msg, error) {
	var p []byte
	m := new(Msg)
	switch w.client.Net {
	case "tcp", "tcp4", "tcp6":
		p = make([]byte, MaxMsgSize)
	case "", "udp", "udp4", "udp6":
		// OPT! TODO(mg)
		p = make([]byte, DefaultMsgSize)
	}
	n, err := w.read(p)
	if err != nil && n == 0 {
		return nil, err
	}
	p = p[:n]
	if err := m.Unpack(p); err != nil {
		return nil, err
	}
	w.rtt = time.Since(w.t)
	m.Size = n
	if t := m.IsTsig(); t != nil {
		secret := t.Hdr.Name
		if _, ok := w.client.TsigSecret[secret]; !ok {
			w.tsigStatus = ErrSecret
			return m, ErrSecret
		}
		// Need to work on the original message p, as that was used to calculate the tsig.
		w.tsigStatus = TsigVerify(p, w.client.TsigSecret[secret], w.tsigRequestMAC, w.tsigTimersOnly)
	}
	return m, w.tsigStatus
}

func (w *reply) read(p []byte) (n int, err error) {
	if w.conn == nil {
		return 0, ErrConnEmpty
	}
	if len(p) < 2 {
		return 0, io.ErrShortBuffer
	}
	attempts := w.client.Attempts
	if attempts == 0 {
		attempts = 1
	}
	switch w.client.Net {
	case "tcp", "tcp4", "tcp6":
		setTimeouts(w)
		for a := 0; a < attempts; a++ {
			n, err = w.conn.(*net.TCPConn).Read(p[0:2])
			if err != nil || n != 2 {
				if e, ok := err.(net.Error); ok && e.Timeout() {
					continue
				}
				return n, err
			}
			l, _ := unpackUint16(p[0:2], 0)
			if l == 0 {
				return 0, ErrShortRead
			}
			if int(l) > len(p) {
				return int(l), io.ErrShortBuffer
			}
			n, err = w.conn.(*net.TCPConn).Read(p[:l])
			if err != nil {
				if e, ok := err.(net.Error); ok && e.Timeout() {
					continue
				}
				return n, err
			}
			i := n
			for i < int(l) {
				j, err := w.conn.(*net.TCPConn).Read(p[i:int(l)])
				if err != nil {
					if e, ok := err.(net.Error); ok && e.Timeout() {
						// We are half way in our read...
						continue
					}
					return i, err
				}
				i += j
			}
			n = i
		}
	case "", "udp", "udp4", "udp6":
		for a := 0; a < attempts; a++ {
			setTimeouts(w)
			n, _, err = w.conn.(*net.UDPConn).ReadFromUDP(p)
			if err == nil {
				return n, err
			}
			if err != nil {
				if e, ok := err.(net.Error); ok && e.Timeout() {
					continue
				}
				return n, err
			}
		}
	}
	return
}

// send sends a dns msg to the address specified in w.
// If the message m contains a TSIG record the transaction
// signature is calculated.
func (w *reply) send(m *Msg) (err error) {
	var out []byte
	if t := m.IsTsig(); t != nil {
		mac := ""
		name := t.Hdr.Name
		if _, ok := w.client.TsigSecret[name]; !ok {
			return ErrSecret
		}
		out, mac, err = TsigGenerate(m, w.client.TsigSecret[name], w.tsigRequestMAC, w.tsigTimersOnly)
		w.tsigRequestMAC = mac
	} else {
		out, err = m.Pack()
	}
	if err != nil {
		return err
	}
	w.t = time.Now()
	if _, err = w.write(out); err != nil {
		return err
	}
	return nil
}

func (w *reply) write(p []byte) (n int, err error) {
	attempts := w.client.Attempts
	if attempts == 0 {
		attempts = 1
	}
	switch w.client.Net {
	case "tcp", "tcp4", "tcp6":
		if len(p) < 2 {
			return 0, io.ErrShortBuffer
		}
		for a := 0; a < attempts; a++ {
			setTimeouts(w)
			a, b := packUint16(uint16(len(p)))
			n, err = w.conn.Write([]byte{a, b})
			if err != nil {
				if e, ok := err.(net.Error); ok && e.Timeout() {
					continue
				}
				return n, err
			}
			if n != 2 {
				return n, io.ErrShortWrite
			}
			n, err = w.conn.Write(p)
			if err != nil {
				if e, ok := err.(net.Error); ok && e.Timeout() {
					continue
				}
				return n, err
			}
			i := n
			if i < len(p) {
				j, err := w.conn.Write(p[i:len(p)])
				if err != nil {
					if e, ok := err.(net.Error); ok && e.Timeout() {
						// We are half way in our write...
						continue
					}
					return i, err
				}
				i += j
			}
			n = i
		}
	case "", "udp", "udp4", "udp6":
		for a := 0; a < attempts; a++ {
			setTimeouts(w)
			n, err = w.conn.(*net.UDPConn).Write(p)
			if err == nil {
				return
			}
			if err != nil {
				if e, ok := err.(net.Error); ok && e.Timeout() {
					continue
				}
				return n, err
			}
		}
	}
	return
}

func setTimeouts(w *reply) {
	if w.client.ReadTimeout == 0 {
		w.conn.SetReadDeadline(time.Now().Add(2 * 1e9))
	} else {
		w.conn.SetReadDeadline(time.Now().Add(w.client.ReadTimeout))
	}

	if w.client.WriteTimeout == 0 {
		w.conn.SetWriteDeadline(time.Now().Add(2 * 1e9))
	} else {
		w.conn.SetWriteDeadline(time.Now().Add(w.client.WriteTimeout))
	}
}
<|endoftext|>


<commit_before>package main

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

	"code.google.com/p/goauth2/oauth"
	"github.com/google/go-github/github"
)

// GitHubAPIOpts are the options for GitHub API.
type GitHubAPIOpts struct {
	// BaseURL is a GitHub API URL that ghr try to access.
	// Default is https://api.github.com/
	BaseURL *url.URL

	// Token is the GitHub API token
	Token string

	// ID is release ID. Each release has its unique ID
	ID int

	// Tag is release tag.
	TagName string

	// RepoName is repository name.
	RepoName string

	// OwnerName is repositoy owner name.
	OwnerName string

	// TargetCommitish specifies the commitish value that determines
	// where the Git tag is created from.
	TargetCommitish string

	// Body describes the contens of the tag.
	Body string

	// If Draft is true, release would be draft (unpublished).
	Draft bool

	// If Prerelease is true, release would be prerelease.
	Prerelease bool

	// UPloadURL is URL to upload artifacts.
	UploadURL string
}

// NewOAuthedClient create client with oauth
func NewOAuthedClient(apiOpts *GitHubAPIOpts) *github.Client {
	// Create OAuth client
	t := &oauth.Transport{
		Token: &oauth.Token{AccessToken: apiOpts.Token},
	}

	// Create GitHub API client with OAuth client
	client := github.NewClient(t.Client())

	// If other URL is provided, Set it
	if apiOpts.BaseURL != nil {
		client.BaseURL = apiOpts.BaseURL
	}

	return client
}

// extractUploadURL extracts uploadURL.
func ExtractUploadURL(apiOpts *GitHubAPIOpts) *url.URL {
	u, _ := url.Parse(apiOpts.UploadURL)
	uploadURL, _ := url.Parse(fmt.Sprintf("%s://%s", u.Scheme, u.Host))
	return uploadURL
}

// checkStatusOK checks http status returned by API is 200
func CheckStatusOK(res *github.Response) error {
	if res.StatusCode != http.StatusOK {
		return fmt.Errorf("Github returned %s\n", res.Status)
	}
	return nil
}

// CheckStatusCreated checks http status returned by API is 201
func CheckStatusCreated(res *github.Response) error {
	if res.StatusCode != http.StatusCreated {
		if res.StatusCode == 422 {
			return fmt.Errorf("Github returned %s (this is probably because the release already exists)\n", res.Status)
		}
		return fmt.Errorf("Github returned %s\n", res.Status)
	}

	return nil
}

// checkStatusNoContent checks http status returned by API is 204
// In github API, this means DELETE request is success.
func CheckStatusNoContent(res *github.Response) error {
	if res.StatusCode != http.StatusNoContent {
		return fmt.Errorf("Github returned %s\n", res.Status)
	}
	return nil
}
<commit_msg>Fix extract upload url<commit_after>package main

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

	"code.google.com/p/goauth2/oauth"
	"github.com/google/go-github/github"
	"strings"
)

// GitHubAPIOpts are the options for GitHub API.
type GitHubAPIOpts struct {
	// BaseURL is a GitHub API URL that ghr try to access.
	// Default is https://api.github.com/
	BaseURL *url.URL

	// Token is the GitHub API token
	Token string

	// ID is release ID. Each release has its unique ID
	ID int

	// Tag is release tag.
	TagName string

	// RepoName is repository name.
	RepoName string

	// OwnerName is repositoy owner name.
	OwnerName string

	// TargetCommitish specifies the commitish value that determines
	// where the Git tag is created from.
	TargetCommitish string

	// Body describes the contens of the tag.
	Body string

	// If Draft is true, release would be draft (unpublished).
	Draft bool

	// If Prerelease is true, release would be prerelease.
	Prerelease bool

	// UPloadURL is URL to upload artifacts.
	UploadURL string
}

// NewOAuthedClient create client with oauth
func NewOAuthedClient(apiOpts *GitHubAPIOpts) *github.Client {
	// Create OAuth client
	t := &oauth.Transport{
		Token: &oauth.Token{AccessToken: apiOpts.Token},
	}

	// Create GitHub API client with OAuth client
	client := github.NewClient(t.Client())

	// If other URL is provided, Set it
	if apiOpts.BaseURL != nil {
		client.BaseURL = apiOpts.BaseURL
	}

	return client
}

// extractUploadURL extracts uploadURL.
func ExtractUploadURL(apiOpts *GitHubAPIOpts) *url.URL {
	bracket := strings.Index(apiOpts.UploadURL, "repos/")
	u, _ := url.Parse(apiOpts.UploadURL[0:bracket])
	return u
}

// checkStatusOK checks http status returned by API is 200
func CheckStatusOK(res *github.Response) error {
	if res.StatusCode != http.StatusOK {
		return fmt.Errorf("Github returned %s\n", res.Status)
	}
	return nil
}

// CheckStatusCreated checks http status returned by API is 201
func CheckStatusCreated(res *github.Response) error {
	if res.StatusCode != http.StatusCreated {
		if res.StatusCode == 422 {
			return fmt.Errorf("Github returned %s (this is probably because the release already exists)\n", res.Status)
		}
		return fmt.Errorf("Github returned %s\n", res.Status)
	}

	return nil
}

// checkStatusNoContent checks http status returned by API is 204
// In github API, this means DELETE request is success.
func CheckStatusNoContent(res *github.Response) error {
	if res.StatusCode != http.StatusNoContent {
		return fmt.Errorf("Github returned %s\n", res.Status)
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/goauth2/oauth"
	"github.com/digitalocean/godo"
)

// create a new client using the provided access token
func NewClient(token string) *Client {
	t := &oauth.Transport{
		Token: &oauth.Token{AccessToken: token},
	}

	return &Client{godo.NewClient(t.Client())}
}

// an invisible wrapper around godo.Client to allow for method additions
type Client struct {
	*godo.Client
}
<commit_msg>Update documentation in client.go<commit_after>package main

import (
	"code.google.com/p/goauth2/oauth"
	"github.com/digitalocean/godo"
)

// NewClient creates a new Client using the provided access token
func NewClient(token string) *Client {
	t := &oauth.Transport{
		Token: &oauth.Token{AccessToken: token},
	}

	return &Client{godo.NewClient(t.Client())}
}

// Client is an invisible wrapper around godo.Client to allow for method
// additions
type Client struct {
	*godo.Client
}
<|endoftext|>