<commit_before>package agqr

import (
	"io"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/PuerkitoBio/goquery"
	"github.com/yosida95/timetable"
	"github.com/yosida95/timetable/internal"
	"golang.org/x/net/html/atom"
)

const (
	TIMETABLE_URL = "http://www.agqr.jp/timetable/streaming.php"
)

func parseProgram(col *goquery.Selection, weekday time.Weekday, prev *timetable.Program, now time.Time) (*timetable.Program, error) {
	h, m, err := internal.ParseTimeColonSeparated(col.Find("div.time").Text())
	if err != nil {
		return nil, err
	}
	if 4 >= h {
		weekday = (weekday + 1) % 7
	}
	nextDuration := internal.WaitTimeToNextOA(now, h, m, weekday)

	title := col.Find("div.title-p")
	url, _ := title.Find("a").Attr("href")

	prog := &timetable.Program{
		Title:  strings.TrimSpace(title.Text()),
		NextOA: now.Add(nextDuration),

		URL: strings.TrimSpace(url),
	}

	col.Find("div.rp").Children().Each(func(_ int, s *goquery.Selection) {
		switch s.Nodes[0].DataAtom {
		case atom.A:
			href, _ := s.Attr("href")
			href = strings.TrimSpace(href)
			if strings.HasPrefix(href, "mailto:") {
				prog.MailAddr = href[7:]
			}
		}
	})

	val, ok := col.Attr("class")
	switch {
	case !ok:
		prog.IsRepeat = true
	case strings.Contains(val, "bg-l"):
		prog.IsLive = true
	case strings.Contains(val, "bg-f"):
		prog.IsFirst = true
	}

	if prev != nil && prev.NextOA.Before(prog.NextOA) {
		prog.Prev = prev
		prev.Duration = prog.NextOA.Sub(prev.NextOA)
		prev.Next = prog
	} else if prev != nil {
		prev.Duration = prog.NextOA.Sub(prev.NextOA.Add(-7 * 24 * time.Hour))
	}

	return prog, nil
}

func parseTimetable(body io.Reader) (root *timetable.Program, err error) {
	doc, err := goquery.NewDocumentFromReader(body)
	if err != nil {
		return
	}

	now := time.Now().Truncate(time.Minute)
	var (
		table  [14]*timetable.Program
		offset [7]int
	)
	doc.Find("table.timetb-ag tbody tr").Each(func(_ int, row *goquery.Selection) {
		i := 0
		row.Children().EachWithBreak(func(_ int, col *goquery.Selection) bool {
			node := col.Nodes[0]
			switch node.DataAtom {
			case atom.Td:
				for ; offset[i] > 0; i++ {
					if i >= 6 {
						return false
					}
				}

				if rowspanStr, ok := col.Attr("rowspan"); ok {
					offset[i], err = strconv.Atoi(strings.TrimSpace(rowspanStr))
					if err != nil {
						return false
					}
				}

				weekday := time.Weekday((i + 1) % 7)
				firstI := i * 2
				lastI := firstI + 1

				prev := table[lastI]
				table[lastI], err = parseProgram(col, weekday, prev, now)
				if err != nil {
					return false
				}
				if table[firstI] == nil {
					table[firstI] = table[lastI]
				}

				if root == nil {
					root = table[firstI]
				} else if root.NextOA.After(table[lastI].NextOA) {
					root = table[lastI]
				}

				i++
			}
			return true
		})
		for i := 0; i < 7; i++ {
			if offset[i] > 0 {
				offset[i]--
			}
		}
	})

	for i := 1; i < 15; i += 2 {
		prev := table[i]
		next := table[(i+1)%14]

		if prev.NextOA.Before(next.NextOA) {
			prev.Duration = next.NextOA.Sub(prev.NextOA)
			prev.Next = next
			next.Prev = prev
		}
	}

	return
}

func BuildTimetable() (*timetable.Program, error) {
	req, err := http.NewRequest("GET", TIMETABLE_URL, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("User-Agent", internal.USERAGENT)

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

	if resp.StatusCode != http.StatusOK {
		return nil, err
	}

	return parseTimetable(resp.Body)
}
<commit_msg>update A&G parser<commit_after>package agqr

import (
	"errors"
	"io"
	"net/http"
	"strconv"
	"strings"
	"time"
	"unicode"

	"github.com/PuerkitoBio/goquery"
	"github.com/yosida95/timetable"
	"github.com/yosida95/timetable/internal"
	"golang.org/x/net/html/atom"
)

const TIMETABLE_URL = "http://www.agqr.jp/timetable/streaming.html"
const START_OF_DAY = 6

func parseTimetable(body io.Reader) (*timetable.Program, error) {
	doc, err := goquery.NewDocumentFromReader(body)
	if err != nil {
		return nil, err
	}

	today := time.Now()
	table := doc.Find("table.timetb-ag").First()

	var root *timetable.Program
	weekdays := make([]*timetable.Program, 7)
	table.Find("thead td").EachWithBreak(func(i int, row *goquery.Selection) bool {
		if i > 6 {
			err = errors.New("excess columns")
			return false
		}

		date := row.Text()
		m, lerr := strconv.Atoi(date[0:2])
		if lerr != nil {
			err = lerr
			return false
		}
		d, lerr := strconv.Atoi(date[3:5])
		if lerr != nil {
			err = lerr
			return false
		}
		y := today.Year()
		if m == 12 && today.Month() == 1 {
			y -= 1
		} else if m == 1 && today.Month() == 12 {
			y += 1
		}

		next := &timetable.Program{
			NextOA: time.Date(y, time.Month(m), d, START_OF_DAY, 0, 0, 0, today.Location()),
		}
		weekdays[i] = next
		if i == 0 {
			root = next
		} else {
			prev := weekdays[i-1]
			if next.NextOA.After(prev.NextOA) {
				prev.Next = next
				next.Prev = prev
			} else {
				root = next
			}
		}
		return true
	})
	if err != nil {
		return nil, err
	}
	if top := weekdays[0]; root != top {
		prev := weekdays[6]
		prev.Next = top
		top.Prev = prev
	}

	cursor := make([]int, 7)
	indices := make([]int, 0, 7)
	table.Find("tbody tr").EachWithBreak(func(_ int, row *goquery.Selection) bool {
		min := cursor[0]
		indices = indices[:0]
		indices = append(indices, 0)
		for i := 1; i < 7; i++ {
			if x := cursor[i]; x <= min {
				if x < min {
					min = x
					indices = indices[:0]
				}
				indices = append(indices, i)
			}
		}

		var hour int
		var oclock bool
		row.Children().EachWithBreak(func(j int, row *goquery.Selection) bool {
			if j > 7 {
				err = errors.New("excess columns")
				return false
			}
			switch row.Get(0).DataAtom {
			case atom.Th:
				if j > 0 {
					err = errors.New("unexpected <th>")
					return false
				}

				hour, err = strconv.Atoi(row.Text())
				if err != nil {
					return false
				}
				oclock = true
			case atom.Td:
				var index int
				if oclock {
					index = indices[j-1]
				} else {
					index = indices[j]
				}

				var span int
				span, err = strconv.Atoi(row.AttrOr("rowspan", "1"))
				if err != nil {
					return false
				}
				cursor[index] += span

				prev := weekdays[index]
				var prog *timetable.Program
				if prev.Duration == 0 {
					prog = prev
				} else {
					prog = &timetable.Program{
						NextOA: prev.NextOA.Add(prev.Duration),
						Prev:   prev,
					}
					if prev.Next != nil {
						prev.Next.Prev = prog
						prog.Next = prev.Next
					}
					prev.Next = prog
					weekdays[index] = prog
				}
				prog.Title = strings.TrimFunc(row.Find("div.title-p").Text(), unicode.IsSpace)
				prog.Duration = time.Duration(span) * time.Minute
				prog.IsRepeat = row.HasClass("bg-repeat")
				prog.IsFirst = !prog.IsRepeat
				prog.IsLive = row.HasClass("bg-l")
				row.Find("a").Each(func(_ int, a *goquery.Selection) {
					href, ok := a.Attr("href")
					if !ok {
						return
					}
					if strings.HasPrefix(href, "mailto:") {
						prog.MailAddr = href[7:]
					} else {
						prog.URL = href
					}
				})

				if oclock && (prog.NextOA.Hour() != hour || prog.NextOA.Minute() != 0) {
					err = errors.New("out of sync")
					return false
				}
			default:
				err = errors.New("unexpected tag")
				return false
			}
			return true
		})
		if err != nil {
			return false
		}
		return true
	})
	if err != nil {
		return nil, err
	}

	return root, nil
}

func BuildTimetable() (*timetable.Program, error) {
	req, err := http.NewRequest("GET", TIMETABLE_URL, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("User-Agent", internal.USERAGENT)

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

	if resp.StatusCode != http.StatusOK {
		return nil, err
	}

	return parseTimetable(resp.Body)
}
<|endoftext|>


<commit_before>package ali

import (
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"encoding/pem"
	"fmt"
	"net/url"
	"sort"
	"strings"
	"time"
)

func urlValues(c *Client, param PayParam) url.Values {
	values := url.Values{}
	values.Add("app_id", c.config.AppID)
	values.Add("method", param.URI())
	values.Add("format", "JSON")
	values.Add("charset", "utf-8")
	values.Add("sign_type", c.config.SignType)
	values.Add("timestamp", generateTimestampStr())
	values.Add("version", "1.0")
	values.Add("biz_content", param.BizContent())

	for k, v := range param.ExtraParams() {
		values.Add(k, v)
	}

	var keys []string
	for k := range values {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	values.Add("sign", signature(keys, values, c.config.AppPrivateKey, c.config.SignType))
	fmt.Printf("VALUES %v\n", values)
	return values
}

func signature(keys []string, values url.Values, privateKey []byte, signType string) string {
	if values == nil {
		values = url.Values{}
	}

	var valueList []string
	for _, k := range keys {
		v := strings.TrimSpace(values.Get(k))
		if v != "" {
			valueList = append(valueList, fmt.Sprintf("%s=%s", k, v))
		}
	}

	concat := strings.Join(valueList, "&")
	fmt.Println("CONCAT", concat)

	var sign string
	if signType == "RSA" {
		sign = signPKCS1v15([]byte(concat), privateKey, crypto.SHA1)
		fmt.Println("RSA", sign)
	} else if signType == "RSA2" {
		sign = signPKCS1v15([]byte(concat), privateKey, crypto.SHA256)
		fmt.Println("RSA2", sign)
	}
	return sign
}

func signPKCS1v15(source, privateKey []byte, hash crypto.Hash) string {
	block, _ := pem.Decode(privateKey)
	if block == nil {
		fmt.Println("BLOCK", block, len(privateKey))
		return ""
	}

	// rsaPrivateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	rsaPrivateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
		fmt.Println("RSAPRIVATE", err)
		return ""
	}

	h := hash.New()
	h.Write(source)
	hashed := h.Sum(nil)

	s, err := rsa.SignPKCS1v15(rand.Reader, rsaPrivateKey.(*rsa.PrivateKey), hash, hashed)
	if err != nil {
		fmt.Println("SIGNPKCS", err)
		return ""
	}
	return base64.StdEncoding.EncodeToString(s)
}

func verify(values url.Values, publicKey []byte, signType string) bool {
	fmt.Println("VALUES", values)
	decoded, err := base64.StdEncoding.DecodeString(values.Get("sign"))
	if err != nil {
		return false
	}

	fmt.Println("DECODED", len(decoded), values.Get("sign"))

	var excluded []string
	for k := range values {
		if k == "sign" || k == "sign_type" {
			continue
		}
		if len(values.Get(k)) > 0 {
			excluded = append(excluded, k)
		}
	}

	sort.Strings(excluded)

	var valueList []string
	for _, k := range excluded {
		v := strings.TrimSpace(values.Get(k))
		if v != "" {
			valueList = append(valueList, fmt.Sprintf("%s=%s", k, v))
		}
	}
	concat := strings.Join(valueList, "&")

	var ok bool
	if signType == "RSA" {
		ok = verifyPKCS1v15([]byte(concat), decoded, publicKey, crypto.SHA1)
	} else if signType == "RSA2" {
		ok = verifyPKCS1v15([]byte(concat), decoded, publicKey, crypto.SHA256)
	}
	return ok
}

func verifyPKCS1v15(source, sign, publicKey []byte, hash crypto.Hash) bool {
	h := hash.New()
	h.Write(source)
	hashed := h.Sum(nil)

	block, _ := pem.Decode(publicKey)
	if block == nil {
		fmt.Println("BLOCK", block)
		return false
	}

	pub, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		fmt.Println("ParsePKIXPublicKey", err)
		return false
	}

	rsaPublicKey := pub.(*rsa.PublicKey)
	err = rsa.VerifyPKCS1v15(rsaPublicKey, hash, hashed, sign)
	if err != nil {
		fmt.Println("VerifyPKCS1v15", err)
		return false
	}
	return true
}

func generateTimestampStr() string {
	now := time.Now()
	year, month, day := now.Date()
	hour, min, sec := now.Clock()
	return fmt.Sprintf("%d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, min, sec)
}

func marshalJSON(val interface{}) string {
	data, err := json.Marshal(val)
	if err != nil {
		return ""
	}
	return string(data)
}
<commit_msg>bugfix<commit_after>package ali

import (
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"encoding/pem"
	"fmt"
	"net/url"
	"sort"
	"strings"
	"time"
)

func urlValues(c *Client, param PayParam) url.Values {
	values := url.Values{}
	values.Add("app_id", c.config.AppID)
	values.Add("method", param.URI())
	values.Add("format", "JSON")
	values.Add("charset", "utf-8")
	values.Add("sign_type", c.config.SignType)
	values.Add("timestamp", generateTimestampStr())
	values.Add("version", "1.0")
	values.Add("biz_content", param.BizContent())

	for k, v := range param.ExtraParams() {
		values.Add(k, v)
	}

	var keys []string
	for k := range values {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	values.Add("sign", signature(keys, values, c.config.AppPrivateKey, c.config.SignType))
	fmt.Printf("VALUES %v\n", values)
	return values
}

func signature(keys []string, values url.Values, privateKey []byte, signType string) string {
	if values == nil {
		values = url.Values{}
	}

	var valueList []string
	for _, k := range keys {
		v := strings.TrimSpace(values.Get(k))
		if v != "" {
			valueList = append(valueList, fmt.Sprintf("%s=%s", k, v))
		}
	}

	concat := strings.Join(valueList, "&")
	fmt.Println("CONCAT", concat)

	var sign string
	if signType == "RSA" {
		sign = signPKCS1v15([]byte(concat), privateKey, crypto.SHA1)
		fmt.Println("RSA", sign)
	} else if signType == "RSA2" {
		sign = signPKCS1v15([]byte(concat), privateKey, crypto.SHA256)
		fmt.Println("RSA2", sign)
	}
	return sign
}

func signPKCS1v15(source, privateKey []byte, hash crypto.Hash) string {
	block, _ := pem.Decode(privateKey)
	if block == nil {
		fmt.Println("BLOCK", block, len(privateKey))
		return ""
	}

	rsaPrivateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	// rsaPrivateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
		fmt.Println("RSAPRIVATE", err)
		return ""
	}

	h := hash.New()
	h.Write(source)
	hashed := h.Sum(nil)

	s, err := rsa.SignPKCS1v15(rand.Reader, rsaPrivateKey, hash, hashed)
	// s, err := rsa.SignPKCS1v15(rand.Reader, rsaPrivateKey.(*rsa.PrivateKey), hash, hashed)
	if err != nil {
		fmt.Println("SIGNPKCS", err)
		return ""
	}
	return base64.StdEncoding.EncodeToString(s)
}

func verify(values url.Values, publicKey []byte, signType string) bool {
	fmt.Println("VALUES", values)
	decoded, err := base64.StdEncoding.DecodeString(values.Get("sign"))
	if err != nil {
		return false
	}

	fmt.Println("DECODED", len(decoded), values.Get("sign"))

	var excluded []string
	for k := range values {
		if k == "sign" || k == "sign_type" {
			continue
		}
		if len(values.Get(k)) > 0 {
			excluded = append(excluded, k)
		}
	}

	sort.Strings(excluded)

	var valueList []string
	for _, k := range excluded {
		v := strings.TrimSpace(values.Get(k))
		if v != "" {
			valueList = append(valueList, fmt.Sprintf("%s=%s", k, v))
		}
	}
	concat := strings.Join(valueList, "&")

	var ok bool
	if signType == "RSA" {
		ok = verifyPKCS1v15([]byte(concat), decoded, publicKey, crypto.SHA1)
	} else if signType == "RSA2" {
		ok = verifyPKCS1v15([]byte(concat), decoded, publicKey, crypto.SHA256)
	}
	return ok
}

func verifyPKCS1v15(source, sign, publicKey []byte, hash crypto.Hash) bool {
	h := hash.New()
	h.Write(source)
	hashed := h.Sum(nil)

	block, _ := pem.Decode(publicKey)
	if block == nil {
		fmt.Println("BLOCK", block)
		return false
	}

	pub, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		fmt.Println("ParsePKIXPublicKey", err)
		return false
	}

	rsaPublicKey := pub.(*rsa.PublicKey)
	err = rsa.VerifyPKCS1v15(rsaPublicKey, hash, hashed, sign)
	if err != nil {
		fmt.Println("VerifyPKCS1v15", err)
		return false
	}
	return true
}

func generateTimestampStr() string {
	now := time.Now()
	year, month, day := now.Date()
	hour, min, sec := now.Clock()
	return fmt.Sprintf("%d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, min, sec)
}

func marshalJSON(val interface{}) string {
	data, err := json.Marshal(val)
	if err != nil {
		return ""
	}
	return string(data)
}
<|endoftext|>


<commit_before>// Note: Consider the API unstable until the code supports at least three different image formats or transports.

package signature

import (
	"bytes"
	"fmt"

	"github.com/mtrmac/gpgme"
)

// SigningMechanism abstracts a way to sign binary blobs and verify their signatures.
// FIXME: Eventually expand on keyIdentity (namespace them between mechanisms to
// eliminate ambiguities, support CA signatures and perhaps other key properties)
type SigningMechanism interface {
	// ImportKeysFromBytes imports public keys from the supplied blob and returns their identities.
	// The blob is assumed to have an appropriate format (the caller is expected to know which one).
	// NOTE: This may modify long-term state (e.g. key storage in a directory underlying the mechanism).
	ImportKeysFromBytes(blob []byte) ([]string, error)
	// Sign creates a (non-detached) signature of input using keyidentity
	Sign(input []byte, keyIdentity string) ([]byte, error)
	// Verify parses unverifiedSignature and returns the content and the signer's identity
	Verify(unverifiedSignature []byte) (contents []byte, keyIdentity string, err error)
}

// A GPG/OpenPGP signing mechanism.
type gpgSigningMechanism struct {
	ctx *gpgme.Context
}

// NewGPGSigningMechanism returns a new GPG/OpenPGP signing mechanism.
func NewGPGSigningMechanism() (SigningMechanism, error) {
	return newGPGSigningMechanismInDirectory("")
}

// newGPGSigningMechanismInDirectory returns a new GPG/OpenPGP signing mechanism, using optionalDir if not empty.
func newGPGSigningMechanismInDirectory(optionalDir string) (SigningMechanism, error) {
	ctx, err := gpgme.New()
	if err != nil {
		return nil, err
	}
	if err = ctx.SetProtocol(gpgme.ProtocolOpenPGP); err != nil {
		return nil, err
	}
	if optionalDir != "" {
		err := ctx.SetEngineInfo(gpgme.ProtocolOpenPGP, "", optionalDir)
		if err != nil {
			return nil, err
		}
	}
	ctx.SetArmor(false)
	ctx.SetTextMode(false)
	return gpgSigningMechanism{ctx: ctx}, nil
}

// ImportKeysFromBytes implements SigningMechanism.ImportKeysFromBytes
func (m gpgSigningMechanism) ImportKeysFromBytes(blob []byte) ([]string, error) {
	inputData, err := gpgme.NewDataBytes(blob)
	if err != nil {
		return nil, err
	}
	res, err := m.ctx.Import(inputData)
	if err != nil {
		return nil, err
	}
	keyIdentities := []string{}
	for _, i := range res.Imports {
		if i.Result == nil {
			keyIdentities = append(keyIdentities, i.Fingerprint)
		}
	}
	return keyIdentities, nil
}

// Sign implements SigningMechanism.Sign
func (m gpgSigningMechanism) Sign(input []byte, keyIdentity string) ([]byte, error) {
	key, err := m.ctx.GetKey(keyIdentity, true)
	if err != nil {
		return nil, err
	}
	inputData, err := gpgme.NewDataBytes(input)
	if err != nil {
		return nil, err
	}
	var sigBuffer bytes.Buffer
	sigData, err := gpgme.NewDataWriter(&sigBuffer)
	if err != nil {
		return nil, err
	}
	if err = m.ctx.Sign([]*gpgme.Key{key}, inputData, sigData, gpgme.SigModeNormal); err != nil {
		return nil, err
	}
	return sigBuffer.Bytes(), nil
}

// Verify implements SigningMechanism.Verify
func (m gpgSigningMechanism) Verify(unverifiedSignature []byte) (contents []byte, keyIdentity string, err error) {
	signedBuffer := bytes.Buffer{}
	signedData, err := gpgme.NewDataWriter(&signedBuffer)
	if err != nil {
		return nil, "", err
	}
	unverifiedSignatureData, err := gpgme.NewDataBytes(unverifiedSignature)
	if err != nil {
		return nil, "", err
	}
	_, sigs, err := m.ctx.Verify(unverifiedSignatureData, nil, signedData)
	if err != nil {
		return nil, "", err
	}
	if len(sigs) != 1 {
		return nil, "", InvalidSignatureError{msg: fmt.Sprintf("Unexpected GPG signature count %d", len(sigs))}
	}
	sig := sigs[0]
	// This is sig.Summary == gpgme.SigSumValid except for key trust, which we handle ourselves
	if sig.Status != nil || sig.Validity == gpgme.ValidityNever || sig.ValidityReason != nil || sig.WrongKeyUsage {
		// FIXME: Better error reporting eventually
		return nil, "", InvalidSignatureError{msg: fmt.Sprintf("Invalid GPG signature: %#v", sig)}
	}
	return signedBuffer.Bytes(), sig.Fingerprint, nil
}
<commit_msg>provide better error when key not found<commit_after>// Note: Consider the API unstable until the code supports at least three different image formats or transports.

package signature

import (
	"bytes"
	"fmt"

	"github.com/mtrmac/gpgme"
)

// SigningMechanism abstracts a way to sign binary blobs and verify their signatures.
// FIXME: Eventually expand on keyIdentity (namespace them between mechanisms to
// eliminate ambiguities, support CA signatures and perhaps other key properties)
type SigningMechanism interface {
	// ImportKeysFromBytes imports public keys from the supplied blob and returns their identities.
	// The blob is assumed to have an appropriate format (the caller is expected to know which one).
	// NOTE: This may modify long-term state (e.g. key storage in a directory underlying the mechanism).
	ImportKeysFromBytes(blob []byte) ([]string, error)
	// Sign creates a (non-detached) signature of input using keyidentity
	Sign(input []byte, keyIdentity string) ([]byte, error)
	// Verify parses unverifiedSignature and returns the content and the signer's identity
	Verify(unverifiedSignature []byte) (contents []byte, keyIdentity string, err error)
}

// A GPG/OpenPGP signing mechanism.
type gpgSigningMechanism struct {
	ctx *gpgme.Context
}

// NewGPGSigningMechanism returns a new GPG/OpenPGP signing mechanism.
func NewGPGSigningMechanism() (SigningMechanism, error) {
	return newGPGSigningMechanismInDirectory("")
}

// newGPGSigningMechanismInDirectory returns a new GPG/OpenPGP signing mechanism, using optionalDir if not empty.
func newGPGSigningMechanismInDirectory(optionalDir string) (SigningMechanism, error) {
	ctx, err := gpgme.New()
	if err != nil {
		return nil, err
	}
	if err = ctx.SetProtocol(gpgme.ProtocolOpenPGP); err != nil {
		return nil, err
	}
	if optionalDir != "" {
		err := ctx.SetEngineInfo(gpgme.ProtocolOpenPGP, "", optionalDir)
		if err != nil {
			return nil, err
		}
	}
	ctx.SetArmor(false)
	ctx.SetTextMode(false)
	return gpgSigningMechanism{ctx: ctx}, nil
}

// ImportKeysFromBytes implements SigningMechanism.ImportKeysFromBytes
func (m gpgSigningMechanism) ImportKeysFromBytes(blob []byte) ([]string, error) {
	inputData, err := gpgme.NewDataBytes(blob)
	if err != nil {
		return nil, err
	}
	res, err := m.ctx.Import(inputData)
	if err != nil {
		return nil, err
	}
	keyIdentities := []string{}
	for _, i := range res.Imports {
		if i.Result == nil {
			keyIdentities = append(keyIdentities, i.Fingerprint)
		}
	}
	return keyIdentities, nil
}

// Sign implements SigningMechanism.Sign
func (m gpgSigningMechanism) Sign(input []byte, keyIdentity string) ([]byte, error) {
	key, err := m.ctx.GetKey(keyIdentity, true)
	if err != nil {
		if e, ok := err.(gpgme.Error); ok && e.Code() == gpgme.ErrorEOF {
			return nil, fmt.Errorf("key %q not found", keyIdentity)
		}
		return nil, err
	}
	inputData, err := gpgme.NewDataBytes(input)
	if err != nil {
		return nil, err
	}
	var sigBuffer bytes.Buffer
	sigData, err := gpgme.NewDataWriter(&sigBuffer)
	if err != nil {
		return nil, err
	}
	if err = m.ctx.Sign([]*gpgme.Key{key}, inputData, sigData, gpgme.SigModeNormal); err != nil {
		return nil, err
	}
	return sigBuffer.Bytes(), nil
}

// Verify implements SigningMechanism.Verify
func (m gpgSigningMechanism) Verify(unverifiedSignature []byte) (contents []byte, keyIdentity string, err error) {
	signedBuffer := bytes.Buffer{}
	signedData, err := gpgme.NewDataWriter(&signedBuffer)
	if err != nil {
		return nil, "", err
	}
	unverifiedSignatureData, err := gpgme.NewDataBytes(unverifiedSignature)
	if err != nil {
		return nil, "", err
	}
	_, sigs, err := m.ctx.Verify(unverifiedSignatureData, nil, signedData)
	if err != nil {
		return nil, "", err
	}
	if len(sigs) != 1 {
		return nil, "", InvalidSignatureError{msg: fmt.Sprintf("Unexpected GPG signature count %d", len(sigs))}
	}
	sig := sigs[0]
	// This is sig.Summary == gpgme.SigSumValid except for key trust, which we handle ourselves
	if sig.Status != nil || sig.Validity == gpgme.ValidityNever || sig.ValidityReason != nil || sig.WrongKeyUsage {
		// FIXME: Better error reporting eventually
		return nil, "", InvalidSignatureError{msg: fmt.Sprintf("Invalid GPG signature: %#v", sig)}
	}
	return signedBuffer.Bytes(), sig.Fingerprint, nil
}
<|endoftext|>


<commit_before>package executor

import (
	gofig "github.com/akutz/gofig/types"
	"github.com/akutz/goof"

	"github.com/codedellemc/rexray/libstorage/api/registry"
	"github.com/codedellemc/rexray/libstorage/api/types"
	"github.com/codedellemc/rexray/libstorage/api/utils"
	"github.com/codedellemc/rexray/libstorage/drivers/storage/cinder"

	"encoding/json"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
	"time"
)

type driver struct {
	config   gofig.Config
	osDriver types.OSDriver
}

func init() {
	registry.RegisterStorageExecutor(cinder.Name, newDriver)
}

func newDriver() types.StorageExecutor {
	return &driver{}
}

func (d *driver) Init(ctx types.Context, config gofig.Config) error {
	d.config = config

	var err error
	if d.osDriver, err = registry.NewOSDriver(runtime.GOOS); err != nil {
		return err
	}
	if err = d.osDriver.Init(ctx, config); err != nil {
		return err
	}

	return nil
}

func (d *driver) Name() string {
	return cinder.Name
}

func (d *driver) InstanceID(ctx types.Context, opts types.Store) (*types.InstanceID, error) {
	fields := map[string]interface{}{}
	uuid, err := getInstanceIDFromMetadataServer()
	if err != nil {
		fields["metadataServer"] = err
		uuid, err = getInstanceIDFromConfigDrive(ctx, d)
		if err != nil {
			fields["configDrive"] = err
			uuid, err = getInstanceIDWithDMIDecode()
			if err != nil {
				fields["dmidecode"] = err
				return nil, goof.WithFields(fields, "unable to get InstanceID from any sources")
			}
		}
	}

	iid := &types.InstanceID{Driver: cinder.Name, ID: strings.ToLower(uuid)}

	return iid, nil
}

func parseUUID(metadata []byte) (string, error) {
	var decodedJSON interface{}
	err := json.Unmarshal(metadata, &decodedJSON)
	if err != nil {
		return "", goof.WithError("error unmarshalling metadata", err)
	}
	decodedJSONMap, ok := decodedJSON.(map[string]interface{})
	if !ok {
		return "", goof.New("error casting metadata decoded JSON")
	}
	uuid, ok := decodedJSONMap["uuid"].(string)
	if !ok {
		return "", goof.New("error casting metadata uuid field")
	}

	return uuid, nil
}

func execCommand(cmd string, args ...string) (string, error) {
	command := exec.Command(cmd, args...)
	out, err := command.Output()
	if exiterr, ok := err.(*exec.ExitError); ok {
		stderr := string(exiterr.Stderr)
		return "", goof.WithFieldE("stderr", stderr, "execute command failed", err)
	} else if err != nil {
		return "", goof.WithError("execute command failed", err)
	}
	return string(out), nil
}

// the code of getInstanceIDFromConfigDrive is mostly copied from k8s OpenStack driver
// https://github.com/kubernetes/kubernetes/blob/master/pkg/cloudprovider/providers/openstack/metadata.go
// Copyright to the original authors (Apache license)

// Config drive is defined as an iso9660 or vfat (deprecated) drive
// with the "config-2" label.
// http://docs.openstack.org/user-guide/cli-config-drive.html
const configDriveLabel = "config-2"
const configDrivePath = "openstack/latest/meta_data.json"

func getInstanceIDFromConfigDrive(ctx types.Context, d *driver) (string, error) {
	// Try to read instance UUID from config drive.
	dev := "/dev/disk/by-label/" + configDriveLabel
	if _, err := os.Stat(dev); os.IsNotExist(err) {
		cmdOut, err := execCommand(
			"blkid", "-l",
			"-t", "LABEL="+configDriveLabel,
			"-o", "device",
		)

		if err != nil {
			return "", goof.WithError("Unable to run blkid", err)
		}
		dev = strings.TrimSpace(string(cmdOut))
	}

	mntdir, err := ioutil.TempDir("", "configdrive")
	if err != nil {
		return "", err
	}
	defer os.Remove(mntdir)

	mountOpts := types.DeviceMountOpts{
		FsType:       "iso9660",
		MountOptions: "ro",
	}
	err = d.osDriver.Mount(ctx, dev, mntdir, &mountOpts)
	if err != nil {
		mountOpts.FsType = "vfat"
		err = d.osDriver.Mount(ctx, dev, mntdir, &mountOpts)
	}
	if err != nil {
		return "", goof.WithFieldE("device", dev, "error mounting configdrive", err)
	}
	defer d.osDriver.Unmount(ctx, mntdir, utils.NewStore())

	metadataBytes, err := ioutil.ReadFile(filepath.Join(mntdir, configDrivePath))
	if err != nil {
		return "", goof.WithError("error reading metadata file on config drive", err)
	}

	return parseUUID(metadataBytes)
}

func getInstanceIDFromMetadataServer() (string, error) {
	const metadataURL = "http://169.254.169.254/openstack/latest/meta_data.json"
	httpClient := http.Client{
		Timeout: time.Duration(5 * time.Second),
	}

	resp, err := httpClient.Get(metadataURL)
	if err != nil {
		return "", goof.WithFieldE("url", metadataURL, "error getting metadata from server", err)
	}
	defer resp.Body.Close()

	metadataBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", goof.WithError("io error reading metadata", err)
	}

	return parseUUID(metadataBytes)
}

func getInstanceIDWithDMIDecode() (string, error) {
	cmdOut, err := execCommand("dmidecode", "-t", "system")
	if err != nil {
		return "", goof.WithError("error calling dmidecode", err)
	}

	rp := regexp.MustCompile("UUID:(.*)")
	uuid := strings.Replace(rp.FindString(string(cmdOut)), "UUID: ", "", -1)

	return strings.ToLower(uuid), nil
}

func (d *driver) NextDevice(
	ctx types.Context,
	opts types.Store) (string, error) {
	return "", types.ErrNotImplemented
}

func (d *driver) LocalDevices(
	ctx types.Context,
	opts *types.LocalDevicesOpts) (*types.LocalDevices, error) {

	/* There is no current method to look up what the volume ID is for
	volumes attached to a host via Cinder. All lookups are done on the
	server-side from the Cinder API
	*/
	return &types.LocalDevices{Driver: d.Name()}, nil
}
<commit_msg>Revert "No longer report invalid local devices"<commit_after>package executor

import (
	gofig "github.com/akutz/gofig/types"
	"github.com/akutz/goof"

	"github.com/codedellemc/rexray/libstorage/api/registry"
	"github.com/codedellemc/rexray/libstorage/api/types"
	"github.com/codedellemc/rexray/libstorage/api/utils"
	"github.com/codedellemc/rexray/libstorage/drivers/storage/cinder"

	"encoding/json"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
	"time"
)

type driver struct {
	config   gofig.Config
	osDriver types.OSDriver
}

func init() {
	registry.RegisterStorageExecutor(cinder.Name, newDriver)
}

func newDriver() types.StorageExecutor {
	return &driver{}
}

func (d *driver) Init(ctx types.Context, config gofig.Config) error {
	d.config = config

	var err error
	if d.osDriver, err = registry.NewOSDriver(runtime.GOOS); err != nil {
		return err
	}
	if err = d.osDriver.Init(ctx, config); err != nil {
		return err
	}

	return nil
}

func (d *driver) Name() string {
	return cinder.Name
}

func (d *driver) InstanceID(ctx types.Context, opts types.Store) (*types.InstanceID, error) {
	fields := map[string]interface{}{}
	uuid, err := getInstanceIDFromMetadataServer()
	if err != nil {
		fields["metadataServer"] = err
		uuid, err = getInstanceIDFromConfigDrive(ctx, d)
		if err != nil {
			fields["configDrive"] = err
			uuid, err = getInstanceIDWithDMIDecode()
			if err != nil {
				fields["dmidecode"] = err
				return nil, goof.WithFields(fields, "unable to get InstanceID from any sources")
			}
		}
	}

	iid := &types.InstanceID{Driver: cinder.Name, ID: strings.ToLower(uuid)}

	return iid, nil
}

func parseUUID(metadata []byte) (string, error) {
	var decodedJSON interface{}
	err := json.Unmarshal(metadata, &decodedJSON)
	if err != nil {
		return "", goof.WithError("error unmarshalling metadata", err)
	}
	decodedJSONMap, ok := decodedJSON.(map[string]interface{})
	if !ok {
		return "", goof.New("error casting metadata decoded JSON")
	}
	uuid, ok := decodedJSONMap["uuid"].(string)
	if !ok {
		return "", goof.New("error casting metadata uuid field")
	}

	return uuid, nil
}

func execCommand(cmd string, args ...string) (string, error) {
	command := exec.Command(cmd, args...)
	out, err := command.Output()
	if exiterr, ok := err.(*exec.ExitError); ok {
		stderr := string(exiterr.Stderr)
		return "", goof.WithFieldE("stderr", stderr, "execute command failed", err)
	} else if err != nil {
		return "", goof.WithError("execute command failed", err)
	}
	return string(out), nil
}

// the code of getInstanceIDFromConfigDrive is mostly copied from k8s OpenStack driver
// https://github.com/kubernetes/kubernetes/blob/master/pkg/cloudprovider/providers/openstack/metadata.go
// Copyright to the original authors (Apache license)

// Config drive is defined as an iso9660 or vfat (deprecated) drive
// with the "config-2" label.
// http://docs.openstack.org/user-guide/cli-config-drive.html
const configDriveLabel = "config-2"
const configDrivePath = "openstack/latest/meta_data.json"

func getInstanceIDFromConfigDrive(ctx types.Context, d *driver) (string, error) {
	// Try to read instance UUID from config drive.
	dev := "/dev/disk/by-label/" + configDriveLabel
	if _, err := os.Stat(dev); os.IsNotExist(err) {
		cmdOut, err := execCommand(
			"blkid", "-l",
			"-t", "LABEL="+configDriveLabel,
			"-o", "device",
		)

		if err != nil {
			return "", goof.WithError("Unable to run blkid", err)
		}
		dev = strings.TrimSpace(string(cmdOut))
	}

	mntdir, err := ioutil.TempDir("", "configdrive")
	if err != nil {
		return "", err
	}
	defer os.Remove(mntdir)

	mountOpts := types.DeviceMountOpts{
		FsType:       "iso9660",
		MountOptions: "ro",
	}
	err = d.osDriver.Mount(ctx, dev, mntdir, &mountOpts)
	if err != nil {
		mountOpts.FsType = "vfat"
		err = d.osDriver.Mount(ctx, dev, mntdir, &mountOpts)
	}
	if err != nil {
		return "", goof.WithFieldE("device", dev, "error mounting configdrive", err)
	}
	defer d.osDriver.Unmount(ctx, mntdir, utils.NewStore())

	metadataBytes, err := ioutil.ReadFile(filepath.Join(mntdir, configDrivePath))
	if err != nil {
		return "", goof.WithError("error reading metadata file on config drive", err)
	}

	return parseUUID(metadataBytes)
}

func getInstanceIDFromMetadataServer() (string, error) {
	const metadataURL = "http://169.254.169.254/openstack/latest/meta_data.json"
	httpClient := http.Client{
		Timeout: time.Duration(5 * time.Second),
	}

	resp, err := httpClient.Get(metadataURL)
	if err != nil {
		return "", goof.WithFieldE("url", metadataURL, "error getting metadata from server", err)
	}
	defer resp.Body.Close()

	metadataBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", goof.WithError("io error reading metadata", err)
	}

	return parseUUID(metadataBytes)
}

func getInstanceIDWithDMIDecode() (string, error) {
	cmdOut, err := execCommand("dmidecode", "-t", "system")
	if err != nil {
		return "", goof.WithError("error calling dmidecode", err)
	}

	rp := regexp.MustCompile("UUID:(.*)")
	uuid := strings.Replace(rp.FindString(string(cmdOut)), "UUID: ", "", -1)

	return strings.ToLower(uuid), nil
}

func (d *driver) NextDevice(
	ctx types.Context,
	opts types.Store) (string, error) {
	return "", types.ErrNotImplemented
}

func (d *driver) LocalDevices(
	ctx types.Context,
	opts *types.LocalDevicesOpts) (*types.LocalDevices, error) {
	devicesMap := make(map[string]string)

	file := "/proc/partitions"
	contentBytes, err := ioutil.ReadFile(file)
	if err != nil {
		return nil,
			goof.WithFieldE("file", file, "error reading file", err)
	}

	content := string(contentBytes)

	lines := strings.Split(content, "\n")
	for _, line := range lines[2:] {
		fields := strings.Fields(line)
		if len(fields) >= 4 {
			devicePath := "/dev/" + fields[3]
			devicesMap[devicePath] = ""
		}
	}

	return &types.LocalDevices{
		Driver:    cinder.Name,
		DeviceMap: devicesMap,
	}, nil
}
<|endoftext|>


<commit_before>/*
Package graceful implements graceful shutdown for HTTP servers by closing idle
connections after receiving a signal. By default, this package listens for
interrupts (i.e., SIGINT), but when it detects that it is running under Einhorn
it will additionally listen for SIGUSR2 as well, giving your application
automatic support for graceful restarts/code upgrades.
*/
package graceful

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

	"github.com/zenazn/goji/graceful/listener"
)

// Most of the code here is lifted straight from net/http

// Type Server is exactly the same as an http.Server, but provides more graceful
// implementations of its methods.
type Server http.Server

func (srv *Server) ListenAndServe() error {
	addr := srv.Addr
	if addr == "" {
		addr = ":http"
	}
	l, e := net.Listen("tcp", addr)
	if e != nil {
		return e
	}
	return srv.Serve(l)
}

func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error {
	addr := srv.Addr
	if addr == "" {
		addr = ":https"
	}
	config := &tls.Config{}
	if srv.TLSConfig != nil {
		*config = *srv.TLSConfig
	}
	if config.NextProtos == nil {
		config.NextProtos = []string{"http/1.1"}
	}

	var err error
	config.Certificates = make([]tls.Certificate, 1)
	config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err
	}

	conn, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}

	tlsListener := tls.NewListener(conn, config)
	return srv.Serve(tlsListener)
}

// ListenAndServe behaves exactly like the net/http function of the same name.
func ListenAndServe(addr string, handler http.Handler) error {
	server := &Server{Addr: addr, Handler: handler}
	return server.ListenAndServe()
}

// ListenAndServeTLS behaves exactly like the net/http function of the same name.
func ListenAndServeTLS(addr, certfile, keyfile string, handler http.Handler) error {
	server := &Server{Addr: addr, Handler: handler}
	return server.ListenAndServeTLS(certfile, keyfile)
}

// Serve behaves exactly like the net/http function of the same name.
func Serve(l net.Listener, handler http.Handler) error {
	server := &Server{Handler: handler}
	return server.Serve(l)
}

// WrapListener wraps an arbitrary net.Listener for use with graceful shutdowns.
// In the background, it uses the listener sub-package to Wrap the listener in
// Deadline mode. If another mode of operation is desired, you should call
// listener.Wrap yourself: this function is smart enough to not double-wrap
// listeners.
func WrapListener(l net.Listener) net.Listener {
	if lt, ok := l.(*listener.T); ok {
		appendListener(lt)
		return lt
	}

	lt := listener.Wrap(l, listener.Deadline)
	appendListener(lt)
	return lt
}
<commit_msg>Disable SSLv3 by default<commit_after>/*
Package graceful implements graceful shutdown for HTTP servers by closing idle
connections after receiving a signal. By default, this package listens for
interrupts (i.e., SIGINT), but when it detects that it is running under Einhorn
it will additionally listen for SIGUSR2 as well, giving your application
automatic support for graceful restarts/code upgrades.
*/
package graceful

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

	"github.com/zenazn/goji/graceful/listener"
)

// Most of the code here is lifted straight from net/http

// Type Server is exactly the same as an http.Server, but provides more graceful
// implementations of its methods.
type Server http.Server

func (srv *Server) ListenAndServe() error {
	addr := srv.Addr
	if addr == "" {
		addr = ":http"
	}
	l, e := net.Listen("tcp", addr)
	if e != nil {
		return e
	}
	return srv.Serve(l)
}

// Unlike the method of the same name on http.Server, this function defaults to
// enforcing TLS 1.0 or higher in order to address the POODLE vulnerability.
// Users who wish to enable SSLv3 must do so by supplying a TLSConfig
// explicitly.
func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error {
	addr := srv.Addr
	if addr == "" {
		addr = ":https"
	}
	config := &tls.Config{
		MinVersion: tls.VersionTLS10,
	}
	if srv.TLSConfig != nil {
		*config = *srv.TLSConfig
	}
	if config.NextProtos == nil {
		config.NextProtos = []string{"http/1.1"}
	}

	var err error
	config.Certificates = make([]tls.Certificate, 1)
	config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err
	}

	conn, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}

	tlsListener := tls.NewListener(conn, config)
	return srv.Serve(tlsListener)
}

// ListenAndServe behaves exactly like the net/http function of the same name.
func ListenAndServe(addr string, handler http.Handler) error {
	server := &Server{Addr: addr, Handler: handler}
	return server.ListenAndServe()
}

// ListenAndServeTLS behaves almost exactly like the net/http function of the
// same name. Unlike net/http, however, this function defaults to enforcing TLS
// 1.0 or higher in order to address the POODLE vulnerability. Users who wish to
// enable SSLv3 must do so by explicitly instantiating a server with an
// appropriately configured TLSConfig property.
func ListenAndServeTLS(addr, certfile, keyfile string, handler http.Handler) error {
	server := &Server{Addr: addr, Handler: handler}
	return server.ListenAndServeTLS(certfile, keyfile)
}

// Serve behaves exactly like the net/http function of the same name.
func Serve(l net.Listener, handler http.Handler) error {
	server := &Server{Handler: handler}
	return server.Serve(l)
}

// WrapListener wraps an arbitrary net.Listener for use with graceful shutdowns.
// In the background, it uses the listener sub-package to Wrap the listener in
// Deadline mode. If another mode of operation is desired, you should call
// listener.Wrap yourself: this function is smart enough to not double-wrap
// listeners.
func WrapListener(l net.Listener) net.Listener {
	if lt, ok := l.(*listener.T); ok {
		appendListener(lt)
		return lt
	}

	lt := listener.Wrap(l, listener.Deadline)
	appendListener(lt)
	return lt
}
<|endoftext|>


<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package utils_test

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"

	gc "launchpad.net/gocheck"

	"launchpad.net/juju-core/utils"
)

type insecureClientSuite struct {
	Server *httptest.Server
}

var _ = gc.Suite(&insecureClientSuite{})

type trivialResponseHandler struct{}

func (t *trivialResponseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Greetings!\n")
}

func (s *insecureClientSuite) SetUpSuite(c *gc.C) {
}

func (s *insecureClientSuite) TearDownSuite(c *gc.C) {
}

func (s *insecureClientSuite) SetUpTest(c *gc.C) {
	s.Server = httptest.NewTLSServer(&trivialResponseHandler{})
}

func (s *insecureClientSuite) TearDownTest(c *gc.C) {
	if s.Server != nil {
		s.Server.Close()
	}
}

func (s *insecureClientSuite) TestDefaultClientFails(c *gc.C) {
	_, err := http.Get(s.Server.URL)
	c.Assert(err, gc.ErrorMatches, "(.|\n)*x509: certificate signed by unknown authority")
}

func (s *insecureClientSuite) TestInsecureClientSucceeds(c *gc.C) {
	response, err := utils.GetNonValidatingHTTPClient().Get(s.Server.URL)
	c.Assert(err, gc.IsNil)
	defer response.Body.Close()
	body, err := ioutil.ReadAll(response.Body)
	c.Assert(err, gc.IsNil)
	c.Check(string(body), gc.Equals, "Greetings!\n")
}

func (s *insecureClientSuite) TestInsecureClientCached(c *gc.C) {
	client1 := utils.GetNonValidatingHTTPClient()
	client2 := utils.GetNonValidatingHTTPClient()
	c.Check(client1, gc.Equals, client2)
}
<commit_msg>Add tests<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package utils_test

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"

	gc "launchpad.net/gocheck"

	"launchpad.net/juju-core/utils"
)

type httpSuite struct {
	Server *httptest.Server
}

var _ = gc.Suite(&httpSuite{})

type trivialResponseHandler struct{}

func (t *trivialResponseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Greetings!\n")
}

func (s *httpSuite) SetUpSuite(c *gc.C) {
}

func (s *httpSuite) TearDownSuite(c *gc.C) {
}

func (s *httpSuite) SetUpTest(c *gc.C) {
	s.Server = httptest.NewTLSServer(&trivialResponseHandler{})
}

func (s *httpSuite) TearDownTest(c *gc.C) {
	if s.Server != nil {
		s.Server.Close()
	}
}

func (s *httpSuite) TestDefaultClientFails(c *gc.C) {
	_, err := http.Get(s.Server.URL)
	c.Assert(err, gc.ErrorMatches, "(.|\n)*x509: certificate signed by unknown authority")
}

func (s *httpSuite) TestValidatingClientGetter(c *gc.C) {
	client1 := utils.GetValidatingHTTPClient()
	client2 := utils.GetHTTPClient(utils.VerifySSLHostnames)
	c.Check(client1, gc.Equals, client2)
}

func (s *httpSuite) TestNonValidatingClientGetter(c *gc.C) {
	client1 := utils.GetNonValidatingHTTPClient()
	client2 := utils.GetHTTPClient(utils.NoVerifySSLHostnames)
	c.Check(client1, gc.Equals, client2)
}

func (s *httpSuite) TestValidatingClientFails(c *gc.C) {
	client := utils.GetValidatingHTTPClient()
	_, err := client.Get(s.Server.URL)
	c.Assert(err, gc.ErrorMatches, "(.|\n)*x509: certificate signed by unknown authority")
}

func (s *httpSuite) TestInsecureClientSucceeds(c *gc.C) {
	response, err := utils.GetNonValidatingHTTPClient().Get(s.Server.URL)
	c.Assert(err, gc.IsNil)
	defer response.Body.Close()
	body, err := ioutil.ReadAll(response.Body)
	c.Assert(err, gc.IsNil)
	c.Check(string(body), gc.Equals, "Greetings!\n")
}

func (s *httpSuite) TestInsecureClientCached(c *gc.C) {
	client1 := utils.GetNonValidatingHTTPClient()
	client2 := utils.GetNonValidatingHTTPClient()
	c.Check(client1, gc.Equals, client2)
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"path"
	"flag"
	"http"
	"log"
	"strings"
	"fmt"
	"time"
	"recipe"
)

func NotFoundHandler(w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Cache-Control", "max-age=10, must-revalidate")
	w.WriteHeader(http.StatusNotFound)
	page := newPage("Not Found")
	page.NotFound = true
	page.Write(w, req)
	return
}

func setCacheControl(w http.ResponseWriter, req *http.Request) {
	if req.Header["X-Draft"] != nil {
		w.Header().Set("Cache-Control", "max-age=1, must-revalidate")
	} else {
		now := time.UTC()
		d := time.Time{2011, 4, 11, 3, 0, 0, time.Monday, 0, "UTC"}
		TTL := int64(86400)
		ttl := TTL - (now.Seconds()-d.Seconds())%TTL  // shift
		w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%d", ttl))
	}
	w.Header().Set("Vary", "Accept-Encoding")
}

func PageHandler(w http.ResponseWriter, req *http.Request) {
	if req.URL.Path == "/" {
		setCacheControl(w, req)
		page := newPage("")
		page.Write(w, req)
		return
	}
	StaticHandler(w, req)
}

func RecipesHandler(w http.ResponseWriter, req *http.Request) {
	var r = recipe.Recipes[path.Base(req.URL.Path)]
	if r != nil {
		var p = strings.Replace(req.URL.Path, "recipes", "recipe", -1)
		w.Header().Set("Location", p)
		w.WriteHeader(http.StatusMovedPermanently)
		return
	}

	if req.URL.Path != "/recipes/" {
		NotFoundHandler(w, req)
		return
	}
	setCacheControl(w, req)
	page := newPage("Recipes")
	page.Recipes = recipe.Recipe_list
	page.Write(w, req)

}

func RecipeHandler(w http.ResponseWriter, req *http.Request) {
	var r = recipe.Recipes[path.Base(req.URL.Path)]
	if r == nil {
		NotFoundHandler(w, req)
		return
	}
	if req.URL.Path[len(req.URL.Path)-1] != '/' {
		http.Redirect(w, req, req.URL.Path+"/", http.StatusMovedPermanently)
		return
	}
	setCacheControl(w, req)
	page := newPage(r.Name + " Recipe")
	page.Recipe = r
	page.Write(w, req)
}

func CanonicalHostHandler(w http.ResponseWriter, req *http.Request) {
	var canonical = "www.eikeon.com"
	if req.Host != canonical {
		http.Redirect(w, req, "http://"+canonical+req.URL.Path, http.StatusMovedPermanently)
	} else {
		NotFoundHandler(w, req)
	}
	// TODO: set CacheControl
}

func StaticHandler(w http.ResponseWriter, req *http.Request) {
	var filename = path.Join(*StaticRoot, req.URL.Path)
	f, err := os.Open(filename)
	if err != nil {
		log.Print(err)
		NotFoundHandler(w, req)
		return
	}
	err = f.Close()

	if strings.Contains(req.URL.Path, "^") {
		w.Header().Set("Cache-Control", "max-age=3153600")
	} else {
		w.Header().Set("Cache-Control", "max-age=1, must-revalidate")
	}
	http.ServeFile(w, req, filename)
}

var Address *string
var StaticRoot *string

func main() {
	Address = flag.String("address", ":9999", "http service address")
	StaticRoot = flag.String("root", "static", "...")
	flag.Parse()

	http.Handle("eikeon.com/", http.HandlerFunc(CanonicalHostHandler))
	http.Handle("/", http.HandlerFunc(PageHandler))
	http.Handle("/recipes/", http.HandlerFunc(RecipesHandler))
	http.Handle("/recipe/", http.HandlerFunc(RecipeHandler))

	err := http.ListenAndServe(*Address, nil)
	if err != nil {
		log.Print("ListenAndServe:", err)
	}
}
<commit_msg>added vary accept encoding header<commit_after>package main

import (
	"os"
	"path"
	"flag"
	"http"
	"log"
	"strings"
	"fmt"
	"time"
	"recipe"
)

func NotFoundHandler(w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Cache-Control", "max-age=10, must-revalidate")
	w.WriteHeader(http.StatusNotFound)
	page := newPage("Not Found")
	page.NotFound = true
	page.Write(w, req)
	return
}

func setCacheControl(w http.ResponseWriter, req *http.Request) {
	if req.Header["X-Draft"] != nil {
		w.Header().Set("Cache-Control", "max-age=1, must-revalidate")
	} else {
		now := time.UTC()
		d := time.Time{2011, 4, 11, 3, 0, 0, time.Monday, 0, "UTC"}
		TTL := int64(86400)
		ttl := TTL - (now.Seconds()-d.Seconds())%TTL  // shift
		w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%d", ttl))
	}
	w.Header().Set("Vary", "Accept-Encoding")
}

func PageHandler(w http.ResponseWriter, req *http.Request) {
	if req.URL.Path == "/" {
		setCacheControl(w, req)
		page := newPage("")
		page.Write(w, req)
		return
	}
	StaticHandler(w, req)
}

func RecipesHandler(w http.ResponseWriter, req *http.Request) {
	var r = recipe.Recipes[path.Base(req.URL.Path)]
	if r != nil {
		var p = strings.Replace(req.URL.Path, "recipes", "recipe", -1)
		w.Header().Set("Location", p)
		w.WriteHeader(http.StatusMovedPermanently)
		return
	}

	if req.URL.Path != "/recipes/" {
		NotFoundHandler(w, req)
		return
	}
	setCacheControl(w, req)
	page := newPage("Recipes")
	page.Recipes = recipe.Recipe_list
	page.Write(w, req)

}

func RecipeHandler(w http.ResponseWriter, req *http.Request) {
	var r = recipe.Recipes[path.Base(req.URL.Path)]
	if r == nil {
		NotFoundHandler(w, req)
		return
	}
	if req.URL.Path[len(req.URL.Path)-1] != '/' {
		http.Redirect(w, req, req.URL.Path+"/", http.StatusMovedPermanently)
		return
	}
	setCacheControl(w, req)
	page := newPage(r.Name + " Recipe")
	page.Recipe = r
	page.Write(w, req)
}

func CanonicalHostHandler(w http.ResponseWriter, req *http.Request) {
	var canonical = "www.eikeon.com"
	if req.Host != canonical {
		http.Redirect(w, req, "http://"+canonical+req.URL.Path, http.StatusMovedPermanently)
	} else {
		NotFoundHandler(w, req)
	}
	// TODO: set CacheControl
}

func StaticHandler(w http.ResponseWriter, req *http.Request) {
	var filename = path.Join(*StaticRoot, req.URL.Path)
	f, err := os.Open(filename)
	if err != nil {
		log.Print(err)
		NotFoundHandler(w, req)
		return
	}
	err = f.Close()

	w.Header().Set("Vary", "Accept-Encoding")
	if strings.Contains(req.URL.Path, "^") {
		w.Header().Set("Cache-Control", "max-age=3153600")
	} else {
		w.Header().Set("Cache-Control", "max-age=1, must-revalidate")
	}
	http.ServeFile(w, req, filename)
}

var Address *string
var StaticRoot *string

func main() {
	Address = flag.String("address", ":9999", "http service address")
	StaticRoot = flag.String("root", "static", "...")
	flag.Parse()

	http.Handle("eikeon.com/", http.HandlerFunc(CanonicalHostHandler))
	http.Handle("/", http.HandlerFunc(PageHandler))
	http.Handle("/recipes/", http.HandlerFunc(RecipesHandler))
	http.Handle("/recipe/", http.HandlerFunc(RecipeHandler))

	err := http.ListenAndServe(*Address, nil)
	if err != nil {
		log.Print("ListenAndServe:", err)
	}
}
<|endoftext|>


<commit_before>package main

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

	// static assets
	_ "github.com/SpectoLabs/hoverfly/statik"
	"github.com/rakyll/statik/fs"

	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/negroni"
	"github.com/go-zoo/bone"
	"github.com/meatballhat/negroni-logrus"
)

// recordedRequests struct encapsulates payload data
type recordedRequests struct {
	Data []Payload `json:"data"`
}

type recordsCount struct {
	Count int `json:"count"`
}

type stateRequest struct {
	Mode        string `json:"mode"`
	Destination string `json:"destination"`
}

type messageResponse struct {
	Message string `json:"message"`
}

func (d *DBClient) startAdminInterface() {
	// starting admin interface
	mux := getBoneRouter(*d)
	n := negroni.Classic()

	loglevel := log.WarnLevel

	if d.cfg.verbose {
		loglevel = log.DebugLevel
	}

	n.Use(negronilogrus.NewCustomMiddleware(loglevel, &log.JSONFormatter{}, "admin"))
	n.UseHandler(mux)

	// admin interface starting message
	log.WithFields(log.Fields{
		"AdminPort": d.cfg.adminPort,
	}).Info("Admin interface is starting...")

	n.Run(fmt.Sprintf(":%s", d.cfg.adminPort))
}

// getBoneRouter returns mux for admin interface
func getBoneRouter(d DBClient) *bone.Mux {
	mux := bone.New()

	// preparing static assets for embedded admin
	statikFS, err := fs.New()
	if err != nil {
		log.WithFields(log.Fields{
			"Error": err.Error(),
		}).Error("Failed to load statikFS, admin UI might not work :(")
	}

	mux.Get("/records", http.HandlerFunc(d.AllRecordsHandler))
	mux.Delete("/records", http.HandlerFunc(d.DeleteAllRecordsHandler))
	mux.Post("/records", http.HandlerFunc(d.ImportRecordsHandler))

	mux.Get("/count", http.HandlerFunc(d.RecordsCount))

	mux.Get("/state", http.HandlerFunc(d.CurrentStateHandler))
	mux.Post("/state", http.HandlerFunc(d.StateHandler))

	mux.Handle("/*", http.FileServer(statikFS))

	return mux
}

// AllRecordsHandler returns JSON content type http response
func (d *DBClient) AllRecordsHandler(w http.ResponseWriter, req *http.Request) {
	records, err := d.cache.GetAllRequests()

	if err == nil {

		w.Header().Set("Content-Type", "application/json")

		var response recordedRequests
		response.Data = records
		b, err := json.Marshal(response)

		if err != nil {
			log.Error(err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
		} else {
			w.Write(b)
			return
		}
	} else {
		log.WithFields(log.Fields{
			"Error": err.Error(),
		}).Error("Failed to get data from cache!")

		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		w.WriteHeader(500) // can't process this entity
		return
	}
}

// RecordsCount returns number of captured requests as a JSON payload
func (d *DBClient) RecordsCount(w http.ResponseWriter, req *http.Request) {
	records, err := d.cache.GetAllRequests()

	if err == nil {

		w.Header().Set("Content-Type", "application/json")

		var response recordsCount
		response.Count = len(records)
		b, err := json.Marshal(response)

		if err != nil {
			log.Error(err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
		} else {
			w.Write(b)
			return
		}
	} else {
		log.WithFields(log.Fields{
			"Error": err.Error(),
		}).Error("Failed to get data from cache!")

		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		w.WriteHeader(500) // can't process this entity
		return
	}
}

func (d *DBClient) StatsHandler(w http.ResponseWriter, req *http.Request) {
	stats := d.counter.Flush()

	var sr statsResponse
	sr.Stats = stats

	w.Header().Set("Content-Type", "application/json")

	b, err := json.Marshal(sr)

	if err != nil {
		log.Error(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
	} else {
		w.Write(b)
		return
	}

}

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

// categoryWSFilterHandler is used for searching categories based on names and keywords through the websocket
func (d *DBClient) StatsWSHandler(w http.ResponseWriter, r *http.Request) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
		return
	}

	for {
		messageType, p, err := conn.ReadMessage()
		if err != nil {
			return
		}
		log.WithFields(log.Fields{
			"message": string(p),
		}).Info("Got message...")

		for _ = range time.Tick(1 * time.Second) {

			stats := d.counter.Flush()
			var sr statsResponse
			sr.Stats = stats

			b, err := json.Marshal(sr)

			if err = conn.WriteMessage(messageType, b); err != nil {
				log.WithFields(log.Fields{
					"message": p,
					"error":   err.Error(),
				}).Error("Got error when writing message...")
				return
			}
		}

	}

}

// ImportRecordsHandler - accepts JSON payload and saves it to cache
func (d *DBClient) ImportRecordsHandler(w http.ResponseWriter, req *http.Request) {

	var requests recordedRequests

	defer req.Body.Close()
	body, err := ioutil.ReadAll(req.Body)

	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	var response messageResponse

	if err != nil {
		// failed to read response body
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Could not read response body!")
		response.Message = "Bad request. Nothing to import!"
		http.Error(w, "Failed to read request body.", 400)
		return
	}

	err = json.Unmarshal(body, &requests)

	if err != nil {
		w.WriteHeader(422) // can't process this entity
		return
	}

	payloads := requests.Data
	if len(payloads) > 0 {
		for _, pl := range payloads {
			bts, err := pl.encode()
			if err != nil {
				log.WithFields(log.Fields{
					"error": err.Error(),
				}).Error("Failed to encode payload")
			} else {
				// recalculating request hash and storing it in database
				r := request{details: pl.Request}
				d.cache.Set([]byte(r.hash()), bts)
			}
		}
		response.Message = fmt.Sprintf("%d requests imported successfully", len(payloads))
	} else {
		response.Message = "Bad request. Nothing to import!"
		w.WriteHeader(400)
	}

	b, err := json.Marshal(response)
	w.Write(b)

}

// DeleteAllRecordsHandler - deletes all captured requests
func (d *DBClient) DeleteAllRecordsHandler(w http.ResponseWriter, req *http.Request) {
	err := d.cache.DeleteBucket(d.cache.requestsBucket)

	w.Header().Set("Content-Type", "application/json")

	var response messageResponse
	if err != nil {
		if err.Error() == "bucket not found" {
			response.Message = fmt.Sprintf("No records found")
			w.WriteHeader(200)
		} else {
			response.Message = fmt.Sprintf("Something went wrong: %s", err.Error())
			w.WriteHeader(500)
		}
	} else {
		response.Message = "Proxy cache deleted successfuly"
		w.WriteHeader(200)
	}
	b, err := json.Marshal(response)

	w.Write(b)
	return
}

// CurrentStateHandler returns current state
func (d *DBClient) CurrentStateHandler(w http.ResponseWriter, req *http.Request) {
	var resp stateRequest
	resp.Mode = d.cfg.GetMode()
	resp.Destination = d.cfg.destination

	b, _ := json.Marshal(resp)
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.Write(b)
}

// StateHandler handles current proxy state
func (d *DBClient) StateHandler(w http.ResponseWriter, r *http.Request) {
	var sr stateRequest

	// this is mainly for testing, since when you create
	if r.Body == nil {
		r.Body = ioutil.NopCloser(bytes.NewBuffer([]byte("")))
	}

	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)

	if err != nil {
		// failed to read response body
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Could not read response body!")
		http.Error(w, "Failed to read request body.", 400)
		return
	}

	err = json.Unmarshal(body, &sr)

	if err != nil {
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		w.WriteHeader(400) // can't process this entity
		return
	}

	availableModes := map[string]bool{
		"virtualize": true,
		"capture":    true,
		"modify":     true,
		"synthesize": true,
	}

	if !availableModes[sr.Mode] {
		log.WithFields(log.Fields{
			"suppliedMode": sr.Mode,
		}).Error("Wrong mode found, can't change state")
		http.Error(w, "Bad mode supplied, available modes: virtualize, capture, modify, synthesize.", 400)
		return
	}

	log.WithFields(log.Fields{
		"newState": sr.Mode,
		"body":     string(body),
	}).Info("Handling state change request!")

	// setting new state
	d.cfg.SetMode(sr.Mode)

	var resp stateRequest
	resp.Mode = d.cfg.GetMode()
	resp.Destination = d.cfg.destination
	b, _ := json.Marshal(resp)
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.Write(b)

}
<commit_msg>imports, mux routes to stats apis<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"time"

	// static assets
	_ "github.com/SpectoLabs/hoverfly/statik"
	"github.com/rakyll/statik/fs"

	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/negroni"
	"github.com/go-zoo/bone"
	"github.com/gorilla/websocket"
	"github.com/meatballhat/negroni-logrus"
)

// recordedRequests struct encapsulates payload data
type recordedRequests struct {
	Data []Payload `json:"data"`
}

type recordsCount struct {
	Count int `json:"count"`
}

type statsResponse struct {
	Stats HoverflyStats `json:"stats"`
}

type stateRequest struct {
	Mode        string `json:"mode"`
	Destination string `json:"destination"`
}

type messageResponse struct {
	Message string `json:"message"`
}

func (d *DBClient) startAdminInterface() {
	// starting admin interface
	mux := getBoneRouter(*d)
	n := negroni.Classic()

	loglevel := log.WarnLevel

	if d.cfg.verbose {
		loglevel = log.DebugLevel
	}

	n.Use(negronilogrus.NewCustomMiddleware(loglevel, &log.JSONFormatter{}, "admin"))
	n.UseHandler(mux)

	// admin interface starting message
	log.WithFields(log.Fields{
		"AdminPort": d.cfg.adminPort,
	}).Info("Admin interface is starting...")

	n.Run(fmt.Sprintf(":%s", d.cfg.adminPort))
}

// getBoneRouter returns mux for admin interface
func getBoneRouter(d DBClient) *bone.Mux {
	mux := bone.New()

	// preparing static assets for embedded admin
	statikFS, err := fs.New()
	if err != nil {
		log.WithFields(log.Fields{
			"Error": err.Error(),
		}).Error("Failed to load statikFS, admin UI might not work :(")
	}

	mux.Get("/records", http.HandlerFunc(d.AllRecordsHandler))
	mux.Delete("/records", http.HandlerFunc(d.DeleteAllRecordsHandler))
	mux.Post("/records", http.HandlerFunc(d.ImportRecordsHandler))

	mux.Get("/count", http.HandlerFunc(d.RecordsCount))
	mux.Get("/stats", http.HandlerFunc(d.StatsHandler))
	mux.Get("/statsws", http.HandlerFunc(d.StatsWSHandler))

	mux.Get("/state", http.HandlerFunc(d.CurrentStateHandler))
	mux.Post("/state", http.HandlerFunc(d.StateHandler))

	mux.Handle("/*", http.FileServer(statikFS))

	return mux
}

// AllRecordsHandler returns JSON content type http response
func (d *DBClient) AllRecordsHandler(w http.ResponseWriter, req *http.Request) {
	records, err := d.cache.GetAllRequests()

	if err == nil {

		w.Header().Set("Content-Type", "application/json")

		var response recordedRequests
		response.Data = records
		b, err := json.Marshal(response)

		if err != nil {
			log.Error(err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
		} else {
			w.Write(b)
			return
		}
	} else {
		log.WithFields(log.Fields{
			"Error": err.Error(),
		}).Error("Failed to get data from cache!")

		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		w.WriteHeader(500) // can't process this entity
		return
	}
}

// RecordsCount returns number of captured requests as a JSON payload
func (d *DBClient) RecordsCount(w http.ResponseWriter, req *http.Request) {
	records, err := d.cache.GetAllRequests()

	if err == nil {

		w.Header().Set("Content-Type", "application/json")

		var response recordsCount
		response.Count = len(records)
		b, err := json.Marshal(response)

		if err != nil {
			log.Error(err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
		} else {
			w.Write(b)
			return
		}
	} else {
		log.WithFields(log.Fields{
			"Error": err.Error(),
		}).Error("Failed to get data from cache!")

		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		w.WriteHeader(500) // can't process this entity
		return
	}
}

func (d *DBClient) StatsHandler(w http.ResponseWriter, req *http.Request) {
	stats := d.counter.Flush()

	var sr statsResponse
	sr.Stats = stats

	w.Header().Set("Content-Type", "application/json")

	b, err := json.Marshal(sr)

	if err != nil {
		log.Error(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
	} else {
		w.Write(b)
		return
	}

}

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

// categoryWSFilterHandler is used for searching categories based on names and keywords through the websocket
func (d *DBClient) StatsWSHandler(w http.ResponseWriter, r *http.Request) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
		return
	}

	for {
		messageType, p, err := conn.ReadMessage()
		if err != nil {
			return
		}
		log.WithFields(log.Fields{
			"message": string(p),
		}).Info("Got message...")

		for _ = range time.Tick(1 * time.Second) {

			stats := d.counter.Flush()
			var sr statsResponse
			sr.Stats = stats

			b, err := json.Marshal(sr)

			if err = conn.WriteMessage(messageType, b); err != nil {
				log.WithFields(log.Fields{
					"message": p,
					"error":   err.Error(),
				}).Error("Got error when writing message...")
				return
			}
		}

	}

}

// ImportRecordsHandler - accepts JSON payload and saves it to cache
func (d *DBClient) ImportRecordsHandler(w http.ResponseWriter, req *http.Request) {

	var requests recordedRequests

	defer req.Body.Close()
	body, err := ioutil.ReadAll(req.Body)

	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	var response messageResponse

	if err != nil {
		// failed to read response body
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Could not read response body!")
		response.Message = "Bad request. Nothing to import!"
		http.Error(w, "Failed to read request body.", 400)
		return
	}

	err = json.Unmarshal(body, &requests)

	if err != nil {
		w.WriteHeader(422) // can't process this entity
		return
	}

	payloads := requests.Data
	if len(payloads) > 0 {
		for _, pl := range payloads {
			bts, err := pl.encode()
			if err != nil {
				log.WithFields(log.Fields{
					"error": err.Error(),
				}).Error("Failed to encode payload")
			} else {
				// recalculating request hash and storing it in database
				r := request{details: pl.Request}
				d.cache.Set([]byte(r.hash()), bts)
			}
		}
		response.Message = fmt.Sprintf("%d requests imported successfully", len(payloads))
	} else {
		response.Message = "Bad request. Nothing to import!"
		w.WriteHeader(400)
	}

	b, err := json.Marshal(response)
	w.Write(b)

}

// DeleteAllRecordsHandler - deletes all captured requests
func (d *DBClient) DeleteAllRecordsHandler(w http.ResponseWriter, req *http.Request) {
	err := d.cache.DeleteBucket(d.cache.requestsBucket)

	w.Header().Set("Content-Type", "application/json")

	var response messageResponse
	if err != nil {
		if err.Error() == "bucket not found" {
			response.Message = fmt.Sprintf("No records found")
			w.WriteHeader(200)
		} else {
			response.Message = fmt.Sprintf("Something went wrong: %s", err.Error())
			w.WriteHeader(500)
		}
	} else {
		response.Message = "Proxy cache deleted successfuly"
		w.WriteHeader(200)
	}
	b, err := json.Marshal(response)

	w.Write(b)
	return
}

// CurrentStateHandler returns current state
func (d *DBClient) CurrentStateHandler(w http.ResponseWriter, req *http.Request) {
	var resp stateRequest
	resp.Mode = d.cfg.GetMode()
	resp.Destination = d.cfg.destination

	b, _ := json.Marshal(resp)
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.Write(b)
}

// StateHandler handles current proxy state
func (d *DBClient) StateHandler(w http.ResponseWriter, r *http.Request) {
	var sr stateRequest

	// this is mainly for testing, since when you create
	if r.Body == nil {
		r.Body = ioutil.NopCloser(bytes.NewBuffer([]byte("")))
	}

	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)

	if err != nil {
		// failed to read response body
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Could not read response body!")
		http.Error(w, "Failed to read request body.", 400)
		return
	}

	err = json.Unmarshal(body, &sr)

	if err != nil {
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		w.WriteHeader(400) // can't process this entity
		return
	}

	availableModes := map[string]bool{
		"virtualize": true,
		"capture":    true,
		"modify":     true,
		"synthesize": true,
	}

	if !availableModes[sr.Mode] {
		log.WithFields(log.Fields{
			"suppliedMode": sr.Mode,
		}).Error("Wrong mode found, can't change state")
		http.Error(w, "Bad mode supplied, available modes: virtualize, capture, modify, synthesize.", 400)
		return
	}

	log.WithFields(log.Fields{
		"newState": sr.Mode,
		"body":     string(body),
	}).Info("Handling state change request!")

	// setting new state
	d.cfg.SetMode(sr.Mode)

	var resp stateRequest
	resp.Mode = d.cfg.GetMode()
	resp.Destination = d.cfg.destination
	b, _ := json.Marshal(resp)
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.Write(b)

}
<|endoftext|>


<commit_before>package asock // import "firepear.net/asock"

// Copyright (c) 2014,2015 Shawn Boyette <shawn@firepear.net>. All
// rights reserved.  Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import (
	"net"
	"sync"
	"time"
)

// Message levels control which messages will be sent to as.Msgr
const (
	All = iota
	Conn
	Error
	Fatal
	Version = "0.11.0"
)

// Asock is a handle on an asock instance. It contains the
// Msgr channel, which is the conduit for notifications from the
// instance.
type Asock struct {
	Msgr chan *Msg
	q    chan bool
	w    *sync.WaitGroup
	s    string       // socket name
	l    net.Listener // listener socket
	d    Dispatch     // dispatch table
	t    int          // timeout
	ml   int          // message level
}

// Config holds values to be passed to the constuctor.
//
// For Unix sockets, Sockname takes the form "/path/to/socket". For
// TCP socks, it is either an IPv4 or IPv6 address followed by the
// desired port number ("127.0.0.1:9090", "[::1]:9090").
//
// Timeout is the number of seconds the socket will wait before timing
// out due to inactivity. Set it to zero for no timeout. Set to a
// negative value for a connection which closes after handling one
// request — so a timeout of -3 gives a connection which closes after
// one request or 3 seconds, whichever happens first.
//
// Msglvl determines which messages will be sent to the socket's
// message channel. Valid values are asock.All, asock.Conn,
// asock.Error, and asock.Fatal.
type Config struct {
	Sockname string
	Timeout  int
	Msglvl   int
}

// Dispatch is the dispatch table which drives asock's behavior. See
// the package Overview for more info on this and DispatchFunc.
type Dispatch map[string]*DispatchFunc

// DispatchFunc instances are the functions called via Dispatch.
//
// Func is the function to be called.
//
// Argmode determines how the bytestream read from the socket will be
// turned into arguments to Func. Valid values are "split" and
// "nosplit".
type DispatchFunc struct {
	Func    func ([][]byte) ([]byte, error)
	Argmode string
}

// Msg is the format which asock uses to communicate informational
// messages and errors to its host program. See the package Overview
// for more info.
type Msg struct {
	Conn int
	Req  int
	Code int
	Txt  string
	Err  error
}

// NewTCP returns an instance of Asock which uses TCP networking. It
// takes two arguments: an instance of Config and an instance of
// Dispatch
func NewTCP(c Config, d Dispatch) (*Asock, error) {
	tcpaddr, err := net.ResolveTCPAddr("tcp", c.Sockname)
	l, err := net.ListenTCP("tcp", tcpaddr)
	if err != nil {
		return nil, err
	}
	return commonNew(c, d, l), nil
}

// NewUnix returns an instance of Asock which uses Unix domain
// networking. It takes two arguments: an instance of Config and an
// instance of Dispatch.
func NewUnix(c Config, d Dispatch) (*Asock, error) {
	l, err := net.ListenUnix("unix", &net.UnixAddr{Name: c.Sockname, Net: "unix"})
	if err != nil {
		return nil, err
	}
	if c.Timeout == -20707 { // triggers the listener to die for failure testing
		c.Timeout = 0
		l.SetDeadline(time.Now().Add(100 * time.Millisecond))
	}
	return commonNew(c, d, l), nil
}

// commonNew does shared setup work for the constructors (mostly so
// that changes to Asock don't have to be mirrored)
func commonNew(c Config, d Dispatch, l net.Listener) *Asock {
	var w sync.WaitGroup
	q := make(chan bool, 1) // master off-switch channel
	m := make(chan *Msg, 32) // error reporting
	a := &Asock{m, q, &w, c.Sockname, l, d, c.Timeout, c.Msglvl}
	a.w.Add(1)
	go a.sockAccept()
	return a
}

// genMsg creates messages and sends them to the Msgr channel.
func (a *Asock) genMsg(conn, req, code, ml int, txt string, err error) {
	// if this message's level is below the instance's level, don't
	// generate the message
	if ml < a.ml {
		return
	}
	select {
	case a.Msgr <- &Msg{conn, req, code, txt, err}:
	default:
	}
}

// Quit handles shutdown and cleanup for an asock instance,
// including waiting for any connections to terminate. When it
// returns, the Asock is fully shut down. See the package Overview
// for more info.
func (a *Asock) Quit() {
	a.q <- true
	a.l.Close()
	a.w.Wait()
	close(a.q)
	close(a.Msgr)
}
<commit_msg>one more doc tweak<commit_after>package asock // import "firepear.net/asock"

// Copyright (c) 2014,2015 Shawn Boyette <shawn@firepear.net>. All
// rights reserved.  Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import (
	"net"
	"sync"
	"time"
)

// Message levels control which messages will be sent to as.Msgr
const (
	All = iota
	Conn
	Error
	Fatal
	Version = "0.11.0"
)

// Asock is a handle on an asock instance. It contains the
// Msgr channel, which is the conduit for notifications from the
// instance.
type Asock struct {
	Msgr chan *Msg
	q    chan bool
	w    *sync.WaitGroup
	s    string       // socket name
	l    net.Listener // listener socket
	d    Dispatch     // dispatch table
	t    int          // timeout
	ml   int          // message level
}

// Config holds values to be passed to the constuctor.
//
// For Unix sockets, Sockname takes the form "/path/to/socket". For
// TCP socks, it is either an IPv4 or IPv6 address followed by the
// desired port number ("127.0.0.1:9090", "[::1]:9090").
//
// Timeout is the number of seconds the socket will wait before timing
// out due to inactivity. Set it to zero for no timeout. Set to a
// negative value for a connection which closes after handling one
// request — so a timeout of -3 gives a connection which closes after
// one request or a read wait of 3 seconds, whichever happens first.
//
// Msglvl determines which messages will be sent to the socket's
// message channel. Valid values are asock.All, asock.Conn,
// asock.Error, and asock.Fatal.
type Config struct {
	Sockname string
	Timeout  int
	Msglvl   int
}

// Dispatch is the dispatch table which drives asock's behavior. See
// the package Overview for more info on this and DispatchFunc.
type Dispatch map[string]*DispatchFunc

// DispatchFunc instances are the functions called via Dispatch.
//
// Func is the function to be called.
//
// Argmode determines how the bytestream read from the socket will be
// turned into arguments to Func. Valid values are "split" and
// "nosplit".
type DispatchFunc struct {
	Func    func ([][]byte) ([]byte, error)
	Argmode string
}

// Msg is the format which asock uses to communicate informational
// messages and errors to its host program. See the package Overview
// for more info.
type Msg struct {
	Conn int
	Req  int
	Code int
	Txt  string
	Err  error
}

// NewTCP returns an instance of Asock which uses TCP networking. It
// takes two arguments: an instance of Config and an instance of
// Dispatch
func NewTCP(c Config, d Dispatch) (*Asock, error) {
	tcpaddr, err := net.ResolveTCPAddr("tcp", c.Sockname)
	l, err := net.ListenTCP("tcp", tcpaddr)
	if err != nil {
		return nil, err
	}
	return commonNew(c, d, l), nil
}

// NewUnix returns an instance of Asock which uses Unix domain
// networking. It takes two arguments: an instance of Config and an
// instance of Dispatch.
func NewUnix(c Config, d Dispatch) (*Asock, error) {
	l, err := net.ListenUnix("unix", &net.UnixAddr{Name: c.Sockname, Net: "unix"})
	if err != nil {
		return nil, err
	}
	if c.Timeout == -20707 { // triggers the listener to die for failure testing
		c.Timeout = 0
		l.SetDeadline(time.Now().Add(100 * time.Millisecond))
	}
	return commonNew(c, d, l), nil
}

// commonNew does shared setup work for the constructors (mostly so
// that changes to Asock don't have to be mirrored)
func commonNew(c Config, d Dispatch, l net.Listener) *Asock {
	var w sync.WaitGroup
	q := make(chan bool, 1) // master off-switch channel
	m := make(chan *Msg, 32) // error reporting
	a := &Asock{m, q, &w, c.Sockname, l, d, c.Timeout, c.Msglvl}
	a.w.Add(1)
	go a.sockAccept()
	return a
}

// genMsg creates messages and sends them to the Msgr channel.
func (a *Asock) genMsg(conn, req, code, ml int, txt string, err error) {
	// if this message's level is below the instance's level, don't
	// generate the message
	if ml < a.ml {
		return
	}
	select {
	case a.Msgr <- &Msg{conn, req, code, txt, err}:
	default:
	}
}

// Quit handles shutdown and cleanup for an asock instance,
// including waiting for any connections to terminate. When it
// returns, the Asock is fully shut down. See the package Overview
// for more info.
func (a *Asock) Quit() {
	a.q <- true
	a.l.Close()
	a.w.Wait()
	close(a.q)
	close(a.Msgr)
}
<|endoftext|>


<commit_before>package drum

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
	"os"
	"strings"
)

// config
// number of 'steps' played by an instrument each song
const numSteps = 16

// Instrument is a high level representation of a single instrument in the
// pattern
type Instrument struct {
	id    uint8
	name  string
	steps [numSteps]bool
}

// Pattern is a high level representation of a track pattern contained within a
// .splice file
type Pattern struct {
	version     string
	tempo       float32
	instruments []Instrument
}

// String implements the stringer.
func (p *Pattern) String() string {

	// create string of track information according to spec
	//	Saved with HW Version: 0.708-alpha
	//	Tempo: 999
	//	(1) Kick	|x---|----|x---|----|
	//	(2) HiHat	|x-x-|x-x-|x-x-|x-x-|

	var buffer bytes.Buffer

	cleanedVersionString := strings.Trim(p.version, "\x00")
	buffer.WriteString(fmt.Sprintf("Saved with HW Version: %s\n", cleanedVersionString))
	buffer.WriteString(fmt.Sprintf("Tempo: %v\n", p.tempo))

	for _, in := range p.instruments {
		buffer.WriteString(fmt.Sprintf("(%v) %s\t", in.id, in.name))

		for i, step := range in.steps {
			if i%4 == 0 {
				buffer.WriteString("|")
			}

			switch {
			case step:
				buffer.WriteString("x")
			default:
				buffer.WriteString("-")
			}
		}

		buffer.WriteString("|\n")
	}

	return buffer.String()
}

// expectedHeader is a string that is expected to be present
// on `.splice` files to be decoded.

//const expectedHeader = [6]byte{'S', 'P', 'L', 'I', 'C', 'E'}
const expectedHeader = "SPLICE"

// DecodeFile decodes the drum machine file found at the provided path and returns
// a pointer to a parsed pattern which is the entry point to the rest of the data.
func DecodeFile(path string) (*Pattern, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	p, err := parseSpliceToPattern(file)
	if err != nil {
		fmt.Println("Error in parseSpliceToPattern: ", err)
		return nil, err
	}

	return p, nil
}

// parseSpliceToPattern decodes the given `.splice` files and stores
// the relevant information in the Pattern struct.
func parseSpliceToPattern(r io.Reader) (*Pattern, error) {
	var p Pattern

	var spliceHeader [len(expectedHeader)]byte
	if err := binary.Read(r, binary.BigEndian, &spliceHeader); err != nil {
		return nil, fmt.Errorf("unable to decode splice header: %v", err)
	}

	if expectedHeader != string(spliceHeader[:len(expectedHeader)]) {
		return nil, fmt.Errorf("decoded SPLICE header does not match expected value")
	}

	var trackSize int64
	if err := binary.Read(r, binary.BigEndian, &trackSize); err != nil {
		return nil, fmt.Errorf("unable to decode trackSize: %v", err)
	}

	var version [32]byte
	if err := binary.Read(r, binary.BigEndian, &version); err != nil {
		return nil, fmt.Errorf("unable to decode version: %v", err)
	}
	p.version = string(version[:])

	if err := binary.Read(r, binary.LittleEndian, &p.tempo); err != nil {
		return nil, fmt.Errorf("unable to decode tempo: %v", err)
	}

	// NOTE: LimitReader needs to take `-36` = version(32) + tempo(4) into
	// account since we've already read in version and tempo
	const offset = 36
	lr := io.LimitReader(r, trackSize-offset)

	for {
		in, err := readInstrumentsFromTrack(lr)
		if err != nil {
			if err != io.EOF {
				return nil, err
			}
			break
		}

		p.instruments = append(p.instruments, in)
	}

	return &p, nil
}

// readInstrumentsFromTrack decodes the instrument information contained within
// the body of the Pattern and appends the newInst to the Pattern.instruments a
// true (bool) is returned when all the instruments have been read.
func readInstrumentsFromTrack(lr io.Reader) (Instrument, error) {
	var in Instrument

	if err := binary.Read(lr, binary.BigEndian, &in.id); err != nil {
		if err == io.EOF {
			return Instrument{}, err
		}
		return Instrument{}, fmt.Errorf("unable to decode instID: %v", err)
	}

	var nameLen int32
	if err := binary.Read(lr, binary.BigEndian, &nameLen); err != nil {
		return Instrument{}, fmt.Errorf("unable to decode instrument nameLen: %v", err)
	}

	name := make([]byte, nameLen)
	if err := binary.Read(lr, binary.LittleEndian, &name); err != nil {
		return Instrument{}, fmt.Errorf("unable to decode instrument nameBuf: %v", err)
	}
	in.name = string(name)

	var steps [numSteps]byte
	if err := binary.Read(lr, binary.LittleEndian, &steps); err != nil {
		return Instrument{}, fmt.Errorf("unable to decode instrument steps: %v", err)
	}

	for i := range steps {
		switch steps[i] {
		case 0x01:
			in.steps[i] = true
		case 0x00:
			in.steps[i] = false
		default:
			return Instrument{}, fmt.Errorf("unexpected values in instrument steps")
		}
	}

	return in, nil
}
<commit_msg>minor comment correction<commit_after>package drum

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
	"os"
	"strings"
)

// config
// number of 'steps' played by an instrument each song
const numSteps = 16

// Instrument is a high level representation of a single instrument in the
// pattern
type Instrument struct {
	id    uint8
	name  string
	steps [numSteps]bool
}

// Pattern is a high level representation of a track pattern contained within a
// .splice file
type Pattern struct {
	version     string
	tempo       float32
	instruments []Instrument
}

// String implements the stringer.
func (p *Pattern) String() string {

	// create string of track information according to spec
	//	Saved with HW Version: 0.708-alpha
	//	Tempo: 999
	//	(1) Kick	|x---|----|x---|----|
	//	(2) HiHat	|x-x-|x-x-|x-x-|x-x-|

	var buffer bytes.Buffer

	cleanedVersionString := strings.Trim(p.version, "\x00")
	buffer.WriteString(fmt.Sprintf("Saved with HW Version: %s\n", cleanedVersionString))
	buffer.WriteString(fmt.Sprintf("Tempo: %v\n", p.tempo))

	for _, in := range p.instruments {
		buffer.WriteString(fmt.Sprintf("(%v) %s\t", in.id, in.name))

		for i, step := range in.steps {
			if i%4 == 0 {
				buffer.WriteString("|")
			}

			switch {
			case step:
				buffer.WriteString("x")
			default:
				buffer.WriteString("-")
			}
		}

		buffer.WriteString("|\n")
	}

	return buffer.String()
}

// expectedHeader is a string that is expected to be present
// on `.splice` files to be decoded.
const expectedHeader = "SPLICE"

// DecodeFile decodes the drum machine file found at the provided path and returns
// a pointer to a parsed pattern which is the entry point to the rest of the data.
func DecodeFile(path string) (*Pattern, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	p, err := parseSpliceToPattern(file)
	if err != nil {
		fmt.Println("Error in parseSpliceToPattern: ", err)
		return nil, err
	}

	return p, nil
}

// parseSpliceToPattern decodes the given `.splice` files and stores
// the relevant information in the Pattern struct.
func parseSpliceToPattern(r io.Reader) (*Pattern, error) {
	var p Pattern

	var spliceHeader [len(expectedHeader)]byte
	if err := binary.Read(r, binary.BigEndian, &spliceHeader); err != nil {
		return nil, fmt.Errorf("unable to decode splice header: %v", err)
	}

	if expectedHeader != string(spliceHeader[:len(expectedHeader)]) {
		return nil, fmt.Errorf("decoded SPLICE header does not match expected value")
	}

	var trackSize int64
	if err := binary.Read(r, binary.BigEndian, &trackSize); err != nil {
		return nil, fmt.Errorf("unable to decode trackSize: %v", err)
	}

	var version [32]byte
	if err := binary.Read(r, binary.BigEndian, &version); err != nil {
		return nil, fmt.Errorf("unable to decode version: %v", err)
	}
	p.version = string(version[:])

	if err := binary.Read(r, binary.LittleEndian, &p.tempo); err != nil {
		return nil, fmt.Errorf("unable to decode tempo: %v", err)
	}

	// NOTE: LimitReader needs to take `-36` = version(32) + tempo(4) into
	// account since we've already read in version and tempo
	const offset = 36
	lr := io.LimitReader(r, trackSize-offset)

	for {
		in, err := readInstrumentsFromTrack(lr)
		if err != nil {
			if err != io.EOF {
				return nil, err
			}
			break
		}

		p.instruments = append(p.instruments, in)
	}

	return &p, nil
}

// readInstrumentsFromTrack decodes the instrument information contained within
// the body of the Pattern and appends the newInst to the Pattern.instruments a
// true (bool) is returned when all the instruments have been read.
func readInstrumentsFromTrack(lr io.Reader) (Instrument, error) {
	var in Instrument

	if err := binary.Read(lr, binary.BigEndian, &in.id); err != nil {
		if err == io.EOF {
			return Instrument{}, err
		}
		return Instrument{}, fmt.Errorf("unable to decode instID: %v", err)
	}

	var nameLen int32
	if err := binary.Read(lr, binary.BigEndian, &nameLen); err != nil {
		return Instrument{}, fmt.Errorf("unable to decode instrument nameLen: %v", err)
	}

	name := make([]byte, nameLen)
	if err := binary.Read(lr, binary.LittleEndian, &name); err != nil {
		return Instrument{}, fmt.Errorf("unable to decode instrument nameBuf: %v", err)
	}
	in.name = string(name)

	var steps [numSteps]byte
	if err := binary.Read(lr, binary.LittleEndian, &steps); err != nil {
		return Instrument{}, fmt.Errorf("unable to decode instrument steps: %v", err)
	}

	for i := range steps {
		switch steps[i] {
		case 0x01:
			in.steps[i] = true
		case 0x00:
			in.steps[i] = false
		default:
			return Instrument{}, fmt.Errorf("unexpected values in instrument steps")
		}
	}

	return in, nil
}
<|endoftext|>


<commit_before>package dymoscale

import (
	"encoding/binary"
	"fmt"
	"io"

	"github.com/dcarley/gousb/usb"
)

const VendorID usb.ID = 0x0922 // Dymo, all devices

var (
	ErrInvalidRead = fmt.Errorf("scale gave invalid reading")
	ErrNeedsTare   = fmt.Errorf("scale reads negative, please tare")
	ErrWrongMode   = fmt.Errorf("scale is in ounces mode, please switch to grams")
)

type Mode int8

const (
	Grams  Mode = 2
	Ounces Mode = 11
)

type Stability int8

const (
	NoWeight  Stability = 2
	NeedsTare Stability = 5
	Stable    Stability = 4
)

type Measurementer interface {
	Grams() (int, error)
}

// Measurement represents a parsed reading from the scale.
type Measurement struct {
	AlwaysThree int8      // Don't know what this is but it's always 3
	Stability   Stability // How accurate the measurement was
	Mode        Mode      // Grams or Ounces
	ScaleFactor int8      // WeightMinor*10^n when Mode is Ounces
	WeightMinor uint8     //
	WeightMajor uint8     // Overflow for WeightMinor, n*256
}

// errors returns an error if the reading is invalid.
func (m *Measurement) errors() error {
	if m.AlwaysThree != 3 || m.Stability == 0 || m.Mode == 0 {
		return ErrInvalidRead
	}

	if m.Stability == NeedsTare {
		return ErrNeedsTare
	}

	if m.Mode != Grams && m.Stability != NoWeight {
		return ErrWrongMode
	}

	return nil
}

// Grams returns the measurement in grams.
func (m *Measurement) Grams() (int, error) {
	if err := m.errors(); err != nil {
		return 0, err
	}

	grams := int(m.WeightMinor) + (int(m.WeightMajor) * 256)
	return grams, nil
}

// ReadMeasurement obtains a Measurement from an io.Reader.
func ReadMeasurement(reader io.Reader) (Measurementer, error) {
	var reading Measurement
	err := binary.Read(reader, binary.LittleEndian, &reading)

	return &reading, err
}

// closeWithError closes all outstanding devices and the context, then
// returns the original error.
func closeWithError(context *usb.Context, devices []*usb.Device, err error) (Scaler, error) {
	for _, dev := range devices {
		dev.Close()
	}
	context.Close()

	return nil, err
}

type Scaler interface {
	ReadRaw() ([]byte, error)
	ReadMeasurement() (Measurementer, error)
	ReadGrams() (int, error)
	Close() error
}

type Scale struct {
	context  *usb.Context
	device   *usb.Device
	endpoint usb.Endpoint
}

// NewScale opens a connection to a Dymo USB scale. You MUST call Close()
// when you're finished.
func NewScale() (Scaler, error) {
	ctx := usb.NewContext()

	devs, err := ctx.ListDevices(func(desc *usb.Descriptor) bool {
		if desc.Vendor == VendorID {
			return true
		}

		return false
	})
	if err != nil {
		return closeWithError(ctx, devs, err)
	}

	if len(devs) != 1 {
		err := fmt.Errorf("expected 1 device, found %d", len(devs))
		return closeWithError(ctx, devs, err)
	}

	dev := devs[0]
	ep, err := dev.OpenEndpoint(
		dev.Configs[0].Config,
		dev.Configs[0].Interfaces[0].Number,
		dev.Configs[0].Interfaces[0].Setups[0].Number,
		dev.Configs[0].Interfaces[0].Setups[0].Endpoints[0].Address,
	)
	if err != nil {
		return closeWithError(ctx, devs, err)
	}

	scale := &Scale{
		context:  ctx,
		device:   dev,
		endpoint: ep,
	}

	return scale, nil
}

// ReadRaw gets a raw reading from the scale.
func (s *Scale) ReadRaw() ([]byte, error) {
	buf := make([]byte, s.endpoint.Info().MaxPacketSize)
	_, err := s.endpoint.Read(buf)

	return buf, err
}

// ReadMeasurement returns a parsed Measurement from the scale.
func (s *Scale) ReadMeasurement() (Measurementer, error) {
	// TODO: Reset on libusb errors here?
	return ReadMeasurement(s.endpoint)
}

// ReadGrams returns a reading from the scale in grams.
func (s *Scale) ReadGrams() (int, error) {
	measurement, err := s.ReadMeasurement()
	if err != nil {
		return 0, err
	}

	return measurement.Grams()
}

// Close closes the USB device and context. If there are any errors then the
// inner-most is returned, but both will still attempt to be closed.
func (s *Scale) Close() error {
	errDev := s.device.Close()
	errCtx := s.context.Close()

	if errDev != nil {
		return errDev
	}
	return errCtx
}
<commit_msg>Recover from device failures<commit_after>package dymoscale

import (
	"encoding/binary"
	"fmt"
	"io"

	"github.com/dcarley/gousb/usb"
)

const VendorID usb.ID = 0x0922 // Dymo, all devices

var (
	ErrInvalidRead = fmt.Errorf("scale gave invalid reading")
	ErrNeedsTare   = fmt.Errorf("scale reads negative, please tare")
	ErrWrongMode   = fmt.Errorf("scale is in ounces mode, please switch to grams")
)

type Mode int8

const (
	Grams  Mode = 2
	Ounces Mode = 11
)

type Stability int8

const (
	NoWeight  Stability = 2
	NeedsTare Stability = 5
	Stable    Stability = 4
)

type Measurementer interface {
	Grams() (int, error)
}

// Measurement represents a parsed reading from the scale.
type Measurement struct {
	AlwaysThree int8      // Don't know what this is but it's always 3
	Stability   Stability // How accurate the measurement was
	Mode        Mode      // Grams or Ounces
	ScaleFactor int8      // WeightMinor*10^n when Mode is Ounces
	WeightMinor uint8     //
	WeightMajor uint8     // Overflow for WeightMinor, n*256
}

// errors returns an error if the reading is invalid.
func (m *Measurement) errors() error {
	if m.AlwaysThree != 3 || m.Stability == 0 || m.Mode == 0 {
		return ErrInvalidRead
	}

	if m.Stability == NeedsTare {
		return ErrNeedsTare
	}

	if m.Mode != Grams && m.Stability != NoWeight {
		return ErrWrongMode
	}

	return nil
}

// Grams returns the measurement in grams.
func (m *Measurement) Grams() (int, error) {
	if err := m.errors(); err != nil {
		return 0, err
	}

	grams := int(m.WeightMinor) + (int(m.WeightMajor) * 256)
	return grams, nil
}

// ReadMeasurement obtains a Measurement from an io.Reader.
func ReadMeasurement(reader io.Reader) (Measurementer, error) {
	var reading Measurement
	err := binary.Read(reader, binary.LittleEndian, &reading)

	return &reading, err
}

// closeWithError closes all outstanding devices and the context, then
// returns the original error.
func closeWithError(context *usb.Context, devices []*usb.Device, err error) (Scaler, error) {
	for _, dev := range devices {
		dev.Close()
	}
	context.Close()

	return nil, err
}

type Scaler interface {
	ReadRaw() ([]byte, error)
	ReadMeasurement() (Measurementer, error)
	ReadGrams() (int, error)
	Close() error
}

type Scale struct {
	context  *usb.Context
	device   *usb.Device
	endpoint usb.Endpoint
}

// NewScale opens a connection to a Dymo USB scale. You MUST call Close()
// when you're finished.
func NewScale() (Scaler, error) {
	ctx := usb.NewContext()

	devs, err := ctx.ListDevices(func(desc *usb.Descriptor) bool {
		if desc.Vendor == VendorID {
			return true
		}

		return false
	})
	if err != nil {
		return closeWithError(ctx, devs, err)
	}

	if len(devs) != 1 {
		err := fmt.Errorf("expected 1 device, found %d", len(devs))
		return closeWithError(ctx, devs, err)
	}

	dev := devs[0]
	ep, err := dev.OpenEndpoint(
		dev.Configs[0].Config,
		dev.Configs[0].Interfaces[0].Number,
		dev.Configs[0].Interfaces[0].Setups[0].Number,
		dev.Configs[0].Interfaces[0].Setups[0].Endpoints[0].Address,
	)
	if err != nil {
		return closeWithError(ctx, devs, err)
	}

	scale := &Scale{
		context:  ctx,
		device:   dev,
		endpoint: ep,
	}

	return scale, nil
}

// ReadRaw gets a raw reading from the scale.
func (s *Scale) ReadRaw() ([]byte, error) {
	buf := make([]byte, s.endpoint.Info().MaxPacketSize)
	_, err := s.endpoint.Read(buf)

	// TODO: Rate limit? Log?
	if err == usb.ERROR_PIPE || err == usb.ERROR_TIMEOUT {
		s.device.Reset()
	}

	return buf, err
}

// ReadMeasurement returns a parsed Measurement from the scale.
func (s *Scale) ReadMeasurement() (Measurementer, error) {
	res, err := ReadMeasurement(s.endpoint)

	// TODO: Rate limit? Log?
	if err == usb.ERROR_PIPE || err == usb.ERROR_TIMEOUT {
		s.device.Reset()
	}

	return res, err
}

// ReadGrams returns a reading from the scale in grams.
func (s *Scale) ReadGrams() (int, error) {
	measurement, err := s.ReadMeasurement()
	if err != nil {
		return 0, err
	}

	return measurement.Grams()
}

// Close closes the USB device and context. If there are any errors then the
// inner-most is returned, but both will still attempt to be closed.
func (s *Scale) Close() error {
	errDev := s.device.Close()
	errCtx := s.context.Close()

	if errDev != nil {
		return errDev
	}
	return errCtx
}
<|endoftext|>