<commit_before>package auth

import (
	"encoding/json"
	"fmt"
	"github.com/timeredbull/tsuru/api/repository/gitosis"
	"github.com/timeredbull/tsuru/db"
	"github.com/timeredbull/tsuru/errors"
	"io"
	"io/ioutil"
	"launchpad.net/mgo/bson"
	"net/http"
	"strings"
)

func CreateUser(w http.ResponseWriter, r *http.Request) error {
	var u User
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}
	err = json.Unmarshal(b, &u)
	if err != nil {
		return &errors.Http{Code: http.StatusBadRequest, Message: err.Error()}
	}
	err = u.Create()
	if err == nil {
		w.WriteHeader(http.StatusCreated)
		return nil
	}

	if u.Get() == nil {
		err = &errors.Http{Code: http.StatusConflict, Message: "This email is already registered"}
	}

	return err
}

func Login(w http.ResponseWriter, r *http.Request) error {
	var pass map[string]string
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}
	err = json.Unmarshal(b, &pass)
	if err != nil {
		return &errors.Http{Code: http.StatusBadRequest, Message: "Invalid JSON"}
	}

	password, ok := pass["password"]
	if !ok {
		msg := "You must provide a password to login"
		return &errors.Http{Code: http.StatusBadRequest, Message: msg}
	}

	u := User{Email: r.URL.Query().Get(":email")}
	err = u.Get()
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "User not found"}
	}

	if u.Login(password) {
		t, _ := u.CreateToken()
		fmt.Fprintf(w, `{"token":"%s"}`, t.Token)
		return nil
	}

	msg := "Authentication failed, wrong password"
	return &errors.Http{Code: http.StatusUnauthorized, Message: msg}
}

func createTeam(name string, u *User) error {
	team := &Team{Name: name, Users: []*User{u}}
	err := db.Session.Teams().Insert(team)
	if err != nil && strings.Contains(err.Error(), "duplicate key error") {
		return &errors.Http{Code: http.StatusConflict, Message: "This team already exists"}
	}
	ch := gitosis.Change{
		Kind: gitosis.AddGroup,
		Args: map[string]string{"group": name},
	}
	gitosis.Changes <- ch
	return nil
}

func CreateTeam(w http.ResponseWriter, r *http.Request, u *User) error {
	var params map[string]string
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}
	err = json.Unmarshal(b, &params)
	if err != nil {
		return &errors.Http{Code: http.StatusBadRequest, Message: err.Error()}
	}
	name, ok := params["name"]
	if !ok {
		msg := "You must provide the team name"
		return &errors.Http{Code: http.StatusBadRequest, Message: msg}
	}
	return createTeam(name, u)
}

func addUserToTeam(email, teamName string, u *User) error {
	team, user := new(Team), new(User)
	selector := bson.M{"name": teamName}
	err := db.Session.Teams().Find(selector).One(team)
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "Team not found"}
	}
	if !team.ContainsUser(u) {
		msg := fmt.Sprintf("You are not authorized to add new users to the team %s", team.Name)
		return &errors.Http{Code: http.StatusUnauthorized, Message: msg}
	}
	err = db.Session.Users().Find(bson.M{"email": email}).One(user)
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "User not found"}
	}
	err = team.AddUser(user)
	if err != nil {
		return &errors.Http{Code: http.StatusConflict, Message: err.Error()}
	}
	err = db.Session.Teams().Update(selector, team)
	if err != nil {
		return err
	}
	for _, key := range user.Keys {
		ch := gitosis.Change{
			Kind: gitosis.AddMember,
			Args: map[string]string{"group": team.Name, "member": key.Name},
		}
		gitosis.Changes <- ch
	}
	return nil
}

func AddUserToTeam(w http.ResponseWriter, r *http.Request, u *User) error {
	team := r.URL.Query().Get(":team")
	email := r.URL.Query().Get(":user")
	return addUserToTeam(email, team, u)
}

func RemoveUserFromTeam(w http.ResponseWriter, r *http.Request, u *User) error {
	team := new(Team)
	selector := bson.M{"name": r.URL.Query().Get(":team")}
	err := db.Session.Teams().Find(selector).One(team)
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "Team not found"}
	}
	if !team.ContainsUser(u) {
		msg := fmt.Sprintf("You are not authorized to remove a member from the team %s", team.Name)
		return &errors.Http{Code: http.StatusUnauthorized, Message: msg}
	}
	if len(team.Users) == 1 {
		msg := "You can not remove this user from this team, because it is the last user within the team, and a team can not be orphaned"
		return &errors.Http{Code: http.StatusForbidden, Message: msg}
	}
	user := User{Email: r.URL.Query().Get(":user")}
	err = team.RemoveUser(&user)
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: err.Error()}
	}
	return db.Session.Teams().Update(selector, team)
}

func getKeyFromBody(b io.Reader) (string, error) {
	var body map[string]string
	content, err := ioutil.ReadAll(b)
	if err != nil {
		return "", err
	}
	err = json.Unmarshal(content, &body)
	if err != nil {
		return "", &errors.Http{Code: http.StatusBadRequest, Message: "Invalid JSON"}
	}
	key, ok := body["key"]
	if !ok || key == "" {
		return "", &errors.Http{Code: http.StatusBadRequest, Message: "Missing key"}
	}
	return key, nil
}

func addKeyToUser(content string, u *User) error {
	key := Key{Content: content}
	if u.hasKey(key) {
		return &errors.Http{Code: http.StatusConflict, Message: "User has this key already"}
	}
	r := make(chan string)
	ch := gitosis.Change{
		Kind:     gitosis.AddKey,
		Args:     map[string]string{"member": u.Email, "key": content},
		Response: r,
	}
	gitosis.Changes <- ch
	var teams []Team
	db.Session.Teams().Find(bson.M{"users.email": u.Email}).All(&teams)
	key.Name = strings.Replace(<-r, ".pub", "", -1)
	for _, team := range teams {
		mch := gitosis.Change{
			Kind: gitosis.AddMember,
			Args: map[string]string{"group": team.Name, "member": key.Name},
		}
		gitosis.Changes <- mch
	}
	u.addKey(key)
	return db.Session.Users().Update(bson.M{"email": u.Email}, u)
}

// AddKeyToUser adds a key to a user.
//
// This function is just an http wrapper around addKeyToUser. The latter function
// exists to be used in other places in the package without the http stuff (request and
// response).
func AddKeyToUser(w http.ResponseWriter, r *http.Request, u *User) error {
	key, err := getKeyFromBody(r.Body)
	if err != nil {
		return err
	}
	return addKeyToUser(key, u)
}

func removeKeyFromUser(content string, u *User) error {
	key, index := u.findKey(Key{Content: content})
	if index < 0 {
		return &errors.Http{Code: http.StatusNotFound, Message: "User does not have this key"}
	}
	u.removeKey(key)
	err := db.Session.Users().Update(bson.M{"email": u.Email}, u)
	if err != nil {
		return err
	}
	ch := gitosis.Change{
		Kind: gitosis.RemoveKey,
		Args: map[string]string{"key": key.Name + ".pub"},
	}
	gitosis.Changes <- ch
	var teams []Team
	db.Session.Teams().Find(bson.M{"users.email": u.Email}).All(&teams)
	for _, team := range teams {
		mch := gitosis.Change{
			Kind: gitosis.RemoveMember,
			Args: map[string]string{"group": team.Name, "member": key.Name},
		}
		gitosis.Changes <- mch
	}
	return nil
}

// RemoveKeyFromUser removes a key from a user.
//
// This function is just an http wrapper around removeKeyFromUser. The latter function
// exists to be used in other places in the package without the http stuff (request and
// response).
func RemoveKeyFromUser(w http.ResponseWriter, r *http.Request, u *User) error {
	key, err := getKeyFromBody(r.Body)
	if err != nil {
		return err
	}
	return removeKeyFromUser(key, u)
}
<commit_msg>api/auth: extracting removeUserFromTeam function<commit_after>package auth

import (
	"encoding/json"
	"fmt"
	"github.com/timeredbull/tsuru/api/repository/gitosis"
	"github.com/timeredbull/tsuru/db"
	"github.com/timeredbull/tsuru/errors"
	"io"
	"io/ioutil"
	"launchpad.net/mgo/bson"
	"net/http"
	"strings"
)

func CreateUser(w http.ResponseWriter, r *http.Request) error {
	var u User
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}
	err = json.Unmarshal(b, &u)
	if err != nil {
		return &errors.Http{Code: http.StatusBadRequest, Message: err.Error()}
	}
	err = u.Create()
	if err == nil {
		w.WriteHeader(http.StatusCreated)
		return nil
	}

	if u.Get() == nil {
		err = &errors.Http{Code: http.StatusConflict, Message: "This email is already registered"}
	}

	return err
}

func Login(w http.ResponseWriter, r *http.Request) error {
	var pass map[string]string
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}
	err = json.Unmarshal(b, &pass)
	if err != nil {
		return &errors.Http{Code: http.StatusBadRequest, Message: "Invalid JSON"}
	}

	password, ok := pass["password"]
	if !ok {
		msg := "You must provide a password to login"
		return &errors.Http{Code: http.StatusBadRequest, Message: msg}
	}

	u := User{Email: r.URL.Query().Get(":email")}
	err = u.Get()
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "User not found"}
	}

	if u.Login(password) {
		t, _ := u.CreateToken()
		fmt.Fprintf(w, `{"token":"%s"}`, t.Token)
		return nil
	}

	msg := "Authentication failed, wrong password"
	return &errors.Http{Code: http.StatusUnauthorized, Message: msg}
}

func createTeam(name string, u *User) error {
	team := &Team{Name: name, Users: []*User{u}}
	err := db.Session.Teams().Insert(team)
	if err != nil && strings.Contains(err.Error(), "duplicate key error") {
		return &errors.Http{Code: http.StatusConflict, Message: "This team already exists"}
	}
	ch := gitosis.Change{
		Kind: gitosis.AddGroup,
		Args: map[string]string{"group": name},
	}
	gitosis.Changes <- ch
	return nil
}

func CreateTeam(w http.ResponseWriter, r *http.Request, u *User) error {
	var params map[string]string
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}
	err = json.Unmarshal(b, &params)
	if err != nil {
		return &errors.Http{Code: http.StatusBadRequest, Message: err.Error()}
	}
	name, ok := params["name"]
	if !ok {
		msg := "You must provide the team name"
		return &errors.Http{Code: http.StatusBadRequest, Message: msg}
	}
	return createTeam(name, u)
}

func addUserToTeam(email, teamName string, u *User) error {
	team, user := new(Team), new(User)
	selector := bson.M{"name": teamName}
	err := db.Session.Teams().Find(selector).One(team)
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "Team not found"}
	}
	if !team.ContainsUser(u) {
		msg := fmt.Sprintf("You are not authorized to add new users to the team %s", team.Name)
		return &errors.Http{Code: http.StatusUnauthorized, Message: msg}
	}
	err = db.Session.Users().Find(bson.M{"email": email}).One(user)
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "User not found"}
	}
	err = team.AddUser(user)
	if err != nil {
		return &errors.Http{Code: http.StatusConflict, Message: err.Error()}
	}
	err = db.Session.Teams().Update(selector, team)
	if err != nil {
		return err
	}
	for _, key := range user.Keys {
		ch := gitosis.Change{
			Kind: gitosis.AddMember,
			Args: map[string]string{"group": team.Name, "member": key.Name},
		}
		gitosis.Changes <- ch
	}
	return nil
}

func AddUserToTeam(w http.ResponseWriter, r *http.Request, u *User) error {
	team := r.URL.Query().Get(":team")
	email := r.URL.Query().Get(":user")
	return addUserToTeam(email, team, u)
}

func removeUserFromTeam(email, teamName string, u *User) error {
	team := new(Team)
	selector := bson.M{"name": teamName}
	err := db.Session.Teams().Find(selector).One(team)
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "Team not found"}
	}
	if !team.ContainsUser(u) {
		msg := fmt.Sprintf("You are not authorized to remove a member from the team %s", team.Name)
		return &errors.Http{Code: http.StatusUnauthorized, Message: msg}
	}
	if len(team.Users) == 1 {
		msg := "You can not remove this user from this team, because it is the last user within the team, and a team can not be orphaned"
		return &errors.Http{Code: http.StatusForbidden, Message: msg}
	}
	user := User{Email: email}
	err = team.RemoveUser(&user)
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: err.Error()}
	}
	return db.Session.Teams().Update(selector, team)
}

func RemoveUserFromTeam(w http.ResponseWriter, r *http.Request, u *User) error {
	email := r.URL.Query().Get(":user")
	team := r.URL.Query().Get(":team")
	return removeUserFromTeam(email, team, u)
}

func getKeyFromBody(b io.Reader) (string, error) {
	var body map[string]string
	content, err := ioutil.ReadAll(b)
	if err != nil {
		return "", err
	}
	err = json.Unmarshal(content, &body)
	if err != nil {
		return "", &errors.Http{Code: http.StatusBadRequest, Message: "Invalid JSON"}
	}
	key, ok := body["key"]
	if !ok || key == "" {
		return "", &errors.Http{Code: http.StatusBadRequest, Message: "Missing key"}
	}
	return key, nil
}

func addKeyToUser(content string, u *User) error {
	key := Key{Content: content}
	if u.hasKey(key) {
		return &errors.Http{Code: http.StatusConflict, Message: "User has this key already"}
	}
	r := make(chan string)
	ch := gitosis.Change{
		Kind:     gitosis.AddKey,
		Args:     map[string]string{"member": u.Email, "key": content},
		Response: r,
	}
	gitosis.Changes <- ch
	var teams []Team
	db.Session.Teams().Find(bson.M{"users.email": u.Email}).All(&teams)
	key.Name = strings.Replace(<-r, ".pub", "", -1)
	for _, team := range teams {
		mch := gitosis.Change{
			Kind: gitosis.AddMember,
			Args: map[string]string{"group": team.Name, "member": key.Name},
		}
		gitosis.Changes <- mch
	}
	u.addKey(key)
	return db.Session.Users().Update(bson.M{"email": u.Email}, u)
}

// AddKeyToUser adds a key to a user.
//
// This function is just an http wrapper around addKeyToUser. The latter function
// exists to be used in other places in the package without the http stuff (request and
// response).
func AddKeyToUser(w http.ResponseWriter, r *http.Request, u *User) error {
	key, err := getKeyFromBody(r.Body)
	if err != nil {
		return err
	}
	return addKeyToUser(key, u)
}

func removeKeyFromUser(content string, u *User) error {
	key, index := u.findKey(Key{Content: content})
	if index < 0 {
		return &errors.Http{Code: http.StatusNotFound, Message: "User does not have this key"}
	}
	u.removeKey(key)
	err := db.Session.Users().Update(bson.M{"email": u.Email}, u)
	if err != nil {
		return err
	}
	ch := gitosis.Change{
		Kind: gitosis.RemoveKey,
		Args: map[string]string{"key": key.Name + ".pub"},
	}
	gitosis.Changes <- ch
	var teams []Team
	db.Session.Teams().Find(bson.M{"users.email": u.Email}).All(&teams)
	for _, team := range teams {
		mch := gitosis.Change{
			Kind: gitosis.RemoveMember,
			Args: map[string]string{"group": team.Name, "member": key.Name},
		}
		gitosis.Changes <- mch
	}
	return nil
}

// RemoveKeyFromUser removes a key from a user.
//
// This function is just an http wrapper around removeKeyFromUser. The latter function
// exists to be used in other places in the package without the http stuff (request and
// response).
func RemoveKeyFromUser(w http.ResponseWriter, r *http.Request, u *User) error {
	key, err := getKeyFromBody(r.Body)
	if err != nil {
		return err
	}
	return removeKeyFromUser(key, u)
}
<|endoftext|>


<commit_before>package dns

import (
	"net"
	"strconv"
	"strings"
)

const hexDigit = "0123456789abcdef"

// Everything is assumed in the ClassINET class. If
// you need other classes you are on your own.

// SetReply creates a reply packet from a request message.
func (dns *Msg) SetReply(request *Msg) *Msg {
	dns.Id = request.Id
	dns.RecursionDesired = request.RecursionDesired // Copy rd bit
	dns.Response = true
	dns.Opcode = OpcodeQuery
	dns.Rcode = RcodeSuccess
	if len(request.Question) > 0 {
		dns.Question = make([]Question, 1)
		dns.Question[0] = request.Question[0]
	}
	return dns
}

// SetQuestion creates a question packet.
func (dns *Msg) SetQuestion(z string, t uint16) *Msg {
	dns.Id = Id()
	dns.RecursionDesired = true
	dns.Question = make([]Question, 1)
	dns.Question[0] = Question{z, t, ClassINET}
	return dns
}

// SetNotify creates a notify packet.
func (dns *Msg) SetNotify(z string) *Msg {
	dns.Opcode = OpcodeNotify
	dns.Authoritative = true
	dns.Id = Id()
	dns.Question = make([]Question, 1)
	dns.Question[0] = Question{z, TypeSOA, ClassINET}
	return dns
}

// SetRcode creates an error packet suitable for the request.
func (dns *Msg) SetRcode(request *Msg, rcode int) *Msg {
	dns.Rcode = rcode
	dns.Opcode = OpcodeQuery
	dns.Response = true
	dns.Id = request.Id
	// Note that this is actually a FORMERR
	if len(request.Question) > 0 {
		dns.Question = make([]Question, 1)
		dns.Question[0] = request.Question[0]
	}
	return dns
}

// SetRcodeFormatError creates a packet with FormError set.
func (dns *Msg) SetRcodeFormatError(request *Msg) *Msg {
	dns.Rcode = RcodeFormatError
	dns.Opcode = OpcodeQuery
	dns.Response = true
	dns.Authoritative = false
	dns.Id = request.Id
	return dns
}

// SetUpdate makes the message a dynamic update packet. It
// sets the ZONE section to: z, TypeSOA, ClassINET.
func (dns *Msg) SetUpdate(z string) *Msg {
	dns.Id = Id()
	dns.Response = false
	dns.Opcode = OpcodeUpdate
	dns.Compress = false // BIND9 cannot handle compression
	dns.Question = make([]Question, 1)
	dns.Question[0] = Question{z, TypeSOA, ClassINET}
	return dns
}

// SetIxfr creates dns msg suitable for requesting an ixfr.
func (dns *Msg) SetIxfr(z string, serial uint32) *Msg {
	dns.Id = Id()
	dns.Question = make([]Question, 1)
	dns.Ns = make([]RR, 1)
	s := new(RR_SOA)
	s.Hdr = RR_Header{z, TypeSOA, ClassINET, defaultTtl, 0}
	s.Serial = serial
	dns.Question[0] = Question{z, TypeIXFR, ClassINET}
	dns.Ns[0] = s
	return dns
}

// SetAxfr creates dns msg suitable for requesting an axfr.
func (dns *Msg) SetAxfr(z string) *Msg {
	dns.Id = Id()
	dns.Question = make([]Question, 1)
	dns.Question[0] = Question{z, TypeAXFR, ClassINET}
	return dns
}

// SetTsig appends a TSIG RR to the message.
// This is only a skeleton TSIG RR that is added as the last RR in the
// additional section. The Tsig is calculated when the message is being send.
func (dns *Msg) SetTsig(z, algo string, fudge, timesigned int64) *Msg {
	t := new(RR_TSIG)
	t.Hdr = RR_Header{z, TypeTSIG, ClassANY, 0, 0}
	t.Algorithm = algo
	t.Fudge = 300
	t.TimeSigned = uint64(timesigned)
	t.OrigId = dns.Id
	dns.Extra = append(dns.Extra, t)
	return dns
}

// SetEdns0 appends a EDNS0 OPT RR to the message.
// TSIG should always the last RR in a message.
func (dns *Msg) SetEdns0(udpsize uint16, do bool) *Msg {
	e := new(RR_OPT)
	e.Hdr.Name = "."
	e.Hdr.Rrtype = TypeOPT
	e.SetUDPSize(udpsize)
	if do {
		e.SetDo()
	}
	dns.Extra = append(dns.Extra, e)
	return dns
}

// IsTsig checks if the message has a TSIG record as the last record
// in the additional section. It returns the TSIG record found or nil.
func (dns *Msg) IsTsig() *RR_TSIG {
	if len(dns.Extra) > 0 {
		if dns.Extra[len(dns.Extra)-1].Header().Rrtype == TypeTSIG {
			return dns.Extra[len(dns.Extra)-1].(*RR_TSIG)
		}
	}
	return nil
}

// IsEdns0 checks if the message has a EDNS0 (OPT) record, any EDNS0
// record in the additional section will do. It returns the OPT record
// found or nil.
func (dns *Msg) IsEdns0() *RR_OPT {
	for _, r := range dns.Extra {
		if r.Header().Rrtype == TypeOPT {
			return r.(*RR_OPT)
		}
	}
	return nil
}

// IsDomainName checks if s is a valid domainname, it returns
// the number of labels, total length and true, when a domain name is valid.
// When false is returned the labelcount and length are not defined.
func IsDomainName(s string) (uint8, uint8, bool) { // copied from net package.
	// See RFC 1035, RFC 3696.
	l := len(s)
	if l == 0 || l > 255 {
		return 0, 0, false
	}
	longer := 0
	// Simplify checking loop: make the name end in a dot.
	// Don't call Fqdn() to save another len(s).
	// Keep in mind that if we do this, otherwise we report a length+1
	if s[l-1] != '.' {
		s += "."
		l++
		longer = 1
	}
	// Preloop check for root label
	if s == "." {
		return 0, 1, true
	}

	last := byte('.')
	ok := false // ok once we've seen a letter or digit
	partlen := 0
	labels := uint8(0)
	for i := 0; i < l; i++ {
		c := s[i]
		switch {
		default:
			return 0, uint8(l - longer), false
		case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_' || c == '*' || c == '/':
			ok = true
			partlen++
		case c == '\\':
			// Ok
		case '0' <= c && c <= '9':
			ok = true
			partlen++
		case c == '-':
			// byte before dash cannot be dot
			if last == '.' {
				return 0, uint8(l - longer), false
			}
			partlen++
		case c == '.':
			// byte before dot cannot be dot
			if last == '.' {
				return 0, uint8(l - longer), false
			}
			if last == '\\' { // Ok, escaped dot.
				partlen++
				break
			}
			if partlen > 63 || partlen == 0 {
				return 0, uint8(l - longer), false
			}
			partlen = 0
			labels++
		}
		last = c
	}
	return labels, uint8(l - longer), ok
}

// IsSubDomain checks if child is indeed a child of the parent.
func IsSubDomain(parent, child string) bool {
	// Entire child is contained in parent
	return CompareLabels(strings.ToLower(parent), strings.ToLower(child)) == LenLabels(parent)
}

// IsFqdn checks if a domain name is fully qualified.
func IsFqdn(s string) bool {
	l := len(s)
	if l == 0 {
		return false // ?
	}
	return s[l-1] == '.'
}

// Fqdns return the fully qualified domain name from s.
// If s is already fully qualified, it behaves as the identity function.
func Fqdn(s string) string {
	if IsFqdn(s) {
		return s
	}
	return s + "."
}

// Copied from the official Go code

// ReverseAddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP
// address addr suitable for rDNS (PTR) record lookup or an error if it fails
// to parse the IP address.
func ReverseAddr(addr string) (arpa string, err error) {
	ip := net.ParseIP(addr)
	if ip == nil {
		return "", &Error{Err: "unrecognized address", Name: addr}
	}
	if ip.To4() != nil {
		return strconv.Itoa(int(ip[15])) + "." + strconv.Itoa(int(ip[14])) + "." + strconv.Itoa(int(ip[13])) + "." +
			strconv.Itoa(int(ip[12])) + ".in-addr.arpa.", nil
	}
	// Must be IPv6
	buf := make([]byte, 0, len(ip)*4+len("ip6.arpa."))
	// Add it, in reverse, to the buffer
	for i := len(ip) - 1; i >= 0; i-- {
		v := ip[i]
		buf = append(buf, hexDigit[v&0xF])
		buf = append(buf, '.')
		buf = append(buf, hexDigit[v>>4])
		buf = append(buf, '.')
	}
	// Append "ip6.arpa." and return (buf already has the final .)
	buf = append(buf, "ip6.arpa."...)
	return string(buf), nil
}
<commit_msg>Add escaped @ is ok<commit_after>package dns

import (
	"net"
	"strconv"
	"strings"
)

const hexDigit = "0123456789abcdef"

// Everything is assumed in the ClassINET class. If
// you need other classes you are on your own.

// SetReply creates a reply packet from a request message.
func (dns *Msg) SetReply(request *Msg) *Msg {
	dns.Id = request.Id
	dns.RecursionDesired = request.RecursionDesired // Copy rd bit
	dns.Response = true
	dns.Opcode = OpcodeQuery
	dns.Rcode = RcodeSuccess
	if len(request.Question) > 0 {
		dns.Question = make([]Question, 1)
		dns.Question[0] = request.Question[0]
	}
	return dns
}

// SetQuestion creates a question packet.
func (dns *Msg) SetQuestion(z string, t uint16) *Msg {
	dns.Id = Id()
	dns.RecursionDesired = true
	dns.Question = make([]Question, 1)
	dns.Question[0] = Question{z, t, ClassINET}
	return dns
}

// SetNotify creates a notify packet.
func (dns *Msg) SetNotify(z string) *Msg {
	dns.Opcode = OpcodeNotify
	dns.Authoritative = true
	dns.Id = Id()
	dns.Question = make([]Question, 1)
	dns.Question[0] = Question{z, TypeSOA, ClassINET}
	return dns
}

// SetRcode creates an error packet suitable for the request.
func (dns *Msg) SetRcode(request *Msg, rcode int) *Msg {
	dns.Rcode = rcode
	dns.Opcode = OpcodeQuery
	dns.Response = true
	dns.Id = request.Id
	// Note that this is actually a FORMERR
	if len(request.Question) > 0 {
		dns.Question = make([]Question, 1)
		dns.Question[0] = request.Question[0]
	}
	return dns
}

// SetRcodeFormatError creates a packet with FormError set.
func (dns *Msg) SetRcodeFormatError(request *Msg) *Msg {
	dns.Rcode = RcodeFormatError
	dns.Opcode = OpcodeQuery
	dns.Response = true
	dns.Authoritative = false
	dns.Id = request.Id
	return dns
}

// SetUpdate makes the message a dynamic update packet. It
// sets the ZONE section to: z, TypeSOA, ClassINET.
func (dns *Msg) SetUpdate(z string) *Msg {
	dns.Id = Id()
	dns.Response = false
	dns.Opcode = OpcodeUpdate
	dns.Compress = false // BIND9 cannot handle compression
	dns.Question = make([]Question, 1)
	dns.Question[0] = Question{z, TypeSOA, ClassINET}
	return dns
}

// SetIxfr creates dns msg suitable for requesting an ixfr.
func (dns *Msg) SetIxfr(z string, serial uint32) *Msg {
	dns.Id = Id()
	dns.Question = make([]Question, 1)
	dns.Ns = make([]RR, 1)
	s := new(RR_SOA)
	s.Hdr = RR_Header{z, TypeSOA, ClassINET, defaultTtl, 0}
	s.Serial = serial
	dns.Question[0] = Question{z, TypeIXFR, ClassINET}
	dns.Ns[0] = s
	return dns
}

// SetAxfr creates dns msg suitable for requesting an axfr.
func (dns *Msg) SetAxfr(z string) *Msg {
	dns.Id = Id()
	dns.Question = make([]Question, 1)
	dns.Question[0] = Question{z, TypeAXFR, ClassINET}
	return dns
}

// SetTsig appends a TSIG RR to the message.
// This is only a skeleton TSIG RR that is added as the last RR in the
// additional section. The Tsig is calculated when the message is being send.
func (dns *Msg) SetTsig(z, algo string, fudge, timesigned int64) *Msg {
	t := new(RR_TSIG)
	t.Hdr = RR_Header{z, TypeTSIG, ClassANY, 0, 0}
	t.Algorithm = algo
	t.Fudge = 300
	t.TimeSigned = uint64(timesigned)
	t.OrigId = dns.Id
	dns.Extra = append(dns.Extra, t)
	return dns
}

// SetEdns0 appends a EDNS0 OPT RR to the message.
// TSIG should always the last RR in a message.
func (dns *Msg) SetEdns0(udpsize uint16, do bool) *Msg {
	e := new(RR_OPT)
	e.Hdr.Name = "."
	e.Hdr.Rrtype = TypeOPT
	e.SetUDPSize(udpsize)
	if do {
		e.SetDo()
	}
	dns.Extra = append(dns.Extra, e)
	return dns
}

// IsTsig checks if the message has a TSIG record as the last record
// in the additional section. It returns the TSIG record found or nil.
func (dns *Msg) IsTsig() *RR_TSIG {
	if len(dns.Extra) > 0 {
		if dns.Extra[len(dns.Extra)-1].Header().Rrtype == TypeTSIG {
			return dns.Extra[len(dns.Extra)-1].(*RR_TSIG)
		}
	}
	return nil
}

// IsEdns0 checks if the message has a EDNS0 (OPT) record, any EDNS0
// record in the additional section will do. It returns the OPT record
// found or nil.
func (dns *Msg) IsEdns0() *RR_OPT {
	for _, r := range dns.Extra {
		if r.Header().Rrtype == TypeOPT {
			return r.(*RR_OPT)
		}
	}
	return nil
}

// IsDomainName checks if s is a valid domainname, it returns
// the number of labels, total length and true, when a domain name is valid.
// When false is returned the labelcount and length are not defined.
// TODO(mg): checks for \DDD
func IsDomainName(s string) (uint8, uint8, bool) { // copied from net package.
	// See RFC 1035, RFC 3696.
	l := len(s)
	if l == 0 || l > 255 {
		return 0, 0, false
	}
	longer := 0
	// Simplify checking loop: make the name end in a dot.
	// Don't call Fqdn() to save another len(s).
	// Keep in mind that if we do this, otherwise we report a length+1
	if s[l-1] != '.' {
		s += "."
		l++
		longer = 1
	}
	// Preloop check for root label
	if s == "." {
		return 0, 1, true
	}

	last := byte('.')
	ok := false // ok once we've seen a letter or digit
	partlen := 0
	labels := uint8(0)
	for i := 0; i < l; i++ {
		c := s[i]
		switch {
		default:
			return 0, uint8(l - longer), false
		case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_' || c == '*' || c == '/':
			ok = true
			partlen++
		case c == '\\':
			// Ok
		case c == '@':
			if last != '\\' {
				return 0, uint8(l - longer), false
			}
			partlen++
		case '0' <= c && c <= '9':
			ok = true
			partlen++
		case c == '-':
			// byte before dash cannot be dot
			if last == '.' {
				return 0, uint8(l - longer), false
			}
			partlen++
		case c == '.':
			// byte before dot cannot be dot
			if last == '.' {
				return 0, uint8(l - longer), false
			}
			if last == '\\' { // Ok, escaped dot.
				partlen++
				break
			}
			if partlen > 63 || partlen == 0 {
				return 0, uint8(l - longer), false
			}
			partlen = 0
			labels++
		}
		last = c
	}
	return labels, uint8(l - longer), ok
}

// IsSubDomain checks if child is indeed a child of the parent.
func IsSubDomain(parent, child string) bool {
	// Entire child is contained in parent
	return CompareLabels(strings.ToLower(parent), strings.ToLower(child)) == LenLabels(parent)
}

// IsFqdn checks if a domain name is fully qualified.
func IsFqdn(s string) bool {
	l := len(s)
	if l == 0 {
		return false // ?
	}
	return s[l-1] == '.'
}

// Fqdns return the fully qualified domain name from s.
// If s is already fully qualified, it behaves as the identity function.
func Fqdn(s string) string {
	if IsFqdn(s) {
		return s
	}
	return s + "."
}

// Copied from the official Go code

// ReverseAddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP
// address addr suitable for rDNS (PTR) record lookup or an error if it fails
// to parse the IP address.
func ReverseAddr(addr string) (arpa string, err error) {
	ip := net.ParseIP(addr)
	if ip == nil {
		return "", &Error{Err: "unrecognized address", Name: addr}
	}
	if ip.To4() != nil {
		return strconv.Itoa(int(ip[15])) + "." + strconv.Itoa(int(ip[14])) + "." + strconv.Itoa(int(ip[13])) + "." +
			strconv.Itoa(int(ip[12])) + ".in-addr.arpa.", nil
	}
	// Must be IPv6
	buf := make([]byte, 0, len(ip)*4+len("ip6.arpa."))
	// Add it, in reverse, to the buffer
	for i := len(ip) - 1; i >= 0; i-- {
		v := ip[i]
		buf = append(buf, hexDigit[v&0xF])
		buf = append(buf, '.')
		buf = append(buf, hexDigit[v>>4])
		buf = append(buf, '.')
	}
	// Append "ip6.arpa." and return (buf already has the final .)
	buf = append(buf, "ip6.arpa."...)
	return string(buf), nil
}
<|endoftext|>


<commit_before>package bot

import (
	"math/rand"
	"regexp"
	"strings"
	"time"

	"fmt"

	"log"

	. "github.com/PoolC/slack_bot/util"
	slack "github.com/nlopes/slack"
)

var (
	remember_re   *regexp.Regexp = regexp.MustCompile("^안즈쨩? 기억해? ([^/]+)/(.+)")
	tell_re       *regexp.Regexp = regexp.MustCompile("^안즈쨩? 알려줘 (.+)")
	kawaii_re     *regexp.Regexp = regexp.MustCompile("^안즈쨩? 카와이")
	give_candy_re *regexp.Regexp = regexp.MustCompile("^안즈쨩? 사탕줄게")
)

type Anzu struct {
	*BaseBot
	rc RedisClient
}

func NewAnzu(token string, stop *chan struct{}, redisClient RedisClient) *Anzu {
	return &Anzu{NewBot(token, stop), redisClient}
}

func anzuMessageProcess(bot *Anzu, e *slack.MessageEvent) interface{} {
	force_accept := false
	switch {
	case e.Text == "사람은 일을 하고 살아야한다. 메우":
		return "이거 놔라 이 퇴근도 못하는 놈이"
	case e.Text == "안즈쨩 뭐해?":
		return "숨셔"
	default:
		if AcceptRE(e.Text, give_candy_re) {
			last := bot.rc.Get(fmt.Sprintf("%s_lastfail", e.User)).String()
			if last == "" {
				return ""
			}
			force_accept = true
			e.Text = last
			log.Printf("Retry : %s    %s", e.Text, last)
		}

		if matched, ok := MatchRE(e.Text, remember_re); ok {
			key, val := strings.TrimSpace(matched[1]), strings.TrimSpace(matched[2])
			var ret string
			switch {
			case key == "" || val == "":
				ret = "에...?"
			case AcceptRE(val, tell_re):
				ret = "에... 귀찮아..."
			case force_accept:
				ret = "응응 기억했어"
				fallthrough
			case rand.Float32() < 0.4:
				bot.rc.Set(key, val, 0)
				if len(ret) == 0 {
					ret = "에... 귀찮지만 기억했어"
				}
			default:
				ret = "귀찮아..."
				bot.rc.Set(fmt.Sprintf("%s_lastfail", e.User), e.Text, time.Duration(300*time.Second))
			}

			return ret
		} else if matched, ok := MatchRE(e.Text, tell_re); ok {
			key := strings.TrimSpace(matched[1])
			val := bot.rc.Get(key).Val()
			var ret string
			switch {
			case val == "":
				ret = "그런거 몰라"
			case force_accept:
				ret = fmt.Sprintf("%s 물어봤지?\n%s\n야", key, val)
			case rand.Float32() < 0.4:
				ret = val
			default:
				bot.rc.Set(fmt.Sprintf("%s_lastfail", e.User), e.Text, time.Duration(300*time.Second))
				ret = "Zzz..."
			}
			return ret
		} else if _, ok := MatchRE(e.Text, kawaii_re); ok {
			return "뭐... 뭐라는거야"
		}
	}
	return nil
}

func (bot *Anzu) onMessageEvent(e *slack.MessageEvent) {
	message := anzuMessageProcess(bot, e)
	switch message.(type) {
	case string:
		bot.sendSimple(e, message.(string))
	}
}
<commit_msg>멍청... String이 아니라 Val...<commit_after>package bot

import (
	"math/rand"
	"regexp"
	"strings"
	"time"

	"fmt"

	. "github.com/PoolC/slack_bot/util"
	slack "github.com/nlopes/slack"
)

var (
	remember_re   *regexp.Regexp = regexp.MustCompile("^안즈쨩? 기억해? ([^/]+)/(.+)")
	tell_re       *regexp.Regexp = regexp.MustCompile("^안즈쨩? 알려줘 (.+)")
	kawaii_re     *regexp.Regexp = regexp.MustCompile("^안즈쨩? 카와이")
	give_candy_re *regexp.Regexp = regexp.MustCompile("^안즈쨩? 사탕줄게")
)

type Anzu struct {
	*BaseBot
	rc RedisClient
}

func NewAnzu(token string, stop *chan struct{}, redisClient RedisClient) *Anzu {
	return &Anzu{NewBot(token, stop), redisClient}
}

func anzuMessageProcess(bot *Anzu, e *slack.MessageEvent) interface{} {
	force_accept := false
	switch {
	case e.Text == "사람은 일을 하고 살아야한다. 메우":
		return "이거 놔라 이 퇴근도 못하는 놈이"
	case e.Text == "안즈쨩 뭐해?":
		return "숨셔"
	default:
		if AcceptRE(e.Text, give_candy_re) {
			last := bot.rc.Get(fmt.Sprintf("%s_lastfail", e.User)).Val()
			if last == "" {
				return ""
			}
			force_accept = true
			e.Text = last
		}

		if matched, ok := MatchRE(e.Text, remember_re); ok {
			key, val := strings.TrimSpace(matched[1]), strings.TrimSpace(matched[2])
			var ret string
			switch {
			case key == "" || val == "":
				ret = "에...?"
			case AcceptRE(val, tell_re):
				ret = "에... 귀찮아..."
			case force_accept:
				ret = "응응 기억했어"
				fallthrough
			case rand.Float32() < 0.4:
				bot.rc.Set(key, val, 0)
				if len(ret) == 0 {
					ret = "에... 귀찮지만 기억했어"
				}
			default:
				ret = "귀찮아..."
				bot.rc.Set(fmt.Sprintf("%s_lastfail", e.User), e.Text, time.Duration(300*time.Second))
			}

			return ret
		} else if matched, ok := MatchRE(e.Text, tell_re); ok {
			key := strings.TrimSpace(matched[1])
			val := bot.rc.Get(key).Val()
			var ret string
			switch {
			case val == "":
				ret = "그런거 몰라"
			case force_accept:
				ret = fmt.Sprintf("%s 물어봤지?\n%s\n야", key, val)
			case rand.Float32() < 0.4:
				ret = val
			default:
				bot.rc.Set(fmt.Sprintf("%s_lastfail", e.User), e.Text, time.Duration(300*time.Second))
				ret = "Zzz..."
			}
			return ret
		} else if _, ok := MatchRE(e.Text, kawaii_re); ok {
			return "뭐... 뭐라는거야"
		}
	}
	return nil
}

func (bot *Anzu) onMessageEvent(e *slack.MessageEvent) {
	message := anzuMessageProcess(bot, e)
	switch message.(type) {
	case string:
		bot.sendSimple(e, message.(string))
	}
}
<|endoftext|>


<commit_before>package icat4json

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

//ErrAPI - API error icat for JSON
type ErrAPI string

func (e ErrAPI) Error() string {
	return "API error: " + string(e)
}

//Msg - result message from icat
type msg struct {
	Messageid string `json:"messageid"`
	Message   string `json:"message"`
}

func (m msg) String() string {
	return fmt.Sprintf("%s (%s)", m.Message, m.Messageid)
}

//Item - itemdata from icat
type Item struct {
	Title      string    `json:"item_title"`
	Link       string    `json:"item_link"`
	Date       time.Time `json:"item_date"`
	Identifier []string  `json:"item_identifier"`
}

//ICAT - data from icat
type ICAT struct {
	Itemdata []Item    `json:"itemdata"`
	Title    string    `json:"docTitle"`
	Fix      string    `json:"docTitleFix"`
	Link     string    `json:"docLink"`
	Date     time.Time `json:"docDate"`
}

//JSON - string with JSON format
type JSON string

func (j JSON) String() string {
	return string(j)
}

//getError returns status if error
func (j JSON) getError() error {
	if strings.Index(j.String(), "messageid") < 0 {
		return nil
	}
	var m msg
	if err := json.Unmarshal([]byte(j), &m); err != nil {
		return ErrAPI(err.Error()) //another format?
	}
	return ErrAPI(m.String())
}

//Decode returns icat data
func (j JSON) Decode() (*ICAT, error) {
	if strings.Index(j.String(), "itemdata") < 0 {
		return nil, ErrAPI("not icat data")
	}
	var i ICAT
	if err := json.Unmarshal([]byte(j), &i); err != nil {
		return nil, ErrAPI(err.Error())
	}
	return &i, nil
}

//Tool options for icat
const (
	ToolICATW = "icatw"
	ToolICATH = "icath"
	urlAPI    = "https://isec-myjvn-feed1.ipa.go.jp/IPARssReader.php"
)

//Get returns JSON string from icat
func Get(tool string) (JSON, error) {
	values := url.Values{
		"tool": {tool},
	}
	ux := strconv.FormatInt(time.Now().Unix(), 10)

	resp, err := http.Get(fmt.Sprintf("%s?%s&%s", urlAPI, ux, values.Encode()))
	if err != nil {
		return "", ErrAPI(err.Error())
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 400 {
		return "", ErrAPI(resp.Status)
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", ErrAPI(err.Error())
	}
	j := JSON(string(body))
	if err := j.getError(); err != nil {
		return j, err
	}
	return j, nil
}
<commit_msg>fix cast bug.<commit_after>package icat4json

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

//ErrAPI - API error icat for JSON
type ErrAPI string

func (e ErrAPI) Error() string {
	return "API error: " + string(e)
}

//Msg - result message from icat
type msg struct {
	Messageid string `json:"messageid"`
	Message   string `json:"message"`
}

func (m msg) String() string {
	return fmt.Sprintf("%s (%s)", m.Message, m.Messageid)
}

//Item - itemdata from icat
type Item struct {
	Title      string    `json:"item_title"`
	Link       string    `json:"item_link"`
	Date       time.Time `json:"item_date"`
	Identifier []string  `json:"item_identifier"`
}

//ICAT - data from icat
type ICAT struct {
	Itemdata []Item    `json:"itemdata"`
	Title    string    `json:"docTitle"`
	Fix      string    `json:"docTitleFix"`
	Link     string    `json:"docLink"`
	Date     time.Time `json:"docDate"`
}

//JSON - string with JSON format
type JSON string

func (j JSON) String() string {
	return string(j)
}

//getError returns status if error
func (j JSON) getError() error {
	if strings.Index(j.String(), "messageid") < 0 {
		return nil
	}
	var m msg
	if err := json.Unmarshal([]byte(j), &m); err != nil {
		return ErrAPI(err.Error()) //another format?
	}
	return ErrAPI(m.String())
}

//Decode returns icat data
func (j JSON) Decode() (*ICAT, error) {
	if strings.Index(j.String(), "itemdata") < 0 {
		return nil, ErrAPI("not icat data")
	}
	var i ICAT
	if err := json.Unmarshal([]byte(j), &i); err != nil {
		return nil, ErrAPI(err.Error())
	}
	return &i, nil
}

//Tool options for icat
const (
	ToolICATW = "icatw"
	ToolICATH = "icath"
	urlAPI    = "https://isec-myjvn-feed1.ipa.go.jp/IPARssReader.php"
)

//Get returns JSON string from icat
func Get(tool string) (JSON, error) {
	values := url.Values{
		"tool": {tool},
	}
	ux := strconv.FormatInt(time.Now().Unix(), 10)

	resp, err := http.Get(fmt.Sprintf("%s?%s&%s", urlAPI, ux, values.Encode()))
	if err != nil {
		return "", ErrAPI(err.Error())
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 400 {
		return "", ErrAPI(resp.Status)
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", ErrAPI(err.Error())
	}
	j := JSON(body)
	if err := j.getError(); err != nil {
		return j, err
	}
	return j, nil
}
<|endoftext|>


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

// Package io provides basic interfaces to I/O primitives.
// Its primary job is to wrap existing implementations of such primitives,
// such as those in package os, into shared public interfaces that
// abstract the functionality, plus some other related primitives.
package io

import "os"

// Error represents an unexpected I/O behavior.
type Error struct {
	ErrorString string
}

func (err *Error) String() string { return err.ErrorString }

// ErrShortWrite means that a write accepted fewer bytes than requested
// but failed to return an explicit error.
var ErrShortWrite os.Error = &Error{"short write"}

// ErrShortBuffer means that a read required a longer buffer than was provided.
var ErrShortBuffer os.Error = &Error{"short buffer"}

// ErrUnexpectedEOF means that os.EOF was encountered in the
// middle of reading a fixed-size block or data structure.
var ErrUnexpectedEOF os.Error = &Error{"unexpected EOF"}

// Reader is the interface that wraps the basic Read method.
//
// Read reads up to len(p) bytes into p.  It returns the number of bytes
// read (0 <= n <= len(p)) and any error encountered.
// Even if Read returns n < len(p),
// it may use all of p as scratch space during the call.
// If some data is available but not len(p) bytes, Read conventionally
// returns what is available rather than block waiting for more.
//
// At the end of the input stream, Read returns 0, os.EOF.
// Read may return a non-zero number of bytes with a non-nil err.
// In particular, a Read that exhausts the input may return n > 0, os.EOF.
type Reader interface {
	Read(p []byte) (n int, err os.Error)
}

// Writer is the interface that wraps the basic Write method.
//
// Write writes len(p) bytes from p to the underlying data stream.
// It returns the number of bytes written from p (0 <= n <= len(p))
// and any error encountered that caused the write to stop early.
// Write must return a non-nil error if it returns n < len(p).
type Writer interface {
	Write(p []byte) (n int, err os.Error)
}

// Closer is the interface that wraps the basic Close method.
type Closer interface {
	Close() os.Error
}

// Seeker is the interface that wraps the basic Seek method.
//
// Seek sets the offset for the next Read or Write to offset,
// interpreted according to whence: 0 means relative to the origin of
// the file, 1 means relative to the current offset, and 2 means
// relative to the end.  Seek returns the new offset and an Error, if
// any.
type Seeker interface {
	Seek(offset int64, whence int) (ret int64, err os.Error)
}

// ReadWriter is the interface that groups the basic Read and Write methods.
type ReadWriter interface {
	Reader
	Writer
}

// ReadCloser is the interface that groups the basic Read and Close methods.
type ReadCloser interface {
	Reader
	Closer
}

// WriteCloser is the interface that groups the basic Write and Close methods.
type WriteCloser interface {
	Writer
	Closer
}

// ReadWriteCloser is the interface that groups the basic Read, Write and Close methods.
type ReadWriteCloser interface {
	Reader
	Writer
	Closer
}

// ReadSeeker is the interface that groups the basic Read and Seek methods.
type ReadSeeker interface {
	Reader
	Seeker
}

// WriteSeeker is the interface that groups the basic Write and Seek methods.
type WriteSeeker interface {
	Writer
	Seeker
}

// ReadWriteSeeker is the interface that groups the basic Read, Write and Seek methods.
type ReadWriteSeeker interface {
	Reader
	Writer
	Seeker
}

// ReaderFrom is the interface that wraps the ReadFrom method.
type ReaderFrom interface {
	ReadFrom(r Reader) (n int64, err os.Error)
}

// WriterTo is the interface that wraps the WriteTo method.
type WriterTo interface {
	WriteTo(w Writer) (n int64, err os.Error)
}

// ReaderAt is the interface that wraps the basic ReadAt method.
//
// ReadAt reads len(p) bytes into p starting at offset off in the
// underlying data stream.  It returns the number of bytes
// read (0 <= n <= len(p)) and any error encountered.
//
// Even if ReadAt returns n < len(p),
// it may use all of p as scratch space during the call.
// If some data is available but not len(p) bytes, ReadAt blocks
// until either all the data is available or an error occurs.
//
// At the end of the input stream, ReadAt returns 0, os.EOF.
// ReadAt may return a non-zero number of bytes with a non-nil err.
// In particular, a ReadAt that exhausts the input may return n > 0, os.EOF.
//
// If ReadAt is reading from an data stream with a seek offset,
// ReadAt should not affect nor be affected by the underlying
// seek offset.
type ReaderAt interface {
	ReadAt(p []byte, off int64) (n int, err os.Error)
}

// WriterAt is the interface that wraps the basic WriteAt method.
//
// WriteAt writes len(p) bytes from p to the underlying data stream
// at offset off.  It returns the number of bytes written from p (0 <= n <= len(p))
// and any error encountered that caused the write to stop early.
// WriteAt must return a non-nil error if it returns n < len(p).
type WriterAt interface {
	WriteAt(p []byte, off int64) (n int, err os.Error)
}

// ByteReader is the interface that wraps the ReadByte method.
//
// ReadByte reads and returns the next byte from the input.
// If no byte is available, err will be set.
type ByteReader interface {
	ReadByte() (c byte, err os.Error)
}

// ByteScanner is the interface that adds the UnreadByte method to the
// basic ReadByte method.
//
// UnreadByte causes the next call to ReadByte to return the same byte
// as the previous call to ReadByte.
// It may be an error to call UnreadByte twice without an intervening
// call to ReadByte.
type ByteScanner interface {
	ByteReader
	UnreadByte() os.Error
}

// RuneReader is the interface that wraps the ReadRune method.
//
// ReadRune reads a single UTF-8 encoded Unicode character
// and returns the rune and its size in bytes. If no character is
// available, err will be set.
type RuneReader interface {
	ReadRune() (rune int, size int, err os.Error)
}

// RuneScanner is the interface that adds the UnreadRune method to the
// basic ReadRune method.
//
// UnreadRune causes the next call to ReadRune to return the same rune
// as the previous call to ReadRune.
// It may be an error to call UnreadRune twice without an intervening
// call to ReadRune.
type RuneScanner interface {
	RuneReader
	UnreadRune() os.Error
}

// WriteString writes the contents of the string s to w, which accepts an array of bytes.
func WriteString(w Writer, s string) (n int, err os.Error) {
	return w.Write([]byte(s))
}

// ReadAtLeast reads from r into buf until it has read at least min bytes.
// It returns the number of bytes copied and an error if fewer bytes were read.
// The error is os.EOF only if no bytes were read.
// If an EOF happens after reading fewer than min bytes,
// ReadAtLeast returns ErrUnexpectedEOF.
// If min is greater than the length of buf, ReadAtLeast returns ErrShortBuffer.
func ReadAtLeast(r Reader, buf []byte, min int) (n int, err os.Error) {
	if len(buf) < min {
		return 0, ErrShortBuffer
	}
	for n < min && err == nil {
		var nn int
		nn, err = r.Read(buf[n:])
		n += nn
	}
	if err == os.EOF {
		if n >= min {
			err = nil
		} else if n > 0 {
			err = ErrUnexpectedEOF
		}
	}
	return
}

// ReadFull reads exactly len(buf) bytes from r into buf.
// It returns the number of bytes copied and an error if fewer bytes were read.
// The error is os.EOF only if no bytes were read.
// If an EOF happens after reading some but not all the bytes,
// ReadFull returns ErrUnexpectedEOF.
func ReadFull(r Reader, buf []byte) (n int, err os.Error) {
	return ReadAtLeast(r, buf, len(buf))
}

// Copyn copies n bytes (or until an error) from src to dst.
// It returns the number of bytes copied and the error, if any.
//
// If dst implements the ReaderFrom interface,
// the copy is implemented by calling dst.ReadFrom(src).
func Copyn(dst Writer, src Reader, n int64) (written int64, err os.Error) {
	// If the writer has a ReadFrom method, use it to do the copy.
	// Avoids a buffer allocation and a copy.
	if rt, ok := dst.(ReaderFrom); ok {
		written, err = rt.ReadFrom(LimitReader(src, n))
		if written < n && err == nil {
			// rt stopped early; must have been EOF.
			err = os.EOF
		}
		return
	}
	buf := make([]byte, 32*1024)
	for written < n {
		l := len(buf)
		if d := n - written; d < int64(l) {
			l = int(d)
		}
		nr, er := src.Read(buf[0:l])
		if nr > 0 {
			nw, ew := dst.Write(buf[0:nr])
			if nw > 0 {
				written += int64(nw)
			}
			if ew != nil {
				err = ew
				break
			}
			if nr != nw {
				err = ErrShortWrite
				break
			}
		}
		if er != nil {
			err = er
			break
		}
	}
	return written, err
}

// Copy copies from src to dst until either EOF is reached
// on src or an error occurs.  It returns the number of bytes
// copied and the error, if any.
//
// If dst implements the ReaderFrom interface,
// the copy is implemented by calling dst.ReadFrom(src).
// Otherwise, if src implements the WriterTo interface,
// the copy is implemented by calling src.WriteTo(dst).
func Copy(dst Writer, src Reader) (written int64, err os.Error) {
	// If the writer has a ReadFrom method, use it to do the copy.
	// Avoids an allocation and a copy.
	if rt, ok := dst.(ReaderFrom); ok {
		return rt.ReadFrom(src)
	}
	// Similarly, if the reader has a WriteTo method, use it to do the copy.
	if wt, ok := src.(WriterTo); ok {
		return wt.WriteTo(dst)
	}
	buf := make([]byte, 32*1024)
	for {
		nr, er := src.Read(buf)
		if nr > 0 {
			nw, ew := dst.Write(buf[0:nr])
			if nw > 0 {
				written += int64(nw)
			}
			if ew != nil {
				err = ew
				break
			}
			if nr != nw {
				err = ErrShortWrite
				break
			}
		}
		if er == os.EOF {
			break
		}
		if er != nil {
			err = er
			break
		}
	}
	return written, err
}

// LimitReader returns a Reader that reads from r
// but stops with os.EOF after n bytes.
// The underlying implementation is a *LimitedReader.
func LimitReader(r Reader, n int64) Reader { return &LimitedReader{r, n} }

// A LimitedReader reads from R but limits the amount of
// data returned to just N bytes. Each call to Read
// updates N to reflect the new amount remaining.
type LimitedReader struct {
	R Reader // underlying reader
	N int64  // max bytes remaining
}

func (l *LimitedReader) Read(p []byte) (n int, err os.Error) {
	if l.N <= 0 {
		return 0, os.EOF
	}
	if int64(len(p)) > l.N {
		p = p[0:l.N]
	}
	n, err = l.R.Read(p)
	l.N -= int64(n)
	return
}

// NewSectionReader returns a SectionReader that reads from r
// starting at offset off and stops with os.EOF after n bytes.
func NewSectionReader(r ReaderAt, off int64, n int64) *SectionReader {
	return &SectionReader{r, off, off, off + n}
}

// SectionReader implements Read, Seek, and ReadAt on a section
// of an underlying ReaderAt.
type SectionReader struct {
	r     ReaderAt
	base  int64
	off   int64
	limit int64
}

func (s *SectionReader) Read(p []byte) (n int, err os.Error) {
	if s.off >= s.limit {
		return 0, os.EOF
	}
	if max := s.limit - s.off; int64(len(p)) > max {
		p = p[0:max]
	}
	n, err = s.r.ReadAt(p, s.off)
	s.off += int64(n)
	return
}

func (s *SectionReader) Seek(offset int64, whence int) (ret int64, err os.Error) {
	switch whence {
	default:
		return 0, os.EINVAL
	case 0:
		offset += s.base
	case 1:
		offset += s.off
	case 2:
		offset += s.limit
	}
	if offset < s.base || offset > s.limit {
		return 0, os.EINVAL
	}
	s.off = offset
	return offset - s.base, nil
}

func (s *SectionReader) ReadAt(p []byte, off int64) (n int, err os.Error) {
	if off < 0 || off >= s.limit-s.base {
		return 0, os.EOF
	}
	off += s.base
	if max := s.limit - off; int64(len(p)) > max {
		p = p[0:max]
	}
	return s.r.ReadAt(p, off)
}

// Size returns the size of the section in bytes.
func (s *SectionReader) Size() int64 { return s.limit - s.base }
<commit_msg>io: clarify Read, ReadAt, Copy, Copyn EOF behavior<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package io provides basic interfaces to I/O primitives.
// Its primary job is to wrap existing implementations of such primitives,
// such as those in package os, into shared public interfaces that
// abstract the functionality, plus some other related primitives.
package io

import "os"

// Error represents an unexpected I/O behavior.
type Error struct {
	ErrorString string
}

func (err *Error) String() string { return err.ErrorString }

// ErrShortWrite means that a write accepted fewer bytes than requested
// but failed to return an explicit error.
var ErrShortWrite os.Error = &Error{"short write"}

// ErrShortBuffer means that a read required a longer buffer than was provided.
var ErrShortBuffer os.Error = &Error{"short buffer"}

// ErrUnexpectedEOF means that os.EOF was encountered in the
// middle of reading a fixed-size block or data structure.
var ErrUnexpectedEOF os.Error = &Error{"unexpected EOF"}

// Reader is the interface that wraps the basic Read method.
//
// Read reads up to len(p) bytes into p.  It returns the number of bytes
// read (0 <= n <= len(p)) and any error encountered.  Even if Read
// returns n < len(p), it may use all of p as scratch space during the call.
// If some data is available but not len(p) bytes, Read conventionally
// returns what is available instead of waiting for more.
//
// When Read encounters an error or end-of-file condition after
// successfully reading n > 0 bytes, it returns the number of
// bytes read.  It may return the (non-nil) error from the same call
// or return the error (and n == 0) from a subsequent call.
// An instance of this general case is that a Reader returning
// a non-zero number of bytes at the end of the input stream may
// return either err == os.EOF or err == nil.  The next Read should
// return 0, os.EOF regardless.
//
// Callers should always process the n > 0 bytes returned before
// considering the error err.  Doing so correctly handles I/O errors
// that happen after reading some bytes and also both of the
// allowed EOF behaviors.
type Reader interface {
	Read(p []byte) (n int, err os.Error)
}

// Writer is the interface that wraps the basic Write method.
//
// Write writes len(p) bytes from p to the underlying data stream.
// It returns the number of bytes written from p (0 <= n <= len(p))
// and any error encountered that caused the write to stop early.
// Write must return a non-nil error if it returns n < len(p).
type Writer interface {
	Write(p []byte) (n int, err os.Error)
}

// Closer is the interface that wraps the basic Close method.
type Closer interface {
	Close() os.Error
}

// Seeker is the interface that wraps the basic Seek method.
//
// Seek sets the offset for the next Read or Write to offset,
// interpreted according to whence: 0 means relative to the origin of
// the file, 1 means relative to the current offset, and 2 means
// relative to the end.  Seek returns the new offset and an Error, if
// any.
type Seeker interface {
	Seek(offset int64, whence int) (ret int64, err os.Error)
}

// ReadWriter is the interface that groups the basic Read and Write methods.
type ReadWriter interface {
	Reader
	Writer
}

// ReadCloser is the interface that groups the basic Read and Close methods.
type ReadCloser interface {
	Reader
	Closer
}

// WriteCloser is the interface that groups the basic Write and Close methods.
type WriteCloser interface {
	Writer
	Closer
}

// ReadWriteCloser is the interface that groups the basic Read, Write and Close methods.
type ReadWriteCloser interface {
	Reader
	Writer
	Closer
}

// ReadSeeker is the interface that groups the basic Read and Seek methods.
type ReadSeeker interface {
	Reader
	Seeker
}

// WriteSeeker is the interface that groups the basic Write and Seek methods.
type WriteSeeker interface {
	Writer
	Seeker
}

// ReadWriteSeeker is the interface that groups the basic Read, Write and Seek methods.
type ReadWriteSeeker interface {
	Reader
	Writer
	Seeker
}

// ReaderFrom is the interface that wraps the ReadFrom method.
type ReaderFrom interface {
	ReadFrom(r Reader) (n int64, err os.Error)
}

// WriterTo is the interface that wraps the WriteTo method.
type WriterTo interface {
	WriteTo(w Writer) (n int64, err os.Error)
}

// ReaderAt is the interface that wraps the basic ReadAt method.
//
// ReadAt reads len(p) bytes into p starting at offset off in the
// underlying input source.  It returns the number of bytes
// read (0 <= n <= len(p)) and any error encountered.
//
// When ReadAt returns n < len(p), it returns a non-nil error
// explaining why more bytes were not returned.  In this respect,
// ReadAt is stricter than Read.
//
// Even if ReadAt returns n < len(p), it may use all of p as scratch
// space during the call.  If some data is available but not len(p) bytes,
// ReadAt blocks until either all the data is available or an error occurs.
// In this respect ReadAt is different from Read.
//
// If the n = len(p) bytes returned by ReadAt are at the end of the
// input source, ReadAt may return either err == os.EOF or err == nil.
//
// If ReadAt is reading from an input source with a seek offset,
// ReadAt should not affect nor be affected by the underlying
// seek offset.
type ReaderAt interface {
	ReadAt(p []byte, off int64) (n int, err os.Error)
}

// WriterAt is the interface that wraps the basic WriteAt method.
//
// WriteAt writes len(p) bytes from p to the underlying data stream
// at offset off.  It returns the number of bytes written from p (0 <= n <= len(p))
// and any error encountered that caused the write to stop early.
// WriteAt must return a non-nil error if it returns n < len(p).
type WriterAt interface {
	WriteAt(p []byte, off int64) (n int, err os.Error)
}

// ByteReader is the interface that wraps the ReadByte method.
//
// ReadByte reads and returns the next byte from the input.
// If no byte is available, err will be set.
type ByteReader interface {
	ReadByte() (c byte, err os.Error)
}

// ByteScanner is the interface that adds the UnreadByte method to the
// basic ReadByte method.
//
// UnreadByte causes the next call to ReadByte to return the same byte
// as the previous call to ReadByte.
// It may be an error to call UnreadByte twice without an intervening
// call to ReadByte.
type ByteScanner interface {
	ByteReader
	UnreadByte() os.Error
}

// RuneReader is the interface that wraps the ReadRune method.
//
// ReadRune reads a single UTF-8 encoded Unicode character
// and returns the rune and its size in bytes. If no character is
// available, err will be set.
type RuneReader interface {
	ReadRune() (rune int, size int, err os.Error)
}

// RuneScanner is the interface that adds the UnreadRune method to the
// basic ReadRune method.
//
// UnreadRune causes the next call to ReadRune to return the same rune
// as the previous call to ReadRune.
// It may be an error to call UnreadRune twice without an intervening
// call to ReadRune.
type RuneScanner interface {
	RuneReader
	UnreadRune() os.Error
}

// WriteString writes the contents of the string s to w, which accepts an array of bytes.
func WriteString(w Writer, s string) (n int, err os.Error) {
	return w.Write([]byte(s))
}

// ReadAtLeast reads from r into buf until it has read at least min bytes.
// It returns the number of bytes copied and an error if fewer bytes were read.
// The error is os.EOF only if no bytes were read.
// If an EOF happens after reading fewer than min bytes,
// ReadAtLeast returns ErrUnexpectedEOF.
// If min is greater than the length of buf, ReadAtLeast returns ErrShortBuffer.
func ReadAtLeast(r Reader, buf []byte, min int) (n int, err os.Error) {
	if len(buf) < min {
		return 0, ErrShortBuffer
	}
	for n < min && err == nil {
		var nn int
		nn, err = r.Read(buf[n:])
		n += nn
	}
	if err == os.EOF {
		if n >= min {
			err = nil
		} else if n > 0 {
			err = ErrUnexpectedEOF
		}
	}
	return
}

// ReadFull reads exactly len(buf) bytes from r into buf.
// It returns the number of bytes copied and an error if fewer bytes were read.
// The error is os.EOF only if no bytes were read.
// If an EOF happens after reading some but not all the bytes,
// ReadFull returns ErrUnexpectedEOF.
func ReadFull(r Reader, buf []byte) (n int, err os.Error) {
	return ReadAtLeast(r, buf, len(buf))
}

// Copyn copies n bytes (or until an error) from src to dst.
// It returns the number of bytes copied and the earliest
// error encountered while copying.  Because Read can
// return the full amount requested as well as an error
// (including os.EOF), so can Copyn.
//
// If dst implements the ReaderFrom interface,
// the copy is implemented by calling dst.ReadFrom(src).
func Copyn(dst Writer, src Reader, n int64) (written int64, err os.Error) {
	// If the writer has a ReadFrom method, use it to do the copy.
	// Avoids a buffer allocation and a copy.
	if rt, ok := dst.(ReaderFrom); ok {
		written, err = rt.ReadFrom(LimitReader(src, n))
		if written < n && err == nil {
			// rt stopped early; must have been EOF.
			err = os.EOF
		}
		return
	}
	buf := make([]byte, 32*1024)
	for written < n {
		l := len(buf)
		if d := n - written; d < int64(l) {
			l = int(d)
		}
		nr, er := src.Read(buf[0:l])
		if nr > 0 {
			nw, ew := dst.Write(buf[0:nr])
			if nw > 0 {
				written += int64(nw)
			}
			if ew != nil {
				err = ew
				break
			}
			if nr != nw {
				err = ErrShortWrite
				break
			}
		}
		if er != nil {
			err = er
			break
		}
	}
	return written, err
}

// Copy copies from src to dst until either EOF is reached
// on src or an error occurs.  It returns the number of bytes
// copied and the first error encountered while copying, if any.
//
// A successful Copy returns err == nil, not err == os.EOF.
// Because Copy is defined to read from src until EOF, it does
// not treat an EOF from Read as an error to be reported.
//
// If dst implements the ReaderFrom interface,
// the copy is implemented by calling dst.ReadFrom(src).
// Otherwise, if src implements the WriterTo interface,
// the copy is implemented by calling src.WriteTo(dst).
func Copy(dst Writer, src Reader) (written int64, err os.Error) {
	// If the writer has a ReadFrom method, use it to do the copy.
	// Avoids an allocation and a copy.
	if rt, ok := dst.(ReaderFrom); ok {
		return rt.ReadFrom(src)
	}
	// Similarly, if the reader has a WriteTo method, use it to do the copy.
	if wt, ok := src.(WriterTo); ok {
		return wt.WriteTo(dst)
	}
	buf := make([]byte, 32*1024)
	for {
		nr, er := src.Read(buf)
		if nr > 0 {
			nw, ew := dst.Write(buf[0:nr])
			if nw > 0 {
				written += int64(nw)
			}
			if ew != nil {
				err = ew
				break
			}
			if nr != nw {
				err = ErrShortWrite
				break
			}
		}
		if er == os.EOF {
			break
		}
		if er != nil {
			err = er
			break
		}
	}
	return written, err
}

// LimitReader returns a Reader that reads from r
// but stops with os.EOF after n bytes.
// The underlying implementation is a *LimitedReader.
func LimitReader(r Reader, n int64) Reader { return &LimitedReader{r, n} }

// A LimitedReader reads from R but limits the amount of
// data returned to just N bytes. Each call to Read
// updates N to reflect the new amount remaining.
type LimitedReader struct {
	R Reader // underlying reader
	N int64  // max bytes remaining
}

func (l *LimitedReader) Read(p []byte) (n int, err os.Error) {
	if l.N <= 0 {
		return 0, os.EOF
	}
	if int64(len(p)) > l.N {
		p = p[0:l.N]
	}
	n, err = l.R.Read(p)
	l.N -= int64(n)
	return
}

// NewSectionReader returns a SectionReader that reads from r
// starting at offset off and stops with os.EOF after n bytes.
func NewSectionReader(r ReaderAt, off int64, n int64) *SectionReader {
	return &SectionReader{r, off, off, off + n}
}

// SectionReader implements Read, Seek, and ReadAt on a section
// of an underlying ReaderAt.
type SectionReader struct {
	r     ReaderAt
	base  int64
	off   int64
	limit int64
}

func (s *SectionReader) Read(p []byte) (n int, err os.Error) {
	if s.off >= s.limit {
		return 0, os.EOF
	}
	if max := s.limit - s.off; int64(len(p)) > max {
		p = p[0:max]
	}
	n, err = s.r.ReadAt(p, s.off)
	s.off += int64(n)
	return
}

func (s *SectionReader) Seek(offset int64, whence int) (ret int64, err os.Error) {
	switch whence {
	default:
		return 0, os.EINVAL
	case 0:
		offset += s.base
	case 1:
		offset += s.off
	case 2:
		offset += s.limit
	}
	if offset < s.base || offset > s.limit {
		return 0, os.EINVAL
	}
	s.off = offset
	return offset - s.base, nil
}

func (s *SectionReader) ReadAt(p []byte, off int64) (n int, err os.Error) {
	if off < 0 || off >= s.limit-s.base {
		return 0, os.EOF
	}
	off += s.base
	if max := s.limit - off; int64(len(p)) > max {
		p = p[0:max]
	}
	return s.r.ReadAt(p, off)
}

// Size returns the size of the section in bytes.
func (s *SectionReader) Size() int64 { return s.limit - s.base }
<|endoftext|>


<commit_before>package eth

import (
	"context"
	"errors"
	"math/big"
	"sync"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

type stubGasPriceOracle struct {
	mu       sync.Mutex
	gasPrice *big.Int
	queries  int
	err      error
}

func newStubGasPriceOracle(gasPrice *big.Int) *stubGasPriceOracle {
	return &stubGasPriceOracle{gasPrice: gasPrice}
}

func (s *stubGasPriceOracle) SetGasPrice(gasPrice *big.Int) {
	s.mu.Lock()
	defer s.mu.Unlock()

	s.gasPrice = gasPrice
}

func (s *stubGasPriceOracle) SetErr(err error) {
	s.err = err
}

func (s *stubGasPriceOracle) Queries() int {
	s.mu.Lock()
	defer s.mu.Unlock()

	return s.queries
}

func (s *stubGasPriceOracle) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
	s.mu.Lock()
	defer s.mu.Unlock()

	if s.err != nil {
		return nil, s.err
	}

	s.queries++

	return s.gasPrice, nil
}

func TestStart(t *testing.T) {
	gasPrice := big.NewInt(777)
	gpo := newStubGasPriceOracle(gasPrice)

	gpm := NewGasPriceMonitor(gpo, 1*time.Hour)

	assert := assert.New(t)

	// Test error from first attempt to fetch gas price

	expErr := errors.New("SuggestGasPrice error")
	gpo.SetErr(expErr)
	update, err := gpm.Start(context.Background())
	assert.Nil(update)
	assert.EqualError(err, expErr.Error())

	// Switch back to no errors for SuggestGasPrice
	gpo.SetErr(nil)

	// Test success

	update, err = gpm.Start(context.Background())
	assert.NotNil(update)
	assert.Nil(err)
	defer gpm.Stop()

	assert.Equal(gasPrice, gpm.GasPrice())

	// Test error when already polling

	update, err = gpm.Start(context.Background())
	assert.Nil(update)
	assert.EqualError(err, "already polling")
}

func TestStart_Polling(t *testing.T) {
	gasPrice1 := big.NewInt(777)
	gasPrice2 := big.NewInt(555)
	gasPrice3 := big.NewInt(888)
	gpo := newStubGasPriceOracle(gasPrice1)

	pollingInterval := 1 * time.Second
	gpm := NewGasPriceMonitor(gpo, pollingInterval)

	assert := assert.New(t)

	update, err := gpm.Start(context.Background())
	require.NotNil(t, update)
	require.Nil(t, err)
	defer gpm.Stop()

	var changes int
	var changesMu sync.Mutex

	getChanges := func() int {
		changesMu.Lock()
		defer changesMu.Unlock()

		return changes
	}

	addChange := func() {
		changesMu.Lock()
		defer changesMu.Unlock()

		changes++
	}

	go func() {
		count := 0

		for count < 2 {
			select {
			case <-update:
				count++

				addChange()
			}
		}
	}()

	// Async update gas price so when the
	// sync sleep finishes, the monitor
	// should have decreased its own gas price
	go func() {
		time.Sleep(1 * pollingInterval)
		gpo.SetGasPrice(gasPrice2)
	}()

	time.Sleep(2 * pollingInterval)

	assert.Greater(gpo.Queries(), 0)
	assert.Equal(gasPrice2, gpm.GasPrice())

	queries := gpo.Queries()

	// Async update gas price so when the
	// sync sleep finishes, the monitor
	// should have increased its own gas price
	go func() {
		time.Sleep(1 * pollingInterval)
		gpo.SetGasPrice(gasPrice3)
	}()

	time.Sleep(2 * pollingInterval)

	// There should be more queries now
	assert.Greater(gpo.Queries(), queries)
	assert.Equal(gasPrice3, gpm.GasPrice())

	assert.Equal(2, getChanges())
}

func TestStart_Polling_ContextCancel(t *testing.T) {
	gasPrice1 := big.NewInt(777)
	gpo := newStubGasPriceOracle(gasPrice1)

	pollingInterval := 1 * time.Second
	gpm := NewGasPriceMonitor(gpo, pollingInterval)

	ctx, cancel := context.WithCancel(context.Background())
	update, err := gpm.Start(ctx)
	require.NotNil(t, update)
	require.Nil(t, err)

	queries := gpo.queries

	// Cancel polling loop
	cancel()

	time.Sleep(1 * time.Second)

	// Ensure there are no more queries
	assert.Equal(t, gpo.queries, queries)
}

func TestStop(t *testing.T) {
	gasPrice := big.NewInt(777)
	gpo := newStubGasPriceOracle(gasPrice)
	gpo.SetGasPrice(gasPrice)

	gpm := NewGasPriceMonitor(gpo, 1*time.Hour)

	assert := assert.New(t)

	// Test error when not polling

	err := gpm.Stop()
	assert.EqualError(err, "not polling")

	// Test success

	update, err := gpm.Start(context.Background())
	require.NotNil(t, update)
	require.Nil(t, err)

	queries := gpo.queries

	err = gpm.Stop()
	assert.Nil(err)

	time.Sleep(1 * time.Second)

	// Ensure there are no more queries
	assert.Equal(queries, gpo.queries)

	// check gasPriceUpdate channel is closed
	_, ok := (<-gpm.update)
	assert.False(ok)
}
<commit_msg>eth: Adjust sleep time to fix flaky gaspricemonitor test<commit_after>package eth

import (
	"context"
	"errors"
	"math/big"
	"sync"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

type stubGasPriceOracle struct {
	mu       sync.Mutex
	gasPrice *big.Int
	queries  int
	err      error
}

func newStubGasPriceOracle(gasPrice *big.Int) *stubGasPriceOracle {
	return &stubGasPriceOracle{gasPrice: gasPrice}
}

func (s *stubGasPriceOracle) SetGasPrice(gasPrice *big.Int) {
	s.mu.Lock()
	defer s.mu.Unlock()

	s.gasPrice = gasPrice
}

func (s *stubGasPriceOracle) SetErr(err error) {
	s.err = err
}

func (s *stubGasPriceOracle) Queries() int {
	s.mu.Lock()
	defer s.mu.Unlock()

	return s.queries
}

func (s *stubGasPriceOracle) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
	s.mu.Lock()
	defer s.mu.Unlock()

	if s.err != nil {
		return nil, s.err
	}

	s.queries++

	return s.gasPrice, nil
}

func TestStart(t *testing.T) {
	gasPrice := big.NewInt(777)
	gpo := newStubGasPriceOracle(gasPrice)

	gpm := NewGasPriceMonitor(gpo, 1*time.Hour)

	assert := assert.New(t)

	// Test error from first attempt to fetch gas price

	expErr := errors.New("SuggestGasPrice error")
	gpo.SetErr(expErr)
	update, err := gpm.Start(context.Background())
	assert.Nil(update)
	assert.EqualError(err, expErr.Error())

	// Switch back to no errors for SuggestGasPrice
	gpo.SetErr(nil)

	// Test success

	update, err = gpm.Start(context.Background())
	assert.NotNil(update)
	assert.Nil(err)
	defer gpm.Stop()

	assert.Equal(gasPrice, gpm.GasPrice())

	// Test error when already polling

	update, err = gpm.Start(context.Background())
	assert.Nil(update)
	assert.EqualError(err, "already polling")
}

func TestStart_Polling(t *testing.T) {
	gasPrice1 := big.NewInt(777)
	gasPrice2 := big.NewInt(555)
	gasPrice3 := big.NewInt(888)
	gpo := newStubGasPriceOracle(gasPrice1)

	pollingInterval := 1 * time.Millisecond
	gpm := NewGasPriceMonitor(gpo, pollingInterval)

	assert := assert.New(t)

	update, err := gpm.Start(context.Background())
	require.NotNil(t, update)
	require.Nil(t, err)
	defer gpm.Stop()

	var changes int
	var changesMu sync.Mutex

	getChanges := func() int {
		changesMu.Lock()
		defer changesMu.Unlock()

		return changes
	}

	addChange := func() {
		changesMu.Lock()
		defer changesMu.Unlock()

		changes++
	}

	go func() {
		count := 0

		for count < 2 {
			select {
			case <-update:
				count++

				addChange()
			}
		}
	}()

	// Async update gas price so when the
	// sync sleep finishes, the monitor
	// should have decreased its own gas price
	go func() {
		gpo.SetGasPrice(gasPrice2)
	}()

	time.Sleep(100 * time.Millisecond)

	assert.Greater(gpo.Queries(), 0)
	assert.Equal(gasPrice2, gpm.GasPrice())

	queries := gpo.Queries()

	// Async update gas price so when the
	// sync sleep finishes, the monitor
	// should have increased its own gas price
	go func() {
		gpo.SetGasPrice(gasPrice3)
	}()

	time.Sleep(100 * time.Millisecond)

	// There should be more queries now
	assert.Greater(gpo.Queries(), queries)
	assert.Equal(gasPrice3, gpm.GasPrice())

	assert.Equal(2, getChanges())
}

func TestStart_Polling_ContextCancel(t *testing.T) {
	gasPrice1 := big.NewInt(777)
	gpo := newStubGasPriceOracle(gasPrice1)

	pollingInterval := 1 * time.Second
	gpm := NewGasPriceMonitor(gpo, pollingInterval)

	ctx, cancel := context.WithCancel(context.Background())
	update, err := gpm.Start(ctx)
	require.NotNil(t, update)
	require.Nil(t, err)

	queries := gpo.queries

	// Cancel polling loop
	cancel()

	time.Sleep(100 * time.Millisecond)

	// Ensure there are no more queries
	assert.Equal(t, gpo.queries, queries)
}

func TestStop(t *testing.T) {
	gasPrice := big.NewInt(777)
	gpo := newStubGasPriceOracle(gasPrice)
	gpo.SetGasPrice(gasPrice)

	gpm := NewGasPriceMonitor(gpo, 1*time.Hour)

	assert := assert.New(t)

	// Test error when not polling

	err := gpm.Stop()
	assert.EqualError(err, "not polling")

	// Test success

	update, err := gpm.Start(context.Background())
	require.NotNil(t, update)
	require.Nil(t, err)

	queries := gpo.queries

	err = gpm.Stop()
	assert.Nil(err)

	time.Sleep(100 * time.Millisecond)

	// Ensure there are no more queries
	assert.Equal(queries, gpo.queries)

	// check gasPriceUpdate channel is closed
	_, ok := (<-gpm.update)
	assert.False(ok)
}
<|endoftext|>


<commit_before>package algoliasearch

import "errors"

type IndexIterator struct {
	cursor string
	index  *Index
	page   BrowseRes
	params map[string]interface{}
	pos    int
}

func NewIndexIterator(index *Index, params map[string]interface{}) (it IndexIterator, err error) {
	it = IndexIterator{
		index:  index,
		params: duplicateMap(params),
		pos:    0,
	}
	err = it.loadNextPage()
	return
}

func (it *IndexIterator) Next() (res map[string]interface{}, err error) {
	// Abort if the user call `Next()` on a IndexIterator that has been
	// initialized without being able to load the first page.
	if len(it.page.Hits) == 0 {
		err = errors.New("No more hits")
		return
	}

	// If the last element of the page has been reached, the next one is loaded
	if it.pos == len(it.page.Hits) {
		if it.cursor == "" {
			err = errors.New("No more hits")
		} else {
			err = it.loadNextPage()
		}

		if err != nil {
			return
		}
	}

	res = it.page.Hits[it.pos]
	it.pos++

	return
}

func (it *IndexIterator) loadNextPage() (err error) {
	// Update the cursor for each new page except for the first one
	if it.cursor != "" {
		it.params["cursor"] = it.cursor
	}

	if it.page, err = it.index.Browse(it.params); err != nil {
		return
	}

	// Return an error if the newly loaded pages contains no results
	if len(it.page.Hits) == 0 {
		err = errors.New("No more hits")
		return
	}

	it.cursor = it.page.Cursor
	it.pos = 0
	return
}
<commit_msg>Add and improve comments of the `IndexIterator`<commit_after>package algoliasearch

import "errors"

// IndexIterator is used by the BrowseAll functions to iterate over all the
// records of an index (or a subset according to what the query was).
type IndexIterator struct {
	cursor string
	index  *Index
	page   BrowseRes
	params map[string]interface{}
	pos    int
}

// NewIndexIterator instantiates a IndexIterator on the `index` and according
// to the given `params`. It is also trying to load the first page of results
// and return an error if something goes wrong.
func NewIndexIterator(index *Index, params map[string]interface{}) (it IndexIterator, err error) {
	it = IndexIterator{
		index:  index,
		params: duplicateMap(params),
		pos:    0,
	}
	err = it.loadNextPage()
	return
}

// Next returns the next record each time is is called. Subsequent pages of
// results are automatically loaded and an error is returned if a problem
// arises. When the last element has been reached, an error is returned with
// the following message: "No more hits".
func (it *IndexIterator) Next() (res map[string]interface{}, err error) {
	// Abort if the user call `Next()` on a IndexIterator that has been
	// initialized without being able to load the first page.
	if len(it.page.Hits) == 0 {
		err = errors.New("No more hits")
		return
	}

	// If the last element of the page has been reached, the next one is loaded
	// or returned an error if the last element of the last page has already
	// been returned.
	if it.pos == len(it.page.Hits) {
		if it.cursor == "" {
			err = errors.New("No more hits")
		} else {
			err = it.loadNextPage()
		}

		if err != nil {
			return
		}
	}

	res = it.page.Hits[it.pos]
	it.pos++

	return
}

// loadNextPage is used internally to load the next page of results, using the
// underlying Browse cursor.
func (it *IndexIterator) loadNextPage() (err error) {
	// Update the cursor for each new page except for the first one
	if it.cursor != "" {
		it.params["cursor"] = it.cursor
	}

	if it.page, err = it.index.Browse(it.params); err != nil {
		return
	}

	// Return an error if the newly loaded pages contains no results
	if len(it.page.Hits) == 0 {
		err = errors.New("No more hits")
		return
	}

	it.cursor = it.page.Cursor
	it.pos = 0
	return
}
<|endoftext|>


<commit_before>package apps

import (
	"github.com/vito/cmdtest"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/vito/cmdtest/matchers"

	. "github.com/pivotal-cf-experimental/cf-acceptance-tests/helpers"
)

var _ = Describe("A running application", func() {
	BeforeEach(func() {
		AppName = RandomName()

		Expect(
			Cf("push", AppName, "-p", doraPath, "-i", "2"),
		).To(Say("Started"))
	})

	AfterEach(func() {
		Expect(Cf("delete", AppName, "-f")).To(Say("OK"))
	})

	It("can be queried for state by instance", func() {
		app := Cf("app", AppName)
		Expect(app).To(Say("#0"))
		Expect(app).To(Say("#1"))
	})

	It("can have its files inspected", func() {
		Expect(Cf("files", AppName)).To(Say("app/"))
		Expect(Cf("files", AppName, "app/")).To(Say("config.ru"))
		Expect(Cf("files", AppName, "app/config.ru")).To(
			Say("run Sinatra::Application"),
		)
	})

	It("can show crash events", func() {
		Expect(Curl(AppUri("/sigterm/KILL"))).To(ExitWith(0))
		Eventually(func() *cmdtest.Session {
			return Cf("events", AppName)
		}, 10).Should(Say("exited"))
	})
})
<commit_msg>fix flaky cf files test<commit_after>package apps

import (
	"github.com/vito/cmdtest"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/vito/cmdtest/matchers"

	. "github.com/pivotal-cf-experimental/cf-acceptance-tests/helpers"
)

var _ = Describe("A running application", func() {
	BeforeEach(func() {
		AppName = RandomName()

		Expect(Cf("push", AppName, "-p", doraPath)).To(Say("Started"))
	})

	AfterEach(func() {
		Expect(Cf("delete", AppName, "-f")).To(Say("OK"))
	})

	It("can have its files inspected", func() {
		// Currently cannot work with multiple instances since GCF always checks instance 0
		Expect(Cf("files", AppName)).To(Say("app/"))
		Expect(Cf("files", AppName, "app/")).To(Say("config.ru"))
		Expect(Cf("files", AppName, "app/config.ru")).To(
			Say("run Sinatra::Application"),
		)
	})

	It("can show crash events", func() {
		Expect(Curl(AppUri("/sigterm/KILL"))).To(ExitWith(0))
		Eventually(func() *cmdtest.Session {
			return Cf("events", AppName)
		}, 10).Should(Say("exited"))
	})

	Context("with multiple instances", func() {
		BeforeEach(func() {
			Expect(
				Cf("scale", AppName, "-i", "2"),
			).To(Say("OK"))
		})

		It("can be queried for state by instance", func() {
			app := Cf("app", AppName)
			Expect(app).To(Say("#0"))
			Expect(app).To(Say("#1"))
		})
	})
})
<|endoftext|>


<commit_before>// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package firebase is the entry point to the Firebase Admin SDK. It provides functionality for initializing App
// instances, which serve as the central entities that provide access to various other Firebase services exposed
// from the SDK.
package firebase

import (
	"errors"

	"cloud.google.com/go/firestore"

	"firebase.google.com/go/auth"
	"firebase.google.com/go/internal"
	"firebase.google.com/go/storage"

	"os"

	"golang.org/x/net/context"
	"golang.org/x/oauth2/google"
	"google.golang.org/api/option"
	"google.golang.org/api/transport"
)

var firebaseScopes = []string{
	"https://www.googleapis.com/auth/cloud-platform",
	"https://www.googleapis.com/auth/datastore",
	"https://www.googleapis.com/auth/devstorage.full_control",
	"https://www.googleapis.com/auth/firebase",
	"https://www.googleapis.com/auth/identitytoolkit",
	"https://www.googleapis.com/auth/userinfo.email",
}

// Version of the Firebase Go Admin SDK.
const Version = "2.1.0"

// An App holds configuration and state common to all Firebase services that are exposed from the SDK.
type App struct {
	creds         *google.DefaultCredentials
	projectID     string
	storageBucket string
	opts          []option.ClientOption
}

// Config represents the configuration used to initialize an App.
type Config struct {
	ProjectID     string
	StorageBucket string
}

// Auth returns an instance of auth.Client.
func (a *App) Auth(ctx context.Context) (*auth.Client, error) {
	conf := &internal.AuthConfig{
		Creds:     a.creds,
		ProjectID: a.projectID,
		Opts:      a.opts,
	}
	return auth.NewClient(ctx, conf)
}

// Storage returns a new instance of storage.Client.
func (a *App) Storage(ctx context.Context) (*storage.Client, error) {
	conf := &internal.StorageConfig{
		Opts:   a.opts,
		Bucket: a.storageBucket,
	}
	return storage.NewClient(ctx, conf)
}

// Firestore returns a new firestore.Client instance from the https://godoc.org/cloud.google.com/go/firestore
// package.
func (a *App) Firestore(ctx context.Context) (*firestore.Client, error) {
	if a.projectID == "" {
		return nil, errors.New("project id is required to access Firestore")
	}
	return firestore.NewClient(ctx, a.projectID, a.opts...)
}

// NewApp creates a new App from the provided config and client options.
//
// If the client options contain a valid credential (a service account file, a refresh token file or an
// oauth2.TokenSource) the App will be authenticated using that credential. Otherwise, NewApp attempts to
// authenticate the App with Google application default credentials.
func NewApp(ctx context.Context, config *Config, opts ...option.ClientOption) (*App, error) {
	o := []option.ClientOption{option.WithScopes(firebaseScopes...)}
	o = append(o, opts...)

	creds, err := transport.Creds(ctx, o...)
	if err != nil {
		return nil, err
	}

	if config == nil {
		config = &Config{}
	}

	var pid string
	if config.ProjectID != "" {
		pid = config.ProjectID
	} else if creds.ProjectID != "" {
		pid = creds.ProjectID
	} else {
		pid = os.Getenv("GCLOUD_PROJECT")
	}

	return &App{
		creds:         creds,
		projectID:     pid,
		storageBucket: config.StorageBucket,
		opts:          o,
	}, nil
}
<commit_msg>Bump version to 2.2.0 (#49)<commit_after>// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package firebase is the entry point to the Firebase Admin SDK. It provides functionality for initializing App
// instances, which serve as the central entities that provide access to various other Firebase services exposed
// from the SDK.
package firebase

import (
	"errors"

	"cloud.google.com/go/firestore"

	"firebase.google.com/go/auth"
	"firebase.google.com/go/internal"
	"firebase.google.com/go/storage"

	"os"

	"golang.org/x/net/context"
	"golang.org/x/oauth2/google"
	"google.golang.org/api/option"
	"google.golang.org/api/transport"
)

var firebaseScopes = []string{
	"https://www.googleapis.com/auth/cloud-platform",
	"https://www.googleapis.com/auth/datastore",
	"https://www.googleapis.com/auth/devstorage.full_control",
	"https://www.googleapis.com/auth/firebase",
	"https://www.googleapis.com/auth/identitytoolkit",
	"https://www.googleapis.com/auth/userinfo.email",
}

// Version of the Firebase Go Admin SDK.
const Version = "2.2.0"

// An App holds configuration and state common to all Firebase services that are exposed from the SDK.
type App struct {
	creds         *google.DefaultCredentials
	projectID     string
	storageBucket string
	opts          []option.ClientOption
}

// Config represents the configuration used to initialize an App.
type Config struct {
	ProjectID     string
	StorageBucket string
}

// Auth returns an instance of auth.Client.
func (a *App) Auth(ctx context.Context) (*auth.Client, error) {
	conf := &internal.AuthConfig{
		Creds:     a.creds,
		ProjectID: a.projectID,
		Opts:      a.opts,
	}
	return auth.NewClient(ctx, conf)
}

// Storage returns a new instance of storage.Client.
func (a *App) Storage(ctx context.Context) (*storage.Client, error) {
	conf := &internal.StorageConfig{
		Opts:   a.opts,
		Bucket: a.storageBucket,
	}
	return storage.NewClient(ctx, conf)
}

// Firestore returns a new firestore.Client instance from the https://godoc.org/cloud.google.com/go/firestore
// package.
func (a *App) Firestore(ctx context.Context) (*firestore.Client, error) {
	if a.projectID == "" {
		return nil, errors.New("project id is required to access Firestore")
	}
	return firestore.NewClient(ctx, a.projectID, a.opts...)
}

// NewApp creates a new App from the provided config and client options.
//
// If the client options contain a valid credential (a service account file, a refresh token file or an
// oauth2.TokenSource) the App will be authenticated using that credential. Otherwise, NewApp attempts to
// authenticate the App with Google application default credentials.
func NewApp(ctx context.Context, config *Config, opts ...option.ClientOption) (*App, error) {
	o := []option.ClientOption{option.WithScopes(firebaseScopes...)}
	o = append(o, opts...)

	creds, err := transport.Creds(ctx, o...)
	if err != nil {
		return nil, err
	}

	if config == nil {
		config = &Config{}
	}

	var pid string
	if config.ProjectID != "" {
		pid = config.ProjectID
	} else if creds.ProjectID != "" {
		pid = creds.ProjectID
	} else {
		pid = os.Getenv("GCLOUD_PROJECT")
	}

	return &App{
		creds:         creds,
		projectID:     pid,
		storageBucket: config.StorageBucket,
		opts:          o,
	}, nil
}
<|endoftext|>