<commit_before>// Copyright 2013

/*
EVE Online API downloader/cacher.

Begin by initializing the default client.
	NewClient(NilCache)

Create a new request for the API page needed.
	req := NewRequest("eve/ConquerableStationList.xml.aspx")

Set any options you may need, including keyid/vcode.
	req.Set("keyid", "1234")
	req.Set("vcode", "abcd")
	req.Set("charactername", "innominate")
	req.Set("characterid", fmt.Sprintf("%d", 123))

Get your response.
	resp, err := req.Do()
	xml := resp.Data
*/
package apicache

import (
	"crypto/rand"
	"encoding/xml"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"strings"
	"sync"
	"time"
)

const sqlDateTime = "2006-01-02 15:04:05"

var DebugLog = log.New(ioutil.Discard, "apicache", log.Ldate|log.Ltime)

//
//  API Error Type, hopefully contains the error code and some useful information.
//  This is CCP so nothing is guaranteed.
//
type APIError struct {
	ErrorCode int    `xml:"code,attr"`
	ErrorText string `xml:",innerxml"`
}

// Error String Generator
func (e APIError) Error() string {
	return fmt.Sprintf("API Error %d: %s", e.ErrorCode, e.ErrorText)
}

// API Client structure.  Must be created using NewClient() function.
type Client struct {
	// Base URL, defaults to CCPs api but can be changed to a proxy
	BaseURL string

	//Alternate user agent to use.
	UserAgent string

	// Default three retries, can be changed at will.
	Retries int

	timeout      time.Duration
	maxIdleConns int

	cacher     Cacher
	httpClient *http.Client

	panicUntil  time.Time
	panicCode   int
	panicReason string

	sync.RWMutex
}

// Default client, first client created lives here as well.
var client *Client

// Default BaseURL for new clients, change as necessary.  Must contain
// trailing slash or bad things happen.
var DefaultBaseURL = "https://api.eveonline.com/"
var DefaultUserAgent = "go apicache by Innominate(github.com/inominate/apicache)"

// Return the default client for
func GetDefaultClient() *Client {
	if client == nil {
		panic("Tried to get nonexistent client, must be initialized first.")
	}

	return client
}

// Create a new API Client.  The first time this is called will become
// the default client.  Requires a cacher.
func NewClient(cacher Cacher) *Client {
	var newClient Client

	newClient.BaseURL = DefaultBaseURL
	newClient.Retries = 5
	newClient.cacher = cacher
	newClient.maxIdleConns = 2

	// Also sets up our initial http client
	newClient.SetTimeout(60 * time.Second)

	if client == nil {
		client = &newClient
	}
	return &newClient
}

func (c *Client) SetMaxIdleConns(maxIdleConns int) {
	// Enforce some sanity.
	if maxIdleConns <= 0 || maxIdleConns >= 64 {
		maxIdleConns = 2
	}
	c.maxIdleConns = maxIdleConns
	c.newHttpClient()
}

// Set timeout for each API request.
func (c *Client) SetTimeout(timeout time.Duration) {
	if timeout.Seconds() <= 0 || timeout.Seconds() > 3600 {
		timeout = 60 * time.Second
	}

	c.timeout = timeout
	c.newHttpClient()
}

// Set max idle conns for the default client
func SetMaxIdleConns(maxIdleConns int) {
	client.SetMaxIdleConns(maxIdleConns)
}

// Set timeout for default client.
func SetTimeout(timeout time.Duration) {
	client.SetTimeout(timeout)
}

func (c *Client) newHttpClient() {
	c.httpClient = &http.Client{
		Transport: &http.Transport{
			Dial: func(netw, addr string) (net.Conn, error) {
				c, err := net.DialTimeout(netw, addr, c.timeout)
				if err != nil {
					return nil, err
				}
				//c.SetDeadline(deadline)
				return c, nil
			},
			ResponseHeaderTimeout: c.timeout,
			MaxIdleConnsPerHost:   c.maxIdleConns,
		},
	}
}

// Create a new request.
func (c *Client) NewRequest(url string) *Request {
	var r Request
	r.params = make(map[string]string)

	if url[0] == '/' {
		url = url[1:]
	}
	r.url = url
	r.client = c

	return &r
}

// Create a new request using default client.
func NewRequest(url string) *Request {
	return client.NewRequest(url)
}

var (
	ErrCannotConnect = fmt.Errorf("Error connecting to API.")
	ErrNetwork       = fmt.Errorf("Network error.")
	ErrHTTP          = fmt.Errorf("HTTP error.")
	ErrForbidden     = fmt.Errorf("HTTP Forbidden, invalid API provided.")
	ErrUnknown       = fmt.Errorf("Unknown Error.")
	ErrXML           = fmt.Errorf("Malformed XML Detected")
	ErrTime          = fmt.Errorf("Malformed cache directive.")
)

type cacheResp struct {
	Error       APIError `xml:"error"`
	CachedUntil string   `xml:"cachedUntil"`
}

// Data structure returned from the API.
type Response struct {
	// Raw XML data
	Data []byte

	// Signals if we used the cache or not
	FromCache bool

	// Data expiration time
	Expires time.Time

	// true if an error occured due to invalid API rather than server problems
	Invalidate bool

	// Contains API Error if one occured
	Error APIError

	//Pass on CCP's HTTP code because why not?
	HTTPCode int
}

func (c *Client) GetCached(r *Request) (retresp *Response, reterr error) {
	resp := &Response{}
	if r.Force {
		return resp, errors.New("cache bypass forced")
	}

	// Check for cached version
	cacheTag := r.cacheTag()
	httpCode, data, expires, err := c.cacher.Get(cacheTag)
	if err == nil && !r.Force {
		resp.Data = data
		resp.FromCache = true
		resp.Expires = expires
		resp.HTTPCode = httpCode

		var cR cacheResp
		err = xml.Unmarshal(data, &cR)
		if err != nil {
			DebugLog.Printf("XML Error: %s", err)
			return resp, ErrXML
		}

		// Handle extended expiration requests.
		if r.Expires.After(expires) {
			resp.Expires = r.Expires
		} else {
			resp.Expires = expires
		}

		// Pass on any API errors
		resp.Error = cR.Error

		return resp, nil
	}
	return resp, err
}

func MakeID() string {
	buf := make([]byte, 5)
	io.ReadFull(rand.Reader, buf)
	return fmt.Sprintf("%x", buf)
}

func (c *Client) postForm(url string, values url.Values) (*http.Response, error) {
	// httpResp, err = c.httpClient.PostForm(c.BaseURL+r.url, formValues)
	req, err := http.NewRequest("POST", url, strings.NewReader(values.Encode()))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	var ua string = DefaultUserAgent
	if c.UserAgent != "" {
		ua = c.UserAgent
	}
	req.Header.Set("User-Agent", ua)

	return c.httpClient.Do(req)
}

// Perform a request, usually called by the request itself.
// User friendly error is enclosed in the response, returned error should be
// for internal use only.
func (c *Client) Do(r *Request) (retresp *Response, reterr error) {
	var data []byte
	var expires time.Time

	cacheTag := r.cacheTag()

	// Check for cached version
	resp, err := c.GetCached(r)
	if err == nil {
		return resp, nil
	}

	// If we're panicking, bail out early and spit back a fake error
	c.RLock()
	if c.panicUntil.After(time.Now()) {
		DebugLog.Printf("Got Request, but we're currently panicing until %s", c.panicUntil.Format(sqlDateTime))
		data := SynthesizeAPIError(c.panicCode, c.panicReason, c.panicUntil.Sub(time.Now()))
		c.RUnlock()

		resp.Data = data
		resp.FromCache = true
		resp.Expires = c.panicUntil
		resp.HTTPCode = 418
		resp.Error = APIError{c.panicCode, c.panicReason}

		return resp, nil
	}
	c.RUnlock()

	// Build parameter list
	formValues := make(url.Values)
	for k, v := range r.params {
		formValues.Set(k, v)
	}

	// Use defer to cache so we can synthesize error pages if necessary
	defer func() {
		if reterr != nil {
			resp.HTTPCode = 504
			resp.Data = SynthesizeAPIError(500, "APIProxy Error: "+reterr.Error(), 5*time.Minute)
		} else if resp.Data == nil {
			resp.HTTPCode = 504
			resp.Data = SynthesizeAPIError(900, "This shouldn't happen.", 15*time.Minute)
		}
		if !r.NoCache {
			err := c.cacher.Store(cacheTag, resp.HTTPCode, resp.Data, resp.Expires)
			if err != nil {
				log.Printf("Cache Error: %s", err)
			}
		}
	}()

	//Post the shit, retry if necessary.

	tries := 0
	var httpResp *http.Response
	for tries < c.Retries {
		tries++

		httpResp, err = c.postForm(c.BaseURL+r.url, formValues)
		if err != nil {
			DebugLog.Printf("Error Connecting to API, retrying: %s", err)
			time.Sleep(3 * time.Second)
			continue
		}
		defer httpResp.Body.Close()

		resp.HTTPCode = httpResp.StatusCode

		// We're going to do this asynchronously so we can time it out, AAAAAAA
		type ioRead struct {
			body []byte
			err  error
		}

		readBodyChan := make(chan ioRead)
		go func() {
			bytes, err := ioutil.ReadAll(httpResp.Body)
			readBodyChan <- ioRead{bytes, err}
			close(readBodyChan)
		}()

		select {
		case readBody := <-readBodyChan:
			err = readBody.err
			data = readBody.body
		case <-time.After(c.timeout):
			data = nil
			err = fmt.Errorf("read timed out after %f seconds", c.timeout.Seconds())

			// if ioutil ever does come back, let's handle it.
			go func() {
				id := MakeID()
				DebugLog.Printf("zombie body read %s: %s ? %s", id, r.url, formValues)
				rb := <-readBodyChan
				DebugLog.Printf("zombie read completed %s: %s - %s ? %s\n%s", id, rb.err, r.url, formValues, rb.body)
			}()
		}
		if err != nil {
			DebugLog.Printf("Error Reading from API(%s), retrying...", err)
			time.Sleep(3 * time.Second)
			continue
		}

		break
		log.Printf("WARNING MAJOR REGRESSION: This should NEVER appear.")
	}
	if err != nil {
		DebugLog.Printf("Failed to access api, giving up: %s - %#v", c.BaseURL+r.url, formValues)
		return resp, ErrNetwork
	}

	//data = SynthesizeAPIError(904, "Your IP address has been temporarily blocked because it is causing too many errors. See the cacheUntil timestamp for when it will be opened again. IPs that continuall    y cause a lot of errors in the API will be permanently banned, please take measures to minimize problematic API calls from your application.", time.Second*30)

	// Get cache directive, bail with an error if anything is wrong with XML or
	// time format.  If these produce an error the rest of the data should be
	// considered worthless.
	var cR cacheResp
	err = xml.Unmarshal(data, &cR)
	if err != nil {
		DebugLog.Printf("XML Error: %s", err)
		return resp, ErrXML
	}

	// Get expiration
	expires, err = time.Parse(sqlDateTime, cR.CachedUntil)
	if err != nil {
		return resp, ErrTime
	}

	// Handle extended expiration requests.
	if r.Expires.After(expires) {
		resp.Expires = r.Expires
	} else {
		resp.Expires = expires
	}

	// Pass on any API errors
	resp.Error = cR.Error

	code := cR.Error.ErrorCode
	if code >= 901 && code <= 905 {
		log.Printf("Major API Error: %d - %s for %s %+v", cR.Error.ErrorCode, cR.Error.ErrorText, r.url, r.params)
		log.Printf("Pausing all API actions until %s...", resp.Expires.Format(sqlDateTime))
		c.Lock()
		c.panicUntil = resp.Expires
		c.panicCode = code
		c.panicReason = cR.Error.ErrorText
		c.Unlock()
	}
	if resp.HTTPCode == 403 || (code >= 100 && code <= 299) {
		resp.Invalidate = true
	}

	resp.Data = data
	return resp, nil
}
<commit_msg>don't invalidate 221's on /account/ endpoints<commit_after>// Copyright 2013

/*
EVE Online API downloader/cacher.

Begin by initializing the default client.
	NewClient(NilCache)

Create a new request for the API page needed.
	req := NewRequest("eve/ConquerableStationList.xml.aspx")

Set any options you may need, including keyid/vcode.
	req.Set("keyid", "1234")
	req.Set("vcode", "abcd")
	req.Set("charactername", "innominate")
	req.Set("characterid", fmt.Sprintf("%d", 123))

Get your response.
	resp, err := req.Do()
	xml := resp.Data
*/
package apicache

import (
	"crypto/rand"
	"encoding/xml"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"strings"
	"sync"
	"time"
)

const sqlDateTime = "2006-01-02 15:04:05"

var DebugLog = log.New(ioutil.Discard, "apicache", log.Ldate|log.Ltime)

//
//  API Error Type, hopefully contains the error code and some useful information.
//  This is CCP so nothing is guaranteed.
//
type APIError struct {
	ErrorCode int    `xml:"code,attr"`
	ErrorText string `xml:",innerxml"`
}

// Error String Generator
func (e APIError) Error() string {
	return fmt.Sprintf("API Error %d: %s", e.ErrorCode, e.ErrorText)
}

// API Client structure.  Must be created using NewClient() function.
type Client struct {
	// Base URL, defaults to CCPs api but can be changed to a proxy
	BaseURL string

	//Alternate user agent to use.
	UserAgent string

	// Default three retries, can be changed at will.
	Retries int

	timeout      time.Duration
	maxIdleConns int

	cacher     Cacher
	httpClient *http.Client

	panicUntil  time.Time
	panicCode   int
	panicReason string

	sync.RWMutex
}

// Default client, first client created lives here as well.
var client *Client

// Default BaseURL for new clients, change as necessary.  Must contain
// trailing slash or bad things happen.
var DefaultBaseURL = "https://api.eveonline.com/"
var DefaultUserAgent = "go apicache by Innominate(github.com/inominate/apicache)"

// Return the default client for
func GetDefaultClient() *Client {
	if client == nil {
		panic("Tried to get nonexistent client, must be initialized first.")
	}

	return client
}

// Create a new API Client.  The first time this is called will become
// the default client.  Requires a cacher.
func NewClient(cacher Cacher) *Client {
	var newClient Client

	newClient.BaseURL = DefaultBaseURL
	newClient.Retries = 5
	newClient.cacher = cacher
	newClient.maxIdleConns = 2

	// Also sets up our initial http client
	newClient.SetTimeout(60 * time.Second)

	if client == nil {
		client = &newClient
	}
	return &newClient
}

func (c *Client) SetMaxIdleConns(maxIdleConns int) {
	// Enforce some sanity.
	if maxIdleConns <= 0 || maxIdleConns >= 64 {
		maxIdleConns = 2
	}
	c.maxIdleConns = maxIdleConns
	c.newHttpClient()
}

// Set timeout for each API request.
func (c *Client) SetTimeout(timeout time.Duration) {
	if timeout.Seconds() <= 0 || timeout.Seconds() > 3600 {
		timeout = 60 * time.Second
	}

	c.timeout = timeout
	c.newHttpClient()
}

// Set max idle conns for the default client
func SetMaxIdleConns(maxIdleConns int) {
	client.SetMaxIdleConns(maxIdleConns)
}

// Set timeout for default client.
func SetTimeout(timeout time.Duration) {
	client.SetTimeout(timeout)
}

func (c *Client) newHttpClient() {
	c.httpClient = &http.Client{
		Transport: &http.Transport{
			Dial: func(netw, addr string) (net.Conn, error) {
				c, err := net.DialTimeout(netw, addr, c.timeout)
				if err != nil {
					return nil, err
				}
				//c.SetDeadline(deadline)
				return c, nil
			},
			ResponseHeaderTimeout: c.timeout,
			MaxIdleConnsPerHost:   c.maxIdleConns,
		},
	}
}

// Create a new request.
func (c *Client) NewRequest(url string) *Request {
	var r Request
	r.params = make(map[string]string)

	if url[0] == '/' {
		url = url[1:]
	}
	r.url = url
	r.client = c

	return &r
}

// Create a new request using default client.
func NewRequest(url string) *Request {
	return client.NewRequest(url)
}

var (
	ErrCannotConnect = fmt.Errorf("Error connecting to API.")
	ErrNetwork       = fmt.Errorf("Network error.")
	ErrHTTP          = fmt.Errorf("HTTP error.")
	ErrForbidden     = fmt.Errorf("HTTP Forbidden, invalid API provided.")
	ErrUnknown       = fmt.Errorf("Unknown Error.")
	ErrXML           = fmt.Errorf("Malformed XML Detected")
	ErrTime          = fmt.Errorf("Malformed cache directive.")
)

type cacheResp struct {
	Error       APIError `xml:"error"`
	CachedUntil string   `xml:"cachedUntil"`
}

// Data structure returned from the API.
type Response struct {
	// Raw XML data
	Data []byte

	// Signals if we used the cache or not
	FromCache bool

	// Data expiration time
	Expires time.Time

	// true if an error occured due to invalid API rather than server problems
	Invalidate bool

	// Contains API Error if one occured
	Error APIError

	//Pass on CCP's HTTP code because why not?
	HTTPCode int
}

func (c *Client) GetCached(r *Request) (retresp *Response, reterr error) {
	resp := &Response{}
	if r.Force {
		return resp, errors.New("cache bypass forced")
	}

	// Check for cached version
	cacheTag := r.cacheTag()
	httpCode, data, expires, err := c.cacher.Get(cacheTag)
	if err == nil && !r.Force {
		resp.Data = data
		resp.FromCache = true
		resp.Expires = expires
		resp.HTTPCode = httpCode

		var cR cacheResp
		err = xml.Unmarshal(data, &cR)
		if err != nil {
			DebugLog.Printf("XML Error: %s", err)
			return resp, ErrXML
		}

		// Handle extended expiration requests.
		if r.Expires.After(expires) {
			resp.Expires = r.Expires
		} else {
			resp.Expires = expires
		}

		// Pass on any API errors
		resp.Error = cR.Error

		return resp, nil
	}
	return resp, err
}

func MakeID() string {
	buf := make([]byte, 5)
	io.ReadFull(rand.Reader, buf)
	return fmt.Sprintf("%x", buf)
}

func (c *Client) postForm(url string, values url.Values) (*http.Response, error) {
	// httpResp, err = c.httpClient.PostForm(c.BaseURL+r.url, formValues)
	req, err := http.NewRequest("POST", url, strings.NewReader(values.Encode()))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	var ua string = DefaultUserAgent
	if c.UserAgent != "" {
		ua = c.UserAgent
	}
	req.Header.Set("User-Agent", ua)

	return c.httpClient.Do(req)
}

// Perform a request, usually called by the request itself.
// User friendly error is enclosed in the response, returned error should be
// for internal use only.
func (c *Client) Do(r *Request) (retresp *Response, reterr error) {
	var data []byte
	var expires time.Time

	cacheTag := r.cacheTag()

	// Check for cached version
	resp, err := c.GetCached(r)
	if err == nil {
		return resp, nil
	}

	// If we're panicking, bail out early and spit back a fake error
	c.RLock()
	if c.panicUntil.After(time.Now()) {
		DebugLog.Printf("Got Request, but we're currently panicing until %s", c.panicUntil.Format(sqlDateTime))
		data := SynthesizeAPIError(c.panicCode, c.panicReason, c.panicUntil.Sub(time.Now()))
		c.RUnlock()

		resp.Data = data
		resp.FromCache = true
		resp.Expires = c.panicUntil
		resp.HTTPCode = 418
		resp.Error = APIError{c.panicCode, c.panicReason}

		return resp, nil
	}
	c.RUnlock()

	// Build parameter list
	formValues := make(url.Values)
	for k, v := range r.params {
		formValues.Set(k, v)
	}

	// Use defer to cache so we can synthesize error pages if necessary
	defer func() {
		if reterr != nil {
			resp.HTTPCode = 504
			resp.Data = SynthesizeAPIError(500, "APIProxy Error: "+reterr.Error(), 5*time.Minute)
		} else if resp.Data == nil {
			resp.HTTPCode = 504
			resp.Data = SynthesizeAPIError(900, "This shouldn't happen.", 15*time.Minute)
		}
		if !r.NoCache {
			err := c.cacher.Store(cacheTag, resp.HTTPCode, resp.Data, resp.Expires)
			if err != nil {
				log.Printf("Cache Error: %s", err)
			}
		}
	}()

	//Post the shit, retry if necessary.

	tries := 0
	var httpResp *http.Response
	for tries < c.Retries {
		tries++

		httpResp, err = c.postForm(c.BaseURL+r.url, formValues)
		if err != nil {
			DebugLog.Printf("Error Connecting to API, retrying: %s", err)
			time.Sleep(3 * time.Second)
			continue
		}
		defer httpResp.Body.Close()

		resp.HTTPCode = httpResp.StatusCode

		// We're going to do this asynchronously so we can time it out, AAAAAAA
		type ioRead struct {
			body []byte
			err  error
		}

		readBodyChan := make(chan ioRead)
		go func() {
			bytes, err := ioutil.ReadAll(httpResp.Body)
			readBodyChan <- ioRead{bytes, err}
			close(readBodyChan)
		}()

		select {
		case readBody := <-readBodyChan:
			err = readBody.err
			data = readBody.body
		case <-time.After(c.timeout):
			data = nil
			err = fmt.Errorf("read timed out after %f seconds", c.timeout.Seconds())

			// if ioutil ever does come back, let's handle it.
			go func() {
				id := MakeID()
				DebugLog.Printf("zombie body read %s: %s ? %s", id, r.url, formValues)
				rb := <-readBodyChan
				DebugLog.Printf("zombie read completed %s: %s - %s ? %s\n%s", id, rb.err, r.url, formValues, rb.body)
			}()
		}
		if err != nil {
			DebugLog.Printf("Error Reading from API(%s), retrying...", err)
			time.Sleep(3 * time.Second)
			continue
		}

		break
		log.Printf("WARNING MAJOR REGRESSION: This should NEVER appear.")
	}
	if err != nil {
		DebugLog.Printf("Failed to access api, giving up: %s - %#v", c.BaseURL+r.url, formValues)
		return resp, ErrNetwork
	}

	//data = SynthesizeAPIError(904, "Your IP address has been temporarily blocked because it is causing too many errors. See the cacheUntil timestamp for when it will be opened again. IPs that continuall    y cause a lot of errors in the API will be permanently banned, please take measures to minimize problematic API calls from your application.", time.Second*30)

	// Get cache directive, bail with an error if anything is wrong with XML or
	// time format.  If these produce an error the rest of the data should be
	// considered worthless.
	var cR cacheResp
	err = xml.Unmarshal(data, &cR)
	if err != nil {
		DebugLog.Printf("XML Error: %s", err)
		return resp, ErrXML
	}

	// Get expiration
	expires, err = time.Parse(sqlDateTime, cR.CachedUntil)
	if err != nil {
		return resp, ErrTime
	}

	// Handle extended expiration requests.
	if r.Expires.After(expires) {
		resp.Expires = r.Expires
	} else {
		resp.Expires = expires
	}

	// Pass on any API errors
	resp.Error = cR.Error

	code := cR.Error.ErrorCode
	if code >= 901 && code <= 905 {
		log.Printf("Major API Error: %d - %s for %s %+v", cR.Error.ErrorCode, cR.Error.ErrorText, r.url, r.params)
		log.Printf("Pausing all API actions until %s...", resp.Expires.Format(sqlDateTime))
		c.Lock()
		c.panicUntil = resp.Expires
		c.panicCode = code
		c.panicReason = cR.Error.ErrorText
		c.Unlock()
	}
	if code >= 200 && code <= 223 {
		resp.Invalidate = true

		// Special check for :ccp:, if a /account/ endpoint is returning a 221
		// it shouldn't actually be invalidated.
		accountIndex := strings.Index(r.url, "account")
		if code == 221 && (accountIndex == 0 || accountIndex == 1) {
			resp.Invalidate = false
		}
	}

	resp.Data = data
	return resp, nil
}
<|endoftext|>


<commit_before>package hawk

import (
	"errors"
	"net/http"
	"strconv"
)

type Client struct {
	Credential *Credential
	Option     *Option
}

func (c *Client) Header(uri string, method string) (string, error) {
	m := &Mac{
		Type:       Header,
		Credential: c.Credential,
		Uri:        uri,
		Method:     method,
		Option:     c.Option,
	}

	mac, err := m.String()
	if err != nil {
		return "", err
	}

	if c.Option.Hash == "" && (method == "POST" || method == "PUT") {
		ph := &PayloadHash{
			ContentType: c.Option.ContentType,
			Payload:     c.Option.Payload,
			Alg:         c.Credential.Alg,
		}
		c.Option.Hash = ph.String()
	}

	header := "Hawk " +
		`id="` + c.Credential.ID + `"` +
		", " +
		`ts="` + strconv.FormatInt(c.Option.TimeStamp, 10) + `"` +
		", " +
		`nonce="` + c.Option.Nonce + `"`
	if c.Option.Hash != "" {
		header = header + ", " + `hash="` + c.Option.Hash + `"`
	}
	if c.Option.Ext != "" {
		header = header + ", " + `ext="` + c.Option.Ext + `"`
	}
	header = header + ", " + `mac="` + mac + `"`
	if c.Option.App != "" {
		header = header + ", " + `app="` + c.Option.App + `"`
		if c.Option.Dlg != "" {
			header = header + ", " + `dlg="` + c.Option.Dlg + `"`
		}
	}

	return header, nil
}

func (c *Client) Authenticate(res *http.Response) (bool, error) {
	artifacts := *c.Option

	wah := res.Header.Get("WWW-Authenticate")
	if wah != "" {
		// TODO: validate WWW-Authenticate Header
	}

	sah := res.Header.Get("Server-Authorization")
	serverAuthAttributes := parseHawkHeader(sah)

	artifacts.Ext = serverAuthAttributes["ext"]
	artifacts.Hash = serverAuthAttributes["hash"]

	m := &Mac{
		Type:       Response,
		Credential: c.Credential,
		Uri:        res.Request.URL.String(),
		Method:     res.Request.Method,
		Option:     &artifacts,
	}

	mac, err := m.String()
	if err != nil {
		return false, err
	}
	if mac != serverAuthAttributes["mac"] {
		return false, errors.New("Bad response mac")
	}

	if c.Option.Payload == "" {
		return false, nil
	}

	if serverAuthAttributes["hash"] == "" {
		return false, errors.New("Missing response hash attribute")
	}

	ph := &PayloadHash{
		ContentType: res.Header.Get("Content-Type"),
		Payload:     c.Option.Payload,
		Alg:         c.Credential.Alg,
	}
	if ph.String() != serverAuthAttributes["hash"] {
		return false, errors.New("Bad response payload mac")
	}

	return true, nil
}
<commit_msg>Fix bug of  Payload check<commit_after>package hawk

import (
	"errors"
	"net/http"
	"strconv"
)

type Client struct {
	Credential *Credential
	Option     *Option
}

func (c *Client) Header(uri string, method string) (string, error) {
	m := &Mac{
		Type:       Header,
		Credential: c.Credential,
		Uri:        uri,
		Method:     method,
		Option:     c.Option,
	}

	mac, err := m.String()
	if err != nil {
		return "", err
	}

	if c.Option.Hash == "" && (method == "POST" || method == "PUT") {
		ph := &PayloadHash{
			ContentType: c.Option.ContentType,
			Payload:     c.Option.Payload,
			Alg:         c.Credential.Alg,
		}
		c.Option.Hash = ph.String()
	}

	header := "Hawk " +
		`id="` + c.Credential.ID + `"` +
		", " +
		`ts="` + strconv.FormatInt(c.Option.TimeStamp, 10) + `"` +
		", " +
		`nonce="` + c.Option.Nonce + `"`
	if c.Option.Hash != "" {
		header = header + ", " + `hash="` + c.Option.Hash + `"`
	}
	if c.Option.Ext != "" {
		header = header + ", " + `ext="` + c.Option.Ext + `"`
	}
	header = header + ", " + `mac="` + mac + `"`
	if c.Option.App != "" {
		header = header + ", " + `app="` + c.Option.App + `"`
		if c.Option.Dlg != "" {
			header = header + ", " + `dlg="` + c.Option.Dlg + `"`
		}
	}

	return header, nil
}

func (c *Client) Authenticate(res *http.Response) (bool, error) {
	artifacts := *c.Option

	wah := res.Header.Get("WWW-Authenticate")
	if wah != "" {
		// TODO: validate WWW-Authenticate Header
	}

	sah := res.Header.Get("Server-Authorization")
	serverAuthAttributes := parseHawkHeader(sah)

	artifacts.Ext = serverAuthAttributes["ext"]
	artifacts.Hash = serverAuthAttributes["hash"]

	m := &Mac{
		Type:       Response,
		Credential: c.Credential,
		Uri:        res.Request.URL.String(),
		Method:     res.Request.Method,
		Option:     &artifacts,
	}

	mac, err := m.String()
	if err != nil {
		return false, err
	}
	if mac != serverAuthAttributes["mac"] {
		return false, errors.New("Bad response mac")
	}

	if c.Option.Payload == "" && res.Request.Method != "POST" && res.Request.Method != "PUT" {
		return true, nil
	}

	if serverAuthAttributes["hash"] == "" {
		return false, errors.New("Missing response hash attribute")
	}

	ph := &PayloadHash{
		ContentType: res.Header.Get("Content-Type"),
		Payload:     c.Option.Payload,
		Alg:         c.Credential.Alg,
	}
	if ph.String() != serverAuthAttributes["hash"] {
		return false, errors.New("Bad response payload mac")
	}

	return true, nil
}
<|endoftext|>


<commit_before>/*
client.go implements RFC 6143 §7.5 Client-to-Server Messages.
See http://tools.ietf.org/html/rfc6143#section-7.5 for more info.
*/
package vnc

import (
	"fmt"
	"strings"
	"unicode"
)

const (
	setPixelFormatMsg = uint8(iota)
	_
	setEncodingsMsg
	framebufferUpdateRequestMsg
	keyEventMsg
	pointerEventMsg
	clientCutTextMsg
)

type SetPixelFormatMessage struct {
	Msg uint8       // message-type
	_   [3]byte     // padding
	PF  PixelFormat // pixel-format
}

// SetPixelFormat sets the format in which pixel values should be sent
// in FramebufferUpdate messages from the server.
//
// See RFC 6143 Section 7.5.1
func (c *ClientConn) SetPixelFormat(pf PixelFormat) error {
	msg := SetPixelFormatMessage{
		Msg: setPixelFormatMsg,
		PF:  pf,
	}
	if err := c.send(msg); err != nil {
		return err
	}

	// Invalidate the color map.
	if pf.TrueColor == RFBFalse {
		c.colorMap = [256]Color{}
	}

	c.pixelFormat = pf
	return nil
}

// SetEncodingsMessage holds the wire format message, sans encoding-type field.
type SetEncodingsMessage struct {
	Msg     uint8   // message-type
	_       [1]byte // padding
	NumEncs uint16  // number-of-encodings
}

// SetEncodings sets the encoding types in which the pixel data can be sent
// from the server. After calling this method, the encs slice given should not
// be modified.
//
// See RFC 6143 Section 7.5.2
func (c *ClientConn) SetEncodings(e []Encoding) error {
	// Prepare message.
	msg := SetEncodingsMessage{
		Msg:     setEncodingsMsg,
		NumEncs: uint16(len(e)),
	}
	var encs []int32 // encoding-type

	for _, v := range e {
		encs = append(encs, int32(v.Type()))
	}

	// Send message.
	if err := c.send(msg); err != nil {
		return err
	}
	if err := c.sendN(encs); err != nil {
		return err
	}

	c.encodings = e
	return nil
}

// FramebufferUpdateRequestMessage holds the wire format message.
type FramebufferUpdateRequestMessage struct {
	Msg           uint8  // message-type
	Inc           uint8  // incremental
	X, Y          uint16 // x-, y-position
	Width, Height uint16 // width, height
}

// Requests a framebuffer update from the server. There may be an indefinite
// time between the request and the actual framebuffer update being received.
//
// See RFC 6143 Section 7.5.3
func (c *ClientConn) FramebufferUpdateRequest(inc uint8, x, y, w, h uint16) error {
	msg := FramebufferUpdateRequestMessage{framebufferUpdateRequestMsg, inc, x, y, w, h}
	if err := c.send(&msg); err != nil {
		return err
	}
	return nil
}

// KeyEventMessage holds the wire format message.
type KeyEventMessage struct {
	Msg      uint8   // message-type
	DownFlag uint8   // down-flag
	_        [2]byte // padding
	Key      uint32  // key
}

const (
	PressKey   = true
	ReleaseKey = false
)

// KeyEvent indicates a key press or release and sends it to the server.
// The key is indicated using the X Window System "keysym" value. Use
// Google to find a reference of these values. To simulate a key press,
// you must send a key with both a down event, and a non-down event.
//
// See RFC 6143 Section 7.5.4.
func (c *ClientConn) KeyEvent(keysym uint32, down bool) error {
	var downFlag uint8 = RFBFalse
	if down {
		downFlag = RFBTrue
	}

	msg := KeyEventMessage{keyEventMsg, downFlag, [2]byte{}, keysym}
	if err := c.send(msg); err != nil {
		return err
	}

	settleUI()
	return nil
}

// ButtonMask represents a mask of pointer presses/releases.
type ButtonMask uint8

// All available button mask components.
const (
	ButtonLeft ButtonMask = 1 << iota
	ButtonMiddle
	ButtonRight
	Button4
	Button5
	Button6
	Button7
	Button8
	ButtonNone = ButtonMask(0)
)

// PointerEventMessage holds the wire format message.
type PointerEventMessage struct {
	Msg  uint8  // message-type
	Mask uint8  // button-mask
	X, Y uint16 // x-, y-position
}

// PointerEvent indicates that pointer movement or a pointer button
// press or release.
//
// The mask is a bitwise mask of various ButtonMask values. When a button
// is set, it is pressed, when it is unset, it is released.
//
// See RFC 6143 Section 7.5.5
func (c *ClientConn) PointerEvent(mask ButtonMask, x, y uint16) error {
	msg := PointerEventMessage{pointerEventMsg, uint8(mask), x, y}
	if err := c.send(msg); err != nil {
		return err
	}

	settleUI()
	return nil
}

// ClientCutTextMessage holds the wire format message, sans the text field.
type ClientCutTextMessage struct {
	Msg    uint8   // message-type
	_      [3]byte // padding
	Length uint32  // length
}

// ClientCutText tells the server that the client has new text in its cut buffer.
// The text string MUST only contain Latin-1 characters. This encoding
// is compatible with Go's native string format, but can only use up to
// unicode.MaxLatin1 values.
//
// See RFC 6143 Section 7.5.6
func (c *ClientConn) ClientCutText(text string) error {
	for _, char := range text {
		if char > unicode.MaxLatin1 {
			return NewVNCError(fmt.Sprintf("Character '%s' is not valid Latin-1", char))
		}
	}

	// Strip carriage-return (0x0d) chars.
	// From RFC: "Ends of lines are represented by the newline character (0x0a)
	// alone. No carriage-return (0x0d) is used."
	text = strings.Join(strings.Split(text, "\r"), "")

	msg := ClientCutTextMessage{
		Msg:    clientCutTextMsg,
		Length: uint32(len(text)),
	}
	if err := c.send(msg); err != nil {
		return err
	}
	if err := c.sendN([]byte(text)); err != nil {
		return err
	}

	settleUI()
	return nil
}

//
// Constants to use for KeyEvents PointerEvents.
//

// Latin 1 (byte 3 = 0)
// ISO/IEC 8859-1 = Unicode U+0020..U+00FF
const (
	KeySpace = iota + 0x0020
	KeyExclam
	KeyQuoteDbl
	KeyNumberSign
	KeyDollar
	KeyPercent
	KeyAmpersand
	KeyApostrophe
	KeyParenLeft
	KeyParenRight
	KeyAsterisk
	KeyPlus
	KeyComma
	KeyMinus
	KeyPeriod
	KeySlash
	Key0
	Key1
	Key2
	Key3
	Key4
	Key5
	Key6
	Key7
	Key8
	Key9
	KeyColon
	KeySemicolon
	KeyLess
	KeyEqual
	KeyGreater
	KeyQuestion
	KeyAt
	KeyA
	KeyB
	KeyC
	KeyD
	KeyE
	KeyF
	KeyG
	KeyH
	KeyI
	KeyJ
	KeyK
	KeyL
	KeyM
	KeyN
	KeyO
	KeyP
	KeyQ
	KeyR
	KeyS
	KeyT
	KeyU
	KeyV
	KeyW
	KeyX
	KeyY
	KeyZ
	KeyBracketLeft
	KeyBackslash
	KeyBracketRight
	KeyAsciiCircum
	KeyUnderscore
	KeyGrave
	Keya
	Keyb
	Keyc
	Keyd
	Keye
	Keyf
	Keyg
	Keyh
	Keyi
	Keyj
	Keyk
	Keyl
	Keym
	Keyn
	Keyo
	Keyp
	Keyq
	Keyr
	Keys
	Keyt
	Keyu
	Keyv
	Keyw
	Keyx
	Keyy
	Keyz
	KeyBraceLeft
	KeyBar
	KeyBraceRight
	KeyAsciiTilde
)
const (
	KeyBackspace = iota + 0xff08
	KeyTab
	KeyLinefeed
	KeyClear
	_
	KeyReturn
)
const (
	KeyPause      = 0xff13
	KeyScrollLock = 0xff14
	KeySysReq     = 0xff15
	KeyEscape     = 0xff1b
)
const (
	KeyHome = iota + 0xff50
	KeyLeft
	KeyUp
	KeyRight
	KeyDown
	KeyPageUp
	KeyPageDown
	KeyEnd
	KeyInsert = 0xff63
)
const (
	KeyF1 = iota + 0xffbe
	KeyF2
	KeyF3
	KeyF4
	KeyF5
	KeyF6
	KeyF7
	KeyF8
	KeyF9
	KeyF10
	KeyF11
	KeyF12
)
const (
	KeyShiftLeft = iota + 0xffe1
	KeyShiftRight
	KeyControlLeft
	KeyControlRight
	KeyCapsLock
	_
	_
	_
	KeyAltLeft
	KeyAltRight
	KeyDelete = 0xffff
)
<commit_msg>Added missing SetPixelFormatMessage comment<commit_after>/*
client.go implements RFC 6143 §7.5 Client-to-Server Messages.
See http://tools.ietf.org/html/rfc6143#section-7.5 for more info.
*/
package vnc

import (
	"fmt"
	"strings"
	"unicode"
)

const (
	setPixelFormatMsg = uint8(iota)
	_
	setEncodingsMsg
	framebufferUpdateRequestMsg
	keyEventMsg
	pointerEventMsg
	clientCutTextMsg
)

// SetPixelFormatMessage holds the wire format message.
type SetPixelFormatMessage struct {
	Msg uint8       // message-type
	_   [3]byte     // padding
	PF  PixelFormat // pixel-format
}

// SetPixelFormat sets the format in which pixel values should be sent
// in FramebufferUpdate messages from the server.
//
// See RFC 6143 Section 7.5.1
func (c *ClientConn) SetPixelFormat(pf PixelFormat) error {
	msg := SetPixelFormatMessage{
		Msg: setPixelFormatMsg,
		PF:  pf,
	}
	if err := c.send(msg); err != nil {
		return err
	}

	// Invalidate the color map.
	if pf.TrueColor == RFBFalse {
		c.colorMap = [256]Color{}
	}

	c.pixelFormat = pf
	return nil
}

// SetEncodingsMessage holds the wire format message, sans encoding-type field.
type SetEncodingsMessage struct {
	Msg     uint8   // message-type
	_       [1]byte // padding
	NumEncs uint16  // number-of-encodings
}

// SetEncodings sets the encoding types in which the pixel data can be sent
// from the server. After calling this method, the encs slice given should not
// be modified.
//
// See RFC 6143 Section 7.5.2
func (c *ClientConn) SetEncodings(e []Encoding) error {
	// Prepare message.
	msg := SetEncodingsMessage{
		Msg:     setEncodingsMsg,
		NumEncs: uint16(len(e)),
	}
	var encs []int32 // encoding-type

	for _, v := range e {
		encs = append(encs, int32(v.Type()))
	}

	// Send message.
	if err := c.send(msg); err != nil {
		return err
	}
	if err := c.sendN(encs); err != nil {
		return err
	}

	c.encodings = e
	return nil
}

// FramebufferUpdateRequestMessage holds the wire format message.
type FramebufferUpdateRequestMessage struct {
	Msg           uint8  // message-type
	Inc           uint8  // incremental
	X, Y          uint16 // x-, y-position
	Width, Height uint16 // width, height
}

// Requests a framebuffer update from the server. There may be an indefinite
// time between the request and the actual framebuffer update being received.
//
// See RFC 6143 Section 7.5.3
func (c *ClientConn) FramebufferUpdateRequest(inc uint8, x, y, w, h uint16) error {
	msg := FramebufferUpdateRequestMessage{framebufferUpdateRequestMsg, inc, x, y, w, h}
	if err := c.send(&msg); err != nil {
		return err
	}
	return nil
}

// KeyEventMessage holds the wire format message.
type KeyEventMessage struct {
	Msg      uint8   // message-type
	DownFlag uint8   // down-flag
	_        [2]byte // padding
	Key      uint32  // key
}

const (
	PressKey   = true
	ReleaseKey = false
)

// KeyEvent indicates a key press or release and sends it to the server.
// The key is indicated using the X Window System "keysym" value. Use
// Google to find a reference of these values. To simulate a key press,
// you must send a key with both a down event, and a non-down event.
//
// See RFC 6143 Section 7.5.4.
func (c *ClientConn) KeyEvent(keysym uint32, down bool) error {
	var downFlag uint8 = RFBFalse
	if down {
		downFlag = RFBTrue
	}

	msg := KeyEventMessage{keyEventMsg, downFlag, [2]byte{}, keysym}
	if err := c.send(msg); err != nil {
		return err
	}

	settleUI()
	return nil
}

// ButtonMask represents a mask of pointer presses/releases.
type ButtonMask uint8

// All available button mask components.
const (
	ButtonLeft ButtonMask = 1 << iota
	ButtonMiddle
	ButtonRight
	Button4
	Button5
	Button6
	Button7
	Button8
	ButtonNone = ButtonMask(0)
)

// PointerEventMessage holds the wire format message.
type PointerEventMessage struct {
	Msg  uint8  // message-type
	Mask uint8  // button-mask
	X, Y uint16 // x-, y-position
}

// PointerEvent indicates that pointer movement or a pointer button
// press or release.
//
// The mask is a bitwise mask of various ButtonMask values. When a button
// is set, it is pressed, when it is unset, it is released.
//
// See RFC 6143 Section 7.5.5
func (c *ClientConn) PointerEvent(mask ButtonMask, x, y uint16) error {
	msg := PointerEventMessage{pointerEventMsg, uint8(mask), x, y}
	if err := c.send(msg); err != nil {
		return err
	}

	settleUI()
	return nil
}

// ClientCutTextMessage holds the wire format message, sans the text field.
type ClientCutTextMessage struct {
	Msg    uint8   // message-type
	_      [3]byte // padding
	Length uint32  // length
}

// ClientCutText tells the server that the client has new text in its cut buffer.
// The text string MUST only contain Latin-1 characters. This encoding
// is compatible with Go's native string format, but can only use up to
// unicode.MaxLatin1 values.
//
// See RFC 6143 Section 7.5.6
func (c *ClientConn) ClientCutText(text string) error {
	for _, char := range text {
		if char > unicode.MaxLatin1 {
			return NewVNCError(fmt.Sprintf("Character '%s' is not valid Latin-1", char))
		}
	}

	// Strip carriage-return (0x0d) chars.
	// From RFC: "Ends of lines are represented by the newline character (0x0a)
	// alone. No carriage-return (0x0d) is used."
	text = strings.Join(strings.Split(text, "\r"), "")

	msg := ClientCutTextMessage{
		Msg:    clientCutTextMsg,
		Length: uint32(len(text)),
	}
	if err := c.send(msg); err != nil {
		return err
	}
	if err := c.sendN([]byte(text)); err != nil {
		return err
	}

	settleUI()
	return nil
}

//
// Constants to use for KeyEvents PointerEvents.
//

// Latin 1 (byte 3 = 0)
// ISO/IEC 8859-1 = Unicode U+0020..U+00FF
const (
	KeySpace = iota + 0x0020
	KeyExclam
	KeyQuoteDbl
	KeyNumberSign
	KeyDollar
	KeyPercent
	KeyAmpersand
	KeyApostrophe
	KeyParenLeft
	KeyParenRight
	KeyAsterisk
	KeyPlus
	KeyComma
	KeyMinus
	KeyPeriod
	KeySlash
	Key0
	Key1
	Key2
	Key3
	Key4
	Key5
	Key6
	Key7
	Key8
	Key9
	KeyColon
	KeySemicolon
	KeyLess
	KeyEqual
	KeyGreater
	KeyQuestion
	KeyAt
	KeyA
	KeyB
	KeyC
	KeyD
	KeyE
	KeyF
	KeyG
	KeyH
	KeyI
	KeyJ
	KeyK
	KeyL
	KeyM
	KeyN
	KeyO
	KeyP
	KeyQ
	KeyR
	KeyS
	KeyT
	KeyU
	KeyV
	KeyW
	KeyX
	KeyY
	KeyZ
	KeyBracketLeft
	KeyBackslash
	KeyBracketRight
	KeyAsciiCircum
	KeyUnderscore
	KeyGrave
	Keya
	Keyb
	Keyc
	Keyd
	Keye
	Keyf
	Keyg
	Keyh
	Keyi
	Keyj
	Keyk
	Keyl
	Keym
	Keyn
	Keyo
	Keyp
	Keyq
	Keyr
	Keys
	Keyt
	Keyu
	Keyv
	Keyw
	Keyx
	Keyy
	Keyz
	KeyBraceLeft
	KeyBar
	KeyBraceRight
	KeyAsciiTilde
)
const (
	KeyBackspace = iota + 0xff08
	KeyTab
	KeyLinefeed
	KeyClear
	_
	KeyReturn
)
const (
	KeyPause      = 0xff13
	KeyScrollLock = 0xff14
	KeySysReq     = 0xff15
	KeyEscape     = 0xff1b
)
const (
	KeyHome = iota + 0xff50
	KeyLeft
	KeyUp
	KeyRight
	KeyDown
	KeyPageUp
	KeyPageDown
	KeyEnd
	KeyInsert = 0xff63
)
const (
	KeyF1 = iota + 0xffbe
	KeyF2
	KeyF3
	KeyF4
	KeyF5
	KeyF6
	KeyF7
	KeyF8
	KeyF9
	KeyF10
	KeyF11
	KeyF12
)
const (
	KeyShiftLeft = iota + 0xffe1
	KeyShiftRight
	KeyControlLeft
	KeyControlRight
	KeyCapsLock
	_
	_
	_
	KeyAltLeft
	KeyAltRight
	KeyDelete = 0xffff
)
<|endoftext|>


<commit_before>// Copyright 2013 Francisco Souza. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package docker provides a client for the Docker remote API.
//
// See http://goo.gl/mxyql for more details on the remote API.
package docker

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/dotcloud/docker"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"reflect"
	"strconv"
	"strings"
)

// ErrInvalidEndpoint is the error returned by NewClient when the given
// endpoint is invalid.
var ErrInvalidEndpoint = errors.New("Invalid endpoint")

// Client is the basic type of this package. It provides methods for
// interaction with the API.
type Client struct {
	endpoint string
	client   *http.Client
}

// NewClient returns a Client instance ready for communication with the
// given server endpoint.
func NewClient(endpoint string) (*Client, error) {
	if !isValid(endpoint) {
		return nil, ErrInvalidEndpoint
	}
	return &Client{endpoint: endpoint, client: http.DefaultClient}, nil
}

func (c *Client) do(method, path string, data interface{}) ([]byte, int, error) {
	var params io.Reader
	if data != nil {
		buf, err := json.Marshal(data)
		if err != nil {
			return nil, -1, err
		}
		params = bytes.NewBuffer(buf)
	}
	req, err := http.NewRequest(method, c.getURL(path), params)
	if err != nil {
		return nil, -1, err
	}
	req.Header.Set("User-Agent", "Docker-Client/"+docker.VERSION)
	if data != nil {
		req.Header.Set("Content-Type", "application/json")
	} else if method == "POST" {
		req.Header.Set("Content-Type", "plain/text")
	}
	resp, err := c.client.Do(req)
	if err != nil {
		if strings.Contains(err.Error(), "connection refused") {
			return nil, -1, fmt.Errorf("Can't connect to docker daemon. Is 'docker -d' running on this host?")
		}
		return nil, -1, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, -1, err
	}
	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
		return nil, resp.StatusCode, newAPIClientError(resp.StatusCode, body)
	}
	return body, resp.StatusCode, nil
}

func (c *Client) getURL(path string) string {
	return fmt.Sprintf("%s/v%f%s", strings.TrimRight(c.endpoint, "/"), docker.APIVERSION, path)
}

func queryString(opts interface{}) string {
	if opts == nil {
		return ""
	}
	value := reflect.ValueOf(opts)
	if value.Kind() == reflect.Ptr {
		value = value.Elem()
	}
	if value.Kind() != reflect.Struct {
		return ""
	}
	items := url.Values(map[string][]string{})
	for i := 0; i < value.NumField(); i++ {
		field := value.Type().Field(i)
		key := field.Tag.Get("qs")
		if key == "" {
			key = strings.ToLower(field.Name)
		}
		v := value.Field(i)
		switch v.Kind() {
		case reflect.Bool:
			if v.Bool() {
				items.Add(key, "1")
			}
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			if v.Int() > 0 {
				items.Add(key, strconv.FormatInt(v.Int(), 10))
			}
		case reflect.Float32, reflect.Float64:
			if v.Float() > 0 {
				items.Add(key, strconv.FormatFloat(v.Float(), 'f', -1, 64))
			}
		case reflect.String:
			if v.String() != "" {
				items.Add(key, v.String())
			}
		}
	}
	return items.Encode()
}

type apiClientError struct {
	status  int
	message string
}

func newAPIClientError(status int, body []byte) *apiClientError {
	return &apiClientError{status: status, message: string(body)}
}

func (e *apiClientError) Error() string {
	return fmt.Sprintf("API error (%d): %s", e.status, e.message)
}

func isValid(endpoint string) bool {
	u, err := url.Parse(endpoint)
	if err != nil {
		return false
	}
	if u.Scheme != "http" && u.Scheme != "https" {
		return false
	}
	_, port, err := net.SplitHostPort(u.Host)
	if err != nil {
		if e, ok := err.(*net.AddrError); ok {
			return e.Err == "missing port in address"
		}
		return false
	}
	number, err := strconv.ParseInt(port, 10, 64)
	return err == nil && number > 0 && number < 65536
}
<commit_msg>client: don't use docker.VERSION<commit_after>// Copyright 2013 Francisco Souza. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package docker provides a client for the Docker remote API.
//
// See http://goo.gl/mxyql for more details on the remote API.
package docker

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/dotcloud/docker"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"reflect"
	"strconv"
	"strings"
)

// ErrInvalidEndpoint is the error returned by NewClient when the given
// endpoint is invalid.
var ErrInvalidEndpoint = errors.New("Invalid endpoint")

// Client is the basic type of this package. It provides methods for
// interaction with the API.
type Client struct {
	endpoint string
	client   *http.Client
}

// NewClient returns a Client instance ready for communication with the
// given server endpoint.
func NewClient(endpoint string) (*Client, error) {
	if !isValid(endpoint) {
		return nil, ErrInvalidEndpoint
	}
	return &Client{endpoint: endpoint, client: http.DefaultClient}, nil
}

func (c *Client) do(method, path string, data interface{}) ([]byte, int, error) {
	var params io.Reader
	if data != nil {
		buf, err := json.Marshal(data)
		if err != nil {
			return nil, -1, err
		}
		params = bytes.NewBuffer(buf)
	}
	req, err := http.NewRequest(method, c.getURL(path), params)
	if err != nil {
		return nil, -1, err
	}
	req.Header.Set("User-Agent", "Docker-Client-API")
	if data != nil {
		req.Header.Set("Content-Type", "application/json")
	} else if method == "POST" {
		req.Header.Set("Content-Type", "plain/text")
	}
	resp, err := c.client.Do(req)
	if err != nil {
		if strings.Contains(err.Error(), "connection refused") {
			return nil, -1, fmt.Errorf("Can't connect to docker daemon. Is 'docker -d' running on this host?")
		}
		return nil, -1, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, -1, err
	}
	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
		return nil, resp.StatusCode, newAPIClientError(resp.StatusCode, body)
	}
	return body, resp.StatusCode, nil
}

func (c *Client) getURL(path string) string {
	return fmt.Sprintf("%s/v%f%s", strings.TrimRight(c.endpoint, "/"), docker.APIVERSION, path)
}

func queryString(opts interface{}) string {
	if opts == nil {
		return ""
	}
	value := reflect.ValueOf(opts)
	if value.Kind() == reflect.Ptr {
		value = value.Elem()
	}
	if value.Kind() != reflect.Struct {
		return ""
	}
	items := url.Values(map[string][]string{})
	for i := 0; i < value.NumField(); i++ {
		field := value.Type().Field(i)
		key := field.Tag.Get("qs")
		if key == "" {
			key = strings.ToLower(field.Name)
		}
		v := value.Field(i)
		switch v.Kind() {
		case reflect.Bool:
			if v.Bool() {
				items.Add(key, "1")
			}
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			if v.Int() > 0 {
				items.Add(key, strconv.FormatInt(v.Int(), 10))
			}
		case reflect.Float32, reflect.Float64:
			if v.Float() > 0 {
				items.Add(key, strconv.FormatFloat(v.Float(), 'f', -1, 64))
			}
		case reflect.String:
			if v.String() != "" {
				items.Add(key, v.String())
			}
		}
	}
	return items.Encode()
}

type apiClientError struct {
	status  int
	message string
}

func newAPIClientError(status int, body []byte) *apiClientError {
	return &apiClientError{status: status, message: string(body)}
}

func (e *apiClientError) Error() string {
	return fmt.Sprintf("API error (%d): %s", e.status, e.message)
}

func isValid(endpoint string) bool {
	u, err := url.Parse(endpoint)
	if err != nil {
		return false
	}
	if u.Scheme != "http" && u.Scheme != "https" {
		return false
	}
	_, port, err := net.SplitHostPort(u.Host)
	if err != nil {
		if e, ok := err.(*net.AddrError); ok {
			return e.Err == "missing port in address"
		}
		return false
	}
	number, err := strconv.ParseInt(port, 10, 64)
	return err == nil && number > 0 && number < 65536
}
<|endoftext|>


<commit_before>package bitbucket

import (
	"encoding/json"
	"fmt"

	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/bitbucket"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
)

type Client struct {
	Auth         *auth
	Users        users
	User         user
	Teams        teams
	Repositories *Repositories
	Pagelen      uint64
}

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

func NewOAuth(i, s string) *Client {
	a := &auth{app_id: 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: %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
	if _, err := fmt.Scan(&code); err != nil {
		log.Fatal(err)
	}
	log.Println(code)
	tok, err := conf.Exchange(ctx, code)
	if err != nil {
		log.Fatal(err)
	}
	a.token = *tok
	return injectClient(a)
}

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

const DEFAULT_PAGE_LENGHT = 10

func injectClient(a *auth) *Client {
	c := &Client{Auth: a, Pagelen: DEFAULT_PAGE_LENGHT}
	c.Repositories = &Repositories{
		c:                  c,
		PullRequests:       &PullRequests{c: c},
		Repository:         &Repository{c: c},
		Commits:            &Commits{c: c},
		Diff:               &Diff{c: c},
		BranchRestrictions: &BranchRestrictions{c: c},
		Webhooks:           &Webhooks{c: c},
	}
	c.Users = &Users{c: c}
	c.User = &User{c: c}
	c.Teams = &Teams{c: c}
	return c
}

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_LENGHT {
			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()
		}
	}

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

	if err != nil {
		return nil, err
	}

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

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

	if (resp.StatusCode != http.StatusOK) && (resp.StatusCode != http.StatusCreated) {
		return nil, fmt.Errorf(resp.Status)
	}

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

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

	var result interface{}
	err = json.Unmarshal(resBodyBytes, &result)
	if err != nil {
		return nil, err
	}

	resultMap, isMap := result.(map[string]interface{})
	if isMap {
		nextIn := resultMap["next"]
		valuesIn := resultMap["values"]
		if nextIn != nil && valuesIn != nil {
			nextUrl := nextIn.(string)
			if nextUrl != "" {
				valuesSlice := valuesIn.([]interface{})
				if valuesSlice != nil {
					nextResult, err := c.execute(method, nextUrl, text)
					if err != nil {
						return nil, err
					}
					nextResultMap, isNextMap := nextResult.(map[string]interface{})
					if !isNextMap {
						return nil, fmt.Errorf("next page result is not map, it's %T", nextResult)
					}
					nextValuesIn := nextResultMap["values"]
					if nextValuesIn == nil {
						return nil, fmt.Errorf("next page result has no values")
					}
					nextValuesSlice, isSlice := nextValuesIn.([]interface{})
					if !isSlice {
						return nil, fmt.Errorf("next page result 'values' is not slice")
					}
					valuesSlice = append(valuesSlice, nextValuesSlice...)
					resultMap["values"] = valuesSlice
					delete(resultMap, "page")
					delete(resultMap, "pagelen")
					delete(resultMap, "size")
					result = resultMap
				}
			}
		}
	}

	return result, nil
}

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

	if len(args) == 1 && args[0] == "" {
		return GetApiBaseURL() + template
	}
	return GetApiBaseURL() + fmt.Sprintf(template, args...)
}
<commit_msg>makeing it easier to read<commit_after>package bitbucket

import (
	"encoding/json"
	"fmt"

	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/bitbucket"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
)

type Client struct {
	Auth         *auth
	Users        users
	User         user
	Teams        teams
	Repositories *Repositories
	Pagelen      uint64
}

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

func NewOAuth(i, s string) *Client {
	a := &auth{app_id: 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)
}

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

const DEFAULT_PAGE_LENGHT = 10

func injectClient(a *auth) *Client {
	c := &Client{Auth: a, Pagelen: DEFAULT_PAGE_LENGHT}
	c.Repositories = &Repositories{
		c:                  c,
		PullRequests:       &PullRequests{c: c},
		Repository:         &Repository{c: c},
		Commits:            &Commits{c: c},
		Diff:               &Diff{c: c},
		BranchRestrictions: &BranchRestrictions{c: c},
		Webhooks:           &Webhooks{c: c},
	}
	c.Users = &Users{c: c}
	c.User = &User{c: c}
	c.Teams = &Teams{c: c}
	return c
}

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_LENGHT {
			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()
		}
	}

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

	if err != nil {
		return nil, err
	}

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

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

	if (resp.StatusCode != http.StatusOK) && (resp.StatusCode != http.StatusCreated) {
		return nil, fmt.Errorf(resp.Status)
	}

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

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

	var result interface{}
	err = json.Unmarshal(resBodyBytes, &result)
	if err != nil {
		return nil, err
	}

	resultMap, isMap := result.(map[string]interface{})
	if isMap {
		nextIn := resultMap["next"]
		valuesIn := resultMap["values"]
		if nextIn != nil && valuesIn != nil {
			nextUrl := nextIn.(string)
			if nextUrl != "" {
				valuesSlice := valuesIn.([]interface{})
				if valuesSlice != nil {
					nextResult, err := c.execute(method, nextUrl, text)
					if err != nil {
						return nil, err
					}
					nextResultMap, isNextMap := nextResult.(map[string]interface{})
					if !isNextMap {
						return nil, fmt.Errorf("next page result is not map, it's %T", nextResult)
					}
					nextValuesIn := nextResultMap["values"]
					if nextValuesIn == nil {
						return nil, fmt.Errorf("next page result has no values")
					}
					nextValuesSlice, isSlice := nextValuesIn.([]interface{})
					if !isSlice {
						return nil, fmt.Errorf("next page result 'values' is not slice")
					}
					valuesSlice = append(valuesSlice, nextValuesSlice...)
					resultMap["values"] = valuesSlice
					delete(resultMap, "page")
					delete(resultMap, "pagelen")
					delete(resultMap, "size")
					result = resultMap
				}
			}
		}
	}

	return result, nil
}

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

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


<commit_before>// Package pusher implements client library for pusher.com socket
package pusher

import (
	"code.google.com/p/go.net/websocket"
)

type Client struct {
	key      string
	conn     *websocket.Conn
	channels []*Channel
}

func New(key string) (*Client, error) {
	url := "ws://ws.pusherapp.com:80/app/" + key + "?protocol=7"

	ws, err := websocket.Dial(url, "", "http://localhost/")
	if err != nil {
		return nil, err
	}

	client := &Client{key, ws, nil}

	go client.poll()

	return client, nil
}

func (c *Client) poll() {
	for {
		var msg Message
		err := websocket.JSON.Receive(c.conn, &msg)
		if err != nil {
			panic(err)
		}

		c.processMessage(&msg)
	}
}

func (c *Client) processMessage(msg *Message) {
	switch msg.Event {
	case "pusher:connection_established":
		//println(msg.Data.(map[string]string)["socket_id"])
	case "connection_established":
		//println(msg.Data.(map[string]string)["socket_id"])
	case "pusher:error":
		//println(msg.Data.(map[string]interface{})["message"].(string))
	default:
		for _, channel := range c.channels {
			if channel.Name == msg.Channel {
				channel.processMessage(msg)
			}
		}
	}
}

func (c *Client) Disconnect() error {
	return c.conn.Close()
}

func (c *Client) Channel(name string) *Channel {
	for _, channel := range c.channels {
		if channel.Name == name {
			return channel
		}
	}

	channel := NewChannel(name)
	c.channels = append(c.channels, channel)
	websocket.JSON.Send(c.conn, NewSubscribeMessage(name))

	return channel
}
<commit_msg>Refactor Client.processMessage()<commit_after>// Package pusher implements client library for pusher.com socket
package pusher

import (
	"code.google.com/p/go.net/websocket"
)

type Client struct {
	key      string
	conn     *websocket.Conn
	channels []*Channel
}

func New(key string) (*Client, error) {
	url := "ws://ws.pusherapp.com:80/app/" + key + "?protocol=7"

	ws, err := websocket.Dial(url, "", "http://localhost/")
	if err != nil {
		return nil, err
	}

	client := &Client{
		key:  key,
		conn: ws,
		channels: []*Channel{
			NewChannel("pusher"),
			NewChannel("pusher_internal"),
		},
	}

	go client.poll()

	return client, nil
}

func (c *Client) poll() {
	for {
		var msg Message
		err := websocket.JSON.Receive(c.conn, &msg)
		if err != nil {
			panic(err)
		}

		c.processMessage(&msg)
	}
}

func (c *Client) processMessage(msg *Message) {
	for _, channel := range c.channels {
		if channel.Name == msg.Channel {
			channel.processMessage(msg)
		}
	}
}

func (c *Client) Disconnect() error {
	return c.conn.Close()
}

func (c *Client) Channel(name string) *Channel {
	for _, channel := range c.channels {
		if channel.Name == name {
			return channel
		}
	}

	channel := NewChannel(name)
	c.channels = append(c.channels, channel)
	websocket.JSON.Send(c.conn, NewSubscribeMessage(name))

	return channel
}
<|endoftext|>


<commit_before>package compilerapi

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

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

type Client struct {
}

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

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

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

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

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

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

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

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

	values := url.Values{}
	values.Set("js_code", string(jsCode[:]))

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

	values.Set("output_format", "json")
	values.Add("output_info", "compiled_code")
	values.Add("output_info", "statistics")
	values.Add("output_info", "warnings")
	values.Add("output_info", "errors")

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

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

	httpClient := http.Client{}

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

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

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

	return &output
}
<commit_msg>default to ECMASCRIPT5_STRICT for now<commit_after>package compilerapi

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

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

type Client struct {
}

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

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

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

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

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

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

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

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

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

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

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

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

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

	httpClient := http.Client{}

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

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

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

	return &output
}
<|endoftext|>


<commit_before>package gohttp

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

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

type Option struct {
	Address        []string
	ConnectTimeout time.Duration
	TLSTimeout     time.Duration
	Timeout        time.Duration
	Agent          string
	Delay          time.Duration
	MaxRedirects   int
	MaxIdleConns   int
	Http2          bool
}

type clientResource struct {
	Transport http.RoundTripper
	Jar       http.CookieJar
}

type useInfo struct {
	Index    int
	LastTime time.Time
}

var defaultOption = &Option{
	ConnectTimeout: 30000 * time.Millisecond,
	TLSTimeout:     15 * time.Second,
	Agent:          "gohttp v1.0",
	Address:        make([]string, 0),
	MaxRedirects:   -1,
	MaxIdleConns:   0,
	Http2:          false,
}

var debug = false
var defaultDialer = &net.Dialer{Timeout: defaultOption.ConnectTimeout}
var defaultTransport = MakeTransport("0.0.0.0")
var defaultCookiejar = MakeCookiejar()

// var proxyTransport *http.Transport

var hostDelay = make(map[string]time.Duration)
var hostDelayLock sync.RWMutex

var defaultGetter = NewIpRollClient(defaultOption.Address...)

func MakeCookiejar() http.CookieJar {
	cookiejarOptions := cookiejar.Options{
		PublicSuffixList: publicsuffix.List,
	}
	jar, _ := cookiejar.New(&cookiejarOptions)

	return jar
}

func MakeClient(transport http.RoundTripper, jar http.CookieJar) *http.Client {
	return &http.Client{Jar: jar, Transport: transport, Timeout: 60 * time.Second}
}

func MakeTransport(ip string) *http.Transport {
	addr, _ := net.ResolveTCPAddr("tcp", ip+":0")
	dialer := &net.Dialer{
		Timeout:   defaultOption.ConnectTimeout,
		LocalAddr: addr,
	}
	transport := &http.Transport{
		Dial:                dialer.Dial,
		Proxy:               http.ProxyFromEnvironment,
		MaxIdleConnsPerHost: defaultOption.MaxIdleConns,
		TLSHandshakeTimeout: defaultOption.TLSTimeout,
	}

	if defaultOption.MaxIdleConns <= 0 {
		transport.DisableKeepAlives = true
	}

	if defaultOption.Http2 {
		transport.Dial = nil
	}

	return transport
}

func SetDebug(d bool) {
	defer hostDelayLock.Unlock()
	hostDelayLock.Lock()

	debug = d
}

func IsDebug() bool {
	defer hostDelayLock.RUnlock()
	hostDelayLock.RLock()
	return debug
}

func SetHostDelay(host string, delay time.Duration) {
	defer hostDelayLock.Unlock()
	hostDelayLock.Lock()
	if d, ok := hostDelay[host]; ok && delay > d {
		hostDelay[host] = delay
		return
	}
	hostDelay[host] = delay
}

func GetHostDelay(host string) time.Duration {
	defer hostDelayLock.RUnlock()
	hostDelayLock.RLock()

	if d, ok := hostDelay[host]; ok {
		return d
	}

	return defaultOption.Delay
}

func SetOption(option *Option) {
	if option.Agent != "" {
		defaultOption.Agent = option.Agent
	}

	if option.ConnectTimeout > 0 {
		defaultOption.ConnectTimeout = option.ConnectTimeout
	}

	if option.TLSTimeout > 0 {
		defaultOption.TLSTimeout = option.TLSTimeout
	}

	if option.Delay > 0 {
		defaultOption.Delay = option.Delay
	}

	if option.Address != nil && len(option.Address) > 0 {
		defaultOption.Address = make([]string, 0)
		defaultOption.Address = append(defaultOption.Address, option.Address...)
		defaultGetter = NewIpRollClient(defaultOption.Address...)
	}

	if option.MaxRedirects > 0 {
		defaultOption.MaxRedirects = option.MaxRedirects
	}

	if option.MaxIdleConns > 0 {
		defaultOption.MaxIdleConns = option.MaxIdleConns
		defaultTransport.MaxIdleConnsPerHost = option.MaxIdleConns
	}

	if option.Http2 {
		defaultOption.Http2 = option.Http2
		defaultTransport.Dial = nil
	}
}

func ResetCookie(urlstr string) error {
	uri, err := url.Parse(urlstr)
	if err != nil {
		return err
	}
	cookies := defaultCookiejar.Cookies(uri)
	for _, c := range cookies {
		c.Expires = time.Now().Add(-1 * time.Hour)
	}
	defaultCookiejar.SetCookies(uri, cookies)

	defaultGetter.ResetCookie(uri)

	return nil
}

func GetDefaultDialer() *net.Dialer {
	return defaultDialer
}

func GetDefaultTransport() *http.Transport {
	return defaultTransport
}

func GetDefaultClient() *http.Client {
	return MakeClient(defaultTransport, defaultCookiejar)
}

func GetDefaultGetter() ClientGetter {
	return defaultGetter
}
<commit_msg>add maxperhost;<commit_after>package gohttp

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

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

type Option struct {
	Address         []string
	ConnectTimeout  time.Duration
	TLSTimeout      time.Duration
	Timeout         time.Duration
	Agent           string
	Delay           time.Duration
	MaxRedirects    int
	MaxIdleConns    int
	MaxConnsPerHost int
	Http2           bool
}

type clientResource struct {
	Transport http.RoundTripper
	Jar       http.CookieJar
}

type useInfo struct {
	Index    int
	LastTime time.Time
}

var defaultOption = &Option{
	ConnectTimeout: 30000 * time.Millisecond,
	TLSTimeout:     30 * time.Second,
	Agent:          "gohttp v1.0",
	Address:        make([]string, 0),
	MaxRedirects:   -1,
	MaxIdleConns:   0,
	Http2:          false,
}

var debug = false
var defaultDialer = &net.Dialer{Timeout: defaultOption.ConnectTimeout}
var defaultTransport = MakeTransport("0.0.0.0")
var defaultCookiejar = MakeCookiejar()

// var proxyTransport *http.Transport

var hostDelay = make(map[string]time.Duration)
var hostDelayLock sync.RWMutex

var defaultGetter = NewIpRollClient(defaultOption.Address...)

func MakeCookiejar() http.CookieJar {
	cookiejarOptions := cookiejar.Options{
		PublicSuffixList: publicsuffix.List,
	}
	jar, _ := cookiejar.New(&cookiejarOptions)

	return jar
}

func MakeClient(transport http.RoundTripper, jar http.CookieJar) *http.Client {
	return &http.Client{Jar: jar, Transport: transport, Timeout: 60 * time.Second}
}

func MakeTransport(ip string) *http.Transport {
	addr, _ := net.ResolveTCPAddr("tcp", ip+":0")
	dialer := &net.Dialer{
		Timeout:   defaultOption.ConnectTimeout,
		LocalAddr: addr,
	}
	transport := &http.Transport{
		Dial:                dialer.Dial,
		Proxy:               http.ProxyFromEnvironment,
		MaxIdleConnsPerHost: defaultOption.MaxIdleConns,
		TLSHandshakeTimeout: defaultOption.TLSTimeout,
	}

	if defaultOption.MaxIdleConns <= 0 {
		transport.DisableKeepAlives = true
	}

	if defaultOption.MaxConnsPerHost > 0 {
		transport.MaxConnsPerHost = defaultOption.MaxConnsPerHost
	}

	if defaultOption.Http2 {
		transport.Dial = nil
	}

	return transport
}

func SetDebug(d bool) {
	defer hostDelayLock.Unlock()
	hostDelayLock.Lock()

	debug = d
}

func IsDebug() bool {
	defer hostDelayLock.RUnlock()
	hostDelayLock.RLock()
	return debug
}

func SetHostDelay(host string, delay time.Duration) {
	defer hostDelayLock.Unlock()
	hostDelayLock.Lock()
	if d, ok := hostDelay[host]; ok && delay > d {
		hostDelay[host] = delay
		return
	}
	hostDelay[host] = delay
}

func GetHostDelay(host string) time.Duration {
	defer hostDelayLock.RUnlock()
	hostDelayLock.RLock()

	if d, ok := hostDelay[host]; ok {
		return d
	}

	return defaultOption.Delay
}

func SetOption(option *Option) {
	if option.Agent != "" {
		defaultOption.Agent = option.Agent
	}

	if option.ConnectTimeout > 0 {
		defaultOption.ConnectTimeout = option.ConnectTimeout
	}

	if option.TLSTimeout > 0 {
		defaultOption.TLSTimeout = option.TLSTimeout
	}

	if option.Delay > 0 {
		defaultOption.Delay = option.Delay
	}

	if option.Address != nil && len(option.Address) > 0 {
		defaultOption.Address = make([]string, 0)
		defaultOption.Address = append(defaultOption.Address, option.Address...)
		defaultGetter = NewIpRollClient(defaultOption.Address...)
	}

	if option.MaxRedirects > 0 {
		defaultOption.MaxRedirects = option.MaxRedirects
	}

	if option.MaxIdleConns > 0 {
		defaultOption.MaxIdleConns = option.MaxIdleConns
		defaultTransport.MaxIdleConnsPerHost = option.MaxIdleConns
	}

	if option.MaxConnsPerHost > 0 {
		defaultOption.MaxConnsPerHost = option.MaxIdleConns
		defaultTransport.MaxConnsPerHost = option.MaxConnsPerHost
	}

	if option.Http2 {
		defaultOption.Http2 = option.Http2
		defaultTransport.Dial = nil
	}
}

func ResetCookie(urlstr string) error {
	uri, err := url.Parse(urlstr)
	if err != nil {
		return err
	}
	cookies := defaultCookiejar.Cookies(uri)
	for _, c := range cookies {
		c.Expires = time.Now().Add(-1 * time.Hour)
	}
	defaultCookiejar.SetCookies(uri, cookies)

	defaultGetter.ResetCookie(uri)

	return nil
}

func GetDefaultDialer() *net.Dialer {
	return defaultDialer
}

func GetDefaultTransport() *http.Transport {
	return defaultTransport
}

func GetDefaultClient() *http.Client {
	return MakeClient(defaultTransport, defaultCookiejar)
}

func GetDefaultGetter() ClientGetter {
	return defaultGetter
}
<|endoftext|>


<commit_before>package main

import (
	statsd "github.com/DataDog/datadog-go/statsd"
	log "github.com/Sirupsen/logrus"
	"strconv"
	"strings"
	"errors"
	"regexp"
	"sort"
)

const sampleRate = 1.0

const (
	routerMsg int = iota
	scalingMsg
	sampleMsg
	metricsTag
	releaseMsg
)

var routerMetricsKeys = []string{"dyno", "method", "status", "path", "host", "code", "desc", "at"}
var sampleMetricsKeys = []string{"source"}
var scalingMetricsKeys = []string{"mailer", "web"}
var customMetricsKeys = []string{"media_type", "output_type", "path", "version"}

type Client struct {
	*statsd.Client
	ExcludedTags map[string]bool
}

var statusCode *regexp.Regexp = regexp.MustCompile(`^(?P<Family>\d)\d\d`)

func statsdClient(addr string) (*Client, error) {

	c, err := statsd.New(addr)
	return &Client{c, make(map[string]bool)}, err
}

func (c *Client) sendToStatsd(in chan *logMetrics) {

	var data *logMetrics
	var ok bool
	for {
		data, ok = <-in

		if !ok { //Exit, channel was closed
			return
		}

		log.WithFields(log.Fields{
			"type":   data.typ,
			"app":    data.app,
			"tags":   data.tags,
			"prefix": data.prefix,
		}).Debug("logMetrics received")

		if data.typ == routerMsg {
			c.sendRouterMsg(data)
		} else if data.typ == sampleMsg {
			c.sendSampleMsg(data)
		} else if data.typ == scalingMsg {
			c.sendEvents(*data.app, "heroku", data.events, *data.tags)
			c.sendScalingMsg(data)
		} else if data.typ == metricsTag {
			c.sendMetricsWithTags(data)
		} else if data.typ == releaseMsg {
			c.sendEvents(*data.app, "app", data.events, *data.tags)
		} else {
			log.WithField("type", data.typ).Warn("Unknown log message")
		}
	}
}

func (c *Client) sendEvents(app string, namespace string, events []string, tags []string) {
	for _, v := range events {
		event := statsd.NewEvent(namespace+"/api: "+app, v)
		event.Tags = tags
		c.Event(event)
		log.WithFields(log.Fields{
			"type":  "event",
			"app":   app,
			"value": v,
		}).Info("Event sent")
	}
}

func (c *Client) extractTags(tags []string, permittedTags []string, metrics map[string]logValue) []string {
	for _, mk := range permittedTags {
		if c.ExcludedTags[mk] {
			continue
		}
		if v, ok := metrics[mk]; ok {
			tags = append(tags, mk+":"+v.Val)
		}
	}
	sort.Strings(tags)
	return tags
}

func addStatusFamilyToTags(data *logMetrics, tags []string) []string {
	if val, ok := data.metrics["status"]; ok {
		match := statusCode.FindStringSubmatch(val.Val)
		if len(match) > 1 {
			tags = append(tags, "statusFamily:"+match[1]+"xx")
		}
	}
	return tags
}

func (c *Client) sendRouterMsg(data *logMetrics) {
	tags := c.extractTags(*data.tags, routerMetricsKeys, data.metrics)
	tags = addStatusFamilyToTags(data, tags)

	log.WithFields(log.Fields{
		"app":    *data.app,
		"tags":   *data.tags,
		"prefix": *data.prefix,
	}).Debug("sendRouterMsg")

	conn, err := strconv.ParseFloat(data.metrics["connect"].Val, 10)
	if err != nil {
		log.WithFields(log.Fields{
			"type":   "router",
			"err":    err,
			"metric": "connect",
		}).Info("Could not parse metric value")
		return
	}
	serv, err := strconv.ParseFloat(data.metrics["service"].Val, 10)
	if err != nil {
		log.WithFields(log.Fields{
			"type":   "router",
			"metric": "service",
			"err":    err,
		}).Info("Could not parse metric value")
		return
	}

	err = c.Histogram(*data.prefix+"heroku.router.request.connect", conn, tags, sampleRate)
	if err != nil {
		log.WithField("error", err).Info("Failed to send Histogram")
	}
	err = c.Histogram(*data.prefix+"heroku.router.request.service", serv, tags, sampleRate)
	if err != nil {
		log.WithField("error", err).Info("Failed to send Histogram")
	}
	if data.metrics["at"].Val == "error" {
		err = c.Count(*data.prefix+"heroku.router.error", 1, tags, 0.1)
		if err != nil {
			log.WithField("error", err).Info("Failed to send Count")
		}
	}
}

func (c *Client) sendSampleMsg(data *logMetrics) {
	tags := c.extractTags(*data.tags, sampleMetricsKeys, data.metrics)

	log.WithFields(log.Fields{
		"app":    *data.app,
		"tags":   tags,
		"prefix": *data.prefix,
	}).Debug("sendSampleMsg")

	for k, v := range data.metrics {
		if strings.Index(k, "#") != -1 {
			m := strings.Replace(strings.Split(k, "#")[1], "_", ".", -1)
			vnum, err := strconv.ParseFloat(v.Val, 10)
			if err == nil {
				err = c.Gauge(*data.prefix+"heroku.dyno."+m, vnum, tags, sampleRate)
				if err != nil {
					log.WithField("error", err).Info("Failed to send Gauge")
				}
			} else {
				log.WithFields(log.Fields{
					"type":   "sample",
					"metric": k,
					"err":    err,
				}).Info("Could not parse metric value")
			}
		}
	}
}

func (c *Client) sendScalingMsg(data *logMetrics) {
	tags := *data.tags

	log.WithFields(log.Fields{
		"app":    *data.app,
		"tags":   tags,
		"prefix": *data.prefix,
	}).Debug("sendScalingMsg")

	for _, mk := range scalingMetricsKeys {
		if v, ok := data.metrics[mk]; ok {
			vnum, err := strconv.ParseFloat(v.Val, 10)
			if err == nil {
				err = c.Gauge(*data.prefix+"heroku.dyno."+mk, vnum, tags, sampleRate)
				if err != nil {
					log.WithField("error", err).Info("Failed to send Gauge")
				}
			} else {
				log.WithFields(log.Fields{
					"type":   "scaling",
					"metric": mk,
					"err":    err,
				}).Info("Could not parse metric value")
			}
		}
	}
}

func (c *Client) sendMetric(metricType string, metricName string, value float64, tags []string) error {
	switch metricType {
	case "metric", "sample": return c.Gauge(metricName, value, tags, sampleRate)
	case "measure": return c.Histogram(metricName, value, tags, sampleRate)
	case "count": return c.Count(metricName, int64(value), tags, sampleRate)
	default: return errors.New("Unknown metric type"+metricType)
	}
}

func (c *Client) sendMetricsWithTags(data *logMetrics) {
	tags := *data.tags

Tags:
	for k, v := range data.metrics {
		if strings.Index(k, "tag#") != -1 {
			if _, err := strconv.Atoi(v.Val); err != nil {
				m := strings.Replace(strings.Split(k, "tag#")[1], "_", ".", -1)
				for _, mk := range customMetricsKeys {
					if m == mk {
						tags = append(tags, mk+":"+v.Val)
						continue Tags
					}
				}
			}
		}
	}

	log.WithFields(log.Fields{
		"app":    *data.app,
		"tags":   tags,
		"prefix": *data.prefix,
	}).Debug("sendMetricTag")

	for k, v := range data.metrics {
		if strings.Index(k, "#") != -1 {
			if vnum, err := strconv.ParseFloat(v.Val, 10); err == nil {
				keySplit := strings.Split(k, "#")
				metricType := keySplit[0]
				m := strings.Replace(keySplit[1], "_", ".", -1)
				err = c.sendMetric(metricType, *data.prefix+"app.metric."+m, vnum, tags)
				if err != nil {
					log.WithField("error", err).Warning("Failed to send Gauge")
				}
			} else {
				log.WithFields(log.Fields{
					"type":   "metrics",
					"metric": k,
					"err":    err,
				}).Debug("Could not parse metric value")
			}
		}
	}
}
<commit_msg>Update client.go<commit_after>package main

import (
	"errors"
	"regexp"
	"sort"
	"strconv"
	"strings"

	statsd "github.com/DataDog/datadog-go/statsd"
	log "github.com/Sirupsen/logrus"
)

const sampleRate = 1.0

const (
	routerMsg int = iota
	scalingMsg
	sampleMsg
	metricsTag
	releaseMsg
)

var routerMetricsKeys = []string{"dyno", "method", "status", "path", "host", "code", "desc", "at"}
var sampleMetricsKeys = []string{"source"}
var scalingMetricsKeys = []string{"mailer", "web"}
var customMetricsKeys = []string{"media_type", "output_type", "path", "version", "name"}

type Client struct {
	*statsd.Client
	ExcludedTags map[string]bool
}

var statusCode *regexp.Regexp = regexp.MustCompile(`^(?P<Family>\d)\d\d`)

func statsdClient(addr string) (*Client, error) {

	c, err := statsd.New(addr)
	return &Client{c, make(map[string]bool)}, err
}

func (c *Client) sendToStatsd(in chan *logMetrics) {

	var data *logMetrics
	var ok bool
	for {
		data, ok = <-in

		if !ok { //Exit, channel was closed
			return
		}

		log.WithFields(log.Fields{
			"type":   data.typ,
			"app":    data.app,
			"tags":   data.tags,
			"prefix": data.prefix,
		}).Debug("logMetrics received")

		if data.typ == routerMsg {
			c.sendRouterMsg(data)
		} else if data.typ == sampleMsg {
			c.sendSampleMsg(data)
		} else if data.typ == scalingMsg {
			c.sendEvents(*data.app, "heroku", data.events, *data.tags)
			c.sendScalingMsg(data)
		} else if data.typ == metricsTag {
			c.sendMetricsWithTags(data)
		} else if data.typ == releaseMsg {
			c.sendEvents(*data.app, "app", data.events, *data.tags)
		} else {
			log.WithField("type", data.typ).Warn("Unknown log message")
		}
	}
}

func (c *Client) sendEvents(app string, namespace string, events []string, tags []string) {
	for _, v := range events {
		event := statsd.NewEvent(namespace+"/api: "+app, v)
		event.Tags = tags
		c.Event(event)
		log.WithFields(log.Fields{
			"type":  "event",
			"app":   app,
			"value": v,
		}).Info("Event sent")
	}
}

func (c *Client) extractTags(tags []string, permittedTags []string, metrics map[string]logValue) []string {
	for _, mk := range permittedTags {
		if c.ExcludedTags[mk] {
			continue
		}
		if v, ok := metrics[mk]; ok {
			tags = append(tags, mk+":"+v.Val)
		}
	}
	sort.Strings(tags)
	return tags
}

func addStatusFamilyToTags(data *logMetrics, tags []string) []string {
	if val, ok := data.metrics["status"]; ok {
		match := statusCode.FindStringSubmatch(val.Val)
		if len(match) > 1 {
			tags = append(tags, "statusFamily:"+match[1]+"xx")
		}
	}
	return tags
}

func (c *Client) sendRouterMsg(data *logMetrics) {
	tags := c.extractTags(*data.tags, routerMetricsKeys, data.metrics)
	tags = addStatusFamilyToTags(data, tags)

	log.WithFields(log.Fields{
		"app":    *data.app,
		"tags":   *data.tags,
		"prefix": *data.prefix,
	}).Debug("sendRouterMsg")

	conn, err := strconv.ParseFloat(data.metrics["connect"].Val, 10)
	if err != nil {
		log.WithFields(log.Fields{
			"type":   "router",
			"err":    err,
			"metric": "connect",
		}).Info("Could not parse metric value")
		return
	}
	serv, err := strconv.ParseFloat(data.metrics["service"].Val, 10)
	if err != nil {
		log.WithFields(log.Fields{
			"type":   "router",
			"metric": "service",
			"err":    err,
		}).Info("Could not parse metric value")
		return
	}

	err = c.Histogram(*data.prefix+"heroku.router.request.connect", conn, tags, sampleRate)
	if err != nil {
		log.WithField("error", err).Info("Failed to send Histogram")
	}
	err = c.Histogram(*data.prefix+"heroku.router.request.service", serv, tags, sampleRate)
	if err != nil {
		log.WithField("error", err).Info("Failed to send Histogram")
	}
	if data.metrics["at"].Val == "error" {
		err = c.Count(*data.prefix+"heroku.router.error", 1, tags, 0.1)
		if err != nil {
			log.WithField("error", err).Info("Failed to send Count")
		}
	}
}

func (c *Client) sendSampleMsg(data *logMetrics) {
	tags := c.extractTags(*data.tags, sampleMetricsKeys, data.metrics)

	log.WithFields(log.Fields{
		"app":    *data.app,
		"tags":   tags,
		"prefix": *data.prefix,
	}).Debug("sendSampleMsg")

	for k, v := range data.metrics {
		if strings.Index(k, "#") != -1 {
			m := strings.Replace(strings.Split(k, "#")[1], "_", ".", -1)
			vnum, err := strconv.ParseFloat(v.Val, 10)
			if err == nil {
				err = c.Gauge(*data.prefix+"heroku.dyno."+m, vnum, tags, sampleRate)
				if err != nil {
					log.WithField("error", err).Info("Failed to send Gauge")
				}
			} else {
				log.WithFields(log.Fields{
					"type":   "sample",
					"metric": k,
					"err":    err,
				}).Info("Could not parse metric value")
			}
		}
	}
}

func (c *Client) sendScalingMsg(data *logMetrics) {
	tags := *data.tags

	log.WithFields(log.Fields{
		"app":    *data.app,
		"tags":   tags,
		"prefix": *data.prefix,
	}).Debug("sendScalingMsg")

	for _, mk := range scalingMetricsKeys {
		if v, ok := data.metrics[mk]; ok {
			vnum, err := strconv.ParseFloat(v.Val, 10)
			if err == nil {
				err = c.Gauge(*data.prefix+"heroku.dyno."+mk, vnum, tags, sampleRate)
				if err != nil {
					log.WithField("error", err).Info("Failed to send Gauge")
				}
			} else {
				log.WithFields(log.Fields{
					"type":   "scaling",
					"metric": mk,
					"err":    err,
				}).Info("Could not parse metric value")
			}
		}
	}
}

func (c *Client) sendMetric(metricType string, metricName string, value float64, tags []string) error {
	switch metricType {
	case "metric", "sample":
		return c.Gauge(metricName, value, tags, sampleRate)
	case "measure":
		return c.Histogram(metricName, value, tags, sampleRate)
	case "count":
		return c.Count(metricName, int64(value), tags, sampleRate)
	default:
		return errors.New("Unknown metric type" + metricType)
	}
}

func (c *Client) sendMetricsWithTags(data *logMetrics) {
	tags := *data.tags

Tags:
	for k, v := range data.metrics {
		if strings.Index(k, "tag#") != -1 {
			if _, err := strconv.Atoi(v.Val); err != nil {
				m := strings.Replace(strings.Split(k, "tag#")[1], "_", ".", -1)
				for _, mk := range customMetricsKeys {
					if m == mk {
						tags = append(tags, mk+":"+v.Val)
						continue Tags
					}
				}
			}
		}
	}

	log.WithFields(log.Fields{
		"app":    *data.app,
		"tags":   tags,
		"prefix": *data.prefix,
	}).Debug("sendMetricTag")

	for k, v := range data.metrics {
		if strings.Index(k, "#") != -1 {
			if vnum, err := strconv.ParseFloat(v.Val, 10); err == nil {
				keySplit := strings.Split(k, "#")
				metricType := keySplit[0]
				m := strings.Replace(keySplit[1], "_", ".", -1)
				err = c.sendMetric(metricType, *data.prefix+"app.metric."+m, vnum, tags)
				if err != nil {
					log.WithField("error", err).Warning("Failed to send Gauge")
				}
			} else {
				log.WithFields(log.Fields{
					"type":   "metrics",
					"metric": k,
					"err":    err,
				}).Debug("Could not parse metric value")
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"testing"
)

type fakeFlagParser struct{}

func (f fakeFlagParser) StringSlice(s string) []string {
	switch s {
	case "tags":
		return []string{"foo", "bar"}
	default:
		panic(fmt.Sprintf("fakeFlagParser does expect argument: %s", s))
	}
}

func (f fakeFlagParser) Int(s string) int {
	switch s {
	case "smart_folder_id":
		return 200
	default:
		panic(fmt.Sprintf("fakeFlagParser does expect argument: %s", s))
	}
}

func TestMakeParams(t *testing.T) {
	flagReturner := fakeFlagParser{}
	params := makeParams(&flagReturner)

	expectedTags := []string{"foo", "bar"}
	actualTags := params.Tags

	if expectedLen, actualLen := len(expectedTags), len(actualTags); expectedLen != actualLen {
		t.Errorf("Wrong amount of tags. Expected %d, got %d", expectedLen, actualLen)
	}

	for i, actualTag := range actualTags {
		if expectedTags[i] != actualTag {
			t.Errorf("Unexpected tag. Expected %s, got %s", expectedTags[1], actualTag)
		}
	}
}
<commit_msg>fixed typo that stopped test from working<commit_after>package main

import (
	"fmt"
	"testing"
)

type fakeFlagParser struct{}

func (f fakeFlagParser) StringSlice(s string) []string {
	switch s {
	case "tags":
		return []string{"foo", "bar"}
	default:
		panic(fmt.Sprintf("fakeFlagParser does expect argument: %s", s))
	}
}

func (f fakeFlagParser) Int(s string) int {
	switch s {
	case "smart-folder-id":
		return 200
	default:
		panic(fmt.Sprintf("fakeFlagParser does expect argument: %s", s))
	}
}

func TestMakeParams(t *testing.T) {
	flagReturner := fakeFlagParser{}
	params := makeParams(&flagReturner)

	expectedTags := []string{"foo", "bar"}
	actualTags := params.Tags

	if expectedLen, actualLen := len(expectedTags), len(actualTags); expectedLen != actualLen {
		t.Errorf("Wrong amount of tags. Expected %d, got %d", expectedLen, actualLen)
	}

	for i, actualTag := range actualTags {
		if expectedTags[i] != actualTag {
			t.Errorf("Unexpected tag. Expected %s, got %s", expectedTags[1], actualTag)
		}
	}
}
<|endoftext|>


<commit_before>package sched

import (
	"bytes"
	"fmt"
	"log"
	"time"

	"github.com/StackExchange/tsaf/_third_party/github.com/StackExchange/scollector/collect"
	"github.com/StackExchange/tsaf/_third_party/github.com/StackExchange/scollector/opentsdb"
	"github.com/StackExchange/tsaf/conf"
	"github.com/StackExchange/tsaf/expr"
)

func (s *Schedule) Status(ak AlertKey) *State {
	s.Lock()
	st := s.status[ak]
	s.Unlock()
	return st
}

func (s *Schedule) Check() {
	s.CheckStart = time.Now().UTC()
	s.RunHistory = make(map[AlertKey]*Event)
	s.cache = opentsdb.NewCache(s.Conf.TsdbHost, s.Conf.ResponseLimit)
	for _, a := range s.Conf.Alerts {
		s.CheckAlert(a)
	}
	s.CheckUnknown()
	checkNotify := false
	silenced := s.Silenced()
	for ak, event := range s.RunHistory {
		state := s.Status(ak)
		last := state.Append(event)
		a := s.Conf.Alerts[ak.Name()]
		if event.Status > StNormal {
			var subject = new(bytes.Buffer)
			if event.Status != StUnknown {
				if err := s.ExecuteSubject(subject, a, state); err != nil {
					log.Println(err)
				}
			}
			state.Subject = subject.String()
			state.Open = true
		}
		// On state increase, clear old notifications and notify current.
		// On state decrease, and if the old alert was already acknowledged, notify current.
		// If the old alert was not acknowledged, do nothing.
		// Do nothing if state did not change.
		notify := func(notifications map[string]*conf.Notification) {
			for _, n := range notifications {
				s.Notify(state, n)
				checkNotify = true
			}
		}
		notifyCurrent := func() {
			state.NeedAck = true
			if _, present := silenced[ak]; present {
				return
			}
			switch event.Status {
			case StCritical, StUnknown:
				notify(a.CritNotification)
			case StWarning:
				notify(a.WarnNotification)
			}
		}
		clearOld := func() {
			state.NeedAck = false
			s.Lock()
			delete(s.Notifications, ak)
			s.Unlock()
		}
		if event.Status > last {
			clearOld()
			notifyCurrent()
		} else if event.Status < last {
			if _, hasOld := s.Notifications[ak]; hasOld {
				notifyCurrent()
			}
		}
	}
	if checkNotify {
		s.nc <- true
	}
	s.Save()
}

func (s *Schedule) CheckUnknown() {
	s.Lock()
	for ak, st := range s.status {
		if st.Forgotten {
			continue
		}
		t := s.Conf.Alerts[ak.Name()].Unknown
		if t == 0 {
			t = s.Conf.Unknown
		}
		if t == 0 {
			continue
		}
		if time.Since(st.Touched) < t {
			continue
		}
		s.RunHistory[ak] = &Event{Status: StUnknown}
	}
	s.Unlock()
}

func (s *Schedule) CheckAlert(a *conf.Alert) {
	log.Printf("checking alert %v", a.Name)
	start := time.Now()
	var warns AlertKeys
	crits, err := s.CheckExpr(a, a.Crit, StCritical, nil)
	if err == nil {
		warns, _ = s.CheckExpr(a, a.Warn, StWarning, crits)
	}
	log.Printf("done checking alert %v (%s): %v crits, %v warns", a.Name, time.Since(start), len(crits), len(warns))
}

func (s *Schedule) CheckExpr(a *conf.Alert, e *expr.Expr, checkStatus Status, ignore AlertKeys) (alerts AlertKeys, err error) {
	if e == nil {
		return
	}
	defer func() {
		if err == nil {
			return
		}
		collect.Add("check.errs", opentsdb.TagSet{"metric": a.Name}, 1)
		log.Println(err)
	}()
	results, _, err := e.ExecuteOpts(s.cache, nil, s.CheckStart, 0)
	if err != nil {
		return
	}
Loop:
	for _, r := range results {
		if a.Squelched(r.Group) {
			continue
		}
		ak := NewAlertKey(a.Name, r.Group)
		for _, v := range ignore {
			if ak == v {
				continue Loop
			}
		}
		state := s.Status(ak)
		if state == nil {
			state = &State{
				Alert: ak.Name(),
				Tags:  r.Group.Tags(),
				Group: r.Group,
			}
			s.Lock()
			s.status[ak] = state
			s.Unlock()
		}
		state.Touch()
		status := checkStatus
		var n float64
		switch v := r.Value.(type) {
		case expr.Number:
			n = float64(v)
		case expr.Scalar:
			n = float64(v)
		default:
			err = fmt.Errorf("expected number or scalar")
			return
		}
		event := s.RunHistory[ak]
		if event == nil {
			event = new(Event)
			s.RunHistory[ak] = event
		}
		result := Result{
			Result: r,
			Expr:   e.String(),
		}
		switch checkStatus {
		case StWarning:
			event.Warn = &result
		case StCritical:
			event.Crit = &result
		}
		if n != 0 {
			alerts = append(alerts, ak)
		} else {
			status = StNormal
		}
		if status != s.RunHistory[ak].Status {
			event.Status = status
			state.Result = &result
		}
	}
	return
}
<commit_msg>Don't need this, normal status is kept in run history<commit_after>package sched

import (
	"bytes"
	"fmt"
	"log"
	"time"

	"github.com/StackExchange/tsaf/_third_party/github.com/StackExchange/scollector/collect"
	"github.com/StackExchange/tsaf/_third_party/github.com/StackExchange/scollector/opentsdb"
	"github.com/StackExchange/tsaf/conf"
	"github.com/StackExchange/tsaf/expr"
)

func (s *Schedule) Status(ak AlertKey) *State {
	s.Lock()
	st := s.status[ak]
	s.Unlock()
	return st
}

func (s *Schedule) Check() {
	s.CheckStart = time.Now().UTC()
	s.RunHistory = make(map[AlertKey]*Event)
	s.cache = opentsdb.NewCache(s.Conf.TsdbHost, s.Conf.ResponseLimit)
	for _, a := range s.Conf.Alerts {
		s.CheckAlert(a)
	}
	s.CheckUnknown()
	checkNotify := false
	silenced := s.Silenced()
	for ak, event := range s.RunHistory {
		state := s.Status(ak)
		last := state.Append(event)
		a := s.Conf.Alerts[ak.Name()]
		if event.Status > StNormal {
			var subject = new(bytes.Buffer)
			if event.Status != StUnknown {
				if err := s.ExecuteSubject(subject, a, state); err != nil {
					log.Println(err)
				}
			}
			state.Subject = subject.String()
			state.Open = true
		}
		// On state increase, clear old notifications and notify current.
		// On state decrease, and if the old alert was already acknowledged, notify current.
		// If the old alert was not acknowledged, do nothing.
		// Do nothing if state did not change.
		notify := func(notifications map[string]*conf.Notification) {
			for _, n := range notifications {
				s.Notify(state, n)
				checkNotify = true
			}
		}
		notifyCurrent := func() {
			state.NeedAck = true
			if _, present := silenced[ak]; present {
				return
			}
			switch event.Status {
			case StCritical, StUnknown:
				notify(a.CritNotification)
			case StWarning:
				notify(a.WarnNotification)
			}
		}
		clearOld := func() {
			state.NeedAck = false
			s.Lock()
			delete(s.Notifications, ak)
			s.Unlock()
		}
		if event.Status > last {
			clearOld()
			notifyCurrent()
		} else if event.Status < last {
			if _, hasOld := s.Notifications[ak]; hasOld {
				notifyCurrent()
			}
		}
	}
	if checkNotify {
		s.nc <- true
	}
	s.Save()
}

func (s *Schedule) CheckUnknown() {
	s.Lock()
	for ak, st := range s.status {
		if st.Forgotten {
			continue
		}
		t := s.Conf.Alerts[ak.Name()].Unknown
		if t == 0 {
			t = s.Conf.Unknown
		}
		if t == 0 {
			continue
		}
		if time.Since(st.Touched) < t {
			continue
		}
		s.RunHistory[ak] = &Event{Status: StUnknown}
	}
	s.Unlock()
}

func (s *Schedule) CheckAlert(a *conf.Alert) {
	log.Printf("checking alert %v", a.Name)
	start := time.Now()
	var warns AlertKeys
	crits, err := s.CheckExpr(a, a.Crit, StCritical, nil)
	if err == nil {
		warns, _ = s.CheckExpr(a, a.Warn, StWarning, crits)
	}
	log.Printf("done checking alert %v (%s): %v crits, %v warns", a.Name, time.Since(start), len(crits), len(warns))
}

func (s *Schedule) CheckExpr(a *conf.Alert, e *expr.Expr, checkStatus Status, ignore AlertKeys) (alerts AlertKeys, err error) {
	if e == nil {
		return
	}
	defer func() {
		if err == nil {
			return
		}
		collect.Add("check.errs", opentsdb.TagSet{"metric": a.Name}, 1)
		log.Println(err)
	}()
	results, _, err := e.ExecuteOpts(s.cache, nil, s.CheckStart, 0)
	if err != nil {
		return
	}
Loop:
	for _, r := range results {
		if a.Squelched(r.Group) {
			continue
		}
		ak := NewAlertKey(a.Name, r.Group)
		for _, v := range ignore {
			if ak == v {
				continue Loop
			}
		}
		state := s.Status(ak)
		if state == nil {
			state = &State{
				Alert: ak.Name(),
				Tags:  r.Group.Tags(),
				Group: r.Group,
			}
			s.Lock()
			s.status[ak] = state
			s.Unlock()
		}
		state.Touch()
		status := checkStatus
		var n float64
		switch v := r.Value.(type) {
		case expr.Number:
			n = float64(v)
		case expr.Scalar:
			n = float64(v)
		default:
			err = fmt.Errorf("expected number or scalar")
			return
		}
		event := s.RunHistory[ak]
		if event == nil {
			event = new(Event)
			s.RunHistory[ak] = event
		}
		result := Result{
			Result: r,
			Expr:   e.String(),
		}
		switch checkStatus {
		case StWarning:
			event.Warn = &result
		case StCritical:
			event.Crit = &result
		}
		if n != 0 {
			alerts = append(alerts, ak)
		} else {
			status = StNormal
		}
		if status > s.RunHistory[ak].Status {
			event.Status = status
			state.Result = &result
		}
	}
	return
}
<|endoftext|>